@endo/eventual-send 0.14.7 → 0.15.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,48 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ### [0.15.1](https://github.com/endojs/endo/compare/@endo/eventual-send@0.15.0...@endo/eventual-send@0.15.1) (2022-04-13)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Revert dud release ([c8a7101](https://github.com/endojs/endo/commit/c8a71017d8d7af10a97909c9da9c5c7e59aed939))
12
+
13
+
14
+
15
+ ## [0.15.0](https://github.com/endojs/endo/compare/@endo/eventual-send@0.14.8...@endo/eventual-send@0.15.0) (2022-04-12)
16
+
17
+
18
+ ### ⚠ BREAKING CHANGES
19
+
20
+ * **far:** rename `Remote` to `FarRef`
21
+
22
+ ### Features
23
+
24
+ * **far:** rename `Remote` to `FarRef` ([7bde2bf](https://github.com/endojs/endo/commit/7bde2bf28e88935606564cebd1b8d284cd70e4ef))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **eventual-send:** evolve types based on marshal requirements ([ff388fa](https://github.com/endojs/endo/commit/ff388fa2f81446c1ae02618b78771dc17ce5c74b))
30
+ * **eventual-send:** unwrap promises more fully ([6ba799f](https://github.com/endojs/endo/commit/6ba799f77e8d55530ecd7617c3ccad22324bade2))
31
+
32
+
33
+
34
+ ### [0.14.8](https://github.com/endojs/endo/compare/@endo/eventual-send@0.14.7...@endo/eventual-send@0.14.8) (2022-03-07)
35
+
36
+
37
+ ### Features
38
+
39
+ * **eventual-send:** provide typing for `Remote<Primary, Local>` ([4d28509](https://github.com/endojs/endo/commit/4d285095a6ea1a78f1a3a4696bc822f5e4dfd43f))
40
+
41
+
42
+ ### Bug Fixes
43
+
44
+ * **eventual-send:** properly declare `E` to be type `EProxy` ([3bdfdf7](https://github.com/endojs/endo/commit/3bdfdf77440f9ddea9bac1e783aaf015e9bcfa62))
45
+
46
+
47
+
6
48
  ### [0.14.7](https://github.com/endojs/endo/compare/@endo/eventual-send@0.14.6...@endo/eventual-send@0.14.7) (2022-03-02)
7
49
 
8
50
  **Note:** Version bump only for package @endo/eventual-send
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@endo/eventual-send",
3
- "version": "0.14.7",
3
+ "version": "0.15.1",
4
4
  "description": "Extend a Promise class to implement the eventual-send API",
5
5
  "type": "module",
6
6
  "main": "src/no-shim.js",
@@ -13,7 +13,7 @@
13
13
  "lint-fix": "yarn lint:eslint --fix && yarn lint:types",
14
14
  "lint-check": "yarn lint",
15
15
  "lint": "yarn lint:types && yarn lint:eslint",
16
- "lint:types": "tsc -p jsconfig.json",
16
+ "lint:types": "tsc",
17
17
  "lint:eslint": "eslint '**/*.js'"
18
18
  },
19
19
  "repository": {
@@ -27,10 +27,11 @@
27
27
  },
28
28
  "homepage": "https://github.com/endojs/endo#readme",
29
29
  "devDependencies": {
30
- "@endo/lockdown": "^0.1.8",
31
- "@endo/ses-ava": "^0.2.20",
30
+ "@endo/lockdown": "^0.1.11",
31
+ "@endo/ses-ava": "^0.2.23",
32
32
  "ava": "^3.12.1",
33
- "c8": "^7.7.3"
33
+ "c8": "^7.7.3",
34
+ "tsd": "^0.19.1"
34
35
  },
35
36
  "keywords": [
36
37
  "eventual send",
@@ -61,5 +62,5 @@
61
62
  ],
62
63
  "timeout": "2m"
63
64
  },
64
- "gitHead": "08973d4fc6358a58d733251b051b2812bb4c651a"
65
+ "gitHead": "ada5203fd64e6828dca74895fd74588e08925d97"
65
66
  }
package/src/E.js CHANGED
@@ -23,7 +23,7 @@ const baseFreezableProxyHandler = {
23
23
  * A Proxy handler for E(x).
24
24
  *
25
25
  * @param {*} x Any value passed to E(x)
26
- * @param {*} HandledPromise
26
+ * @param {import('./index').HandledPromiseConstructor} HandledPromise
27
27
  * @returns {ProxyHandler} the Proxy handler
28
28
  */
29
29
  function EProxyHandler(x, HandledPromise) {
@@ -47,7 +47,7 @@ function EProxyHandler(x, HandledPromise) {
47
47
  * It is a variant on the E(x) Proxy handler.
48
48
  *
49
49
  * @param {*} x Any value passed to E.sendOnly(x)
50
- * @param {*} HandledPromise
50
+ * @param {import('./index').HandledPromiseConstructor} HandledPromise
51
51
  * @returns {ProxyHandler} the Proxy handler
52
52
  */
53
53
  function EsendOnlyProxyHandler(x, HandledPromise) {
@@ -70,6 +70,10 @@ function EsendOnlyProxyHandler(x, HandledPromise) {
70
70
  });
71
71
  }
72
72
 
73
+ /**
74
+ * @param {import('./index').HandledPromiseConstructor} HandledPromise
75
+ * @returns {import('./index').EProxy}
76
+ */
73
77
  export default function makeE(HandledPromise) {
74
78
  function E(x) {
75
79
  const handler = EProxyHandler(x, HandledPromise);
@@ -94,7 +98,7 @@ export default function makeE(HandledPromise) {
94
98
  return harden(new Proxy(() => {}, handler));
95
99
  };
96
100
 
97
- E.when = (x, onfulfilled = undefined, onrejected = undefined) => {
101
+ E.when = (x, onfulfilled, onrejected) => {
98
102
  const [onsuccess, onfailure] = trackTurns([onfulfilled, onrejected]);
99
103
  return HandledPromise.resolve(x).then(onsuccess, onfailure);
100
104
  };
@@ -393,7 +393,7 @@ export const makeHandledPromise = () => {
393
393
  );
394
394
  };
395
395
 
396
- /** @type {import('.').HandledPromiseStaticMethods} */
396
+ /** @type {import('.').HandledPromiseStaticMethods & Pick<PromiseConstructor, 'resolve'>} */
397
397
  const staticMethods = {
398
398
  get(target, prop) {
399
399
  prop = coerceToObjectProperty(prop);
package/src/index.d.ts CHANGED
@@ -1,16 +1,41 @@
1
- /* eslint-disable no-shadow,no-use-before-define,no-var,vars-on-top */
2
1
  // Type definitions for eventual-send
3
- // TODO: Add jsdocs.
4
2
 
5
- type Property = string | number | symbol;
3
+ /**
4
+ * @file Type definitions for @agoric/eventual-send
5
+ *
6
+ * Some useful background knowledge:
7
+ *
8
+ * `Omit<T, U>` means to return a record type `T2` which has none of the properties whose keys are part of `U`.
9
+ * `Omit<{a: 1, b: 2, c: 3}, 'b'>` is the type `{a: 1, c: 3}`.
10
+ *
11
+ * `Pick<T, U>` means to return a record type `T2` which has only the properties whose keys are part of `U`.
12
+ * `Pick<{a: 1, b: 2, c: 3}, 'b'>` is the type `{b: 2}`.
13
+ *
14
+ * `PromiseLike<T>` is a thenable which resolves to `T`.
15
+ *
16
+ * `Promise<PromiseLike<T>>` doesn't handle recursion and is distinct from `T`.
17
+ *
18
+ * `Unpromise<PromiseLike<T>>` strips off just one layer and is just `T`. `Unpromise<PromiseLike<PromiseLIke<T>>` is `PromiseLike<T>`.
19
+ *
20
+ * `Awaited<PromiseLike<T>>` recurses, and is just `T`.
21
+ * `Awaited<PromiseLike<PromiseLike<T>>>` is just `T` as well.
22
+ *
23
+ * @see {@link https://www.typescriptlang.org/docs/handbook/2/generics.html#handbook-content}
24
+ * @see {@link https://www.typescriptlang.org/docs/handbook/2/conditional-types.html}
25
+ */
6
26
 
7
- type ERef<T> = PromiseLike<T> | T;
27
+ export type Callable = (...args: any[]) => any;
28
+
29
+ // Same as https://github.com/microsoft/TypeScript/issues/31394
30
+ export type ERef<T> = PromiseLike<T> | T;
31
+
32
+ export declare const EmptyObj: {};
8
33
 
9
34
  // Type for an object that must only be invoked with E. It supports a given
10
35
  // interface but declares all the functions as asyncable.
11
36
  export type EOnly<T> = T extends (...args: infer P) => infer R
12
- ? (...args: P) => ERef<R> | EOnly<R>
13
- : T extends Record<string | number | symbol, Function>
37
+ ? (...args: P) => ERef<Awaited<R>> | EOnly<Awaited<R>>
38
+ : T extends Record<PropertyKey, Callable>
14
39
  ? ERef<
15
40
  {
16
41
  [K in keyof T]: EOnly<T[K]>;
@@ -18,30 +43,99 @@ export type EOnly<T> = T extends (...args: infer P) => infer R
18
43
  >
19
44
  : ERef<T>;
20
45
 
21
- type Unpromise<T> = T extends ERef<infer U> ? U : T;
46
+ /**
47
+ * Return a union of property names/symbols/numbers P for which the record element T[P]'s type extends U.
48
+ *
49
+ * Given const x = { a: 123, b: 'hello', c: 42, 49: () => {}, 53: 67 },
50
+ *
51
+ * FilteredKeys<typeof x, number> is the type 'a' | 'c' | 53.
52
+ * FilteredKeys<typeof x, string> is the type 'b'.
53
+ * FilteredKeys<typeof x, 42 | 67> is the type 'c' | 53.
54
+ * FilteredKeys<typeof x, boolean> is the type never.
55
+ */
56
+ export type FilteredKeys<T, U> = {
57
+ [P in keyof T]: T[P] extends U ? P : never;
58
+ }[keyof T];
59
+
60
+ /**
61
+ * `DataOnly<T>` means to return a record type `T2` consisting only of properties that are *not* functions.
62
+ */
63
+ export type DataOnly<T> = Omit<T, FilteredKeys<T, Callable>>;
22
64
 
23
- type Parameters<T> = T extends (...args: infer T) => any ? T : any;
24
- type ReturnType<T> = T extends (...args: any[]) => infer T ? T : any;
65
+ // Nominal type to carry the local and remote interfaces of a Remotable.
66
+ export declare class RemotableBrand<L, R> {
67
+ // The local properties of the object.
68
+ private localProperties: L;
69
+
70
+ // The type of all the remotely-callable functions.
71
+ private remoteCallable: R;
72
+ }
25
73
 
26
- interface EHandler<T> {
27
- get?: (p: T, name: Property, returnedP?: Promise<unknown>) => any;
28
- getSendOnly?: (p: T, name: Property) => void;
29
- applyFunction?: (p: T, args: unknown[], returnedP?: Promise<unknown>) => any;
74
+ /**
75
+ * Creates a type that accepts both near and marshalled references that were
76
+ * returned from `Remotable` or `Far`, and also promises for such references.
77
+ */
78
+ export type FarRef<Primary, Local = DataOnly<Primary>> = ERef<
79
+ Local & RemotableBrand<Local, Primary>
80
+ >;
81
+
82
+ /**
83
+ * `PickCallable<T>` means to return a single root callable or a record type
84
+ * consisting only of properties that are functions.
85
+ */
86
+ export type PickCallable<T> = T extends Callable
87
+ ? (...args: Parameters<T>) => ReturnType<T> // a root callable, no methods
88
+ : Pick<T, FilteredKeys<T, Callable>>; // any callable methods
89
+
90
+ /**
91
+ * `RemoteFunctions<T>` means to return the functions and properties that are remotely callable.
92
+ */
93
+ export type RemoteFunctions<T> = T extends RemotableBrand<infer L, infer R> // if a given T is some remote interface R
94
+ ? PickCallable<R> // then use the function properties of R
95
+ : Awaited<T> extends RemotableBrand<infer L, infer R> // otherwise, if the final resolution of T is some remote interface R
96
+ ? PickCallable<R> // then use the function properties of R
97
+ : T extends PromiseLike<infer U>
98
+ ? Awaited<T> // otherwise, use the final resolution of that T
99
+ : T;
100
+
101
+ export type LocalRecord<T> = T extends RemotableBrand<infer L, infer R>
102
+ ? L
103
+ : Awaited<T> extends RemotableBrand<infer L, infer R>
104
+ ? L
105
+ : T extends PromiseLike<infer U>
106
+ ? Awaited<T>
107
+ : T;
108
+ export interface EHandler<T> {
109
+ get?: (p: T, name: PropertyKey, returnedP?: Promise<unknown>) => unknown;
110
+ getSendOnly?: (p: T, name: PropertyKey) => void;
111
+ applyFunction?: (
112
+ p: T,
113
+ args: unknown[],
114
+ returnedP?: Promise<unknown>,
115
+ ) => unknown;
30
116
  applyFunctionSendOnly?: (p: T, args: unknown[]) => void;
31
117
  applyMethod?: (
32
118
  p: T,
33
- name: Property | undefined,
119
+ name: PropertyKey | undefined,
34
120
  args: unknown[],
35
121
  returnedP?: Promise<unknown>,
36
- ) => any;
122
+ ) => unknown;
37
123
  applyMethodSendOnly?: (
38
124
  p: T,
39
- name: Property | undefined,
125
+ name: PropertyKey | undefined,
40
126
  args: unknown[],
41
127
  ) => void;
42
128
  }
43
129
 
44
- type HandledExecutor<R> = (
130
+ export type ResolveWithPresenceOptionsBag<T extends Object> = {
131
+ proxy?: {
132
+ handler: ProxyHandler<T>;
133
+ target: unknown;
134
+ revokerCallback?: (revoker: () => void) => void;
135
+ };
136
+ };
137
+
138
+ export type HandledExecutor<R> = (
45
139
  resolveHandled: (value?: R) => void,
46
140
  rejectHandled: (reason?: unknown) => void,
47
141
  resolveWithPresence: (
@@ -50,30 +144,24 @@ type HandledExecutor<R> = (
50
144
  ) => object,
51
145
  ) => void;
52
146
 
53
- type ResolveWithPresenceOptionsBag<T extends Object> = {
54
- proxy?: {
55
- handler: ProxyHandler<T>;
56
- target: unknown;
57
- revokerCallback?: (revoker: () => void) => void;
58
- };
59
- };
60
-
61
147
  declare interface HandledPromiseStaticMethods {
62
- resolve<T>(x: T): Promise<Unpromise<T>>;
63
- resolve(): Promise<undefined>;
64
148
  applyFunction(target: unknown, args: unknown[]): Promise<unknown>;
65
149
  applyFunctionSendOnly(target: unknown, args: unknown[]): void;
66
150
  applyMethod(
67
151
  target: unknown,
68
- prop: Property | undefined,
152
+ prop: PropertyKey | undefined,
69
153
  args: unknown[],
70
154
  ): Promise<unknown>;
71
- applyMethodSendOnly(target: unknown, prop: Property, args: unknown[]): void;
72
- get(target: unknown, prop: Property): Promise<unknown>;
73
- getSendOnly(target: unknown, prop: Property): void;
155
+ applyMethodSendOnly(
156
+ target: unknown,
157
+ prop: PropertyKey,
158
+ args: unknown[],
159
+ ): void;
160
+ get(target: unknown, prop: PropertyKey): Promise<unknown>;
161
+ getSendOnly(target: unknown, prop: PropertyKey): void;
74
162
  }
75
163
 
76
- declare interface HandledPromiseConstructor
164
+ export interface HandledPromiseConstructor
77
165
  extends PromiseConstructor,
78
166
  HandledPromiseStaticMethods {
79
167
  new <R>(
@@ -83,43 +171,59 @@ declare interface HandledPromiseConstructor
83
171
  prototype: Promise<unknown>;
84
172
  }
85
173
 
86
- declare var HandledPromise: HandledPromiseConstructor;
87
-
88
174
  declare namespace global {
175
+ // eslint-disable-next-line vars-on-top,no-var
89
176
  var HandledPromise: HandledPromiseConstructor;
90
177
  }
91
178
 
92
- declare function makeHandledPromise(): HandledPromiseConstructor;
179
+ export declare const HandledPromise: HandledPromiseConstructor;
180
+
181
+ /**
182
+ * "E" short for "Eventual", what we call something that has to return a promise.
183
+ */
184
+ type ECallable<T extends Callable> = ReturnType<T> extends PromiseLike<infer U>
185
+ ? T // function already returns a promise
186
+ : (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>; // make it return a promise
93
187
 
94
188
  /* Types for E proxy calls. */
95
- type ESingleMethod<T> = {
96
- readonly [P in keyof T]: (
97
- ...args: Parameters<T[P]>
98
- ) => Promise<Unpromise<ReturnType<T[P]>>>;
189
+
190
+ /**
191
+ * Transform each function in T to return a promise
192
+ */
193
+ type EMethods<T> = {
194
+ readonly [P in keyof T]: T[P] extends Callable ? ECallable<T[P]> : never;
99
195
  };
100
- type ESingleCall<T> = T extends Function
101
- ? ((...args: Parameters<T>) => Promise<Unpromise<ReturnType<T>>>) &
102
- ESingleMethod<Required<T>>
103
- : ESingleMethod<Required<T>>;
104
- type ESingleGet<T> = {
105
- readonly [P in keyof T]: Promise<Unpromise<T[P]>>;
196
+
197
+ type ECallableOrMethods<T> = T extends Callable
198
+ ? ECallable<T> & EMethods<Required<T>>
199
+ : EMethods<Required<T>>;
200
+
201
+ type EGetters<T> = {
202
+ readonly [P in keyof T]: T[P] extends PromiseLike<infer U>
203
+ ? T[P]
204
+ : Promise<Awaited<T[P]>>;
106
205
  };
107
206
 
108
207
  /* Same types for send-only. */
109
- type ESingleMethodOnly<T> = {
110
- readonly [P in keyof T]: (...args: Parameters<T[P]>) => void;
111
- };
112
- type ESingleCallOnly<T> = T extends Function
113
- ? ((...args: Parameters<T>) => void) & ESingleMethodOnly<T>
114
- : ESingleMethodOnly<T>;
115
- type ESingleGetOnly<T> = {
116
- readonly [P in keyof T]: void;
208
+ type ESendOnlyCallable<T extends Callable> = (
209
+ ...args: Parameters<T>
210
+ ) => Promise<void>;
211
+
212
+ type ESendOnlyMethods<T> = {
213
+ readonly [P in keyof T]: T[P] extends Callable
214
+ ? ESendOnlyCallable<T[P]>
215
+ : never;
117
216
  };
118
217
 
218
+ type ESendOnlyCallableOrMethods<T> = T extends Callable
219
+ ? ESendOnlyCallable<T> & ESendOnlyMethods<Required<T>>
220
+ : ESendOnlyMethods<Required<T>>;
221
+
119
222
  interface ESendOnly {
120
- <T>(x: T): ESingleCallOnly<Unpromise<T>>;
223
+ <T>(x: T): ESendOnlyCallableOrMethods<RemoteFunctions<T>>;
121
224
  }
122
225
 
226
+ // Generic on the proxy target {T}
123
227
  interface EProxy {
124
228
  /**
125
229
  * E(x) returns a proxy on which you can call arbitrary methods. Each of
@@ -127,10 +231,10 @@ interface EProxy {
127
231
  * whatever 'x' designates (or resolves to) in a future turn, not this
128
232
  * one.
129
233
  *
130
- * @param {*} x target for method/function call
131
- * @returns {ESingleCall} method/function call proxy
234
+ * @param x target for method/function call
235
+ * @returns method/function call proxy
132
236
  */
133
- <T>(x: T): ESingleCall<Unpromise<T>>;
237
+ <T>(x: T): ECallableOrMethods<RemoteFunctions<T>>;
134
238
 
135
239
  /**
136
240
  * E.get(x) returns a proxy on which you can get arbitrary properties.
@@ -138,16 +242,16 @@ interface EProxy {
138
242
  * value will be the property fetched from whatever 'x' designates (or
139
243
  * resolves to) in a future turn, not this one.
140
244
  *
141
- * @param {*} x target for property get
142
- * @returns {ESingleGet} property get proxy
245
+ * @param x target for property get
246
+ * @returns property get proxy
143
247
  */
144
- readonly get: <T>(x: T) => ESingleGet<Unpromise<T>>;
248
+ readonly get: <T>(x: T) => EGetters<LocalRecord<T>>;
145
249
 
146
250
  /**
147
251
  * E.resolve(x) converts x to a handled promise. It is
148
252
  * shorthand for HandledPromise.resolve(x)
149
253
  */
150
- readonly resolve: <T>(x: T) => Promise<Unpromise<T>>;
254
+ readonly resolve: <T>(x: T) => Promise<Awaited<T>>;
151
255
 
152
256
  /**
153
257
  * E.when(x, res, rej) is equivalent to
@@ -155,7 +259,7 @@ interface EProxy {
155
259
  */
156
260
  readonly when: <T, U>(
157
261
  x: T,
158
- onfulfilled?: (value: Unpromise<T>) => ERef<U>,
262
+ onfulfilled?: (value: Awaited<T>) => ERef<U>,
159
263
  onrejected?: (reason: any) => ERef<U>,
160
264
  ) => Promise<U>;
161
265
 
@@ -0,0 +1,48 @@
1
+ /* eslint-disable @endo/no-polymorphic-call, import/no-extraneous-dependencies, no-restricted-globals, prettier/prettier */
2
+ import { expectType } from 'tsd';
3
+ import { E } from '../test/get-hp.js';
4
+ import { DataOnly, ERef } from './index.js';
5
+
6
+ type FarRef<
7
+ Primary,
8
+ Local = DataOnly<Primary>
9
+ > = import('@endo/eventual-send').FarRef<Primary, Local>;
10
+
11
+ // Check the legacy ERef type
12
+ const foo = async (a: ERef<{ bar(): string; baz: number }>) => {
13
+ const { baz } = await a;
14
+
15
+ expectType<Promise<string>>(E(a).bar());
16
+
17
+ // Should be type error, but isn't.
18
+ (await a).bar();
19
+
20
+ expectType<Promise<number>>(E.get(a).baz);
21
+
22
+ // Should be type error, but isn't.
23
+ expectType<Promise<() => string>>(E.get(a).bar);
24
+
25
+ // @ts-expect-error - calling a directly is not typed, but works.
26
+ a.bar();
27
+ };
28
+
29
+ // FarRef<T>
30
+ const foo2 = async (a: FarRef<{ bar(): string; baz: number }>) => {
31
+ const { baz } = await a;
32
+ expectType<number>(baz);
33
+
34
+ expectType<Promise<string>>(E(a).bar());
35
+
36
+ // @ts-expect-error - awaiting remotes cannot get functions
37
+ (await a).bar;
38
+
39
+ expectType<Promise<number>>(E.get(a).baz);
40
+
41
+ // @ts-expect-error - E.get cannot obtain remote functions
42
+ E.get(a).bar;
43
+
44
+ expectType<number>((await a).baz);
45
+
46
+ // @ts-expect-error - calling directly is valid but not yet in the typedef
47
+ a.bar;
48
+ };