@agoric/vow 0.1.1-dev-01a1123.0 → 0.1.1-dev-a4f86eb.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/vow",
3
- "version": "0.1.1-dev-01a1123.0+01a1123",
3
+ "version": "0.1.1-dev-a4f86eb.0+a4f86eb",
4
4
  "description": "Remote (shortening and disconnection-tolerant) Promise-likes",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -20,8 +20,8 @@
20
20
  "lint:types": "tsc"
21
21
  },
22
22
  "dependencies": {
23
- "@agoric/base-zone": "0.1.1-dev-01a1123.0+01a1123",
24
- "@agoric/internal": "0.3.3-dev-01a1123.0+01a1123",
23
+ "@agoric/base-zone": "0.1.1-dev-a4f86eb.0+a4f86eb",
24
+ "@agoric/internal": "0.3.3-dev-a4f86eb.0+a4f86eb",
25
25
  "@endo/env-options": "^1.1.4",
26
26
  "@endo/eventual-send": "^1.2.2",
27
27
  "@endo/pass-style": "^1.4.0",
@@ -53,5 +53,5 @@
53
53
  "typeCoverage": {
54
54
  "atLeast": 89.6
55
55
  },
56
- "gitHead": "01a1123a526b11e11fa1ef80e0c532a616cd29b9"
56
+ "gitHead": "a4f86eb7fd602980a40d00d739897090d3667d3d"
57
57
  }
package/src/tools.d.ts CHANGED
@@ -2,7 +2,7 @@ export function prepareVowTools(zone: Zone, powers?: {
2
2
  isRetryableReason?: IsRetryableReason | undefined;
3
3
  } | undefined): {
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
- watch: <T = any, TResult1 = T, TResult2 = never, C = any>(specimenP: import("./types.js").ERef<T | import("./types.js").Vow<T>>, watcher?: import("./types.js").Watcher<T, TResult1, TResult2> | undefined, watcherContext?: C | undefined) => import("./types.js").Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
5
+ watch: <T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]>(specimenP: import("./types.js").ERef<T | import("./types.js").Vow<T>>, watcher?: import("./types.js").Watcher<T, TResult1, TResult2, C> | undefined, ...watcherArgs: C) => import("./types.js").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
7
  allVows: (vows: unknown[]) => import("./types.js").Vow<any[]>;
8
8
  };
package/src/types.d.ts CHANGED
@@ -40,9 +40,9 @@ export type VowResolver<T = any> = {
40
40
  resolve(value?: T | PromiseVow<T>): void;
41
41
  reject(reason?: any): void;
42
42
  };
43
- export type Watcher<T = any, TResult1 = T, TResult2 = never, C = any> = {
44
- onFulfilled?: ((value: T, context?: C) => Vow<TResult1> | PromiseVow<TResult1> | TResult1) | undefined;
45
- onRejected?: ((reason: any) => Vow<TResult2> | PromiseVow<TResult2> | TResult2) | undefined;
43
+ export type Watcher<T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]> = {
44
+ onFulfilled?: ((value: T, ...args: C) => Vow<TResult1> | PromiseVow<TResult1> | TResult1) | undefined;
45
+ onRejected?: ((reason: any, ...args: C) => Vow<TResult2> | PromiseVow<TResult2> | TResult2) | undefined;
46
46
  };
47
47
  import type { RemotableObject } from '@endo/pass-style';
48
48
  import type { Remote } from '@agoric/internal';
@@ -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;;;;;oBAMlB,CAAC,IACD,CACZ,CAAK,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvC,CAAK,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC/C,CAAK,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;IACZ,GAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAChB,QAAY,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,UACR,CAAC;2BAEO,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;2BACjE,GAAG,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;;qCAjF5C,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;;;;;oBAMlB,CAAC,IACD,CACZ,CAAK,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvC,CAAK,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC/C,CAAK,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;IACZ,GAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAChB,QAAY,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;;qCAjFxD,kBAAkB;4BAC3B,kBAAkB;gCAFd,kBAAkB"}
package/src/types.js CHANGED
@@ -81,8 +81,8 @@ export {};
81
81
  * @template [T=any]
82
82
  * @template [TResult1=T]
