@agoric/async-flow 0.1.1-upgrade-17-dev-a1453b2.0 → 0.1.1-upgrade-18-dev-d7c994b.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 (43) hide show
  1. package/CHANGELOG.md +0 -20
  2. package/docs/async-flow-states.key +0 -0
  3. package/docs/async-flow-states.md +2 -1
  4. package/docs/async-flow-states.png +0 -0
  5. package/index.d.ts +1 -2
  6. package/index.js +1 -2
  7. package/package.json +18 -19
  8. package/src/async-flow.d.ts.map +1 -1
  9. package/src/async-flow.js +47 -3
  10. package/src/endowments.d.ts +0 -1
  11. package/src/endowments.d.ts.map +1 -1
  12. package/src/endowments.js +7 -35
  13. package/src/equate.d.ts.map +1 -1
  14. package/src/equate.js +16 -8
  15. package/src/log-store.d.ts +39 -2
  16. package/src/log-store.d.ts.map +1 -1
  17. package/src/log-store.js +121 -22
  18. package/src/replay-membrane.d.ts +23 -4
  19. package/src/replay-membrane.d.ts.map +1 -1
  20. package/src/replay-membrane.js +61 -35
  21. package/src/type-guards.d.ts.map +1 -1
  22. package/src/type-guards.js +1 -0
  23. package/src/types-index.d.ts +2 -0
  24. package/src/types.d.ts +86 -166
  25. package/src/types.d.ts.map +1 -0
  26. package/src/types.ts +213 -0
  27. package/test/_utils.js +22 -0
  28. package/test/async-flow-crank.test.js +4 -6
  29. package/test/async-flow-early-completion.test.js +28 -8
  30. package/test/async-flow-no-this.js +2 -4
  31. package/test/async-flow.test.js +13 -5
  32. package/test/bad-host.test.js +13 -3
  33. package/test/endowments.test.js +22 -2
  34. package/test/equate.test.js +5 -5
  35. package/test/exports.test.js +8 -0
  36. package/test/log-store.test.js +46 -11
  37. package/test/prepare-test-env-ava.js +2 -20
  38. package/test/replay-membrane-eventual.test.js +97 -41
  39. package/test/snapshots/exports.test.js.md +14 -0
  40. package/test/snapshots/exports.test.js.snap +0 -0
  41. package/tsconfig.build.tsbuildinfo +1 -0
  42. package/tsconfig.json +0 -3
  43. /package/src/{types.js → types-index.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,21 +1 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- ### 0.1.1-u17.0 (2024-09-17)
7
-
8
-
9
- ### Features
10
-
11
- * **async-flow:** asyncFlow ([#9097](https://github.com/Agoric/agoric-sdk/issues/9097)) ([16095c5](https://github.com/Agoric/agoric-sdk/commit/16095c5076043133aff0f25721131be2ca1ef5af)), closes [#9302](https://github.com/Agoric/agoric-sdk/issues/9302) [#9125](https://github.com/Agoric/agoric-sdk/issues/9125) [#9126](https://github.com/Agoric/agoric-sdk/issues/9126) [#9153](https://github.com/Agoric/agoric-sdk/issues/9153) [#9154](https://github.com/Agoric/agoric-sdk/issues/9154) [#9280](https://github.com/Agoric/agoric-sdk/issues/9280) [#9126](https://github.com/Agoric/agoric-sdk/issues/9126)
12
- * **async-flow:** endowments ([#9566](https://github.com/Agoric/agoric-sdk/issues/9566)) ([4390d8c](https://github.com/Agoric/agoric-sdk/commit/4390d8c21fd8ac80a9c83f55d38c52e3f98faa1e)), closes [Error#1](https://github.com/Agoric/Error/issues/1) [Error#1](https://github.com/Agoric/Error/issues/1)
13
- * **async-flow:** error on guest E use ([#9443](https://github.com/Agoric/agoric-sdk/issues/9443)) ([e193e66](https://github.com/Agoric/agoric-sdk/commit/e193e66fc578b1c00eda5f5ab6599f1cfbdc542f)), closes [#9299](https://github.com/Agoric/agoric-sdk/issues/9299) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322)
14
- * **asyncFlow:** Stopgap E support ([#9519](https://github.com/Agoric/agoric-sdk/issues/9519)) ([4adf64f](https://github.com/Agoric/agoric-sdk/commit/4adf64fd53a1a3c68ca52728710830201c9a4418)), closes [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9299](https://github.com/Agoric/agoric-sdk/issues/9299) [#9443](https://github.com/Agoric/agoric-sdk/issues/9443) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9299](https://github.com/Agoric/agoric-sdk/issues/9299) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322) [#9322](https://github.com/Agoric/agoric-sdk/issues/9322)
15
- * error on membrane promise ([94dfab1](https://github.com/Agoric/agoric-sdk/commit/94dfab1cfd960aada452753b1482ed797bc2da13))
16
- * **types:** HostOf, GuestOf ([35380af](https://github.com/Agoric/agoric-sdk/commit/35380af4bbda51be2a9cd047f9c4992791090e94))
17
- * **types:** infer orchestrate() return ([67665a8](https://github.com/Agoric/agoric-sdk/commit/67665a89cb162c350389e52fd812c895d42f37fb))
18
-
19
-
20
-
21
1
  # Change Log
Binary file
@@ -10,6 +10,7 @@
10
10
 
11
11
  - ***Replaying***. To start ***Replaying***, the activation first translates the saved activation arguments from host to guest, invokes the guest function, and starts the membrane replaying from its durable log. The replay is finished when the last log entry has been replayed. Once replaying is finished, the activation has caught up and transitions back to ***Running***.
12
12
 
13
- - ***Failed***. If during the ***Replaying*** state the guest activation fails to exactly reproduce its previously logged behavior, it goes into the inactive ***Failed*** state, with a diagnostic explaining how the replay failed, so it can be repaired by another future upgrade. As of the next reincarnation, the failure status is cleared and it starts ***Replaying*** again, hoping not to fail this time. If replay failed because the guest async function did not reproduce its previous behavior, then the upgrade needs to replace the function with one which does. If the replay failed because of a failure of the `asyncFlow` mechanism, whether a bug or merely hitting a case that is not yet implemented, then the upgrade needs to replace the relevant part of `asyncFlow`'s mechanism.
13
+ - ***Failed***. If the guest activation misbehaves during the ***Replaying*** state (by failing to exactly produce its previously logged behavior) or during the ***Running*** state (by an invalid or unsupported interaction with the `asyncFlow` mechanism), it goes into the inactive ***Failed*** state, with a diagnostic explaining how the replay failed, so it can be repaired by another future upgrade. As of the next reincarnation, the failure status is cleared and it starts ***Replaying***, then ***Running*** again, hoping not to fail this time. If replay or running failed because the previous guest async function misbehaved, then to make progress, an upgrade needs to replace the function with one which behaves correctly. If a ***Replaying*** or ***Running*** guest failed because of a failure of the `asyncFlow` mechanism, whether a bug or merely hitting a case that is not yet implemented, then an upgrade needs to replace the relevant part of the `asyncFlow`'s mechanism.
14
14
 
15
15
  - ***Done***. The guest async function invocation returned a promise for its eventual outcome. Once that promise settles, we assume that the job of the guest activation is done. It then goes into a durably ***Done*** state, dropping all its bookkeeping beyond just remembering the corresponding settled outcome vow, and that it is ***Done***. The replay logs and membrane state of this activation are dropped, to be garbage collected.
16
+
Binary file
package/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export * from "./src/async-flow.js";
2
- export * from "./src/types.js";
3
- export { makeSharedStateRecord } from "./src/endowments.js";
2
+ export * from "./src/types-index.js";
4
3
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from './src/async-flow.js';
2
- export * from './src/types.js';
3
- export { makeSharedStateRecord } from './src/endowments.js';
2
+ export * from './src/types-index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/async-flow",
3
- "version": "0.1.1-upgrade-17-dev-a1453b2.0+a1453b2",
3
+ "version": "0.1.1-upgrade-18-dev-d7c994b.0+d7c994b",
4
4
  "description": "Upgrade async functions at await points by replay",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/Agoric/agoric-sdk",
@@ -8,7 +8,7 @@
8
8
  "scripts": {
9
9
  "build": "exit 0",
10
10
  "prepack": "tsc --build tsconfig.build.json",
11
- "postpack": "git clean -f '*.d.ts*'",
11
+ "postpack": "git clean -f '*.d.ts*' '*.tsbuildinfo'",
12
12
  "test": "ava",
13
13
  "test:c8": "c8 $C8_OPTIONS ava --config=ava-nesm.config.js",
14
14
  "test:xs": "exit 0",
@@ -24,23 +24,22 @@
24
24
  "author": "Agoric",
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "@agoric/base-zone": "0.1.1-upgrade-17-dev-a1453b2.0+a1453b2",
28
- "@agoric/internal": "0.4.0-upgrade-17-dev-a1453b2.0+a1453b2",
29
- "@agoric/store": "0.9.3-upgrade-17-dev-a1453b2.0+a1453b2",
30
- "@agoric/vow": "0.2.0-upgrade-17-dev-a1453b2.0+a1453b2",
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"
27
+ "@agoric/base-zone": "0.1.1-upgrade-18-dev-d7c994b.0+d7c994b",
28
+ "@agoric/internal": "0.3.3-upgrade-18-dev-d7c994b.0+d7c994b",
29
+ "@agoric/store": "0.9.3-upgrade-18-dev-d7c994b.0+d7c994b",
30
+ "@agoric/vow": "0.1.1-upgrade-18-dev-d7c994b.0+d7c994b",
31
+ "@endo/common": "^1.2.7",
32
+ "@endo/errors": "^1.2.7",
33
+ "@endo/eventual-send": "^1.2.7",
34
+ "@endo/marshal": "^1.6.1",
35
+ "@endo/pass-style": "^1.4.6",
36
+ "@endo/patterns": "^1.4.6",
37
+ "@endo/promise-kit": "^1.1.7"
38
38
  },
39
39
  "devDependencies": {
40
- "@agoric/swingset-liveslots": "0.10.3-upgrade-17-dev-a1453b2.0+a1453b2",
41
- "@agoric/zone": "0.3.0-upgrade-17-dev-a1453b2.0+a1453b2",
42
- "@endo/env-options": "^1.1.6",
43
- "@endo/ses-ava": "^1.2.5",
40
+ "@agoric/swingset-vat": "0.32.3-upgrade-18-dev-d7c994b.0+d7c994b",
41
+ "@agoric/zone": "0.2.3-upgrade-18-dev-d7c994b.0+d7c994b",
42
+ "@endo/env-options": "^1.1.7",
44
43
  "ava": "^5.3.0",
45
44
  "tsd": "^0.31.1"
46
45
  },
@@ -61,7 +60,7 @@
61
60
  "workerThreads": false
62
61
  },
63
62
  "typeCoverage": {
64
- "atLeast": 77.1
63
+ "atLeast": 76.93
65
64
  },
66
- "gitHead": "a1453b2877b017a7f5b43a92364067d001901953"
65
+ "gitHead": "d7c994b8d33c0cd22b257f3e33b579588ab6c6d8"
67
66
  }
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"async-flow.d.ts","sourceRoot":"","sources":["async-flow.js"],"names":[],"mappings":"AAgDO,iDAHI,IAAI;gCAgDF,IAAI,OACJ,MAAM,kBACN,cAAc;qBACC,OAAO;;;YA4BzB;;eAEG;4BADU,SAAS;YAkCtB;;;;;eAKG;;;;;;;;;;;;;;;;;gBA4TmB,CAAC,SAAlB,cAAgB,QAClB,IAAI,OACJ,MAAM,aACN,CAAC;qBACc,OAAO;sBACpB,OAAO,CAAC,CAAC;;;;;;;;EAsDvB;6BAIY,UAAU,aAlfZ,IAAI;gCAgDF,IAAI,OACJ,MAAM,kBACN,cAAc;qBACC,OAAO;;;YA4BzB;;eAEG;4BADU,SAAS;YAkCtB;;;;;eAKG;;;;;;;;;;;;;;;;;gBA4TmB,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;0BAjiBV,mBAAmB;wCACoD,iBAAiB;oCAAjB,iBAAiB;+BAAjB,iBAAiB;4BAAjB,iBAAiB"}
package/src/async-flow.js CHANGED
@@ -11,7 +11,7 @@ import { prepareEndowmentTools } from './endowments.js';
11
11
  import { LogEntryShape, FlowStateShape } from './type-guards.js';
12
12
 
13
13
  /**
14
- * @import {WeakMapStore} from '@agoric/store'
14
+ * @import {WeakMapStore, MapStore} from '@agoric/store'
15
15
  * @import {Zone} from '@agoric/base-zone'
16
16
  * @import {FlowState, GuestAsyncFunc, HostAsyncFuncWrapper, HostOf, PreparationOptions} from '../src/types.js'
17
17
  * @import {ReplayMembrane} from '../src/replay-membrane.js'
@@ -55,6 +55,9 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
55
55
  { vowTools },
56
56
  ),
57
57
  makeBijection = prepareBijection(outerZone, unwrap),
58
+ panicHandler = err => {
59
+ throw err;
60
+ },
58
61
  } = outerOptions;
59
62
  const { watch, makeVowKit } = vowTools;
60
63
 
@@ -67,7 +70,7 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
67
70
  keyShape: M.remotable('flow'), // flowState !== 'Done'
68
71
  });
69
72
 
70
- /** @type WeakMapStore<AsyncFlow, ReplayMembrane> */
73
+ /** @type {WeakMapStore<AsyncFlow, ReplayMembrane>} */
71
74
  const membraneMap = makeScalarWeakMapStore('membraneFor', {
72
75
  keyShape: M.remotable('flow'),
73
76
  valueShape: M.remotable('membrane'),
@@ -247,6 +250,12 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
247
250
  guestResultP,
248
251
  gFulfillment => {
249
252
  if (bijection.hasGuest(guestResultP)) {
253
+ !log.isReplaying() ||
254
+ panic(
255
+ makeError(
256
+ X`guest fulfilled with ${gFulfillment} before finishing replay`,
257
+ ),
258
+ );
250
259
  outcomeKit.resolver.resolve(
251
260
  membrane.guestToHost(gFulfillment),
252
261
  );
@@ -262,11 +271,46 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
262
271
  // in the `guestResultP` being absent from the bijection,
263
272
  // so this leave the outcome vow unsettled, as it must.
264
273
  if (bijection.hasGuest(guestResultP)) {
274
+ !log.isReplaying() ||
275
+ panic(
276
+ makeError(
277
+ X`guest rejected with ${guestReason} before finishing replay`,
278
+ ),
279
+ );
265
280
  outcomeKit.resolver.reject(membrane.guestToHost(guestReason));
266
281
  admin.complete();
267
282
  }
268
283
  },
269
- );
284
+ )
285
+ .then(
286
+ () => {
287
+ if (flow.getFlowState() === 'Failed') {
288
+ // If the flow fails, we need to trigger the panic handler with
289
+ // the failure.
290
+ throw flow.getOptFatalProblem();
291
+ }
292
+ },
293
+ maybePanicReason => {
294
+ if (flow.getFlowState() === 'Failed') {
295
+ const err = flow.getOptFatalProblem();
296
+ // TODO: Annotate maybePanicReason robustly with err if it
297
+ // is indeed not the same as one we already threw from
298
+ // panic.
299
+ throw err;
300
+ }
301
+
302
+ // Definitely not a reason from an existing panic, so raise a new panic.
303
+ const err = makeError(
304
+ X`internal: unexpected error in guest completion handling ${maybePanicReason}`,
305
+ );
306
+ try {
307
+ panic(err);
308
+ } catch (_e) {
309
+ throw err;
310
+ }
311
+ },
312
+ )
313
+ .catch(panicHandler);
270
314
  },
271
315
  wake() {
272
316
  const { facets } = this;
@@ -1,7 +1,6 @@
1
1
  export function forwardingMethods(rem: any): {
2
2
  [k: string]: any;
3
3
  };
4
- export function makeSharedStateRecord<K extends string | number | symbol, R extends Record<K, any>>(dataRecord: R): R;
5
4
  export function prepareEndowmentTools(outerZone: Zone, outerOptions?: PreparationOptions | undefined): {
6
5
  prepareEndowment: (zone: Zone, tag: string, e: unknown) => any;
7
6
  unwrap: (wrapped: any, guestWrapped: any) => any;
@@ -1 +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"}
1
+ {"version":3,"file":"endowments.d.ts","sourceRoot":"","sources":["endowments.js"],"names":[],"mappings":"AAiDO;;EASN;AAMM,iDAHI,IAAI;6BA4GF,IAAI,OACJ,MAAM,KACN,OAAO;;EAyFnB;4BA7OY,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO;6BAiP1E,UAAU,aA3MZ,IAAI;6BA4GF,IAAI,OACJ,MAAM,KACN,OAAO;;EA6F0B;0BAvPvB,mBAAmB;wCAEL,iBAAiB"}
package/src/endowments.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import { Fail } from '@endo/errors';
2
2
  import { E } from '@endo/eventual-send';
3
3
  import { isPromise } from '@endo/promise-kit';
4
- import { isRemotable, isPassable, GET_METHOD_NAMES } from '@endo/pass-style';
4
+ import {
5
+ isRemotable,
6
+ isPassable,
7
+ GET_METHOD_NAMES,
8
+ Far,
9
+ } from '@endo/pass-style';
5
10
  import { M, objectMap } from '@endo/patterns';
6
11
  import { prepareVowTools, toPassableCap } from '@agoric/vow';
7
12
  import { isVow } from '@agoric/vow/src/vow-utils.js';
@@ -53,38 +58,6 @@ export const forwardingMethods = rem => {
53
58
  return fromEntries(keys.map(makeMethodEntry));
54
59
  };
55
60
 
56
- /**
57
- * Given a possibly mutable (and therefore unhardened) record, return a
58
- * corresponding state record that acts identically for normal
59
- * gets and sets, but is implemented using accessors, so it will be recognized
60
- * as a state record.
61
- *
62
- * @template { string | number | symbol } K
63
- * @template {Record<K, any>} R
64
- * @param {R} dataRecord
65
- * @returns {R}
66
- */
67
- export const makeSharedStateRecord = dataRecord =>
68
- harden(
69
- create(
70
- objectPrototype,
71
- fromEntries(
72
- ownKeys(dataRecord).flatMap(key =>
73
- entries(
74
- getOwnPropertyDescriptors({
75
- get [key]() {
76
- return dataRecord[key];
77
- },
78
- set [key](newValue) {
79
- dataRecord[key] = newValue;
80
- },
81
- }),
82
- ),
83
- ),
84
- ),
85
- ),
86
- );
87
-
88
61
  /**
89
62
  * @param {Zone} outerZone
90
63
  * @param {PreparationOptions} [outerOptions]
@@ -95,8 +68,7 @@ export const prepareEndowmentTools = (outerZone, outerOptions = {}) => {
95
68
 
96
69
  const functionUnwrapper = outerZone.exo('FunctionUnwrapper', UnwrapperI, {
97
70
  unwrap(guestWrapped) {
98
- const unwrapped = (...args) => guestWrapped.apply(args);
99
- return harden(unwrapped);
71
+ return Far('UnwrappedFunction', (...args) => guestWrapped.apply(args));
100
72
  },
101
73
  });
102
74
 
@@ -1 +1 @@
1
- {"version":3,"file":"equate.d.ts","sourceRoot":"","sources":["equate.js"],"names":[],"mappings":"AAQO,iFAiHN"}
1
+ {"version":3,"file":"equate.d.ts","sourceRoot":"","sources":["equate.js"],"names":[],"mappings":"AAQO,iFAyHN"}
package/src/equate.js CHANGED
@@ -31,7 +31,14 @@ export const makeEquate = bijection => {
31
31
  Fail`unequal ${g} vs ${h}`;
32
32
  return;
33
33
  }
34
- if (bijection.has(g, h)) {
34
+ if (bijection.hasGuest(g) && bijection.guestToHost(g) === h) {
35
+ // Note that this can be true even when
36
+ // `bijection.hostToGuest(h) !== g`
37
+ // but only when the two guests represent the same host, as
38
+ // happens with unwrapping. That why we do this one-way test
39
+ // rather than the two way `bijection.has` test.
40
+ // Even in this one-way case, we have still satisfied
41
+ // the equate, so return.
35
42
  return;
36
43
  }
37
44
  const gPassStyle = passStyleOf(g);
@@ -41,7 +48,8 @@ export const makeEquate = bijection => {
41
48
  // TODO when we do, delete the `throw Fail` line and uncomment
42
49
  // the two lines below it.
43
50
  // We *do* support passing a guest wrapper of a hostVow back
44
- // to the host, but that would be cause by `bijection.has` above.
51
+ // to the host, but that would be caught by `bijection.guestToHost`
52
+ // test above.
45
53
  throw Fail`guest promises not yet passable`;
46
54
  // `init` does not yet do enough checking anyway. For this case,
47
55
  // we should ensure that h is a host wrapper of a guest promise,
@@ -92,10 +100,10 @@ export const makeEquate = bijection => {
92
100
  case 'remotable': {
93
101
  // Note that we can send a guest wrapping of a host remotable
94
102
  // back to the host,
95
- // but that should have already been taken care of by the
96
- // `bijection.has` above.
103
+ // but that should have already been caught by the
104
+ // `bijection.guestToHost` above.
97
105
  throw Fail`cannot yet send guest remotables to host ${g} vs ${h}`;
98
- // `init` does not yet do enough checking anyway. For this case,
106
+ // `unwrapInit` does not yet do enough checking anyway. For this case,
99
107
  // we should ensure that h is a host wrapper of a guest remotable,
100
108
  // which is a wrapping we don't yet support.
101
109
  // bijection.unwrapInit(g, h);
@@ -104,10 +112,10 @@ export const makeEquate = bijection => {
104
112
  case 'promise': {
105
113
  // Note that we can send a guest wrapping of a host promise
106
114
  // (or vow) back to the host,
107
- // but that should have already been taken care of by the
108
- // `bijection.has` above.
115
+ // but that should have already been caught by the
116
+ // `bijection.guestToHost` above.
109
117
  throw Fail`cannot yet send guest promises to host ${g} vs ${h}`;
110
- // `init` does not yet do enough checking anyway. For this case,
118
+ // `unwrapInit` does not yet do enough checking anyway. For this case,
111
119
  // we should ensure that h is a host wrapper of a guest promise,
112
120
  // which is a wrapping we don't yet support.
113
121
  // bijection.unwrapInit(g, h);
@@ -1,24 +1,61 @@
1
+ export function nextGeneration(zone: Zone): number;
1
2
  export function prepareLogStore(zone: Zone): () => import("@endo/exo").Guarded<{
2
3
  reset(): void;
3
4
  dispose(): void;
5
+ getUnfilteredIndex(): number;
4
6
  getIndex(): number;
5
7
  getLength(): number;
6
8
  isReplaying(): boolean;
9
+ /**
10
+ * @returns {LogEntry}
11
+ */
7
12
  peekEntry(): LogEntry;
13
+ /**
14
+ * @returns {LogEntry}
15
+ */
16
+ nextUnfilteredEntry(): LogEntry;
17
+ /**
18
+ * @returns {LogEntry}
19
+ */
8
20
  nextEntry(): LogEntry;
9
- pushEntry(entry: any): number;
21
+ /**
22
+ * @param {LogEntry} latestEntry
23
+ */
24
+ pushEntry(latestEntry: LogEntry): number;
25
+ /**
26
+ * @returns {LogEntry[]}
27
+ */
28
+ dumpUnfiltered(): LogEntry[];
10
29
  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])[];
11
30
  promiseReplayDone(): Promise<undefined>;
12
31
  }>;
13
32
  export type LogStore = ReturnType<ReturnType<(zone: Zone) => () => import("@endo/exo").Guarded<{
14
33
  reset(): void;
15
34
  dispose(): void;
35
+ getUnfilteredIndex(): number;
16
36
  getIndex(): number;
17
37
  getLength(): number;
18
38
  isReplaying(): boolean;
39
+ /**
40
+ * @returns {LogEntry}
41
+ */
19
42
  peekEntry(): LogEntry;
43
+ /**
44
+ * @returns {LogEntry}
45
+ */
46
+ nextUnfilteredEntry(): LogEntry;
47
+ /**
48
+ * @returns {LogEntry}
49
+ */
20
50
  nextEntry(): LogEntry;
21
- pushEntry(entry: any): number;
51
+ /**
52
+ * @param {LogEntry} latestEntry
53
+ */
54
+ pushEntry(latestEntry: LogEntry): number;
55
+ /**
56
+ * @returns {LogEntry[]}
57
+ */
58
+ dumpUnfiltered(): LogEntry[];
22
59
  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])[];
23
60
  promiseReplayDone(): Promise<undefined>;
24
61
  }>>>;
@@ -1 +1 @@
1
- {"version":3,"file":"log-store.d.ts","sourceRoot":"","sources":["log-store.js"],"names":[],"mappings":"AA+BO,sCAFI,IAAI;;;;;;;;;;;GAuId;uBAGY,UAAU,CAAC,UAAU,QA1IvB,IAAI;;;;;;;;;;;GA0IoC,CAAC;0BA/J7B,mBAAmB;8BAEL,YAAY"}
1
+ {"version":3,"file":"log-store.d.ts","sourceRoot":"","sources":["log-store.js"],"names":[],"mappings":"AAmCO,qCAHI,IAAI,GACF,MAAM,CAgBlB;AAOM,sCAFI,IAAI;;;;;;;IAoGT;;OAEG;iBADU,QAAQ;IAcrB;;OAEG;2BADU,QAAQ;IAgBrB;;OAEG;iBADU,QAAQ;IAWrB;;OAEG;2BADQ,QAAQ;IAuCnB;;OAEG;sBADU,QAAQ,EAAE;;;GAwB5B;uBAGY,UAAU,CAAC,UAAU,QApNvB,IAAI;;;;;;;IAoGT;;OAEG;iBADU,QAAQ;IAcrB;;OAEG;2BADU,QAAQ;IAgBrB;;OAEG;iBADU,QAAQ;IAWrB;;OAEG;2BADQ,QAAQ;IAuCnB;;OAEG;sBADU,QAAQ,EAAE;;;GA2BsB,CAAC;0BAlQ7B,mBAAmB;8BAEL,YAAY"}
package/src/log-store.js CHANGED
@@ -14,31 +14,76 @@ import { makeEphemera } from './ephemera.js';
14
14
  const LogStoreI = M.interface('LogStore', {
15
15
  reset: M.call().returns(),
16
16
  dispose: M.call().returns(),
17
+ getUnfilteredIndex: M.call().returns(M.number()),
17
18
  getIndex: M.call().returns(M.number()),
18
19
  getLength: M.call().returns(M.number()),
19
20
  isReplaying: M.call().returns(M.boolean()),
20
21
  peekEntry: M.call().returns(LogEntryShape),
21
22
  nextEntry: M.call().returns(LogEntryShape),
23
+ nextUnfilteredEntry: M.call().returns(LogEntryShape),
22
24
  pushEntry: M.call(LogEntryShape).returns(M.number()),
25
+ dumpUnfiltered: M.call().returns(M.arrayOf(LogEntryShape)),
23
26
  dump: M.call().returns(M.arrayOf(LogEntryShape)),
24
27
  promiseReplayDone: M.call().returns(M.promise()),
25
28
  });
26
29
 
30
+ /**
31
+ * Get a generation number to use in `startGeneration` log entries.
32
+ *
33
+ * @param {Zone} zone
34
+ * @returns {number}
35
+ */
36
+ export const nextGeneration = zone => {
37
+ /** @type {MapStore<'generation', number>} */
38
+ const logStoreMetadata = zone.mapStore('LogStoreMetadata');
39
+ const generationKey = 'generation';
40
+
41
+ if (!logStoreMetadata.has(generationKey)) {
42
+ const firstGen = 0;
43
+ logStoreMetadata.init(generationKey, firstGen);
44
+ return firstGen;
45
+ }
46
+
47
+ const nextGen = logStoreMetadata.get(generationKey) + 1;
48
+ logStoreMetadata.set(generationKey, nextGen);
49
+ return nextGen;
50
+ };
51
+
27
52
  /**
28
53
  * A growable, replayable, sequence of `LogEntry`s.
29
54
  *
30
55
  * @param {Zone} zone
31
56
  */
32
57
  export const prepareLogStore = zone => {
58
+ /**
59
+ * Ensure that any new or reset LogStore instance that (for a given
60
+ * incarnation) pushes at least one entry will insert these entries first,
61
+ * even if the log is reset, replayed, and pushed to in the same incarnation.
62
+ * @type {LogEntry[]}
63
+ */
64
+ const initialPush = harden([['startGeneration', nextGeneration(zone)]]);
65
+
66
+ /**
67
+ * A predicate to indicate whether the entry is normally visible to the
68
+ * LogStore user, or is a more internal entry that only is visible via the
69
+ * Unfiltered methods.
70
+ * @param {LogEntry} entry
71
+ */
72
+ const entryIsVisible = entry => entry[0] !== 'startGeneration';
73
+
33
74
  /**
34
75
  * @type {Ephemera<LogStore, {
35
- * index: number
36
- * replayDoneKit: PromiseKit<undefined>
76
+ * index: number;
77
+ * unfilteredIndex: number;
78
+ * initialPush: LogEntry[] | undefined;
79
+ * replayDoneKit: PromiseKit<undefined>;
37
80
  * }>}
38
81
  */
39
82
  const tmp = makeEphemera(log => {
40
83
  const result = {
41
84
  index: 0,
85
+ unfilteredIndex: 0,
86
+ initialPush,
42
87
  replayDoneKit: makePromiseKit(),
43
88
  };
44
89
  if (log.getLength() === 0) {
@@ -82,6 +127,12 @@ export const prepareLogStore = zone => {
82
127
  tmp.resetFor(self);
83
128
  mapStore.clear();
84
129
  },
130
+ getUnfilteredIndex() {
131
+ const { self } = this;
132
+ const eph = tmp.for(self);
133
+
134
+ return eph.unfilteredIndex;
135
+ },
85
136
  getIndex() {
86
137
  const { self } = this;
87
138
  const eph = tmp.for(self);
@@ -99,52 +150,96 @@ export const prepareLogStore = zone => {
99
150
  const { mapStore } = state;
100
151
  const eph = tmp.for(self);
101
152
 
102
- return eph.index < mapStore.getSize();
153
+ return eph.unfilteredIndex < mapStore.getSize();
103
154
  },
155
+ /**
156
+ * @returns {LogEntry}
157
+ */
104
158
  peekEntry() {
105
159
  const { state, self } = this;
106
160
  const { mapStore } = state;
107
161
  const eph = tmp.for(self);
108
162
 
109
163
  self.isReplaying() ||
110
- Fail`No longer replaying: ${q(eph.index)} vs ${q(
164
+ Fail`No longer replaying: ${q(eph.unfilteredIndex)} vs ${q(
111
165
  mapStore.getSize(),
112
166
  )}`;
113
- const result = mapStore.get(eph.index);
167
+ const result = mapStore.get(eph.unfilteredIndex);
114
168
  return result;
115
169
  },
116
- nextEntry() {
170
+ /**
171
+ * @returns {LogEntry}
172
+ */
173
+ nextUnfilteredEntry() {
117
174
  const { self } = this;
118
175
  const eph = tmp.for(self);
119
176
 
120
177
  const result = self.peekEntry();
121
- eph.index += 1;
178
+ eph.unfilteredIndex += 1;
179
+ if (entryIsVisible(result)) {
180
+ eph.index += 1;
181
+ }
122
182
  if (!self.isReplaying()) {
123
183
  eph.replayDoneKit.resolve(undefined);
124
184
  }
125
185
  return result;
126
186
  },
127
- pushEntry(entry) {
187
+ /**
188
+ * @returns {LogEntry}
189
+ */
190
+ nextEntry() {
191
+ const { self } = this;
192
+ let result = self.nextUnfilteredEntry();
193
+ while (!entryIsVisible(result)) {
194
+ self.isReplaying() || Fail`Unexpected entry at log tail: ${result}`;
195
+ result = self.nextUnfilteredEntry();
196
+ }
197
+ return result;
198
+ },
199
+ /**
200
+ * @param {LogEntry} latestEntry
201
+ */
202
+ pushEntry(latestEntry) {
128
203
  const { state, self } = this;
204
+
129
205
  const { mapStore } = state;
130
206
  const eph = tmp.for(self);
131
207
 
132
208
  !self.isReplaying() ||
133
- Fail`still replaying: ${q(eph.index)} vs ${q(mapStore.getSize())}`;
134
- eph.index === mapStore.getSize() ||
135
- Fail`internal: index confusion ${q(eph.index)} vs ${q(
136
- mapStore.getSize(),
137
- )}`;
138
- mapStore.init(eph.index, entry);
139
- eph.index += 1;
140
- eph.index === mapStore.getSize() ||
141
- Fail`internal: index confusion ${q(eph.index)} vs ${q(
142
- mapStore.getSize(),
143
- )}`;
144
- // console.log('LOG ENTRY ', eph.index - 1, entry);
145
- return eph.index;
209
+ Fail`still replaying: ${q(eph.unfilteredIndex)} vs ${q(mapStore.getSize())}`;
210
+
211
+ const pushOne = entry => {
212
+ eph.unfilteredIndex === mapStore.getSize() ||
213
+ Fail`internal: unfilteredIndex confusion ${q(eph.unfilteredIndex)} vs ${q(
214
+ mapStore.getSize(),
215
+ )}`;
216
+ mapStore.init(eph.unfilteredIndex, entry);
217
+ eph.unfilteredIndex += 1;
218
+ if (entryIsVisible(entry)) {
219
+ eph.index += 1;
220
+ }
221
+ eph.unfilteredIndex === mapStore.getSize() ||
222
+ Fail`internal: unfilteredIndex confusion ${q(eph.unfilteredIndex)} vs ${q(
223
+ mapStore.getSize(),
224
+ )}`;
225
+ };
226
+
227
+ if (eph.initialPush) {
228
+ const initialEntries = eph.initialPush;
229
+ eph.initialPush = undefined;
230
+ for (const initialEntry of initialEntries) {
231
+ pushOne(initialEntry);
232
+ }
233
+ }
234
+ pushOne(latestEntry);
235
+
236
+ // console.log('LOG ENTRY ', eph.unfilteredIndex - 1, entry);
237
+ return eph.unfilteredIndex;
146
238
  },
147
- dump() {
239
+ /**
240
+ * @returns {LogEntry[]}
241
+ */
242
+ dumpUnfiltered() {
148
243
  const { state } = this;
149
244
  const { mapStore } = state;
150
245
  const len = mapStore.getSize();
@@ -154,6 +249,10 @@ export const prepareLogStore = zone => {
154
249
  }
155
250
  return harden(result);
156
251
  },
252
+ dump() {
253
+ const { self } = this;
254
+ return harden(self.dumpUnfiltered().filter(entryIsVisible));
255
+ },
157
256
  promiseReplayDone() {
158
257
  const { self } = this;
159
258
  const eph = tmp.for(self);