@endo/eventual-send 0.15.5 → 0.16.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/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
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.16.0](https://github.com/endojs/endo/compare/@endo/eventual-send@0.15.5...@endo/eventual-send@0.16.0) (2022-08-23)
7
+
8
+
9
+ ### ⚠ BREAKING CHANGES
10
+
11
+ * **eventual-send:** Disallow using E proxy methods as functions (#1255)
12
+
13
+ ### Bug Fixes
14
+
15
+ * **eventual-send:** Disallow using E proxy methods as functions ([#1255](https://github.com/endojs/endo/issues/1255)) ([43b7962](https://github.com/endojs/endo/commit/43b796232634b54c9e7de1c0a2349d22c29fc384))
16
+ * typedef default onfulfilled handler for E.when ([c5582ca](https://github.com/endojs/endo/commit/c5582ca7473e0a5d94ef4753ff54e0626cdb1d0a))
17
+
18
+
19
+
6
20
  ### [0.15.5](https://github.com/endojs/endo/compare/@endo/eventual-send@0.15.4...@endo/eventual-send@0.15.5) (2022-06-28)
7
21
 
8
22
  **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.15.5",
3
+ "version": "0.16.0",
4
4
  "description": "Extend a Promise class to implement the eventual-send API",
5
5
  "type": "module",
6
6
  "main": "src/no-shim.js",
@@ -27,8 +27,8 @@
27
27
  },
28
28
  "homepage": "https://github.com/endojs/endo#readme",
29
29
  "devDependencies": {
30
- "@endo/lockdown": "^0.1.15",
31
- "@endo/ses-ava": "^0.2.27",
30
+ "@endo/lockdown": "^0.1.16",
31
+ "@endo/ses-ava": "^0.2.28",
32
32
  "ava": "^3.12.1",
33
33
  "c8": "^7.7.3",
34
34
  "tsd": "^0.19.1"
@@ -62,5 +62,5 @@
62
62
  ],
63
63
  "timeout": "2m"
64
64
  },
65
- "gitHead": "a311acb02115271fbda6953734d0b4f52aa85892"
65
+ "gitHead": "7dc29059b201826295cbf2deb28bb2ed70f5ec1f"
66
66
  }
package/src/E.js CHANGED
@@ -3,6 +3,8 @@ import { trackTurns } from './track-turns.js';
3
3
 
4
4
  /// <reference path="index.d.ts" />
5
5
 
6
+ const { details: X, quote: q } = assert;
7
+
6
8
  /** @type {ProxyHandler<any>} */
7
9
  const baseFreezableProxyHandler = {
8
10
  set(_target, _prop, _value) {
@@ -19,6 +21,15 @@ const baseFreezableProxyHandler = {
19
21
  },
20
22
  };
21
23
 
24
+ // E Proxy handlers pretend that any property exists on the target and returns
25
+ // a function for their value. While this function is "bound" by context, it is
26
+ // meant to be called as a method. For that reason, the returned function
27
+ // includes a check that the `this` argument corresponds to the initial
28
+ // receiver when the function was retrieved.
29
+ // E Proxy handlers also forward direct calls to the target in case the remote
30
+ // is a function instead of an object. No such receiver checks are necessary in
31
+ // that case.
32
+
22
33
  /**
23
34
  * A Proxy handler for E(x).
24
35
  *
@@ -29,8 +40,28 @@ const baseFreezableProxyHandler = {
29
40
  function EProxyHandler(x, HandledPromise) {
30
41
  return harden({
31
42
  ...baseFreezableProxyHandler,
32
- get(_target, p, _receiver) {
33
- return harden((...args) => HandledPromise.applyMethod(x, p, args));
43
+ get(_target, p, receiver) {
44
+ return harden(
45
+ {
46
+ // This function purposely checks the `this` value (see above)
47
+ // In order to be `this` sensitive it is defined using concise method
48
+ // syntax rather than as an arrow function. To ensure the function
49
+ // is not constructable, it also avoids the `function` syntax.
50
+ [p](...args) {
51
+ if (this !== receiver) {
52
+ // Reject the async function call
53
+ return HandledPromise.reject(
54
+ assert.error(
55
+ X`Unexpected receiver for "${p}" method of E(${q(x)})`,
56
+ ),
57
+ );
58
+ }
59
+
60
+ return HandledPromise.applyMethod(x, p, args);
61
+ },
62
+ // @ts-expect-error https://github.com/microsoft/TypeScript/issues/50319
63
+ }[p],
64
+ );
34
65
  },
35
66
  apply(_target, _thisArg, argArray = []) {
36
67
  return HandledPromise.applyFunction(x, argArray);
@@ -53,11 +84,26 @@ function EProxyHandler(x, HandledPromise) {
53
84
  function EsendOnlyProxyHandler(x, HandledPromise) {
54
85
  return harden({
55
86
  ...baseFreezableProxyHandler,
56
- get(_target, p, _receiver) {
57
- return (...args) => {
58
- HandledPromise.applyMethodSendOnly(x, p, args);
59
- return undefined;
60
- };
87
+ get(_target, p, receiver) {
88
+ return harden(
89
+ {
90
+ // This function purposely checks the `this` value (see above)
91
+ // In order to be `this` sensitive it is defined using concise method
92
+ // syntax rather than as an arrow function. To ensure the function
93
+ // is not constructable, it also avoids the `function` syntax.
94
+ [p](...args) {
95
+ // Throw since the function returns nothing
96
+ assert.equal(
97
+ this,
98
+ receiver,
99
+ X`Unexpected receiver for "${p}" method of E.sendOnly(${q(x)})`,
100
+ );
101
+ HandledPromise.applyMethodSendOnly(x, p, args);
102
+ return undefined;
103
+ },
104
+ // @ts-expect-error https://github.com/microsoft/TypeScript/issues/50319
105
+ }[p],
106
+ );
61
107
  },
62
108
  apply(_target, _thisArg, argsArray = []) {
63
109
  HandledPromise.applyFunctionSendOnly(x, argsArray);
package/src/index.d.ts CHANGED
@@ -257,7 +257,7 @@ interface EProxy {
257
257
  * E.when(x, res, rej) is equivalent to
258
258
  * HandledPromise.resolve(x).then(res, rej)
259
259
  */
260
- readonly when: <T, U>(
260
+ readonly when: <T, U = Awaited<T>>(
261
261
  x: T,
262
262
  onfulfilled?: (value: Awaited<T>) => ERef<U>,
263
263
  onrejected?: (reason: any) => ERef<U>,
@@ -46,3 +46,26 @@ const foo2 = async (a: FarRef<{ bar(): string; baz: number }>) => {
46
46
  // @ts-expect-error - calling directly is valid but not yet in the typedef
47
47
  a.bar;
48
48
  };
49
+
50
+ // when
51
+ const aPromise = Promise.resolve('a');
52
+ const onePromise = Promise.resolve(1);
53
+ const remoteString: ERef<string> = Promise.resolve('remote');
54
+ E.when(Promise.all([aPromise, onePromise, remoteString])).then(
55
+ ([str, num, remote]) => {
56
+ expectType<string>(str);
57
+ expectType<number>(num);
58
+ expectType<string>(remote);
59
+ },
60
+ );
61
+ E.when(
62
+ Promise.all([aPromise, onePromise, remoteString]),
63
+ ([str, num, remote]) => {
64
+ expectType<string>(str);
65
+ expectType<number>(num);
66
+ expectType<string>(remote);
67
+ return { something: 'new' };
68
+ },
69
+ ).then(result => {
70
+ expectType<{ something: string }>(result);
71
+ });