@agoric/vow 0.1.1-dev-c5284e4.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 +115 -0
- package/package.json +53 -0
- package/src/E.d.ts +156 -0
- package/src/E.d.ts.map +1 -0
- package/src/E.js +448 -0
- package/src/index.d.ts +4 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +6 -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 +9 -0
- package/src/tools.d.ts.map +1 -0
- package/src/tools.js +19 -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 +44 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +78 -0
- package/src/vow-utils.d.ts +4 -0
- package/src/vow-utils.d.ts.map +1 -0
- package/src/vow-utils.js +34 -0
- package/src/vow.d.ts +3 -0
- package/src/vow.d.ts.map +1 -0
- package/src/vow.js +138 -0
- package/src/watch-promise.d.ts +10 -0
- package/src/watch-promise.d.ts.map +1 -0
- package/src/watch-promise.js +61 -0
- package/src/watch.d.ts +3 -0
- package/src/watch.d.ts.map +1 -0
- package/src/watch.js +163 -0
- package/src/when.d.ts +3 -0
- package/src/when.d.ts.map +1 -0
- package/src/when.js +56 -0
package/src/watch.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { getVowPayload, basicE } from './vow-utils.js';
|
|
3
|
+
import { PromiseWatcherI, watchPromiseShim } from './watch-promise.js';
|
|
4
|
+
|
|
5
|
+
const { apply } = Reflect;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {typeof watchPromiseShim} watchPromise
|
|
9
|
+
*/
|
|
10
|
+
const makeWatchNextStep =
|
|
11
|
+
watchPromise =>
|
|
12
|
+
/**
|
|
13
|
+
* If the specimen is a vow, obtain a fresh shortened promise from it,
|
|
14
|
+
* otherwise coerce the non-vow specimen to a promise. Then, associate a
|
|
15
|
+
* (usually durable) watcher object with the promise.
|
|
16
|
+
*
|
|
17
|
+
* @param {any} specimen
|
|
18
|
+
* @param {import('./watch-promise.js').PromiseWatcher} promiseWatcher
|
|
19
|
+
*/
|
|
20
|
+
(specimen, promiseWatcher) => {
|
|
21
|
+
let promise;
|
|
22
|
+
const payload = getVowPayload(specimen);
|
|
23
|
+
if (payload) {
|
|
24
|
+
promise = basicE(payload.vowV0).shorten();
|
|
25
|
+
} else {
|
|
26
|
+
promise = basicE.resolve(specimen);
|
|
27
|
+
}
|
|
28
|
+
watchPromise(promise, promiseWatcher);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {import('./types.js').VowResolver | undefined} resolver
|
|
33
|
+
* @param {import('./types.js').Watcher<unknown, unknown, unknown> | undefined} watcher
|
|
34
|
+
* @param {keyof Required<import('./types.js').Watcher>} wcb
|
|
35
|
+
* @param {unknown} value
|
|
36
|
+
* @param {unknown} [watcherContext]
|
|
37
|
+
*/
|
|
38
|
+
const settle = (resolver, watcher, wcb, value, watcherContext) => {
|
|
39
|
+
try {
|
|
40
|
+
let chainedValue = value;
|
|
41
|
+
const w = watcher && watcher[wcb];
|
|
42
|
+
if (w) {
|
|
43
|
+
chainedValue = apply(w, watcher, [value, watcherContext]);
|
|
44
|
+
} else if (wcb === 'onRejected') {
|
|
45
|
+
throw value;
|
|
46
|
+
}
|
|
47
|
+
resolver && resolver.resolve(chainedValue);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
if (resolver) {
|
|
50
|
+
resolver.reject(e);
|
|
51
|
+
} else {
|
|
52
|
+
// for host's unhandled rejection handler to catch
|
|
53
|
+
throw e;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {import('@agoric/base-zone').Zone} zone
|
|
60
|
+
* @param {(reason: any) => boolean} isRetryableReason
|
|
61
|
+
* @param {ReturnType<typeof makeWatchNextStep>} watchNextStep
|
|
62
|
+
*/
|
|
63
|
+
const preparePromiseWatcher = (zone, isRetryableReason, watchNextStep) =>
|
|
64
|
+
zone.exoClass(
|
|
65
|
+
'PromiseWatcher',
|
|
66
|
+
PromiseWatcherI,
|
|
67
|
+
/**
|
|
68
|
+
* @template [T=any]
|
|
69
|
+
* @template [TResult1=T]
|
|
70
|
+
* @template [TResult2=never]
|
|
71
|
+
* @param {import('./types.js').VowResolver<TResult1 | TResult2>} resolver
|
|
72
|
+
* @param {import('./types.js').Watcher<T, TResult1, TResult2>} [watcher]
|
|
73
|
+
* @param {unknown} [watcherContext]
|
|
74
|
+
*/
|
|
75
|
+
(resolver, watcher, watcherContext) => {
|
|
76
|
+
const state = {
|
|
77
|
+
vow: /** @type {unknown} */ (undefined),
|
|
78
|
+
resolver,
|
|
79
|
+
watcher,
|
|
80
|
+
watcherContext,
|
|
81
|
+
};
|
|
82
|
+
return /** @type {Partial<typeof state>} */ (state);
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
/** @type {Required<import('./watch-promise.js').PromiseWatcher>['onFulfilled']} */
|
|
86
|
+
onFulfilled(value) {
|
|
87
|
+
const { watcher, watcherContext, resolver } = this.state;
|
|
88
|
+
if (getVowPayload(value)) {
|
|
89
|
+
// We've been shortened, so reflect our state accordingly, and go again.
|
|
90
|
+
this.state.vow = value;
|
|
91
|
+
watchNextStep(value, this.self);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this.state.watcher = undefined;
|
|
95
|
+
this.state.resolver = undefined;
|
|
96
|
+
settle(resolver, watcher, 'onFulfilled', value, watcherContext);
|
|
97
|
+
},
|
|
98
|
+
/** @type {Required<import('./watch-promise.js').PromiseWatcher>['onRejected']} */
|
|
99
|
+
onRejected(reason) {
|
|
100
|
+
const { vow, watcher, watcherContext, resolver } = this.state;
|
|
101
|
+
if (vow && isRetryableReason(reason)) {
|
|
102
|
+
watchNextStep(vow, this.self);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.state.resolver = undefined;
|
|
106
|
+
this.state.watcher = undefined;
|
|
107
|
+
settle(resolver, watcher, 'onRejected', reason, watcherContext);
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {import('@agoric/base-zone').Zone} zone
|
|
114
|
+
* @param {() => import('./types.js').VowKit<any>} makeVowKit
|
|
115
|
+
* @param {typeof watchPromiseShim} [watchPromise]
|
|
116
|
+
* @param {(reason: any) => boolean} [isRetryableReason]
|
|
117
|
+
*/
|
|
118
|
+
export const prepareWatch = (
|
|
119
|
+
zone,
|
|
120
|
+
makeVowKit,
|
|
121
|
+
watchPromise = watchPromiseShim,
|
|
122
|
+
isRetryableReason = _reason => false,
|
|
123
|
+
) => {
|
|
124
|
+
const watchNextStep = makeWatchNextStep(watchPromise);
|
|
125
|
+
const makePromiseWatcher = preparePromiseWatcher(
|
|
126
|
+
zone,
|
|
127
|
+
isRetryableReason,
|
|
128
|
+
watchNextStep,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @template [T=any]
|
|
133
|
+
* @template [TResult1=T]
|
|
134
|
+
* @template [TResult2=T]
|
|
135
|
+
* @param {import('./types.js').ERef<T | import('./types.js').Vow<T>>} specimenP
|
|
136
|
+
* @param {import('./types.js').Watcher<T, TResult1, TResult2>} [watcher]
|
|
137
|
+
* @param {unknown} [watcherContext]
|
|
138
|
+
*/
|
|
139
|
+
const watch = (specimenP, watcher, watcherContext) => {
|
|
140
|
+
/** @type {import('./types.js').VowKit<TResult1 | TResult2>} */
|
|
141
|
+
const { resolver, vow } = makeVowKit();
|
|
142
|
+
|
|
143
|
+
// Create a promise watcher to track vows, retrying upon rejection as
|
|
144
|
+
// controlled by `isRetryableReason`.
|
|
145
|
+
const promiseWatcher = makePromiseWatcher(
|
|
146
|
+
resolver,
|
|
147
|
+
watcher,
|
|
148
|
+
watcherContext,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Coerce the specimen to a promise, and start the watcher cycle.
|
|
152
|
+
watchPromise(basicE.resolve(specimenP), promiseWatcher);
|
|
153
|
+
|
|
154
|
+
return vow;
|
|
155
|
+
};
|
|
156
|
+
harden(watch);
|
|
157
|
+
|
|
158
|
+
return watch;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
harden(prepareWatch);
|
|
162
|
+
|
|
163
|
+
/** @typedef {ReturnType<typeof prepareWatch>} Watch */
|
package/src/when.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export function makeWhen(isRetryableReason?: ((reason: any) => boolean) | undefined): <T = any, TResult1 = import("./E.js").Unwrap<T>, TResult2 = never>(specimenP: T, onFulfilled?: ((value: import("./E.js").Unwrap<T>) => TResult1 | PromiseLike<TResult1>) | undefined, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined) => Promise<TResult1 | TResult2>;
|
|
2
|
+
export type When = ReturnType<typeof makeWhen>;
|
|
3
|
+
//# sourceMappingURL=when.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"when.d.ts","sourceRoot":"","sources":["when.js"],"names":[],"mappings":"AAMO,uDAFa,GAAG,KAAK,OAAO,6NAWb,GAAG,oFAoCxB;mBAIa,WAAW,eAAe,CAAC"}
|
package/src/when.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { getVowPayload, basicE } from './vow-utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {(reason: any) => boolean} [isRetryableReason]
|
|
6
|
+
*/
|
|
7
|
+
export const makeWhen = (isRetryableReason = () => false) => {
|
|
8
|
+
/**
|
|
9
|
+
* Shorten `specimenP` until we achieve a final result.
|
|
10
|
+
*
|
|
11
|
+
* @template [T=any]
|
|
12
|
+
* @template [TResult1=import('./E.js').Unwrap<T>]
|
|
13
|
+
* @template [TResult2=never]
|
|
14
|
+
* @param {T} specimenP value to unwrap
|
|
15
|
+
* @param {(value: import('./E.js').Unwrap<T>) => TResult1 | PromiseLike<TResult1>} [onFulfilled]
|
|
16
|
+
* @param {(reason: any) => TResult2 | PromiseLike<TResult2>} [onRejected]
|
|
17
|
+
* @returns {Promise<TResult1 | TResult2>}
|
|
18
|
+
*/
|
|
19
|
+
const when = async (specimenP, onFulfilled, onRejected) => {
|
|
20
|
+
// Ensure we don't run until a subsequent turn.
|
|
21
|
+
await null;
|
|
22
|
+
Promise.prototype.then;
|
|
23
|
+
|
|
24
|
+
// Ensure we have a presence that won't be disconnected later.
|
|
25
|
+
let result = await specimenP;
|
|
26
|
+
let payload = getVowPayload(result);
|
|
27
|
+
while (payload) {
|
|
28
|
+
result = await basicE(payload.vowV0)
|
|
29
|
+
.shorten()
|
|
30
|
+
.catch(e => {
|
|
31
|
+
if (isRetryableReason(e)) {
|
|
32
|
+
// Shorten the same specimen to try again.
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
throw e;
|
|
36
|
+
});
|
|
37
|
+
// Advance to the next vow.
|
|
38
|
+
payload = getVowPayload(result);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const unwrapped = /** @type {import('./E.js').Unwrap<T>} */ (result);
|
|
42
|
+
|
|
43
|
+
// We've extracted the final result.
|
|
44
|
+
if (onFulfilled == null && onRejected == null) {
|
|
45
|
+
return /** @type {TResult1} */ (unwrapped);
|
|
46
|
+
}
|
|
47
|
+
return basicE.resolve(unwrapped).then(onFulfilled, onRejected);
|
|
48
|
+
};
|
|
49
|
+
harden(when);
|
|
50
|
+
|
|
51
|
+
return when;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
harden(makeWhen);
|
|
55
|
+
|
|
56
|
+
/** @typedef {ReturnType<typeof makeWhen>} When */
|