@agoric/internal 0.3.3-other-dev-3eb1a1d.0 → 0.3.3-other-dev-d15096d.0.d15096d

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.
Files changed (111) hide show
  1. package/README.md +1 -4
  2. package/package.json +29 -23
  3. package/src/action-types.d.ts +2 -1
  4. package/src/action-types.d.ts.map +1 -1
  5. package/src/action-types.js +4 -4
  6. package/src/batched-deliver.d.ts +6 -2
  7. package/src/batched-deliver.d.ts.map +1 -1
  8. package/src/batched-deliver.js +7 -2
  9. package/src/callback.d.ts +10 -12
  10. package/src/callback.d.ts.map +1 -1
  11. package/src/callback.js +23 -16
  12. package/src/chain-storage-paths.d.ts.map +1 -1
  13. package/src/chain-utils.d.ts +3 -1
  14. package/src/chain-utils.d.ts.map +1 -1
  15. package/src/chain-utils.js +6 -1
  16. package/src/cli-utils.d.ts +2 -0
  17. package/src/cli-utils.d.ts.map +1 -0
  18. package/src/cli-utils.js +21 -0
  19. package/src/config.d.ts +21 -4
  20. package/src/config.d.ts.map +1 -1
  21. package/src/config.js +25 -2
  22. package/src/debug.d.ts +4 -1
  23. package/src/debug.d.ts.map +1 -1
  24. package/src/debug.js +26 -13
  25. package/src/errors.d.ts +1 -1
  26. package/src/errors.d.ts.map +1 -1
  27. package/src/hex.d.ts +15 -0
  28. package/src/hex.d.ts.map +1 -0
  29. package/src/hex.js +105 -0
  30. package/src/index.d.ts +5 -3
  31. package/src/index.js +13 -4
  32. package/src/js-utils.d.ts +38 -1
  33. package/src/js-utils.d.ts.map +1 -1
  34. package/src/js-utils.js +184 -14
  35. package/src/lib-chainStorage.d.ts +45 -32
  36. package/src/lib-chainStorage.d.ts.map +1 -1
  37. package/src/lib-chainStorage.js +24 -32
  38. package/src/lib-nodejs/ava-unhandled-rejection.d.ts +13 -0
  39. package/src/lib-nodejs/ava-unhandled-rejection.d.ts.map +1 -0
  40. package/src/lib-nodejs/ava-unhandled-rejection.js +66 -0
  41. package/src/lib-nodejs/spawnSubprocessWorker.d.ts +0 -2
  42. package/src/lib-nodejs/spawnSubprocessWorker.d.ts.map +1 -1
  43. package/src/lib-nodejs/spawnSubprocessWorker.js +5 -3
  44. package/src/lib-nodejs/waitUntilQuiescent.d.ts +3 -0
  45. package/src/lib-nodejs/waitUntilQuiescent.d.ts.map +1 -1
  46. package/src/lib-nodejs/waitUntilQuiescent.js +5 -1
  47. package/src/marshal/board-client-utils.d.ts +19 -0
  48. package/src/marshal/board-client-utils.d.ts.map +1 -0
  49. package/src/{marshal.js → marshal/board-client-utils.js} +30 -36
  50. package/src/marshal/cap-data.d.ts +3 -0
  51. package/src/marshal/cap-data.d.ts.map +1 -0
  52. package/src/marshal/cap-data.js +20 -0
  53. package/src/marshal/inaccessible-val.d.ts +2 -0
  54. package/src/marshal/inaccessible-val.d.ts.map +1 -0
  55. package/src/marshal/inaccessible-val.js +15 -0
  56. package/src/marshal/pure-data.d.ts +8 -0
  57. package/src/marshal/pure-data.d.ts.map +1 -0
  58. package/src/marshal/pure-data.js +14 -0
  59. package/src/marshal/wrap-marshaller.d.ts +33 -0
  60. package/src/marshal/wrap-marshaller.d.ts.map +1 -0
  61. package/src/marshal/wrap-marshaller.js +439 -0
  62. package/src/method-tools.d.ts.map +1 -1
  63. package/src/method-tools.js +8 -50
  64. package/src/metrics.d.ts +183 -0
  65. package/src/metrics.d.ts.map +1 -0
  66. package/src/metrics.js +476 -0
  67. package/src/module-utils.d.ts +2 -0
  68. package/src/module-utils.d.ts.map +1 -0
  69. package/src/module-utils.js +27 -0
  70. package/src/natural-sort.d.ts +2 -0
  71. package/src/natural-sort.d.ts.map +1 -0
  72. package/src/natural-sort.js +83 -0
  73. package/src/netstring.d.ts +2 -2
  74. package/src/netstring.d.ts.map +1 -1
  75. package/src/netstring.js +1 -0
  76. package/src/node/buffer-line-transform.d.ts +12 -7
  77. package/src/node/buffer-line-transform.d.ts.map +1 -1
  78. package/src/node/buffer-line-transform.js +8 -4
  79. package/src/node/fs-stream.d.ts +4 -1
  80. package/src/node/fs-stream.d.ts.map +1 -1
  81. package/src/node/fs-stream.js +28 -27
  82. package/src/node/shutdown.d.ts.map +1 -1
  83. package/src/node/shutdown.js +2 -0
  84. package/src/priority-senders.d.ts +3 -1
  85. package/src/priority-senders.d.ts.map +1 -1
  86. package/src/priority-senders.js +7 -2
  87. package/src/queue.d.ts +1 -1
  88. package/src/queue.d.ts.map +1 -1
  89. package/src/ses-utils.d.ts +24 -5
  90. package/src/ses-utils.d.ts.map +1 -1
  91. package/src/ses-utils.js +189 -15
  92. package/src/storage-test-utils.d.ts +25 -7
  93. package/src/storage-test-utils.d.ts.map +1 -1
  94. package/src/storage-test-utils.js +141 -22
  95. package/src/tagged.d.ts +4 -1
  96. package/src/testing-utils.js +1 -1
  97. package/src/tmpDir.d.ts +2 -0
  98. package/src/tmpDir.d.ts.map +1 -0
  99. package/src/tmpDir.js +17 -0
  100. package/src/tokens.d.ts.map +1 -1
  101. package/src/typeGuards.d.ts +19 -0
  102. package/src/typeGuards.d.ts.map +1 -1
  103. package/src/typeGuards.js +16 -0
  104. package/src/types.d.ts +30 -17
  105. package/src/types.d.ts.map +1 -1
  106. package/src/types.ts +43 -18
  107. package/src/work-pool.d.ts +13 -0
  108. package/src/work-pool.d.ts.map +1 -0
  109. package/src/work-pool.js +233 -0
  110. package/src/marshal.d.ts +0 -20
  111. package/src/marshal.d.ts.map +0 -1
