@agoric/internal 0.2.2-pismo-dev-0d5327f.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/package.json +15 -7
  3. package/src/action-types.d.ts +16 -0
  4. package/src/action-types.d.ts.map +1 -0
  5. package/src/action-types.js +17 -0
  6. package/src/batched-deliver.d.ts +15 -0
  7. package/src/batched-deliver.d.ts.map +1 -0
  8. package/src/batched-deliver.js +50 -0
  9. package/src/callback.d.ts +23 -0
  10. package/src/callback.d.ts.map +1 -0
  11. package/src/callback.js +322 -0
  12. package/src/chain-storage-paths.d.ts +16 -0
  13. package/src/chain-storage-paths.d.ts.map +1 -0
  14. package/src/chain-storage-paths.js +17 -0
  15. package/src/config.d.ts +25 -0
  16. package/src/config.d.ts.map +1 -0
  17. package/src/config.js +20 -3
  18. package/src/debug.d.ts +2 -0
  19. package/src/debug.d.ts.map +1 -0
  20. package/src/debug.js +41 -0
  21. package/src/index.d.ts +6 -0
  22. package/src/index.d.ts.map +1 -0
  23. package/src/index.js +7 -2
  24. package/src/lib-chainStorage.d.ts +179 -0
  25. package/src/lib-chainStorage.d.ts.map +1 -0
  26. package/src/lib-chainStorage.js +304 -0
  27. package/src/magic-cookie-test-only.d.ts +2 -0
  28. package/src/magic-cookie-test-only.d.ts.map +1 -0
  29. package/src/magic-cookie-test-only.js +11 -0
  30. package/src/method-tools.d.ts +3 -0
  31. package/src/method-tools.d.ts.map +1 -0
  32. package/src/method-tools.js +110 -0
  33. package/src/node/buffer-line-transform.d.ts +41 -0
  34. package/src/node/buffer-line-transform.d.ts.map +1 -0
  35. package/src/node/buffer-line-transform.js +119 -0
  36. package/src/node/createBundles.d.ts +4 -0
  37. package/src/node/createBundles.d.ts.map +1 -0
  38. package/src/node/createBundles.js +80 -0
  39. package/src/node/fs-stream.d.ts +8 -0
  40. package/src/node/fs-stream.d.ts.map +1 -0
  41. package/src/node/fs-stream.js +105 -0
  42. package/src/node/shutdown.d.ts +6 -0
  43. package/src/node/shutdown.d.ts.map +1 -0
  44. package/src/node/shutdown.js +81 -0
  45. package/src/priority-senders.d.ts +31 -0
  46. package/src/priority-senders.d.ts.map +1 -0
  47. package/src/priority-senders.js +104 -0
  48. package/src/queue.d.ts +2 -0
  49. package/src/queue.d.ts.map +1 -0
  50. package/src/queue.js +58 -0
  51. package/src/scratch.d.ts +19 -0
  52. package/src/scratch.d.ts.map +1 -0
  53. package/src/scratch.js +52 -0
  54. package/src/storage-test-utils.d.ts +99 -0
  55. package/src/storage-test-utils.d.ts.map +1 -0
  56. package/src/storage-test-utils.js +228 -0
  57. package/src/testing-utils.d.ts +2 -0
  58. package/src/testing-utils.d.ts.map +1 -0
  59. package/src/testing-utils.js +14 -0
  60. package/src/typeGuards.d.ts +2 -0
  61. package/src/typeGuards.d.ts.map +1 -0
  62. package/src/typeGuards.js +5 -0
  63. package/src/types.d.ts +20 -0
  64. package/src/upgrade-api.d.ts +8 -0
  65. package/src/upgrade-api.d.ts.map +1 -0
  66. package/src/upgrade-api.js +41 -0
  67. package/src/utils.d.ts +67 -0
  68. package/src/utils.d.ts.map +1 -0
  69. package/src/utils.js +232 -124
