@agoric/async-flow 0.1.1-upgrade-16-dev-d492653.0 → 0.1.1-upgrade-17-dev-3b97a9f.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 (46) hide show
  1. package/index.d.ts +2 -0
  2. package/index.js +2 -0
  3. package/package.json +20 -19
  4. package/src/async-flow.d.ts +19 -12
  5. package/src/async-flow.d.ts.map +1 -1
  6. package/src/async-flow.js +31 -16
  7. package/src/bijection.d.ts +7 -4
  8. package/src/bijection.d.ts.map +1 -1
  9. package/src/bijection.js +90 -15
  10. package/src/convert.d.ts +1 -0
  11. package/src/convert.d.ts.map +1 -1
  12. package/src/convert.js +7 -5
  13. package/src/endowments.d.ts +16 -0
  14. package/src/endowments.d.ts.map +1 -0
  15. package/src/endowments.js +294 -0
  16. package/src/ephemera.d.ts +1 -0
  17. package/src/ephemera.d.ts.map +1 -1
  18. package/src/ephemera.js +4 -0
  19. package/src/equate.js +4 -4
  20. package/src/log-store.d.ts +4 -2
  21. package/src/log-store.d.ts.map +1 -1
  22. package/src/log-store.js +5 -1
  23. package/src/replay-membrane.d.ts +19 -50
  24. package/src/replay-membrane.d.ts.map +1 -1
  25. package/src/replay-membrane.js +218 -18
  26. package/src/type-guards.d.ts.map +1 -1
  27. package/src/type-guards.js +21 -7
  28. package/src/types.d.ts +211 -60
  29. package/src/types.js +1 -164
  30. package/test/async-flow-crank.test.js +6 -0
  31. package/test/async-flow-early-completion.test.js +2 -0
  32. package/test/async-flow-no-this.js +6 -0
  33. package/test/async-flow.test.js +5 -2
  34. package/test/bad-host.test.js +5 -0
  35. package/test/bijection.test.js +12 -6
  36. package/test/convert.test.js +5 -0
  37. package/test/endowments.test.js +157 -0
  38. package/test/equate.test.js +6 -2
  39. package/test/log-store.test.js +9 -1
  40. package/test/replay-membrane-eventual.test.js +134 -8
  41. package/test/replay-membrane-settlement.test.js +24 -5
  42. package/test/replay-membrane-zombie.test.js +43 -14
  43. package/test/replay-membrane.test.js +39 -13
  44. package/test/types.test-d.ts +73 -0
  45. package/tsconfig.json +2 -0
  46. package/src/types.d.ts.map +0 -1
package/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from "./src/async-flow.js";
2
+ export * from "./src/types.js";
3
+ export { makeSharedStateRecord } from "./src/endowments.js";
2
4
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1 +1,3 @@
1
1
  export * from './src/async-flow.js';
2
+ export * from './src/types.js';
3
+ export { makeSharedStateRecord } from './src/endowments.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/async-flow",
3
- "version": "0.1.1-upgrade-16-dev-d492653.0+d492653",
3
+ "version": "0.1.1-upgrade-17-dev-3b97a9f.0+3b97a9f",
4
4
  "description": "Upgrade async functions at await points by replay",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/Agoric/agoric-sdk",
@@ -24,24 +24,25 @@
24
24
  "author": "Agoric",
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "@agoric/base-zone": "0.1.1-upgrade-16-dev-d492653.0+d492653",
28
- "@agoric/store": "0.9.3-upgrade-16-dev-d492653.0+d492653",
29
- "@agoric/vow": "0.1.1-upgrade-16-dev-d492653.0+d492653",
30
- "@endo/common": "^1.2.2",
31
- "@endo/errors": "^1.2.2",
32
- "@endo/eventual-send": "^1.2.2",
33
- "@endo/marshal": "^1.5.0",
34
- "@endo/pass-style": "^1.4.0",
35
- "@endo/patterns": "^1.4.0",
36
- "@endo/promise-kit": "^1.1.2"
27
+ "@agoric/base-zone": "0.1.1-upgrade-17-dev-3b97a9f.0+3b97a9f",
28
+ "@agoric/internal": "0.3.3-upgrade-17-dev-3b97a9f.0+3b97a9f",
29
+ "@agoric/store": "0.9.3-upgrade-17-dev-3b97a9f.0+3b97a9f",
30
+ "@agoric/vow": "0.1.1-upgrade-17-dev-3b97a9f.0+3b97a9f",
31
+ "@endo/common": "^1.2.5",
32
+ "@endo/errors": "^1.2.5",
33
+ "@endo/eventual-send": "^1.2.5",
34
+ "@endo/marshal": "^1.5.3",
35
+ "@endo/pass-style": "^1.4.3",
36
+ "@endo/patterns": "^1.4.3",
37
+ "@endo/promise-kit": "^1.1.5"
37
38
  },