83
83
  * @template [TResult2=never]
84
- * @template [C=any] watcher context
84
+ * @template {any[]} [C=any[]] watcher args
85
85
  * @typedef {object} Watcher
86
- * @property {(value: T, context?: C) => Vow<TResult1> | PromiseVow<TResult1> | TResult1} [onFulfilled]
87
- * @property {(reason: any) => Vow<TResult2> | PromiseVow<TResult2> | TResult2} [onRejected]
86
+ * @property {(value: T, ...args: C) => Vow<TResult1> | PromiseVow<TResult1> | TResult1} [onFulfilled]
87
+ * @property {(reason: any, ...args: C) => Vow<TResult2> | PromiseVow<TResult2> | TResult2} [onRejected]
88
88
  */
@@ -111,7 +111,7 @@ export const prepareWatchUtils = (zone, watch, makeVowKit) => {
111
111
  }
112
112
  resolver.resolve(harden(results));
113
113
  },
114
- onRejected(value, { id }) {
114
+ onRejected(value, { id, index: _index }) {
115
115
  const { idToVowState } = this.state;
116
116
  if (!idToVowState.has(id)) {
117
117
  // First rejection wins.
package/src/watch.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export function prepareWatch(zone: Zone, makeVowKit: () => VowKit<any>, isRetryableReason?: ((reason: any, lastValue: any) => any) | undefined): <T = any, TResult1 = T, TResult2 = never, C = any>(specimenP: ERef<T | Vow<T>>, watcher?: Watcher<T, TResult1, TResult2> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
1
+ export function prepareWatch(zone: Zone, makeVowKit: () => VowKit<any>, isRetryableReason?: ((reason: any, lastValue: any) => any) | undefined): <T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]>(specimenP: ERef<T | Vow<T>>, watcher?: Watcher<T, TResult1, TResult2, C> | undefined, ...watcherArgs: C) => Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
2
2
  export type Watch = ReturnType<typeof prepareWatch>;
3
3
  import type { Zone } from '@agoric/base-zone';
4
4
  import type { VowKit } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["watch.js"],"names":[],"mappings":"AAkIO,mCAJI,IAAI,cACJ,MAAM,OAAO,GAAG,CAAC,gCACR,GAAG,aAAa,GAAG,KAAK,GAAG,iBAe/B,CAAC,QACD,QAAQ,MACR,QAAQ,UACR,CAAC,mBACJ,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,+NA0B5B;oBAIa,UAAU,CAAC,OAAO,YAAY,CAAC;0BA1KJ,mBAAmB;4BACmB,YAAY;yBAAZ,YAAY;0BAAZ,YAAY;6BAAZ,YAAY"}
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["watch.js"],"names":[],"mappings":"AA2JO,mCAJI,IAAI,cACJ,MAAM,OAAO,GAAG,CAAC,gCACR,GAAG,aAAa,GAAG,KAAK,GAAG,iBAe/B,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG,qBACT,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,2EAEhB,CAAC,yIAoBb;oBAIa,UAAU,CAAC,OAAO,YAAY,CAAC;0BA/LJ,mBAAmB;4BACmB,YAAY;yBAAZ,YAAY;0BAAZ,YAAY;6BAAZ,YAAY"}
package/src/watch.js CHANGED
@@ -38,14 +38,14 @@ const makeWatchNextStep =
38
38
  * @param {Watcher<unknown, unknown, unknown> | undefined} watcher
39
39
  * @param {keyof Required<Watcher>} wcb
40
40
  * @param {unknown} value
41
- * @param {unknown} [watcherContext]
41
+ * @param {unknown[]} [watcherArgs]
42
42
  */
43
- const settle = (resolver, watcher, wcb, value, watcherContext) => {
43
+ const settle = (resolver, watcher, wcb, value, watcherArgs = []) => {
44
44
  try {
45
45
  let chainedValue = value;
46
46
  const w = watcher && watcher[wcb];
47
47
  if (w) {
48
- chainedValue = apply(w, watcher, [value, watcherContext]);
48
+ chainedValue = apply(w, watcher, [value, ...watcherArgs]);
49
49
  } else if (wcb === 'onRejected') {
50
50
  throw value;
51
51
  }
@@ -65,8 +65,23 @@ const settle = (resolver, watcher, wcb, value, watcherContext) => {
65
65
  * @param {IsRetryableReason} isRetryableReason
66
66
  * @param {ReturnType<typeof makeWatchNextStep>} watchNextStep
67
67
  */
68
- const preparePromiseWatcher = (zone, isRetryableReason, watchNextStep) =>
69
- zone.exoClass(
68
+ const preparePromiseWatcher = (zone, isRetryableReason, watchNextStep) => {
69
+ // We use an ephemeral WeakSet for the previously seen vows in a watch operation
70
+ // While watch is durable, it suffices to detect the cycle in a single incarnation
71
+ /** @type {WeakMap<PromiseWatcher, WeakSet<any>>} */
72
+ const watcherSeenPayloads = new WeakMap();
73
+
74
+ /** @param {PromiseWatcher} watcher */
75
+ const getSeenPayloads = watcher => {
76
+ let seenPayloads = watcherSeenPayloads.get(watcher);
77
+ if (!seenPayloads) {
78
+ seenPayloads = new WeakSet();
79
+ watcherSeenPayloads.set(watcher, seenPayloads);
80
+ }
81
+ return seenPayloads;
82
+ };
83
+
84
+ return zone.exoClass(
70
85
  'PromiseWatcher',
71
86
  PromiseWatcherI,
72
87
  /**
@@ -75,36 +90,44 @@ const preparePromiseWatcher = (zone, isRetryableReason, watchNextStep) =>
75
90
  * @template [TResult2=never]
76
91
  * @param {VowResolver<TResult1 | TResult2>} resolver
77
92
  * @param {Watcher<T, TResult1, TResult2>} [watcher]
78
- * @param {unknown} [watcherContext]
93
+ * @param {unknown[]} [watcherArgs]
79
94
  */
80
- (resolver, watcher, watcherContext) => {
95
+ (resolver, watcher, watcherArgs) => {
81
96
  const state = {
82
97
  vow: /** @type {unknown} */ (undefined),
83
98
  priorRetryValue: /** @type {any} */ (undefined),
84
99
  resolver,
85
100
  watcher,
86
- watcherContext: harden(watcherContext),
101
+ watcherArgs: harden(watcherArgs),
87
102
  };
88
103
  return /** @type {Partial<typeof state>} */ (state);
89
104
  },
90
105
  {
91
106
  /** @type {Required<PromiseWatcher>['onFulfilled']} */
92
107
  onFulfilled(value) {
93
- const { watcher, watcherContext, resolver } = this.state;
94
- if (getVowPayload(value)) {
108
+ const { watcher, watcherArgs, resolver } = this.state;
109
+ const payload = getVowPayload(value);
110
+ if (payload) {
111
+ const seenPayloads = getSeenPayloads(this.self);
112
+ // TODO: rely on endowed helper to get storable cap from payload
113
+ if (seenPayloads.has(payload.vowV0)) {
114
+ return this.self.onRejected(Error('Vow resolution cycle detected'));
115
+ }
116
+ seenPayloads.add(payload.vowV0);
95
117
  // We've been shortened, so reflect our state accordingly, and go again.
96
118
  this.state.vow = value;
97
119
  watchNextStep(value, this.self);
98
120
  return;
99
121
  }
122
+ watcherSeenPayloads.delete(this.self);
100
123
  this.state.priorRetryValue = undefined;
101
124
  this.state.watcher = undefined;
102
125
  this.state.resolver = undefined;
103
- settle(resolver, watcher, 'onFulfilled', value, watcherContext);
126
+ settle(resolver, watcher, 'onFulfilled', value, watcherArgs);
104
127
  },
105
128
  /** @type {Required<PromiseWatcher>['onRejected']} */
106
129
  onRejected(reason) {
107
- const { vow, watcher, watcherContext, resolver, priorRetryValue } =
130
+ const { vow, watcher, watcherArgs, resolver, priorRetryValue } =
108
131
  this.state;
109
132
  if (vow) {
110
133
  const retryValue = isRetryableReason(reason, priorRetryValue);
@@ -115,13 +138,15 @@ const preparePromiseWatcher = (zone, isRetryableReason, watchNextStep) =>
115
138
  return;
116
139
  }
117
140
  }
141
+ watcherSeenPayloads.delete(this.self);
118
142
  this.state.priorRetryValue = undefined;
119
143
  this.state.resolver = undefined;
120
144
  this.state.watcher = undefined;
121
- settle(resolver, watcher, 'onRejected', reason, watcherContext);
145
+ settle(resolver, watcher, 'onRejected', reason, watcherArgs);
122
146
  },
123
147
  },
124
148
  );
149
+ };
125
150
 
126
151
  /**
127
152
  * @param {Zone} zone
@@ -144,12 +169,12 @@ export const prepareWatch = (
144
169
  * @template [T=any]
145
170
  * @template [TResult1=T]
146
171
  * @template [TResult2=never]
147
- * @template [C=any] watcher context
172
+ * @template {any[]} [C=any[]] watcher args
148
173
  * @param {ERef<T | Vow<T>>} specimenP
149
- * @param {Watcher<T, TResult1, TResult2>} [watcher]
150
- * @param {C} [watcherContext]
174
+ * @param {Watcher<T, TResult1, TResult2, C>} [watcher]
175
+ * @param {C} watcherArgs
151
176
  */
152
- const watch = (specimenP, watcher, watcherContext) => {
177
+ const watch = (specimenP, watcher, ...watcherArgs) => {
153
178
  /** @typedef {Exclude<TResult1, void> | Exclude<TResult2, void>} Voidless */
154
179
  /** @typedef {Voidless extends never ? TResult1 : Voidless} Narrowest */
155
180
  /** @type {VowKit<Narrowest>} */
@@ -157,11 +182,7 @@ export const prepareWatch = (
157
182
 
158
183
  // Create a promise watcher to track vows, retrying upon rejection as
159
184
  // controlled by `isRetryableReason`.
160
- const promiseWatcher = makePromiseWatcher(
161
- resolver,
162
- watcher,
163
- watcherContext,
164
- );
185
+ const promiseWatcher = makePromiseWatcher(resolver, watcher, watcherArgs);
165
186
 
166
187
  // Coerce the specimen to a promise, and start the watcher cycle.
167
188
  zone.watchPromise(basicE.resolve(specimenP), promiseWatcher);
package/src/when.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"when.d.ts","sourceRoot":"","sources":["when.js"],"names":[],"mappings":"AAQO,8EAMQ,CAAC,EACA,QAAQ,eACR,QAAQ,qBACX,CAAC,yBACO,QAAQ,CAAC,CAAC,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,sCAC9C,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,kBAC/C,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,CA2C1C;mBAIa,UAAU,CAAC,OAAO,QAAQ,CAAC;uCAhEO,YAAY;6BAAZ,YAAY"}
1
+ {"version":3,"file":"when.d.ts","sourceRoot":"","sources":["when.js"],"names":[],"mappings":"AAQO,8EAMQ,CAAC,EACA,QAAQ,eACR,QAAQ,qBACX,CAAC,yBACO,QAAQ,CAAC,CAAC,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,sCAC9C,GAAG,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,kBAC/C,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAmD1C;mBAIa,UAAU,CAAC,OAAO,QAAQ,CAAC;uCAxEO,YAAY;6BAAZ,YAAY"}
package/src/when.js CHANGED
@@ -28,11 +28,19 @@ export const makeWhen = (
28
28
  let result = await specimenP;
29
29
  let payload = getVowPayload(result);
30
30
  let priorRetryValue;
31
+ const seenPayloads = new WeakSet();
31
32
  while (payload) {
32
- result = await basicE(payload.vowV0)
33
+ // TODO: rely on endowed helpers for getting storable cap and performing
34
+ // shorten "next step"
35
+ const { vowV0 } = payload;
36
+ if (seenPayloads.has(vowV0)) {
37
+ throw Error('Vow resolution cycle detected');
38
+ }
39
+ result = await basicE(vowV0)
33
40
  .shorten()
34
41
  .then(
35
42
  res => {
43
+ seenPayloads.add(vowV0);
36
44
  priorRetryValue = undefined;
37
45
  return res;
38
46
  },