@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/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 */