@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.
@@ -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"}