@endo/eventual-send 1.0.1 → 1.1.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/README.md +0 -2
- package/package.json +8 -7
- package/src/E.d.ts +1 -9
- package/src/E.d.ts.map +1 -1
- package/src/E.js +53 -18
- package/src/local.d.ts +2 -2
- package/src/local.d.ts.map +1 -1
- package/src/local.js +42 -14
- package/src/message-breakpoints.d.ts +44 -0
- package/src/message-breakpoints.d.ts.map +1 -0
- package/src/message-breakpoints.js +179 -0
- package/src/track-turns.d.ts.map +1 -1
- package/src/track-turns.js +7 -11
- package/utils.d.ts +1 -0
- package/utils.js +1 -0
package/README.md
CHANGED
|
@@ -8,8 +8,6 @@ Create a HandledPromise class to implement the eventual-send API. This API is u
|
|
|
8
8
|
|
|
9
9
|
## How to use
|
|
10
10
|
|
|
11
|
-
> Note: If you're writing an application, you probably don't want to use this package directly. You'll want to use the eventual-send `~.` operator (tildot) provided in [SES](https://github.com/Agoric/SES) or other platforms.
|
|
12
|
-
|
|
13
11
|
To install the `HandledPromise` global property shim, do:
|
|
14
12
|
|
|
15
13
|
```js
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@endo/eventual-send",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/endojs/endo.git"
|
|
29
|
+
"url": "git+https://github.com/endojs/endo.git",
|
|
30
|
+
"directory": "packages/eventual-send"
|
|
30
31
|
},
|
|
31
32
|
"author": "Endo contributors",
|
|
32
33
|
"license": "Apache-2.0",
|
|
@@ -35,11 +36,11 @@
|
|
|
35
36
|
},
|
|
36
37
|
"homepage": "https://github.com/endojs/endo#readme",
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"@endo/env-options": "^1.
|
|
39
|
+
"@endo/env-options": "^1.1.1"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@endo/lockdown": "^1.0.
|
|
42
|
-
"@endo/ses-ava": "^1.
|
|
42
|
+
"@endo/lockdown": "^1.0.3",
|
|
43
|
+
"@endo/ses-ava": "^1.1.1",
|
|
43
44
|
"ava": "^5.3.0",
|
|
44
45
|
"c8": "^7.14.0",
|
|
45
46
|
"tsd": "^0.28.1"
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"timeout": "2m"
|
|
71
72
|
},
|
|
72
73
|
"typeCoverage": {
|
|
73
|
-
"atLeast": 77.
|
|
74
|
+
"atLeast": 77.81
|
|
74
75
|
},
|
|
75
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "f731c5c12f8d185dbf2daf53ec6a57e5ac56e4e9"
|
|
76
77
|
}
|
package/src/E.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
export default makeE;
|
|
2
2
|
export type EProxy = ReturnType<(HandledPromise: {
|
|
3
3
|
new <R>(executor: import("./handled-promise.js").HandledExecutor<R>, unfulfilledHandler?: import("./handled-promise.js").Handler<Promise<unknown>> | undefined): Promise<R>;
|
|
4
|
-
prototype: Promise<unknown>;
|
|
5
|
-
* E.sendOnly returns a proxy similar to E, but for which the results
|
|
6
|
-
* are ignored (undefined is returned).
|
|
7
|
-
*
|
|
8
|
-
* @template T
|
|
9
|
-
* @param {T} x target for method/function call
|
|
10
|
-
* @returns {ESendOnlyCallableOrMethods<RemoteFunctions<T>>} method/function call proxy
|
|
11
|
-
* @readonly
|
|
12
|
-
*/
|
|
4
|
+
prototype: Promise<unknown>;
|
|
13
5
|
} & PromiseConstructor & import("./handled-promise.js").HandledPromiseStaticMethods) => (<T>(x: T) => ECallableOrMethods<RemoteFunctions<T>>) & {
|
|
14
6
|
/**
|
|
15
7
|
* E.get(x) returns a proxy on which you can get arbitrary properties.
|
package/src/E.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"E.d.ts","sourceRoot":"","sources":["E.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"E.d.ts","sourceRoot":"","sources":["E.js"],"names":[],"mappings":";qBAoPc;;;;IAhEN;;;;;;;;;;OAUG;;IAMH;;;;;;;;OAQG;;;;;;IAGH;;;;;;;;OAQG;;IAMH;;;;;;;;;;;OAWG;;EAYoB;;;;;yDAQlB,KAAK,KAAK,GAAG,OAAO,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;;;;;0BAQ9D,KAAK,CAAC,EAAE,aAAa,CAAC,EAAE,OAAO,SAAS,EAAE,QAAQ,CAAC,CAAC;sBAMpD,YAAY,CAAC,CAAC,GAAG,CAAC;yHAQZ,WAAW,CAAC,CAAC,KAAK,QAAQ,QAAQ,WAAW,CAAC,CAAC,CAAC,CAAC;;;gFAwB7C,WAAW,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC;;4CAczC,CACZ,CAAK,SAAS,OAAO,SAAS,EAAE,QAAQ,GAChC,kBAAkB,CAAC,CAAC,GAAG,iBAAiB,SAAS,CAAC,CAAC,CAAC,GACpD,iBAAiB,SAAS,CAAC,CAAC,CAAC,CAClC;oCAKS,CACZ,CAAK,SAAS,OAAO,SAAS,EAAE,QAAQ,GAChC,UAAU,CAAC,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,GACpC,SAAS,SAAS,CAAC,CAAC,CAAC,CAC1B;;;;;;;;;;;;;;;;+EAyBe,WAAW,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC;;;;;;uCAmCrC;IACZ,OAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxB,OAAW,EAAE,OAAO,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACvC;;;;;wEAUe,WAAW,CAAC,CAAC,KAAK,KAAK,QAAQ,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AA3OxE;;GAEG;AACH,uCAFW,OAAO,SAAS,EAAE,yBAAyB;IAgB9C;;;;;;;;;;OAUG;;IAMH;;;;;;;;OAQG;;;;;;IAGH;;;;;;;;OAQG;;IAMH;;;;;;;;;;;OAWG;yIAHiB,GAAG;EAW9B"}
|
package/src/E.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { trackTurns } from './track-turns.js';
|
|
2
|
+
import { makeMessageBreakpointTester } from './message-breakpoints.js';
|
|
2
3
|
|
|
3
4
|
const { details: X, quote: q, Fail } = assert;
|
|
4
5
|
const { assign, create } = Object;
|
|
5
6
|
|
|
7
|
+
const onSend = makeMessageBreakpointTester('ENDO_SEND_BREAKPOINTS');
|
|
8
|
+
|
|
6
9
|
/** @type {ProxyHandler<any>} */
|
|
7
10
|
const baseFreezableProxyHandler = {
|
|
8
11
|
set(_target, _prop, _value) {
|
|
@@ -31,38 +34,55 @@ const baseFreezableProxyHandler = {
|
|
|
31
34
|
/**
|
|
32
35
|
* A Proxy handler for E(x).
|
|
33
36
|
*
|
|
34
|
-
* @param {any}
|
|
37
|
+
* @param {any} recipient Any value passed to E(x)
|
|
35
38
|
* @param {import('./types').HandledPromiseConstructor} HandledPromise
|
|
36
39
|
* @returns {ProxyHandler} the Proxy handler
|
|
37
40
|
*/
|
|
38
|
-
const makeEProxyHandler = (
|
|
41
|
+
const makeEProxyHandler = (recipient, HandledPromise) =>
|
|
39
42
|
harden({
|
|
40
43
|
...baseFreezableProxyHandler,
|
|
41
|
-
get: (_target,
|
|
44
|
+
get: (_target, propertyKey, receiver) => {
|
|
42
45
|
return harden(
|
|
43
46
|
{
|
|
44
47
|
// This function purposely checks the `this` value (see above)
|
|
45
48
|
// In order to be `this` sensitive it is defined using concise method
|
|
46
49
|
// syntax rather than as an arrow function. To ensure the function
|
|
47
50
|
// is not constructable, it also avoids the `function` syntax.
|
|
48
|
-
[
|
|
51
|
+
[propertyKey](...args) {
|
|
49
52
|
if (this !== receiver) {
|
|
50
53
|
// Reject the async function call
|
|
51
54
|
return HandledPromise.reject(
|
|
52
55
|
assert.error(
|
|
53
|
-
X`Unexpected receiver for "${
|
|
56
|
+
X`Unexpected receiver for "${q(propertyKey)}" method of E(${q(
|
|
57
|
+
recipient,
|
|
58
|
+
)})`,
|
|
54
59
|
),
|
|
55
60
|
);
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
if (onSend && onSend.shouldBreakpoint(recipient, propertyKey)) {
|
|
64
|
+
// eslint-disable-next-line no-debugger
|
|
65
|
+
debugger; // LOOK UP THE STACK
|
|
66
|
+
// Stopped at a breakpoint on eventual-send of a method-call
|
|
67
|
+
// message,
|
|
68
|
+
// so that you can walk back on the stack to see how we came to
|
|
69
|
+
// make this eventual-send
|
|
70
|
+
}
|
|
71
|
+
return HandledPromise.applyMethod(recipient, propertyKey, args);
|
|
59
72
|
},
|
|
60
73
|
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/50319
|
|
61
|
-
}[
|
|
74
|
+
}[propertyKey],
|
|
62
75
|
);
|
|
63
76
|
},
|
|
64
77
|
apply: (_target, _thisArg, argArray = []) => {
|
|
65
|
-
|
|
78
|
+
if (onSend && onSend.shouldBreakpoint(recipient, undefined)) {
|
|
79
|
+
// eslint-disable-next-line no-debugger
|
|
80
|
+
debugger; // LOOK UP THE STACK
|
|
81
|
+
// Stopped at a breakpoint on eventual-send of a function-call message,
|
|
82
|
+
// so that you can walk back on the stack to see how we came to
|
|
83
|
+
// make this eventual-send
|
|
84
|
+
}
|
|
85
|
+
return HandledPromise.applyFunction(recipient, argArray);
|
|
66
86
|
},
|
|
67
87
|
has: (_target, _p) => {
|
|
68
88
|
// We just pretend everything exists.
|
|
@@ -74,35 +94,50 @@ const makeEProxyHandler = (x, HandledPromise) =>
|
|
|
74
94
|
* A Proxy handler for E.sendOnly(x)
|
|
75
95
|
* It is a variant on the E(x) Proxy handler.
|
|
76
96
|
*
|
|
77
|
-
* @param {any}
|
|
97
|
+
* @param {any} recipient Any value passed to E.sendOnly(x)
|
|
78
98
|
* @param {import('./types').HandledPromiseConstructor} HandledPromise
|
|
79
99
|
* @returns {ProxyHandler} the Proxy handler
|
|
80
100
|
*/
|
|
81
|
-
const makeESendOnlyProxyHandler = (
|
|
101
|
+
const makeESendOnlyProxyHandler = (recipient, HandledPromise) =>
|
|
82
102
|
harden({
|
|
83
103
|
...baseFreezableProxyHandler,
|
|
84
|
-
get: (_target,
|
|
104
|
+
get: (_target, propertyKey, receiver) => {
|
|
85
105
|
return harden(
|
|
86
106
|
{
|
|
87
107
|
// This function purposely checks the `this` value (see above)
|
|
88
108
|
// In order to be `this` sensitive it is defined using concise method
|
|
89
109
|
// syntax rather than as an arrow function. To ensure the function
|
|
90
110
|
// is not constructable, it also avoids the `function` syntax.
|
|
91
|
-
[
|
|
111
|
+
[propertyKey](...args) {
|
|
92
112
|
// Throw since the function returns nothing
|
|
93
113
|
this === receiver ||
|
|
94
|
-
Fail`Unexpected receiver for "${q(
|
|
95
|
-
|
|
96
|
-
)})`;
|
|
97
|
-
|
|
114
|
+
Fail`Unexpected receiver for "${q(
|
|
115
|
+
propertyKey,
|
|
116
|
+
)}" method of E.sendOnly(${q(recipient)})`;
|
|
117
|
+
if (onSend && onSend.shouldBreakpoint(recipient, propertyKey)) {
|
|
118
|
+
// eslint-disable-next-line no-debugger
|
|
119
|
+
debugger; // LOOK UP THE STACK
|
|
120
|
+
// Stopped at a breakpoint on eventual-send of a method-call
|
|
121
|
+
// message,
|
|
122
|
+
// so that you can walk back on the stack to see how we came to
|
|
123
|
+
// make this eventual-send
|
|
124
|
+
}
|
|
125
|
+
HandledPromise.applyMethodSendOnly(recipient, propertyKey, args);
|
|
98
126
|
return undefined;
|
|
99
127
|
},
|
|
100
128
|
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/50319
|
|
101
|
-
}[
|
|
129
|
+
}[propertyKey],
|
|
102
130
|
);
|
|
103
131
|
},
|
|
104
132
|
apply: (_target, _thisArg, argsArray = []) => {
|
|
105
|
-
|
|
133
|
+
if (onSend && onSend.shouldBreakpoint(recipient, undefined)) {
|
|
134
|
+
// eslint-disable-next-line no-debugger
|
|
135
|
+
debugger; // LOOK UP THE STACK
|
|
136
|
+
// Stopped at a breakpoint on eventual-send of a function-call message,
|
|
137
|
+
// so that you can walk back on the stack to see how we came to
|
|
138
|
+
// make this eventual-send
|
|
139
|
+
}
|
|
140
|
+
HandledPromise.applyFunctionSendOnly(recipient, argsArray);
|
|
106
141
|
return undefined;
|
|
107
142
|
},
|
|
108
143
|
has: (_target, _p) => {
|
package/src/local.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function getMethodNames(val: any): (string | symbol)[];
|
|
2
|
-
export function localApplyFunction(
|
|
3
|
-
export function localApplyMethod(
|
|
2
|
+
export function localApplyFunction(recipient: any, args: any): any;
|
|
3
|
+
export function localApplyMethod(recipient: any, methodName: any, args: any): any;
|
|
4
4
|
export function localGet(t: any, key: any): any;
|
|
5
5
|
//# sourceMappingURL=local.d.ts.map
|
package/src/local.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["local.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["local.js"],"names":[],"mappings":"AA8CO,oCAHI,GAAG,GACD,CAAC,MAAM,GAAC,MAAM,CAAC,EAAE,CAqB7B;AAKM,mEAkBN;AAEM,kFAqCN;AAEM,gDAAmC"}
|
package/src/local.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { makeMessageBreakpointTester } from './message-breakpoints.js';
|
|
2
|
+
|
|
1
3
|
const { details: X, quote: q, Fail } = assert;
|
|
2
4
|
|
|
3
5
|
const { getOwnPropertyDescriptors, getPrototypeOf, freeze } = Object;
|
|
@@ -5,6 +7,8 @@ const { apply, ownKeys } = Reflect;
|
|
|
5
7
|
|
|
6
8
|
const ntypeof = specimen => (specimen === null ? 'null' : typeof specimen);
|
|
7
9
|
|
|
10
|
+
const onDelivery = makeMessageBreakpointTester('ENDO_DELIVERY_BREAKPOINTS');
|
|
11
|
+
|
|
8
12
|
/**
|
|
9
13
|
* TODO Consolidate with `isObject` that's currently in `@endo/marshal`
|
|
10
14
|
*
|
|
@@ -64,39 +68,63 @@ export const getMethodNames = val => {
|
|
|
64
68
|
// ses creates `harden`, and so cannot rely on `harden` at top level.
|
|
65
69
|
freeze(getMethodNames);
|
|
66
70
|
|
|
67
|
-
export const localApplyFunction = (
|
|
68
|
-
typeof
|
|
71
|
+
export const localApplyFunction = (recipient, args) => {
|
|
72
|
+
typeof recipient === 'function' ||
|
|
69
73
|
assert.fail(
|
|
70
|
-
X`Cannot invoke target as a function; typeof target is ${q(
|
|
74
|
+
X`Cannot invoke target as a function; typeof target is ${q(
|
|
75
|
+
ntypeof(recipient),
|
|
76
|
+
)}`,
|
|
71
77
|
TypeError,
|
|
72
78
|
);
|
|
73
|
-
|
|
79
|
+
if (onDelivery && onDelivery.shouldBreakpoint(recipient, undefined)) {
|
|
80
|
+
// eslint-disable-next-line no-debugger
|
|
81
|
+
debugger; // STEP INTO APPLY
|
|
82
|
+
// Stopped at a breakpoint on this delivery of an eventual function call
|
|
83
|
+
// so that you can step *into* the following `apply` in order to see the
|
|
84
|
+
// function call as it happens. Or step *over* to see what happens
|
|
85
|
+
// after the function call returns.
|
|
86
|
+
}
|
|
87
|
+
const result = apply(recipient, undefined, args);
|
|
88
|
+
return result;
|
|
74
89
|
};
|
|
75
90
|
|
|
76
|
-
export const localApplyMethod = (
|
|
77
|
-
if (
|
|
91
|
+
export const localApplyMethod = (recipient, methodName, args) => {
|
|
92
|
+
if (methodName === undefined || methodName === null) {
|
|
78
93
|
// Base case; bottom out to apply functions.
|
|
79
|
-
return localApplyFunction(
|
|
94
|
+
return localApplyFunction(recipient, args);
|
|
80
95
|
}
|
|
81
|
-
if (
|
|
96
|
+
if (recipient === undefined || recipient === null) {
|
|
82
97
|
assert.fail(
|
|
83
|
-
X`Cannot deliver ${q(
|
|
84
|
-
ntypeof(
|
|
98
|
+
X`Cannot deliver ${q(methodName)} to target; typeof target is ${q(
|
|
99
|
+
ntypeof(recipient),
|
|
85
100
|
)}`,
|
|
86
101
|
TypeError,
|
|
87
102
|
);
|
|
88
103
|
}
|
|
89
|
-
const fn =
|
|
104
|
+
const fn = recipient[methodName];
|
|
90
105
|
if (fn === undefined) {
|
|
91
106
|
assert.fail(
|
|
92
|
-
X`target has no method ${q(
|
|
107
|
+
X`target has no method ${q(methodName)}, has ${q(
|
|
108
|
+
getMethodNames(recipient),
|
|
109
|
+
)}`,
|
|
93
110
|
TypeError,
|
|
94
111
|
);
|
|
95
112
|
}
|
|
96
113
|
const ftype = ntypeof(fn);
|
|
97
114
|
typeof fn === 'function' ||
|
|
98
|
-
Fail`invoked method ${q(
|
|
99
|
-
|
|
115
|
+
Fail`invoked method ${q(methodName)} is not a function; it is a ${q(
|
|
116
|
+
ftype,
|
|
117
|
+
)}`;
|
|
118
|
+
if (onDelivery && onDelivery.shouldBreakpoint(recipient, methodName)) {
|
|
119
|
+
// eslint-disable-next-line no-debugger
|
|
120
|
+
debugger; // STEP INTO APPLY
|
|
121
|
+
// Stopped at a breakpoint on this delivery of an eventual method call
|
|
122
|
+
// so that you can step *into* the following `apply` in order to see the
|
|
123
|
+
// method call as it happens. Or step *over* to see what happens
|
|
124
|
+
// after the method call returns.
|
|
125
|
+
}
|
|
126
|
+
const result = apply(fn, recipient, args);
|
|
127
|
+
return result;
|
|
100
128
|
};
|
|
101
129
|
|
|
102
130
|
export const localGet = (t, key) => t[key];
|
|
@@ -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":"AAyFO,wDAHI,MAAM,GACJ,uBAAuB,GAAG,SAAS,CA0F/C;;;;;;;;6BA1KY,MAAM,GAAG,GAAG;;;;;;;8BAQZ,MAAM,GAAG,GAAG;;;;;;;;6BAOZ,MAAM,GAAG,GAAG;;;;;;;iCAcZ,OAAO,cAAc,EAAE,OAAO,eAAe,EAAE,cAAc,CAAC,CAAC;;;;;;;8BAS/D,OAAO,eAAe,EAAE,OAAO,cAAc,EAAE,cAAc,CAAC,CAAC;;oBAK9D,MAAM,kBAAkB;sCACN,kBAAkB,KAAK,IAAI;kCAE3C,MAAM,cACL,MAAM,GAAG,MAAM,GAAG,SAAS,KACpC,OAAO"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { getEnvironmentOption } from '@endo/env-options';
|
|
2
|
+
|
|
3
|
+
const { quote: q, Fail } = assert;
|
|
4
|
+
|
|
5
|
+
const { hasOwn, freeze, entries } = Object;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {string | '*'} MatchStringTag
|
|
9
|
+
* A star `'*'` matches any recipient. Otherwise, the string is
|
|
10
|
+
* matched against the value of a recipient's `@@toStringTag`
|
|
11
|
+
* after stripping out any leading `'Alleged: '` or `'DebugName: '`
|
|
12
|
+
* prefix. For objects defined with `Far` this is the first argument,
|
|
13
|
+
* known as the `farName`. For exos, this is the tag.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {string | '*'} MatchMethodName
|
|
17
|
+
* A star `'*'` matches any method name. Otherwise, the string is
|
|
18
|
+
* matched against the method name. Currently, this is only an exact match.
|
|
19
|
+
* However, beware that we may introduce a string syntax for
|
|
20
|
+
* symbol method names.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {number | '*'} MatchCountdown
|
|
24
|
+
* A star `'*'` will always breakpoint. Otherwise, the string
|
|
25
|
+
* must be a non-negative integer. Once that is zero, always breakpoint.
|
|
26
|
+
* Otherwise decrement by one each time it matches until it reaches zero.
|
|
27
|
+
* In other words, the countdown represents the number of
|
|
28
|
+
* breakpoint occurrences to skip before actually breakpointing.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This is the external JSON representation, in which
|
|
33
|
+
* - the outer property name is the class-like tag or '*',
|
|
34
|
+
* - the inner property name is the method name or '*',
|
|
35
|
+
* - the value is a non-negative integer countdown or '*'.
|
|
36
|
+
*
|
|
37
|
+
* @typedef {Record<MatchStringTag, Record<MatchMethodName, MatchCountdown>>} MessageBreakpoints
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* This is the internal JSON representation, in which
|
|
42
|
+
* - the outer property name is the method name or '*',
|
|
43
|
+
* - the inner property name is the class-like tag or '*',
|
|
44
|
+
* - the value is a non-negative integer countdown or '*'.
|
|
45
|
+
*
|
|
46
|
+
* @typedef {Record<MatchMethodName, Record<MatchStringTag, MatchCountdown>>} BreakpointTable
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {object} MessageBreakpointTester
|
|
51
|
+
* @property {() => MessageBreakpoints} getBreakpoints
|
|
52
|
+
* @property {(newBreakpoints?: MessageBreakpoints) => void} setBreakpoints
|
|
53
|
+
* @property {(
|
|
54
|
+
* recipient: object,
|
|
55
|
+
* methodName: string | symbol | undefined
|
|
56
|
+
* ) => boolean} shouldBreakpoint
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {any} val
|
|
61
|
+
* @returns {val is Record<string, any>}
|
|
62
|
+
*/
|
|
63
|
+
const isJSONRecord = val =>
|
|
64
|
+
typeof val === 'object' && val !== null && !Array.isArray(val);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Return `tag` after stripping off any `'Alleged: '` or `'DebugName: '`
|
|
68
|
+
* prefix if present.
|
|
69
|
+
* ```js
|
|
70
|
+
* simplifyTag('Alleged: moola issuer') === 'moola issuer'
|
|
71
|
+
* ```
|
|
72
|
+
* If there are multiple such prefixes, only the outer one is removed.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} tag
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
const simplifyTag = tag => {
|
|
78
|
+
for (const prefix of ['Alleged: ', 'DebugName: ']) {
|
|
79
|
+
if (tag.startsWith(prefix)) {
|
|
80
|
+
return tag.slice(prefix.length);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return tag;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} optionName
|
|
88
|
+
* @returns {MessageBreakpointTester | undefined}
|
|
89
|
+
*/
|
|
90
|
+
export const makeMessageBreakpointTester = optionName => {
|
|
91
|
+
let breakpoints = JSON.parse(getEnvironmentOption(optionName, 'null'));
|
|
92
|
+
|
|
93
|
+
if (breakpoints === null) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @type {BreakpointTable} */
|
|
98
|
+
let breakpointsTable;
|
|
99
|
+
|
|
100
|
+
const getBreakpoints = () => breakpoints;
|
|
101
|
+
freeze(getBreakpoints);
|
|
102
|
+
|
|
103
|
+
const setBreakpoints = (newBreakpoints = breakpoints) => {
|
|
104
|
+
isJSONRecord(newBreakpoints) ||
|
|
105
|
+
Fail`Expected ${q(optionName)} option to be a JSON breakpoints record`;
|
|
106
|
+
|
|
107
|
+
/** @type {BreakpointTable} */
|
|
108
|
+
// @ts-expect-error confused by __proto__
|
|
109
|
+
const newBreakpointsTable = { __proto__: null };
|
|
110
|
+
|
|
111
|
+
for (const [tag, methodBPs] of entries(newBreakpoints)) {
|
|
112
|
+
tag === simplifyTag(tag) ||
|
|
113
|
+
Fail`Just use simple tag ${q(simplifyTag(tag))} rather than ${q(tag)}`;
|
|
114
|
+
isJSONRecord(methodBPs) ||
|
|
115
|
+
Fail`Expected ${q(optionName)} option's ${q(
|
|
116
|
+
tag,
|
|
117
|
+
)} to be a JSON methods breakpoints record`;
|
|
118
|
+
for (const [methodName, count] of entries(methodBPs)) {
|
|
119
|
+
count === '*' ||
|
|
120
|
+
(typeof count === 'number' &&
|
|
121
|
+
Number.isSafeInteger(count) &&
|
|
122
|
+
count >= 0) ||
|
|
123
|
+
Fail`Expected ${q(optionName)} option's ${q(tag)}.${q(
|
|
124
|
+
methodName,
|
|
125
|
+
)} to be "*" or a non-negative integer`;
|
|
126
|
+
|
|
127
|
+
const classBPs = hasOwn(newBreakpointsTable, methodName)
|
|
128
|
+
? newBreakpointsTable[methodName]
|
|
129
|
+
: (newBreakpointsTable[methodName] = {
|
|
130
|
+
// @ts-expect-error confused by __proto__
|
|
131
|
+
__proto__: null,
|
|
132
|
+
});
|
|
133
|
+
classBPs[tag] = count;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
breakpoints = newBreakpoints;
|
|
137
|
+
breakpointsTable = newBreakpointsTable;
|
|
138
|
+
};
|
|
139
|
+
freeze(setBreakpoints);
|
|
140
|
+
|
|
141
|
+
const shouldBreakpoint = (recipient, methodName) => {
|
|
142
|
+
if (methodName === undefined || methodName === null) {
|
|
143
|
+
// TODO enable function breakpointing
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const classBPs = breakpointsTable[methodName] || breakpointsTable['*'];
|
|
147
|
+
if (classBPs === undefined) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
let tag = simplifyTag(recipient[Symbol.toStringTag]);
|
|
151
|
+
let count = classBPs[tag];
|
|
152
|
+
if (count === undefined) {
|
|
153
|
+
tag = '*';
|
|
154
|
+
count = classBPs[tag];
|
|
155
|
+
if (count === undefined) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (count === '*') {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (count === 0) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
assert(typeof count === 'number' && count >= 1);
|
|
166
|
+
classBPs[tag] = count - 1;
|
|
167
|
+
return false;
|
|
168
|
+
};
|
|
169
|
+
freeze(shouldBreakpoint);
|
|
170
|
+
|
|
171
|
+
const breakpointTester = freeze({
|
|
172
|
+
getBreakpoints,
|
|
173
|
+
setBreakpoints,
|
|
174
|
+
shouldBreakpoint,
|
|
175
|
+
});
|
|
176
|
+
breakpointTester.setBreakpoints();
|
|
177
|
+
return breakpointTester;
|
|
178
|
+
};
|
|
179
|
+
freeze(makeMessageBreakpointTester);
|
package/src/track-turns.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"track-turns.d.ts","sourceRoot":"","sources":["track-turns.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"track-turns.d.ts","sourceRoot":"","sources":["track-turns.js"],"names":[],"mappings":"AAwFO,mEAiBN;;;;;uCAMuB,GAAG,EAAE,KAAK,GAAG"}
|
package/src/track-turns.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* global globalThis */
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
getEnvironmentOption,
|
|
4
|
+
environmentOptionsListHas,
|
|
5
|
+
} from '@endo/env-options';
|
|
5
6
|
|
|
6
7
|
// NOTE: We can't import these because they're not in scope before lockdown.
|
|
7
8
|
// import { assert, details as X, Fail } from '@agoric/assert';
|
|
@@ -17,18 +18,13 @@ let hiddenPriorError;
|
|
|
17
18
|
let hiddenCurrentTurn = 0;
|
|
18
19
|
let hiddenCurrentEvent = 0;
|
|
19
20
|
|
|
20
|
-
const DEBUG = getEnvironmentOption('DEBUG', '');
|
|
21
|
-
|
|
22
21
|
// Turn on if you seem to be losing error logging at the top of the event loop
|
|
23
|
-
const VERBOSE =
|
|
22
|
+
const VERBOSE = environmentOptionsListHas('DEBUG', 'track-turns');
|
|
24
23
|
|
|
25
24
|
// Track-turns is disabled by default and can be enabled by an environment
|
|
26
25
|
// option.
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
throw TypeError(`unrecognized TRACK_TURNS ${JSON.stringify(TRACK_TURNS)}`);
|
|
30
|
-
}
|
|
31
|
-
const ENABLED = (TRACK_TURNS || 'disabled') === 'enabled';
|
|
26
|
+
const ENABLED =
|
|
27
|
+
getEnvironmentOption('TRACK_TURNS', 'disabled', ['enabled']) === 'enabled';
|
|
32
28
|
|
|
33
29
|
// We hoist the following functions out of trackTurns() to discourage the
|
|
34
30
|
// closures from holding onto 'args' or 'func' longer than necessary,
|
package/utils.d.ts
CHANGED
package/utils.js
CHANGED