@@ -0,0 +1,41 @@
1
+ // @ts-check
2
+ // @jessie-check
3
+ import { isObject } from '@endo/marshal';
4
+
5
+ /**
6
+ * @typedef {{ name: string, upgradeMessage: string, incarnationNumber: number }} DisconnectionObject
7
+ */
8
+
9
+ /**
10
+ * Makes an Error-like object for use as the rejection value of promises
11
+ * abandoned by upgrade.
12
+ *
13
+ * @param {string} upgradeMessage
14
+ * @param {number} toIncarnationNumber
15
+ * @returns {DisconnectionObject}
16
+ */
17
+ export const makeUpgradeDisconnection = (upgradeMessage, toIncarnationNumber) =>
18
+ harden({
19
+ name: 'vatUpgraded',
20
+ upgradeMessage,
21
+ incarnationNumber: toIncarnationNumber,
22
+ });
23
+ harden(makeUpgradeDisconnection);
24
+
25
+ // TODO: Simplify once we have @endo/patterns (or just export the shape).
26
+ // const upgradeDisconnectionShape = harden({
27
+ // name: 'vatUpgraded',
28
+ // upgradeMessage: M.string(),
29
+ // incarnationNumber: M.number(),
30
+ // });
31
+ // const isUpgradeDisconnection = err => matches(err, upgradeDisconnectionShape);
32
+ /**
33
+ * @param {any} err
34
+ * @returns {err is DisconnectionObject}
35
+ */
36
+ export const isUpgradeDisconnection = err =>
37
+ isObject(err) &&
38
+ err.name === 'vatUpgraded' &&
39
+ typeof err.upgradeMessage === 'string' &&
40
+ typeof err.incarnationNumber === 'number';
41
+ harden(isUpgradeDisconnection);
package/src/utils.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ export const BASIS_POINTS: 10000n;
2
+ export function fromUniqueEntries<K, V>(allEntries: Iterable<[K, V]>): {};
3
+ export function objectMap<O extends Record<string, any>, R>(original: O, mapFn: (value: O[keyof O], key: keyof O) => R): { [P in keyof O]: R; };
4
+ export function listDifference(leftNames: Array<string | symbol>, rightNames: Array<string | symbol>): (string | symbol)[];
5
+ export function throwLabeled(innerErr: Error, label: string | number, ErrorConstructor?: ErrorConstructor | undefined): never;
6
+ export function applyLabelingError<A, R>(func: (...args: A[]) => R, args: A[], label?: string | number | undefined): R;
7
+ /**
8
+ * @template T
9
+ * @typedef {{[KeyType in keyof T]: T[KeyType]} & {}} Simplify
10
+ * flatten the type output to improve type hints shown in editors
11
+ * https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts
12
+ */
13
+ /**
14
+ * @typedef {(...args: any[]) => any} Callable
15
+ */
16
+ /**
17
+ * @template {{}} T
18
+ * @typedef {{ [K in keyof T]: T[K] extends Callable ? T[K] : DeeplyAwaited<T[K]> }} DeeplyAwaitedObject
19
+ */
20
+ /**
21
+ * @template T
22
+ * @typedef {T extends PromiseLike<any> ? Awaited<T> : T extends {} ? Simplify<DeeplyAwaitedObject<T>> : Awaited<T>} DeeplyAwaited
23
+ */
24
+ /**
25
+ * A more constrained version of {deeplyFulfilled} for type safety until
26
+ * https://github.com/endojs/endo/issues/1257
27
+ * Useful in starting contracts that need all terms to be fulfilled
28
+ * in order to be durable.
29
+ *
30
+ * @type {<T extends {}>(unfulfilledTerms: T) => Promise<DeeplyAwaited<T>>}
31
+ */
32
+ export const deeplyFulfilledObject: <T extends {}>(unfulfilledTerms: T) => Promise<DeeplyAwaited<T>>;
33
+ export function makeMeasureSeconds(currentTimeMillisec: typeof import('perf_hooks').performance.now): <T>(fn: () => Promise<T>) => Promise<{
34
+ result: T;
35
+ duration: number;
36
+ }>;
37
+ export function makeAggregateError(errors: Error[], message?: string | undefined): Error;
38
+ export function PromiseAllOrErrors<T>(items: readonly (T | PromiseLike<T>)[]): Promise<T[]>;
39
+ /**
40
+ * @type {<T>(
41
+ * trier: () => Promise<T>,
42
+ * finalizer: (error?: unknown) => Promise<void>,
43
+ * ) => Promise<T>}
44
+ */ export const aggregateTryFinally: <T>(trier: () => Promise<T>, finalizer: (error?: unknown) => Promise<void>) => Promise<T>;
45
+ export function assertAllDefined<T extends Record<string, unknown>>(obj: T): asserts obj is AllDefined<T>;
46
+ export const forever: AsyncIterable<undefined>;
47
+ export function whileTrue<T>(produce: () => T): AsyncIterable<Awaited<T>>;
48
+ export function untilTrue<T>(produce: () => T): AsyncIterable<Awaited<T>>;
49
+ /** @type { <X, Y>(xs: X[], ys: Y[]) => [X, Y][]} */
50
+ export const zip: <X, Y>(xs: X[], ys: Y[]) => [X, Y][];
51
+ /** @type { <T extends Record<string, ERef<any>>>(obj: T) => Promise<{ [K in keyof T]: Awaited<T[K]>}> } */
52
+ export const allValues: <T extends Record<string, any>>(obj: T) => Promise<{ [K in keyof T]: Awaited<T[K]>; }>;
53
+ export function synchronizedTee<T = unknown>(sourceStream: AsyncIterator<T, void, void>, readerCount: number): AsyncGenerator<T, void, void>[];
54
+ /**
55
+ * <T>
56
+ */
57
+ export type ERef<T> = import('@endo/eventual-send').ERef<T>;
58
+ /**
59
+ * flatten the type output to improve type hints shown in editors
60
+ * https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts
61
+ */
62
+ export type Simplify<T> = { [KeyType_1 in keyof T]: T[KeyType_1]; };
63
+ export type Callable = (...args: any[]) => any;
64
+ export type DeeplyAwaitedObject<T extends {}> = { [K in keyof T]: T[K] extends Callable ? T[K] : DeeplyAwaited<T[K]>; };
65
+ export type DeeplyAwaited<T> = T extends PromiseLike<any> ? Awaited<T> : T extends {} ? Simplify<DeeplyAwaitedObject<T>> : Awaited<T>;
66
+ export type AllDefined<T extends Record<string, unknown>> = { [P in keyof T]: Exclude<T[P], undefined>; };
67
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["utils.js"],"names":[],"mappings":"AAcA,kCAAoC;AAa7B,0EAcN;AA0CM,gJAIN;AAOM,0CAHI,MAAM,MAAM,GAAG,MAAM,CAAC,cACtB,MAAM,MAAM,GAAG,MAAM,CAAC,uBAKhC;AASM,uCALI,KAAK,SACL,MAAM,GAAC,MAAM,oDAEX,KAAK,CAYjB;AAUM,uHAyBN;AAGD;;;;;GAKG;AAEH;;GAEG;AAEH;;;GAGG;AAEH;;;GAGG;AAEH;;;;;;;GAOG;AACH,qGAGE;AAUK,wDAHI,cAAc,YAAY,EAAE,WAAW,CAAC,GAAG;;cACmB,MAAM;GAW9E;AAMM,2CAHI,KAAK,EAAE,uCAcjB;AAOM,4FAeN;AAED;;;;;GAKG,CAAC,mFAFoB,OAAO,KAAK,QAAQ,IAAI,CAAC,gBAY7C;AAiBG,0GAUN;AAQD,+CAAoD;AAU7C,0EAUH;AAUG,0EAaH;AAEJ,oDAAoD;AACpD,uDAAqE;AAErE,2GAA2G;AAC3G,+GAIE;AAWK,sGAFI,MAAM,mCA4FhB;;;;sBAlbyB,OAAO,qBAAqB,EAAE,IAAI,CAAC,CAAC,CAAC;;;;;;iCAkJxC,GAAG,EAAE,KAAK,GAAG;;+BAUvB,CAAC,SAAS,YAAY,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,SAAS,oBAAoB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC"}
package/src/utils.js CHANGED
@@ -1,19 +1,25 @@
1
1
  // @ts-check