38
39
  "devDependencies": {
39
- "@agoric/internal": "0.3.3-upgrade-16-dev-d492653.0+d492653",
40
- "@agoric/swingset-liveslots": "0.10.3-upgrade-16-dev-d492653.0+d492653",
41
- "@agoric/zone": "0.2.3-upgrade-16-dev-d492653.0+d492653",
42
- "@endo/env-options": "^1.1.4",
43
- "@endo/ses-ava": "^1.2.2",
44
- "ava": "^5.3.0"
40
+ "@agoric/swingset-liveslots": "0.10.3-upgrade-17-dev-3b97a9f.0+3b97a9f",
41
+ "@agoric/zone": "0.2.3-upgrade-17-dev-3b97a9f.0+3b97a9f",
42
+ "@endo/env-options": "^1.1.6",
43
+ "@endo/ses-ava": "^1.2.5",
44
+ "ava": "^5.3.0",
45
+ "tsd": "^0.31.1"
45
46
  },
46
47
  "publishConfig": {
47
48
  "access": "public"
@@ -60,7 +61,7 @@
60
61
  "workerThreads": false
61
62
  },
62
63
  "typeCoverage": {
63
- "atLeast": 77.83
64
+ "atLeast": 77.1
64
65
  },
65
- "gitHead": "d49265372b3cdd681558791b0c780e1c3062bdcc"
66
+ "gitHead": "3b97a9f0bc7d5dcd24d20dd81ca2af5d59d6e43d"
66
67
  }
@@ -15,8 +15,8 @@ export function prepareAsyncFlowTools(outerZone: Zone, outerOptions?: Preparatio
15
15
  */
16
16
  restart(eager?: boolean | undefined): void;
17
17
  wake(): void;
18
- getOutcome(): Vow<any>;
19
- dump(): ([op: "doFulfill", vow: Vow<Passable>, fulfillment: Passable] | [op: "doReject", vow: Vow<Passable>, reason: Passable] | [op: "doReturn", callIndex: number, result: Passable] | [op: "doThrow", callIndex: number, problem: Passable] | [op: "checkCall", target: Passable, optVerb: PropertyKey | undefined, args: Passable[], callIndex: number])[];
18
+ getOutcome(): import("@agoric/vow").Vow<any>;
19
+ dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSendOnly", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSend", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
20
20
  getOptFatalProblem(): any;
21
21
  };
22
22
  admin: {
@@ -29,19 +29,20 @@ export function prepareAsyncFlowTools(outerZone: Zone, outerOptions?: Preparatio
29
29
  onRejected(_fulfillment: any): void;
30
30
  };
31
31
  }>;
32
- asyncFlow: (zone: Zone, tag: string, guestFunc: GuestAsyncFunc, options?: {
32
+ asyncFlow: <F extends GuestAsyncFunc>(zone: Zone, tag: string, guestFunc: F, options?: {
33
33
  startEager?: boolean;
34
- } | undefined) => HostAsyncFuncWrapper;
34
+ } | undefined) => HostOf<F>;
35
35
  adminAsyncFlow: import("@endo/exo").Guarded<{
36
- getFailures(): import("@endo/patterns").CopyMap<any, Passable>;
36
+ getFailures(): import("@endo/patterns").CopyMap<any, import("@endo/pass-style").Passable>;
37
37
  wakeAll(): void;
38
38
  getFlowForOutcomeVow(outcomeVow: any): any;
39
39
  }>;
40
40
  allWokenP: Promise<void>;
41
+ prepareEndowment: (zone: Zone, tag: string, e: unknown) => any;
41
42
  };