package/src/ses-utils.js CHANGED
@@ -5,15 +5,41 @@
5
5
  * either directly or indirectly (e.g. by @endo imports).
6
6
  */
7
7
 
8
- import { q, Fail, makeError, annotateError, X } from '@endo/errors';
9
- import { deeplyFulfilled, isObject } from '@endo/marshal';
8
+ import { objectMap } from '@endo/common/object-map.js';
9
+ import { objectMetaMap } from '@endo/common/object-meta-map.js';
10
+ import { fromUniqueEntries } from '@endo/common/from-unique-entries.js';
11
+ import { q, b, Fail, makeError, annotateError, X } from '@endo/errors';
12
+ import { deeplyFulfilled, isPrimitive } from '@endo/pass-style';
10
13
  import { makePromiseKit } from '@endo/promise-kit';
11
14
  import { makeQueue } from '@endo/stream';
15
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
16
+ // @ts-ignore TS7016 The 'jessie.js' library may need to update its package.json or typings
12
17
  import { asyncGenerate } from 'jessie.js';
18
+ import { logLevels } from './js-utils.js';
19
+
20
+ /**
21
+ * @import {LimitedConsole} from './js-utils.js';
22
+ * @import {AsyncQueue} from '@endo/stream';
23
+ * @import {PromiseKit} from '@endo/promise-kit';
24
+ */
25
+
26
+ /** @import {ERef} from '@endo/far'; */
27
+ /** @import {RemotableBrand} from '@endo/eventual-send'; */
28
+ /** @import {Primitive, RemotableObject} from '@endo/pass-style'; */
29
+ /** @import {Permit, Attenuated} from './types.js'; */
30
+
31
+ export { objectMap, objectMetaMap, fromUniqueEntries };
13
32
 
14
33
  const { fromEntries, keys, values } = Object;
15
34
 
16
- /** @import {ERef} from '@endo/far' */
35
+ /** @param {(level: string) => (...args: unknown[]) => void} makeLogger */
36
+ export const makeLimitedConsole = makeLogger => {
37
+ const limitedConsole = /** @type {any} */ (
38
+ fromEntries(logLevels.map(level => [level, makeLogger(level)]))
39
+ );
40
+ return /** @type {LimitedConsole} */ (harden(limitedConsole));
41
+ };
42
+ harden(makeLimitedConsole);
17
43
 
18
44
  /**
19
45
  * @template T
@@ -37,9 +63,11 @@ const { fromEntries, keys, values } = Object;
37
63
  * @template T
38
64
  * @typedef {T extends PromiseLike<any>
39
65
  * ? Awaited<T>
40
- * : T extends {}
41
- * ? Simplify<DeeplyAwaitedObject<T>>
42
- * : Awaited<T>} DeeplyAwaited
66
+ * : T extends RemotableBrand<any, any> | RemotableObject
67
+ * ? T
68
+ * : T extends {}
69
+ * ? Simplify<DeeplyAwaitedObject<T>>
70
+ * : Awaited<T>} DeeplyAwaited
43
71
  */
44
72
 
45
73
  /**
@@ -50,7 +78,7 @@ const { fromEntries, keys, values } = Object;
50
78
  * @type {<T extends {}>(unfulfilledTerms: T) => Promise<DeeplyAwaited<T>>}
51
79
  */
52
80
  export const deeplyFulfilledObject = async obj => {
53
- isObject(obj) || Fail`param must be an object`;
81
+ !isPrimitive(obj) || Fail`param must be an object`;
54
82
  return deeplyFulfilled(obj);
55
83
  };