2
- import { E } from '@endo/eventual-send';
2
+ // @jessie-check
3
+
4
+ import { E } from '@endo/far';
3
5
  import { deeplyFulfilled, isObject } from '@endo/marshal';
4
- import { isPromise } from '@endo/promise-kit';
6
+ import { isPromise, makePromiseKit } from '@endo/promise-kit';
7
+ import { makeQueue } from '@endo/stream';
8
+ import { asyncGenerate, makeSet } from 'jessie.js';
9
+
10
+ const { entries, fromEntries, keys, values } = Object;
11
+ const { ownKeys } = Reflect;
5
12
 
6
- /** @typedef {import('@endo/marshal/src/types').Remotable} Remotable */
13
+ const { details: X, quote: q, Fail } = assert;
7
14
 
8
- const { getPrototypeOf, create, entries, fromEntries } = Object;
9
- const { ownKeys, apply } = Reflect;
15
+ export const BASIS_POINTS = 10_000n;
10
16
 
11
- const { details: X, quote: q } = assert;
17
+ /** @template T @typedef {import('@endo/eventual-send').ERef<T>} ERef<T> */
12
18
 
13
19
  /**
14
20
  * Throws if multiple entries use the same property name. Otherwise acts
15
- * like `Object.fromEntries`. Use it to protect from property names
16
- * computed from user-provided data.
21
+ * like `Object.fromEntries` but hardens the result.
22
+ * Use it to protect from property names computed from user-provided data.
17
23
  *
18
24
  * @template K,V
19
25
  * @param {Iterable<[K,V]>} allEntries
@@ -21,18 +27,18 @@ const { details: X, quote: q } = assert;
21
27
  */
