@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/src/E.js ADDED
@@ -0,0 +1,484 @@
1
+ // @ts-check
2
+ /**
3
+ * @file provides a `makeE` that can be parameterized with an `unwrap` function
4
+ * and corresponding `import('./types').EUnwrap<T>`. These will be used to
5
+ * extract the final settlement from a chain of PromiseLikes and PromiseSteps or
6
+ * similar non-thenable pseudo-promises.
7
+ *
8
+ * `@agoric/vow/vat.js` uses this mechanism to export a `V` function with
9
+ * similar behaviour as the default `E`, augmented with automatic unwrapping of
10
+ * recipient Vows as if they were PromiseLikes.
11
+ */
12
+
13
+ /*
14
+ * TODO: Once this implementation has been polished and well-tested, it is
15
+ * designed to be a drop-in replacement for the version in
16
+ * `@endo/eventual-send/src/E.js` which contained no concept of "unwrap",
17
+ */
18
+ import { trackTurns } from './track-turns.js';
19
+ import { makeMessageBreakpointTester } from './message-breakpoints.js';
20
+
21
+ const { details: X, quote: q, Fail } = assert;
22
+ const { assign, create } = Object;
23
+
24
+ const onSend = makeMessageBreakpointTester('ENDO_SEND_BREAKPOINTS');
25
+
26
+ /**
27
+ * @import { HandledPromiseConstructor, RemotableBrand } from '@endo/eventual-send'
28
+ * @import { EUnwrap, EUnwrapOptions } from './types.js'
29
+ */
30
+ /** @typedef {(...args: unknown[]) => any} Callable */
31
+
32
+ /** @type {ProxyHandler<any>} */
33
+ const baseFreezableProxyHandler = {
34
+ set(_target, _prop, _value) {
35
+ return false;
36
+ },
37
+ isExtensible(_target) {
38
+ return false;
39
+ },
40
+ setPrototypeOf(_target, _value) {
41
+ return false;
42
+ },
43
+ deleteProperty(_target, _prop) {
44
+ return false;
45
+ },
46
+ };
47
+
48
+ // E Proxy handlers pretend that any property exists on the target and returns
49
+ // a function for their value. While this function is "bound" by context, it is
50
+ // meant to be called as a method. For that reason, the returned function
51
+ // includes a check that the `this` argument corresponds to the initial
52
+ // receiver when the function was retrieved.
53
+ // E Proxy handlers also forward direct calls to the target in case the remote
54
+ // is a function instead of an object. No such receiver checks are necessary in
55
+ // that case.
56
+
57
+ /**
58
+ * A Proxy handler for E(x).
59
+ *
60
+ * @param {any} recipient Any value passed to E(x)
61
+ * @param {HandledPromiseConstructor} HandledPromise
62
+ * @param {Unwrapper} unwrap
63
+ * @param {EOptions} [opts]
64
+ * @returns {ProxyHandler<any>} the Proxy handler
65
+ */
66
+ const makeEProxyHandler = (recipient, HandledPromise, unwrap, opts) =>
67
+ harden({
68
+ ...baseFreezableProxyHandler,
69
+ get: (_target, propertyKey, receiver) => {
70
+ return harden(
71
+ {
72
+ /**
73
+ * This function purposely checks the `this` value (see above)
74
+ * In order to be `this` sensitive it is defined using concise method
75
+ * syntax rather than as an arrow function. To ensure the function
76
+ * is not constructable, it also avoids the `function` syntax.
77
+ * @param {...any[]} args
78
+ */
79
+ [propertyKey](...args) {
80
+ if (this !== receiver) {
81
+ // Reject the async function call
82
+ return HandledPromise.reject(
83
+ assert.error(
84
+ X`Unexpected receiver for "${q(propertyKey)}" method of E(${q(
85
+ recipient,
86
+ )})`,
87
+ ),
88
+ );
89
+ }
90
+
91
+ if (onSend && onSend.shouldBreakpoint(recipient, propertyKey)) {
92
+ // eslint-disable-next-line no-debugger
93
+ debugger; // LOOK UP THE STACK
94
+ // Stopped at a breakpoint on eventual-send of a method-call message,
95
+ // so that you can walk back on the stack to see how we came to
96
+ // make this eventual-send
97
+ }
98
+ return unwrap(
99
+ HandledPromise.applyMethod(
100
+ unwrap(recipient, 'method recipient'),
101
+ propertyKey,
102
+ args,
103
+ ),
104
+ 'method result',
105
+ opts,
106
+ );
107
+ },
108
+ // @ts-expect-error https://github.com/microsoft/TypeScript/issues/50319
109
+ }[propertyKey],
110
+ );
111
+ },
112
+ apply: (_target, _thisArg, argArray = []) => {
113
+ if (onSend && onSend.shouldBreakpoint(recipient, undefined)) {
114
+ // eslint-disable-next-line no-debugger
115
+ debugger; // LOOK UP THE STACK
116
+ // Stopped at a breakpoint on eventual-send of a function-call message,
117
+ // so that you can walk back on the stack to see how we came to
118
+ // make this eventual-send
119
+ }
120
+ return unwrap(
121
+ HandledPromise.applyFunction(
122
+ unwrap(recipient, 'function recipient'),
123
+ argArray,
124
+ ),
125
+ 'function result',
126
+ );
127
+ },
128
+ has: (_target, _p) => {
129
+ // We just pretend everything exists.
130
+ return true;
131
+ },
132
+ });
133
+
134
+ /**
135
+ * A Proxy handler for E.sendOnly(x)
136
+ * It is a variant on the E(x) Proxy handler.
137
+ *
138
+ * @param {any} recipient Any value passed to E.sendOnly(x)
139
+ * @param {HandledPromiseConstructor} HandledPromise
140
+ * @param {Unwrapper} unwrap
141
+ * @param {EOptions} [opts]
142
+ * @returns {ProxyHandler<any>} the Proxy handler
143
+ */
144
+ const makeESendOnlyProxyHandler = (recipient, HandledPromise, unwrap, opts) =>
145
+ harden({
146
+ ...baseFreezableProxyHandler,
147
+ get: (_target, propertyKey, receiver) => {
148
+ return harden(
149
+ {
150
+ /**
151
+ * This function purposely checks the `this` value (see above)
152
+ * In order to be `this` sensitive it is defined using concise method
153
+ * syntax rather than as an arrow function. To ensure the function
154
+ * is not constructable, it also avoids the `function` syntax.
155
+ * @param {...any[]} args
156
+ */
157
+ [propertyKey](...args) {
158
+ // Throw since the function returns nothing
159
+ this === receiver ||
160
+ Fail`Unexpected receiver for "${q(
161
+ propertyKey,
162
+ )}" method of E.sendOnly(${q(recipient)})`;
163
+ if (onSend && onSend.shouldBreakpoint(recipient, propertyKey)) {
164
+ // eslint-disable-next-line no-debugger
165
+ debugger; // LOOK UP THE STACK
166
+ // Stopped at a breakpoint on eventual-send of a method-call message,
167
+ // so that you can walk back on the stack to see how we came to
168
+ // make this eventual-send
169
+ }
170
+ HandledPromise.applyMethodSendOnly(
171
+ unwrap(recipient, 'send-only method recipient', opts),
172
+ propertyKey,
173
+ args,
174
+ );
175
+ return unwrap(undefined, 'send-only method return', opts);
176
+ },
177
+ // @ts-expect-error https://github.com/microsoft/TypeScript/issues/50319
178
+ }[propertyKey],
179
+ );
180
+ },
181
+ apply: (_target, _thisArg, argsArray = []) => {
182
+ if (onSend && onSend.shouldBreakpoint(recipient, undefined)) {
183
+ // eslint-disable-next-line no-debugger
184
+ debugger; // LOOK UP THE STACK
185
+ // Stopped at a breakpoint on eventual-send of a function-call message,
186
+ // so that you can walk back on the stack to see how we came to
187
+ // make this eventual-send
188
+ }
189
+ HandledPromise.applyFunctionSendOnly(
190
+ unwrap(recipient, 'send-only function recipient'),
191
+ argsArray,
192
+ );
193
+ return undefined;
194
+ },
195
+ has: (_target, _p) => {
196
+ // We just pretend that everything exists.
197
+ return true;
198
+ },
199
+ });
200
+
201
+ /**
202
+ * A Proxy handler for E.get(x)
203
+ * It is a variant on the E(x) Proxy handler.
204
+ *
205
+ * @param {any} x Any value passed to E.get(x)
206
+ * @param {HandledPromiseConstructor} HandledPromise
207
+ * @param {Unwrapper} unwrap
208
+ * @param {EOptions} [opts]
209
+ * @returns {ProxyHandler<any>} the Proxy handler
210
+ */
211
+ const makeEGetProxyHandler = (x, HandledPromise, unwrap, opts) =>
212
+ harden({
213
+ ...baseFreezableProxyHandler,
214
+ has: (_target, _prop) => true,
215
+ get: (_target, prop) =>
216
+ unwrap(
217
+ HandledPromise.get(unwrap(x, 'get target'), prop),
218
+ 'get result',
219
+ opts,
220
+ ),
221
+ });
222
+
223
+ /** @param {any} x */
224
+ const resolve = x => HandledPromise.resolve(x);
225
+
226
+ /**
227
+ * @template [A={}]
228
+ * @param {HandledPromiseConstructor} HandledPromise
229
+ * @param {object} [powers]
230
+ * @param {Unwrapper} [powers.unwrap]
231
+ * @param {A} [powers.additional]
232
+ */
233
+ const makeE = (HandledPromise, powers = {}) => {
234
+ const {
235
+ additional = /** @type {A} */ ({}),
236
+ unwrap = /** @type {NonNullable<typeof powers.unwrap>} */ (resolve),
237
+ } = powers;
238
+
239
+ return harden(
240
+ assign(
241
+ /**
242
+ * E(x) returns a proxy on which you can call arbitrary methods. Each of these
243
+ * method calls returns a promise. The method will be invoked on whatever
244
+ * 'x' designates (or resolves to) in a future turn, not this one.
245
+ *
246
+ * @template T
247
+ * @param {T} x target for method/function call
248
+ * @param {EOptions} [opts]
249
+ * @returns {ECallableOrMethods<RemoteFunctions<T>>} method/function call proxy
250
+ */
251
+ (x, opts) =>
252
+ harden(
253
+ new Proxy(
254
+ () => {},
255
+ makeEProxyHandler(x, HandledPromise, unwrap, opts),
256
+ ),
257
+ ),
258
+ {
259
+ /**
260
+ * E.get(x) returns a proxy on which you can get arbitrary properties.
261
+ * Each of these properties returns a promise for the property. The promise
262
+ * value will be the property fetched from whatever 'x' designates (or
263
+ * resolves to) in a future turn, not this one.
264
+ *
265
+ * @template T
266
+ * @param {T} x target for property get
267
+ * @param {EOptions} [opts]
268
+ * @returns {EGetters<LocalRecord<T>>} property get proxy
269
+ * @readonly
270
+ */
271
+ get: (x, opts) =>
272
+ harden(
273
+ new Proxy(
274
+ create(null),
275
+ makeEGetProxyHandler(x, HandledPromise, unwrap, opts),
276
+ ),
277
+ ),
278
+
279
+ /**
280
+ * E.resolve(x) converts x to a handled promise. It is
281
+ * shorthand for HandledPromise.resolve(x)
282
+ *
283
+ * @template T
284
+ * @param {T} x value to convert to a handled promise
285
+ * @param {EOptions} [opts]
286
+ * @returns {Promise<Awaited<T>>} handled promise for x
287
+ * @readonly
288
+ */
289
+ resolve: (x, opts) => resolve(unwrap(x, 'resolve', opts)),
290
+
291
+ /**
292
+ * E.sendOnly returns a proxy similar to E, but for which the results
293
+ * are ignored (undefined is returned).
294
+ *
295
+ * @template T
296
+ * @param {T} x target for method/function call
297
+ * @param {EOptions} [opts]
298
+ * @returns {ESendOnlyCallableOrMethods<RemoteFunctions<T>>} method/function call proxy
299
+ * @readonly
300
+ */
301
+ sendOnly: (x, opts) =>
302
+ harden(
303
+ new Proxy(
304
+ () => {},
305
+ makeESendOnlyProxyHandler(x, HandledPromise, unwrap, opts),
306
+ ),
307
+ ),
308
+
309
+ /**
310
+ * E.when(x, res, rej) is equivalent to
311
+ * unwrap(x).then(onfulfilled, onrejected)
312
+ *
313
+ * @template T
314
+ * @template [TResult1=EUnwrap<T>]
315
+ * @template [TResult2=never]
316
+ * @param {ERef<T>} x value to convert to a handled promise
317
+ * @param {(value: EUnwrap<T>) => ERef<TResult1>} [onfulfilled]
318
+ * @param {(reason: any) => ERef<TResult2>} [onrejected]
319
+ * @param {EOptions} [opts]
320
+ * @returns {Promise<TResult1 | TResult2>}
321
+ * @readonly
322
+ */
323
+ when: (x, onfulfilled, onrejected, opts) => {
324
+ const unwrapped = resolve(unwrap(x, 'E.when', opts));
325
+ if (onfulfilled == null && onrejected == null) {
326
+ return unwrapped;
327
+ }
328
+ return unwrapped.then(
329
+ ...trackTurns(/** @type {const} */ ([onfulfilled, onrejected])),
330
+ );
331
+ },
332
+ },
333
+ additional,
334
+ ),
335
+ );
336
+ };
337
+
338
+ export default makeE;
339
+
340
+ /** @typedef {ReturnType<makeE>} EProxy */
341
+
342
+ /**
343
+ * `DataOnly<T>` means to return a record type `T2` consisting only of
344
+ * properties that are *not* functions.
345
+ *
346
+ * @template T The type to be filtered.
347
+ * @typedef {Omit<T, FilteredKeys<T, Callable>>} DataOnly
348
+ */
349
+
350
+ /**
351
+ * @see {@link https://github.com/microsoft/TypeScript/issues/31394}
352
+ * @template T
353
+ * @typedef {PromiseLike<T> | T} ERef
354
+ */
355
+
356
+ /**
357
+ * @template {Callable} T
358
+ * @typedef {ReturnType<T> extends EUnwrap<ReturnType<T>>
359
+ * ? (...args: Parameters<T>) => Promise<ReturnType<T>>
360
+ * : ReturnType<T> extends Promise<EUnwrap<ReturnType<T>>>
361
+ * ? T
362
+ * : (...args: Parameters<T>) => Promise<EUnwrap<ReturnType<T>>>} ECallable
363
+ */
364
+
365
+ /**
366
+ * @template T
367
+ * @typedef {{
368
+ * readonly [P in keyof T]: T[P] extends Callable
369
+ * ? ECallable<T[P]>
370
+ * : never;
371
+ * }} EMethods
372
+ */
373
+
374
+ /**
375
+ * @template T
376
+ * @typedef {{
377
+ * readonly [P in keyof T]: T[P] extends PromiseLike<any>
378
+ * ? T[P]
379
+ * : Promise<Awaited<T[P]>>;
380
+ * }} EGetters
381
+ */
382
+
383
+ /**
384
+ * @template {Callable} T
385
+ * @typedef {(...args: Parameters<T>) => Promise<void>} ESendOnlyCallable
386
+ */
387
+
388
+ /**
389
+ * @template T
390
+ * @typedef {{
391
+ * readonly [P in keyof T]: T[P] extends Callable
392
+ * ? ESendOnlyCallable<T[P]>
393
+ * : never;
394
+ * }} ESendOnlyMethods
395
+ */
396
+
397
+ /**
398
+ * @template T
399
+ * @typedef {(
400
+ * T extends Callable
401
+ * ? ESendOnlyCallable<T> & ESendOnlyMethods<Required<T>>
402
+ * : ESendOnlyMethods<Required<T>>
403
+ * )} ESendOnlyCallableOrMethods
404
+ */
405
+
406
+ /**
407
+ * @template T
408
+ * @typedef {(
409
+ * T extends Callable
410
+ * ? ECallable<T> & EMethods<Required<T>>
411
+ * : EMethods<Required<T>>
412
+ * )} ECallableOrMethods
413
+ */
414
+
415
+ /**
416
+ * Return a union of property names/symbols/numbers P for which the record element T[P]'s type extends U.
417
+ *
418
+ * Given const x = { a: 123, b: 'hello', c: 42, 49: () => {}, 53: 67 },
419
+ *
420
+ * FilteredKeys<typeof x, number> is the type 'a' | 'c' | 53.
421
+ * FilteredKeys<typeof x, string> is the type 'b'.
422
+ * FilteredKeys<typeof x, 42 | 67> is the type 'c' | 53.
423
+ * FilteredKeys<typeof x, boolean> is the type never.
424
+ *
425
+ * @template T
426
+ * @template U
427
+ * @typedef {{ [P in keyof T]: T[P] extends U ? P : never; }[keyof T]} FilteredKeys
428
+ */
429
+
430
+ /**
431
+ * `PickCallable<T>` means to return a single root callable or a record type
432
+ * consisting only of properties that are functions.
433
+ *
434
+ * @template T
435
+ * @typedef {(
436
+ * T extends Callable
437
+ * ? (...args: Parameters<T>) => ReturnType<T> // a root callable, no methods
438
+ * : FilteredKeys<T, Callable> extends never
439
+ * ? any
440
+ * : Pick<T, FilteredKeys<T, Callable>> // any callable methods
441
+ * )} PickCallable
442
+ */
443
+
444
+ /**
445
+ * `RemoteFunctions<T>` means to return the functions and properties that are remotely callable.
446
+ *
447
+ * @template T
448
+ * @typedef {(
449
+ * EUnwrap<T> extends RemotableBrand<any, infer R> // if a given T will settle to some remote interface R
450
+ * ? PickCallable<R> // then return the callable properties of R
451
+ * : PickCallable<EUnwrap<T>> // otherwise return the callable properties of the settled T
452
+ * )} RemoteFunctions
453
+ */
454
+
455
+ /**
456
+ * @template T
457
+ * @typedef {(
458
+ * T extends RemotableBrand<infer L, any>
459
+ * ? L
460
+ * : EUnwrap<T>
461
+ * )} LocalRecord
462
+ */
463
+
464
+ /**
465
+ * Type for an object that must only be invoked with E. It supports a given
466
+ * interface but declares all the functions as asyncable.
467
+ *
468
+ * @template T
469
+ * @typedef {(
470
+ * T extends Callable
471
+ * ? (...args: Parameters<T>) => ERef<Awaited<EOnly<ReturnType<T>>>>
472
+ * : T extends Record<PropertyKey, Callable>
473
+ * ? {
474
+ * [K in keyof T]: T[K] extends Callable
475
+ * ? (...args: Parameters<T[K]>) => ERef<Awaited<EOnly<ReturnType<T[K]>>>>
476
+ * : T[K];
477
+ * }
478
+ * : T
479
+ * )} EOnly
480
+ */
481
+
482
+ /** @typedef {<T>(x: T, retryContext?: string, opts?: EOptions) => Promise<EUnwrap<T>>} Unwrapper */
483
+
484
+ /** @typedef {EUnwrapOptions} EOptions */
package/src/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./tools.js";
2
+ export * from "./types.js";
3
+ export * from "@agoric/internal/src/types.js";
4
+ export { default as makeE } from "./E.js";
5
+ export { ReactionGuard, VowShape, EVow$, OrVow$, toPassableCap } from "./vow-utils.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":""}
package/src/index.js ADDED
@@ -0,0 +1,16 @@
1
+ // @ts-check
2
+ export * from './tools.js';
3
+ export { default as makeE } from './E.js';
4
+ export {
5
+ ReactionGuard,
6
+ VowShape,
7
+ EVow$,
8
+ OrVow$,
9
+ toPassableCap,
10
+ } from './vow-utils.js';
11
+
12
+ // eslint-disable-next-line import/export
13
+ export * from './types.js';
14
+
15
+ // XXX re-exporting the Remote type for back-compat
16
+ export * from '@agoric/internal/src/types.js';
@@ -0,0 +1,44 @@
1
+ export function makeMessageBreakpointTester(optionName: string): MessageBreakpointTester | undefined;
2
+ /**
3
+ * A star `'*'` matches any recipient. Otherwise, the string is
4
+ * matched against the value of a recipient's `@@toStringTag`
5
+ * after stripping out any leading `'Alleged: '` or `'DebugName: '`
6
+ * prefix. For objects defined with `Far` this is the first argument,
7
+ * known as the `farName`. For exos, this is the tag.
8
+ */
9
+ export type MatchStringTag = string | "*";
10
+ /**
11
+ * A star `'*'` matches any method name. Otherwise, the string is
12
+ * matched against the method name. Currently, this is only an exact match.
13
+ * However, beware that we may introduce a string syntax for
14
+ * symbol method names.
15
+ */
16
+ export type MatchMethodName = string | "*";
17
+ /**
18
+ * A star `'*'` will always breakpoint. Otherwise, the string
19
+ * must be a non-negative integer. Once that is zero, always breakpoint.
20
+ * Otherwise decrement by one each time it matches until it reaches zero.
21
+ * In other words, the countdown represents the number of
22
+ * breakpoint occurrences to skip before actually breakpointing.
23
+ */
24
+ export type MatchCountdown = number | "*";
25
+ /**
26
+ * This is the external JSON representation, in which
27
+ * - the outer property name is the class-like tag or '*',
28
+ * - the inner property name is the method name or '*',
29
+ * - the value is a non-negative integer countdown or '*'.
30
+ */
31
+ export type MessageBreakpoints = Record<MatchStringTag, Record<MatchMethodName, MatchCountdown>>;
32
+ /**
33
+ * This is the internal JSON representation, in which
34
+ * - the outer property name is the method name or '*',
35
+ * - the inner property name is the class-like tag or '*',
36
+ * - the value is a non-negative integer countdown or '*'.
37
+ */
38
+ export type BreakpointTable = Record<MatchMethodName, Record<MatchStringTag, MatchCountdown>>;
39
+ export type MessageBreakpointTester = {
40
+ getBreakpoints: () => MessageBreakpoints;
41
+ setBreakpoints: (newBreakpoints?: MessageBreakpoints) => void;
42
+ shouldBreakpoint: (recipient: object, methodName: string | symbol | undefined) => boolean;
43
+ };
44
+ //# sourceMappingURL=message-breakpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-breakpoints.d.ts","sourceRoot":"","sources":["message-breakpoints.js"],"names":[],"mappings":"AA0FO,wDAHI,MAAM,GACJ,uBAAuB,GAAG,SAAS,CA0F/C;;;;;;;;6BA1KY,MAAM,GAAG,GAAG;;;;;;;8BAQZ,MAAM,GAAG,GAAG;;;;;;;;6BAOZ,MAAM,GAAG,GAAG;;;;;;;iCAcZ,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;;;;;;;8BAS/D,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;;oBAK9D,MAAM,kBAAkB;oBACxB,CAAC,cAAc,CAAC,EAAE,kBAAkB,KAAK,IAAI;sBAC7C,CACb,SAAa,EAAE,MAAM,EACrB,UAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,KACpC,OAAO"}