56
84
 
@@ -69,6 +97,101 @@ const makeAggregateError =
69
97
  });
70
98
  };
71
99
 
100
+ /**
101
+ * Assert that a string has a prefix, and return the part that follows it.
102
+ *
103
+ * @param {string} prefix
104
+ * @param {string} str
105
+ * @returns {string}
106
+ */
107
+ export const stripPrefix = (prefix, str) => {
108
+ str.startsWith(prefix) || Fail`${str} is missing prefix ${q(prefix)}`;
109
+ return str.slice(prefix.length);
110
+ };
111
+
112
+ /**
113
+ * Throw an error with an own "code" data property, supporting identification
114
+ * without parsing `message`. Note that such errors are not Passable and thus
115
+ * cannot appear inside a Passable structure, and even at top level the "code"
116
+ * property will be silently dropped by marshalling.
117
+ *
118
+ * @template {string} Code
119
+ * @param {Parameters<typeof makeError>[0]} details
120
+ * @param {Code} code
121
+ * @param {Parameters<typeof makeError>[2] & {
122
+ * constructor?: Parameters<typeof makeError>[1];
123
+ * }} [opts]
124
+ */
125
+ export const throwErrorCode = (details, code, opts) => {
126
+ const err = makeError(details, opts?.constructor, {
127
+ ...opts,
128
+ sanitize: false,
129
+ });
130
+ Object.defineProperty(err, 'code', { value: code, enumerable: true });
131
+ harden(err);
132
+ throw err;
133
+ };
134
+
135
+ /**
136
+ * Synchronusly invoke a function with the opportunity to handle any error
137
+ * similarly to a Promise `catch` callback (e.g., substituting a non-error
138
+ * returned value or throwing a possibly-new error). This is useful for (among
139
+ * other things) replacing generic error messages with specific ones (as in
140
+ * {@see tryJsonParse}).
141
+ *
142
+ * @template {(...args: any[]) => any} F
143
+ * @template [U=ReturnType<F>]
144
+ * @param {F} fn to invoke
145
+ * @param {(err: Error) => U} transformError callback to receive any error from
146
+ * invoking `fn` and either return a substitute value or throw a potentially
147
+ * new error
148
+ * @param {Parameters<F>} args for `fn`
149
+ * @returns {ReturnType<F> | U}
150
+ */
151
+ export const tryNow = (fn, transformError, ...args) => {
152
+ try {
153
+ return fn(...args);
154
+ } catch (err) {
155
+ try {
156
+ return transformError(err);
157
+ } catch (newErr) {
158
+ // Try to associate `err` with `newErr`.
159
+ if (!newErr.cause) {
160
+ const desc = {
161
+ value: err,
162
+ configurable: true,
163
+ enumerable: false,
164
+ writable: true,
165
+ };
166
+ if (!Reflect.defineProperty(newErr, 'cause', desc)) {
167
+ assert.note(newErr, err.message);
168
+ }
169
+ }
170
+ throw newErr;
171
+ }
172
+ }
173
+ };
174
+
175
+ /**
176
+ * Parse JSON text, or handle an error by e.g. substituting a default value or
177
+ * replacing it with one bearing a non-generic message.
178
+ *
179
+ * @param {string} jsonText
180
+ * @param {string | ((err: Error) => unknown)} onError an error message or
181
+ * callback
182
+ */
183
+ export const tryJsonParse = (jsonText, onError) => {
184
+ const transformError =
185
+ typeof onError === 'function'
186
+ ? onError
187
+ : err => Fail([`${onError}: ${err.message} for input `, ''], jsonText);
188
+ return tryNow(() => {
189
+ const type = typeof jsonText;
190
+ type === 'string' || Fail`Input must be a string, not ${b(type)}`;
191
+ return JSON.parse(jsonText);
192
+ }, transformError);
193
+ };
194
+
72
195
  /**
73
196
  * @template T
74
197
  * @param {readonly (T | PromiseLike<T>)[]} items
@@ -163,6 +286,61 @@ export const assertAllDefined = obj => {
163
286
  }
164
287
  };
165
288
 
289
+ /**
290
+ * Attenuate `specimen` to only properties allowed by `permit`.
291
+ *
292
+ * @template T
293
+ * @template {Permit<T>} P
294
+ * @param {T} specimen
295
+ * @param {P} permit
296
+ * @param {<U, SubP extends Permit<U>>(attenuation: U, permit: SubP) => U} [transform]
297
+ * used to replace the results of recursive picks (but not blanket permits)
298
+ * @returns {Attenuated<T, P>}
299
+ */
300
+ export const attenuate = (specimen, permit, transform = x => x) => {
301
+ // Fast-path for no attenuation.
302
+ if (permit === true || typeof permit === 'string') {
303
+ return /** @type {Attenuated<T, P>} */ (specimen);
304
+ }
305
+
306
+ /** @type {string[]} */
307
+ const path = [];
308
+ /**
309
+ * @template SubT
310
+ * @template {Exclude<Permit<SubT>, Primitive>} SubP
311
+ * @type {(specimen: SubT, permit: SubP) => Attenuated<SubT, SubP>}
312
+ */
313
+ const extract = (subSpecimen, subPermit) => {
314
+ if (subPermit === null || typeof subPermit !== 'object') {
315
+ throw path.length === 0
316
+ ? Fail`invalid permit: ${q(permit)}`
317
+ : Fail`invalid permit at path ${q(path)}: ${q(subPermit)}`;
318
+ } else if (subSpecimen === null || typeof subSpecimen !== 'object') {
319
+ throw path.length === 0
320
+ ? Fail`specimen must be an object for permit ${q(permit)}`
321
+ : Fail`specimen at path ${q(path)} must be an object for permit ${q(subPermit)}`;
322
+ }
323
+ const picks = Object.entries(subPermit).map(([subKey, deepPermit]) => {
324
+ if (!Object.hasOwn(subSpecimen, subKey)) {
325
+ throw Fail`specimen is missing path ${q(path.concat(subKey))}`;
326
+ }
327
+ const deepSpecimen = Reflect.get(subSpecimen, subKey);
328
+ if (deepPermit === true || typeof deepPermit === 'string') {
329
+ return [subKey, deepSpecimen];
330
+ }
331
+ path.push(subKey);
332
+ const extracted = extract(/** @type {any} */ (deepSpecimen), deepPermit);
333
+ const entry = [subKey, extracted];
334
+ path.pop();
335
+ return entry;
336
+ });
337
+ return transform(Object.fromEntries(picks), subPermit);
338
+ };
339
+
340
+ // @ts-expect-error cast
341
+ return extract(specimen, permit);
342
+ };
343
+
166
344
  /** @type {IteratorResult<undefined, never>} */