22
28
  export const fromUniqueEntries = allEntries => {
23
29
  const entriesArray = [...allEntries];
24
- const result = fromEntries(entriesArray);
30
+ const result = harden(fromEntries(entriesArray));
25
31
  if (ownKeys(result).length === entriesArray.length) {
26
32
  return result;
27
33
  }
28
- const names = new Set();
34
+ const names = makeSet();
29
35
  for (const [name, _] of entriesArray) {
30
36
  if (names.has(name)) {
31
- assert.fail(X`collision on property name ${q(name)}: ${entriesArray}`);
37
+ Fail`collision on property name ${q(name)}: ${entriesArray}`;
32
38
  }
33
39
  names.add(name);
34
40
  }
35
- assert.fail(X`internal: failed to create object from unique entries`);
41
+ throw Fail`internal: failed to create object from unique entries`;
36
42
  };
37
43
  harden(fromUniqueEntries);
38
44
 
@@ -82,8 +88,12 @@ export const objectMap = (original, mapFn) => {
82
88
  };
83
89
  harden(objectMap);
84
90
 
91
+ /**
92
+ * @param {Array<string | symbol>} leftNames
93
+ * @param {Array<string | symbol>} rightNames
94
+ */
85
95
  export const listDifference = (leftNames, rightNames) => {
86
- const rightSet = new Set(rightNames);
96
+ const rightSet = makeSet(rightNames);
87
97
  return leftNames.filter(name => !rightSet.has(name));
88
98
  };
89
99
  harden(listDifference);
@@ -91,7 +101,7 @@ harden(listDifference);
91
101
  /**
92
102
  * @param {Error} innerErr
93
103
  * @param {string|number} label
94
- * @param {ErrorConstructor=} ErrorConstructor
104
+ * @param {ErrorConstructor} [ErrorConstructor]
95
105
  * @returns {never}
96
106
  */
