@agoric/internal 0.2.2-pismo-dev-50dc068.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/package.json +15 -7
- package/src/action-types.d.ts +16 -0
- package/src/action-types.d.ts.map +1 -0
- package/src/action-types.js +17 -0
- package/src/batched-deliver.d.ts +15 -0
- package/src/batched-deliver.d.ts.map +1 -0
- package/src/batched-deliver.js +50 -0
- package/src/callback.d.ts +23 -0
- package/src/callback.d.ts.map +1 -0
- package/src/callback.js +322 -0
- package/src/chain-storage-paths.d.ts +16 -0
- package/src/chain-storage-paths.d.ts.map +1 -0
- package/src/chain-storage-paths.js +17 -0
- package/src/config.d.ts +25 -0
- package/src/config.d.ts.map +1 -0
- package/src/config.js +20 -3
- package/src/debug.d.ts +2 -0
- package/src/debug.d.ts.map +1 -0
- package/src/debug.js +41 -0
- package/src/index.d.ts +6 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +7 -2
- package/src/lib-chainStorage.d.ts +179 -0
- package/src/lib-chainStorage.d.ts.map +1 -0
- package/src/lib-chainStorage.js +304 -0
- package/src/magic-cookie-test-only.d.ts +2 -0
- package/src/magic-cookie-test-only.d.ts.map +1 -0
- package/src/magic-cookie-test-only.js +11 -0
- package/src/method-tools.d.ts +3 -0
- package/src/method-tools.d.ts.map +1 -0
- package/src/method-tools.js +110 -0
- package/src/node/buffer-line-transform.d.ts +41 -0
- package/src/node/buffer-line-transform.d.ts.map +1 -0
- package/src/node/buffer-line-transform.js +119 -0
- package/src/node/createBundles.d.ts +4 -0
- package/src/node/createBundles.d.ts.map +1 -0
- package/src/node/createBundles.js +80 -0
- package/src/node/fs-stream.d.ts +8 -0
- package/src/node/fs-stream.d.ts.map +1 -0
- package/src/node/fs-stream.js +105 -0
- package/src/node/shutdown.d.ts +6 -0
- package/src/node/shutdown.d.ts.map +1 -0
- package/src/node/shutdown.js +81 -0
- package/src/priority-senders.d.ts +31 -0
- package/src/priority-senders.d.ts.map +1 -0
- package/src/priority-senders.js +104 -0
- package/src/queue.d.ts +2 -0
- package/src/queue.d.ts.map +1 -0
- package/src/queue.js +58 -0
- package/src/scratch.d.ts +19 -0
- package/src/scratch.d.ts.map +1 -0
- package/src/scratch.js +52 -0
- package/src/storage-test-utils.d.ts +99 -0
- package/src/storage-test-utils.d.ts.map +1 -0
- package/src/storage-test-utils.js +228 -0
- package/src/testing-utils.d.ts +2 -0
- package/src/testing-utils.d.ts.map +1 -0
- package/src/testing-utils.js +14 -0
- package/src/typeGuards.d.ts +2 -0
- package/src/typeGuards.d.ts.map +1 -0
- package/src/typeGuards.js +5 -0
- package/src/types.d.ts +20 -0
- package/src/upgrade-api.d.ts +8 -0
- package/src/upgrade-api.d.ts.map +1 -0
- package/src/upgrade-api.js +41 -0
- package/src/utils.d.ts +67 -0
- package/src/utils.d.ts.map +1 -0
- package/src/utils.js +232 -124
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { E, Far } from '@endo/far';
|
|
2
|
+
|
|
3
|
+
const { Fail, quote: q } = assert;
|
|
4
|
+
|
|
5
|
+
const PRIORITY_SENDERS_NAMESPACE_RE = /^[a-zA-Z0-9_-]{1,50}$/;
|
|
6
|
+
|
|
7
|
+
/** @type {(namespace: string) => string} */
|
|
8
|
+
export const normalizeSenderNamespace = namespace => {
|
|
9
|
+
const candidate = namespace.replace(/[ ,()]/g, '_');
|
|
10
|
+
PRIORITY_SENDERS_NAMESPACE_RE.test(candidate) ||
|
|
11
|
+
Fail`invalid namespace ${q(namespace)}`;
|
|
12
|
+
return candidate;
|
|
13
|
+
};
|
|
14
|
+
harden(normalizeSenderNamespace);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* XXX lets holder manage sender list for all namespaces
|
|
18
|
+
*
|
|
19
|
+
* @param {ERef<import('./lib-chainStorage.js').StorageNode>} sendersNode
|
|
20
|
+
*/
|
|
21
|
+
export const makePrioritySendersManager = sendersNode => {
|
|
22
|
+
/**
|
|
23
|
+
* address to tuple with storage node and set of namespaces that requested priority
|
|
24
|
+
*
|
|
25
|
+
* @type {Map<string, readonly [node: StorageNode, namespaces: Set<string>]>}
|
|
26
|
+
*/
|
|
27
|
+
const addressRecords = new Map();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Write a list of namespaces into a storage node.
|
|
31
|
+
*
|
|
32
|
+
* @param {import('./lib-chainStorage.js').StorageNode} node
|
|
33
|
+
* @param {Set<string>} namespaces
|
|
34
|
+
*/
|
|
35
|
+
const refreshVstorage = (node, namespaces) => {
|
|
36
|
+
return E(node).setValue(
|
|
37
|
+
// if the list set is empty, the string will be '' and thus deleted from IAVL
|
|
38
|
+
[...namespaces.keys()].sort().join(','),
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const provideRecordForAddress = async address => {
|
|
43
|
+
const extant = addressRecords.get(address);
|
|
44
|
+
if (extant) {
|
|
45
|
+
return extant;
|
|
46
|
+
}
|
|
47
|
+
const node = await E(sendersNode).makeChildNode(address, {
|
|
48
|
+
sequence: false,
|
|
49
|
+
});
|
|
50
|
+
/** @type {readonly [ node: StorageNode, namespaces: Set<string> ]} */
|
|
51
|
+
const r = [node, new Set()];
|
|
52
|
+
addressRecords.set(address, r);
|
|
53
|
+
return r;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return Far('prioritySenders manager', {
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} rawNamespace
|
|
59
|
+
* @param {string} address
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
add: async (rawNamespace, address) => {
|
|
63
|
+
const namespace = normalizeSenderNamespace(rawNamespace);
|
|
64
|
+
|
|
65
|
+
const record = await provideRecordForAddress(address);
|
|
66
|
+
|
|
67
|
+
const [node, namespaces] = record;
|
|
68
|
+
if (namespaces.has(namespace)) {
|
|
69
|
+
throw Fail`namespace ${q(namespace)} already has address ${q(address)}`;
|
|
70
|
+
}
|
|
71
|
+
namespaces.add(namespace);
|
|
72
|
+
|
|
73
|
+
return refreshVstorage(node, namespaces);
|
|
74
|
+
},
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} rawNamespace
|
|
77
|
+
* @param {string} address
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
remove: (rawNamespace, address) => {
|
|
81
|
+
const namespace = normalizeSenderNamespace(rawNamespace);
|
|
82
|
+
const record = addressRecords.get(address);
|
|
83
|
+
if (!record) {
|
|
84
|
+
throw Fail`address not registered: ${q(address)}`;
|
|
85
|
+
}
|
|
86
|
+
const [node, namespaces] = record;
|
|
87
|
+
if (!namespaces.has(namespace)) {
|
|
88
|
+
throw Fail`namespace ${q(namespace)} does not have address ${q(
|
|
89
|
+
address,
|
|
90
|
+
)}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
namespaces.delete(namespace);
|
|
94
|
+
if (namespaces.size === 0) {
|
|
95
|
+
addressRecords.delete(address);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return refreshVstorage(node, namespaces);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
harden(makePrioritySendersManager);
|
|
103
|
+
|
|
104
|
+
/** @typedef {ReturnType<typeof makePrioritySendersManager>} PrioritySendersManager */
|
package/src/queue.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["queue.js"],"names":[],"mappings":"AAQO,uHAiDN"}
|
package/src/queue.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @jessie-check
|
|
2
|
+
|
|
3
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return a function that can wrap an async or sync method, but
|
|
7
|
+
* ensures only one of them (in order) is running at a time.
|
|
8
|
+
*/
|
|
9
|
+
export const makeWithQueue = () => {
|
|
10
|
+
const queue = [];
|
|
11
|
+
|
|
12
|
+
// Execute the thunk at the front of the queue.
|
|
13
|
+
const dequeue = () => {
|
|
14
|
+
if (!queue.length) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const [thunk, resolve, reject] = queue[0];
|
|
18
|
+
// Run the thunk in a new turn.
|
|
19
|
+
Promise.resolve()
|
|
20
|
+
.then(thunk)
|
|
21
|
+
// Resolve or reject our caller with the thunk's value.
|
|
22
|
+
.then(resolve, reject)
|
|
23
|
+
// Rerun dequeue() after settling.
|
|
24
|
+
.finally(() => {
|
|
25
|
+
queue.shift();
|
|
26
|
+
if (queue.length) {
|
|
27
|
+
dequeue();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @template {any[]} T
|
|
34
|
+
* @template R
|
|
35
|
+
* @param {(...args: T) => Promise<R>} inner
|
|
36
|
+
*/
|
|
37
|
+
return function withQueue(inner) {
|
|
38
|
+
/**
|
|
39
|
+
* @param {T} args
|
|
40
|
+
* @returns {Promise<R>}
|
|
41
|
+
*/
|
|
42
|
+
return function queueCall(...args) {
|
|
43
|
+
// Curry the arguments into the inner function, and
|
|
44
|
+
// resolve/reject with whatever the inner function does.
|
|
45
|
+
const thunk = _ => inner(...args);
|
|
46
|
+
const pr = makePromiseKit();
|
|
47
|
+
queue.push([thunk, pr.resolve, pr.reject]);
|
|
48
|
+
|
|
49
|
+
if (queue.length === 1) {
|
|
50
|
+
// Start running immediately.
|
|
51
|
+
dequeue();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Allow the caller to retrieve our thunk's results.
|
|
55
|
+
return pr.promise;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
};
|
package/src/scratch.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default function makeScratchPad(): {
|
|
2
|
+
delete: (keyP: any) => Promise<void>;
|
|
3
|
+
get: (keyP: any) => Promise<any>;
|
|
4
|
+
lookup: (...path: any[]) => any;
|
|
5
|
+
init: (keyP: any, objP: any) => Promise<any>;
|
|
6
|
+
keys: () => Promise<any[]>;
|
|
7
|
+
list: () => Promise<any[]>;
|
|
8
|
+
set: (keyP: any, objP: any) => Promise<any>;
|
|
9
|
+
} & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
10
|
+
delete: (keyP: any) => Promise<void>;
|
|
11
|
+
get: (keyP: any) => Promise<any>;
|
|
12
|
+
lookup: (...path: any[]) => any;
|
|
13
|
+
init: (keyP: any, objP: any) => Promise<any>;
|
|
14
|
+
keys: () => Promise<any[]>;
|
|
15
|
+
list: () => Promise<any[]>;
|
|
16
|
+
set: (keyP: any, objP: any) => Promise<any>;
|
|
17
|
+
}>;
|
|
18
|
+
export type ScratchPad = ReturnType<typeof makeScratchPad>;
|
|
19
|
+
//# sourceMappingURL=scratch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scratch.d.ts","sourceRoot":"","sources":["scratch.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgDC;yBACa,WAAW,qBAAqB,CAAC"}
|
package/src/scratch.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { E, Far } from '@endo/far';
|
|
2
|
+
|
|
3
|
+
export default function makeScratchPad() {
|
|
4
|
+
const map = new Map();
|
|
5
|
+
|
|
6
|
+
const keys = async () => {
|
|
7
|
+
const keyList = [...map.keys()];
|
|
8
|
+
return harden(keyList.sort());
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const scratch = Far('scratchPad', {
|
|
12
|
+
delete: async keyP => {
|
|
13
|
+
const key = await keyP;
|
|
14
|
+
map.delete(key);
|
|
15
|
+
},
|
|
16
|
+
get: async keyP => {
|
|
17
|
+
const key = await keyP;
|
|
18
|
+
return map.get(key);
|
|
19
|
+
},
|
|
20
|
+
lookup: (...path) => {
|
|
21
|
+
if (path.length === 0) {
|
|
22
|
+
return scratch;
|
|
23
|
+
}
|
|
24
|
+
const [first, ...rest] = path;
|
|
25
|
+
const firstValue = E(scratch).get(first);
|
|
26
|
+
if (rest.length === 0) {
|
|
27
|
+
return firstValue;
|
|
28
|
+
}
|
|
29
|
+
return E(firstValue).lookup(...rest);
|
|
30
|
+
},
|
|
31
|
+
// Initialize a key only if it doesn't already exist. Needed for atomicity
|
|
32
|
+
// between multiple invocations.
|
|
33
|
+
init: async (keyP, objP) => {
|
|
34
|
+
const [key, obj] = await Promise.all([keyP, objP]);
|
|
35
|
+
if (map.has(key)) {
|
|
36
|
+
throw Error(`Scratchpad already has key ${key}`);
|
|
37
|
+
}
|
|
38
|
+
map.set(key, obj);
|
|
39
|
+
return key;
|
|
40
|
+
},
|
|
41
|
+
keys,
|
|
42
|
+
// Legacy alias for `keys`.
|
|
43
|
+
list: keys,
|
|
44
|
+
set: async (keyP, objP) => {
|
|
45
|
+
const [key, obj] = await Promise.all([keyP, objP]);
|
|
46
|
+
map.set(key, obj);
|
|
47
|
+
return key;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
return scratch;
|
|
51
|
+
}
|
|
52
|
+
/** @typedef {ReturnType<typeof makeScratchPad>} ScratchPad */
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export function slotToRemotable(_slotId: string, iface?: string): import("@endo/eventual-send").RemotableBrand<{}, {}>;
|
|
2
|
+
/**
|
|
3
|
+
* A basic marshaller whose unserializer produces Remotables. It can
|
|
4
|
+
* only serialize plain data, not Remotables.
|
|
5
|
+
*/
|
|
6
|
+
export const defaultMarshaller: {
|
|
7
|
+
toCapData: import("@endo/marshal/src/marshal.js").ToCapData<string>;
|
|
8
|
+
fromCapData: import("@endo/marshal/src/marshal.js").FromCapData<string>;
|
|
9
|
+
serialize: import("@endo/marshal/src/marshal.js").ToCapData<string>;
|
|
10
|
+
unserialize: import("@endo/marshal/src/marshal.js").FromCapData<string>;
|
|
11
|
+
};
|
|
12
|
+
export function slotStringUnserialize(capData: any): any;
|
|
13
|
+
export function makeFakeStorageKit(rootPath: string, rootOptions?: [handleStorageMessage: (message: import("./lib-chainStorage.js").StorageMessage) => any, rootPath: string, rootOptions?: {
|
|
14
|
+
sequence?: boolean | undefined;
|
|
15
|
+
} | undefined][2]): {
|
|
16
|
+
rootNode: {
|
|
17
|
+
getPath(): string;
|
|
18
|
+
getStoreKey(): Promise<import("./lib-chainStorage.js").VStorageKey>;
|
|
19
|
+
makeChildNode(name: string, childNodeOptions?: {
|
|
20
|
+
sequence?: boolean | undefined;
|
|
21
|
+
} | undefined): import("./lib-chainStorage.js").StorageNode;
|
|
22
|
+
setValue(value: string): Promise<void>;
|
|
23
|
+
} & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
24
|
+
getPath(): string;
|
|
25
|
+
getStoreKey(): Promise<import("./lib-chainStorage.js").VStorageKey>;
|
|
26
|
+
makeChildNode(name: string, childNodeOptions?: {
|
|
27
|
+
sequence?: boolean | undefined;
|
|
28
|
+
} | undefined): import("./lib-chainStorage.js").StorageNode;
|
|
29
|
+
setValue(value: string): Promise<void>;
|
|
30
|
+
}>;
|
|
31
|
+
data: Map<string, string>;
|
|
32
|
+
messages: import("./lib-chainStorage.js").StorageMessage[];
|
|
33
|
+
toStorage: (message: import('../src/lib-chainStorage.js').StorageMessage) => string | number | any[] | {
|
|
34
|
+
storeName: string;
|
|
35
|
+
storeSubkey: string;
|
|
36
|
+
} | null | undefined;
|
|
37
|
+
};
|
|
38
|
+
export function makeMockChainStorageRoot(): {
|
|
39
|
+
/**
|
|
40
|
+
* Defaults to deserializing slot references into plain Remotable
|
|
41
|
+
* objects having the specified interface name (as from `Far(iface)`),
|
|
42
|
+
* but can accept a different marshaller for producing Remotables
|
|
43
|
+
* that e.g. embed the slot string in their iface name.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} path
|
|
46
|
+
* @param {import('./lib-chainStorage.js').Marshaller} marshaller
|
|
47
|
+
* @returns {unknown}
|
|
48
|
+
*/
|
|
49
|
+
getBody: (path: string, marshaller?: import('./lib-chainStorage.js').Marshaller) => unknown;
|
|
50
|
+
keys: () => string[];
|
|
51
|
+
getPath(): string;
|
|
52
|
+
getStoreKey(): Promise<import("./lib-chainStorage.js").VStorageKey>;
|
|
53
|
+
makeChildNode(name: string, childNodeOptions?: {
|
|
54
|
+
sequence?: boolean | undefined;
|
|
55
|
+
} | undefined): import("./lib-chainStorage.js").StorageNode;
|
|
56
|
+
setValue(value: string): Promise<void>;
|
|
57
|
+
} & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
58
|
+
/**
|
|
59
|
+
* Defaults to deserializing slot references into plain Remotable
|
|
60
|
+
* objects having the specified interface name (as from `Far(iface)`),
|
|
61
|
+
* but can accept a different marshaller for producing Remotables
|
|
62
|
+
* that e.g. embed the slot string in their iface name.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} path
|
|
65
|
+
* @param {import('./lib-chainStorage.js').Marshaller} marshaller
|
|
66
|
+
* @returns {unknown}
|
|
67
|
+
*/
|
|
68
|
+
getBody: (path: string, marshaller?: import('./lib-chainStorage.js').Marshaller) => unknown;
|
|
69
|
+
keys: () => string[];
|
|
70
|
+
getPath(): string;
|
|
71
|
+
getStoreKey(): Promise<import("./lib-chainStorage.js").VStorageKey>;
|
|
72
|
+
makeChildNode(name: string, childNodeOptions?: {
|
|
73
|
+
sequence?: boolean | undefined;
|
|
74
|
+
} | undefined): import("./lib-chainStorage.js").StorageNode;
|
|
75
|
+
setValue(value: string): Promise<void>;
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* A map corresponding with a total function such that `get(key)`
|
|
79
|
+
* is assumed to always succeed.
|
|
80
|
+
*/
|
|
81
|
+
export type TotalMap<K, V> = {
|
|
82
|
+
[Symbol.iterator]: () => IterableIterator<[K, V]>;
|
|
83
|
+
[Symbol.toStringTag]: string;
|
|
84
|
+
entries: () => IterableIterator<[K, V]>;
|
|
85
|
+
keys: () => IterableIterator<K>;
|
|
86
|
+
values: () => IterableIterator<V>;
|
|
87
|
+
has: (key: K) => boolean;
|
|
88
|
+
size: number;
|
|
89
|
+
set: (key: K, value: V) => Map<K, V>;
|
|
90
|
+
clear: () => void;
|
|
91
|
+
delete: (key: K) => boolean;
|
|
92
|
+
forEach: (callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any) => void;
|
|
93
|
+
} & {
|
|
94
|
+
get: (key: K) => V;
|
|
95
|
+
};
|
|
96
|
+
export type TotalMapFrom<T> = T extends Map<infer K, infer V> ? TotalMap<K, V> : never;
|
|
97
|
+
export type FakeStorageKit = ReturnType<typeof makeFakeStorageKit>;
|
|
98
|
+
export type MockChainStorageRoot = ReturnType<typeof makeMockChainStorageRoot>;
|
|
99
|
+
//# sourceMappingURL=storage-test-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-test-utils.d.ts","sourceRoot":"","sources":["storage-test-utils.js"],"names":[],"mappings":"AA+BO,yCAHI,MAAM,UACN,MAAM,wDAGC;AAElB;;;GAGG;AACH;;;;;EAEG;AAsBW,+CAAU,GAAG,GAAK,GAAG,CAAA;AAgC5B,6CAHI,MAAM,gBACN;;eAAwC,CAAC,CAAC;;;;;;;;;;;;;;;;;;yBAyBvC,OAAO,4BAA4B,EAAE,cAAc;;;;EAkFhE;AAIM;IAIH;;;;;;;;;OASG;oBAHQ,MAAM,eACN,OAAO,uBAAuB,EAAE,UAAU,KACxC,OAAO;;;;;;;;;IARpB;;;;;;;;;OASG;oBAHQ,MAAM,eACN,OAAO,uBAAuB,EAAE,UAAU,KACxC,OAAO;;;;;;;;GAWvB;;;;;;;;;;;;;;;;;;eAjNiF,CAAC,KAAK,CAAC;;;6BAwL3E,WAAY,yBAAyB,CAAC;mCA0BtC,WAAW,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { Far } from '@endo/far';
|
|
3
|
+
import { makeMarshal, Remotable } from '@endo/marshal';
|
|
4
|
+
import {
|
|
5
|
+
isStreamCell,
|
|
6
|
+
makeChainStorageRoot,
|
|
7
|
+
unmarshalFromVstorage,
|
|
8
|
+
} from './lib-chainStorage.js';
|
|
9
|
+
import { bindAllMethods } from './method-tools.js';
|
|
10
|
+
|
|
11
|
+
const { Fail } = assert;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A map corresponding with a total function such that `get(key)`
|
|
15
|
+
* is assumed to always succeed.
|
|
16
|
+
*
|
|
17
|
+
* @template K, V
|
|
18
|
+
* @typedef {{[k in Exclude<keyof Map<K, V>, 'get'>]: Map<K, V>[k]} & {get: (key: K) => V}} TotalMap
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* @template T
|
|
22
|
+
* @typedef {T extends Map<infer K, infer V> ? TotalMap<K, V> : never} TotalMapFrom
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A convertSlotToVal function that produces basic Remotables. Assumes
|
|
27
|
+
* that all slots are Remotables (i.e. none are Promises).
|
|
28
|
+
*
|
|
29
|
+
* @param {string} _slotId
|
|
30
|
+
* @param {string} iface
|
|
31
|
+
*/
|
|
32
|
+
export const slotToRemotable = (_slotId, iface = 'Remotable') =>
|
|
33
|
+
Remotable(iface);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A basic marshaller whose unserializer produces Remotables. It can
|
|
37
|
+
* only serialize plain data, not Remotables.
|
|
38
|
+
*/
|
|
39
|
+
export const defaultMarshaller = makeMarshal(undefined, slotToRemotable, {
|
|
40
|
+
serializeBodyFormat: 'smallcaps',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A deserializer which produces slot strings instead of Remotables,
|
|
45
|
+
* so if `a = Far('iface')`, and serializing `{ a }` into `capData`
|
|
46
|
+
* assigned it slot `board123`, then `slotStringUnserialize(capData)`
|
|
47
|
+
* would produce `{ a: 'board123' }`.
|
|
48
|
+
*
|
|
49
|
+
* This may be useful for display purposes.
|
|
50
|
+
*
|
|
51
|
+
* Limitations:
|
|
52
|
+
* * it cannot handle Symbols (registered or well-known)
|
|
53
|
+
* * it can handle BigInts, but serialized data that contains a
|
|
54
|
+
* particular unusual string will be unserialized into a BigInt by
|
|
55
|
+
* mistake
|
|
56
|
+
* * it cannot handle Promises, NaN, +/- Infinity, undefined, or
|
|
57
|
+
* other non-JSONable JavaScript values
|
|
58
|
+
*/
|
|
59
|
+
const makeSlotStringUnserialize = () => {
|
|
60
|
+
/** @type { (slot: string, iface: string) => any } */
|
|
61
|
+
const identitySlotToValFn = (slot, _) => Far('unk', { toJSON: () => slot });
|
|
62
|
+
const { fromCapData } = makeMarshal(undefined, identitySlotToValFn);
|
|
63
|
+
/** @type { (capData: any) => any } */
|
|
64
|
+
const unserialize = capData =>
|
|
65
|
+
JSON.parse(
|
|
66
|
+
JSON.stringify(fromCapData(capData), (_, val) => {
|
|
67
|
+
if (typeof val === 'bigint') {
|
|
68
|
+
// JSON cannot accept BigInts. This unusual string is a
|
|
69
|
+
// cheap alternative to a proper Hilbert Hotel.
|
|
70
|
+
return `@encromulate:${val}`;
|
|
71
|
+
} else {
|
|
72
|
+
return val;
|
|
73
|
+
}
|
|
74
|
+
}),
|
|
75
|
+
(_key, val) => {
|
|
76
|
+
if (typeof val === 'string' && val.startsWith('@encromulate')) {
|
|
77
|
+
return BigInt(val.split(':')[1]);
|
|
78
|
+
} else {
|
|
79
|
+
return val;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
return harden(unserialize);
|
|
84
|
+
};
|
|
85
|
+
export const slotStringUnserialize = makeSlotStringUnserialize();
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* For testing, creates a chainStorage root node over an in-memory map
|
|
89
|
+
* and exposes both the map and the sequence of received messages.
|
|
90
|
+
* The `sequence` option defaults to true.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} rootPath
|
|
93
|
+
* @param {Parameters<typeof makeChainStorageRoot>[2]} [rootOptions]
|
|
94
|
+
*/
|
|
95
|
+
export const makeFakeStorageKit = (rootPath, rootOptions) => {
|
|
96
|
+
const resolvedOptions = { sequence: true, ...rootOptions };
|
|
97
|
+
/** @type {TotalMap<string, string>} */
|
|
98
|
+
const data = new Map();
|
|
99
|
+
/** @param {string} prefix */
|
|
100
|
+
const getChildEntries = prefix => {
|
|
101
|
+
assert(prefix.endsWith('.'));
|
|
102
|
+
const childEntries = new Map();
|
|
103
|
+
for (const [path, value] of data.entries()) {
|
|
104
|
+
if (!path.startsWith(prefix)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const [segment, ...suffix] = path.slice(prefix.length).split('.');
|
|
108
|
+
if (suffix.length === 0) {
|
|
109
|
+
childEntries.set(segment, value);
|
|
110
|
+
} else if (!childEntries.has(segment)) {
|
|
111
|
+
childEntries.set(segment, null);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return childEntries;
|
|
115
|
+
};
|
|
116
|
+
/** @type {import('../src/lib-chainStorage.js').StorageMessage[]} */
|
|
117
|
+
const messages = [];
|
|
118
|
+
/** @param {import('../src/lib-chainStorage.js').StorageMessage} message */
|
|
119
|
+
// eslint-disable-next-line consistent-return
|
|
120
|
+
const toStorage = message => {
|
|
121
|
+
messages.push(message);
|
|
122
|
+
switch (message.method) {
|
|
123
|
+
case 'getStoreKey': {
|
|
124
|
+
const [key] = message.args;
|
|
125
|
+
return { storeName: 'swingset', storeSubkey: `fake:${key}` };
|
|
126
|
+
}
|
|
127
|
+
case 'get': {
|
|
128
|
+
const [key] = message.args;
|
|
129
|
+
return data.has(key) ? data.get(key) : null;
|
|
130
|
+
}
|
|
131
|
+
case 'children': {
|
|
132
|
+
const [key] = message.args;
|
|
133
|
+
const childEntries = getChildEntries(`${key}.`);
|
|
134
|
+
return [...childEntries.keys()];
|
|
135
|
+
}
|
|
136
|
+
case 'entries': {
|
|
137
|
+
const [key] = message.args;
|
|
138
|
+
const childEntries = getChildEntries(`${key}.`);
|
|
139
|
+
return [...childEntries.entries()].map(entry =>
|
|
140
|
+
entry[1] != null ? entry : [entry[0]],
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
case 'set':
|
|
144
|
+
case 'setWithoutNotify': {
|
|
145
|
+
/** @type {import('../src/lib-chainStorage.js').StorageEntry[]} */
|
|
146
|
+
const newEntries = message.args;
|
|
147
|
+
for (const [key, value] of newEntries) {
|
|
148
|
+
if (value != null) {
|
|
149
|
+
data.set(key, value);
|
|
150
|
+
} else {
|
|
151
|
+
data.delete(key);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case 'append': {
|
|
157
|
+
/** @type {import('../src/lib-chainStorage.js').StorageEntry[]} */
|
|
158
|
+
const newEntries = message.args;
|
|
159
|
+
for (const [key, value] of newEntries) {
|
|
160
|
+
value != null || Fail`attempt to append with no value`;
|
|
161
|
+
// In the absence of block boundaries, everything goes in a single StreamCell.
|
|
162
|
+
const oldVal = data.get(key);
|
|
163
|
+
let streamCell;
|
|
164
|
+
if (oldVal != null) {
|
|
165
|
+
try {
|
|
166
|
+
streamCell = JSON.parse(oldVal);
|
|
167
|
+
assert(isStreamCell(streamCell));
|
|
168
|
+
} catch (_err) {
|
|
169
|
+
streamCell = undefined;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (streamCell === undefined) {
|
|
173
|
+
streamCell = {
|
|
174
|
+
blockHeight: '0',
|
|
175
|
+
values: oldVal != null ? [oldVal] : [],
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
streamCell.values.push(value);
|
|
179
|
+
data.set(key, JSON.stringify(streamCell));
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case 'size':
|
|
184
|
+
// Intentionally incorrect because it counts non-child descendants,
|
|
185
|
+
// but nevertheless supports a "has children" test.
|
|
186
|
+
return [...data.keys()].filter(k => k.startsWith(`${message.args[0]}.`))
|
|
187
|
+
.length;
|
|
188
|
+
default:
|
|
189
|
+
throw Error(`unsupported method: ${message.method}`);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const rootNode = makeChainStorageRoot(toStorage, rootPath, resolvedOptions);
|
|
193
|
+
return {
|
|
194
|
+
rootNode,
|
|
195
|
+
// eslint-disable-next-line object-shorthand
|
|
196
|
+
data: /** @type {Map<string, string>} */ (data),
|
|
197
|
+
messages,
|
|
198
|
+
toStorage,
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
harden(makeFakeStorageKit);
|
|
202
|
+
/** @typedef {ReturnType< typeof makeFakeStorageKit>} FakeStorageKit */
|
|
203
|
+
|
|
204
|
+
export const makeMockChainStorageRoot = () => {
|
|
205
|
+
const { rootNode, data } = makeFakeStorageKit('mockChainStorageRoot');
|
|
206
|
+
return Far('mockChainStorage', {
|
|
207
|
+
...bindAllMethods(rootNode),
|
|
208
|
+
/**
|
|
209
|
+
* Defaults to deserializing slot references into plain Remotable
|
|
210
|
+
* objects having the specified interface name (as from `Far(iface)`),
|
|
211
|
+
* but can accept a different marshaller for producing Remotables
|
|
212
|
+
* that e.g. embed the slot string in their iface name.
|
|
213
|
+
*
|
|
214
|
+
* @param {string} path
|
|
215
|
+
* @param {import('./lib-chainStorage.js').Marshaller} marshaller
|
|
216
|
+
* @returns {unknown}
|
|
217
|
+
*/
|
|
218
|
+
getBody: (path, marshaller = defaultMarshaller) => {
|
|
219
|
+
data.size || Fail`no data in storage`;
|
|
220
|
+
/** @type {ReturnType<typeof import('@endo/marshal').makeMarshal>['fromCapData']} */
|
|
221
|
+
const fromCapData = (...args) =>
|
|
222
|
+
Reflect.apply(marshaller.fromCapData, marshaller, args);
|
|
223
|
+
return unmarshalFromVstorage(data, path, fromCapData);
|
|
224
|
+
},
|
|
225
|
+
keys: () => [...data.keys()],
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
/** @typedef {ReturnType<typeof makeMockChainStorageRoot>} MockChainStorageRoot */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing-utils.d.ts","sourceRoot":"","sources":["testing-utils.js"],"names":[],"mappings":"AAWO,mDACwC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** @file note this cannot be called test-utils.js due to https://github.com/Agoric/agoric-sdk/issues/7503 */
|
|
2
|
+
/* global setImmediate */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A workaround for some issues with fake time in tests.
|
|
6
|
+
*
|
|
7
|
+
* Lines of test code can depend on async promises outside the test
|
|
8
|
+
* resolving before they run. Awaiting this function result ensures
|
|
9
|
+
* that all promises that can do resolve.
|
|
10
|
+
* Note that this doesn't mean all outstanding promises.
|
|
11
|
+
*/
|
|
12
|
+
export const eventLoopIteration = async () =>
|
|
13
|
+
new Promise(resolve => setImmediate(resolve));
|
|
14
|
+
harden(eventLoopIteration);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeGuards.d.ts","sourceRoot":"","sources":["typeGuards.js"],"names":[],"mappings":"AAIA,gEAA2D"}
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
export declare class Callback<I extends (...args: unknown[]) => any> {
|
|
3
|
+
private iface: I;
|
|
4
|
+
|
|
5
|
+
public target: any;
|
|
6
|
+
|
|
7
|
+
public methodName?: PropertyKey;
|
|
8
|
+
|
|
9
|
+
public bound: unknown[];
|
|
10
|
+
|
|
11
|
+
public isSync: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare class SyncCallback<
|
|
15
|
+
I extends (...args: unknown[]) => any,
|
|
16
|
+
> extends Callback<I> {
|
|
17
|
+
private syncIface: I;
|
|
18
|
+
|
|
19
|
+
public isSync: true;
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function makeUpgradeDisconnection(upgradeMessage: string, toIncarnationNumber: number): DisconnectionObject;
|
|
2
|
+
export function isUpgradeDisconnection(err: any): err is DisconnectionObject;
|
|
3
|
+
export type DisconnectionObject = {
|
|
4
|
+
name: string;
|
|
5
|
+
upgradeMessage: string;
|
|
6
|
+
incarnationNumber: number;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=upgrade-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade-api.d.ts","sourceRoot":"","sources":["upgrade-api.js"],"names":[],"mappings":"AAgBO,yDAJI,MAAM,uBACN,MAAM,GACJ,mBAAmB,CAO5B;AAcG,4CAHI,GAAG,8BAO6B;kCAlC9B;IAAE,MAAM,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE"}
|