167
345
  const notDone = harden({ done: false, value: undefined });
168
346
 
@@ -244,7 +422,7 @@ export const synchronizedTee = (sourceStream, readerCount) => {
244
422
  * (value: PromiseLike<IteratorResult<T>>) => void
245
423
  * >} QueuePayload
246
424
  */
247
- /** @type {import('@endo/stream').AsyncQueue<QueuePayload>[]} */
425
+ /** @type {AsyncQueue<QueuePayload>[]} */
248
426
  const queues = [];
249
427
 
250
428
  /** @returns {Promise<void>} */
@@ -298,7 +476,7 @@ export const synchronizedTee = (sourceStream, readerCount) => {
298
476
  };
299
477
 
300
478
  const readers = Array.from({ length: readerCount }).map(() => {
301
- /** @type {import('@endo/stream').AsyncQueue<QueuePayload>} */
479
+ /** @type {AsyncQueue<QueuePayload>} */
302
480
  const queue = makeQueue();
303
481
  queues.push(queue);
304
482
 
@@ -306,9 +484,7 @@ export const synchronizedTee = (sourceStream, readerCount) => {
306
484
  const reader = harden({
307
485
  async next() {
308
486
  /**
309
- * @type {import('@endo/promise-kit').PromiseKit<
310
- * IteratorResult<T>
311
- * >}
487
+ * @type {PromiseKit<IteratorResult<T>>}
312
488
  */
313
489
  const { promise, resolve } = makePromiseKit();
314
490
  queue.put({ value: resolve, done: false });
@@ -316,9 +492,7 @@ export const synchronizedTee = (sourceStream, readerCount) => {
316
492
  },
317
493
  async return() {
318
494
  /**
319
- * @type {import('@endo/promise-kit').PromiseKit<
320
- * IteratorResult<T>
321
- * >}
495
+ * @type {PromiseKit<IteratorResult<T>>}
322
496
  */
323
497
  const { promise, resolve } = makePromiseKit();
324
498
  queue.put({ value: resolve, done: true });
@@ -9,8 +9,21 @@ export const defaultMarshaller: {
9
9
  serialize: import("@endo/marshal").ToCapData<string>;
10
10
  unserialize: import("@endo/marshal").FromCapData<string>;
11
11
  };
12
+ export namespace defaultSerializer {
13
+ let parse: (text: string) => unknown;
14
+ let stringify: (obj: any) => string;
15
+ }
12
16
  export const slotStringUnserialize: (capData: any) => any;
13
- export function makeFakeStorageKit(rootPath: string, rootOptions?: Parameters<typeof makeChainStorageRoot>[2]): {
17
+ export function makeAsyncQueue<T>(): {
18
+ enqueue: (data: T) => void;
19
+ iterable: {
20
+ [Symbol.asyncIterator](): AsyncGenerator<Awaited<T> | undefined, void, unknown>;
21
+ };
22
+ cancel: () => boolean;
23
+ };
24
+ export function makeFakeStorageKit(rootPath: string, rootOptions?: Parameters<typeof makeChainStorageRoot>[2], { eachMessage }?: {
25
+ eachMessage?: (m: StorageMessage) => void;
26
+ }): {
14
27
  rootNode: import("@endo/exo").Guarded<{
15
28
  getPath(): string;
16
29
  getStoreKey(): Promise<VStorageKey>;
@@ -20,17 +33,19 @@ export function makeFakeStorageKit(rootPath: string, rootOptions?: Parameters<ty
20
33
  setValue(value: string): Promise<void>;
21
34
  }>;
22
35
  data: Map<string, string>;
36
+ updateNewCellBlockHeight: (blockHeight?: number) => void;
37
+ getValues: (path: string) => string[];
23
38
  messages: StorageMessage[];
24
- toStorage: ((message: StorageMessage) => string | number | any[] | {
39
+ toStorage: ((message: StorageMessage) => string | number | true | any[] | {
25
40
  storeName: string;
26
41
  storeSubkey: string;
27
- } | null | undefined) & import("@endo/pass-style").RemotableObject<`Alleged: ${string}`> & import("@endo/eventual-send").RemotableBrand<{}, (message: StorageMessage) => string | number | any[] | {
42
+ } | null) & import("@endo/pass-style").RemotableObject<`Alleged: ${string}`> & import("@endo/eventual-send").RemotableBrand<{}, (message: StorageMessage) => string | number | true | any[] | {
28
43
  storeName: string;
29
44
  storeSubkey: string;
30
- } | null | undefined>;
45
+ } | null>;
31
46
  };
32
47
  export function makeMockChainStorageRoot(): MockChainStorageRoot;
33
- export function documentStorageSchema(t: import("ava").ExecutionContext<unknown>, storage: MockChainStorageRoot | FakeStorageKit, opts: ({
48
+ export function documentStorageSchema(t: ExecutionContext<unknown>, storage: MockChainStorageRoot | FakeStorageKit, opts: ({
34
49
  note: string;
35
50
  } | {
36
51
  node: string;
@@ -38,7 +53,9 @@ export function documentStorageSchema(t: import("ava").ExecutionContext<unknown>
38
53
  }) & ({
39
54
  pattern: string;
40
55
  replacement: string;
41
- } | {})): Promise<void>;
56
+ } | {}) & {
57
+ showValue?: (v: string) => unknown;
58
+ }): Promise<void>;
42
59
  export type FakeStorageKit = ReturnType<typeof makeFakeStorageKit>;
43
60
  export type MockChainStorageRootMethods = {
44
61
  /**
@@ -52,7 +69,8 @@ export type MockChainStorageRootMethods = {
52
69
  };
53
70
  export type MockChainStorageRoot = StorageNode & MockChainStorageRootMethods;
54
71
  import { makeChainStorageRoot } from './lib-chainStorage.js';
55
- import type { StorageNode } from './lib-chainStorage.js';
56
72
  import type { StorageMessage } from './lib-chainStorage.js';
73
+ import type { StorageNode } from './lib-chainStorage.js';
74
+ import type { ExecutionContext } from 'ava';
57
75
  import type { Marshaller } from './lib-chainStorage.js';
58
76
  //# sourceMappingURL=storage-test-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-test-utils.d.ts","sourceRoot":"","sources":["storage-test-utils.js"],"names":[],"mappings":"AAwBO,yCAHI,MAAM,UACN,MAAM,6GAGC;AAElB;;;GAGG;AACH;;;;;EAEG;AA4CH,8CAtBuB,GAAG,KAAK,GAAG,CAsB+B;AAU1D,6CAHI,MAAM,gBACN,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC;;;;;oBA2GjD,CAAA;;;;UAEiB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;;0BAjFxB,cAAc;;;0JAAd,cAAc;;;;EAqF7B;AAoBM,4CADO,oBAAoB,CA6BjC;AAQM,yCALI,OAAO,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,WACvC,oBAAoB,GAAG,cAAc,QACrC,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9D,CAAK;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,EAAE,CAAC,iBAkCnD;6BAtFa,UAAU,CAAC,OAAO,kBAAkB,CAAC;;;;;;;;aAIrC,CACT,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,UAAU,EACvB,KAAK,CAAC,EAAE,MAAM,KACX,OAAO;UAKD,MAAM,MAAM,EAAE;;mCAEd,WAAW,GAAG,2BAA2B;qCAjNJ,uBAAuB;iCAMF,uBAAuB;oCAAvB,uBAAuB;gCAAvB,uBAAuB"}
1
+ {"version":3,"file":"storage-test-utils.d.ts","sourceRoot":"","sources":["storage-test-utils.js"],"names":[],"mappings":"AA0BO,yCAHI,MAAM,UACN,MAAM,6GAGC;AAElB;;;GAGG;AACH;;;;;EAEG;;eAMU,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO;mBAEzB,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM;;AA8CjC,8CAtBuB,GAAG,KAAK,GAAG,CAsB+B;AAa1D,+BAFM,CAAC;oBASA,CAAC;;;;;EA6Bd;AAWM,6CAJI,MAAM,gBACN,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,oBAC1C;IAAE,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAA;CAAE;;;;;oBAwD3B,CAAC;;;;UA4FT,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;;sBAd3B,MAAM,KACJ,MAAM,EAAE;;0BA/FP,cAAc;;;8IAAd,cAAc;;;;EAkH7B;AAoBM,4CADO,oBAAoB,CA2BjC;AAUM,yCAPI,iBAAiB,OAAO,CAAC,WACzB,oBAAoB,GAAG,cAAc,QACrC,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAC9D,CAAK;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,EAAE,CAAC,GAAG;IAChD,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;CACpC,iBAkDL;6BAtGa,UAAU,CAAC,OAAO,kBAAkB,CAAC;;;;;;;;aAIrC,CACT,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,UAAU,EACvB,KAAK,CAAC,EAAE,MAAM,KACX,OAAO;UAKD,MAAM,MAAM,EAAE;;mCAEd,WAAW,GAAG,2BAA2B;qCAxTJ,uBAAuB;oCAOU,uBAAuB;iCAAvB,uBAAuB;sCACxE,KAAK;gCAD4C,uBAAuB"}
@@ -2,15 +2,17 @@
2
2
  import { Fail } from '@endo/errors';
3
3
  import { Far } from '@endo/far';
4
4
  import { makeMarshal, Remotable } from '@endo/marshal';
5
- import { unmarshalFromVstorage } from './marshal.js';
6
5
  import { makeTracer } from './debug.js';
6
+ import { NonNullish } from './errors.js';
7
7
  import { isStreamCell, makeChainStorageRoot } from './lib-chainStorage.js';
8
+ import { unmarshalFromVstorage } from './marshal/board-client-utils.js';
8
9
  import { bindAllMethods } from './method-tools.js';
9
10
  import { eventLoopIteration } from './testing-utils.js';
10
11
 
11
12
  /**
12
13
  * @import {TotalMap} from './types.js';
13
- * @import {Marshaller, StorageEntry, StorageMessage, StorageNode} from './lib-chainStorage.js';
14
+ * @import {Marshaller, StorageEntry, StorageMessage, StorageNode, StreamCell} from './lib-chainStorage.js';
15
+ * @import {ExecutionContext} from 'ava';
14
16
  */
15
17
 
16
18
  const trace = makeTracer('StorTU', false);
@@ -33,6 +35,16 @@ export const defaultMarshaller = makeMarshal(undefined, slotToRemotable, {
33
35
  serializeBodyFormat: 'smallcaps',
34
36
  });
35
37
 
38
+ /**
39
+ * Serialize/deserialize functions using {@link defaultMarshaller}
40
+ */
41
+ export const defaultSerializer = {
42
+ /** @type {(text: string) => unknown} */
43
+ parse: txt => defaultMarshaller.fromCapData(JSON.parse(txt)),
44
+ /** @type {(obj: any) => string} */
45
+ stringify: obj => JSON.stringify(defaultMarshaller.toCapData(obj)),
46
+ };
47
+
36
48
  /**
37
49
  * A deserializer which produces slot strings instead of Remotables, so if `a =
38
50
  * Far('iface')`, and serializing `{ a }` into `capData` assigned it slot
@@ -77,6 +89,55 @@ const makeSlotStringUnserialize = () => {
77
89
  };
78
90
  export const slotStringUnserialize = makeSlotStringUnserialize();
79
91
 
92
+ /**
93
+ * @example to iterate over storage messages
94
+ *
95
+ * ```js
96
+ * const q = makeAsyncQueue<StorageMessage>();
97
+ * const storage = makeFakeStorageKit('published', undefined, { eachMessage: q.enqueue });
98
+ * for (const message of q.iterable) { ... }
99
+ * ```
100
+ *
101
+ * @template T
102
+ */
103
+ export const makeAsyncQueue = () => {
104
+ /** @type {T[]} */
105
+ const queue = [];
106
+ /** @type {null | ((r: undefined) => void)} */
107
+ let resolve = null;
108
+ let done = false;
109
+
110
+ /** @param {T} data */
111
+ const enqueue = data => {
112
+ queue.push(data);
113
+ if (resolve) {
114
+ resolve(undefined);
115
+ resolve = null;
116
+ }
117
+ };
118
+
119
+ let alreadyIterating = false;
120
+ const iterable = {
121
+ async *[Symbol.asyncIterator]() {
122
+ assert(!alreadyIterating, `AsyncQueue is already iterating`);
123
+ alreadyIterating = true;
124
+ await null;
125
+ while (!done) {
126
+ if (queue.length === 0) {
127
+ await new Promise(r => (resolve = r));
128
+ }
129
+
130
+ while (queue.length > 0) {
131
+ yield queue.shift();
132
+ }
133
+ }
134
+ },
135
+ };
136
+
137
+ const cancel = () => (done = true);
138
+ return harden({ enqueue, iterable, cancel });
139
+ };
140
+
80
141
  /**
81
142
  * For testing, creates a chainStorage root node over an in-memory map and
82
143
  * exposes both the map and the sequence of received messages. The `sequence`
@@ -84,11 +145,24 @@ export const slotStringUnserialize = makeSlotStringUnserialize();
84
145
  *
85
146
  * @param {string} rootPath
86
147
  * @param {Parameters<typeof makeChainStorageRoot>[2]} [rootOptions]
148
+ * @param {{ eachMessage?: (m: StorageMessage) => void }} [spyOpts]
87
149
  */
88
- export const makeFakeStorageKit = (rootPath, rootOptions) => {
150
+ export const makeFakeStorageKit = (
151
+ rootPath,
152
+ rootOptions,
153
+ { eachMessage } = {},
154
+ ) => {
89
155
  const resolvedOptions = { sequence: true, ...rootOptions };
90
156
  /** @type {TotalMap<string, string>} */
91
157
  const data = new Map();
158
+ let currentBlockHeight = 0;
159
+
160
+ const updateNewCellBlockHeight = (blockHeight = currentBlockHeight + 1) => {
161
+ blockHeight > currentBlockHeight ||
162
+ Fail`blockHeight ${blockHeight} must be greater than ${currentBlockHeight}`;
163
+ currentBlockHeight = blockHeight;
164
+ };
165
+
92
166
  /** @param {string} prefix */
93
167
  const getChildEntries = prefix => {
94
168
  assert(prefix.endsWith('.'));
@@ -114,6 +188,7 @@ export const makeFakeStorageKit = (rootPath, rootOptions) => {
114
188
  /** @param {StorageMessage} message */
115
189
  message => {
116
190
  messages.push(message);
191
+ eachMessage?.(message);
117
192
  switch (message.method) {
118
193
  case 'getStoreKey': {
119
194
  const [key] = message.args;
@@ -147,7 +222,7 @@ export const makeFakeStorageKit = (rootPath, rootOptions) => {
147
222
  data.delete(key);
148
223
  }
149
224
  }
150
- break;
225
+ return true;
151
226
  }
152
227
  case 'append': {
153
228
  trace('toStorage append', message);
@@ -155,8 +230,10 @@ export const makeFakeStorageKit = (rootPath, rootOptions) => {
155
230
  const newEntries = message.args;
156
231
  for (const [key, value] of newEntries) {
157
232
  value != null || Fail`attempt to append with no value`;
158
- // In the absence of block boundaries, everything goes in a single StreamCell.
159
- const oldVal = data.get(key);
233
+
234
+ /** @type {string | undefined} */
235
+ let oldVal = data.get(key);
236
+ /** @type {StreamCell | undefined} */
160
237
  let streamCell;
161
238
  if (oldVal != null) {
162
239
  try {
@@ -165,17 +242,26 @@ export const makeFakeStorageKit = (rootPath, rootOptions) => {
165
242
  } catch (_err) {
166
243
  streamCell = undefined;
167
244
  }
245
+ // StreamCells reset at block boundaries.
246
+ if (
247
+ streamCell &&
248
+ Number(streamCell.blockHeight) !== currentBlockHeight
249
+ ) {
250
+ streamCell = undefined;
251
+ oldVal = undefined;
252
+ }
168
253
  }
254
+
169
255
  if (streamCell === undefined) {
170
256
  streamCell = {
171
- blockHeight: '0',
257
+ blockHeight: String(currentBlockHeight),
172
258
  values: oldVal != null ? [oldVal] : [],
173
259
  };
174
260
  }
175
261
  streamCell.values.push(value);
176
262
  data.set(key, JSON.stringify(streamCell));
177
263
  }
178
- break;
264
+ return true;
179
265
  }
180
266
  case 'size':
181
267
  // Intentionally incorrect because it counts non-child descendants,
@@ -189,10 +275,27 @@ export const makeFakeStorageKit = (rootPath, rootOptions) => {
189
275
  },
190
276
  );
191
277
  const rootNode = makeChainStorageRoot(toStorage, rootPath, resolvedOptions);
278
+
279
+ /**
280
+ * Get the values at a sequence node
281
+ *
282
+ * @param {string} path
283
+ * @returns {string[]}
284
+ */
285
+ const getValues = path => {
286
+ assert(resolvedOptions.sequence);
287
+ const nodeData = data.get(path);
288
+ assert(nodeData, `no data at path ${path}`);
289
+ const wrapper = JSON.parse(nodeData);
290
+ return wrapper.values;
291
+ };
292
+
192
293
  return {
193
294
  rootNode,
194
- // eslint-disable-next-line object-shorthand
295
+
195
296
  data: /** @type {Map<string, string>} */ (data),
297
+ updateNewCellBlockHeight,
298
+ getValues,
196
299
  messages,
197
300
  toStorage,
198
301
  };
@@ -234,9 +337,7 @@ export const makeMockChainStorageRoot = () => {
234
337
  getBody: (path, marshaller = defaultMarshaller, index = -1) => {
235
338
  data.size || Fail`no data in storage`;
236
339
  /**
237
- * @type {ReturnType<
238
- * typeof import('@endo/marshal').makeMarshal
239
- * >['fromCapData']}
340
+ * @type {ReturnType<typeof makeMarshal>['fromCapData']}
240
341
  */
241
342
  const fromCapData = (...args) =>
242
343
  Reflect.apply(marshaller.fromCapData, marshaller, args);
@@ -247,33 +348,51 @@ export const makeMockChainStorageRoot = () => {
247
348
  };
248
349
 
249
350
  /**
250
- * @param {import('ava').ExecutionContext<unknown>} t
351
+ * @param {ExecutionContext<unknown>} t
251
352
  * @param {MockChainStorageRoot | FakeStorageKit} storage
252
353
  * @param {({ note: string } | { node: string; owner: string }) &
253
- * ({ pattern: string; replacement: string } | {})} opts
354
+ * ({ pattern: string; replacement: string } | {}) & {
355
+ * showValue?: (v: string) => unknown;
356
+ * }} opts
254
357
  */
255
358
  export const documentStorageSchema = async (t, storage, opts) => {
256
359
  // chainStorage publication is unsynchronized
257
360
  await eventLoopIteration();
258
361
 
362
+ const getLast = (/** @type {string} */ cell) =>
363
+ JSON.parse(cell).values.at(-1) || assert.fail();
364
+ const { showValue = s => s } = opts;
365
+ /** @type {(d: Map<string, string>, k: string) => unknown} */
366
+ const getBodyDefault = (d, k) => showValue(getLast(NonNullish(d.get(k))));
367
+
259
368
  const [keys, getBody] =
260
369
  'keys' in storage
261
370
  ? [storage.keys(), (/** @type {string} */ k) => storage.getBody(k)]
262
- : [storage.data.keys(), (/** @type {string} */ k) => storage.data.get(k)];
371
+ : [
372
+ storage.data.keys(),
373
+ (/** @type {string} */ k) => getBodyDefault(storage.data, k),
374
+ ];
263
375
 
264
376
  const { pattern, replacement } =
265
377
  'pattern' in opts
266
378
  ? opts
267
379
  : { pattern: 'mockChainStorageRoot.', replacement: 'published.' };
268
- const illustration = [...keys].sort().map(
380
+
381
+ const pruned = [...keys]
382
+ .sort()
383
+ .filter(
384
+ 'node' in opts
385
+ ? key =>
386
+ key
387
+ .replace(pattern, replacement)
388
+ .startsWith(`published.${opts.node}`)
389
+ : _entry => true,
390
+ );
391
+
392
+ const illustration = pruned.map(
269
393
  /** @type {(k: string) => [string, unknown]} */
270
394
  key => [key.replace(pattern, replacement), getBody(key)],
271
395
  );
272
- const pruned = illustration.filter(
273
- 'node' in opts
274
- ? ([key, _]) => key.startsWith(`published.${opts.node}`)
275
- : _entry => true,
276
- );
277
396
 
278
397
  const note =
279
398
  'note' in opts
@@ -283,5 +402,5 @@ export const documentStorageSchema = async (t, storage, opts) => {
283
402
  The example below illustrates the schema of the data published there.
284
403
 
285
404
  See also board marshalling conventions (_to appear_).`;
286
- t.snapshot(pruned, note + boilerplate);
405
+ t.snapshot(illustration, note + boilerplate);
287
406
  };
package/src/tagged.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- /** @file adapted from https://raw.githubusercontent.com/sindresorhus/type-fest/main/source/opaque.d.ts */
1
+ /** @file adapted from https://raw.githubusercontent.com/sindresorhus/type-fest/main/source/tagged.d.ts */
2
+
3
+ // different name to avoid confusion with pass-style "tagged"
4
+ export { Tagged as TypeTag };
2
5
 
3
6
  declare const tag: unique symbol;
4
7
 
@@ -28,7 +28,7 @@ const stringOrTag = value => {
28
28
  };
29
29
  /**
30
30
  * @param {MapStore} store
31
- * @returns {object} tree of the contents of the storeÂ
31
+ * @returns {object} tree of the contents of the store
32
32
  */
33
33
  export const inspectMapStore = store => {
34
34
  /** @type {Record<string, unknown>} */
@@ -0,0 +1,2 @@
1
+ export function makeTempDirFactory(tmp: Pick<typeof import("tmp"), "dirSync">): (prefix?: string) => [dirName: string, cleanup: () => void];
2
+ //# sourceMappingURL=tmpDir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmpDir.d.ts","sourceRoot":"","sources":["tmpDir.js"],"names":[],"mappings":"AAMO,wCAFI,IAAI,CAAC,oBAAa,EAAE,SAAS,CAAC,aAGlB,MAAM,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,CASvE"}
package/src/tmpDir.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Create a `tmpDir` function that synchronously creates a temporary directory
3
+ * and a function for deleting it along with any added contents.
4
+ *
5
+ * @param {Pick<import('tmp'), 'dirSync'>} tmp
6
+ */
7
+ export const makeTempDirFactory = tmp => {
8
+ /** @type {(prefix?: string) => [dirName: string, cleanup: () => void]} */
9
+ const tmpDir = prefix => {
10
+ const { name, removeCallback } = tmp.dirSync({
11
+ prefix,
12
+ unsafeCleanup: true,
13
+ });
14
+ return [name, removeCallback];
15
+ };
16
+ return tmpDir;
17
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["tokens.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;2BAEc,KAAK,GAAG,KAAK;AAA3B,4CAA4C;AAE5C;;;GAGG;AACH,yBAAkB"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["tokens.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;2BAEc,KAAK,GAAG,KAAK;AAA3B,4CAA4C;AAE5C;;;GAGG;AACH,mBAAY,KAAK,CAAC"}