42
43
  export type AsyncFlowTools = ReturnType<(outerZone: Zone, outerOptions?: PreparationOptions | undefined) => {
43
44
  prepareAsyncFlowKit: (zone: Zone, tag: string, guestAsyncFunc: GuestAsyncFunc, options?: {
44
- startEager?: boolean | undefined;
45
+ startEager?: boolean;
45
46
  } | undefined) => (activationArgs: any) => import("@endo/exo").GuardedKit<{
46
47
  flow: {
47
48
  /**
@@ -56,8 +57,8 @@ export type AsyncFlowTools = ReturnType<(outerZone: Zone, outerOptions?: Prepara
56
57
  */
57
58
  restart(eager?: boolean | undefined): void;
58
59
  wake(): void;
59
- getOutcome(): Vow<any>;
60
- dump(): ([op: "doFulfill", vow: Vow<Passable>, fulfillment: Passable] | [op: "doReject", vow: Vow<Passable>, reason: Passable] | [op: "doReturn", callIndex: number, result: Passable] | [op: "doThrow", callIndex: number, problem: Passable] | [op: "checkCall", target: Passable, optVerb: PropertyKey | undefined, args: Passable[], callIndex: number])[];
60
+ getOutcome(): import("@agoric/vow").Vow<any>;
61
+ dump(): ([op: "doFulfill", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, fulfillment: import("@endo/pass-style").Passable] | [op: "doReject", vow: import("@agoric/vow").Vow<import("@endo/pass-style").Passable>, reason: import("@endo/pass-style").Passable] | [op: "doReturn", callIndex: number, result: import("@endo/pass-style").Passable] | [op: "doThrow", callIndex: number, problem: import("@endo/pass-style").Passable] | [op: "checkCall", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSendOnly", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number] | [op: "checkSend", target: import("@endo/pass-style").Passable, optVerb: PropertyKey | undefined, args: import("@endo/pass-style").Passable[], callIndex: number])[];
61
62
  getOptFatalProblem(): any;
62
63
  };
63
64
  admin: {
@@ -70,18 +71,24 @@ export type AsyncFlowTools = ReturnType<(outerZone: Zone, outerOptions?: Prepara
70
71
  onRejected(_fulfillment: any): void;
71
72
  };
72
73
  }>;
73
- asyncFlow: (zone: Zone, tag: string, guestFunc: GuestAsyncFunc, options?: {
74
- startEager?: boolean | undefined;
75
- } | undefined) => HostAsyncFuncWrapper;
74
+ asyncFlow: <F extends GuestAsyncFunc>(zone: Zone, tag: string, guestFunc: F, options?: {
75
+ startEager?: boolean;
76
+ } | undefined) => HostOf<F>;
76
77
  adminAsyncFlow: import("@endo/exo").Guarded<{
77
- getFailures(): import("@endo/patterns").CopyMap<any, Passable>;
78
+ getFailures(): import("@endo/patterns").CopyMap<any, import("@endo/pass-style").Passable>;
78
79
  wakeAll(): void;
79
80
  getFlowForOutcomeVow(outcomeVow: any): any;
80
81
  }>;
81
82
  allWokenP: Promise<void>;
83
+ prepareEndowment: (zone: Zone, tag: string, e: unknown) => any;
82
84
  }>;
83
85
  export type AdminAsyncFlow = AsyncFlowTools["adminAsyncFlow"];
84
86
  export type MakeAsyncFlowKit = ReturnType<AsyncFlowTools["prepareAsyncFlowKit"]>;
85
87
  export type AsyncFlowKit = ReturnType<MakeAsyncFlowKit>;
86
88
  export type AsyncFlow = AsyncFlowKit["flow"];
89
+ import type { Zone } from '@agoric/base-zone';
90
+ import type { PreparationOptions } from '../src/types.js';
91
+ import type { GuestAsyncFunc } from '../src/types.js';
92
+ import type { FlowState } from '../src/types.js';
93
+ import type { HostOf } from '../src/types.js';
87
94
  //# sourceMappingURL=async-flow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"async-flow.d.ts","sourceRoot":"","sources":["async-flow.js"],"names":[],"mappings":"AA4CO;2CAuCM,MAAM,kBACN,cAAc;qBACC,OAAO;;;YA4BzB;;eAEG;4BADU,SAAS;YAkCtB;;;;;eAKG;;;;;;;;;;;;;;;;;iCAkRA,MAAM,aACN,cAAc;qBACC,OAAO;sBACpB,oBAAoB;;;;;;;EAkDlC;6BAIY,UAAU;2CAjZV,MAAM,kBACN,cAAc;;;;YA6BjB;;eAEG;;YAiCH;;;;;eAKG;;;;;;;;;;;;;;;;;iCAkRA,MAAM,aACN,cAAc;;;;;;;;;EAwDmB;6BAIjC,cAAc,CAAC,gBAAgB,CAAC;+BAIhC,UAAU,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAC;2BAIjD,UAAU,CAAC,gBAAgB,CAAC;wBAI5B,YAAY,CAAC,MAAM,CAAC"}
1
+ {"version":3,"file":"async-flow.d.ts","sourceRoot":"","sources":["async-flow.js"],"names":[],"mappings":"AAgDO,iDAHI,IAAI;gCA6CF,IAAI,OACJ,MAAM,kBACN,cAAc;qBACC,OAAO;;;YA4BzB;;eAEG;4BADU,SAAS;YAkCtB;;;;;eAKG;;;;;;;;;;;;;;;;;gBAmRmB,CAAC,SAAlB,cAAgB,QAClB,IAAI,OACJ,MAAM,aACN,CAAC;qBACc,OAAO;sBACpB,OAAO,CAAC,CAAC;;;;;;;;EAsDvB;6BAIY,UAAU,aAtcZ,IAAI;gCA6CF,IAAI,OACJ,MAAM,kBACN,cAAc;qBACC,OAAO;;;YA4BzB;;eAEG;4BADU,SAAS;YAkCtB;;;;;eAKG;;;;;;;;;;;;;;;;;gBAmRmB,CAAC,SAAlB,cAAgB,QAClB,IAAI,OACJ,MAAM,aACN,CAAC;qBACc,OAAO;sBACpB,OAAO,CAAC,CAAC;;;;;;;;EA0DsB;6BAIjC,cAAc,CAAC,gBAAgB,CAAC;+BAIhC,UAAU,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAC;2BAIjD,UAAU,CAAC,gBAAgB,CAAC;wBAI5B,YAAY,CAAC,MAAM,CAAC;0BArfV,mBAAmB;wCACoD,iBAAiB;oCAAjB,iBAAiB;+BAAjB,iBAAiB;4BAAjB,iBAAiB"}
package/src/async-flow.js CHANGED
@@ -7,10 +7,14 @@ import { prepareVowTools, toPassableCap, VowShape } from '@agoric/vow';
7
7
  import { makeReplayMembrane } from './replay-membrane.js';
8
8
  import { prepareLogStore } from './log-store.js';
9
9
  import { prepareBijection } from './bijection.js';
10
+ import { prepareEndowmentTools } from './endowments.js';
10
11
  import { LogEntryShape, FlowStateShape } from './type-guards.js';
11
12
 
12
13
  /**
13
- * @import { WeakMapStore } from '@agoric/store'
14
+ * @import {WeakMapStore} from '@agoric/store'
15
+ * @import {Zone} from '@agoric/base-zone'
16
+ * @import {FlowState, GuestAsyncFunc, HostAsyncFuncWrapper, HostOf, PreparationOptions} from '../src/types.js'
17
+ * @import {ReplayMembrane} from '../src/replay-membrane.js'
14
18
  */
15
19
 
16
20
  const { defineProperties } = Object;
@@ -46,7 +50,11 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
46
50
  const {
47
51
  vowTools = prepareVowTools(outerZone),
48
52
  makeLogStore = prepareLogStore(outerZone),
49
- makeBijection = prepareBijection(outerZone),
53
+ endowmentTools: { prepareEndowment, unwrap } = prepareEndowmentTools(
54
+ outerZone,
55
+ { vowTools },
56
+ ),
57
+ makeBijection = prepareBijection(outerZone, unwrap),
50
58
  } = outerOptions;
51
59
  const { watch, makeVowKit } = vowTools;
52
60
 
@@ -170,7 +178,7 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
170
178
  eagerWakers.delete(flow);
171
179
  }
172
180
 
173
- const wakeWatch = vowish => {
181
+ const watchWake = vowish => {
174
182
  // Extra paranoid because we're getting
175
183
  // "promise watcher must be a virtual object"
176
184
  // in the general vicinity.
@@ -181,13 +189,13 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
181
189
  watch(vowish, wakeWatcher);
182
190
  };
183
191
  const panic = err => admin.panic(err);
184
- const membrane = makeReplayMembrane(
192
+ const membrane = makeReplayMembrane({
185
193
  log,
186
194
  bijection,
187
195
  vowTools,
188
- wakeWatch,
196
+ watchWake,
189
197
  panic,
190
- );
198
+ });
191
199
  initMembrane(flow, membrane);
192
200
  const guestArgs = membrane.hostToGuest(activationArgs);
193
201
 
@@ -218,7 +226,9 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
218
226
  // gating condition, the next line could grow the bijection
219
227
  // of a failed flow, subverting other gating checks on bijection
220
228
  // membership.
221
- bijection.init(guestResultP, outcomeKit.vow);
229
+ const g = bijection.unwrapInit(guestResultP, outcomeKit.vow);
230
+ g === guestResultP ||
231
+ Fail`internal: promises should not be unwrapped ${g}`;
222
232
  }
223
233
  // log is driven at first by guestAyncFunc interaction through the
224
234
  // membrane with the host activationArgs. At the end of its first
@@ -415,7 +425,7 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
415
425
  const asyncFlowKit = internalMakeAsyncFlowKit(activationArgs);
416
426
  const { flow } = asyncFlowKit;
417
427
 
418
- const vow = toPassableCap(flow.getOutcome());
428
+ const vow = flow.getOutcome();
419
429
  flowForOutcomeVowKey.init(toPassableCap(vow), flow);
420
430
  flow.restart();
421
431
  return asyncFlowKit;
@@ -424,21 +434,25 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
424
434
  };
425
435
 
426
436
  /**
437
+ * @template {GuestAsyncFunc} F
427
438
  * @param {Zone} zone
428
439
  * @param {string} tag
429
- * @param {GuestAsyncFunc} guestFunc
440
+ * @param {F} guestFunc
430
441
  * @param {{ startEager?: boolean }} [options]
431
- * @returns {HostAsyncFuncWrapper}
442
+ * @returns {HostOf<F>}
432
443
  */
433
444
  const asyncFlow = (zone, tag, guestFunc, options = undefined) => {
434
445
  const makeAsyncFlowKit = prepareAsyncFlowKit(zone, tag, guestFunc, options);
435
446
  const hostFuncName = `${tag}_hostFlow`;
436
- const wrapperFunc = {
437
- [hostFuncName](...args) {
438
- const { flow } = makeAsyncFlowKit(args);
439
- return flow.getOutcome();
440
- },
441
- }[hostFuncName];
447
+
448
+ const wrapperFunc = /** @type {HostOf<F>} */ (
449
+ {
450
+ [hostFuncName](...args) {
451
+ const { flow } = makeAsyncFlowKit(args);
452
+ return flow.getOutcome();
453
+ },
454
+ }[hostFuncName]
455
+ );
442
456
  defineProperties(wrapperFunc, {
443
457
  length: { value: guestFunc.length },
444
458
  });
@@ -477,6 +491,7 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
477
491
  asyncFlow,
478
492
  adminAsyncFlow,
479
493
  allWokenP,
494
+ prepareEndowment,
480
495
  });
