@agoric/base-zone 0.1.1-dev-7ffae88.0 → 0.1.1-orchestration-dev-096c4e8.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/package.json +12 -9
- package/src/prepare-revocable.d.ts +36 -0
- package/src/prepare-revocable.d.ts.map +1 -0
- package/src/prepare-revocable.js +138 -0
- package/test/prepare-test-env-ava.js +3 -4
- package/test/test-prepare-revocable.js +117 -0
- package/zone-helpers.d.ts +2 -0
- package/zone-helpers.d.ts.map +1 -0
- package/zone-helpers.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/base-zone",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-orchestration-dev-096c4e8.0+096c4e8",
|
|
4
4
|
"description": "Allocation zone abstraction library and heap implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/Agoric/agoric-sdk",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": "./src/index.js",
|
|
22
|
+
"./zone-helpers.js": "./zone-helpers.js",
|
|
22
23
|
"./heap.js": "./heap.js",
|
|
23
24
|
"./tools/*": "./tools/*"
|
|
24
25
|
},
|
|
@@ -26,14 +27,16 @@
|
|
|
26
27
|
"author": "Agoric",
|
|
27
28
|
"license": "Apache-2.0",
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@agoric/store": "0.9.3-dev-
|
|
30
|
-
"@endo/
|
|
31
|
-
"@endo/
|
|
32
|
-
"@endo/
|
|
33
|
-
"@endo/
|
|
30
|
+
"@agoric/store": "0.9.3-orchestration-dev-096c4e8.0+096c4e8",
|
|
31
|
+
"@endo/common": "^1.1.0",
|
|
32
|
+
"@endo/exo": "^1.2.1",
|
|
33
|
+
"@endo/far": "^1.0.4",
|
|
34
|
+
"@endo/pass-style": "^1.2.0",
|
|
35
|
+
"@endo/patterns": "^1.2.0"
|
|
34
36
|
},
|
|
35
37
|
"devDependencies": {
|
|
36
|
-
"@endo/init": "^1.0.
|
|
38
|
+
"@endo/init": "^1.0.4",
|
|
39
|
+
"@endo/ses-ava": "^1.1.2",
|
|
37
40
|
"ava": "^5.3.0"
|
|
38
41
|
},
|
|
39
42
|
"publishConfig": {
|
|
@@ -53,7 +56,7 @@
|
|
|
53
56
|
"workerThreads": false
|
|
54
57
|
},
|
|
55
58
|
"typeCoverage": {
|
|
56
|
-
"atLeast":
|
|
59
|
+
"atLeast": 89.25
|
|
57
60
|
},
|
|
58
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "096c4e8fce80e9a509b0e1a30fda11736c4570e1"
|
|
59
62
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function prepareRevocableKit<U extends unknown = any>(zone: import('@agoric/base-zone').Zone, uKindName: string, uMethodNames: (string | symbol)[], options?: RevocableKitOptions<any> | undefined): (underlying: U) => RevocableKit<U>;
|
|
2
|
+
export type Revoker = {
|
|
3
|
+
revoke: () => boolean;
|
|
4
|
+
};
|
|
5
|
+
export type RevocableKit<U extends unknown = any> = {
|
|
6
|
+
revoker: Revoker;
|
|
7
|
+
/**
|
|
8
|
+
* Forwards to the underlying exo object, until revoked
|
|
9
|
+
*/
|
|
10
|
+
revocable: U;
|
|
11
|
+
};
|
|
12
|
+
export type RevocableKitThis<U = any> = {
|
|
13
|
+
facets: RevocableKit<U>;
|
|
14
|
+
state: {
|
|
15
|
+
underlying: U;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export type RevocableKitOptions<U extends unknown = any> = {
|
|
19
|
+
/**
|
|
20
|
+
* The `interfaceName` of the underlying interface guard.
|
|
21
|
+
* Defaults to the `uKindName`.
|
|
22
|
+
*/
|
|
23
|
+
uInterfaceName?: string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* For guarding the `extraMethods`, if you include them below. These appear
|
|
26
|
+
* only on the synthesized interface guard for the revocable caretaker, and
|
|
27
|
+
* do not necessarily correspond to any method of the underlying.
|
|
28
|
+
*/
|
|
29
|
+
extraMethodGuards?: Record<string | symbol, import("@endo/patterns").MethodGuard> | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Extra methods adding behavior only to the revocable caretaker, and
|
|
32
|
+
* do not necessarily correspond to any methods of the underlying.
|
|
33
|
+
*/
|
|
34
|
+
extraMethods?: Record<string | symbol, (this: RevocableKitThis<U>, ...args: any[]) => any> | undefined;
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=prepare-revocable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare-revocable.d.ts","sourceRoot":"","sources":["prepare-revocable.js"],"names":[],"mappings":"AA6DO,mEATI,OAAO,mBAAmB,EAAE,IAAI,aAChC,MAAM,gBAEN,CAAC,MAAM,GAAC,MAAM,CAAC,EAAE,sFAiF3B;;YAjIa,MAAM,OAAO;;;aAMb,OAAO;;;;eACP,CAAC;;;YAOD,aAAa,CAAC,CAAC;WACf;QAAE,UAAU,EAAE,CAAC,CAAA;KAAE;;;;;;;;;;;;;;;;;;kDAkBnB,iBAAiB,CAAC,CAAC,WAAW,GAAG,EAAE,KAAK,GAAG"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { M } from '@endo/patterns';
|
|
2
|
+
import { fromUniqueEntries } from '@endo/common/from-unique-entries.js';
|
|
3
|
+
|
|
4
|
+
const { Fail, quote: q } = assert;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {object} Revoker
|
|
8
|
+
* @property {() => boolean} revoke
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @template {any} [U=any]
|
|
13
|
+
* @typedef {object} RevocableKit
|
|
14
|
+
* @property {Revoker} revoker
|
|
15
|
+
* @property {U} revocable
|
|
16
|
+
* Forwards to the underlying exo object, until revoked
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @template [U=any]
|
|
21
|
+
* @typedef {object} RevocableKitThis
|
|
22
|
+
* @property {RevocableKit<U>} facets
|
|
23
|
+
* @property {{ underlying: U }} state
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @template {any} [U=any]
|
|
28
|
+
* @typedef {object} RevocableKitOptions
|
|
29
|
+
* @property {string} [uInterfaceName]
|
|
30
|
+
* The `interfaceName` of the underlying interface guard.
|
|
31
|
+
* Defaults to the `uKindName`.
|
|
32
|
+
* @property {Record<
|
|
33
|
+
* string|symbol,
|
|
34
|
+
* import('@endo/patterns').MethodGuard
|
|
35
|
+
* >} [extraMethodGuards]
|
|
36
|
+
* For guarding the `extraMethods`, if you include them below. These appear
|
|
37
|
+
* only on the synthesized interface guard for the revocable caretaker, and
|
|
38
|
+
* do not necessarily correspond to any method of the underlying.
|
|
39
|
+
* @property {Record<
|
|
40
|
+
* string|symbol,
|
|
41
|
+
* (this: RevocableKitThis<U>, ...args: any[]) => any
|
|
42
|
+
* >} [extraMethods]
|
|
43
|
+
* Extra methods adding behavior only to the revocable caretaker, and
|
|
44
|
+
* do not necessarily correspond to any methods of the underlying.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Make an exo class kit for wrapping an underlying exo class,
|
|
49
|
+
* where the wrapper is a revocable forwarder.
|
|
50
|
+
*
|
|
51
|
+
* @deprecated Change to `prepareRevocableMakerKit` once #8977 happens
|
|
52
|
+
* @template {any} [U=any]
|
|
53
|
+
* @param {import('@agoric/base-zone').Zone} zone
|
|
54
|
+
* @param {string} uKindName
|
|
55
|
+
* The `kindName` of the underlying exo class
|
|
56
|
+
* @param {(string|symbol)[]} uMethodNames
|
|
57
|
+
* The method names of the underlying exo class that should be represented
|
|
58
|
+
* by transparently-forwarding methods of the revocable caretaker.
|
|
59
|
+
* @param {RevocableKitOptions} [options]
|
|
60
|
+
* @returns {(underlying: U) => RevocableKit<U>}
|
|
61
|
+
*/
|
|
62
|
+
export const prepareRevocableKit = (
|
|
63
|
+
zone,
|
|
64
|
+
uKindName,
|
|
65
|
+
uMethodNames,
|
|
66
|
+
options = {},
|
|
67
|
+
) => {
|
|
68
|
+
const {
|
|
69
|
+
uInterfaceName = uKindName,
|
|
70
|
+
extraMethodGuards = undefined,
|
|
71
|
+
extraMethods = undefined,
|
|
72
|
+
} = options;
|
|
73
|
+
const RevocableIKit = harden({
|
|
74
|
+
revoker: M.interface(`${uInterfaceName}_revoker`, {
|
|
75
|
+
revoke: M.call().returns(M.boolean()),
|
|
76
|
+
}),
|
|
77
|
+
revocable: M.interface(
|
|
78
|
+
`${uInterfaceName}_revocable`,
|
|
79
|
+
{
|
|
80
|
+
...extraMethodGuards,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
defaultGuards: 'raw',
|
|
84
|
+
},
|
|
85
|
+
),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const revocableKindName = `${uKindName}_caretaker`;
|
|
89
|
+
|
|
90
|
+
const makeRevocableKit = zone.exoClassKit(
|
|
91
|
+
revocableKindName,
|
|
92
|
+
RevocableIKit,
|
|
93
|
+
underlying => ({
|
|
94
|
+
underlying,
|
|
95
|
+
}),
|
|
96
|
+
{
|
|
97
|
+
revoker: {
|
|
98
|
+
revoke() {
|
|
99
|
+
const { state } = this;
|
|
100
|
+
if (state.underlying === undefined) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
state.underlying = undefined;
|
|
104
|
+
return true;
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
revocable: {
|
|
108
|
+
...fromUniqueEntries(
|
|
109
|
+
uMethodNames.map(name => [
|
|
110
|
+
name,
|
|
111
|
+
{
|
|
112
|
+
// Use concise method syntax for exo methods
|
|
113
|
+
[name](...args) {
|
|
114
|
+
// @ts-expect-error normal exo-this typing confusion
|
|
115
|
+
const { underlying } = this.state;
|
|
116
|
+
underlying !== undefined ||
|
|
117
|
+
Fail`${q(revocableKindName)} revoked`;
|
|
118
|
+
return underlying[name](...args);
|
|
119
|
+
},
|
|
120
|
+
// @ts-expect-error using possible symbol as index type
|
|
121
|
+
}[name],
|
|
122
|
+
]),
|
|
123
|
+
),
|
|
124
|
+
...extraMethods,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
stateShape: {
|
|
129
|
+
underlying: M.opt(M.remotable('underlying')),
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
135
|
+
// @ts-ignore parameter confusion
|
|
136
|
+
return makeRevocableKit;
|
|
137
|
+
};
|
|
138
|
+
harden(prepareRevocableKit);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Modeled on test-heap-classes.js
|
|
2
|
+
|
|
3
|
+
import { test } from './prepare-test-env-ava.js';
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line import/order
|
|
6
|
+
import { M } from '@endo/patterns';
|
|
7
|
+
import { makeHeapZone } from '../src/heap.js';
|
|
8
|
+
import { prepareRevocableKit } from '../src/prepare-revocable.js';
|
|
9
|
+
|
|
10
|
+
const UpCounterI = M.interface('UpCounter', {
|
|
11
|
+
incr: M.call()
|
|
12
|
+
// TODO M.number() should not be needed to get a better error message
|
|
13
|
+
.optional(M.and(M.number(), M.gte(0)))
|
|
14
|
+
.returns(M.number()),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const DownCounterI = M.interface('DownCounter', {
|
|
18
|
+
decr: M.call()
|
|
19
|
+
// TODO M.number() should not be needed to get a better error message
|
|
20
|
+
.optional(M.and(M.number(), M.gte(0)))
|
|
21
|
+
.returns(M.number()),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('test revoke defineVirtualExoClass', t => {
|
|
25
|
+
const zone = makeHeapZone('rootZone');
|
|
26
|
+
|
|
27
|
+
const makeUnderlyingUpCounter = zone.exoClass(
|
|
28
|
+
'UpCounter',
|
|
29
|
+
UpCounterI,
|
|
30
|
+
/** @param {number} [x] */
|
|
31
|
+
(x = 0) => ({ x }),
|
|
32
|
+
{
|
|
33
|
+
incr(y = 1) {
|
|
34
|
+
const { state } = this;
|
|
35
|
+
state.x += y;
|
|
36
|
+
return state.x;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const makeRevocableUpCounterKit = prepareRevocableKit(zone, 'UpCounter', [
|
|
42
|
+
'incr',
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
const makeUpCounterKit = x =>
|
|
46
|
+
makeRevocableUpCounterKit(makeUnderlyingUpCounter(x));
|
|
47
|
+
|
|
48
|
+
const { revoker, revocable: upCounter } = makeUpCounterKit(3);
|
|
49
|
+
t.is(upCounter.incr(5), 8);
|
|
50
|
+
t.is(revoker.revoke(), true);
|
|
51
|
+
t.throws(() => upCounter.incr(1), {
|
|
52
|
+
message: '"UpCounter_caretaker" revoked',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('test revoke defineVirtualExoClassKit', t => {
|
|
57
|
+
const zone = makeHeapZone('rootZone');
|
|
58
|
+
|
|
59
|
+
const makeUnderlyingCounterKit = zone.exoClassKit(
|
|
60
|
+
'Counter',
|
|
61
|
+
{ up: UpCounterI, down: DownCounterI },
|
|
62
|
+
/** @param {number} [x] */
|
|
63
|
+
(x = 0) => ({ x }),
|
|
64
|
+
{
|
|
65
|
+
up: {
|
|
66
|
+
incr(y = 1) {
|
|
67
|
+
const { state } = this;
|
|
68
|
+
state.x += y;
|
|
69
|
+
return state.x;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
down: {
|
|
73
|
+
decr(y = 1) {
|
|
74
|
+
const { state } = this;
|
|
75
|
+
state.x -= y;
|
|
76
|
+
return state.x;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const makeRevocableUpCounterKit = prepareRevocableKit(
|
|
83
|
+
zone,
|
|
84
|
+
'UpCounter',
|
|
85
|
+
['incr'],
|
|
86
|
+
{
|
|
87
|
+
extraMethodGuards: {
|
|
88
|
+
selfRevoke: M.call().returns(M.boolean()),
|
|
89
|
+
},
|
|
90
|
+
extraMethods: {
|
|
91
|
+
selfRevoke() {
|
|
92
|
+
const { revoker } = this.facets;
|
|
93
|
+
return revoker.revoke();
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const makeCounterKit = x => {
|
|
100
|
+
const { up: upCounter, down: downCounter } = makeUnderlyingCounterKit(x);
|
|
101
|
+
const { revocable: revocableUpCounter } =
|
|
102
|
+
makeRevocableUpCounterKit(upCounter);
|
|
103
|
+
return harden({
|
|
104
|
+
up: revocableUpCounter,
|
|
105
|
+
down: downCounter,
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const { up: upCounter, down: downCounter } = makeCounterKit(3);
|
|
110
|
+
t.is(upCounter.incr(5), 8);
|
|
111
|
+
t.is(downCounter.decr(), 7);
|
|
112
|
+
t.is(upCounter.selfRevoke(), true);
|
|
113
|
+
t.throws(() => upCounter.incr(3), {
|
|
114
|
+
message: '"UpCounter_caretaker" revoked',
|
|
115
|
+
});
|
|
116
|
+
t.is(downCounter.decr(), 6);
|
|
117
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zone-helpers.d.ts","sourceRoot":"","sources":["zone-helpers.js"],"names":[],"mappings":""}
|
package/zone-helpers.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src/prepare-revocable.js';
|