@agoric/vow 0.1.1-dev-9cdb01d.0 → 0.1.1-dev-3e9ff43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,7 @@ Here they are: {
27
27
  ```
28
28
 
29
29
  You can use `heapVowE` exported from `@agoric/vow`, which converts a chain of
30
- promises and vows to a promise for its final fulfilment, by unwrapping any
30
+ promises and vows to a promise for its final fulfillment, by unwrapping any
31
31
  intermediate vows:
32
32
 
33
33
  ```js
@@ -77,6 +77,67 @@ const { watch, makeVowKit } = prepareVowTools(vowZone);
77
77
  // Vows and resolvers you create can be saved in durable stores.
78
78
  ```
79
79
 
80
+ ## VowTools
81
+
82
+ VowTools are a set of utility functions for working with Vows in Agoric smart contracts and vats. These tools help manage asynchronous operations in a way that's resilient to vat upgrades, ensuring your smart contract can handle long-running processes reliably.
83
+
84
+ ### Usage
85
+
86
+ VowTools are typically prepared in the start function of a smart contract or vat and passed in as a power to exos.
87
+
88
+
89
+ ```javascript
90
+ import { prepareVowTools } from '@agoric/vow/vat.js';
91
+ import { makeDurableZone } from '@agoric/zone/durable.js';
92
+
93
+ export const start = async (zcf, privateArgs, baggage) => {
94
+ const zone = makeDurableZone(baggage);
95
+ const vowTools = prepareVowTools(zone.subZone('vows'));
96
+
97
+ // Use vowTools here...
98
+ }
99
+ ```
100
+
101
+ ### Available Tools
102
+
103
+ #### `when(vowOrPromise)`
104
+ Returns a Promise for the fulfillment of the very end of the `vowOrPromise` chain. It can retry disconnections due to upgrades of other vats, but cannot survive the upgrade of the calling vat.
105
+
106
+ #### `watch(promiseOrVow, [watcher], [context])`
107
+ Watch a Vow and optionally provide a `watcher` with `onFulfilled`/`onRejected` handlers and a `context` value for the handlers. When handlers are not provided the fulfillment or rejection will simply pass through.
108
+
109
+ It also registers pending Promises, so if the current vat is upgraded, the watcher is rejected because the Promise was lost when the heap was reset.
110
+
111
+ #### `all(arrayOfPassables, [watcher], [context])`
112
+ Vow-tolerant implementation of Promise.all that takes an iterable of vows and other Passables and returns a single Vow. It resolves with an array of values when all of the input's promises or vows are fulfilled and rejects with the first rejection reason when any of the input's promises or vows are rejected.
113
+
114
+ #### `allSettled(arrayOfPassables, [watcher], [context])`
115
+ Vow-tolerant implementation of Promise.allSettled that takes an iterable of vows and other Passables and returns a single Vow. It resolves when all of the input's promises or vows are settled with an array of settled outcome objects.
116
+
117
+ #### `asVow(fn)`
118
+ Takes a function that might return synchronously, throw an Error, or return a Promise or Vow and returns a Vow.
119
+
120
+ #### `asPromise(vow)`
121
+ Converts a Vow back into a Promise.
122
+
123
+ ### Example
124
+
125
+ ```javascript
126
+ const { when, watch, all, allSettled } = vowTools;
127
+
128
+ // Using watch to create a Vow
129
+ const myVow = watch(someAsyncOperation());
130
+
131
+ // Using when to resolve a Vow
132
+ const result = await when(myVow);
133
+
134
+ // Using all
135
+ const results = await when(all([vow, vowForVow, promise]));
136
+
137
+ // Using allSettled
138
+ const outcomes = await when(allSettled([vow, vowForVow, promise]));
139
+ ```
140
+
80
141
  ## Internals
81
142
 
82
143
  The current "version 0" vow internals expose a `shorten()` method, returning a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/vow",
3
- "version": "0.1.1-dev-9cdb01d.0+9cdb01d",
3
+ "version": "0.1.1-dev-3e9ff43.0+3e9ff43",
4
4
  "description": "Remote (shortening and disconnection-tolerant) Promise-likes",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -19,8 +19,8 @@
19
19
  "lint:types": "tsc"
20
20
  },
21
21
  "dependencies": {
22
- "@agoric/base-zone": "0.1.1-dev-9cdb01d.0+9cdb01d",
23
- "@agoric/internal": "0.3.3-dev-9cdb01d.0+9cdb01d",
22
+ "@agoric/base-zone": "0.1.1-dev-3e9ff43.0+3e9ff43",
23
+ "@agoric/internal": "0.3.3-dev-3e9ff43.0+3e9ff43",
24
24
  "@endo/env-options": "^1.1.6",
25
25
  "@endo/errors": "^1.2.5",
26
26
  "@endo/eventual-send": "^1.2.5",
@@ -55,5 +55,5 @@
55
55
  "typeCoverage": {
56
56
  "atLeast": 89.96
57
57
  },
58
- "gitHead": "9cdb01dde5fdc3418fc9d615ab640d9f859555e6"
58
+ "gitHead": "3e9ff4365c55c19a4b40d2d2741ac791fa287851"
59
59
  }
package/src/tools.d.ts CHANGED
@@ -4,7 +4,15 @@ export function prepareBasicVowTools(zone: Zone, powers?: {
4
4
  when: <T, TResult1 = import("./types.js").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("./types.js").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
5
5
  watch: <T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]>(specimenP: EVow<T>, watcher?: import("./types.js").Watcher<T, TResult1, TResult2, C> | undefined, ...watcherArgs: C) => Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
6
6
  makeVowKit: <T>() => import("./types.js").VowKit<T>;
7
- allVows: (maybeVows: EVow<unknown>[]) => Vow<any[]>;
7
+ all: (maybeVows: unknown[]) => Vow<any[]>;
8
+ allVows: (maybeVows: unknown[]) => Vow<any[]>;
9
+ allSettled: (maybeVows: unknown[]) => Vow<({
10
+ status: "fulfilled";
11
+ value: any;
12
+ } | {
13
+ status: "rejected";
14
+ reason: any;
15
+ })[]>;
8
16
  asVow: <T extends unknown>(fn: (...args: any[]) => Vow<Awaited<T>> | Awaited<T> | import("./types.js").PromiseVow<T>) => Vow<Awaited<T>>;
9
17
  asPromise: AsPromiseFunction;
10
18
  retriable: <F extends (...args: any[]) => Promise<any>>(fnZone: Zone, name: string, fn: F) => F extends (...args: infer Args) => Promise<infer R> ? (...args: Args) => Vow<R> : never;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["tools.js"],"names":[],"mappings":"AAoBO,2CAJI,IAAI;;;;;;yBAwCF,KAAK,OAAO,CAAC,EAAE;oCAqB8U,GAAG;;gBArC3T,CAAC,SAApC,CAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAE,UACpC,IAAI,QACJ,MAAM,MACN,CAAC,KACC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK;EA6BrG;uBAGa,UAAU,CAAC,OAAO,oBAAoB,CAAC;0BApE9B,mBAAmB;uCAC8B,YAAY;0BAAZ,YAAY;yBAAZ,YAAY;uCAAZ,YAAY"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["tools.js"],"names":[],"mappings":"AAqBO,2CAJI,IAAI;;;;;;qBA4CF,OAAO,EAAE;yBAAT,OAAO,EAAE;4BAgBT,OAAO,EAAE;;;;;;;oCAFc,GACpC;;gBAnCkD,CAAC,SAApC,CAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAE,UACpC,IAAI,QACJ,MAAM,MACN,CAAC,KACC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK;EAmDrG;uBAGa,UAAU,CAAC,OAAO,oBAAoB,CAAC;0BA3F9B,mBAAmB;uCAE8B,YAAY;0BAAZ,YAAY;yBAAZ,YAAY;uCAAZ,YAAY"}
package/src/tools.js CHANGED
@@ -7,6 +7,7 @@ import { makeWhen } from './when.js';
7
7
 
8
8
  /**
9
9
  * @import {Zone} from '@agoric/base-zone';
10
+ * @import {Passable} from '@endo/pass-style';
10
11
  * @import {IsRetryableReason, AsPromiseFunction, EVow, Vow, ERef} from './types.js';
11
12
  */
12
13
 
@@ -52,11 +53,31 @@ export const prepareBasicVowTools = (zone, powers = {}) => {
52
53
  };
53
54
 
54
55
  /**
55
- * Vow-tolerant implementation of Promise.all.
56
+ * Vow-tolerant implementation of Promise.all that takes an iterable of vows
57
+ * and other {@link Passable}s and returns a single {@link Vow}. It resolves
58
+ * with an array of values when all of the input's promises or vows are
59
+ * fulfilled and rejects when any of the input's promises or vows are
60
+ * rejected with the first rejection reason.
56
61
  *
57
- * @param {EVow<unknown>[]} maybeVows
62
+ * @param {unknown[]} maybeVows
58
63
  */
59
- const allVows = maybeVows => watchUtils.all(maybeVows);
64
+ const all = maybeVows => watchUtils.all(maybeVows);
65
+
66
+ /**
67
+ * @param {unknown[]} maybeVows
68
+ * @deprecated use `vowTools.all`
69
+ */
70
+ const allVows = all;
71
+
72
+ /**
73
+ * Vow-tolerant implementation of Promise.allSettled that takes an iterable
74
+ * of vows and other {@link Passable}s and returns a single {@link Vow}. It
75
+ * resolves when all of the input's promises or vows are settled with an
76
+ * array of settled outcome objects.
77
+ *
78
+ * @param {unknown[]} maybeVows
79
+ */
80
+ const allSettled = maybeVows => watchUtils.allSettled(maybeVows);
60
81
 
61
82
  /** @type {AsPromiseFunction} */
62
83
  const asPromise = (specimenP, ...watcherArgs) =>
@@ -66,7 +87,9 @@ export const prepareBasicVowTools = (zone, powers = {}) => {
66
87
  when,
67
88
  watch,
68
89
  makeVowKit,
90
+ all,
69
91
  allVows,
92
+ allSettled,
70
93
  asVow,
71
94
  asPromise,
72
95
  retriable,
package/src/types.d.ts CHANGED
@@ -35,6 +35,9 @@ export type VowV0<T = any> = {
35
35
  export type VowPayload<T = any> = {
36
36
  vowV0: RemotableObject & Remote<VowV0<T>>;
37
37
  };
38
+ /**
39
+ * Vows are objects that represent promises that can be stored durably.
40
+ */
38
41
  export type Vow<T = any> = CopyTagged<"Vow", VowPayload<T>>;
39
42
  export type VowKit<T = any> = {
40
43
  vow: Vow<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":";;;yCAaW,GAAG,mBACH,GAAG,KAED,GAAG;;;;;uBAKH,CAAC,IACD,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;iBAKnB,CAAC,IACD,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;;;;iBAKlB,CAAC,IACD,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;;;;;oBAMhB,CAAC,IACD,CACR,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACnC,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC3C,CAAC,CACF;;;;;;;kBAIU,CAAC;;;;;;;aAMD,MAAM,OAAO,CAAC,CAAC,CAAC;;uBAOhB,CAAC;WAED,eAAe,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;;gBAIlC,CAAC,UACF,WAAW,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;mBAI/B,CAAC,UACF;IACR,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACZ,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;CAC1B;wBAIU,CAAC,UACF;IAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;CAAE;oBAIvE,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG;2BAEE,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;2BAChE,GAAG,WAAW,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;;;;;8BAM5E,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG,wBAET,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,2FAGd,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;qCArGP,kBAAkB;4BAC3B,kBAAkB;gCAFd,kBAAkB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":";;;yCAaW,GAAG,mBACH,GAAG,KAED,GAAG;;;;;uBAKH,CAAC,IACD,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;iBAKnB,CAAC,IACD,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;;;;iBAKlB,CAAC,IACD,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;;;;;oBAMhB,CAAC,IACD,CACR,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACnC,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC3C,CAAC,CACF;;;;;;;kBAIU,CAAC;;;;;;;aAMD,MAAM,OAAO,CAAC,CAAC,CAAC;;uBAOhB,CAAC;WAED,eAAe,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;;;;;gBAKlC,CAAC,UACF,WAAW,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;mBAI/B,CAAC,UACF;IACR,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACZ,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;CAC1B;wBAIU,CAAC,UACF;IAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;CAAE;oBAIvE,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG;2BAEE,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;2BAChE,GAAG,WAAW,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;;;;;8BAM5E,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG,wBAET,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,2FAGd,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;qCAtGP,kBAAkB;4BAC3B,kBAAkB;gCAFd,kBAAkB"}
package/src/types.js CHANGED
@@ -66,6 +66,7 @@ export {};
66
66
  */
67
67
 
68
68
  /**
69
+ * Vows are objects that represent promises that can be stored durably.
69
70
  * @template [T=any]
70
71
  * @typedef {CopyTagged<'Vow', VowPayload<T>>} Vow
71
72
  */
@@ -4,10 +4,16 @@ export function prepareWatchUtils(zone: Zone, { watch, when, makeVowKit, isRetry
4
4
  makeVowKit: () => VowKit<any>;
5
5
  isRetryableReason: IsRetryableReason;
6
6
  }): () => import("@endo/exo").Guarded<{
7
- /**
8
- * @param {EVow<unknown>[]} vows
9
- */
10
- all(vows: EVow<unknown>[]): import("./types.js").Vow<any[]>;
7
+ /** @param {unknown[]} specimens */
8
+ all(specimens: unknown[]): Vow<any[]>;
9
+ /** @param {unknown[]} specimens */
10
+ allSettled(specimens: unknown[]): Vow<({
11
+ status: "fulfilled";
12
+ value: any;
13
+ } | {
14
+ status: "rejected";
15
+ reason: any;
16
+ })[]>;
11
17
  /** @type {AsPromiseFunction} */
12
18
  asPromise(specimenP: any, watcher: import("./types.js").Watcher<any, any, never, any[]> | undefined, watcherArgs: any[] | undefined): Promise<any>;
13
19
  }>;
@@ -16,5 +22,5 @@ import type { Watch } from './watch.js';
16
22
  import type { When } from './when.js';
17
23
  import type { VowKit } from './types.js';
18
24
  import type { IsRetryableReason } from './types.js';
19
- import type { EVow } from './types.js';
25
+ import type { Vow } from './types.js';
20
26
  //# sourceMappingURL=watch-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch-utils.d.ts","sourceRoot":"","sources":["watch-utils.js"],"names":[],"mappings":"AA4CO,wCAPI,IAAI,kDAEZ;IAAsB,KAAK,EAAnB,KAAK;IACQ,IAAI,EAAjB,IAAI;IACsB,UAAU,EAApC,MAAM,OAAO,GAAG,CAAC;IACS,iBAAiB,EAA3C,iBAAiB;CAC3B;IAsCO;;OAEG;cADQ,KAAK,OAAO,CAAC,EAAE;IAuC1B,gCAAgC;;GAgGvC;0BAhNsB,mBAAmB;2BAClB,YAAY;0BACb,WAAW;4BACmC,YAAY;uCAAZ,YAAY;0BAAZ,YAAY"}
1
+ {"version":3,"file":"watch-utils.d.ts","sourceRoot":"","sources":["watch-utils.js"],"names":[],"mappings":"AA4CO,wCAPI,IAAI,kDAEZ;IAAsB,KAAK,EAAnB,KAAK;IACQ,IAAI,EAAjB,IAAI;IACsB,UAAU,EAApC,MAAM,OAAO,GAAG,CAAC;IACS,iBAAiB,EAA3C,iBAAiB;CAC3B;IA4CO,mCAAmC;mBAAvB,OAAO,EAAE;IAIrB,mCAAmC;0BAAvB,OAAO,EAAE,GAED,IAAI,CAAC;QAAC,MAAM,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAC,GAAG;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAC,CAAC,EAAE,CAAC;IAIlG,gCAAgC;;GAoLvC;0BA5QsB,mBAAmB;2BAClB,YAAY;0BACb,WAAW;4BACkC,YAAY;uCAAZ,YAAY;yBAAZ,YAAY"}
@@ -10,7 +10,7 @@ const { Fail, bare, details: X } = assert;
10
10
  * @import {Zone} from '@agoric/base-zone';
11
11
  * @import {Watch} from './watch.js';
12
12
  * @import {When} from './when.js';
13
- * @import {VowKit, AsPromiseFunction, IsRetryableReason, EVow} from './types.js';
13
+ * @import {VowKit, AsPromiseFunction, IsRetryableReason, Vow} from './types.js';
14
14
  */
15
15
 
16
16
  const VowShape = M.tagged(
@@ -54,11 +54,16 @@ export const prepareWatchUtils = (
54
54
  {
55
55
  utils: M.interface('Utils', {
56
56
  all: M.call(M.arrayOf(M.any())).returns(VowShape),
57
+ allSettled: M.call(M.arrayOf(M.any())).returns(VowShape),
57
58
  asPromise: M.call(M.raw()).rest(M.raw()).returns(M.promise()),
58
59
  }),
59
60
  watcher: M.interface('Watcher', {
60
- onFulfilled: M.call(M.any()).rest(M.any()).returns(M.any()),
61
- onRejected: M.call(M.any()).rest(M.any()).returns(M.any()),
61
+ onFulfilled: M.call(M.raw()).rest(M.raw()).returns(M.raw()),
62
+ onRejected: M.call(M.raw()).rest(M.raw()).returns(M.raw()),
63
+ }),
64
+ helper: M.interface('Helper', {
65
+ createVow: M.call(M.arrayOf(M.any()), M.boolean()).returns(VowShape),
66
+ processResult: M.call(M.raw()).rest(M.raw()).returns(M.undefined()),
62
67
  }),
63
68
  retryRejectionPromiseWatcher: PromiseWatcherI,
64
69
  },
@@ -68,6 +73,7 @@ export const prepareWatchUtils = (
68
73
  * @property {number} remaining
69
74
  * @property {MapStore<number, any>} resultsMap
70
75
  * @property {VowKit['resolver']} resolver
76
+ * @property {boolean} [isAllSettled]
71
77
  */
72
78
  /** @type {MapStore<bigint, VowState>} */
73
79
  const idToVowState = detached.mapStore('idToVowState');
@@ -79,32 +85,83 @@ export const prepareWatchUtils = (
79
85
  },
80
86
  {
81
87
  utils: {
88
+ /** @param {unknown[]} specimens */
89
+ all(specimens) {
90
+ return this.facets.helper.createVow(specimens, false);
91
+ },
92
+ /** @param {unknown[]} specimens */
93
+ allSettled(specimens) {
94
+ return /** @type {Vow<({status: 'fulfilled', value: any} | {status: 'rejected', reason: any})[]>} */ (
95
+ this.facets.helper.createVow(specimens, true)
96
+ );
97
+ },
98
+ /** @type {AsPromiseFunction} */
99
+ asPromise(specimenP, ...watcherArgs) {
100
+ // Watch the specimen in case it is an ephemeral promise.
101
+ const vow = watch(specimenP, ...watcherArgs);
102
+ const promise = when(vow);
103
+ // Watch the ephemeral result promise to ensure that if its settlement is
104
+ // lost due to upgrade of this incarnation, we will at least cause an
105
+ // unhandled rejection in the new incarnation.
106
+ zone.watchPromise(promise, this.facets.retryRejectionPromiseWatcher);
107
+
108
+ return promise;
109
+ },
110
+ },
111
+ watcher: {
82
112
  /**
83
- * @param {EVow<unknown>[]} vows
113
+ * @param {unknown} value
114
+ * @param {object} ctx
115
+ * @param {bigint} ctx.id
116
+ * @param {number} ctx.index
117
+ * @param {number} ctx.numResults
118
+ * @param {boolean} ctx.isAllSettled
84
119
  */
85
- all(vows) {
120
+ onFulfilled(value, ctx) {
121
+ this.facets.helper.processResult(value, ctx, 'fulfilled');
122
+ },
123
+ /**
124
+ * @param {unknown} reason
125
+ * @param {object} ctx
126
+ * @param {bigint} ctx.id
127
+ * @param {number} ctx.index
128
+ * @param {number} ctx.numResults
129
+ * @param {boolean} ctx.isAllSettled
130
+ */
131
+ onRejected(reason, ctx) {
132
+ this.facets.helper.processResult(reason, ctx, 'rejected');
133
+ },
134
+ },
135
+ helper: {
136
+ /**
137
+ * @param {unknown[]} specimens
138
+ * @param {boolean} isAllSettled
139
+ */
140
+ createVow(specimens, isAllSettled) {
86
141
  const { nextId: id, idToVowState } = this.state;
87
142
  /** @type {VowKit<any[]>} */
88
143
  const kit = makeVowKit();
89
144
 
90
- // Preserve the order of the vow results.
91
- for (let index = 0; index < vows.length; index += 1) {
92
- watch(vows[index], this.facets.watcher, {
145
+ // Preserve the order of the results.
146
+ for (let index = 0; index < specimens.length; index += 1) {
147
+ watch(specimens[index], this.facets.watcher, {
93
148
  id,
94
149
  index,
95
- numResults: vows.length,
150
+ numResults: specimens.length,
151
+ isAllSettled,
96
152
  });
97
153
  }
98
154
 
99
- if (vows.length > 0) {
155
+ if (specimens.length > 0) {
100
156
  // Save the state until rejection or all fulfilled.
101
157
  this.state.nextId += 1n;
102
158
  idToVowState.init(
103
159
  id,
104
160
  harden({
105
161
  resolver: kit.resolver,
106
- remaining: vows.length,
162
+ remaining: specimens.length,
107
163
  resultsMap: detached.mapStore('resultsMap'),
164
+ isAllSettled,
108
165
  }),
109
166
  );
110
167
  const idToNonStorableResults = provideLazyMap(
@@ -119,27 +176,36 @@ export const prepareWatchUtils = (
119
176
  }
120
177
  return kit.vow;
121
178
  },
122
- /** @type {AsPromiseFunction} */
123
- asPromise(specimenP, ...watcherArgs) {
124
- // Watch the specimen in case it is an ephemeral promise.
125
- const vow = watch(specimenP, ...watcherArgs);
126
- const promise = when(vow);
127
- // Watch the ephemeral result promise to ensure that if its settlement is
128
- // lost due to upgrade of this incarnation, we will at least cause an
129
- // unhandled rejection in the new incarnation.
130
- zone.watchPromise(promise, this.facets.retryRejectionPromiseWatcher);
131
-
132
- return promise;
133
- },
134
- },
135
- watcher: {
136
- onFulfilled(value, { id, index, numResults }) {
179
+ /**
180
+ * @param {unknown} result
181
+ * @param {object} ctx
182
+ * @param {bigint} ctx.id
183
+ * @param {number} ctx.index
184
+ * @param {number} ctx.numResults
185
+ * @param {boolean} ctx.isAllSettled
186
+ * @param {'fulfilled' | 'rejected'} status
187
+ */
188
+ processResult(result, { id, index, numResults, isAllSettled }, status) {
137
189
  const { idToVowState } = this.state;
138
190
  if (!idToVowState.has(id)) {
139
191
  // Resolution of the returned vow happened already.
140
192
  return;
141
193
  }
142
194
  const { remaining, resultsMap, resolver } = idToVowState.get(id);
195
+ if (!isAllSettled && status === 'rejected') {
196
+ // For 'all', we reject immediately on the first rejection
197
+ idToVowState.delete(id);
198
+ resolver.reject(result);
199
+ return;
200
+ }
201
+
202
+ const possiblyWrappedResult = isAllSettled
203
+ ? harden({
204
+ status,
205
+ [status === 'fulfilled' ? 'value' : 'reason']: result,
206
+ })
207
+ : result;
208
+
143
209
  const idToNonStorableResults = provideLazyMap(
144
210
  utilsToNonStorableResults,
145
211
  this.facets.utils,
@@ -152,15 +218,16 @@ export const prepareWatchUtils = (
152
218
  );
153
219
 
154
220
  // Capture the fulfilled value.
155
- if (zone.isStorable(value)) {
156
- resultsMap.init(index, value);
221
+ if (zone.isStorable(possiblyWrappedResult)) {
222
+ resultsMap.init(index, possiblyWrappedResult);
157
223
  } else {
158
- nonStorableResults.set(index, value);
224
+ nonStorableResults.set(index, possiblyWrappedResult);
159
225
  }
160
226
  const vowState = harden({
161
227
  remaining: remaining - 1,
162
228
  resultsMap,
163
229
  resolver,
230
+ isAllSettled,
164
231
  });
165
232
  if (vowState.remaining > 0) {
166
233
  idToVowState.set(id, vowState);
@@ -177,9 +244,12 @@ export const prepareWatchUtils = (
177
244
  results[i] = resultsMap.get(i);
178
245
  } else {
179
246
  numLost += 1;
247
+ results[i] = isAllSettled
248
+ ? { status: 'rejected', reason: 'Unstorable result was lost' }
249
+ : undefined;
180
250
  }
181
251
  }
182
- if (numLost > 0) {
252
+ if (numLost > 0 && !isAllSettled) {
183
253
  resolver.reject(
184
254
  assert.error(X`${numLost} unstorable results were lost`),
185
255
  );
@@ -187,16 +257,6 @@ export const prepareWatchUtils = (
187
257
  resolver.resolve(harden(results));
188
258
  }
189
259
  },
190
- onRejected(value, { id, index: _index, numResults: _numResults }) {
191
- const { idToVowState } = this.state;
192
- if (!idToVowState.has(id)) {
193
- // First rejection wins.
194
- return;
195
- }
196
- const { resolver } = idToVowState.get(id);
197
- idToVowState.delete(id);
198
- resolver.reject(value);
199
- },
200
260
  },
201
261
  retryRejectionPromiseWatcher: {
202
262
  onFulfilled(_result) {},