@agoric/async-flow 0.1.1-dev-c19f1b5.0 → 0.1.1-dev-27cc9b8.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/async-flow",
3
- "version": "0.1.1-dev-c19f1b5.0+c19f1b5",
3
+ "version": "0.1.1-dev-27cc9b8.0+27cc9b8",
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,9 +24,9 @@
24
24
  "author": "Agoric",
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "@agoric/base-zone": "0.1.1-dev-c19f1b5.0+c19f1b5",
28
- "@agoric/store": "0.9.3-dev-c19f1b5.0+c19f1b5",
29
- "@agoric/vow": "0.1.1-dev-c19f1b5.0+c19f1b5",
27
+ "@agoric/base-zone": "0.1.1-dev-27cc9b8.0+27cc9b8",
28
+ "@agoric/store": "0.9.3-dev-27cc9b8.0+27cc9b8",
29
+ "@agoric/vow": "0.1.1-dev-27cc9b8.0+27cc9b8",
30
30
  "@endo/common": "^1.2.2",
31
31
  "@endo/errors": "^1.2.2",
32
32
  "@endo/eventual-send": "^1.2.2",
@@ -36,9 +36,9 @@
36
36
  "@endo/promise-kit": "^1.1.2"
37
37
  },
38
38
  "devDependencies": {
39
- "@agoric/internal": "0.3.3-dev-c19f1b5.0+c19f1b5",
40
- "@agoric/swingset-liveslots": "0.10.3-dev-c19f1b5.0+c19f1b5",
41
- "@agoric/zone": "0.2.3-dev-c19f1b5.0+c19f1b5",
39
+ "@agoric/internal": "0.3.3-dev-27cc9b8.0+27cc9b8",
40
+ "@agoric/swingset-liveslots": "0.10.3-dev-27cc9b8.0+27cc9b8",
41
+ "@agoric/zone": "0.2.3-dev-27cc9b8.0+27cc9b8",
42
42
  "@endo/env-options": "^1.1.4",
43
43
  "@endo/ses-ava": "^1.2.2",
44
44
  "ava": "^5.3.0"
@@ -62,5 +62,5 @@
62
62
  "typeCoverage": {
63
63
  "atLeast": 77.83
64
64
  },
65
- "gitHead": "c19f1b5b918e83f8f45c05c0683ee0f97ed0e415"
65
+ "gitHead": "27cc9b8be59abb048f1ea226b96697c3dccc4d0f"
66
66
  }
