@agoric/async-flow 0.1.1-calypso-dev-84eb287.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 (58) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +201 -0
  3. package/README.md +40 -0
  4. package/docs/async-flow-states.key +0 -0
  5. package/docs/async-flow-states.md +15 -0
  6. package/docs/async-flow-states.png +0 -0
  7. package/index.d.ts +3 -0
  8. package/index.d.ts.map +1 -0
  9. package/index.js +2 -0
  10. package/package.json +66 -0
  11. package/src/async-flow.d.ts +94 -0
  12. package/src/async-flow.d.ts.map +1 -0
  13. package/src/async-flow.js +520 -0
  14. package/src/bijection.d.ts +31 -0
  15. package/src/bijection.d.ts.map +1 -0
  16. package/src/bijection.js +207 -0
  17. package/src/convert.d.ts +6 -0
  18. package/src/convert.d.ts.map +1 -0
  19. package/src/convert.js +133 -0
  20. package/src/endowments.d.ts +16 -0
  21. package/src/endowments.d.ts.map +1 -0
  22. package/src/endowments.js +292 -0
  23. package/src/ephemera.d.ts +3 -0
  24. package/src/ephemera.d.ts.map +1 -0
  25. package/src/ephemera.js +39 -0
  26. package/src/equate.d.ts +2 -0
  27. package/src/equate.d.ts.map +1 -0
  28. package/src/equate.js +123 -0
  29. package/src/log-store.d.ts +27 -0
  30. package/src/log-store.d.ts.map +1 -0
  31. package/src/log-store.js +169 -0
  32. package/src/replay-membrane.d.ts +40 -0
  33. package/src/replay-membrane.d.ts.map +1 -0
  34. package/src/replay-membrane.js +752 -0
  35. package/src/type-guards.d.ts +4 -0
  36. package/src/type-guards.d.ts.map +1 -0
  37. package/src/type-guards.js +68 -0
  38. package/src/types.d.ts +67 -0
  39. package/src/types.d.ts.map +1 -0
  40. package/src/types.js +196 -0
  41. package/test/async-flow-crank.test.js +102 -0
  42. package/test/async-flow-early-completion.test.js +203 -0
  43. package/test/async-flow-no-this.js +65 -0
  44. package/test/async-flow.test.js +383 -0
  45. package/test/bad-host.test.js +210 -0
  46. package/test/bijection.test.js +124 -0
  47. package/test/convert.test.js +132 -0
  48. package/test/endowments.test.js +157 -0
  49. package/test/equate.test.js +120 -0
  50. package/test/log-store.test.js +120 -0
  51. package/test/prepare-test-env-ava.js +28 -0
  52. package/test/replay-membrane-eventual.test.js +217 -0
  53. package/test/replay-membrane-settlement.test.js +173 -0
  54. package/test/replay-membrane-zombie.test.js +187 -0
  55. package/test/replay-membrane.test.js +297 -0
  56. package/tsconfig.build.json +11 -0
  57. package/tsconfig.json +13 -0
  58. package/typedoc.json +8 -0
