@agoric/internal 0.3.3-other-dev-8f8782b.0 → 0.3.3-other-dev-fbe72e7.0.fbe72e7
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/README.md +7 -2
- package/exported.js +2 -0
- package/package.json +41 -19
- package/src/action-types.d.ts +51 -5
- package/src/action-types.d.ts.map +1 -1
- package/src/action-types.js +74 -15
- package/src/batched-deliver.d.ts +9 -6
- package/src/batched-deliver.d.ts.map +1 -1
- package/src/batched-deliver.js +9 -3
- package/src/callback.d.ts +24 -17
- package/src/callback.d.ts.map +1 -1
- package/src/callback.js +55 -61
- package/src/chain-storage-paths.d.ts +2 -3
- package/src/chain-storage-paths.d.ts.map +1 -1
- package/src/chain-storage-paths.js +2 -3
- package/src/chain-utils.d.ts +27 -0
- package/src/chain-utils.d.ts.map +1 -0
- package/src/chain-utils.js +62 -0
- package/src/cli-utils.d.ts +2 -0
- package/src/cli-utils.d.ts.map +1 -0
- package/src/cli-utils.js +21 -0
- package/src/config.d.ts +22 -12
- package/src/config.d.ts.map +1 -1
- package/src/config.js +23 -10
- package/src/debug.d.ts +1 -1
- package/src/errors.d.ts +2 -0
- package/src/errors.d.ts.map +1 -0
- package/src/errors.js +16 -0
- package/src/hex.d.ts +15 -0
- package/src/hex.d.ts.map +1 -0
- package/src/hex.js +105 -0
- package/src/index.d.ts +10 -1
- package/src/index.js +13 -2
- package/src/install-ses-debug.d.ts +2 -0
- package/src/install-ses-debug.d.ts.map +1 -0
- package/src/install-ses-debug.js +6 -0
- package/src/js-utils.d.ts +40 -0
- package/src/js-utils.d.ts.map +1 -0
- package/src/js-utils.js +237 -0
- package/src/lib-chainStorage.d.ts +59 -67
- package/src/lib-chainStorage.d.ts.map +1 -1
- package/src/lib-chainStorage.js +95 -92
- package/src/lib-nodejs/ava-unhandled-rejection.d.ts +13 -0
- package/src/lib-nodejs/ava-unhandled-rejection.d.ts.map +1 -0
- package/src/lib-nodejs/ava-unhandled-rejection.js +66 -0
- package/src/lib-nodejs/engine-gc.d.ts +3 -0
- package/src/lib-nodejs/engine-gc.d.ts.map +1 -0
- package/src/lib-nodejs/engine-gc.js +22 -0
- package/src/lib-nodejs/gc-and-finalize.d.ts +2 -0
- package/src/lib-nodejs/gc-and-finalize.d.ts.map +1 -0
- package/src/lib-nodejs/gc-and-finalize.js +91 -0
- package/src/lib-nodejs/spawnSubprocessWorker.d.ts +15 -0
- package/src/lib-nodejs/spawnSubprocessWorker.d.ts.map +1 -0
- package/src/lib-nodejs/spawnSubprocessWorker.js +89 -0
- package/src/lib-nodejs/waitUntilQuiescent.d.ts +2 -0
- package/src/lib-nodejs/waitUntilQuiescent.d.ts.map +1 -0
- package/src/lib-nodejs/waitUntilQuiescent.js +18 -0
- package/src/lib-nodejs/worker-protocol.d.ts +4 -0
- package/src/lib-nodejs/worker-protocol.d.ts.map +1 -0
- package/src/lib-nodejs/worker-protocol.js +54 -0
- package/src/magic-cookie-test-only.js +2 -2
- package/src/marshal.d.ts +33 -0
- package/src/marshal.d.ts.map +1 -0
- package/src/marshal.js +156 -0
- package/src/method-tools.d.ts +1 -0
- package/src/method-tools.d.ts.map +1 -1
- package/src/method-tools.js +33 -62
- package/src/metrics.d.ts +183 -0
- package/src/metrics.d.ts.map +1 -0
- package/src/metrics.js +476 -0
- package/src/module-utils.d.ts +2 -0
- package/src/module-utils.d.ts.map +1 -0
- package/src/module-utils.js +27 -0
- package/src/natural-sort.d.ts +2 -0
- package/src/natural-sort.d.ts.map +1 -0
- package/src/natural-sort.js +48 -0
- package/src/netstring.d.ts +24 -0
- package/src/netstring.d.ts.map +1 -0
- package/src/netstring.js +125 -0
- package/src/node/buffer-line-transform.d.ts +20 -16
- package/src/node/buffer-line-transform.d.ts.map +1 -1
- package/src/node/buffer-line-transform.js +12 -9
- package/src/node/createBundles.d.ts.map +1 -1
- package/src/node/createBundles.js +12 -3
- package/src/node/fs-stream.d.ts +1 -1
- package/src/node/fs-stream.d.ts.map +1 -1
- package/src/node/fs-stream.js +48 -37
- package/src/node/shutdown.d.ts.map +1 -1
- package/src/node/shutdown.js +0 -1
- package/src/priority-senders.d.ts +2 -1
- package/src/priority-senders.d.ts.map +1 -1
- package/src/priority-senders.js +10 -4
- package/src/queue.d.ts +1 -1
- package/src/queue.d.ts.map +1 -1
- package/src/queue.js +7 -8
- package/src/scratch.d.ts +1 -1
- package/src/scratch.d.ts.map +1 -1
- package/src/ses-utils.d.ts +68 -0
- package/src/ses-utils.d.ts.map +1 -0
- package/src/ses-utils.js +422 -0
- package/src/storage-test-utils.d.ts +49 -84
- package/src/storage-test-utils.d.ts.map +1 -1
- package/src/storage-test-utils.js +234 -116
- package/src/tagged.d.ts +152 -0
- package/src/testing-utils.d.ts +2 -0
- package/src/testing-utils.d.ts.map +1 -1
- package/src/testing-utils.js +44 -5
- package/src/tmpDir.d.ts +2 -0
- package/src/tmpDir.d.ts.map +1 -0
- package/src/tmpDir.js +17 -0
- package/src/tokens.d.ts +34 -0
- package/src/tokens.d.ts.map +1 -0
- package/src/tokens.js +35 -0
- package/src/typeCheck.d.ts +9 -0
- package/src/typeCheck.d.ts.map +1 -0
- package/src/typeCheck.js +23 -0
- package/src/typeGuards.d.ts +17 -0
- package/src/typeGuards.d.ts.map +1 -1
- package/src/typeGuards.js +20 -0
- package/src/types-index.d.ts +1 -0
- package/src/types-index.js +2 -0
- package/src/types.d.ts +83 -18
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +129 -0
- package/src/upgrade-api.d.ts +14 -4
- package/src/upgrade-api.d.ts.map +1 -1
- package/src/upgrade-api.js +50 -18
- package/CHANGELOG.md +0 -106
- package/src/utils.d.ts +0 -67
- package/src/utils.d.ts.map +0 -1
- package/src/utils.js +0 -451
package/src/lib-chainStorage.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Fail } from '@endo/errors';
|
|
4
|
+
import { E, Far } from '@endo/far';
|
|
5
|
+
import { M } from '@endo/patterns';
|
|
6
|
+
import { makeHeapZone } from '@agoric/base-zone/heap.js';
|
|
5
7
|
import * as cb from './callback.js';
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @import {ERef} from '@endo/far';
|
|
11
|
+
* @import {PassableCap} from '@endo/marshal';
|
|
12
|
+
* @import {TypedPattern} from './types.js';
|
|
13
|
+
*/
|
|
8
14
|
|
|
9
15
|
/** @typedef {ReturnType<typeof import('@endo/marshal').makeMarshal>} Marshaller */
|
|
10
16
|
/** @typedef {Pick<Marshaller, 'fromCapData'>} Unserializer */
|
|
@@ -26,10 +32,17 @@ const { Fail } = assert;
|
|
|
26
32
|
* @property {T[]} values
|
|
27
33
|
*/
|
|
28
34
|
|
|
35
|
+
/** @type {TypedPattern<StreamCell>} */
|
|
36
|
+
export const StreamCellShape = harden({
|
|
37
|
+
blockHeight: M.string(),
|
|
38
|
+
values: M.array(),
|
|
39
|
+
});
|
|
40
|
+
|
|
29
41
|
/**
|
|
30
42
|
* This represents a node in an IAVL tree.
|
|
31
43
|
*
|
|
32
|
-
* The active implementation is x/vstorage, an Agoric extension of the Cosmos
|
|
44
|
+
* The active implementation is x/vstorage, an Agoric extension of the Cosmos
|
|
45
|
+
* SDK.
|
|
33
46
|
*
|
|
34
47
|
* Vstorage is a hierarchical externally-reachable storage structure that
|
|
35
48
|
* identifies children by restricted ASCII name and is associated with arbitrary
|
|
@@ -37,9 +50,13 @@ const { Fail } = assert;
|
|
|
37
50
|
*
|
|
38
51
|
* @typedef {object} StorageNode
|
|
39
52
|
* @property {(data: string) => Promise<void>} setValue publishes some data
|
|
40
|
-
* @property {() => string} getPath the chain storage path at which the node was
|
|
53
|
+
* @property {() => string} getPath the chain storage path at which the node was
|
|
54
|
+
* constructed
|
|
41
55
|
* @property {() => Promise<VStorageKey>} getStoreKey DEPRECATED use getPath
|
|
42
|
-
* @property {(
|
|
56
|
+
* @property {(
|
|
57
|
+
* subPath: string,
|
|
58
|
+
* options?: { sequence?: boolean },
|
|
59
|
+
* ) => StorageNode} makeChildNode
|
|
43
60
|
*/
|
|
44
61
|
|
|
45
62
|
const ChainStorageNodeI = M.interface('StorageNode', {
|
|
@@ -53,9 +70,8 @@ const ChainStorageNodeI = M.interface('StorageNode', {
|
|
|
53
70
|
|
|
54
71
|
/**
|
|
55
72
|
* This is an imperfect heuristic to navigate the migration from value cells to
|
|
56
|
-
* stream cells.
|
|
57
|
-
*
|
|
58
|
-
* and we do not intend to create any more legacy value cells.
|
|
73
|
+
* stream cells. At time of writing, no legacy cells have the same shape as a
|
|
74
|
+
* stream cell, and we do not intend to create any more legacy value cells.
|
|
59
75
|
*
|
|
60
76
|
* @param {any} cell
|
|
61
77
|
* @returns {cell is StreamCell}
|
|
@@ -68,64 +84,20 @@ export const isStreamCell = cell =>
|
|
|
68
84
|
/^0$|^[1-9][0-9]*$/.test(cell.blockHeight);
|
|
69
85
|
harden(isStreamCell);
|
|
70
86
|
|
|
71
|
-
// TODO: Consolidate with `insistCapData` functions from swingset-liveslots,
|
|
72
|
-
// swingset-xsnap-supervisor, etc.
|
|
73
|
-
/**
|
|
74
|
-
* @param {unknown} data
|
|
75
|
-
* @returns {asserts data is import('@endo/marshal').CapData<string>}
|
|
76
|
-
*/
|
|
77
|
-
export const assertCapData = data => {
|
|
78
|
-
assert.typeof(data, 'object');
|
|
79
|
-
assert(data);
|
|
80
|
-
assert.typeof(data.body, 'string');
|
|
81
|
-
assert(Array.isArray(data.slots));
|
|
82
|
-
// XXX check that the .slots array elements are actually strings
|
|
83
|
-
};
|
|
84
|
-
harden(assertCapData);
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Read and unmarshal a value from a map representation of vstorage data
|
|
88
|
-
*
|
|
89
|
-
* @param {Map<string, string>} data
|
|
90
|
-
* @param {string} key
|
|
91
|
-
* @param {ReturnType<typeof import('@endo/marshal').makeMarshal>['fromCapData']} fromCapData
|
|
92
|
-
* @param {number} [index=-1] index of the desired value in a deserialized stream cell
|
|
93
|
-
*/
|
|
94
|
-
export const unmarshalFromVstorage = (data, key, fromCapData, index = -1) => {
|
|
95
|
-
const serialized = data.get(key) || Fail`no data for ${key}`;
|
|
96
|
-
assert.typeof(serialized, 'string');
|
|
97
|
-
|
|
98
|
-
const streamCell = JSON.parse(serialized);
|
|
99
|
-
if (!isStreamCell(streamCell)) {
|
|
100
|
-
throw Fail`not a StreamCell: ${streamCell}`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const { values } = streamCell;
|
|
104
|
-
values.length > 0 || Fail`no StreamCell values: ${streamCell}`;
|
|
105
|
-
|
|
106
|
-
const marshalled = values.at(index);
|
|
107
|
-
assert.typeof(marshalled, 'string');
|
|
108
|
-
|
|
109
|
-
/** @type {import("@endo/marshal").CapData<string>} */
|
|
110
|
-
const capData = harden(JSON.parse(marshalled));
|
|
111
|
-
assertCapData(capData);
|
|
112
|
-
|
|
113
|
-
const unmarshalled = fromCapData(capData);
|
|
114
|
-
return unmarshalled;
|
|
115
|
-
};
|
|
116
|
-
harden(unmarshalFromVstorage);
|
|
117
|
-
|
|
118
87
|
/**
|
|
119
88
|
* @typedef {object} StoredFacet
|
|
120
|
-
* @property {() => Promise<string>} getPath the chain storage path at which the
|
|
89
|
+
* @property {() => Promise<string>} getPath the chain storage path at which the
|
|
90
|
+
* node was constructed
|
|
121
91
|
* @property {StorageNode['getStoreKey']} getStoreKey DEPRECATED use getPath
|
|
122
|
-
* @property {() => Unserializer} getUnserializer get the unserializer for the
|
|
92
|
+
* @property {() => Unserializer} getUnserializer get the unserializer for the
|
|
93
|
+
* stored data
|
|
123
94
|
*/
|
|
124
95
|
|
|
125
96
|
// TODO: Formalize segment constraints.
|
|
126
97
|
// Must be nonempty and disallow (unescaped) `.`, and for simplicity
|
|
127
98
|
// (and future possibility of e.g. escaping) we currently limit to
|
|
128
99
|
// ASCII alphanumeric plus underscore and dash.
|
|
100
|
+
// Should remain consistent with golang/cosmos/x/vstorage/types/path_keys.go
|
|
129
101
|
const pathSegmentPattern = /^[a-zA-Z0-9_-]{1,100}$/;
|
|
130
102
|
|
|
131
103
|
/** @type {(name: string) => void} */
|
|
@@ -138,44 +110,65 @@ harden(assertPathSegment);
|
|
|
138
110
|
/**
|
|
139
111
|
* Must match the switch in vstorage.go using `vstorageMessage` type
|
|
140
112
|
*
|
|
141
|
-
* @typedef {
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
113
|
+
* @typedef {'get'
|
|
114
|
+
* | 'getStoreKey'
|
|
115
|
+
* | 'has'
|
|
116
|
+
* | 'children'
|
|
117
|
+
* | 'entries'
|
|
118
|
+
* | 'values'
|
|
119
|
+
* | 'size'} StorageGetByPathMessageMethod
|
|
120
|
+
*
|
|
121
|
+
*
|
|
122
|
+
* @typedef {'set' | 'setWithoutNotify' | 'append'} StorageUpdateEntriesMessageMethod
|
|
123
|
+
*
|
|
124
|
+
*
|
|
125
|
+
* @typedef {StorageGetByPathMessageMethod
|
|
126
|
+
* | StorageUpdateEntriesMessageMethod} StorageMessageMethod
|
|
127
|
+
*
|
|
128
|
+
*
|
|
129
|
+
* @typedef {[path: string]} StorageGetByPathMessageArgs
|
|
130
|
+
*
|
|
131
|
+
* @typedef {[path: string, value?: string | null]} StorageEntry
|
|
132
|
+
*
|
|
133
|
+
* @typedef {StorageEntry[]} StorageUpdateEntriesMessageArgs
|
|
134
|
+
*
|
|
147
135
|
* @typedef {{
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
136
|
+
* method: StorageGetByPathMessageMethod;
|
|
137
|
+
* args: StorageGetByPathMessageArgs;
|
|
138
|
+
* }
|
|
139
|
+
* | {
|
|
140
|
+
* method: StorageUpdateEntriesMessageMethod;
|
|
141
|
+
* args: StorageUpdateEntriesMessageArgs;
|
|
142
|
+
* }} StorageMessage
|
|
154
143
|
*/
|
|
155
144
|
|
|
156
145
|
/**
|
|
157
|
-
* @param {import('@agoric/zone').Zone} zone
|
|
146
|
+
* @param {import('@agoric/base-zone').Zone} zone
|
|
158
147
|
*/
|
|
159
148
|
export const prepareChainStorageNode = zone => {
|
|
160
149
|
/**
|
|
161
150
|
* Create a storage node for a given backing storage interface and path.
|
|
162
151
|
*
|
|
163
|
-
* @param {import('./
|
|
164
|
-
*
|
|
165
|
-
*
|
|
152
|
+
* @param {import('./types.js').Callback<
|
|
153
|
+
* (message: StorageMessage) => any
|
|
154
|
+
* >} messenger
|
|
155
|
+
* a callback for sending a storageMessage object to the storage
|
|
156
|
+
* implementation (cf. golang/cosmos/x/vstorage/vstorage.go)
|
|
166
157
|
* @param {string} path
|
|
167
158
|
* @param {object} [options]
|
|
168
|
-
* @param {boolean} [options.sequence] set values with `append` messages
|
|
169
|
-
* so the backing implementation employs a
|
|
170
|
-
* preserves each value set within a single block.
|
|
171
|
-
*
|
|
159
|
+
* @param {boolean} [options.sequence] set values with `append` messages
|
|
160
|
+
* rather than `set` messages so the backing implementation employs a
|
|
161
|
+
* wrapping structure that preserves each value set within a single block.
|
|
162
|
+
* Child nodes default to inheriting this option from their parent.
|
|
172
163
|
* @returns {StorageNode}
|
|
173
164
|
*/
|
|
174
165
|
const makeChainStorageNode = zone.exoClass(
|
|
175
166
|
'ChainStorageNode',
|
|
176
167
|
ChainStorageNodeI,
|
|
177
168
|
/**
|
|
178
|
-
* @param {import('./
|
|
169
|
+
* @param {import('./types.js').Callback<
|
|
170
|
+
* (message: StorageMessage) => any
|
|
171
|
+
* >} messenger
|
|
179
172
|
* @param {string} path
|
|
180
173
|
* @param {object} [options]
|
|
181
174
|
* @param {boolean} [options.sequence]
|
|
@@ -200,7 +193,12 @@ export const prepareChainStorageNode = zone => {
|
|
|
200
193
|
args: [path],
|
|
201
194
|
});
|
|
202
195
|
},
|
|
203
|
-
/**
|
|
196
|
+
/**
|
|
197
|
+
* @type {(
|
|
198
|
+
* name: string,
|
|
199
|
+
* childNodeOptions?: { sequence?: boolean },
|
|
200
|
+
* ) => StorageNode}
|
|
201
|
+
*/
|
|
204
202
|
makeChildNode(name, childNodeOptions = {}) {
|
|
205
203
|
const { sequence, path, messenger } = this.state;
|
|
206
204
|
assertPathSegment(name);
|
|
@@ -239,19 +237,20 @@ export const prepareChainStorageNode = zone => {
|
|
|
239
237
|
return makeChainStorageNode;
|
|
240
238
|
};
|
|
241
239
|
|
|
242
|
-
const makeHeapChainStorageNode = prepareChainStorageNode(
|
|
240
|
+
const makeHeapChainStorageNode = prepareChainStorageNode(makeHeapZone());
|
|
243
241
|
|
|
244
242
|
/**
|
|
245
|
-
* Create a heap-based root storage node for a given backing function and root
|
|
243
|
+
* Create a heap-based root storage node for a given backing function and root
|
|
244
|
+
* path.
|
|
246
245
|
*
|
|
247
246
|
* @param {(message: StorageMessage) => any} handleStorageMessage a function for
|
|
248
|
-
*
|
|
249
|
-
*
|
|
247
|
+
* sending a storageMessage object to the storage implementation (cf.
|
|
248
|
+
* golang/cosmos/x/vstorage/vstorage.go)
|
|
250
249
|
* @param {string} rootPath
|
|
251
250
|
* @param {object} [rootOptions]
|
|
252
251
|
* @param {boolean} [rootOptions.sequence] employ a wrapping structure that
|
|
253
|
-
*
|
|
254
|
-
*
|
|
252
|
+
* preserves each value set within a single block, and default child nodes to
|
|
253
|
+
* do the same
|
|
255
254
|
*/
|
|
256
255
|
export function makeChainStorageRoot(
|
|
257
256
|
handleStorageMessage,
|
|
@@ -266,11 +265,15 @@ export function makeChainStorageRoot(
|
|
|
266
265
|
}
|
|
267
266
|
|
|
268
267
|
/**
|
|
269
|
-
* @returns {StorageNode} an object that confirms to StorageNode API but does
|
|
268
|
+
* @returns {StorageNode} an object that confirms to StorageNode API but does
|
|
269
|
+
* not store anywhere.
|
|
270
270
|
*/
|
|
271
271
|
const makeNullStorageNode = () => {
|
|
272
272
|
// XXX re-use "ChainStorage" methods above which don't actually depend on chains
|
|
273
|
-
return makeChainStorageRoot(
|
|
273
|
+
return makeChainStorageRoot(
|
|
274
|
+
Far('NullMessenger', () => null),
|
|
275
|
+
'null',
|
|
276
|
+
);
|
|
274
277
|
};
|
|
275
278
|
|
|
276
279
|
/**
|
|
@@ -278,7 +281,7 @@ const makeNullStorageNode = () => {
|
|
|
278
281
|
* falling back to an inert object with the correct interface (but incomplete
|
|
279
282
|
* behavior) when that is unavailable.
|
|
280
283
|
*
|
|
281
|
-
* @param {
|
|
284
|
+
* @param {ERef<StorageNode?>} storageNodeRef
|
|
282
285
|
* @param {string} childName
|
|
283
286
|
* @returns {Promise<StorageNode>}
|
|
284
287
|
*/
|
|
@@ -291,9 +294,9 @@ harden(makeStorageNodeChild);
|
|
|
291
294
|
|
|
292
295
|
// TODO find a better module for this
|
|
293
296
|
/**
|
|
294
|
-
* @param {
|
|
295
|
-
* @param {
|
|
296
|
-
* @returns {(value:
|
|
297
|
+
* @param {ERef<StorageNode>} storageNode
|
|
298
|
+
* @param {ERef<Marshaller>} marshaller
|
|
299
|
+
* @returns {(value: PassableCap) => Promise<void>}
|
|
297
300
|
*/
|
|
298
301
|
export const makeSerializeToStorage = (storageNode, marshaller) => {
|
|
299
302
|
return async value => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {ExecutionContext, Macro, TestFn} from 'ava';
|
|
3
|
+
*/
|
|
4
|
+
export const AVA_EXPECT_UNHANDLED_REJECTIONS: "AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS";
|
|
5
|
+
export const SUBTEST_PREFIX: "(unhandled rejection subprocess): ";
|
|
6
|
+
export function makeExpectUnhandledRejection<C>({ test, importMetaUrl }: {
|
|
7
|
+
test: TestFn<C>;
|
|
8
|
+
importMetaUrl: string;
|
|
9
|
+
}): (expectedUnhandled: number) => Macro<[name: string, impl: (t: ExecutionContext<C>) => any], C>;
|
|
10
|
+
import type { TestFn } from 'ava';
|
|
11
|
+
import type { ExecutionContext } from 'ava';
|
|
12
|
+
import type { Macro } from 'ava';
|
|
13
|
+
//# sourceMappingURL=ava-unhandled-rejection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ava-unhandled-rejection.d.ts","sourceRoot":"","sources":["ava-unhandled-rejection.js"],"names":[],"mappings":"AAOA;;GAEG;AAEH,8CACE,wCAAwC,CAAC;AAE3C,6BAA8B,oCAAoC,CAAC;AAW5D,6CARM,CAAC,2BAEX;IAA0B,IAAI,EAAtB,OAAO,CAAC,CAAC;IACM,aAAa,EAA5B,MAAM;CACd,GAAU,CACR,iBAAiB,EAAE,MAAM,KACtB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CA0CtE;4BAzDiD,KAAK;sCAAL,KAAK;2BAAL,KAAK"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
import engineGC from './engine-gc.js';
|
|
6
|
+
import { makeGcAndFinalize } from './gc-and-finalize.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @import {ExecutionContext, Macro, TestFn} from 'ava';
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const AVA_EXPECT_UNHANDLED_REJECTIONS =
|
|
13
|
+
'AGORIC_AVA_EXPECT_UNHANDLED_REJECTIONS';
|
|
14
|
+
|
|
15
|
+
export const SUBTEST_PREFIX = '(unhandled rejection subprocess): ';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @template C
|
|
19
|
+
* @param {object} powers
|
|
20
|
+
* @param {TestFn<C>} powers.test
|
|
21
|
+
* @param {string} powers.importMetaUrl
|
|
22
|
+
* @returns {(
|
|
23
|
+
* expectedUnhandled: number,
|
|
24
|
+
* ) => Macro<[name: string, impl: (t: ExecutionContext<C>) => any], C>}
|
|
25
|
+
*/
|
|
26
|
+
export const makeExpectUnhandledRejection = ({ test, importMetaUrl }) => {
|
|
27
|
+
const self = fileURLToPath(importMetaUrl);
|
|
28
|
+
const gcAndFinalize = makeGcAndFinalize(engineGC);
|
|
29
|
+
|
|
30
|
+
if (process.env[AVA_EXPECT_UNHANDLED_REJECTIONS]) {
|
|
31
|
+
return _expectedUnhandled =>
|
|
32
|
+
test.macro({
|
|
33
|
+
title: (_, name, _impl) => SUBTEST_PREFIX + name,
|
|
34
|
+
exec: async (t, _name, impl) => {
|
|
35
|
+
await null;
|
|
36
|
+
try {
|
|
37
|
+
const result = await impl(t);
|
|
38
|
+
return result;
|
|
39
|
+
} finally {
|
|
40
|
+
await gcAndFinalize();
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return expectedUnhandled =>
|
|
47
|
+
test.macro({
|
|
48
|
+
title: (_, name, _impl) => name,
|
|
49
|
+
exec: async (t, name, _impl) =>
|
|
50
|
+
new Promise((resolve, reject) => {
|
|
51
|
+
const ps = spawn('ava', [self, '-m', SUBTEST_PREFIX + name], {
|
|
52
|
+
env: {
|
|
53
|
+
...process.env,
|
|
54
|
+
[AVA_EXPECT_UNHANDLED_REJECTIONS]: `${expectedUnhandled}`,
|
|
55
|
+
},
|
|
56
|
+
stdio: ['ignore', 'inherit', 'inherit', 'ignore'],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
ps.on('close', code => {
|
|
60
|
+
t.is(code, 0, `got exit code ${code}, expected 0 for ${name}`);
|
|
61
|
+
resolve();
|
|
62
|
+
});
|
|
63
|
+
ps.on('error', reject);
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-gc.d.ts","sourceRoot":"","sources":["engine-gc.js"],"names":[],"mappings":";AAoBA,mCAAwB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import v8 from 'v8';
|
|
2
|
+
import vm from 'vm';
|
|
3
|
+
|
|
4
|
+
/* global globalThis */
|
|
5
|
+
let bestGC = globalThis.gc;
|
|
6
|
+
if (typeof bestGC !== 'function') {
|
|
7
|
+
// Node.js v8 wizardry.
|
|
8
|
+
v8.setFlagsFromString('--expose_gc');
|
|
9
|
+
bestGC = vm.runInNewContext('gc');
|
|
10
|
+
assert(bestGC);
|
|
11
|
+
// We leave --expose_gc turned on, otherwise AVA's shared workers
|
|
12
|
+
// may race and disable it before we manage to extract the
|
|
13
|
+
// binding. This won't cause 'gc' to be visible to new Compartments
|
|
14
|
+
// because SES strips out everything it doesn't recognize.
|
|
15
|
+
|
|
16
|
+
// // Hide the gc global from new contexts/workers.
|
|
17
|
+
// v8.setFlagsFromString('--no-expose_gc');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Export a const.
|
|
21
|
+
const engineGC = bestGC;
|
|
22
|
+
export default engineGC;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gc-and-finalize.d.ts","sourceRoot":"","sources":["gc-and-finalize.js"],"names":[],"mappings":"AAkEA,qEAwBC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/* global setImmediate */
|
|
2
|
+
|
|
3
|
+
/* A note on our GC terminology:
|
|
4
|
+
*
|
|
5
|
+
* We define four states for any JS object to be in:
|
|
6
|
+
*
|
|
7
|
+
* REACHABLE: There exists a path from some root (live export or top-level
|
|
8
|
+
* global) to this object, making it ineligible for collection. Userspace vat
|
|
9
|
+
* code has a strong reference to it (and userspace is not given access to
|
|
10
|
+
* WeakRef, so it has no weak reference that might be used to get access).
|
|
11
|
+
*
|
|
12
|
+
* UNREACHABLE: There is no strong reference from a root to the object.
|
|
13
|
+
* Userspace vat code has no means to access the object, although liveslots
|
|
14
|
+
* might (via a WeakRef). The object is eligible for collection, but that
|
|
15
|
+
* collection has not yet happened. The liveslots WeakRef is still alive: if
|
|
16
|
+
* it were to call `.deref()`, it would get the object.
|
|
17
|
+
*
|
|
18
|
+
* COLLECTED: The JS engine has performed enough GC to notice the
|
|
19
|
+
* unreachability of the object, and has collected it. The liveslots WeakRef
|
|
20
|
+
* is dead: `wr.deref() === undefined`. Neither liveslots nor userspace has
|
|
21
|
+
* any way to reach the object, and never will again. A finalizer callback
|
|
22
|
+
* has been queued, but not yet executed.
|
|
23
|
+
*
|
|
24
|
+
* FINALIZED: The JS engine has run the finalizer callback. Once the
|
|
25
|
+
* callback completes, the object is thoroughly dead and unremembered,
|
|
26
|
+
* and no longer exists in one of these four states.
|
|
27
|
+
*
|
|
28
|
+
* The transition from REACHABLE to UNREACHABLE always happens as a result of
|
|
29
|
+
* a message delivery or resolution notification (e.g when userspace
|
|
30
|
+
* overwrites a variable, deletes a Map entry, or a callback on the promise
|
|
31
|
+
* queue which closed over some objects is retired and deleted).
|
|
32
|
+
*
|
|
33
|
+
* The transition from UNREACHABLE to COLLECTED can happen spontaneously, as
|
|
34
|
+
* the JS engine decides it wants to perform GC. It will also happen
|
|
35
|
+
* deliberately if we provoke a GC call with a magic function like `gc()`
|
|
36
|
+
* (when Node.js imports `engine-gc`, which is morally-equivalent to
|
|
37
|
+
* running with `--expose-gc`, or when XS is configured to provide it as a
|
|
38
|
+
* C-level callback). We can force GC, but we cannot prevent it from happening
|
|
39
|
+
* at other times.
|
|
40
|
+
*
|
|
41
|
+
* FinalizationRegistry callbacks are defined to run on their own turn, so
|
|
42
|
+
* the transition from COLLECTED to FINALIZED occurs at a turn boundary.
|
|
43
|
+
* Node.js appears to schedule these finalizers on the timer/IO queue, not
|
|
44
|
+
* the promise/microtask queue. So under Node.js, you need a `setImmediate()`
|
|
45
|
+
* or two to ensure that finalizers have had a chance to run. XS is different
|
|
46
|
+
* but responds well to similar techniques.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/*
|
|
50
|
+
* `gcAndFinalize` must be defined in the start compartment. It uses
|
|
51
|
+
* platform-specific features to provide a function which provokes a full GC
|
|
52
|
+
* operation: all "UNREACHABLE" objects should transition to "COLLECTED"
|
|
53
|
+
* before it returns. In addition, `gcAndFinalize()` returns a Promise. This
|
|
54
|
+
* Promise will resolve (with `undefined`) after all FinalizationRegistry
|
|
55
|
+
* callbacks have executed, causing all COLLECTED objects to transition to
|
|
56
|
+
* FINALIZED. If the caller can manage call gcAndFinalize with an empty
|
|
57
|
+
* promise queue, then their .then callback will also start with an empty
|
|
58
|
+
* promise queue, and there will be minimal uncollected unreachable objects
|
|
59
|
+
* in the heap when it begins.
|
|
60
|
+
*
|
|
61
|
+
* `gcAndFinalize` depends upon platform-specific tools to provoke a GC sweep
|
|
62
|
+
* and wait for finalizers to run: a `gc()` function, and `setImmediate`. If
|
|
63
|
+
* these tools do not exist, this function will do nothing, and return a
|
|
64
|
+
* dummy pre-resolved Promise.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
export function makeGcAndFinalize(gcPower) {
|
|
68
|
+
if (typeof gcPower !== 'function') {
|
|
69
|
+
if (gcPower !== false) {
|
|
70
|
+
// We weren't explicitly disabled, so warn.
|
|
71
|
+
console.warn(
|
|
72
|
+
Error(`no gcPower() function; skipping finalizer provocation`),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return async function gcAndFinalize() {
|
|
77
|
+
if (typeof gcPower !== 'function') {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// on Node.js, GC seems to work better if the promise queue is empty first
|
|
82
|
+
await new Promise(setImmediate);
|
|
83
|
+
// on xsnap, we must do it twice for some reason
|
|
84
|
+
await new Promise(setImmediate);
|
|
85
|
+
gcPower();
|
|
86
|
+
// this gives finalizers a chance to run
|
|
87
|
+
await new Promise(setImmediate);
|
|
88
|
+
// Node.js seems to need another for promises to get cleared out
|
|
89
|
+
await new Promise(setImmediate);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function startSubprocessWorker(execPath: any, procArgs?: any[], { netstringMaxChunkSize }?: {
|
|
2
|
+
netstringMaxChunkSize?: undefined;
|
|
3
|
+
}): {
|
|
4
|
+
fromChild: {
|
|
5
|
+
on: (...args: any[]) => import("stream").Transform;
|
|
6
|
+
};
|
|
7
|
+
toChild: {
|
|
8
|
+
write: (...args: any[]) => boolean;
|
|
9
|
+
};
|
|
10
|
+
terminate: () => void;
|
|
11
|
+
done: Promise<any>;
|
|
12
|
+
};
|
|
13
|
+
export type IOType = import("child_process").IOType;
|
|
14
|
+
export type Writable = import("stream").Writable;
|
|
15
|
+
//# sourceMappingURL=spawnSubprocessWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawnSubprocessWorker.d.ts","sourceRoot":"","sources":["spawnSubprocessWorker.js"],"names":[],"mappings":"AA8BA;;;;;;;;;;;EA0DC;qBApEa,OAAO,eAAe,EAAE,MAAM;uBAC9B,OAAO,QAAQ,EAAE,QAAQ"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// this file is loaded by the controller, in the start compartment
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
4
|
+
import { NonNullish } from '../errors.js';
|
|
5
|
+
import { arrayEncoderStream, arrayDecoderStream } from './worker-protocol.js';
|
|
6
|
+
import {
|
|
7
|
+
netstringEncoderStream,
|
|
8
|
+
netstringDecoderStream,
|
|
9
|
+
} from '../netstring.js';
|
|
10
|
+
|
|
11
|
+
// Start a subprocess from a given executable, and arrange a bidirectional
|
|
12
|
+
// message channel with a "supervisor" within that process. Return a {
|
|
13
|
+
// toChild, fromChild } pair of Streams which accept/emit hardened Arrays of
|
|
14
|
+
// JSON-serializable data.
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line no-unused-vars
|
|
17
|
+
function parentLog(first, ...args) {
|
|
18
|
+
// console.error(`--parent: ${first}`, ...args);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @typedef {import('child_process').IOType} IOType */
|
|
22
|
+
/** @typedef {import('stream').Writable} Writable */
|
|
23
|
+
|
|
24
|
+
// we send on fd3, and receive on fd4. We pass fd1/2 (stdout/err) through, so
|
|
25
|
+
// console log/err from the child shows up normally. We don't use Node's
|
|
26
|
+
// built-in serialization feature ('ipc') because the child process won't
|
|
27
|
+
// always be Node.
|
|
28
|
+
/** @type {IOType[]} */
|
|
29
|
+
const stdio = harden(['inherit', 'inherit', 'inherit', 'pipe', 'pipe']);
|
|
30
|
+
|
|
31
|
+
export function startSubprocessWorker(
|
|
32
|
+
execPath,
|
|
33
|
+
procArgs = [],
|
|
34
|
+
{ netstringMaxChunkSize = undefined } = {},
|
|
35
|
+
) {
|
|
36
|
+
const proc = spawn(execPath, procArgs, { stdio });
|
|
37
|
+
|
|
38
|
+
const toChild = arrayEncoderStream();
|
|
39
|
+
toChild
|
|
40
|
+
.pipe(netstringEncoderStream())
|
|
41
|
+
.pipe(/** @type {Writable} */ (proc.stdio[3]));
|
|
42
|
+
// proc.stdio[4].setEncoding('utf-8');
|
|
43
|
+
const fromChild = NonNullish(proc.stdio[4])
|
|
44
|
+
.pipe(netstringDecoderStream(netstringMaxChunkSize))
|
|
45
|
+
.pipe(arrayDecoderStream());
|
|
46
|
+
|
|
47
|
+
// fromChild.addListener('data', data => parentLog(`fd4 data`, data));
|
|
48
|
+
// toChild.write('hello child');
|
|
49
|
+
|
|
50
|
+
const pk = makePromiseKit();
|
|
51
|
+
|
|
52
|
+
proc.once('exit', code => {
|
|
53
|
+
parentLog('child exit', code);
|
|
54
|
+
pk.resolve(code);
|
|
55
|
+
});
|
|
56
|
+
proc.once('error', e => {
|
|
57
|
+
parentLog('child error', e);
|
|
58
|
+
pk.reject(e);
|
|
59
|
+
});
|
|
60
|
+
parentLog(`waiting on child`);
|
|
61
|
+
|
|
62
|
+
function terminate() {
|
|
63
|
+
proc.kill();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// the Transform objects don't like being hardened, so we wrap the methods
|
|
67
|
+
// that get used
|
|
68
|
+
/* @type {typeof fromChild} */
|
|
69
|
+
const wrappedFromChild = {
|
|
70
|
+
on: (...args) =>
|
|
71
|
+
fromChild.on(
|
|
72
|
+
.../** @type {Parameters<(typeof fromChild)['on']>} */ (args),
|
|
73
|
+
),
|
|
74
|
+
};
|
|
75
|
+
/* @type {typeof toChild} */
|
|
76
|
+
const wrappedToChild = {
|
|
77
|
+
write: (...args) =>
|
|
78
|
+
toChild.write(
|
|
79
|
+
.../** @type {Parameters<(typeof toChild)['write']>} */ (args),
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return harden({
|
|
84
|
+
fromChild: wrappedFromChild,
|
|
85
|
+
toChild: wrappedToChild,
|
|
86
|
+
terminate,
|
|
87
|
+
done: pk.promise,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waitUntilQuiescent.d.ts","sourceRoot":"","sources":["waitUntilQuiescent.js"],"names":[],"mappings":"AAMA,oDAWC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* global setImmediate */
|
|
2
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
3
|
+
|
|
4
|
+
// This can only be imported from the Start Compartment, where 'setImmediate'
|
|
5
|
+
// is available.
|
|
6
|
+
|
|
7
|
+
export function waitUntilQuiescent() {
|
|
8
|
+
// the delivery might cause some number of (native) Promises to be
|
|
9
|
+
// created and resolved, so we use the IO queue to detect when the
|
|
10
|
+
// Promise queue is empty. The IO queue (setImmediate and setTimeout) is
|
|
11
|
+
// lower-priority than the Promise queue on browsers and Node 11, but on
|
|
12
|
+
// Node 10 it is higher. So this trick requires Node 11.
|
|
13
|
+
// https://jsblog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3
|
|
14
|
+
/** @type {import('@endo/promise-kit').PromiseKit<void>} */
|
|
15
|
+
const { promise: queueEmptyP, resolve } = makePromiseKit();
|
|
16
|
+
setImmediate(() => resolve());
|
|
17
|
+
return queueEmptyP;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-protocol.d.ts","sourceRoot":"","sources":["worker-protocol.js"],"names":[],"mappings":"AAMA,gDAqBC;AAED,gDAwBC;0BApDyB,QAAQ"}
|