97
107
  export const throwLabeled = (innerErr, label, ErrorConstructor = undefined) => {
@@ -125,110 +135,54 @@ export const applyLabelingError = (func, args, label = undefined) => {
125
135
  throwLabeled(err, label);
126
136
  }
127
137
  if (isPromise(result)) {
128
- // @ts-expect-error If result is a rejected promise, this will
129
- // return a promise with a different rejection reason. But this
130
- // confuses TypeScript because it types that case as `Promise<never>`
131
- // which is cool for a promise that will never fulfll.
132
- // But TypeScript doesn't understand that this will only happen
133
- // when `result` was a rejected promise. In only this case `R`
134
- // should already allow `Promise<never>` as a subtype.
135
- return E.when(result, undefined, reason => throwLabeled(reason, label));
138
+ // If result is a rejected promise, this will return a promise with a
139
+ // different rejection reason. But this confuses TypeScript because it types
140
+ // that case as `Promise<never>` which is cool for a promise that will never
141
+ // fulfill. But TypeScript doesn't understand that this will only happen
142
+ // when `result` was a rejected promise. In only this case `R` should
143
+ // already allow `Promise<never>` as a subtype.
144
+ /** @type {unknown} */
145
+ const relabeled = E.when(result, undefined, reason =>
146
+ throwLabeled(reason, label),
147
+ );
148
+ return /** @type {R} */ (relabeled);
136
149
  } else {
137
150
  return result;
138
151
  }
139
152
  };
140
153
  harden(applyLabelingError);
141
154
 
142
- const compareStringified = (left, right) => {
143
- left = String(left);
144
- right = String(right);
145
- // eslint-disable-next-line no-nested-ternary
146
- return left < right ? -1 : left > right ? 1 : 0;
147
- };
148
-
149
155
  /**
150
- * @param {object} obj
151
- * @returns {(string|symbol)[]}
156
+ * @template T
157
+ * @typedef {{[KeyType in keyof T]: T[KeyType]} & {}} Simplify
158
+ * flatten the type output to improve type hints shown in editors
159
+ * https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts
152
160
  */
153
- export const getMethodNames = obj => {
154
- const result = [];
155
- while (obj !== null && obj !== Object.prototype) {
156
- const mNames = ownKeys(obj).filter(name => typeof obj[name] === 'function');
157
- result.push(...mNames);
158
- obj = getPrototypeOf(obj);
159
- }
160
- result.sort(compareStringified);
161
- return harden(result);
162
- };
163
- harden(getMethodNames);
164
161
 
165
162
  /**
166
- * TODO This function exists only to ease the
167
- * https://github.com/Agoric/agoric-sdk/pull/5970 transition, from all methods
168
- * being own properties to methods being inherited from a common prototype.
169
- * This transition breaks two patterns used in prior code: autobinding,
170
- * and enumerating methods by enumerating own properties. For both, the
171
- * preferred repairs are
172
- * * autobinding: Replace, for example,
173
- * `foo(obj.method)` with `foo(arg => `obj.method(arg))`. IOW, stop relying
174
- * on expressions like `obj.method` to extract a method still bound to the
175
- * state of `obj` because, for virtual and durable objects,
176
- * they no longer will after #5970.
177
- * * method enumeration: Replace, for example
178
- * `Reflect.ownKeys(obj)` with `getMethodNames(obj)`.
179
- *
180
- * Once all problematic cases have been converted in this manner, this
181
- * `bindAllMethods` hack can and TODO should be deleted. However, we currently
182
- * have no reliable static way to track down and fix all autobinding sites.
183
- * For those objects that have not yet been fully repaired by the above two
184
- * techniques, `bindAllMethods` creates an object that acts much like the
185
- * pre-#5970 objects, with all their methods as instance-bound own properties.
186
- * It does this by making a new object inheriting from `obj` where the new
187
- * object has bound own methods overridding all the methods it would have
188
- * inherited from `obj`.
189
- *
190
- * @param {Remotable} obj
191
- * @returns {Remotable}
163
+ * @typedef {(...args: any[]) => any} Callable
192
164
  */