481
496
  };
482
497
  harden(prepareAsyncFlowTools);
@@ -1,6 +1,6 @@
1
- export function prepareBijection(zone: Zone): () => import("@endo/exo").Guarded<{
1
+ export function prepareBijection(zone: Zone, unwrap?: ((hostWrapper: PassableCap | Vow, guestWrapper: PassableCap) => unknown) | undefined): () => import("@endo/exo").Guarded<{
2
2
  reset(): void;
3
- init(g: any, h: any): void;
3
+ unwrapInit(g: any, h: any): unknown;
4
4
  hasGuest(g: any): boolean;
5
5
  hasHost(h: any): boolean;
6
6
  has(g: any, h: any): boolean;
@@ -16,13 +16,16 @@ export type VowishStore = ReturnType<(name: string) => {
16
16
  has: (k: any) => boolean;
17
17
  get: (k: any) => any;
18
18
  }>>;
19
- export type Bijection = ReturnType<ReturnType<(zone: Zone) => () => import("@endo/exo").Guarded<{
19
+ export type Bijection = ReturnType<ReturnType<(zone: Zone, unwrap?: ((hostWrapper: PassableCap | Vow, guestWrapper: PassableCap) => unknown) | undefined) => () => import("@endo/exo").Guarded<{
20
20
  reset(): void;
21
- init(g: any, h: any): void;
21
+ unwrapInit(g: any, h: any): unknown;
22
22
  hasGuest(g: any): boolean;
23
23
  hasHost(h: any): boolean;
24
24
  has(g: any, h: any): boolean;
25
25
  guestToHost(g: any): any;
26
26
  hostToGuest(h: any): any;
27
27
  }>>>;
28
+ import type { Zone } from '@agoric/base-zone';
29
+ import type { PassableCap } from '@endo/pass-style';
30
+ import type { Vow } from '@agoric/vow';
28
31
  //# sourceMappingURL=bijection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bijection.d.ts","sourceRoot":"","sources":["bijection.js"],"names":[],"mappings":"AA0DO;;;;;;;;GAoEN;0BAzEa,UAAU,QAhCb,MAAM;;;;;;;;GAgCwB;wBA6E5B,UAAU,CAAC,UAAU;;;;;;;;GAAkB,CAAC"}
1
+ {"version":3,"file":"bijection.d.ts","sourceRoot":"","sources":["bijection.js"],"names":[],"mappings":"AA+GO,uCAJI,IAAI,0BACU,WAAW,GAAG,GAAG,gBAAgB,WAAW,KAAK,OAAO;;;;;;;;GA6FhF;0BAtIa,UAAU,QAhCb,MAAM;;;;;;;;GAgCwB;wBA0I5B,UAAU,CAAC,UAAU,QAlGvB,IAAI,0BACU,WAAW,GAAG,GAAG,gBAAgB,WAAW,KAAK,OAAO;;;;;;;;GAiG7B,CAAC;0BArM9B,mBAAmB;iCADZ,kBAAkB;yBAE1B,aAAa"}
package/src/bijection.js CHANGED
@@ -1,19 +1,33 @@
1
1
  import { b, Fail } from '@endo/errors';
2
2
  import { M } from '@endo/patterns';
3
- import { Far } from '@endo/pass-style';
3
+ import { Far, isPassable } from '@endo/pass-style';
4
4
  import { toPassableCap } from '@agoric/vow';
5
5
  import { makeEphemera } from './ephemera.js';
6
6
 
7
+ /**
8
+ * @import {PassableCap} from '@endo/pass-style'
9
+ * @import {Zone} from '@agoric/base-zone'
10
+ * @import {Vow} from '@agoric/vow'
11
+ * @import {Ephemera} from './types.js';
12
+ */
13
+
7
14
  const BijectionI = M.interface('Bijection', {
8
15
  reset: M.call().returns(),
9
- init: M.call(M.any(), M.any()).returns(),
10
- hasGuest: M.call(M.any()).returns(M.boolean()),
16
+ unwrapInit: M.call(M.raw(), M.any()).returns(M.raw()),
17
+ hasGuest: M.call(M.raw()).returns(M.boolean()),
11
18
  hasHost: M.call(M.any()).returns(M.boolean()),
12
- has: M.call(M.any(), M.any()).returns(M.boolean()),
13
- guestToHost: M.call(M.any()).returns(M.any()),
14
- hostToGuest: M.call(M.any()).returns(M.any()),
19
+ has: M.call(M.raw(), M.any()).returns(M.boolean()),
20
+ guestToHost: M.call(M.raw()).returns(M.any()),
21
+ hostToGuest: M.call(M.any()).returns(M.raw()),
15
22
  });
16
23
 
24
+ /**
25
+ * @param {unknown} k
26
+ */
27
+ const toKey = k =>
28
+ // @ts-expect-error k specificity
29
+ isPassable(k) ? toPassableCap(k) : k;
30
+
17
31
  /**
18
32
  * Makes a store like a WeakMapStore except that Promises and Vows can also be
19
33
  * used as keys.
@@ -34,15 +48,15 @@ const makeVowishStore = name => {
34
48
 
35
49
  return Far(name, {
36
50
  init: (k, v) => {
37
- const k2 = toPassableCap(k);
51
+ const k2 = toKey(k);
38
52
  !map.has(k2) ||
39
53
  // separate line so I can set a breakpoint
40
54
  Fail`${b(name)} key already bound: ${k} -> ${map.get(k2)} vs ${v}`;
41
55
  map.set(k2, v);
42
56
  },
43
- has: k => map.has(toPassableCap(k)),
57
+ has: k => map.has(toKey(k)),
44
58
  get: k => {
45
- const k2 = toPassableCap(k);
59
+ const k2 = toKey(k);
46
60
  map.has(k2) ||
47
61
  // separate line so I can set a breakpoint
48
62
  Fail`${b(name)} key not found: ${k}`;
@@ -54,14 +68,59 @@ const makeVowishStore = name => {
54
68
  /** @typedef {ReturnType<makeVowishStore>} VowishStore */
55
69
 
56
70
  /**
71
+ * As suggested by the name, this *mostly* represents a mathematical bijection,
72
+ * i.e., a one-to-one mapping. But rather than a general bijection map store
73
+ * (which would be interesting), this one is specialized to support the
74
+ * async-flow replay-membrane, where the two sides are "guest" and "host".
75
+ *
76
+ * If `unwrap` is omitted, it defaults to an identity function on its
77
+ * `guestWrapper` argument, in which case this does represent exactly a
78
+ * mathematical bijection between host and guest.
79
+ *
80
+ * If `unwrap` is provided, it supports the unwrapping of guest wrappers, into
81
+ * so-call unwrapped guests, like state records or functions,
82
+ * that are not themselves `Passable`. This was motivated to support endowments,
83
+ * which are often similar non-passables on the host-side.
84
+ * However, it can support the unwrapping of any guest remotable wrapper.
85
+ * When `unwrap` returns something `!==` its `guestWrapper` argument,
86
+ * then we preserve the bijection (one-to-one mapping) between the host
87
+ * and the unwrapped guest. To support the internal bookkeeping of the
88
+ * replay-membrane, we also map the guestWrapper to that same host, but
89
+ * not vice versa. Since the guest wrapper should not be visible outside
90
+ * the replay-membrane, this extra bookkeeping should be invisible.
91
+ *
92
+ * This bijection only grows monotonically until reset, which clears the entire
93
+ * mapping. Until reset, each pair, once entered, cannot be altered or deleted.
94
+ * The mapping itself is completely ephemeral, but the bijection object itself
95
+ * is durable. The mapping is also effectively reset by reincarnation, i.e,
96
+ * on upgrade.
97
+ * See also https://github.com/Agoric/agoric-sdk/issues/9365
98
+ *
99
+ * To eventually address https://github.com/Agoric/agoric-sdk/issues/9301
100
+ * the bijection itself persists to support passing guest-created remotables
101
+ * and promises through the membrane.
102
+ * The resulting host wrappers must not only survive upgrade, then must
103
+ * reestablish their mapping to the correct corresponding guest objects that
104
+ * they are taken to wrap. We plan to do this via `equate` repopulating
105
+ * the bijection by the time the host wrapper needs to know what
106
+ * corresponding guest it is now taken to wrap.
107
+ *
57
108
  * @param {Zone} zone
109
+ * @param {(hostWrapper: PassableCap | Vow, guestWrapper: PassableCap) => unknown} [unwrap]
110
+ * defaults to identity function on `guestWrapper` arg
58
111
  */
59
- export const prepareBijection = zone => {
112
+ export const prepareBijection = (
113
+ zone,
114
+ unwrap = (_hostWrapper, guestWrapper) => guestWrapper,
115
+ ) => {
60
116
  /** @type {Ephemera<Bijection, VowishStore>} */
61
117
  const g2h = makeEphemera(() => makeVowishStore('guestToHost'));
62
118
  /** @type {Ephemera<Bijection, VowishStore>} */
63
119
  const h2g = makeEphemera(() => makeVowishStore('hostToGuest'));
64
120
 
121
+ // Guest arguments and results are now unguarded, i.e., guarded by `M.raw()`,
122
+ // so that they can be non-passables. Therefore, we need to harden these
123
+ // here.
65
124
  return zone.exoClass('Bijection', BijectionI, () => ({}), {
66
125
  reset() {
67
126
  const { self } = this;
@@ -69,20 +128,31 @@ export const prepareBijection = zone => {
69
128
  g2h.resetFor(self);
70
129
  h2g.resetFor(self);
71
130
  },
72
- init(g, h) {
131
+ unwrapInit(g, h) {
132
+ harden(g);
73
133
  const { self } = this;
74
134
  const guestToHost = g2h.for(self);
75
135
  const hostToGuest = h2g.for(self);
76
136
 
137
+ const gUnwrapped = unwrap(h, g);
77
138
  !hostToGuest.has(h) ||
78
- Fail`hostToGuest key already bound: ${h} -> ${hostToGuest.get(h)} vs ${g}`;
79
- guestToHost.init(g, h);
80
- hostToGuest.init(h, g);
81
- self.has(g, h) ||
139
+ Fail`hostToGuest key already bound: ${h} -> ${hostToGuest.get(h)} vs ${gUnwrapped}`;
140
+ guestToHost.init(gUnwrapped, h);
141
+ hostToGuest.init(h, gUnwrapped);
142
+ self.has(gUnwrapped, h) ||
82
143
  // separate line so I can set a breakpoint
83
144
  Fail`internal: ${g} <-> ${h}`;
145
+ if (g !== gUnwrapped) {
146
+ // When they are different, also map g to h without mapping h to g
147
+ !guestToHost.has(g) ||
148
+ // separate line so I can set a breakpoint
149
+ Fail`hidden guest wrapper already bound ${g}`;
150
+ guestToHost.init(g, h);
151
+ }
152
+ return gUnwrapped;
84
153
  },
85
154
  hasGuest(g) {
155
+ harden(g);
86
156
  const { self } = this;
87
157
  const guestToHost = g2h.for(self);
88
158
 
@@ -95,6 +165,7 @@ export const prepareBijection = zone => {
95
165
  return hostToGuest.has(h);
96
166
  },
97
167
  has(g, h) {
168
+ harden(g);
98
169
  const { self } = this;
99
170
  const guestToHost = g2h.for(self);
100
171
  const hostToGuest = h2g.for(self);
@@ -112,6 +183,7 @@ export const prepareBijection = zone => {
112
183
  }
113
184
  },
114
185
  guestToHost(g) {
186
+ harden(g);
115
187
  const { self } = this;
116
188
  const guestToHost = g2h.for(self);
117
189
 
@@ -121,6 +193,9 @@ export const prepareBijection = zone => {
121
193
  const { self } = this;
122
194
  const hostToGuest = h2g.for(self);
123
195
 
196
+ // Even though result is unguarded, i.e., guarded by `M.raw()`, don't
197
+ // need to harden here because was already harden when added to
198
+ // collection.
124
199
  return hostToGuest.get(h);
125
200
  },
126
201
  });
package/src/convert.d.ts CHANGED
@@ -2,4 +2,5 @@ export function makeConvertKit(bijection: any, makeGuestForHostRemotable: any, m
2
2
  guestToHost: (specimen: Passable, label?: string | undefined) => any;
3
3
  hostToGuest: (specimen: Passable, label?: string | undefined) => any;
4
4
  };
5
+ import type { Passable } from '@endo/pass-style';
5
6
  //# sourceMappingURL=convert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["convert.js"],"names":[],"mappings":"AA2EO;;;EAsDN"}
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["convert.js"],"names":[],"mappings":"AA+EO;4BARM,QAAQ;4BAAR,QAAQ;EA4DpB;8BAtH0B,kBAAkB"}
package/src/convert.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Fail, X, annotateError, makeError, q } from '@endo/errors';
2
2
  import { throwLabeled } from '@endo/common/throw-labeled.js';
3
+ import { objectMap } from '@endo/common/object-map.js';
3
4
  import {
4
5
  getErrorConstructor,
5
6
  getTag,
@@ -8,7 +9,10 @@ import {
8
9
  passStyleOf,
9
10
  } from '@endo/pass-style';
10
11
  import { isVow } from '@agoric/vow/src/vow-utils.js';
11
- import { objectMap } from '@endo/common/object-map.js';
12
+
13
+ /**
14
+ * @import {Passable} from '@endo/pass-style'
15
+ */
12
16
 
13
17
  const makeConvert = (convertRemotable, convertPromiseOrVow, convertError) => {
14
18
  const convertRecur = (specimen, label) => {
@@ -106,16 +110,14 @@ export const makeConvertKit = (
106
110
  return bijection.hostToGuest(hRem);
107
111
  }
108
112
  const gRem = makeGuestForHostRemotable(hRem);
109
- bijection.init(gRem, hRem);
110
- return gRem;
113
+ return bijection.unwrapInit(gRem, hRem);
111
114
  },
112
115
  hVow => {
113
116
  if (bijection.hasHost(hVow)) {
114
117
  return bijection.hostToGuest(hVow);
115
118
  }
116
119
  const gP = makeGuestForHostVow(hVow);
117
- bijection.init(gP, hVow);
118
- return gP;
120
+ return bijection.unwrapInit(gP, hVow);
119
121
  },
120
122
  hErr => {
121
123
  const gErr = harden(
@@ -0,0 +1,16 @@
1
+ export function forwardingMethods(rem: any): {
2
+ [k: string]: any;
3
+ };
4
+ export function makeSharedStateRecord<K extends string | number | symbol, R extends Record<K, any>>(dataRecord: R): R;
5
+ export function prepareEndowmentTools(outerZone: Zone, outerOptions?: PreparationOptions | undefined): {
6
+ prepareEndowment: (zone: Zone, tag: string, e: unknown) => any;
7
+ unwrap: (wrapped: any, guestWrapped: any) => any;
8
+ };
9
+ export type EndowmentKind = "promise" | "storable" | "far" | "function" | "array" | "record" | "state";
10
+ export type EndowmentTools = ReturnType<(outerZone: Zone, outerOptions?: PreparationOptions | undefined) => {
11
+ prepareEndowment: (zone: Zone, tag: string, e: unknown) => any;
12
+ unwrap: (wrapped: any, guestWrapped: any) => any;
13
+ }>;
14
+ import type { Zone } from '@agoric/base-zone';
15
+ import type { PreparationOptions } from '../src/types.js';
16
+ //# sourceMappingURL=endowments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endowments.d.ts","sourceRoot":"","sources":["endowments.js"],"names":[],"mappings":"AA4CO;;EASN;AAaM,sCALmC,CAAC,SAA5B,MAAM,GAAG,MAAM,GAAG,MAAQ,EACX,CAAC,SAAjB,MAAM,CAAC,CAAC,EAAE,GAAG,CAAE,cAClB,CAAC,GACC,CAAC,CAqBX;AAMI,iDAHI,IAAI;6BA6GF,IAAI,OACJ,MAAM,KACN,OAAO;;EAyFnB;4BA9QY,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO;6BAkR1E,UAAU,aA5MZ,IAAI;6BA6GF,IAAI,OACJ,MAAM,KACN,OAAO;;EA6F0B;0BAxRvB,mBAAmB;wCAEL,iBAAiB"}