@agoric/async-flow 0.1.1-dev-16095c5.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/CHANGELOG.md +1 -0
- package/LICENSE +201 -0
- package/README.md +40 -0
- package/docs/async-flow-states.key +0 -0
- package/docs/async-flow-states.md +15 -0
- package/docs/async-flow-states.png +0 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -0
- package/index.js +1 -0
- package/package.json +67 -0
- package/src/async-flow.d.ts +87 -0
- package/src/async-flow.d.ts.map +1 -0
- package/src/async-flow.js +502 -0
- package/src/bijection.d.ts +28 -0
- package/src/bijection.d.ts.map +1 -0
- package/src/bijection.js +132 -0
- package/src/convert.d.ts +5 -0
- package/src/convert.d.ts.map +1 -0
- package/src/convert.js +131 -0
- package/src/ephemera.d.ts +2 -0
- package/src/ephemera.d.ts.map +1 -0
- package/src/ephemera.js +35 -0
- package/src/equate.d.ts +2 -0
- package/src/equate.d.ts.map +1 -0
- package/src/equate.js +123 -0
- package/src/log-store.d.ts +25 -0
- package/src/log-store.d.ts.map +1 -0
- package/src/log-store.js +165 -0
- package/src/replay-membrane.d.ts +71 -0
- package/src/replay-membrane.d.ts.map +1 -0
- package/src/replay-membrane.js +435 -0
- package/src/type-guards.d.ts +4 -0
- package/src/type-guards.d.ts.map +1 -0
- package/src/type-guards.js +54 -0
- package/src/types.d.ts +70 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +164 -0
- package/test/async-flow-crank.test.js +96 -0
- package/test/async-flow-no-this.js +59 -0
- package/test/async-flow.test.js +380 -0
- package/test/bad-host.test.js +205 -0
- package/test/bijection.test.js +118 -0
- package/test/convert.test.js +127 -0
- package/test/equate.test.js +116 -0
- package/test/log-store.test.js +112 -0
- package/test/prepare-test-env-ava.js +28 -0
- package/test/replay-membrane-settlement.test.js +154 -0
- package/test/replay-membrane-zombie.test.js +158 -0
- package/test/replay-membrane.test.js +271 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +13 -0
- package/typedoc.json +8 -0
package/src/convert.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Fail, X, annotateError, makeError, q } from '@endo/errors';
|
|
2
|
+
import { throwLabeled } from '@endo/common/throw-labeled.js';
|
|
3
|
+
import {
|
|
4
|
+
getErrorConstructor,
|
|
5
|
+
getTag,
|
|
6
|
+
isObject,
|
|
7
|
+
makeTagged,
|
|
8
|
+
passStyleOf,
|
|
9
|
+
} from '@endo/pass-style';
|
|
10
|
+
import { isVow } from '@agoric/vow/src/vow-utils.js';
|
|
11
|
+
import { objectMap } from '@endo/common/object-map.js';
|
|
12
|
+
|
|
13
|
+
const makeConvert = (convertRemotable, convertPromiseOrVow, convertError) => {
|
|
14
|
+
const convertRecur = (specimen, label) => {
|
|
15
|
+
// Open code the synchronous part of applyLabelingError, because
|
|
16
|
+
// we need to preserve returned promise identity.
|
|
17
|
+
// TODO switch to Richard Gibson's suggestion for a better way
|
|
18
|
+
// to keep track of the error labeling.
|
|
19
|
+
// See https://github.com/endojs/endo/pull/1795#issuecomment-1756093032
|
|
20
|
+
if (label === undefined) {
|
|
21
|
+
// eslint-disable-next-line no-use-before-define
|
|
22
|
+
return innerConvert(specimen);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// eslint-disable-next-line no-use-before-define
|
|
26
|
+
return innerConvert(specimen);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
throwLabeled(err, label);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const innerConvert = specimen => {
|
|
33
|
+
if (!isObject(specimen)) {
|
|
34
|
+
return specimen;
|
|
35
|
+
}
|
|
36
|
+
const passStyle = passStyleOf(specimen);
|
|
37
|
+
switch (passStyle) {
|
|
38
|
+
case 'copyArray': {
|
|
39
|
+
return specimen.map((element, i) => convertRecur(element, i));
|
|
40
|
+
}
|
|
41
|
+
case 'copyRecord': {
|
|
42
|
+
return objectMap(specimen, (value, name) => convertRecur(value, name));
|
|
43
|
+
}
|
|
44
|
+
case 'tagged': {
|
|
45
|
+
if (isVow(specimen)) {
|
|
46
|
+
return convertPromiseOrVow(specimen);
|
|
47
|
+
}
|
|
48
|
+
const tag = getTag(specimen);
|
|
49
|
+
const { payload } = specimen;
|
|
50
|
+
return makeTagged(tag, convertRecur(payload, `${tag} payload`));
|
|
51
|
+
}
|
|
52
|
+
case 'error': {
|
|
53
|
+
return convertError(specimen);
|
|
54
|
+
}
|
|
55
|
+
case 'remotable': {
|
|
56
|
+
return convertRemotable(specimen);
|
|
57
|
+
}
|
|
58
|
+
case 'promise': {
|
|
59
|
+
return convertPromiseOrVow(specimen);
|
|
60
|
+
}
|
|
61
|
+
default: {
|
|
62
|
+
throw Fail`unexpected passStyle ${q(passStyle)}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {Passable} specimen
|
|
69
|
+
* @param {string} [label]
|
|
70
|
+
*/
|
|
71
|
+
const convert = (specimen, label = undefined) =>
|
|
72
|
+
convertRecur(harden(specimen), label);
|
|
73
|
+
return harden(convert);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const makeConvertKit = (
|
|
77
|
+
bijection,
|
|
78
|
+
makeGuestForHostRemotable,
|
|
79
|
+
makeGuestForHostVow,
|
|
80
|
+
) => {
|
|
81
|
+
const guestToHost = makeConvert(
|
|
82
|
+
gRem => {
|
|
83
|
+
if (bijection.hasGuest(gRem)) {
|
|
84
|
+
return bijection.guestToHost(gRem);
|
|
85
|
+
}
|
|
86
|
+
throw Fail`cannot yet send guest remotables ${gRem}`;
|
|
87
|
+
},
|
|
88
|
+
gProm => {
|
|
89
|
+
if (bijection.hasGuest(gProm)) {
|
|
90
|
+
return bijection.guestToHost(gProm);
|
|
91
|
+
}
|
|
92
|
+
throw Fail`cannot yet send guest promises ${gProm}`;
|
|
93
|
+
},
|
|
94
|
+
gErr => {
|
|
95
|
+
const hErr = harden(
|
|
96
|
+
makeError(gErr.message, getErrorConstructor(gErr.name)),
|
|
97
|
+
);
|
|
98
|
+
annotateError(hErr, X`from guest error ${gErr}`);
|
|
99
|
+
return hErr;
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const hostToGuest = makeConvert(
|
|
104
|
+
hRem => {
|
|
105
|
+
if (bijection.hasHost(hRem)) {
|
|
106
|
+
return bijection.hostToGuest(hRem);
|
|
107
|
+
}
|
|
108
|
+
const gRem = makeGuestForHostRemotable(hRem);
|
|
109
|
+
bijection.init(gRem, hRem);
|
|
110
|
+
return gRem;
|
|
111
|
+
},
|
|
112
|
+
hVow => {
|
|
113
|
+
if (bijection.hasHost(hVow)) {
|
|
114
|
+
return bijection.hostToGuest(hVow);
|
|
115
|
+
}
|
|
116
|
+
const gP = makeGuestForHostVow(hVow);
|
|
117
|
+
bijection.init(gP, hVow);
|
|
118
|
+
return gP;
|
|
119
|
+
},
|
|
120
|
+
hErr => {
|
|
121
|
+
const gErr = harden(
|
|
122
|
+
makeError(hErr.message, getErrorConstructor(hErr.name)),
|
|
123
|
+
);
|
|
124
|
+
annotateError(gErr, X`from host error ${hErr}`);
|
|
125
|
+
return gErr;
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return harden({ guestToHost, hostToGuest });
|
|
130
|
+
};
|
|
131
|
+
harden(makeConvertKit);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ephemera.d.ts","sourceRoot":"","sources":["ephemera.js"],"names":[],"mappings":"AAkBO,6BAFe,CAAC,4BAAC,CAAC,gCADd,CAAC,IAAI,EACM,CAAC,AADJ,KACK,CAAC,AADA,GACZ,QAAQ,CAAC,CAAC,EAAC,CAAC,CAAC,CAiBzB"}
|
package/src/ephemera.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Used by a possibly-durable exo to store per-instance ephemeral state.
|
|
3
|
+
* Each ephemera is created at the exo class prepare level, and then
|
|
4
|
+
* used from within the exo class methods to get state `eph.for(self)`.
|
|
5
|
+
* At the beginning of a new incarnation, there is no such state, so
|
|
6
|
+
* the first time it is accessed, it is initialized from `reinit(self)`.
|
|
7
|
+
* The ephemeral state can be dropped explicitly during an incarnation
|
|
8
|
+
* with `eph.resetFor(self)`, in which case the `eph.for(self)` will
|
|
9
|
+
* call it to be reinitialized again from `reinit(self)`.
|
|
10
|
+
*
|
|
11
|
+
* TODO consolidate with `makeEphemeraProvider` from `@agoric/zoe`, since
|
|
12
|
+
* they are serving similar purposes in similar ways.
|
|
13
|
+
*
|
|
14
|
+
* @template {WeakKey} [S=WeakKey]
|
|
15
|
+
* @template {any} [V=any]
|
|
16
|
+
* @param {(self: S) => V} reinit
|
|
17
|
+
* @returns {Ephemera<S,V>}
|
|
18
|
+
*/
|
|
19
|
+
export const makeEphemera = reinit => {
|
|
20
|
+
/** @type {WeakMap<S,V>} */
|
|
21
|
+
const map = new WeakMap();
|
|
22
|
+
|
|
23
|
+
return harden({
|
|
24
|
+
for(self) {
|
|
25
|
+
if (!map.has(self)) {
|
|
26
|
+
map.set(self, reinit(self));
|
|
27
|
+
}
|
|
28
|
+
return /** @type {V} */ (map.get(self));
|
|
29
|
+
},
|
|
30
|
+
resetFor(self) {
|
|
31
|
+
return map.delete(self);
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
harden(makeEphemera);
|
package/src/equate.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"equate.d.ts","sourceRoot":"","sources":["equate.js"],"names":[],"mappings":"AAQO,iFAiHN"}
|
package/src/equate.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Fail, X, annotateError, q } from '@endo/errors';
|
|
2
|
+
import { throwLabeled } from '@endo/common/throw-labeled.js';
|
|
3
|
+
import { getTag, isObject, passStyleOf } from '@endo/pass-style';
|
|
4
|
+
import { isVow } from '@agoric/vow/src/vow-utils.js';
|
|
5
|
+
import { recordNames } from '@endo/marshal';
|
|
6
|
+
|
|
7
|
+
const { is } = Object;
|
|
8
|
+
|
|
9
|
+
export const makeEquate = bijection => {
|
|
10
|
+
const equate = (g, h, label) => {
|
|
11
|
+
// Open code the synchronous part of applyLabelingError, because
|
|
12
|
+
// we need to preserve returned promise identity.
|
|
13
|
+
// TODO switch to Richard Gibson's suggestion for a better way
|
|
14
|
+
// to keep track of the error labeling.
|
|
15
|
+
if (label === undefined) {
|
|
16
|
+
// eslint-disable-next-line no-use-before-define
|
|
17
|
+
innerEquate(g, h);
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
// eslint-disable-next-line no-use-before-define
|
|
21
|
+
innerEquate(g, h);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
throwLabeled(err, label);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const innerEquate = (g, h) => {
|
|
28
|
+
if (!isObject(g)) {
|
|
29
|
+
is(g, h) ||
|
|
30
|
+
// separate line so I can set a breakpoint
|
|
31
|
+
Fail`unequal ${g} vs ${h}`;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (bijection.has(g, h)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const gPassStyle = passStyleOf(g);
|
|
38
|
+
if (gPassStyle === 'promise' && isVow(h)) {
|
|
39
|
+
// Important special case, because vows have passStyle 'tagged'.
|
|
40
|
+
// However, we do not yet support passing guest promise to host.
|
|
41
|
+
// TODO when we do, delete the `throw Fail` line and uncomment
|
|
42
|
+
// the two lines below it.
|
|
43
|
+
// We *do* support passing a guest wrapper of a hostVow back
|
|
44
|
+
// to the host, but that would be cause by `bijection.has` above.
|
|
45
|
+
throw Fail`guest promises not yet passable`;
|
|
46
|
+
// `init` does not yet do enough checking anyway. For this case,
|
|
47
|
+
// we should ensure that h is a host wrapper of a guest promise,
|
|
48
|
+
// which is a wrapping we don't yet support.
|
|
49
|
+
// bijection.init(g, h);
|
|
50
|
+
// return;
|
|
51
|
+
}
|
|
52
|
+
const hPassStyle = passStyleOf(h);
|
|
53
|
+
gPassStyle === hPassStyle ||
|
|
54
|
+
Fail`unequal passStyles ${q(gPassStyle)} vs ${q(hPassStyle)}`;
|
|
55
|
+
switch (gPassStyle) {
|
|
56
|
+
case 'copyArray': {
|
|
57
|
+
equate(g.length, h.length, 'length');
|
|
58
|
+
// eslint-disable-next-line github/array-foreach
|
|
59
|
+
g.forEach((gEl, i) => equate(gEl, h[i], i));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
case 'copyRecord': {
|
|
63
|
+
const gNames = recordNames(g);
|
|
64
|
+
const hNames = recordNames(h);
|
|
65
|
+
equate(gNames, hNames, 'propertyNames');
|
|
66
|
+
for (const name of gNames) {
|
|
67
|
+
equate(g[name], h[name], name);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
case 'tagged': {
|
|
72
|
+
equate(getTag(g), getTag(h), 'tag');
|
|
73
|
+
equate(g.payload, h.payload, 'payload');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
case 'error': {
|
|
77
|
+
equate(g.name, h.name, 'error name');
|
|
78
|
+
// For errors, all that needs to agree on replay is the `name`
|
|
79
|
+
// property. All others can differ. That's because everything else
|
|
80
|
+
// is assumed to be diagnostic info useful to programmers, which
|
|
81
|
+
// we'd like to improve over time. No programmatic use of additional
|
|
82
|
+
// error diagnostic info other than to better inform developers.
|
|
83
|
+
// A program should not take a semantically significant branch
|
|
84
|
+
// based on any of this diagnostic info, aside from `name`.
|
|
85
|
+
//
|
|
86
|
+
// Error annotations are not observable outside the console output,
|
|
87
|
+
// so this does not breach membrane isolation.
|
|
88
|
+
annotateError(g, X`replay of error ${h}`);
|
|
89
|
+
annotateError(h, X`replayed as error ${g}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
case 'remotable': {
|
|
93
|
+
// Note that we can send a guest wrapping of a host remotable
|
|
94
|
+
// back to the host,
|
|
95
|
+
// but that should have already been taken care of by the
|
|
96
|
+
// `bijection.has` above.
|
|
97
|
+
throw Fail`cannot yet send guest remotables to host ${g} vs ${h}`;
|
|
98
|
+
// `init` does not yet do enough checking anyway. For this case,
|
|
99
|
+
// we should ensure that h is a host wrapper of a guest remotable,
|
|
100
|
+
// which is a wrapping we don't yet support.
|
|
101
|
+
// bijection.init(g, h);
|
|
102
|
+
// return;
|
|
103
|
+
}
|
|
104
|
+
case 'promise': {
|
|
105
|
+
// Note that we can send a guest wrapping of a host promise
|
|
106
|
+
// (or vow) back to the host,
|
|
107
|
+
// but that should have already been taken care of by the
|
|
108
|
+
// `bijection.has` above.
|
|
109
|
+
throw Fail`cannot yet send guest promises to host ${g} vs ${h}`;
|
|
110
|
+
// `init` does not yet do enough checking anyway. For this case,
|
|
111
|
+
// we should ensure that h is a host wrapper of a guest promise,
|
|
112
|
+
// which is a wrapping we don't yet support.
|
|
113
|
+
// bijection.init(g, h);
|
|
114
|
+
// return;
|
|
115
|
+
}
|
|
116
|
+
default: {
|
|
117
|
+
throw Fail`unexpected passStyle ${q(gPassStyle)}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return harden(equate);
|
|
122
|
+
};
|
|
123
|
+
harden(makeEquate);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function prepareLogStore(zone: Zone): () => import("@endo/exo").Guarded<{
|
|
2
|
+
reset(): void;
|
|
3
|
+
dispose(): void;
|
|
4
|
+
getIndex(): number;
|
|
5
|
+
getLength(): number;
|
|
6
|
+
isReplaying(): boolean;
|
|
7
|
+
peekEntry(): LogEntry;
|
|
8
|
+
nextEntry(): LogEntry;
|
|
9
|
+
pushEntry(entry: any): number;
|
|
10
|
+
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])[];
|
|
11
|
+
promiseReplayDone(): Promise<undefined>;
|
|
12
|
+
}>;
|
|
13
|
+
export type LogStore = ReturnType<ReturnType<(zone: Zone) => () => import("@endo/exo").Guarded<{
|
|
14
|
+
reset(): void;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
getIndex(): number;
|
|
17
|
+
getLength(): number;
|
|
18
|
+
isReplaying(): boolean;
|
|
19
|
+
peekEntry(): LogEntry;
|
|
20
|
+
nextEntry(): LogEntry;
|
|
21
|
+
pushEntry(entry: any): number;
|
|
22
|
+
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])[];
|
|
23
|
+
promiseReplayDone(): Promise<undefined>;
|
|
24
|
+
}>>>;
|
|
25
|
+
//# sourceMappingURL=log-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-store.d.ts","sourceRoot":"","sources":["log-store.js"],"names":[],"mappings":"AA4BO;;;;;;;;;;;GAoIN;uBAGY,UAAU,CAAC,UAAU;;;;;;;;;;;GAAiB,CAAC"}
|
package/src/log-store.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Fail, q } from '@endo/errors';
|
|
2
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
3
|
+
import { M } from '@endo/patterns';
|
|
4
|
+
import { LogEntryShape } from './type-guards.js';
|
|
5
|
+
import { makeEphemera } from './ephemera.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @import {MapStore} from '@agoric/store'
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const LogStoreI = M.interface('LogStore', {
|
|
12
|
+
reset: M.call().returns(),
|
|
13
|
+
dispose: M.call().returns(),
|
|
14
|
+
getIndex: M.call().returns(M.number()),
|
|
15
|
+
getLength: M.call().returns(M.number()),
|
|
16
|
+
isReplaying: M.call().returns(M.boolean()),
|
|
17
|
+
peekEntry: M.call().returns(LogEntryShape),
|
|
18
|
+
nextEntry: M.call().returns(LogEntryShape),
|
|
19
|
+
pushEntry: M.call(LogEntryShape).returns(M.number()),
|
|
20
|
+
dump: M.call().returns(M.arrayOf(LogEntryShape)),
|
|
21
|
+
promiseReplayDone: M.call().returns(M.promise()),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A growable, replayable, sequence of `LogEntry`s.
|
|
26
|
+
*
|
|
27
|
+
* @param {Zone} zone
|
|
28
|
+
*/
|
|
29
|
+
export const prepareLogStore = zone => {
|
|
30
|
+
/**
|
|
31
|
+
* @type {Ephemera<LogStore, {
|
|
32
|
+
* index: number
|
|
33
|
+
* replayDoneKit: PromiseKit<undefined>
|
|
34
|
+
* }>}
|
|
35
|
+
*/
|
|
36
|
+
const tmp = makeEphemera(log => {
|
|
37
|
+
const result = {
|
|
38
|
+
index: 0,
|
|
39
|
+
replayDoneKit: makePromiseKit(),
|
|
40
|
+
};
|
|
41
|
+
if (log.getLength() === 0) {
|
|
42
|
+
result.replayDoneKit.resolve(undefined);
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return zone.exoClass(
|
|
48
|
+
'LogStore',
|
|
49
|
+
LogStoreI,
|
|
50
|
+
() => {
|
|
51
|
+
/**
|
|
52
|
+
* Really used to emulate a zone-storable vector, i.e., what in
|
|
53
|
+
* conventional JS you'd use a mutable array for, where you mutate
|
|
54
|
+
* only by `.push`
|
|
55
|
+
*
|
|
56
|
+
* @type {MapStore<number, LogEntry>}
|
|
57
|
+
*/
|
|
58
|
+
const mapStore = zone.detached().mapStore('logMapStore', {
|
|
59
|
+
keyShape: M.number(),
|
|
60
|
+
valueShape: LogEntryShape,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
mapStore,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
reset() {
|
|
68
|
+
const { self } = this;
|
|
69
|
+
tmp.resetFor(self);
|
|
70
|
+
|
|
71
|
+
// TODO: Should we resolve replayDoneKit here, in case we're
|
|
72
|
+
// transitioning to a Failed state, so that any pending watchers
|
|
73
|
+
// can exit?
|
|
74
|
+
},
|
|
75
|
+
dispose() {
|
|
76
|
+
const { state, self } = this;
|
|
77
|
+
const { mapStore } = state;
|
|
78
|
+
|
|
79
|
+
tmp.resetFor(self);
|
|
80
|
+
mapStore.clear();
|
|
81
|
+
},
|
|
82
|
+
getIndex() {
|
|
83
|
+
const { self } = this;
|
|
84
|
+
const eph = tmp.for(self);
|
|
85
|
+
|
|
86
|
+
return eph.index;
|
|
87
|
+
},
|
|
88
|
+
getLength() {
|
|
89
|
+
const { state } = this;
|
|
90
|
+
const { mapStore } = state;
|
|
91
|
+
|
|
92
|
+
return mapStore.getSize();
|
|
93
|
+
},
|
|
94
|
+
isReplaying() {
|
|
95
|
+
const { state, self } = this;
|
|
96
|
+
const { mapStore } = state;
|
|
97
|
+
const eph = tmp.for(self);
|
|
98
|
+
|
|
99
|
+
return eph.index < mapStore.getSize();
|
|
100
|
+
},
|
|
101
|
+
peekEntry() {
|
|
102
|
+
const { state, self } = this;
|
|
103
|
+
const { mapStore } = state;
|
|
104
|
+
const eph = tmp.for(self);
|
|
105
|
+
|
|
106
|
+
self.isReplaying() ||
|
|
107
|
+
Fail`No longer replaying: ${q(eph.index)} vs ${q(
|
|
108
|
+
mapStore.getSize(),
|
|
109
|
+
)}`;
|
|
110
|
+
const result = mapStore.get(eph.index);
|
|
111
|
+
return result;
|
|
112
|
+
},
|
|
113
|
+
nextEntry() {
|
|
114
|
+
const { self } = this;
|
|
115
|
+
const eph = tmp.for(self);
|
|
116
|
+
|
|
117
|
+
const result = self.peekEntry();
|
|
118
|
+
eph.index += 1;
|
|
119
|
+
if (!self.isReplaying()) {
|
|
120
|
+
eph.replayDoneKit.resolve(undefined);
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
},
|
|
124
|
+
pushEntry(entry) {
|
|
125
|
+
const { state, self } = this;
|
|
126
|
+
const { mapStore } = state;
|
|
127
|
+
const eph = tmp.for(self);
|
|
128
|
+
|
|
129
|
+
!self.isReplaying() ||
|
|
130
|
+
Fail`still replaying: ${q(eph.index)} vs ${q(mapStore.getSize())}`;
|
|
131
|
+
eph.index === mapStore.getSize() ||
|
|
132
|
+
Fail`internal: index confusion ${q(eph.index)} vs ${q(
|
|
133
|
+
mapStore.getSize(),
|
|
134
|
+
)}`;
|
|
135
|
+
mapStore.init(eph.index, entry);
|
|
136
|
+
eph.index += 1;
|
|
137
|
+
eph.index === mapStore.getSize() ||
|
|
138
|
+
Fail`internal: index confusion ${q(eph.index)} vs ${q(
|
|
139
|
+
mapStore.getSize(),
|
|
140
|
+
)}`;
|
|
141
|
+
return eph.index;
|
|
142
|
+
},
|
|
143
|
+
dump() {
|
|
144
|
+
const { state } = this;
|
|
145
|
+
const { mapStore } = state;
|
|
146
|
+
const len = mapStore.getSize();
|
|
147
|
+
const result = [];
|
|
148
|
+
for (let i = 0; i < len; i += 1) {
|
|
149
|
+
result.push(mapStore.get(i));
|
|
150
|
+
}
|
|
151
|
+
return harden(result);
|
|
152
|
+
},
|
|
153
|
+
promiseReplayDone() {
|
|
154
|
+
const { self } = this;
|
|
155
|
+
const eph = tmp.for(self);
|
|
156
|
+
|
|
157
|
+
return eph.replayDoneKit.promise;
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @typedef {ReturnType<ReturnType<prepareLogStore>>} LogStore
|
|
165
|
+
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export function makeReplayMembrane(log: import("@endo/exo").Guarded<{
|
|
2
|
+
reset(): void;
|
|
3
|
+
dispose(): void;
|
|
4
|
+
getIndex(): number;
|
|
5
|
+
getLength(): number;
|
|
6
|
+
isReplaying(): boolean;
|
|
7
|
+
peekEntry(): LogEntry;
|
|
8
|
+
nextEntry(): LogEntry;
|
|
9
|
+
pushEntry(entry: any): number;
|
|
10
|
+
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])[];
|
|
11
|
+
promiseReplayDone(): Promise<undefined>;
|
|
12
|
+
}>, bijection: import("@endo/exo").Guarded<{
|
|
13
|
+
reset(): void;
|
|
14
|
+
init(g: any, h: any): void;
|
|
15
|
+
hasGuest(g: any): boolean;
|
|
16
|
+
hasHost(h: any): boolean;
|
|
17
|
+
has(g: any, h: any): boolean;
|
|
18
|
+
guestToHost(g: any): any;
|
|
19
|
+
hostToGuest(h: any): any;
|
|
20
|
+
}>, vowTools: {
|
|
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>;
|
|
23
|
+
makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
|
|
24
|
+
allVows: (vows: any) => Vow<any>;
|
|
25
|
+
}, watchWake: (vowish: Promise<any> | Vow) => void, panic: (problem: Error) => never): {
|
|
26
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
27
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
28
|
+
wake: () => void;
|
|
29
|
+
stop: () => void;
|
|
30
|
+
} & import("@endo/pass-style").RemotableObject<`Alleged: ${string}`> & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
31
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
32
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
33
|
+
wake: () => void;
|
|
34
|
+
stop: () => void;
|
|
35
|
+
}>;
|
|
36
|
+
export type ReplayMembrane = ReturnType<(log: import("@endo/exo").Guarded<{
|
|
37
|
+
reset(): void;
|
|
38
|
+
dispose(): void;
|
|
39
|
+
getIndex(): number;
|
|
40
|
+
getLength(): number;
|
|
41
|
+
isReplaying(): boolean;
|
|
42
|
+
peekEntry(): LogEntry;
|
|
43
|
+
nextEntry(): LogEntry;
|
|
44
|
+
pushEntry(entry: any): number;
|
|
45
|
+
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])[];
|
|
46
|
+
promiseReplayDone(): Promise<undefined>;
|
|
47
|
+
}>, bijection: import("@endo/exo").Guarded<{
|
|
48
|
+
reset(): void;
|
|
49
|
+
init(g: any, h: any): void;
|
|
50
|
+
hasGuest(g: any): boolean;
|
|
51
|
+
hasHost(h: any): boolean;
|
|
52
|
+
has(g: any, h: any): boolean;
|
|
53
|
+
guestToHost(g: any): any;
|
|
54
|
+
hostToGuest(h: any): any;
|
|
55
|
+
}>, vowTools: {
|
|
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>;
|
|
58
|
+
makeVowKit: <T_2>() => import("@agoric/vow").VowKit<T_2>;
|
|
59
|
+
allVows: (vows: any) => Vow<any>;
|
|
60
|
+
}, watchWake: (vowish: Promise<any> | Vow) => void, panic: (problem: Error) => never) => {
|
|
61
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
62
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
63
|
+
wake: () => void;
|
|
64
|
+
stop: () => void;
|
|
65
|
+
} & import("@endo/pass-style").RemotableObject<`Alleged: ${string}`> & import("@endo/eventual-send").RemotableBrand<{}, {
|
|
66
|
+
hostToGuest: (specimen: Passable, label?: string | undefined) => any;
|
|
67
|
+
guestToHost: (specimen: Passable, label?: string | undefined) => any;
|
|
68
|
+
wake: () => void;
|
|
69
|
+
stop: () => void;
|
|
70
|
+
}>>;
|
|
71
|
+
//# sourceMappingURL=replay-membrane.d.ts.map
|
|
@@ -0,0 +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"}
|