@@ -0,0 +1,297 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ asyncFlowVerbose,
8
+ } from './prepare-test-env-ava.js';
9
+
10
+ import { Fail } from '@endo/errors';
11
+ import { isPromise } from '@endo/promise-kit';
12
+ import { prepareVowTools } from '@agoric/vow';
13
+ import { makeHeapZone } from '@agoric/zone/heap.js';
14
+ import { makeVirtualZone } from '@agoric/zone/virtual.js';
15
+ import { makeDurableZone } from '@agoric/zone/durable.js';
16
+
17
+ import { prepareLogStore } from '../src/log-store.js';
18
+ import { prepareBijection } from '../src/bijection.js';
19
+ import { makeReplayMembrane } from '../src/replay-membrane.js';
20
+
21
+ /**
22
+ * @import {PromiseKit} from '@endo/promise-kit'
23
+ * @import {Zone} from '@agoric/base-zone'
24
+ * @import {MapStore} from '@agoric/store';
25
+ * @import {LogStore} from '../src/log-store.js';
26
+ * @import {Bijection} from '../src/bijection.js';
27
+ */
28
+
29
+ const watchWake = _vowish => {};
30
+ const panic = problem => Fail`panic over ${problem}`;
31
+
32
+ /**
33
+ * @param {Zone} zone
34
+ * @param {number} [k]
35
+ */
36
+ const prepareOrchestra = (zone, k = 1) =>
37
+ zone.exoClass(
38
+ 'Orchestra',
39
+ undefined,
40
+ (factor, vow, resolver) => ({ factor, vow, resolver }),
41
+ {
42
+ scale(n) {
43
+ const { state } = this;
44
+ return k * state.factor * n;
45
+ },
46
+ vow() {
47
+ const { state } = this;
48
+ return state.vow;
49
+ },
50
+ resolve(x) {
51
+ const { state } = this;
52
+ state.resolver.resolve(x);
53
+ },
54
+ },
55
+ );
56
+
57
+ /**
58
+ * @param {any} t
59
+ * @param {Zone} zone
60
+ * @param {boolean} [showOnConsole]
61
+ */
62
+ const testFirstPlay = async (t, zone, showOnConsole = false) => {
63
+ const vowTools = prepareVowTools(zone);
64
+ const { makeVowKit } = vowTools;
65
+ const makeLogStore = prepareLogStore(zone);
66
+ const makeBijection = prepareBijection(zone);
67
+ const makeOrchestra = prepareOrchestra(zone);
68
+ const { vow: v1, resolver: r1 } = makeVowKit();
69
+ const { vow: v2, resolver: r2 } = makeVowKit();
70
+
71
+ const log = zone.makeOnce('log', () => makeLogStore());
72
+ const bijection = zone.makeOnce('bij', makeBijection);
73
+
74
+ const mem = makeReplayMembrane({
75
+ log,
76
+ bijection,
77
+ vowTools,
78
+ watchWake,
79
+ panic,
80
+ });
81
+
82
+ const g1 = mem.hostToGuest(v1);
83
+ t.true(isPromise(g1));
84
+ r1.resolve('x');
85
+ t.is(await g1, 'x');
86
+
87
+ const hOrch7 = makeOrchestra(7, v2, r2);
88
+ t.false(bijection.hasHost(hOrch7));
89
+ const gOrch7 = mem.hostToGuest(hOrch7);
90
+ t.true(bijection.has(gOrch7, hOrch7));
91
+
92
+ const prod = gOrch7.scale(3);
93
+ t.is(prod, 21);
94
+
95
+ let gErr;
96
+ try {
97
+ gOrch7.scale(9n);
98
+ } catch (e) {
99
+ gErr = e;
100
+ }
101
+
102
+ // TODO make E work across the membrane *well*
103
+ // TODO also try E on remote promise
104
+ // const prodP = E(gOrch7).scale(33);
105
+ // t.is(await prodP, 231);
106
+ // const badP = E(gOrch7).scale(99n);
107
+ // let gErr1;
108
+ // try {
109
+ // await badP;
110
+ // } catch (e) {
111
+ // gErr1 = e;
112
+ // }
113
+ // t.is(gErr1.name, 'TypeError');
114
+
115
+ t.deepEqual(log.dump(), [
116
+ ['doFulfill', v1, 'x'],
117
+ ['checkCall', hOrch7, 'scale', [3], 1],
118
+ ['doReturn', 1, 21],
119
+ ['checkCall', hOrch7, 'scale', [9n], 3],
120
+ ['doThrow', 3, mem.guestToHost(gErr)],
121
+ ]);
122
+
123
+ if (showOnConsole) {
124
+ // To see the annotation chain. Once we're synced with the next ses-ava,
125
+ // change this to a t.log, so we will see the annotation chain in context.
126
+ t.log('gErr', gErr);
127
+ }
128
+ };
129
+
130
+ /**
131
+ * @param {any} t
132
+ * @param {Zone} zone
133
+ */
134
+ const testBadReplay = async (t, zone) => {
135
+ const vowTools = prepareVowTools(zone);
136
+ prepareLogStore(zone);
137
+ prepareBijection(zone);
138
+ prepareOrchestra(zone);
139
+
140
+ const log = /** @type {LogStore} */ (
141
+ zone.makeOnce('log', () => Fail`need log`)
142
+ );
143
+ const bijection = /** @type {Bijection} */ (
144
+ zone.makeOnce('bij', () => Fail`need bij`)
145
+ );
146
+
147
+ const dump = log.dump();
148
+ const v1 = dump[0][1];
149
+ const hOrch7 = dump[1][1];
150
+ const hErr = dump[4][2];
151
+
152
+ t.false(bijection.hasHost(hOrch7));
153
+
154
+ t.deepEqual(dump, [
155
+ ['doFulfill', v1, 'x'],
156
+ ['checkCall', hOrch7, 'scale', [3], 1],
157
+ ['doReturn', 1, 21],
158
+ ['checkCall', hOrch7, 'scale', [9n], 3],
159
+ ['doThrow', 3, hErr],
160
+ ]);
161
+
162
+ const mem = makeReplayMembrane({
163
+ log,
164
+ bijection,
165
+ vowTools,
166
+ watchWake,
167
+ panic,
168
+ });
169
+
170
+ const g1 = mem.hostToGuest(v1);
171
+ mem.wake();
172
+ t.is(await g1, 'x');
173
+ const gOrch7 = mem.hostToGuest(hOrch7);
174
+ t.true(bijection.has(gOrch7, hOrch7));
175
+
176
+ // failure of guest to reproduce behavior from previous incarnations
177
+ t.throws(() => gOrch7.scale(4), {
178
+ message: /^panic over "\[Error: replay/,
179
+ });
180
+ };
181
+
182
+ /**
183
+ * @param {any} t
184
+ * @param {Zone} zone
185
+ */
186
+ const testGoodReplay = async (t, zone) => {
187
+ const vowTools = prepareVowTools(zone);
188
+ prepareLogStore(zone);
189
+ prepareBijection(zone);
190
+ prepareOrchestra(zone, 2); // 2 is new incarnation behavior change
191
+
192
+ const log = /** @type {LogStore} */ (
193
+ zone.makeOnce('log', () => Fail`need log`)
194
+ );
195
+ const bijection = /** @type {Bijection} */ (
196
+ zone.makeOnce('bij', () => Fail`need bij`)
197
+ );
198
+
199
+ const dump = log.dump();
200
+ const v1 = dump[0][1];
201
+ const hOrch7 = dump[1][1];
202
+ const hErr = dump[4][2];
203
+
204
+ t.false(bijection.hasHost(hOrch7));
205
+
206
+ t.deepEqual(dump, [
207
+ ['doFulfill', v1, 'x'],
208
+ ['checkCall', hOrch7, 'scale', [3], 1],
209
+ ['doReturn', 1, 21],
210
+ ['checkCall', hOrch7, 'scale', [9n], 3],
211
+ ['doThrow', 3, hErr],
212
+ ]);
213
+
214
+ const oldLogLen = dump.length;
215
+
216
+ const mem = makeReplayMembrane({
217
+ log,
218
+ bijection,
219
+ vowTools,
220
+ watchWake,
221
+ panic,
222
+ });
223
+
224
+ const g1 = mem.hostToGuest(v1);
225
+ mem.wake();
226
+ t.is(await g1, 'x');
227
+ const gOrch7 = mem.hostToGuest(hOrch7);
228
+ t.true(bijection.has(gOrch7, hOrch7));
229
+
230
+ // replay
231
+ const prodA = gOrch7.scale(3);
232
+ t.is(prodA, 21); // According to log of earlier incarnations
233
+ // let gErr;
234
+ try {
235
+ gOrch7.scale(9n);
236
+ } catch (e) {
237
+ // gErr = e;
238
+ }
239
+
240
+ // new play
241
+ const prodB = gOrch7.scale(3);
242
+ t.is(prodB, 42); // According to new incarnation behavior
243
+
244
+ const g2 = gOrch7.vow();
245
+ const h2 = mem.guestToHost(g2);
246
+ t.true(isPromise(g2));
247
+ const pairA = [gOrch7, g1];
248
+ gOrch7.resolve(pairA);
249
+ const pairB = await g2;
250
+ const [gOrchB, gB] = pairB;
251
+ t.not(pairB, pairA);
252
+ t.is(gOrchB, gOrch7);
253
+ t.is(gB, g1);
254
+
255
+ t.deepEqual(log.dump(), [
256
+ ['doFulfill', v1, 'x'],
257
+ ['checkCall', hOrch7, 'scale', [3], 1],
258
+ ['doReturn', 1, 21],
259
+ ['checkCall', hOrch7, 'scale', [9n], 3],
260
+ ['doThrow', 3, hErr],
261
+
262
+ ['checkCall', hOrch7, 'scale', [3], oldLogLen],
263
+ ['doReturn', oldLogLen, 42],
264
+ ['checkCall', hOrch7, 'vow', [], oldLogLen + 2],
265
+ ['doReturn', oldLogLen + 2, h2],
266
+ ['checkCall', hOrch7, 'resolve', [[hOrch7, v1]], oldLogLen + 4],
267
+ ['doReturn', oldLogLen + 4, undefined],
268
+ ['doFulfill', h2, [hOrch7, v1]],
269
+ ]);
270
+ };
271
+
272
+ test.serial('test heap replay-membrane', async t => {
273
+ const zone = makeHeapZone('heapRoot');
274
+ return testFirstPlay(t, zone, asyncFlowVerbose());
275
+ });
276
+
277
+ test.serial('test virtual replay-membrane', async t => {
278
+ annihilate();
279
+ const zone = makeVirtualZone('virtualRoot');
280
+ return testFirstPlay(t, zone);
281
+ });
282
+
283
+ test.serial('test durable replay-membrane', async t => {
284
+ annihilate();
285
+
286
+ nextLife();
287
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
288
+ await testFirstPlay(t, zone1);
289
+
290
+ nextLife();
291
+ const zone2 = makeDurableZone(getBaggage(), 'durableRoot');
292
+ await testBadReplay(t, zone2);
293
+
294
+ nextLife();
295
+ const zone3 = makeDurableZone(getBaggage(), 'durableRoot');
296
+ return testGoodReplay(t, zone3);
297
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": [
3
+ "./tsconfig.json",
4
+ "../../tsconfig-build-options.json"
5
+ ],
6
+ "exclude": [
7
+ "scripts",
8
+ "test",
9
+ "**/exports.js",
10
+ ]
11
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ // This file can contain .js-specific Typescript compiler config.
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "maxNodeModuleJsDepth": 2,
6
+ },
7
+ "include": [
8
+ "*.js",
9
+ "scripts",
10
+ "src/**/*.js",
11
+ "test/**/*.js",
12
+ ],
13
+ }
package/typedoc.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": [
3
+ "../../typedoc.base.json"
4
+ ],
5
+ "entryPoints": [
6
+ "index.js",
7
+ ]
8
+ }