documentation and tslint settings to check for docs
This commit is contained in:
parent
08bd3dc0e8
commit
e7fa87bcc0
@ -30,33 +30,37 @@ function rAF(cb: (ts: number) => void) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Badge for Chrome that renders a Taler logo with a rotating ring if some
|
||||
* background activity is happening.
|
||||
*/
|
||||
export class ChromeBadge implements Badge {
|
||||
canvas: HTMLCanvasElement;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
private canvas: HTMLCanvasElement;
|
||||
private ctx: CanvasRenderingContext2D;
|
||||
/**
|
||||
* True if animation running. The animation
|
||||
* might still be running even if we're not busy anymore,
|
||||
* just to transition to the "normal" state in a animated way.
|
||||
*/
|
||||
animationRunning: boolean = false;
|
||||
private animationRunning: boolean = false;
|
||||
|
||||
/**
|
||||
* Is the wallet still busy? Note that we do not stop the
|
||||
* animation immediately when the wallet goes idle, but
|
||||
* instead slowly close the gap.
|
||||
*/
|
||||
isBusy: boolean = false;
|
||||
private isBusy: boolean = false;
|
||||
|
||||
/**
|
||||
* Current rotation angle, ranges from 0 to rotationAngleMax.
|
||||
*/
|
||||
rotationAngle: number = 0;
|
||||
private rotationAngle: number = 0;
|
||||
|
||||
/**
|
||||
* While animating, how wide is the current gap in the circle?
|
||||
* Ranges from 0 to openMax.
|
||||
*/
|
||||
gapWidth: number = 0;
|
||||
private gapWidth: number = 0;
|
||||
|
||||
/**
|
||||
* Maximum value for our rotationAngle, corresponds to 2 Pi.
|
||||
@ -207,14 +211,6 @@ export class ChromeBadge implements Badge {
|
||||
rAF(step);
|
||||
}
|
||||
|
||||
setText(s: string) {
|
||||
chrome.browserAction.setBadgeText({text: s});
|
||||
}
|
||||
|
||||
setColor(c: string) {
|
||||
chrome.browserAction.setBadgeBackgroundColor({color: c});
|
||||
}
|
||||
|
||||
startBusy() {
|
||||
if (this.isBusy) {
|
||||
return;
|
||||
|
@ -37,7 +37,7 @@ export interface StateHolder<T> {
|
||||
* but has multiple state holders.
|
||||
*/
|
||||
export abstract class ImplicitStateComponent<PropType> extends React.Component<PropType, any> {
|
||||
_implicit = {needsUpdate: false, didMount: false};
|
||||
private _implicit = {needsUpdate: false, didMount: false};
|
||||
componentDidMount() {
|
||||
this._implicit.didMount = true;
|
||||
if (this._implicit.needsUpdate) {
|
||||
|
@ -108,6 +108,10 @@ namespace RpcFunctions {
|
||||
return preCoin;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create and sign a message to request payback for a coin.
|
||||
*/
|
||||
export function createPaybackRequest(coin: CoinRecord): PaybackRequest {
|
||||
const p = new native.PaybackRequestPS({
|
||||
coin_blind: native.RsaBlindingKeySecret.fromCrock(coin.blindingKey),
|
||||
@ -127,6 +131,9 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a payment signature is valid.
|
||||
*/
|
||||
export function isValidPaymentSignature(sig: string, contractHash: string, merchantPub: string): boolean {
|
||||
const p = new native.PaymentSignaturePS({
|
||||
contract_hash: native.HashCode.fromCrock(contractHash),
|
||||
@ -140,6 +147,9 @@ namespace RpcFunctions {
|
||||
nativePub);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a wire fee is correctly signed.
|
||||
*/
|
||||
export function isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
|
||||
const p = new native.MasterWireFeePS({
|
||||
closing_fee: (new native.Amount(wf.closingFee)).toNbo(),
|
||||
@ -160,6 +170,9 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the signature of a denomination is valid.
|
||||
*/
|
||||
export function isValidDenom(denom: DenominationRecord,
|
||||
masterPub: string): boolean {
|
||||
const p = new native.DenominationKeyValidityPS({
|
||||
@ -189,6 +202,9 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new EdDSA key pair.
|
||||
*/
|
||||
export function createEddsaKeypair(): {priv: string, pub: string} {
|
||||
const priv = native.EddsaPrivateKey.create();
|
||||
const pub = priv.getPublicKey();
|
||||
@ -196,6 +212,9 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unblind a blindly signed value.
|
||||
*/
|
||||
export function rsaUnblind(sig: string, bk: string, pk: string): string {
|
||||
const denomSig = native.rsaUnblind(native.RsaSignature.fromCrock(sig),
|
||||
native.RsaBlindingKeySecret.fromCrock(bk),
|
||||
@ -278,6 +297,9 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new refresh session.
|
||||
*/
|
||||
export function createRefreshSession(exchangeBaseUrl: string,
|
||||
kappa: number,
|
||||
meltCoin: CoinRecord,
|
||||
@ -398,6 +420,9 @@ namespace RpcFunctions {
|
||||
return b.hash().toCrock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a denomination public key.
|
||||
*/
|
||||
export function hashDenomPub(denomPub: string): string {
|
||||
return native.RsaPublicKey.fromCrock(denomPub).encode().hash().toCrock();
|
||||
}
|
||||
|
@ -259,7 +259,7 @@ interface Arena {
|
||||
* Arena that must be manually destroyed.
|
||||
*/
|
||||
class SimpleArena implements Arena {
|
||||
heap: ArenaObject[];
|
||||
protected heap: ArenaObject[];
|
||||
|
||||
constructor() {
|
||||
this.heap = [];
|
||||
@ -774,7 +774,7 @@ export class EccSignaturePurpose extends PackedArenaObject {
|
||||
return this.payloadSize + 8;
|
||||
}
|
||||
|
||||
payloadSize: number;
|
||||
private payloadSize: number;
|
||||
|
||||
constructor(purpose: SignaturePurpose,
|
||||
payload: PackedArenaObject,
|
||||
|
@ -22,10 +22,22 @@ const fork = require("child_process").fork;
|
||||
|
||||
const nodeWorkerEntry = path.join(__dirname, "nodeWorkerEntry.js");
|
||||
|
||||
/**
|
||||
* Worker implementation that uses node subprocesses.
|
||||
*/
|
||||
export class Worker {
|
||||
child: any;
|
||||
private child: any;
|
||||
|
||||
/**
|
||||
* Function to be called when we receive a message from the worker thread.
|
||||
*/
|
||||
onmessage: undefined | ((m: any) => void);
|
||||
|
||||
/**
|
||||
* Function to be called when we receive an error from the worker thread.
|
||||
*/
|
||||
onerror: undefined | ((m: any) => void);
|
||||
|
||||
constructor(scriptFilename: string) {
|
||||
this.child = fork(nodeWorkerEntry);
|
||||
this.onerror = undefined;
|
||||
@ -55,6 +67,9 @@ export class Worker {
|
||||
this.child.send({scriptFilename, cwd: process.cwd()});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event listener for either an "error" or "message" event.
|
||||
*/
|
||||
addEventListener(event: "message" | "error", fn: (x: any) => void): void {
|
||||
switch (event) {
|
||||
case "message":
|
||||
@ -66,10 +81,16 @@ export class Worker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to the worker thread.
|
||||
*/
|
||||
postMessage (msg: any) {
|
||||
this.child.send(JSON.stringify({data: msg}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly terminate the worker thread.
|
||||
*/
|
||||
terminate () {
|
||||
this.child.kill("SIGINT");
|
||||
}
|
||||
|
@ -83,6 +83,10 @@ export function canonicalJson(obj: any): string {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for deep equality of two objects.
|
||||
* Only arrays, objects and primitives are supported.
|
||||
*/
|
||||
export function deepEquals(x: any, y: any): boolean {
|
||||
if (x === y) {
|
||||
return true;
|
||||
@ -98,6 +102,10 @@ export function deepEquals(x: any, y: any): boolean {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map from a collection to a list or results and then
|
||||
* concatenate the results.
|
||||
*/
|
||||
export function flatMap<T, U>(xs: T[], f: (x: T) => U[]): U[] {
|
||||
return xs.reduce((acc: U[], next: T) => [...f(next), ...acc], []);
|
||||
}
|
||||
|
@ -15,9 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configurable logging.
|
||||
*
|
||||
* @author Florian Dold
|
||||
* Configurable logging. Allows to log persistently to a database.
|
||||
*/
|
||||
|
||||
import {
|
||||
@ -26,8 +24,14 @@ import {
|
||||
openPromise,
|
||||
} from "./query";
|
||||
|
||||
/**
|
||||
* Supported log levels.
|
||||
*/
|
||||
export type Level = "error" | "debug" | "info" | "warn";
|
||||
|
||||
// Right now, our debug/info/warn/debug loggers just use the console based
|
||||
// loggers. This might change in the future.
|
||||
|
||||
function makeInfo() {
|
||||
return console.info.bind(console, "%o");
|
||||
}
|
||||
@ -44,6 +48,9 @@ function makeDebug() {
|
||||
return console.log.bind(console, "%o");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message using the configurable logger.
|
||||
*/
|
||||
export async function log(msg: string, level: Level = "info"): Promise<void> {
|
||||
const ci = getCallInfo(2);
|
||||
return record(level, msg, undefined, ci.file, ci.line, ci.column);
|
||||
@ -122,17 +129,50 @@ function parseStackLine(stackLine: string): Frame {
|
||||
|
||||
let db: IDBDatabase|undefined;
|
||||
|
||||
/**
|
||||
* A structured log entry as stored in the database.
|
||||
*/
|
||||
export interface LogEntry {
|
||||
/**
|
||||
* Soure code column where the error occured.
|
||||
*/
|
||||
col?: number;
|
||||
/**
|
||||
* Additional detail for the log statement.
|
||||
*/
|
||||
detail?: string;
|
||||
/**
|
||||
* Id of the log entry, used as primary
|
||||
* key for the database.
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* Log level, see [[Level}}.
|
||||
*/
|
||||
level: string;
|
||||
/**
|
||||
* Line where the log was created from.
|
||||
*/
|
||||
line?: number;
|
||||
/**
|
||||
* The actual log message.
|
||||
*/
|
||||
msg: string;
|
||||
/**
|
||||
* The source file where the log enctry
|
||||
* was created from.
|
||||
*/
|
||||
source?: string;
|
||||
/**
|
||||
* Time when the log entry was created.
|
||||
*/
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all logs. Only use for debugging, since this returns all logs ever made
|
||||
* at once without pagination.
|
||||
*/
|
||||
export async function getLogs(): Promise<LogEntry[]> {
|
||||
if (!db) {
|
||||
db = await openLoggingDb();
|
||||
@ -147,6 +187,9 @@ export async function getLogs(): Promise<LogEntry[]> {
|
||||
*/
|
||||
let barrier: any;
|
||||
|
||||
/**
|
||||
* Record an exeption in the log.
|
||||
*/
|
||||
export async function recordException(msg: string, e: any): Promise<void> {
|
||||
let stack: string|undefined;
|
||||
let frame: Frame|undefined;
|
||||
@ -165,6 +208,9 @@ export async function recordException(msg: string, e: any): Promise<void> {
|
||||
return record("error", e.toString(), stack, frame.file, frame.line, frame.column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a log entry in the database.
|
||||
*/
|
||||
export async function record(level: Level,
|
||||
msg: string,
|
||||
detail?: string,
|
||||
@ -215,6 +261,10 @@ const loggingDbVersion = 1;
|
||||
|
||||
const logsStore: Store<LogEntry> = new Store<LogEntry>("logs");
|
||||
|
||||
/**
|
||||
* Get a handle to the IndexedDB used to store
|
||||
* logs.
|
||||
*/
|
||||
export function openLoggingDb(): Promise<IDBDatabase> {
|
||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const req = indexedDB.open("taler-logging", loggingDbVersion);
|
||||
@ -238,7 +288,22 @@ export function openLoggingDb(): Promise<IDBDatabase> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message at severity info.
|
||||
*/
|
||||
export const info = makeInfo();
|
||||
|
||||
/**
|
||||
* Log a message at severity debug.
|
||||
*/
|
||||
export const debug = makeDebug();
|
||||
|
||||
/**
|
||||
* Log a message at severity warn.
|
||||
*/
|
||||
export const warn = makeWarn();
|
||||
|
||||
/**
|
||||
* Log a message at severity error.
|
||||
*/
|
||||
export const error = makeError();
|
||||
|
95
src/query.ts
95
src/query.ts
@ -53,6 +53,9 @@ export class Store<T> {
|
||||
* Definition of an index.
|
||||
*/
|
||||
export class Index<S extends IDBValidKey, T> {
|
||||
/**
|
||||
* Name of the store that this index is associated with.
|
||||
*/
|
||||
storeName: string;
|
||||
|
||||
constructor(s: Store<T>, public indexName: string, public keyPath: string | string[]) {
|
||||
@ -127,16 +130,26 @@ export interface QueryStream<T> {
|
||||
* Query result that consists of at most one value.
|
||||
*/
|
||||
export interface QueryValue<T> {
|
||||
/**
|
||||
* Apply a function to a query value.
|
||||
*/
|
||||
map<S>(f: (x: T) => S): QueryValue<S>;
|
||||
/**
|
||||
* Conditionally execute either of two queries based
|
||||
* on a property of this query value.
|
||||
*
|
||||
* Useful to properly implement complex queries within a transaction (as
|
||||
* opposed to just computing the conditional and then executing either
|
||||
* branch). This is necessary since IndexedDB does not allow long-lived
|
||||
* transactions.
|
||||
*/
|
||||
cond<R>(f: (x: T) => boolean, onTrue: (r: QueryRoot) => R, onFalse: (r: QueryRoot) => R): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
abstract class BaseQueryValue<T> implements QueryValue<T> {
|
||||
root: QueryRoot;
|
||||
|
||||
constructor(root: QueryRoot) {
|
||||
this.root = root;
|
||||
constructor(public root: QueryRoot) {
|
||||
}
|
||||
|
||||
map<S>(f: (x: T) => S): QueryValue<S> {
|
||||
@ -160,8 +173,9 @@ abstract class BaseQueryValue<T> implements QueryValue<T> {
|
||||
}
|
||||
|
||||
class FirstQueryValue<T> extends BaseQueryValue<T> {
|
||||
gotValue = false;
|
||||
s: QueryStreamBase<T>;
|
||||
private gotValue = false;
|
||||
private s: QueryStreamBase<T>;
|
||||
|
||||
constructor(stream: QueryStreamBase<T>) {
|
||||
super(stream.root);
|
||||
this.s = stream;
|
||||
@ -183,13 +197,8 @@ class FirstQueryValue<T> extends BaseQueryValue<T> {
|
||||
}
|
||||
|
||||
class MapQueryValue<T, S> extends BaseQueryValue<S> {
|
||||
mapFn: (x: T) => S;
|
||||
v: BaseQueryValue<T>;
|
||||
|
||||
constructor(v: BaseQueryValue<T>, mapFn: (x: T) => S) {
|
||||
constructor(private v: BaseQueryValue<T>, private mapFn: (x: T) => S) {
|
||||
super(v.root);
|
||||
this.v = v;
|
||||
this.mapFn = mapFn;
|
||||
}
|
||||
|
||||
subscribeOne(f: SubscribeOneFn): void {
|
||||
@ -226,11 +235,7 @@ abstract class QueryStreamBase<T> implements QueryStream<T>, PromiseLike<void> {
|
||||
abstract subscribe(f: (isDone: boolean,
|
||||
value: any,
|
||||
tx: IDBTransaction) => void): void;
|
||||
|
||||
root: QueryRoot;
|
||||
|
||||
constructor(root: QueryRoot) {
|
||||
this.root = root;
|
||||
constructor(public root: QueryRoot) {
|
||||
}
|
||||
|
||||
first(): QueryValue<T> {
|
||||
@ -313,13 +318,8 @@ type SubscribeOneFn = (value: any, tx: IDBTransaction) => void;
|
||||
type FlatMapFn<T> = (v: T) => T[];
|
||||
|
||||
class QueryStreamFilter<T> extends QueryStreamBase<T> {
|
||||
s: QueryStreamBase<T>;
|
||||
filterFn: FilterFn;
|
||||
|
||||
constructor(s: QueryStreamBase<T>, filterFn: FilterFn) {
|
||||
constructor(public s: QueryStreamBase<T>, public filterFn: FilterFn) {
|
||||
super(s.root);
|
||||
this.s = s;
|
||||
this.filterFn = filterFn;
|
||||
}
|
||||
|
||||
subscribe(f: SubscribeFn) {
|
||||
@ -337,13 +337,8 @@ class QueryStreamFilter<T> extends QueryStreamBase<T> {
|
||||
|
||||
|
||||
class QueryStreamFlatMap<T, S> extends QueryStreamBase<S> {
|
||||
s: QueryStreamBase<T>;
|
||||
flatMapFn: (v: T) => S[];
|
||||
|
||||
constructor(s: QueryStreamBase<T>, flatMapFn: (v: T) => S[]) {
|
||||
constructor(public s: QueryStreamBase<T>, public flatMapFn: (v: T) => S[]) {
|
||||
super(s.root);
|
||||
this.s = s;
|
||||
this.flatMapFn = flatMapFn;
|
||||
}
|
||||
|
||||
subscribe(f: SubscribeFn) {
|
||||
@ -362,13 +357,8 @@ class QueryStreamFlatMap<T, S> extends QueryStreamBase<S> {
|
||||
|
||||
|
||||
class QueryStreamMap<S, T> extends QueryStreamBase<T> {
|
||||
s: QueryStreamBase<S>;
|
||||
mapFn: (v: S) => T;
|
||||
|
||||
constructor(s: QueryStreamBase<S>, mapFn: (v: S) => T) {
|
||||
constructor(public s: QueryStreamBase<S>, public mapFn: (v: S) => T) {
|
||||
super(s.root);
|
||||
this.s = s;
|
||||
this.mapFn = mapFn;
|
||||
}
|
||||
|
||||
subscribe(f: SubscribeFn) {
|
||||
@ -385,18 +375,9 @@ class QueryStreamMap<S, T> extends QueryStreamBase<T> {
|
||||
|
||||
|
||||
class QueryStreamIndexJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
|
||||
s: QueryStreamBase<T>;
|
||||
storeName: string;
|
||||
key: any;
|
||||
indexName: string;
|
||||
|
||||
constructor(s: QueryStreamBase<T>, storeName: string, indexName: string,
|
||||
key: any) {
|
||||
constructor(public s: QueryStreamBase<T>, public storeName: string, public indexName: string,
|
||||
public key: any) {
|
||||
super(s.root);
|
||||
this.s = s;
|
||||
this.storeName = storeName;
|
||||
this.key = key;
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
subscribe(f: SubscribeFn) {
|
||||
@ -420,18 +401,9 @@ class QueryStreamIndexJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
|
||||
|
||||
|
||||
class QueryStreamIndexJoinLeft<T, S> extends QueryStreamBase<JoinLeftResult<T, S>> {
|
||||
s: QueryStreamBase<T>;
|
||||
storeName: string;
|
||||
key: any;
|
||||
indexName: string;
|
||||
|
||||
constructor(s: QueryStreamBase<T>, storeName: string, indexName: string,
|
||||
key: any) {
|
||||
constructor(public s: QueryStreamBase<T>, public storeName: string, public indexName: string,
|
||||
public key: any) {
|
||||
super(s.root);
|
||||
this.s = s;
|
||||
this.storeName = storeName;
|
||||
this.key = key;
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
subscribe(f: SubscribeFn) {
|
||||
@ -461,16 +433,9 @@ class QueryStreamIndexJoinLeft<T, S> extends QueryStreamBase<JoinLeftResult<T, S
|
||||
|
||||
|
||||
class QueryStreamKeyJoin<T, S> extends QueryStreamBase<JoinResult<T, S>> {
|
||||
s: QueryStreamBase<T>;
|
||||
storeName: string;
|
||||
key: any;
|
||||
|
||||
constructor(s: QueryStreamBase<T>, storeName: string,
|
||||
key: any) {
|
||||
constructor(public s: QueryStreamBase<T>, public storeName: string,
|
||||
public key: any) {
|
||||
super(s.root);
|
||||
this.s = s;
|
||||
this.storeName = storeName;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
subscribe(f: SubscribeFn) {
|
||||
|
527
src/types.ts
527
src/types.ts
@ -28,32 +28,77 @@
|
||||
*/
|
||||
import { Checkable } from "./checkable";
|
||||
|
||||
/**
|
||||
* Non-negative financial amount. Fractional values are expressed as multiples
|
||||
* of 1e-8.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class AmountJson {
|
||||
/**
|
||||
* Value, must be an integer.
|
||||
*/
|
||||
@Checkable.Number
|
||||
value: number;
|
||||
readonly value: number;
|
||||
|
||||
/**
|
||||
* Fraction, must be an integer. Represent 1/1e8 of a unit.
|
||||
*/
|
||||
@Checkable.Number
|
||||
fraction: number;
|
||||
readonly fraction: number;
|
||||
|
||||
/**
|
||||
* Currency of the amount.
|
||||
*/
|
||||
@Checkable.String
|
||||
currency: string;
|
||||
readonly currency: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => AmountJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Amount with a sign.
|
||||
*/
|
||||
export interface SignedAmountJson {
|
||||
/**
|
||||
* The absolute amount.
|
||||
*/
|
||||
amount: AmountJson;
|
||||
/**
|
||||
* Sign.
|
||||
*/
|
||||
isNegative: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A reserve record as stored in the wallet's database.
|
||||
*/
|
||||
export interface ReserveRecord {
|
||||
/**
|
||||
* The reserve public key.
|
||||
*/
|
||||
reserve_pub: string;
|
||||
/**
|
||||
* The reserve private key.
|
||||
*/
|
||||
reserve_priv: string;
|
||||
/**
|
||||
* The exchange base URL.
|
||||
*/
|
||||
exchange_base_url: string;
|
||||
/**
|
||||
* Time when the reserve was created.
|
||||
*/
|
||||
created: number;
|
||||
/**
|
||||
* Time when the reserve was last queried,
|
||||
* or 'null' if it was never queried.
|
||||
*/
|
||||
last_query: number | null;
|
||||
/**
|
||||
* Current amount left in the reserve
|
||||
@ -65,17 +110,16 @@ export interface ReserveRecord {
|
||||
* be higher than the requested_amount
|
||||
*/
|
||||
requested_amount: AmountJson;
|
||||
|
||||
|
||||
/**
|
||||
* What's the current amount that sits
|
||||
* in precoins?
|
||||
*/
|
||||
precoin_amount: AmountJson;
|
||||
|
||||
|
||||
/**
|
||||
* The bank conformed that the reserve will eventually
|
||||
* be filled with money.
|
||||
*/
|
||||
confirmed: boolean;
|
||||
|
||||
/**
|
||||
* We got some payback to this reserve. We'll cease to automatically
|
||||
* withdraw money from it.
|
||||
@ -106,6 +150,9 @@ export interface CurrencyRecord {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Response for the create reserve request to the wallet.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class CreateReserveResponse {
|
||||
/**
|
||||
@ -115,52 +162,114 @@ export class CreateReserveResponse {
|
||||
@Checkable.String
|
||||
exchange: string;
|
||||
|
||||
/**
|
||||
* Reserve public key of the newly created reserve.
|
||||
*/
|
||||
@Checkable.String
|
||||
reservePub: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => CreateReserveResponse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of a denomination.
|
||||
*/
|
||||
export enum DenominationStatus {
|
||||
/**
|
||||
* Verification was delayed.
|
||||
*/
|
||||
Unverified,
|
||||
/**
|
||||
* Verified as valid.
|
||||
*/
|
||||
VerifiedGood,
|
||||
/**
|
||||
* Verified as invalid.
|
||||
*/
|
||||
VerifiedBad,
|
||||
}
|
||||
|
||||
/**
|
||||
* Denomination record as stored in the wallet's database.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class DenominationRecord {
|
||||
/**
|
||||
* Value of one coin of the denomination.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
value: AmountJson;
|
||||
|
||||
/**
|
||||
* The denomination public key.
|
||||
*/
|
||||
@Checkable.String
|
||||
denomPub: string;
|
||||
|
||||
/**
|
||||
* Hash of the denomination public key.
|
||||
* Stored in the database for faster lookups.
|
||||
*/
|
||||
@Checkable.String
|
||||
denomPubHash: string;
|
||||
|
||||
/**
|
||||
* Fee for withdrawing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeWithdraw: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for depositing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeDeposit: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refreshing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeRefresh: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refunding.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeRefund: AmountJson;
|
||||
|
||||
/**
|
||||
* Validity start date of the denomination.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampStart: string;
|
||||
|
||||
/**
|
||||
* Date after which the currency can't be withdrawn anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampExpireWithdraw: string;
|
||||
|
||||
/**
|
||||
* Date after the denomination officially doesn't exist anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampExpireLegal: string;
|
||||
|
||||
/**
|
||||
* Data after which coins of this denomination can't be deposited anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampExpireDeposit: string;
|
||||
|
||||
/**
|
||||
* Signature by the exchange's master key over the denomination
|
||||
* information.
|
||||
*/
|
||||
@Checkable.String
|
||||
masterSig: string;
|
||||
|
||||
@ -178,9 +287,16 @@ export class DenominationRecord {
|
||||
@Checkable.Boolean
|
||||
isOffered: boolean;
|
||||
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => Denomination;
|
||||
}
|
||||
|
||||
@ -189,59 +305,124 @@ export class DenominationRecord {
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class Denomination {
|
||||
/**
|
||||
* Value of one coin of the denomination.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
value: AmountJson;
|
||||
|
||||
/**
|
||||
* Public signing key of the denomination.
|
||||
*/
|
||||
@Checkable.String
|
||||
denom_pub: string;
|
||||
|
||||
/**
|
||||
* Fee for withdrawing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_withdraw: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for depositing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_deposit: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refreshing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_refresh: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refunding.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_refund: AmountJson;
|
||||
|
||||
/**
|
||||
* Start date from which withdraw is allowed.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_start: string;
|
||||
|
||||
/**
|
||||
* End date for withdrawing.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_expire_withdraw: string;
|
||||
|
||||
/**
|
||||
* Expiration date after which the exchange can forget about
|
||||
* the currency.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_expire_legal: string;
|
||||
|
||||
/**
|
||||
* Date after which the coins of this denomination can't be
|
||||
* deposited anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_expire_deposit: string;
|
||||
|
||||
/**
|
||||
* Signature over the denomination information by the exchange's master
|
||||
* signing key.
|
||||
*/
|
||||
@Checkable.String
|
||||
master_sig: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => Denomination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Auditor information.
|
||||
*/
|
||||
export interface Auditor {
|
||||
// official name
|
||||
/**
|
||||
* Official name.
|
||||
*/
|
||||
name: string;
|
||||
|
||||
// Auditor's public key
|
||||
/**
|
||||
* Auditor's public key.
|
||||
*/
|
||||
auditor_pub: string;
|
||||
|
||||
// Base URL of the auditor
|
||||
/**
|
||||
* Base URL of the auditor.
|
||||
*/
|
||||
url: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exchange record as stored in the wallet's database.
|
||||
*/
|
||||
export interface ExchangeRecord {
|
||||
/**
|
||||
* Base url of the exchange.
|
||||
*/
|
||||
baseUrl: string;
|
||||
/**
|
||||
* Master public key of the exchange.
|
||||
*/
|
||||
masterPublicKey: string;
|
||||
/**
|
||||
* Auditors (partially) auditing the exchange.
|
||||
*/
|
||||
auditors: Auditor[];
|
||||
|
||||
/**
|
||||
* Currency that the exchange offers.
|
||||
*/
|
||||
currency: string;
|
||||
|
||||
/**
|
||||
@ -282,14 +463,36 @@ export interface PreCoinRecord {
|
||||
coinValue: AmountJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Planchet for a coin during refrehs.
|
||||
*/
|
||||
export interface RefreshPreCoinRecord {
|
||||
/**
|
||||
* Public key for the coin.
|
||||
*/
|
||||
publicKey: string;
|
||||
/**
|
||||
* Private key for the coin.
|
||||
*/
|
||||
privateKey: string;
|
||||
/**
|
||||
* Blinded public key.
|
||||
*/
|
||||
coinEv: string;
|
||||
/**
|
||||
* Blinding key used.
|
||||
*/
|
||||
blindingKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request that we send to the exchange to get a payback.
|
||||
*/
|
||||
export interface PaybackRequest {
|
||||
/**
|
||||
* Denomination public key of the coin we want to get
|
||||
* paid back.
|
||||
*/
|
||||
denom_pub: string;
|
||||
|
||||
/**
|
||||
@ -297,13 +500,26 @@ export interface PaybackRequest {
|
||||
*/
|
||||
denom_sig: string;
|
||||
|
||||
/**
|
||||
* Coin public key of the coin we want to refund.
|
||||
*/
|
||||
coin_pub: string;
|
||||
|
||||
/**
|
||||
* Blinding key that was used during withdraw,
|
||||
* used to prove that we were actually withdrawing the coin.
|
||||
*/
|
||||
coin_blind_key_secret: string;
|
||||
|
||||
/**
|
||||
* Signature made by the coin, authorizing the payback.
|
||||
*/
|
||||
coin_sig: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response that we get from the exchange for a payback request.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class PaybackConfirmation {
|
||||
/**
|
||||
@ -344,6 +560,10 @@ export class PaybackConfirmation {
|
||||
@Checkable.String
|
||||
exchange_pub: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => PaybackConfirmation;
|
||||
}
|
||||
|
||||
@ -378,15 +598,19 @@ export interface RefreshSessionRecord {
|
||||
*/
|
||||
newDenoms: string[];
|
||||
|
||||
|
||||
/**
|
||||
* Precoins for each cut-and-choose instance.
|
||||
*/
|
||||
preCoinsForGammas: RefreshPreCoinRecord[][];
|
||||
|
||||
|
||||
/**
|
||||
* The transfer keys, kappa of them.
|
||||
*/
|
||||
transferPubs: string[];
|
||||
|
||||
/**
|
||||
* Private keys for the transfer public keys.
|
||||
*/
|
||||
transferPrivs: string[];
|
||||
|
||||
/**
|
||||
@ -399,23 +623,73 @@ export interface RefreshSessionRecord {
|
||||
*/
|
||||
hash: string;
|
||||
|
||||
/**
|
||||
* Base URL for the exchange we're doing the refresh with.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Is this session finished?
|
||||
*/
|
||||
finished: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deposit permission for a single coin.
|
||||
*/
|
||||
export interface CoinPaySig {
|
||||
/**
|
||||
* Signature by the coin.
|
||||
*/
|
||||
coin_sig: string;
|
||||
/**
|
||||
* Public key of the coin being spend.
|
||||
*/
|
||||
coin_pub: string;
|
||||
/**
|
||||
* Signature made by the denomination public key.
|
||||
*/
|
||||
ub_sig: string;
|
||||
/**
|
||||
* The denomination public key associated with this coin.
|
||||
*/
|
||||
denom_pub: string;
|
||||
/**
|
||||
* The amount that is subtracted from this coin with this payment.
|
||||
*/
|
||||
f: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of a coin.
|
||||
*/
|
||||
export enum CoinStatus {
|
||||
Fresh, TransactionPending, Dirty, Refreshed, PaybackPending, PaybackDone,
|
||||
/**
|
||||
* Withdrawn and never shown to anybody.
|
||||
*/
|
||||
Fresh,
|
||||
/**
|
||||
* Currently planned to be sent to a merchant for a transaction.
|
||||
*/
|
||||
TransactionPending,
|
||||
/**
|
||||
* Used for a completed transaction and now dirty.
|
||||
*/
|
||||
Dirty,
|
||||
/**
|
||||
* A coin that was refreshed.
|
||||
*/
|
||||
Refreshed,
|
||||
/**
|
||||
* Coin marked to be paid back, but payback not finished.
|
||||
*/
|
||||
PaybackPending,
|
||||
/**
|
||||
* Coin fully paid back.
|
||||
*/
|
||||
PaybackDone,
|
||||
}
|
||||
|
||||
|
||||
@ -462,6 +736,10 @@ export interface CoinRecord {
|
||||
*/
|
||||
suspended?: boolean;
|
||||
|
||||
/**
|
||||
* Blinding key used when withdrawing the coin.
|
||||
* Potentionally sed again during payback.
|
||||
*/
|
||||
blindingKey: string;
|
||||
|
||||
/**
|
||||
@ -477,29 +755,70 @@ export interface CoinRecord {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about an exchange as stored inside a
|
||||
* merchant's contract terms.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ExchangeHandle {
|
||||
/**
|
||||
* Master public signing key of the exchange.
|
||||
*/
|
||||
@Checkable.String
|
||||
master_pub: string;
|
||||
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
@Checkable.String
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ExchangeHandle;
|
||||
}
|
||||
|
||||
export interface WalletBalance {
|
||||
[currency: string]: WalletBalanceEntry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping from currency names to detailed balance
|
||||
* information for that particular currency.
|
||||
*/
|
||||
export interface WalletBalance {
|
||||
/**
|
||||
* Mapping from currency name to defailed balance info.
|
||||
*/
|
||||
[currency: string]: WalletBalanceEntry;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Detailed wallet balance for a particular currency.
|
||||
*/
|
||||
export interface WalletBalanceEntry {
|
||||
/**
|
||||
* Directly available amount.
|
||||
*/
|
||||
available: AmountJson;
|
||||
/**
|
||||
* Amount that we're waiting for (refresh, withdrawal).
|
||||
*/
|
||||
pendingIncoming: AmountJson;
|
||||
/**
|
||||
* Amount that's marked for a pending payment.
|
||||
*/
|
||||
pendingPayment: AmountJson;
|
||||
/**
|
||||
* Amount that was paid back and we could withdraw again.
|
||||
*/
|
||||
paybackAmount: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about a merchant.
|
||||
*/
|
||||
interface Merchant {
|
||||
/**
|
||||
* label for a location with the business address of the merchant
|
||||
@ -524,108 +843,235 @@ interface Merchant {
|
||||
instance?: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contract terms from a merchant.
|
||||
*/
|
||||
@Checkable.Class({validate: true})
|
||||
export class Contract {
|
||||
|
||||
validate() {
|
||||
private validate() {
|
||||
if (this.exchanges.length === 0) {
|
||||
throw Error("no exchanges in contract");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash of the merchant's wire details.
|
||||
*/
|
||||
@Checkable.String
|
||||
H_wire: string;
|
||||
|
||||
/**
|
||||
* Wire method the merchant wants to use.
|
||||
*/
|
||||
@Checkable.String
|
||||
wire_method: string;
|
||||
|
||||
/**
|
||||
* Human-readable short summary of the contract.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.String)
|
||||
summary?: string;
|
||||
|
||||
/**
|
||||
* Nonce used to ensure freshness.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.String)
|
||||
nonce?: string;
|
||||
|
||||
/**
|
||||
* Total amount payable.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Auditors accepted by the merchant.
|
||||
*/
|
||||
@Checkable.List(Checkable.AnyObject)
|
||||
auditors: any[];
|
||||
|
||||
/**
|
||||
* Deadline to pay for the contract.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.String)
|
||||
pay_deadline: string;
|
||||
|
||||
/**
|
||||
* Delivery locations.
|
||||
*/
|
||||
@Checkable.Any
|
||||
locations: any;
|
||||
|
||||
/**
|
||||
* Maximum deposit fee covered by the merchant.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
max_fee: AmountJson;
|
||||
|
||||
/**
|
||||
* Information about the merchant.
|
||||
*/
|
||||
@Checkable.Any
|
||||
merchant: any;
|
||||
|
||||
/**
|
||||
* Public key of the merchant.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchant_pub: string;
|
||||
|
||||
/**
|
||||
* List of accepted exchanges.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(ExchangeHandle))
|
||||
exchanges: ExchangeHandle[];
|
||||
|
||||
/**
|
||||
* Products that are sold in this contract.
|
||||
*/
|
||||
@Checkable.List(Checkable.AnyObject)
|
||||
products: any[];
|
||||
|
||||
/**
|
||||
* Deadline for refunds.
|
||||
*/
|
||||
@Checkable.String
|
||||
refund_deadline: string;
|
||||
|
||||
/**
|
||||
* Time when the contract was generated by the merchant.
|
||||
*/
|
||||
@Checkable.String
|
||||
timestamp: string;
|
||||
|
||||
/**
|
||||
* Order id to uniquely identify the purchase within
|
||||
* one merchant instance.
|
||||
*/
|
||||
@Checkable.String
|
||||
order_id: string;
|
||||
|
||||
/**
|
||||
* URL to post the payment to.
|
||||
*/
|
||||
@Checkable.String
|
||||
pay_url: string;
|
||||
|
||||
/**
|
||||
* Fulfillment URL to view the product or
|
||||
* delivery status.
|
||||
*/
|
||||
@Checkable.String
|
||||
fulfillment_url: string;
|
||||
|
||||
/**
|
||||
* Share of the wire fee that must be settled with one payment.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Number)
|
||||
wire_fee_amortization?: number;
|
||||
|
||||
/**
|
||||
* Maximum wire fee that the merchant agrees to pay for.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Value(AmountJson))
|
||||
max_wire_fee?: AmountJson;
|
||||
|
||||
/**
|
||||
* Extra data, interpreted by the mechant only.
|
||||
*/
|
||||
@Checkable.Any
|
||||
extra: any;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => Contract;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire fee for one wire method as stored in the
|
||||
* wallet's database.
|
||||
*/
|
||||
export interface WireFee {
|
||||
/**
|
||||
* Fee for wire transfers.
|
||||
*/
|
||||
wireFee: AmountJson;
|
||||
|
||||
/**
|
||||
* Fees to close and refund a reserve.
|
||||
*/
|
||||
closingFee: AmountJson;
|
||||
|
||||
/**
|
||||
* Start date of the fee.
|
||||
*/
|
||||
startStamp: number;
|
||||
|
||||
/**
|
||||
* End date of the fee.
|
||||
*/
|
||||
endStamp: number;
|
||||
|
||||
/**
|
||||
* Signature made by the exchange master key.
|
||||
*/
|
||||
sig: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire fees for an exchange.
|
||||
*/
|
||||
export interface ExchangeWireFeesRecord {
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
feesForType: { [type: string]: WireFee[] };
|
||||
|
||||
/**
|
||||
* Mapping from wire method type to the wire fee.
|
||||
*/
|
||||
feesForType: { [wireMethod: string]: WireFee[] };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Coins used for a payment, with signatures authorizing the payment and the
|
||||
* coins with remaining value updated to accomodate for a payment.
|
||||
*/
|
||||
export type PayCoinInfo = Array<{ updatedCoin: CoinRecord, sig: CoinPaySig }>;
|
||||
|
||||
|
||||
/**
|
||||
* Amount helpers.
|
||||
*/
|
||||
export namespace Amounts {
|
||||
/**
|
||||
* Number of fractional units that one value unit represents.
|
||||
*/
|
||||
export const fractionalBase = 1e8;
|
||||
|
||||
/**
|
||||
* Result of a possibly overflowing operation.
|
||||
*/
|
||||
export interface Result {
|
||||
/**
|
||||
* Resulting, possibly saturated amount.
|
||||
*/
|
||||
amount: AmountJson;
|
||||
// Was there an over-/underflow?
|
||||
/**
|
||||
* Was there an over-/underflow?
|
||||
*/
|
||||
saturated: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the largest amount that is safely representable.
|
||||
*/
|
||||
export function getMaxAmount(currency: string): AmountJson {
|
||||
return {
|
||||
currency,
|
||||
@ -634,6 +1080,9 @@ export namespace Amounts {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an amount that represents zero units of a currency.
|
||||
*/
|
||||
export function getZero(currency: string): AmountJson {
|
||||
return {
|
||||
currency,
|
||||
@ -642,6 +1091,13 @@ export namespace Amounts {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add two amounts. Return the result and whether
|
||||
* the addition overflowed. The overflow is always handled
|
||||
* by saturating and never by wrapping.
|
||||
*
|
||||
* Throws when currencies don't match.
|
||||
*/
|
||||
export function add(first: AmountJson, ...rest: AmountJson[]): Result {
|
||||
const currency = first.currency;
|
||||
let value = first.value + Math.floor(first.fraction / fractionalBase);
|
||||
@ -663,7 +1119,13 @@ export namespace Amounts {
|
||||
return { amount: { currency, value, fraction }, saturated: false };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subtract two amounts. Return the result and whether
|
||||
* the subtraction overflowed. The overflow is always handled
|
||||
* by saturating and never by wrapping.
|
||||
*
|
||||
* Throws when currencies don't match.
|
||||
*/
|
||||
export function sub(a: AmountJson, ...rest: AmountJson[]): Result {
|
||||
const currency = a.currency;
|
||||
let value = a.value;
|
||||
@ -691,6 +1153,10 @@ export namespace Amounts {
|
||||
return { amount: { currency, value, fraction }, saturated: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two amounts. Returns 0 when equal, -1 when a < b
|
||||
* and +1 when a > b. Throws when currencies don't match.
|
||||
*/
|
||||
export function cmp(a: AmountJson, b: AmountJson): number {
|
||||
if (a.currency !== b.currency) {
|
||||
throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
|
||||
@ -715,6 +1181,9 @@ export namespace Amounts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of an amount.
|
||||
*/
|
||||
export function copy(a: AmountJson): AmountJson {
|
||||
return {
|
||||
currency: a.currency,
|
||||
@ -723,6 +1192,9 @@ export namespace Amounts {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide an amount. Throws on division by zero.
|
||||
*/
|
||||
export function divide(a: AmountJson, n: number): AmountJson {
|
||||
if (n === 0) {
|
||||
throw Error(`Division by 0`);
|
||||
@ -738,7 +1210,10 @@ export namespace Amounts {
|
||||
};
|
||||
}
|
||||
|
||||
export function isNonZero(a: AmountJson) {
|
||||
/**
|
||||
* Check if an amount is non-zero.
|
||||
*/
|
||||
export function isNonZero(a: AmountJson): boolean {
|
||||
return a.value > 0 || a.fraction > 0;
|
||||
}
|
||||
|
||||
@ -759,7 +1234,13 @@ export namespace Amounts {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listener for notifications from the wallet.
|
||||
*/
|
||||
export interface Notifier {
|
||||
/**
|
||||
* Called when a new notification arrives.
|
||||
*/
|
||||
notify(): void;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ test("coin selection 1", (t) => {
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
|
||||
];
|
||||
|
||||
const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.1"));
|
||||
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.1"));
|
||||
if (!res) {
|
||||
t.fail();
|
||||
return;
|
||||
@ -86,7 +86,7 @@ test("coin selection 2", (t) => {
|
||||
// Merchant covers the fee, this one shouldn't be used
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
|
||||
];
|
||||
const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
|
||||
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
|
||||
if (!res) {
|
||||
t.fail();
|
||||
return;
|
||||
@ -103,7 +103,7 @@ test("coin selection 3", (t) => {
|
||||
// this coin should be selected instead of previous one with fee
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
|
||||
];
|
||||
const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
|
||||
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
|
||||
if (!res) {
|
||||
t.fail();
|
||||
return;
|
||||
@ -119,7 +119,7 @@ test("coin selection 4", (t) => {
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
|
||||
];
|
||||
const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
|
||||
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
|
||||
if (!res) {
|
||||
t.fail();
|
||||
return;
|
||||
@ -135,7 +135,7 @@ test("coin selection 5", (t) => {
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
|
||||
];
|
||||
const res = wallet.selectCoins(cds, a("EUR:4.0"), a("EUR:0.2"));
|
||||
const res = wallet.selectPayCoins(cds, a("EUR:4.0"), a("EUR:0.2"));
|
||||
t.true(!res);
|
||||
t.pass();
|
||||
});
|
||||
@ -146,7 +146,7 @@ test("coin selection 6", (t) => {
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
|
||||
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
|
||||
];
|
||||
const res = wallet.selectCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
|
||||
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
|
||||
t.true(!res);
|
||||
t.pass();
|
||||
});
|
||||
|
183
src/wallet.ts
183
src/wallet.ts
@ -81,7 +81,14 @@ import URI = require("urijs");
|
||||
* Named tuple of coin and denomination.
|
||||
*/
|
||||
export interface CoinWithDenom {
|
||||
/**
|
||||
* A coin. Must have the same denomination public key as the associated
|
||||
* denomination.
|
||||
*/
|
||||
coin: CoinRecord;
|
||||
/**
|
||||
* An associated denomination.
|
||||
*/
|
||||
denom: DenominationRecord;
|
||||
}
|
||||
|
||||
@ -92,6 +99,9 @@ export interface CoinWithDenom {
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class Payback {
|
||||
/**
|
||||
* The hash of the denomination public key for which the payback is offered.
|
||||
*/
|
||||
@Checkable.String
|
||||
h_denom_pub: string;
|
||||
}
|
||||
@ -102,67 +112,123 @@ export class Payback {
|
||||
*/
|
||||
@Checkable.Class({extra: true})
|
||||
export class KeysJson {
|
||||
/**
|
||||
* List of offered denominations.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(Denomination))
|
||||
denoms: Denomination[];
|
||||
|
||||
/**
|
||||
* The exchange's master public key.
|
||||
*/
|
||||
@Checkable.String
|
||||
master_public_key: string;
|
||||
|
||||
/**
|
||||
* The list of auditors (partially) auditing the exchange.
|
||||
*/
|
||||
@Checkable.Any
|
||||
auditors: any[];
|
||||
|
||||
/**
|
||||
* Timestamp when this response was issued.
|
||||
*/
|
||||
@Checkable.String
|
||||
list_issue_date: string;
|
||||
|
||||
/**
|
||||
* List of paybacks for compromised denominations.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(Payback))
|
||||
payback?: Payback[];
|
||||
|
||||
/**
|
||||
* Short-lived signing keys used to sign online
|
||||
* responses.
|
||||
*/
|
||||
@Checkable.Any
|
||||
signkeys: any;
|
||||
|
||||
@Checkable.String
|
||||
eddsa_pub: string;
|
||||
|
||||
@Checkable.String
|
||||
eddsa_sig: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => KeysJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire fees as anounced by the exchange.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
class WireFeesJson {
|
||||
/**
|
||||
* Cost of a wire transfer.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
wire_fee: AmountJson;
|
||||
|
||||
/**
|
||||
* Cost of clising a reserve.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
closing_fee: AmountJson;
|
||||
|
||||
/**
|
||||
* Signature made with the exchange's master key.
|
||||
*/
|
||||
@Checkable.String
|
||||
sig: string;
|
||||
|
||||
/**
|
||||
* Date from which the fee applies.
|
||||
*/
|
||||
@Checkable.String
|
||||
start_date: string;
|
||||
|
||||
/**
|
||||
* Data after which the fee doesn't apply anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
end_date: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => WireFeesJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about wire transfer methods supported
|
||||
* by the exchange.
|
||||
*/
|
||||
@Checkable.Class({extra: true})
|
||||
class WireDetailJson {
|
||||
/**
|
||||
* Name of the wire transfer method.
|
||||
*/
|
||||
@Checkable.String
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Fees associated with the wire transfer method.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(WireFeesJson))
|
||||
fees: WireFeesJson[];
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => WireDetailJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request to mark a reserve as confirmed.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class CreateReserveRequest {
|
||||
/**
|
||||
@ -177,10 +243,17 @@ export class CreateReserveRequest {
|
||||
@Checkable.String
|
||||
exchange: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => CreateReserveRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request to mark a reserve as confirmed.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ConfirmReserveRequest {
|
||||
/**
|
||||
@ -190,21 +263,40 @@ export class ConfirmReserveRequest {
|
||||
@Checkable.String
|
||||
reservePub: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ConfirmReserveRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Offer record, stored in the wallet's database.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class OfferRecord {
|
||||
/**
|
||||
* The contract that was offered by the merchant.
|
||||
*/
|
||||
@Checkable.Value(Contract)
|
||||
contract: Contract;
|
||||
|
||||
/**
|
||||
* Signature by the merchant over the contract details.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchant_sig: string;
|
||||
|
||||
/**
|
||||
* Hash of the contract terms.
|
||||
*/
|
||||
@Checkable.String
|
||||
H_contract: string;
|
||||
|
||||
/**
|
||||
* Time when the offer was made.
|
||||
*/
|
||||
@Checkable.Number
|
||||
offer_time: number;
|
||||
|
||||
@ -214,14 +306,41 @@ export class OfferRecord {
|
||||
@Checkable.Optional(Checkable.Number)
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => OfferRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity history record.
|
||||
*/
|
||||
export interface HistoryRecord {
|
||||
/**
|
||||
* Type of the history event.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Time when the activity was recorded.
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* Subject of the entry. Used to group multiple history records together.
|
||||
* Only the latest history record with the same subjectId will be shown.
|
||||
*/
|
||||
subjectId?: string;
|
||||
|
||||
/**
|
||||
* Details used when rendering the history record.
|
||||
*/
|
||||
detail: any;
|
||||
|
||||
/**
|
||||
* Level of detail of the history entry.
|
||||
*/
|
||||
level: HistoryLevel;
|
||||
}
|
||||
|
||||
@ -246,6 +365,11 @@ interface TransactionRecord {
|
||||
finished: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Level of detail at which a history
|
||||
* entry should be shown.
|
||||
*/
|
||||
export enum HistoryLevel {
|
||||
Trace = 1,
|
||||
Developer = 2,
|
||||
@ -254,19 +378,34 @@ export enum HistoryLevel {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Badge that shows activity for the wallet.
|
||||
*/
|
||||
export interface Badge {
|
||||
setText(s: string): void;
|
||||
setColor(c: string): void;
|
||||
/**
|
||||
* Start indicating background activity.
|
||||
*/
|
||||
startBusy(): void;
|
||||
|
||||
/**
|
||||
* Stop indicating background activity.
|
||||
*/
|
||||
stopBusy(): void;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Nonce record as stored in the wallet's database.
|
||||
*/
|
||||
export interface NonceRecord {
|
||||
priv: string;
|
||||
pub: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configuration key/value entries to configure
|
||||
* the wallet.
|
||||
*/
|
||||
export interface ConfigRecord {
|
||||
key: string;
|
||||
value: any;
|
||||
@ -328,10 +467,17 @@ function isWithdrawableDenom(d: DenominationRecord) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result of selecting coins, contains the exchange, and selected
|
||||
* coins with their denomination.
|
||||
*/
|
||||
export type CoinSelectionResult = {exchangeUrl: string, cds: CoinWithDenom[]}|undefined;
|
||||
|
||||
export function selectCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
|
||||
depositFeeLimit: AmountJson): CoinWithDenom[]|undefined {
|
||||
/**
|
||||
* Select coins for a payment under the merchant's constraints.
|
||||
*/
|
||||
export function selectPayCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
|
||||
depositFeeLimit: AmountJson): CoinWithDenom[]|undefined {
|
||||
if (cds.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
@ -406,7 +552,11 @@ function getWithdrawDenomList(amountAvailable: AmountJson,
|
||||
return ds;
|
||||
}
|
||||
|
||||
/* tslint:disable:completed-docs */
|
||||
|
||||
/**
|
||||
* The stores and indices for the wallet database.
|
||||
*/
|
||||
export namespace Stores {
|
||||
class ExchangeStore extends Store<ExchangeRecord> {
|
||||
constructor() {
|
||||
@ -489,6 +639,7 @@ export namespace Stores {
|
||||
super("exchangeWireFees", {keyPath: "exchangeBaseUrl"});
|
||||
}
|
||||
}
|
||||
|
||||
export const exchanges = new ExchangeStore();
|
||||
export const exchangeWireFees = new ExchangeWireFeesStore();
|
||||
export const nonces = new NonceStore();
|
||||
@ -504,6 +655,8 @@ export namespace Stores {
|
||||
export const config = new ConfigStore();
|
||||
}
|
||||
|
||||
/* tslint:enable:completed-docs */
|
||||
|
||||
|
||||
interface CoinsForPaymentArgs {
|
||||
allowedAuditors: Auditor[];
|
||||
@ -517,13 +670,15 @@ interface CoinsForPaymentArgs {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The platform-independent wallet implementation.
|
||||
*/
|
||||
export class Wallet {
|
||||
private db: IDBDatabase;
|
||||
private http: HttpRequestLibrary;
|
||||
private badge: Badge;
|
||||
private notifier: Notifier;
|
||||
public cryptoApi: CryptoApi;
|
||||
|
||||
private cryptoApi: CryptoApi;
|
||||
private processPreCoinConcurrent = 0;
|
||||
private processPreCoinThrottle: {[url: string]: number} = {};
|
||||
|
||||
@ -748,7 +903,7 @@ export class Wallet {
|
||||
}
|
||||
}
|
||||
|
||||
const res = selectCoins(cds, remainingAmount, depositFeeLimit);
|
||||
const res = selectPayCoins(cds, remainingAmount, depositFeeLimit);
|
||||
if (res) {
|
||||
return {
|
||||
cds: res,
|
||||
|
70
src/wxApi.ts
70
src/wxApi.ts
@ -14,6 +14,14 @@
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface to the wallet through WebExtension messaging.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import {
|
||||
AmountJson,
|
||||
CoinRecord,
|
||||
@ -25,12 +33,11 @@ import {
|
||||
ReserveRecord,
|
||||
} from "./types";
|
||||
|
||||
|
||||
/**
|
||||
* Interface to the wallet through WebExtension messaging.
|
||||
* @author Florian Dold
|
||||
* Query the wallet for the coins that would be used to withdraw
|
||||
* from a given reserve.
|
||||
*/
|
||||
|
||||
|
||||
export function getReserveCreationInfo(baseUrl: string,
|
||||
amount: AmountJson): Promise<ReserveCreationInfo> {
|
||||
const m = { type: "reserve-creation-info", detail: { baseUrl, amount } };
|
||||
@ -48,7 +55,8 @@ export function getReserveCreationInfo(baseUrl: string,
|
||||
});
|
||||
}
|
||||
|
||||
export async function callBackend(type: string, detail?: any): Promise<any> {
|
||||
|
||||
async function callBackend(type: string, detail?: any): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
chrome.runtime.sendMessage({ type, detail }, (resp) => {
|
||||
if (resp && resp.error) {
|
||||
@ -60,55 +68,107 @@ export async function callBackend(type: string, detail?: any): Promise<any> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all exchanges the wallet knows about.
|
||||
*/
|
||||
export async function getExchanges(): Promise<ExchangeRecord[]> {
|
||||
return await callBackend("get-exchanges");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all currencies the exchange knows about.
|
||||
*/
|
||||
export async function getCurrencies(): Promise<CurrencyRecord[]> {
|
||||
return await callBackend("get-currencies");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information about a specific currency.
|
||||
*/
|
||||
export async function getCurrency(name: string): Promise<CurrencyRecord|null> {
|
||||
return await callBackend("currency-info", {name});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information about a specific exchange.
|
||||
*/
|
||||
export async function getExchangeInfo(baseUrl: string): Promise<ExchangeRecord> {
|
||||
return await callBackend("exchange-info", {baseUrl});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace an existing currency record with the one given. The currency to
|
||||
* replace is specified inside the currency record.
|
||||
*/
|
||||
export async function updateCurrency(currencyRecord: CurrencyRecord): Promise<void> {
|
||||
return await callBackend("update-currency", { currencyRecord });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all reserves the wallet has at an exchange.
|
||||
*/
|
||||
export async function getReserves(exchangeBaseUrl: string): Promise<ReserveRecord[]> {
|
||||
return await callBackend("get-reserves", { exchangeBaseUrl });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all reserves for which a payback is available.
|
||||
*/
|
||||
export async function getPaybackReserves(): Promise<ReserveRecord[]> {
|
||||
return await callBackend("get-payback-reserves");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Withdraw the payback that is available for a reserve.
|
||||
*/
|
||||
export async function withdrawPaybackReserve(reservePub: string): Promise<ReserveRecord[]> {
|
||||
return await callBackend("withdraw-payback-reserve", { reservePub });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all coins withdrawn from the given exchange.
|
||||
*/
|
||||
export async function getCoins(exchangeBaseUrl: string): Promise<CoinRecord[]> {
|
||||
return await callBackend("get-coins", { exchangeBaseUrl });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all precoins withdrawn from the given exchange.
|
||||
*/
|
||||
export async function getPreCoins(exchangeBaseUrl: string): Promise<PreCoinRecord[]> {
|
||||
return await callBackend("get-precoins", { exchangeBaseUrl });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all denoms offered by the given exchange.
|
||||
*/
|
||||
export async function getDenoms(exchangeBaseUrl: string): Promise<DenominationRecord[]> {
|
||||
return await callBackend("get-denoms", { exchangeBaseUrl });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start refreshing a coin.
|
||||
*/
|
||||
export async function refresh(coinPub: string): Promise<void> {
|
||||
return await callBackend("refresh-coin", { coinPub });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request payback for a coin. Only works for non-refreshed coins.
|
||||
*/
|
||||
export async function payback(coinPub: string): Promise<void> {
|
||||
return await callBackend("payback-coin", { coinPub });
|
||||
}
|
||||
|
@ -340,8 +340,9 @@ async function dispatch(handlers: any, req: any, sender: any, sendResponse: any)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ChromeNotifier implements Notifier {
|
||||
ports: Port[] = [];
|
||||
private ports: Port[] = [];
|
||||
|
||||
constructor() {
|
||||
chrome.runtime.onConnect.addListener((port) => {
|
||||
@ -483,6 +484,11 @@ function clearRateLimitCache() {
|
||||
rateLimitCache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to run for the WebExtension backend.
|
||||
*
|
||||
* Sets up all event handlers and other machinery.
|
||||
*/
|
||||
export async function wxMain() {
|
||||
window.onerror = (m, source, lineno, colno, error) => {
|
||||
logging.record("error", m + error, undefined, source || "(unknown)", lineno || 0, colno || 0);
|
||||
|
30
tslint.json
30
tslint.json
@ -27,7 +27,35 @@
|
||||
"array-type": [true, "array-simple"],
|
||||
"class-name": false,
|
||||
"no-bitwise": false,
|
||||
"file-header": [true, "GNU General Public License"]
|
||||
"file-header": [true, "GNU General Public License"],
|
||||
"completed-docs": [true, {
|
||||
"methods": {
|
||||
"privacies": ["public"],
|
||||
"locations": "all"
|
||||
},
|
||||
"properties": {
|
||||
"privacies": ["public"],
|
||||
"locations": ["all"]
|
||||
},
|
||||
"functions": {
|
||||
"visibilities": ["exported"]
|
||||
},
|
||||
"interfaces": {
|
||||
"visibilities": ["exported"]
|
||||
},
|
||||
"types": {
|
||||
"visibilities": ["exported"]
|
||||
},
|
||||
"enums": {
|
||||
"visibilities": ["exported"]
|
||||
},
|
||||
"classes": {
|
||||
"visibilities": ["exported"]
|
||||
},
|
||||
"namespaces": {
|
||||
"visibilities": ["exported"]
|
||||
}
|
||||
}]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user