193
- export const bindAllMethods = obj =>
194
- harden(
195
- create(
196
- obj,
197
- fromEntries(
198
- getMethodNames(obj).map(name => [
199
- name,
200
- {
201
- value: (...args) => apply(obj[name], obj, args),
202
- enumerable: true,
203
- },
204
- ]),
205
- ),
206
- ),
207
- );
208
- harden(bindAllMethods);
209
165
 
210
166
  /**
211
167
  * @template {{}} T
212
- * @typedef {{ [K in keyof T]: DeeplyAwaited<T[K]> }} DeeplyAwaitedObject
168
+ * @typedef {{ [K in keyof T]: T[K] extends Callable ? T[K] : DeeplyAwaited<T[K]> }} DeeplyAwaitedObject
213
169
  */
214
170
 
215
171
  /**
216
- * Caveats:
217
- * - doesn't recur within Promise results
218
- * - resulting type has wrapper in its name
219
- *
220
172
  * @template T
221
- * @typedef {T extends PromiseLike ? Awaited<T> : T extends {} ? DeeplyAwaitedObject<T> : Awaited<T>} DeeplyAwaited
173
+ * @typedef {T extends PromiseLike<any> ? Awaited<T> : T extends {} ? Simplify<DeeplyAwaitedObject<T>> : Awaited<T>} DeeplyAwaited
222
174
  */
223
175
 
224
176
  /**
225
- * A more constrained version of {deeplyFulfilled} for type safety until https://github.com/endojs/endo/issues/1257
226
- * Useful in starting contracts that need all terms to be fulfilled in order to be durable.
177
+ * A more constrained version of {deeplyFulfilled} for type safety until
178
+ * https://github.com/endojs/endo/issues/1257
179
+ * Useful in starting contracts that need all terms to be fulfilled
180
+ * in order to be durable.
227
181
  *
228
- * @type {<T extends {}>(unfulfilledTerms: T) => import('@endo/eventual-send').ERef<DeeplyAwaited<T>>}
182
+ * @type {<T extends {}>(unfulfilledTerms: T) => Promise<DeeplyAwaited<T>>}
229
183
  */