@@ -19,9 +19,9 @@ export function makeReplayMembrane(log: import("@endo/exo").Guarded<{
19
19
  hostToGuest(h: any): any;
20
20
  }>, vowTools: {
21
21
  when: <T, TResult1 = import("@agoric/vow").Unwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("@agoric/vow").Unwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
22
- watch: <T_1 = unknown, TResult1_1 = T_1, TResult2_1 = T_1, C = unknown>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<TResult1_1 | TResult2_1>;
22
+ watch: <T_1 = any, TResult1_1 = T_1, TResult2_1 = never, C = any>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1_1, void> | Exclude<TResult2_1, void> extends never ? TResult1_1 : Exclude<TResult1_1, void> | Exclude<TResult2_1, void>>;
23
23
  makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
24
- allVows: (vows: any) => Vow<any>;
24
+ allVows: (vows: unknown[]) => Vow<any[]>;
25
25
  }, watchWake: (vowish: Promise<any> | Vow) => void, panic: (problem: Error) => never): {
26
26
  hostToGuest: (specimen: Passable, label?: string | undefined) => any;
27
27
  guestToHost: (specimen: Passable, label?: string | undefined) => any;
@@ -54,9 +54,9 @@ export type ReplayMembrane = ReturnType<(log: import("@endo/exo").Guarded<{
54
54
  hostToGuest(h: any): any;
55
55
  }>, vowTools: {
56
56
  when: <T, TResult1 = import("@agoric/vow").Unwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("@agoric/vow").Unwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
57
- watch: <T_1 = unknown, TResult1_1 = T_1, TResult2_1 = T_1, C = unknown>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<TResult1_1 | TResult2_1>;
57
+ watch: <T_1 = any, TResult1_1 = T_1, TResult2_1 = never, C = any>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1_1, void> | Exclude<TResult2_1, void> extends never ? TResult1_1 : Exclude<TResult1_1, void> | Exclude<TResult2_1, void>>;
58
58
  makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
59
- allVows: (vows: any) => Vow<any>;
59
+ allVows: (vows: unknown[]) => Vow<any[]>;
60
60
  }, watchWake: (vowish: Promise<any> | Vow) => void, panic: (problem: Error) => never) => {
61
61
  hostToGuest: (specimen: Passable, label?: string | undefined) => any;
62
62
  guestToHost: (specimen: Passable, label?: string | undefined) => any;
@@ -1 +1 @@
1
- {"version":3,"file":"replay-membrane.d.ts","sourceRoot":"","sources":["replay-membrane.js"],"names":[],"mappings":"AAkBO;;;;;;;;;;;;;;;;;;;;uFATK,CAAC;;;;8CAMyB,IAAI,SAC/B,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;;;;;;;;;;GA+ZnC;6BAGa,UAAU;;;;;;;;;;;;;;;;;;;;uFAzaZ,CAAC;;;;2DAOF,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;;;;;;;;;;GAkaQ"}
1
+ {"version":3,"file":"replay-membrane.d.ts","sourceRoot":"","sources":["replay-membrane.js"],"names":[],"mappings":"AAkBO;;;;;;;;;;;;;;;;;;;;uFATK,CAAC;;;oBAkBwB,SAEhC;8CAdiC,IAAI,SAC/B,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;;;;;;;;;;GA+ZnC;6BAGa,UAAU;;;;;;;;;;;;;;;;;;;;uFAzaZ,CAAC;;;oBAkBwB,SAEhC;2DAbM,CAAC,OAAO,EAAE,KAAK,KAAK,KAAK;;;;;;;;;;GAkaQ"}
package/src/types.d.ts CHANGED
@@ -13,9 +13,9 @@ type HostAsyncFuncWrapper = (...activationArgs: Host[]) => HostVow;
13
13
  type PreparationOptions = {
14
14
  vowTools?: {
15
15
  when: <T, TResult1 = import("@agoric/vow").Unwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("@agoric/vow").Unwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
16
- watch: <T_1 = unknown, TResult1_1 = T_1, TResult2_1 = T_1, C = unknown>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<TResult1_1 | TResult2_1>;
16
+ watch: <T_1 = any, TResult1_1 = T_1, TResult2_1 = never, C = any>(specimenP: import("@agoric/vow").ERef<T_1 | Vow<T_1>>, watcher?: import("@agoric/vow").Watcher<T_1, TResult1_1, TResult2_1> | undefined, watcherContext?: C | undefined) => Vow<Exclude<TResult1_1, void> | Exclude<TResult2_1, void> extends never ? TResult1_1 : Exclude<TResult1_1, void> | Exclude<TResult2_1, void>>;
17
17
  makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
18
- allVows: (vows: any) => Vow<any>;
18
+ allVows: (vows: unknown[]) => Vow<any[]>;
19
19
  } | undefined;
20
20
  makeLogStore?: (() => import("@endo/exo").Guarded<{
21
21
  reset(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":"iBAWa,SAAS,GACrB,UAAsB,GACtB,WAAuB,GACvB,QAAoB,GACpB,MAAkB;WAMN,CAAC,gCAAD,CAAC;UAKD,CAAC,gCAAD,CAAC;;;;;aAQQ,CAAC;;;sBAIV,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,cAAS;4BAI9C,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,KAAK,OAAO;;;2FA7BlC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAwCL,QAAQ,GAAC,OAAO;eAIhB;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAA;CAAC;cAStB,CAAC,4BADK,CAAC;SAAd,CAAC,IAAI,EACE,CAAC,AADA,KAAK,CAAC;cACd,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI;;;;;;;gBAQlB,CAAE,iEAAiE;AAC/E,EAAQ,EAAE,WAAW,EACrB,GAAS,EAAE,OAAO,EAClB,WAAiB,EAAE,IAAI,CAClB,GAAG,CACR,EAAQ,EAAE,UAAU,EACpB,GAAS,EAAE,OAAO,EAClB,MAAY,EAAE,IAAI,CACb,GAAG,CACR,EAAQ,EAAE,UAAU,EACpB,SAAe,EAAE,MAAM,EACvB,MAAY,EAAE,IAAI,CACb,GAAG,CACR,EAAQ,EAAE,SAAS,EACnB,SAAe,EAAE,MAAM,EACvB,OAAa,EAAE,IAAI,CACd,GAAG,CAAE,qEAAqE;AAC/E,EAAQ,EAAE,WAAW,EACrB,MAAY,EAAE,IAAI,EAClB,OAAa,EAAE,WAAW,GAAC,SAAS,EACpC,IAAU,EAAE,IAAI,EAAE,EAClB,SAAe,EAAE,MAAM,CAClB;gCAhGuB,mBAAmB;8BACrB,kBAAkB;0BACtB,mBAAmB;yBACV,aAAa;8BAAb,aAAa;8BAClB,gBAAgB;+BACf,gBAAgB;oCACX,sBAAsB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":"iBAWa,SAAS,GACrB,UAAsB,GACtB,WAAuB,GACvB,QAAoB,GACpB,MAAkB;WAMN,CAAC,gCAAD,CAAC;UAKD,CAAC,gCAAD,CAAC;;;;;aAQQ,CAAC;;;sBAIV,CAAC,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,KAAK,cAAS;4BAI9C,CAAC,GAAG,cAAc,EAAE,IAAI,EAAE,KAAK,OAAO;;;2FA7BlC,CAAC;;;wBAyBN,SAAQ;;;;;;;;;;;;;;;;;;;;;;;;mBAeP,QAAQ,GAAC,OAAO;eAIhB;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAC7B;IAAC,IAAI,EAAE,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAA;CAAC;cAStB,CAAC,4BADK,CAAC;SAAd,CAAC,IAAI,EACE,CAAC,AADA,KAAK,CAAC;cACd,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI;;;;;;;gBAQlB,CAAE,iEAAiE;AAC/E,EAAQ,EAAE,WAAW,EACrB,GAAS,EAAE,OAAO,EAClB,WAAiB,EAAE,IAAI,CAClB,GAAG,CACR,EAAQ,EAAE,UAAU,EACpB,GAAS,EAAE,OAAO,EAClB,MAAY,EAAE,IAAI,CACb,GAAG,CACR,EAAQ,EAAE,UAAU,EACpB,SAAe,EAAE,MAAM,EACvB,MAAY,EAAE,IAAI,CACb,GAAG,CACR,EAAQ,EAAE,SAAS,EACnB,SAAe,EAAE,MAAM,EACvB,OAAa,EAAE,IAAI,CACd,GAAG,CAAE,qEAAqE;AAC/E,EAAQ,EAAE,WAAW,EACrB,MAAY,EAAE,IAAI,EAClB,OAAa,EAAE,WAAW,GAAC,SAAS,EACpC,IAAU,EAAE,IAAI,EAAE,EAClB,SAAe,EAAE,MAAM,CAClB;gCAhGuB,mBAAmB;8BACrB,kBAAkB;0BACtB,mBAAmB;yBACV,aAAa;8BAAb,aAAa;8BAClB,gBAAgB;+BACf,gBAAgB;oCACX,sBAAsB"}
@@ -0,0 +1,201 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ } from './prepare-test-env-ava.js';
8
+
9
+ import { Fail } from '@endo/errors';
10
+ import { passStyleOf } from '@endo/pass-style';
11
+ import { makeCopyMap } from '@endo/patterns';
12
+ import { makePromiseKit } from '@endo/promise-kit';
13
+ import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
14
+ import { isVow } from '@agoric/vow/src/vow-utils.js';
15
+ import { prepareVowTools } from '@agoric/vow';
16
+ import { makeDurableZone } from '@agoric/zone/durable.js';
17
+
18
+ import { prepareAsyncFlowTools } from '../src/async-flow.js';
19
+
20
+ /**
21
+ * @import {AsyncFlow} from '../src/async-flow.js'
22
+ */
23
+
24
+ /**
25
+ * @param {Zone} zone
26
+ * @param {number} [k]
27
+ */
28
+ const prepareOrchestra = (zone, k = 1) =>
29
+ zone.exoClass(
30
+ 'Orchestra',
31
+ undefined,
32
+ (factor, vow, resolver) => ({ factor, vow, resolver }),
33
+ {
34
+ scale(n) {
35
+ const { state } = this;
36
+ return k * state.factor * n;
37
+ },
38
+ vow() {
39
+ const { state } = this;
40
+ return state.vow;
41
+ },
42
+ resolve(x) {
43
+ const { state } = this;
44
+ state.resolver.resolve(x);
45
+ },
46
+ },
47
+ );
48
+
49
+ /** @typedef {ReturnType<ReturnType<prepareOrchestra>>} Orchestra */
50
+
51
+ const firstLogLen = 7;
52
+
53
+ /**
54
+ * @param {any} t
55
+ * @param {Zone} zone
56
+ */
57
+ const testFirstPlay = async (t, zone) => {
58
+ t.log('firstPlay started');
59
+ const vowTools = prepareVowTools(zone);
60
+ const { asyncFlow, adminAsyncFlow } = prepareAsyncFlowTools(zone, {
61
+ vowTools,
62
+ });
63
+ const makeOrchestra = prepareOrchestra(zone);
64
+ const { makeVowKit } = vowTools;
65
+
66
+ const { vow: v1, resolver: r1 } = zone.makeOnce('v1', () => makeVowKit());
67
+ const { vow: v2, resolver: r2 } = zone.makeOnce('v2', () => makeVowKit());
68
+ const { vow: v3, resolver: _r3 } = zone.makeOnce('v3', () => makeVowKit());
69
+ const hOrch7 = zone.makeOnce('hOrch7', () => makeOrchestra(7, v2, r2));
70
+
71
+ // purposely violate rule that guestMethod is closed.
72
+ const { promise: promiseStep, resolve: resolveStep } = makePromiseKit();
73
+
74
+ const { guestMethod } = {
75
+ async guestMethod(gOrch7, g1, _p3) {
76
+ t.log(' firstPlay about to await g1');
77
+ t.is(await g1, 'x');
78
+ const p2 = gOrch7.vow();
79
+ const prod = gOrch7.scale(3);
80
+ t.is(prod, 21);
81
+
82
+ let gErr;
83
+ try {
84
+ gOrch7.scale(9n);
85
+ } catch (e) {
86
+ gErr = e;
87
+ }
88
+ t.is(gErr.name, 'TypeError');
89
+
90
+ resolveStep(true);
91
+ t.log(' firstPlay to hang awaiting p2');
92
+ // awaiting a promise that won't be resolved until next incarnation
93
+ await p2;
94
+ t.fail('must not reach here in first incarnation');
95
+ },
96
+ };
97
+
98
+ const wrapperFunc = asyncFlow(zone, 'AsyncFlow1', guestMethod);
99
+
100
+ const outcomeV = zone.makeOnce('outcomeV', () => wrapperFunc(hOrch7, v1, v3));
101
+
102
+ t.true(isVow(outcomeV));
103
+ r1.resolve('x');
104
+
105
+ const flow = zone.makeOnce('flow', () =>
106
+ adminAsyncFlow.getFlowForOutcomeVow(outcomeV),
107
+ );
108
+ t.is(passStyleOf(flow), 'remotable');
109
+
110
+ await promiseStep;
111
+
112
+ const logDump = flow.dump();
113
+ t.is(logDump.length, firstLogLen);
114
+ t.deepEqual(logDump, [
115
+ ['doFulfill', v1, 'x'],
116
+ ['checkCall', hOrch7, 'vow', [], 1],
117
+ ['doReturn', 1, v2],
118
+ ['checkCall', hOrch7, 'scale', [3], 3],
119
+ ['doReturn', 3, 21],
120
+ ['checkCall', hOrch7, 'scale', [9n], 5],
121
+ [
122
+ 'doThrow',
123
+ 5,
124
+ TypeError('Cannot mix BigInt and other types, use explicit conversions'),
125
+ ],
126
+ ]);
127
+ t.log('firstPlay done');
128
+ };
129
+
130
+ /**
131
+ * Test from bug https://github.com/Agoric/agoric-sdk/issues/9465
132
+ *
133
+ * @param {any} t
134
+ * @param {Zone} zone
135
+ */
136
+ const testBadShortReplay = async (t, zone) => {
137
+ t.log('badShortReplay started');
138
+ const vowTools = prepareVowTools(zone);
139
+ const { asyncFlow, adminAsyncFlow } = prepareAsyncFlowTools(zone, {
140
+ vowTools,
141
+ });
142
+ prepareOrchestra(zone);
143
+ const { when } = vowTools;
144
+
145
+ // purposely violate rule that guestMethod is closed.
146
+ const { promise: promiseStep, resolve: resolveStep } = makePromiseKit();
147
+
148
+ const { guestMethod } = {
149
+ async guestMethod(_gOrch7, _g1, _p3) {
150
+ t.log(' badReplay return early');
151
+ resolveStep(true);
152
+ return 'bad';
153
+ },
154
+ };
155
+
156
+ // `asyncFlow` can be used simply to re-prepare the guest function
157
+ // by ignoring the returned wrapper function. If the wrapper function is
158
+ // invoked, that would be a *new* activation with a new outcome and
159
+ // flow, and would have nothing to do with the existing one.
160
+ asyncFlow(zone, 'AsyncFlow1', guestMethod);
161
+
162
+ const outcomeV = /** @type {Vow} */ (
163
+ zone.makeOnce('outcomeV', () => Fail`need outcomeV`)
164
+ );
165
+ const flow = /** @type {AsyncFlow} */ (
166
+ zone.makeOnce('flow', () => Fail`need flow`)
167
+ );
168
+ const flow1 = adminAsyncFlow.getFlowForOutcomeVow(outcomeV);
169
+ t.is(flow, flow1);
170
+ t.is(passStyleOf(flow), 'remotable');
171
+
172
+ await promiseStep;
173
+
174
+ const replayProblem = flow.getOptFatalProblem();
175
+ t.log(' badShortReplay failures', replayProblem);
176
+ t.true(replayProblem instanceof Error);
177
+
178
+ const outcome = when(outcomeV);
179
+ await eventLoopIteration();
180
+
181
+ t.is(await Promise.race([outcome, Promise.resolve('good')]), 'good');
182
+
183
+ t.deepEqual(
184
+ adminAsyncFlow.getFailures(),
185
+ makeCopyMap([[flow, replayProblem]]),
186
+ );
187
+
188
+ t.log('badShortReplay done');
189
+ };
190
+
191
+ test.serial.failing('test durable async-flow early completion', async t => {
192
+ annihilate();
193
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
194
+ await testFirstPlay(t, zone1);
195
+
196
+ await eventLoopIteration();
197
+
198
+ nextLife();
199
+ const zone2a = makeDurableZone(getBaggage(), 'durableRoot');
200
+ await testBadShortReplay(t, zone2a);
201
+ });