@agoric/vow 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.
- package/LICENSE +201 -0
- package/README.md +114 -0
- package/package.json +58 -0
- package/src/E.d.ts +165 -0
- package/src/E.d.ts.map +1 -0
- package/src/E.js +484 -0
- package/src/index.d.ts +6 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +16 -0
- package/src/message-breakpoints.d.ts +44 -0
- package/src/message-breakpoints.d.ts.map +1 -0
- package/src/message-breakpoints.js +180 -0
- package/src/tools.d.ts +25 -0
- package/src/tools.d.ts.map +1 -0
- package/src/tools.js +50 -0
- package/src/track-turns.d.ts +7 -0
- package/src/track-turns.d.ts.map +1 -0
- package/src/track-turns.js +114 -0
- package/src/types.d.ts +62 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +116 -0
- package/src/vow-e.d.ts +14 -0
- package/src/vow-e.d.ts.map +1 -0
- package/src/vow-e.js +101 -0
- package/src/vow-utils.d.ts +20 -0
- package/src/vow-utils.d.ts.map +1 -0
- package/src/vow-utils.js +112 -0
- package/src/vow.d.ts +7 -0
- package/src/vow.d.ts.map +1 -0
- package/src/vow.js +216 -0
- package/src/watch-utils.d.ts +20 -0
- package/src/watch-utils.d.ts.map +1 -0
- package/src/watch-utils.js +220 -0
- package/src/watch.d.ts +8 -0
- package/src/watch.d.ts.map +1 -0
- package/src/watch.js +199 -0
- package/src/when.d.ts +5 -0
- package/src/when.d.ts.map +1 -0
- package/src/when.js +76 -0
- package/vat.js +43 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { getEnvironmentOption } from '@endo/env-options';
|
|
3
|
+
|
|
4
|
+
const { quote: q, Fail } = assert;
|
|
5
|
+
|
|
6
|
+
const { hasOwn, freeze, entries } = Object;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {string | '*'} MatchStringTag
|
|
10
|
+
* A star `'*'` matches any recipient. Otherwise, the string is
|
|
11
|
+
* matched against the value of a recipient's `@@toStringTag`
|
|
12
|
+
* after stripping out any leading `'Alleged: '` or `'DebugName: '`
|
|
13
|
+
* prefix. For objects defined with `Far` this is the first argument,
|
|
14
|
+
* known as the `farName`. For exos, this is the tag.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {string | '*'} MatchMethodName
|
|
18
|
+
* A star `'*'` matches any method name. Otherwise, the string is
|
|
19
|
+
* matched against the method name. Currently, this is only an exact match.
|
|
20
|
+
* However, beware that we may introduce a string syntax for
|
|
21
|
+
* symbol method names.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {number | '*'} MatchCountdown
|
|
25
|
+
* A star `'*'` will always breakpoint. Otherwise, the string
|
|
26
|
+
* must be a non-negative integer. Once that is zero, always breakpoint.
|
|
27
|
+
* Otherwise decrement by one each time it matches until it reaches zero.
|
|
28
|
+
* In other words, the countdown represents the number of
|
|
29
|
+
* breakpoint occurrences to skip before actually breakpointing.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* This is the external JSON representation, in which
|
|
34
|
+
* - the outer property name is the class-like tag or '*',
|
|
35
|
+
* - the inner property name is the method name or '*',
|
|
36
|
+
* - the value is a non-negative integer countdown or '*'.
|
|
37
|
+
*
|
|
38
|
+
* @typedef {Record<MatchStringTag, Record<MatchMethodName, MatchCountdown>>} MessageBreakpoints
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* This is the internal JSON representation, in which
|
|
43
|
+
* - the outer property name is the method name or '*',
|
|
44
|
+
* - the inner property name is the class-like tag or '*',
|
|
45
|
+
* - the value is a non-negative integer countdown or '*'.
|
|
46
|
+
*
|
|
47
|
+
* @typedef {Record<MatchMethodName, Record<MatchStringTag, MatchCountdown>>} BreakpointTable
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {object} MessageBreakpointTester
|
|
52
|
+
* @property {() => MessageBreakpoints} getBreakpoints
|
|
53
|
+
* @property {(newBreakpoints?: MessageBreakpoints) => void} setBreakpoints
|
|
54
|
+
* @property {(
|
|
55
|
+
* recipient: object,
|
|
56
|
+
* methodName: string | symbol | undefined
|
|
57
|
+
* ) => boolean} shouldBreakpoint
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {any} val
|
|
62
|
+
* @returns {val is Record<string, any>}
|
|
63
|
+
*/
|
|
64
|
+
const isJSONRecord = val =>
|
|
65
|
+
typeof val === 'object' && val !== null && !Array.isArray(val);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Return `tag` after stripping off any `'Alleged: '` or `'DebugName: '`
|
|
69
|
+
* prefix if present.
|
|
70
|
+
* ```js
|
|
71
|
+
* simplifyTag('Alleged: moola issuer') === 'moola issuer'
|
|
72
|
+
* ```
|
|
73
|
+
* If there are multiple such prefixes, only the outer one is removed.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} tag
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
const simplifyTag = tag => {
|
|
79
|
+
for (const prefix of ['Alleged: ', 'DebugName: ']) {
|
|
80
|
+
if (tag.startsWith(prefix)) {
|
|
81
|
+
return tag.slice(prefix.length);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return tag;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {string} optionName
|
|
89
|
+
* @returns {MessageBreakpointTester | undefined}
|
|
90
|
+
*/
|
|
91
|
+
export const makeMessageBreakpointTester = optionName => {
|
|
92
|
+
let breakpoints = JSON.parse(getEnvironmentOption(optionName, 'null'));
|
|
93
|
+
|
|
94
|
+
if (breakpoints === null) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @type {BreakpointTable} */
|
|
99
|
+
let breakpointsTable;
|
|
100
|
+
|
|
101
|
+
const getBreakpoints = () => breakpoints;
|
|
102
|
+
freeze(getBreakpoints);
|
|
103
|
+
|
|
104
|
+
const setBreakpoints = (newBreakpoints = breakpoints) => {
|
|
105
|
+
isJSONRecord(newBreakpoints) ||
|
|
106
|
+
Fail`Expected ${q(optionName)} option to be a JSON breakpoints record`;
|
|
107
|
+
|
|
108
|
+
/** @type {BreakpointTable} */
|
|
109
|
+
// @ts-expect-error confused by __proto__
|
|
110
|
+
const newBreakpointsTable = { __proto__: null };
|
|
111
|
+
|
|
112
|
+
for (const [tag, methodBPs] of entries(newBreakpoints)) {
|
|
113
|
+
tag === simplifyTag(tag) ||
|
|
114
|
+
Fail`Just use simple tag ${q(simplifyTag(tag))} rather than ${q(tag)}`;
|
|
115
|
+
isJSONRecord(methodBPs) ||
|
|
116
|
+
Fail`Expected ${q(optionName)} option's ${q(
|
|
117
|
+
tag,
|
|
118
|
+
)} to be a JSON methods breakpoints record`;
|
|
119
|
+
for (const [methodName, count] of entries(methodBPs)) {
|
|
120
|
+
count === '*' ||
|
|
121
|
+
(typeof count === 'number' &&
|
|
122
|
+
Number.isSafeInteger(count) &&
|
|
123
|
+
count >= 0) ||
|
|
124
|
+
Fail`Expected ${q(optionName)} option's ${q(tag)}.${q(
|
|
125
|
+
methodName,
|
|
126
|
+
)} to be "*" or a non-negative integer`;
|
|
127
|
+
|
|
128
|
+
const classBPs = hasOwn(newBreakpointsTable, methodName)
|
|
129
|
+
? newBreakpointsTable[methodName]
|
|
130
|
+
: (newBreakpointsTable[methodName] = {
|
|
131
|
+
// @ts-expect-error confused by __proto__
|
|
132
|
+
__proto__: null,
|
|
133
|
+
});
|
|
134
|
+
classBPs[tag] = count;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
breakpoints = newBreakpoints;
|
|
138
|
+
breakpointsTable = newBreakpointsTable;
|
|
139
|
+
};
|
|
140
|
+
freeze(setBreakpoints);
|
|
141
|
+
|
|
142
|
+
const shouldBreakpoint = (recipient, methodName) => {
|
|
143
|
+
if (methodName === undefined || methodName === null) {
|
|
144
|
+
// TODO enable function breakpointing
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
const classBPs = breakpointsTable[methodName] || breakpointsTable['*'];
|
|
148
|
+
if (classBPs === undefined) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
let tag = simplifyTag(recipient[Symbol.toStringTag]);
|
|
152
|
+
let count = classBPs[tag];
|
|
153
|
+
if (count === undefined) {
|
|
154
|
+
tag = '*';
|
|
155
|
+
count = classBPs[tag];
|
|
156
|
+
if (count === undefined) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (count === '*') {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
if (count === 0) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
assert(typeof count === 'number' && count >= 1);
|
|
167
|
+
classBPs[tag] = count - 1;
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
freeze(shouldBreakpoint);
|
|
171
|
+
|
|
172
|
+
const breakpointTester = freeze({
|
|
173
|
+
getBreakpoints,
|
|
174
|
+
setBreakpoints,
|
|
175
|
+
shouldBreakpoint,
|
|
176
|
+
});
|
|
177
|
+
breakpointTester.setBreakpoints();
|
|
178
|
+
return breakpointTester;
|
|
179
|
+
};
|
|
180
|
+
freeze(makeMessageBreakpointTester);
|
package/src/tools.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function prepareVowTools(zone: Zone, powers?: {
|
|
2
|
+
isRetryableReason?: IsRetryableReason | undefined;
|
|
3
|
+
} | undefined): {
|
|
4
|
+
E: (<T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => ECallableOrMethods<RemoteFunctions<T>>) & {
|
|
5
|
+
readonly get: <T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => EGetters<LocalRecord<T>>;
|
|
6
|
+
readonly resolve: <T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => Promise<Awaited<T>>;
|
|
7
|
+
readonly sendOnly: <T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => ESendOnlyCallableOrMethods<RemoteFunctions<T>>;
|
|
8
|
+
readonly when: <T, TResult1 = import("./types.js").EUnwrap<T>, TResult2 = never>(x: import("./E.js").ERef<T>, onfulfilled?: ((value: import("./types.js").EUnwrap<T>) => ERef<TResult1>) | undefined, onrejected?: ((reason: any) => ERef<TResult2>) | undefined, opts?: import("./types.js").EUnwrapOptions | undefined) => Promise<TResult1 | TResult2>;
|
|
9
|
+
} & {
|
|
10
|
+
when: <T, TResult1 = import("./types.js").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("./types.js").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
11
|
+
watch: <T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]>(specimenP: EVow<T>, watcher?: import("./types.js").Watcher<T, TResult1, TResult2, C> | undefined, ...watcherArgs: C) => import("./types.js").Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
|
|
12
|
+
};
|
|
13
|
+
when: <T, TResult1 = import("./types.js").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("./types.js").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
14
|
+
watch: <T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]>(specimenP: EVow<T>, watcher?: import("./types.js").Watcher<T, TResult1, TResult2, C> | undefined, ...watcherArgs: C) => import("./types.js").Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
|
|
15
|
+
makeVowKit: <T>() => import("./types.js").VowKit<T>;
|
|
16
|
+
allVows: (maybeVows: EVow<unknown>[]) => import("./types.js").Vow<any[]>;
|
|
17
|
+
asVow: <T extends unknown>(fn: (...args: any[]) => import("./types.js").Vow<Awaited<T>> | Awaited<T> | import("./types.js").PromiseVow<T>) => import("./types.js").Vow<Awaited<T>>;
|
|
18
|
+
asPromise: AsPromiseFunction;
|
|
19
|
+
};
|
|
20
|
+
export type VowTools = ReturnType<typeof prepareVowTools>;
|
|
21
|
+
import type { Zone } from '@agoric/base-zone';
|
|
22
|
+
import type { IsRetryableReason } from './types.js';
|
|
23
|
+
import type { EVow } from './types.js';
|
|
24
|
+
import type { AsPromiseFunction } from './types.js';
|
|
25
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["tools.js"],"names":[],"mappings":"AAkBO,sCAJI,IAAI;;;;;;;;;;;;;;;yBAuBF,KAAK,OAAO,CAAC,EAAE;oCAaqyC,GAAG;;EAJn0C;uBAGa,UAAU,CAAC,OAAO,eAAe,CAAC;0BAxCzB,mBAAmB;uCACmB,YAAY;0BAAZ,YAAY;uCAAZ,YAAY"}
|
package/src/tools.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { makeWhen } from './when.js';
|
|
3
|
+
import { prepareVowKit } from './vow.js';
|
|
4
|
+
import { prepareWatch } from './watch.js';
|
|
5
|
+
import { prepareWatchUtils } from './watch-utils.js';
|
|
6
|
+
import { makeAsVow } from './vow-utils.js';
|
|
7
|
+
import { makeVowE } from './vow-e.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @import {Zone} from '@agoric/base-zone';
|
|
11
|
+
* @import {IsRetryableReason, AsPromiseFunction, EVow} from './types.js';
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {Zone} zone
|
|
16
|
+
* @param {object} [powers]
|
|
17
|
+
* @param {IsRetryableReason} [powers.isRetryableReason]
|
|
18
|
+
*/
|
|
19
|
+
export const prepareVowTools = (zone, powers = {}) => {
|
|
20
|
+
const { isRetryableReason = /** @type {IsRetryableReason} */ (() => false) } =
|
|
21
|
+
powers;
|
|
22
|
+
const makeVowKit = prepareVowKit(zone);
|
|
23
|
+
const when = makeWhen(isRetryableReason);
|
|
24
|
+
const watch = prepareWatch(zone, makeVowKit, isRetryableReason);
|
|
25
|
+
const makeWatchUtils = prepareWatchUtils(zone, {
|
|
26
|
+
watch,
|
|
27
|
+
when,
|
|
28
|
+
makeVowKit,
|
|
29
|
+
isRetryableReason,
|
|
30
|
+
});
|
|
31
|
+
const watchUtils = makeWatchUtils();
|
|
32
|
+
const asVow = makeAsVow(makeVowKit);
|
|
33
|
+
const E = makeVowE(zone, { isRetryableReason, when, watch });
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Vow-tolerant implementation of Promise.all.
|
|
37
|
+
*
|
|
38
|
+
* @param {EVow<unknown>[]} maybeVows
|
|
39
|
+
*/
|
|
40
|
+
const allVows = maybeVows => watchUtils.all(maybeVows);
|
|
41
|
+
|
|
42
|
+
/** @type {AsPromiseFunction} */
|
|
43
|
+
const asPromise = (specimenP, ...watcherArgs) =>
|
|
44
|
+
watchUtils.asPromise(specimenP, ...watcherArgs);
|
|
45
|
+
|
|
46
|
+
return harden({ E, when, watch, makeVowKit, allVows, asVow, asPromise });
|
|
47
|
+
};
|
|
48
|
+
harden(prepareVowTools);
|
|
49
|
+
|
|
50
|
+
/** @typedef {ReturnType<typeof prepareVowTools>} VowTools */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function trackTurns<T extends TurnStarterFn[]>(funcs: T): T;
|
|
2
|
+
/**
|
|
3
|
+
* An optional function that is not this-sensitive, expected to be called at
|
|
4
|
+
* bottom of stack to start a new turn.
|
|
5
|
+
*/
|
|
6
|
+
export type TurnStarterFn = ((...args: any[]) => any) | undefined;
|
|
7
|
+
//# sourceMappingURL=track-turns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track-turns.d.ts","sourceRoot":"","sources":["track-turns.js"],"names":[],"mappings":"AAyFO,2BAJwB,CAAC,SAAlB,aAAa,EAAG,SACnB,CAAC,GACC,CAAC,CAmBb;;;;;4BAMY,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,GAAG,SAAS"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/* global globalThis */
|
|
3
|
+
import {
|
|
4
|
+
getEnvironmentOption,
|
|
5
|
+
environmentOptionsListHas,
|
|
6
|
+
} from '@endo/env-options';
|
|
7
|
+
|
|
8
|
+
// NOTE: We can't import these because they're not in scope before lockdown.
|
|
9
|
+
// import { assert, details as X, Fail } from '@agoric/assert';
|
|
10
|
+
|
|
11
|
+
// WARNING: Global Mutable State!
|
|
12
|
+
// This state is communicated to `assert` that makes it available to the
|
|
13
|
+
// causal console, which affects the console log output. Normally we
|
|
14
|
+
// regard the ability to see console log output as a meta-level privilege
|
|
15
|
+
// analogous to the ability to debug. Aside from that, this module should
|
|
16
|
+
// not have any observably mutable state.
|
|
17
|
+
|
|
18
|
+
let hiddenPriorError;
|
|
19
|
+
let hiddenCurrentTurn = 0;
|
|
20
|
+
let hiddenCurrentEvent = 0;
|
|
21
|
+
|
|
22
|
+
// Turn on if you seem to be losing error logging at the top of the event loop
|
|
23
|
+
const VERBOSE = environmentOptionsListHas('DEBUG', 'track-turns');
|
|
24
|
+
|
|
25
|
+
// Track-turns is disabled by default and can be enabled by an environment
|
|
26
|
+
// option.
|
|
27
|
+
const ENABLED =
|
|
28
|
+
getEnvironmentOption('TRACK_TURNS', 'disabled', ['enabled']) === 'enabled';
|
|
29
|
+
|
|
30
|
+
// We hoist the following functions out of trackTurns() to discourage the
|
|
31
|
+
// closures from holding onto 'args' or 'func' longer than necessary,
|
|
32
|
+
// which we've seen cause HandledPromise arguments to be retained for
|
|
33
|
+
// a surprisingly long time.
|
|
34
|
+
|
|
35
|
+
const addRejectionNote = detailsNote => reason => {
|
|
36
|
+
if (reason instanceof Error) {
|
|
37
|
+
assert.note(reason, detailsNote);
|
|
38
|
+
}
|
|
39
|
+
if (VERBOSE) {
|
|
40
|
+
console.log('REJECTED at top of event loop', reason);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const wrapFunction =
|
|
45
|
+
(func, sendingError, X) =>
|
|
46
|
+
(...args) => {
|
|
47
|
+
hiddenPriorError = sendingError;
|
|
48
|
+
hiddenCurrentTurn += 1;
|
|
49
|
+
hiddenCurrentEvent = 0;
|
|
50
|
+
try {
|
|
51
|
+
let result;
|
|
52
|
+
try {
|
|
53
|
+
result = func(...args);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if (err instanceof Error) {
|
|
56
|
+
assert.note(
|
|
57
|
+
err,
|
|
58
|
+
X`Thrown from: ${hiddenPriorError}:${hiddenCurrentTurn}.${hiddenCurrentEvent}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (VERBOSE) {
|
|
62
|
+
console.log('THROWN to top of event loop', err);
|
|
63
|
+
}
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
// Must capture this now, not when the catch triggers.
|
|
67
|
+
const detailsNote = X`Rejection from: ${hiddenPriorError}:${hiddenCurrentTurn}.${hiddenCurrentEvent}`;
|
|
68
|
+
Promise.resolve(result).catch(addRejectionNote(detailsNote));
|
|
69
|
+
return result;
|
|
70
|
+
} finally {
|
|
71
|
+
hiddenPriorError = undefined;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Given a list of `TurnStarterFn`s, returns a list of `TurnStarterFn`s whose
|
|
77
|
+
* `this`-free call behaviors are not observably different to those that
|
|
78
|
+
* cannot see console output. The only purpose is to cause additional
|
|
79
|
+
* information to appear on the console.
|
|
80
|
+
*
|
|
81
|
+
* The call to `trackTurns` is itself a sending event, that occurs in some call
|
|
82
|
+
* stack in some turn number at some event number within that turn. Each call
|
|
83
|
+
* to any of the returned `TurnStartFn`s is a receiving event that begins a new
|
|
84
|
+
* turn. This sending event caused each of those receiving events.
|
|
85
|
+
*
|
|
86
|
+
* @template {TurnStarterFn[]} T
|
|
87
|
+
* @param {T} funcs
|
|
88
|
+
* @returns {T}
|
|
89
|
+
*/
|
|
90
|
+
export const trackTurns = funcs => {
|
|
91
|
+
if (!ENABLED || typeof globalThis === 'undefined' || !globalThis.assert) {
|
|
92
|
+
return funcs;
|
|
93
|
+
}
|
|
94
|
+
const { details: X } = assert;
|
|
95
|
+
|
|
96
|
+
hiddenCurrentEvent += 1;
|
|
97
|
+
const sendingError = Error(
|
|
98
|
+
`Event: ${hiddenCurrentTurn}.${hiddenCurrentEvent}`,
|
|
99
|
+
);
|
|
100
|
+
if (hiddenPriorError !== undefined) {
|
|
101
|
+
assert.note(sendingError, X`Caused by: ${hiddenPriorError}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return /** @type {T} */ (
|
|
105
|
+
funcs.map(func => func && wrapFunction(func, sendingError, X))
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* An optional function that is not this-sensitive, expected to be called at
|
|
111
|
+
* bottom of stack to start a new turn.
|
|
112
|
+
*
|
|
113
|
+
* @typedef {((...args: any[]) => any) | undefined} TurnStarterFn
|
|
114
|
+
*/
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return truthy if a rejection reason should result in a retry.
|
|
3
|
+
*/
|
|
4
|
+
export type IsRetryableReason = (reason: any, priorRetryValue?: any) => any;
|
|
5
|
+
/**
|
|
6
|
+
* Return type of a function that may
|
|
7
|
+
* return a promise or a vow.
|
|
8
|
+
*/
|
|
9
|
+
export type PromiseVow<T> = Promise<T | Vow<T>>;
|
|
10
|
+
export type EUnwrapOptions = {
|
|
11
|
+
watch?: boolean | [] | AnyWatcher | [AnyWatcher, ...args: any[]] | undefined;
|
|
12
|
+
};
|
|
13
|
+
export type ERef<T> = T | PromiseLike<T>;
|
|
14
|
+
/**
|
|
15
|
+
* Eventually a value T or Vow for it.
|
|
16
|
+
*/
|
|
17
|
+
export type EVow<T> = ERef<T | Vow<T>>;
|
|
18
|
+
/**
|
|
19
|
+
* Follow the chain of vow shortening to the end, returning the final value.
|
|
20
|
+
* This is used within E, so we must narrow the type to its remote form.
|
|
21
|
+
*/
|
|
22
|
+
export type EUnwrap<T> = (T extends Vow<infer U> ? EUnwrap<U> : T extends PromiseLike<infer U> ? EUnwrap<U> : T);
|
|
23
|
+
/**
|
|
24
|
+
* The first version of the vow implementation
|
|
25
|
+
* object. CAVEAT: These methods must never be changed or added to, to provide
|
|
26
|
+
* forward/backward compatibility. Create a new object and bump its version
|
|
27
|
+
* number instead.
|
|
28
|
+
*/
|
|
29
|
+
export type VowV0<T = any> = {
|
|
30
|
+
/**
|
|
31
|
+
* Attempt to unwrap all vows in this
|
|
32
|
+
* promise chain, returning a promise for the final value. A rejection may
|
|
33
|
+
* indicate a temporary routing failure requiring a retry, otherwise that the
|
|
34
|
+
* decider of the terminal promise rejected it.
|
|
35
|
+
*/
|
|
36
|
+
shorten: () => Promise<T>;
|
|
37
|
+
};
|
|
38
|
+
export type VowPayload<T = any> = {
|
|
39
|
+
vowV0: RemotableObject & Remote<VowV0<T>>;
|
|
40
|
+
};
|
|
41
|
+
export type Vow<T = any> = CopyTagged<"Vow", VowPayload<T>>;
|
|
42
|
+
export type VowKit<T = any> = {
|
|
43
|
+
vow: Vow<T>;
|
|
44
|
+
resolver: VowResolver<T>;
|
|
45
|
+
};
|
|
46
|
+
export type VowResolver<T = any> = {
|
|
47
|
+
resolve(value?: T | PromiseVow<T>): void;
|
|
48
|
+
reject(reason?: any): void;
|
|
49
|
+
};
|
|
50
|
+
export type Watcher<T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]> = {
|
|
51
|
+
onFulfilled?: ((value: T, ...args: C) => Vow<TResult1> | PromiseVow<TResult1> | TResult1) | undefined;
|
|
52
|
+
onRejected?: ((reason: any, ...args: C) => Vow<TResult2> | PromiseVow<TResult2> | TResult2) | undefined;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Converts a vow or promise to a promise, ensuring proper handling of ephemeral promises.
|
|
56
|
+
*/
|
|
57
|
+
export type AsPromiseFunction<T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]> = (specimenP: ERef<T | Vow<T>>, watcher?: Watcher<T, TResult1, TResult2, C> | undefined, watcherArgs?: C | undefined) => Promise<TResult1 | TResult2>;
|
|
58
|
+
export type AnyWatcher = Watcher<any, any, any, any[]>;
|
|
59
|
+
import type { RemotableObject } from '@endo/pass-style';
|
|
60
|
+
import type { Remote } from '@agoric/internal';
|
|
61
|
+
import type { CopyTagged } from '@endo/pass-style';
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.js"],"names":[],"mappings":";;;yCAYW,GAAG,oBACH,GAAG,KAED,GAAG;;;;;uBAKH,CAAC,IACD,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;;;;iBAUnB,CAAC,IACD,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;;;;iBAKlB,CAAC,IACD,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;;;;;oBAMhB,CAAC,IACD,CACZ,CAAK,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvC,CAAK,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAC/C,CAAK,CACF;;;;;;;kBAIU,CAAC;;;;;;;aAMD,MAAM,OAAO,CAAC,CAAC,CAAC;;uBAOhB,CAAC;WAED,eAAe,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;;gBAIlC,CAAC,UACF,WAAW,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;mBAI/B,CAAC,UACF;IACZ,GAAO,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAChB,QAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;CAC1B;wBAIU,CAAC,UACF;IAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;CAAE;oBAIvE,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG;2BAEE,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;2BAChE,GAAG,WAAW,CAAC,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,GAAG,QAAQ;;;;;8BAM5E,CAAC,QACD,QAAQ,MACR,QAAQ,UACA,CAAC,SAAT,GAAG,EAAG,wBAET,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,2FAGd,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;yBAI5B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;qCA7GR,kBAAkB;4BAC3B,kBAAkB;gCAFd,kBAAkB"}
|
package/src/types.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
export {};
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @import {CopyTagged} from '@endo/pass-style'
|
|
6
|
+
* @import {RemotableObject} from '@endo/pass-style';
|
|
7
|
+
* @import {Remote} from '@agoric/internal';
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @callback IsRetryableReason
|
|
12
|
+
* Return truthy if a rejection reason should result in a retry.
|
|
13
|
+
* @param {any} reason
|
|
14
|
+
* @param {any} [priorRetryValue] the previous value returned by this function
|
|
15
|
+
* when deciding whether to retry the same logical operation
|
|
16
|
+
* @returns {any} If falsy, the reason is not retryable. If truthy, the
|
|
17
|
+
* priorRetryValue for the next call.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @template T
|
|
22
|
+
* @typedef {Promise<T | Vow<T>>} PromiseVow Return type of a function that may
|
|
23
|
+
* return a promise or a vow.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} EUnwrapOptions
|
|
28
|
+
* @property {boolean | AnyWatcher | [] | [AnyWatcher, ...args: any[]]} [watch]
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @template T
|
|
33
|
+
* @typedef {T | PromiseLike<T>} ERef
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Eventually a value T or Vow for it.
|
|
38
|
+
* @template T
|
|
39
|
+
* @typedef {ERef<T | Vow<T>>} EVow
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Follow the chain of vow shortening to the end, returning the final value.
|
|
44
|
+
* This is used within E, so we must narrow the type to its remote form.
|
|
45
|
+
* @template T
|
|
46
|
+
* @typedef {(
|
|
47
|
+
* T extends Vow<infer U> ? EUnwrap<U> :
|
|
48
|
+
* T extends PromiseLike<infer U> ? EUnwrap<U> :
|
|
49
|
+
* T
|
|
50
|
+
* )} EUnwrap
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @template [T=any]
|
|
55
|
+
* @typedef {object} VowV0 The first version of the vow implementation
|
|
56
|
+
* object. CAVEAT: These methods must never be changed or added to, to provide
|
|
57
|
+
* forward/backward compatibility. Create a new object and bump its version
|
|
58
|
+
* number instead.
|
|
59
|
+
*
|
|
60
|
+
* @property {() => Promise<T>} shorten Attempt to unwrap all vows in this
|
|
61
|
+
* promise chain, returning a promise for the final value. A rejection may
|
|
62
|
+
* indicate a temporary routing failure requiring a retry, otherwise that the
|
|
63
|
+
* decider of the terminal promise rejected it.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @template [T=any]
|
|
68
|
+
* @typedef {object} VowPayload
|
|
69
|
+
* @property {RemotableObject & Remote<VowV0<T>>} vowV0
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @template [T=any]
|
|
74
|
+
* @typedef {CopyTagged<'Vow', VowPayload<T>>} Vow
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @template [T=any]
|
|
79
|
+
* @typedef {{
|
|
80
|
+
* vow: Vow<T>,
|
|
81
|
+
* resolver: VowResolver<T>,
|
|
82
|
+
* }} VowKit
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @template [T=any]
|
|
87
|
+
* @typedef {{ resolve(value?: T | PromiseVow<T>): void, reject(reason?: any): void }} VowResolver
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @template [T=any]
|
|
92
|
+
* @template [TResult1=T]
|
|
93
|
+
* @template [TResult2=never]
|
|
94
|
+
* @template {any[]} [C=any[]] watcher args
|
|
95
|
+
* @typedef {object} Watcher
|
|
96
|
+
* @property {(value: T, ...args: C) => Vow<TResult1> | PromiseVow<TResult1> | TResult1} [onFulfilled]
|
|
97
|
+
* @property {(reason: any, ...args: C) => Vow<TResult2> | PromiseVow<TResult2> | TResult2} [onRejected]
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Converts a vow or promise to a promise, ensuring proper handling of ephemeral promises.
|
|
102
|
+
*
|
|
103
|
+
* @template [T=any]
|
|
104
|
+
* @template [TResult1=T]
|
|
105
|
+
* @template [TResult2=never]
|
|
106
|
+
* @template {any[]} [C=any[]]
|
|
107
|
+
* @callback AsPromiseFunction
|
|
108
|
+
* @param {ERef<T | Vow<T>>} specimenP
|
|
109
|
+
* @param {Watcher<T, TResult1, TResult2, C>} [watcher]
|
|
110
|
+
* @param {C} [watcherArgs]
|
|
111
|
+
* @returns {Promise<TResult1 | TResult2>}
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @typedef {Watcher<any, any, any, any[]>} AnyWatcher
|
|
116
|
+
*/
|
package/src/vow-e.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function makeVowE(zone: import("@agoric/base-zone").Zone, { when, watch, isRetryableReason }: {
|
|
2
|
+
when: import("./when.js").When;
|
|
3
|
+
watch: import("./watch.js").Watch;
|
|
4
|
+
isRetryableReason: import("./types.js").IsRetryableReason;
|
|
5
|
+
}): (<T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => ECallableOrMethods<RemoteFunctions<T>>) & {
|
|
6
|
+
readonly get: <T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => EGetters<LocalRecord<T>>;
|
|
7
|
+
readonly resolve: <T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => Promise<Awaited<T>>;
|
|
8
|
+
readonly sendOnly: <T>(x: T, opts?: import("./types.js").EUnwrapOptions | undefined) => ESendOnlyCallableOrMethods<RemoteFunctions<T>>;
|
|
9
|
+
readonly when: <T, TResult1 = import("./types.js").EUnwrap<T>, TResult2 = never>(x: import("./E.js").ERef<T>, onfulfilled?: ((value: import("./types.js").EUnwrap<T>) => ERef<TResult1>) | undefined, onrejected?: ((reason: any) => ERef<TResult2>) | undefined, opts?: import("./types.js").EUnwrapOptions | undefined) => Promise<TResult1 | TResult2>;
|
|
10
|
+
} & {
|
|
11
|
+
when: <T, TResult1 = import("./types.js").EUnwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("./types.js").EUnwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
12
|
+
watch: <T = any, TResult1 = T, TResult2 = never, C extends any[] = any[]>(specimenP: import("./types.js").EVow<T>, watcher?: import("./types.js").Watcher<T, TResult1, TResult2, C> | undefined, ...watcherArgs: C) => import("./types.js").Vow<Exclude<TResult1, void> | Exclude<TResult2, void> extends never ? TResult1 : Exclude<TResult1, void> | Exclude<TResult2, void>>;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=vow-e.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vow-e.d.ts","sourceRoot":"","sources":["vow-e.js"],"names":[],"mappings":"AAkBO,+BANI,OAAO,mBAAmB,EAAE,IAAI,sCAExC;IAAyC,IAAI,EAArC,OAAO,WAAW,EAAE,IAAI;IACW,KAAK,EAAxC,OAAO,YAAY,EAAE,KAAK;IACqB,iBAAiB,EAAhE,OAAO,YAAY,EAAE,iBAAiB;CAChD;;;;;;;;EAmFA"}
|