230
- export const deeplyFulfilledObject = obj => {
231
- assert(isObject(obj), 'param must be an object');
184
+ export const deeplyFulfilledObject = async obj => {
185
+ isObject(obj) || Fail`param must be an object`;
232
186
  return deeplyFulfilled(obj);
233
187
  };
234
188
 
@@ -241,6 +195,7 @@ export const deeplyFulfilledObject = obj => {
241
195
  * @returns {<T>(fn: () => Promise<T>) => Promise<{ result: T, duration: number }>}
242
196
  */
243
197
  export const makeMeasureSeconds = currentTimeMillisec => {
198
+ /** @param {() => any} fn */
244
199
  const measureSeconds = async fn => {
245
200
  const t0 = currentTimeMillisec();
246
201
  const result = await fn();
@@ -255,7 +210,7 @@ export const makeMeasureSeconds = currentTimeMillisec => {
255
210
  * @param {string} [message]
256
211
  */
257
212
  export const makeAggregateError = (errors, message) => {
258
- const err = new Error(message);
213
+ const err = Error(message);
259
214
  Object.defineProperties(err, {
260
215
  name: {
261
216
  value: 'AggregateError',
@@ -269,11 +224,11 @@ export const makeAggregateError = (errors, message) => {
269
224
 
270
225
  /**
271
226
  * @template T
272
- * @param {readonly (T | PromiseLike<T>)[]} values
227
+ * @param {readonly (T | PromiseLike<T>)[]} items
273
228
  * @returns {Promise<T[]>}
274
229
  */
275
- export const PromiseAllOrErrors = async values => {
276
- return Promise.allSettled(values).then(results => {
230
+ export const PromiseAllOrErrors = async items => {
231
+ return Promise.allSettled(items).then(results => {
277
232
  const errors = /** @type {PromiseRejectedResult[]} */ (
278
233
  results.filter(({ status }) => status === 'rejected')
279
234
  ).map(result => result.reason);
@@ -307,37 +262,190 @@ export const PromiseAllOrErrors = async values => {
307
262
  );
308
263
 
309
264
  /**
310
- * @param {import("fs").ReadStream | import("fs").WriteStream} stream
311
- * @returns {Promise<void>}
265
+ * @template {Record<string, unknown>} T
266
+ * @typedef {{[P in keyof T]: Exclude<T[P], undefined>;}} AllDefined
267
+ */
268
+
269
+ /**
270
+ * Concise way to check values are available from object literal shorthand.
271
+ * Throws error message to specify the missing values.
272
+ *
273
+ * @template {Record<string, unknown>} T
274
+ * @param {T} obj
275
+ * @throws if any value in the object entries is not defined
276
+ * @returns {asserts obj is AllDefined<T>}
312
277
  */
313
- export const fsStreamReady = stream =>
314
- new Promise((resolve, reject) => {
315
- if (stream.destroyed) {
316
- reject(new Error('Stream already destroyed'));
317
- return;
278
+
279
+ export const assertAllDefined = obj => {
280
+ const missing = [];
281
+ for (const [key, val] of Object.entries(obj)) {
282
+ if (val === undefined) {
283
+ missing.push(key);
318
284
  }
285
+ }
286
+ if (missing.length > 0) {
287
+ Fail`missing ${q(missing)}`;
288
+ }
289
+ };
290
+
291
+ /** @type {IteratorResult<undefined, never>} */
292
+ const notDone = harden({ done: false, value: undefined });
293
+
294
+ /** @type {IteratorResult<never, void>} */
295
+ const alwaysDone = harden({ done: true, value: undefined });
296
+
297
+ export const forever = asyncGenerate(() => notDone);
319
298
 
320
- if (!stream.pending) {
321
- resolve();
322
- return;
299
+ /**
300
+ * @template T
301
+ * @param {() => T} produce
302
+ * The value of `await produce()` is used for its truthiness vs falsiness.
303
+ * IOW, it is coerced to a boolean so the caller need not bother doing this
304
+ * themselves.
305
+ * @returns {AsyncIterable<Awaited<T>>}
306
+ */
307
+ export const whileTrue = produce =>
308
+ asyncGenerate(async () => {
309
+ const value = await produce();
310
+ if (!value) {
311
+ return alwaysDone;
323
312
  }
313
+ return harden({
314
+ done: false,
315
+ value,
316
+ });
317
+ });
324
318
 
325
- const onReady = () => {
326
- cleanup(); // eslint-disable-line no-use-before-define
327
- resolve();
328
- };
319
+ /**
320
+ * @template T
321
+ * @param {() => T} produce
322
+ * The value of `await produce()` is used for its truthiness vs falsiness.
323
+ * IOW, it is coerced to a boolean so the caller need not bother doing this
324
+ * themselves.
325
+ * @returns {AsyncIterable<Awaited<T>>}
326
+ */
327
+ export const untilTrue = produce =>
328
+ asyncGenerate(async () => {
329
+ const value = await produce();
330
+ if (value) {
331
+ return harden({
332
+ done: true,
333
+ value,
334
+ });
335
+ }
336
+ return harden({
337
+ done: false,
338
+ value,
339
+ });
340
+ });
329
341
 
330
- /** @param {Error} err */
331
- const onError = err => {
332
- cleanup(); // eslint-disable-line no-use-before-define
333
- reject(err);
334
- };
342
+ /** @type { <X, Y>(xs: X[], ys: Y[]) => [X, Y][]} */
343
+ export const zip = (xs, ys) => harden(xs.map((x, i) => [x, ys[+i]]));
335
344
 
336
- const cleanup = () => {
337
- stream.off('ready', onReady);
338
- stream.off('error', onError);
339
- };
345
+ /** @type { <T extends Record<string, ERef<any>>>(obj: T) => Promise<{ [K in keyof T]: Awaited<T[K]>}> } */
346
+ export const allValues = async obj => {
347
+ const resolved = await Promise.all(values(obj));
348
+ // @ts-expect-error cast
349
+ return harden(fromEntries(zip(keys(obj), resolved)));
350
+ };
340
351
 
341
- stream.on('ready', onReady);
342
- stream.on('error', onError);
352
+ /**
353
+ * A tee implementation where all readers are synchronized with each other.
354
+ * They all consume the source stream in lockstep, and any one returning or
355
+ * throwing early will affect the others.
356
+ *
357
+ * @template [T=unknown]
358
+ * @param {AsyncIterator<T, void, void>} sourceStream
359
+ * @param {number} readerCount
360
+ */
361
+ export const synchronizedTee = (sourceStream, readerCount) => {
362
+ /** @type {IteratorReturnResult<void> | undefined} */
363
+ let doneResult;
364
+
365
+ /** @typedef {IteratorResult<(value: PromiseLike<IteratorResult<T>>) => void>} QueuePayload */
366
+ /** @type {import('@endo/stream').AsyncQueue<QueuePayload>[]} */
367
+ const queues = [];
368
+
369
+ /** @returns {Promise<void>} */
370
+ const pullNext = async () => {
371
+ const requests = await Promise.allSettled(queues.map(queue => queue.get()));
372
+ const rejections = [];
373
+ /** @type {Array<(value: PromiseLike<IteratorResult<T>>) => void>} */
374
+ const resolvers = [];
375
+ let done = false;
376
+ for (const settledResult of requests) {
377
+ if (settledResult.status === 'rejected') {
378
+ rejections.push(settledResult.reason);
379
+ } else {
380
+ done ||= !!settledResult.value.done;
381
+ resolvers.push(settledResult.value.value);
382
+ }
383
+ }
384
+ /** @type {Promise<IteratorResult<T>>} */
385
+ let result;
386
+ if (doneResult) {
387
+ result = Promise.resolve(doneResult);
388
+ } else if (rejections.length) {
389
+ const error = assert.error(assert.details`Teed stream threw`);
390
+ assert.note(error, assert.details`Teed rejections: ${rejections}`);
391
+ result =
392
+ sourceStream.throw?.(error) ||
393
+ Promise.resolve(sourceStream.return?.()).then(() =>
394
+ Promise.reject(error),
395
+ );
396
+ } else if (done) {
397
+ result =
398
+ sourceStream.return?.() ||
399
+ Promise.resolve({ done: true, value: undefined });
400
+ } else {
401
+ result = sourceStream.next();
402
+ }
403
+ result.then(
404
+ r => {
405
+ if (r.done) {
406
+ doneResult = r;
407
+ }
408
+ },
409
+ () => {
410
+ doneResult = { done: true, value: undefined };
411
+ },
412
+ );
413
+ resolvers.forEach(resolve => resolve(result));
414
+ return pullNext();
415
+ };
416
+
417
+ const readers = Array.from({ length: readerCount }).map(() => {
418
+ /** @type {import('@endo/stream').AsyncQueue<QueuePayload>} */
419
+ const queue = makeQueue();
420
+ queues.push(queue);
421
+
422
+ /** @type {AsyncGenerator<T, void, void>} */
423
+ const reader = harden({
424
+ async next() {
425
+ /** @type {import('@endo/promise-kit').PromiseKit<IteratorResult<T>>} */
426
+ const { promise, resolve } = makePromiseKit();
427
+ queue.put({ value: resolve, done: false });
428
+ return promise;
429
+ },
430
+ async return() {
431
+ /** @type {import('@endo/promise-kit').PromiseKit<IteratorResult<T>>} */
432
+ const { promise, resolve } = makePromiseKit();
433
+ queue.put({ value: resolve, done: true });
434
+ return promise;
435
+ },
436
+ async throw(reason) {
437
+ const rejection = Promise.reject(reason);
438
+ queue.put(rejection);
439
+ return rejection;
440
+ },
441
+ // eslint-disable-next-line no-restricted-globals
442
+ [Symbol.asyncIterator]() {
443
+ return reader;
444
+ },
445
+ });
446
+ return reader;
343
447
  });
448
+
449
+ void pullNext();
450
+ return readers;
451
+ };