@agoric/base-zone 0.1.1-u20.0 → 0.1.1-u21.0.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 +4 -1
- package/package.json +8 -8
- package/src/prepare-attenuator.d.ts +31 -0
- package/src/prepare-attenuator.d.ts.map +1 -0
- package/src/prepare-attenuator.js +170 -0
- package/src/prepare-revocable.d.ts +8 -5
- package/src/prepare-revocable.d.ts.map +1 -1
- package/src/prepare-revocable.js +40 -49
- package/test/prepare-attenuator.test.js +110 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/zone-helpers.d.ts +1 -0
- package/zone-helpers.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,15 @@
|
|
|
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.1.1-
|
|
6
|
+
### 0.1.1-u21.0 (2025-06-19)
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
### Features
|
|
10
10
|
|
|
11
11
|
* **base-zone:** add `zone.watchPromise` ([9ce80d0](https://github.com/Agoric/agoric-sdk/commit/9ce80d06c0a56471d2da9f372b0b2d93d31d159a))
|
|
12
12
|
* **base-zone:** alt revocable api using amplifier ([#8977](https://github.com/Agoric/agoric-sdk/issues/8977)) ([5cdf6e3](https://github.com/Agoric/agoric-sdk/commit/5cdf6e3a8b4fbb5cb8e276e6efeec65d9c3d6623))
|
|
13
|
+
* **base-zone:** attenuator as simplified revocable ([#11314](https://github.com/Agoric/agoric-sdk/issues/11314)) ([c962ba0](https://github.com/Agoric/agoric-sdk/commit/c962ba0771022947027b9bd76339a3ab21406b20))
|
|
14
|
+
* **base-zone:** expose `makeRevocableKit` for pure Exos ([b0b2af0](https://github.com/Agoric/agoric-sdk/commit/b0b2af0a7b5f8402abf836e126a9d7d758fed7dc))
|
|
13
15
|
* **base-zone:** new package ([b7bc677](https://github.com/Agoric/agoric-sdk/commit/b7bc677238eee5969ac0a95dc066434ef676216e))
|
|
14
16
|
* **types:** generic zone.mapStore ([a9b055d](https://github.com/Agoric/agoric-sdk/commit/a9b055dcab34b9c9b136dd430e1e2251d80c5039))
|
|
15
17
|
* **watchUtils:** handle non-storables ([8c27c67](https://github.com/Agoric/agoric-sdk/commit/8c27c6725ba7ef4b71d3ab0ccfdbddd755bcd926))
|
|
@@ -18,3 +20,4 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
|
|
18
20
|
### Bug Fixes
|
|
19
21
|
|
|
20
22
|
* **base-zone,zone:** import `isPassable` from @endo/pass-style ([#9230](https://github.com/Agoric/agoric-sdk/issues/9230)) ([fbd8633](https://github.com/Agoric/agoric-sdk/commit/fbd8633ae9f8420a589dd9bc32925418f2dde060))
|
|
23
|
+
* **base-zone:** Oops. Forgot to export `prepareAttenuatorMaker` ([#11315](https://github.com/Agoric/agoric-sdk/issues/11315)) ([af493a9](https://github.com/Agoric/agoric-sdk/commit/af493a9026387fc69cbfd57486f00fd33608594d)), closes [#11314](https://github.com/Agoric/agoric-sdk/issues/11314) [#11314](https://github.com/Agoric/agoric-sdk/issues/11314)
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/base-zone",
|
|
3
|
-
"version": "0.1.1-
|
|
3
|
+
"version": "0.1.1-u21.0.1",
|
|
4
4
|
"description": "Allocation zone abstraction library and heap implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": "https://github.com/Agoric/agoric-sdk",
|
|
7
7
|
"main": "./src/index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "exit 0",
|
|
10
|
-
"prepack": "tsc --build tsconfig.build.json",
|
|
10
|
+
"prepack": "yarn run -T tsc --build tsconfig.build.json",
|
|
11
11
|
"postpack": "git clean -f '*.d.ts*' '*.tsbuildinfo'",
|
|
12
12
|
"test": "ava",
|
|
13
13
|
"test:c8": "c8 --all $C8_OPTIONS ava",
|
|
14
14
|
"test:xs": "exit 0",
|
|
15
15
|
"lint-fix": "yarn lint:eslint --fix",
|
|
16
|
-
"lint": "run-s --continue-on-error lint:*",
|
|
17
|
-
"lint:types": "tsc",
|
|
18
|
-
"lint:eslint": "eslint ."
|
|
16
|
+
"lint": "yarn run -T run-s --continue-on-error 'lint:*'",
|
|
17
|
+
"lint:types": "yarn run -T tsc",
|
|
18
|
+
"lint:eslint": "yarn run -T eslint ."
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
21
|
".": "./src/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"author": "Agoric",
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agoric/store": "
|
|
30
|
+
"@agoric/store": "0.9.3-u21.0.1",
|
|
31
31
|
"@endo/common": "^1.2.10",
|
|
32
32
|
"@endo/errors": "^1.2.10",
|
|
33
33
|
"@endo/exo": "^1.5.9",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
|
-
"node": "^
|
|
47
|
+
"node": "^20.9 || ^22.11"
|
|
48
48
|
},
|
|
49
49
|
"ava": {
|
|
50
50
|
"files": [
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"typeCoverage": {
|
|
60
60
|
"atLeast": 91.4
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "16519b2de1eb2afda2b4ec866f55eadd4bb18223"
|
|
63
63
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function wrapperMethods(wrapperKindName: string, uMethodNames: PropertyKey[], extraMethods?: Record<PropertyKey, (...args: any[]) => any>): {
|
|
2
|
+
[x: string]: ((...args: any[]) => any) | ((...args: any[]) => any);
|
|
3
|
+
};
|
|
4
|
+
export function prepareAttenuatorMaker<U = any>(zone: import("@agoric/base-zone").Zone, uKindName: string, uMethodNames: (keyof U)[], options?: AttenuatorOptions<U>): MakeAttenuator;
|
|
5
|
+
export function attenuateOne<U extends unknown>(underlying: U, uMethodNames: (keyof U)[], options?: AttenuatorOptions<U>): Partial<U>;
|
|
6
|
+
export type MakeAttenuator<U = any> = (underlying: U) => Partial<U>;
|
|
7
|
+
export type AttenuatorThis<U = any> = {
|
|
8
|
+
self: Partial<U>;
|
|
9
|
+
state: {
|
|
10
|
+
underlying: U;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type AttenuatorOptions<U = any> = {
|
|
14
|
+
/**
|
|
15
|
+
* The `interfaceName` of the underlying interface guard.
|
|
16
|
+
* Defaults to the `uKindName`.
|
|
17
|
+
*/
|
|
18
|
+
uInterfaceName?: string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* For guarding the `extraMethods`, if you include them below. These appear
|
|
21
|
+
* only on the synthesized interface guard for the attenuator, and
|
|
22
|
+
* do not necessarily correspond to any method of the underlying.
|
|
23
|
+
*/
|
|
24
|
+
extraMethodGuards?: Record<PropertyKey, import("@endo/patterns").MethodGuard> | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Extra methods adding behavior only to the attenuator, and
|
|
27
|
+
* do not necessarily correspond to any methods of the underlying.
|
|
28
|
+
*/
|
|
29
|
+
extraMethods?: Record<PropertyKey, (this: AttenuatorThis<U>, ...args: any[]) => any> | undefined;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=prepare-attenuator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare-attenuator.d.ts","sourceRoot":"","sources":["prepare-attenuator.js"],"names":[],"mappings":"AA2BO,gDAJI,MAAM,gBACN,WAAW,EAAE,iBACb,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;4BAAd,GAAG,EAAE,KAAK,GAAG;EA6BlD;AAoDG,uCAVO,CAAC,cACJ,OAAO,mBAAmB,EAAE,IAAI,aAChC,MAAM,gBAEN,CAAC,MAAM,CAAC,CAAC,EAAE,YAGX,iBAAiB,CAAC,CAAC,CAAC,GAClB,cAAc,CA8B1B;AAqBM,6BANY,CAAC,8BACT,CAAC,gBACD,CAAC,MAAM,CAAC,CAAC,EAAE,YACX,iBAAiB,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,CAAC,CAAC,CAetB;2BA9Ga,CAAC,uBAEJ,CAAC,KACC,OAAO,CAAC,CAAC,CAAC;2BAIT,CAAC;UAED,OAAO,CAAC,CAAC,CAAC;WACV;QAAE,UAAU,EAAE,CAAC,CAAA;KAAE;;8BAIjB,CAAC;;;;;;;;;;;;;;;;8CAcH,cAAc,CAAC,CAAC,CAAC,WAAW,GAAG,EAAE,KAAK,GAAG"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Fail, q } from '@endo/errors';
|
|
2
|
+
import { fromUniqueEntries } from '@endo/common/from-unique-entries.js';
|
|
3
|
+
import { M } from '@endo/patterns';
|
|
4
|
+
import { makeHeapZone } from './heap.js';
|
|
5
|
+
|
|
6
|
+
// This attenuator implementation is just a simplification of the
|
|
7
|
+
// revocable kit implementation in prepare-revocable.js. The revocable kit
|
|
8
|
+
// provides both attenuation and revocation, since we can have both for the
|
|
9
|
+
// price of just one level of indirection. But if you don't need revocation,
|
|
10
|
+
// the revocable kit has more API complexity than you need.
|
|
11
|
+
//
|
|
12
|
+
// We could have built `prepareAttenuatorMaker` as a trivial wrapper around
|
|
13
|
+
// `prepareRevocableMakerKit`. But then we'd have the weird artifact that the
|
|
14
|
+
// `extraMethods` would see a `{ state, facets: { revocable, revoker }}` context
|
|
15
|
+
// rather than a `{ state, self }` context. So instead, we copied and edited it
|
|
16
|
+
// down. Please co-maintain these two modules.
|
|
17
|
+
|
|
18
|
+
// Because the attenuator just uses an exo class rather than a class kit,
|
|
19
|
+
// it cannot support the privilege separation of a distinct revoker facet.
|
|
20
|
+
// But it can still support `selfRevoke` as a separate `extraMethod`, as shown
|
|
21
|
+
// in a testcase at `prepare-attenuator.test.js`
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} wrapperKindName
|
|
25
|
+
* @param {PropertyKey[]} uMethodNames
|
|
26
|
+
* @param {Record<PropertyKey, (...args: any[]) => any>} [extraMethods]
|
|
27
|
+
*/
|
|
28
|
+
export const wrapperMethods = (
|
|
29
|
+
wrapperKindName,
|
|
30
|
+
uMethodNames,
|
|
31
|
+
extraMethods = {},
|
|
32
|
+
) =>
|
|
33
|
+
harden({
|
|
34
|
+
...fromUniqueEntries(
|
|
35
|
+
uMethodNames.map(name => [
|
|
36
|
+
name,
|
|
37
|
+
{
|
|
38
|
+
// Use concise method syntax for exo methods
|
|
39
|
+
[name](...args) {
|
|
40
|
+
// @ts-expect-error normal exo-this typing confusion
|
|
41
|
+
const { underlying } = this.state;
|
|
42
|
+
|
|
43
|
+
// Because attenuators still support someone adding `selfRevoke`
|
|
44
|
+
// as an `extraMethod`, this test is still useful. See the
|
|
45
|
+
// testcase in `prepare-attenuator.test.js`.
|
|
46
|
+
underlying !== undefined || Fail`${q(wrapperKindName)} revoked`;
|
|
47
|
+
|
|
48
|
+
return underlying[name](...args);
|
|
49
|
+
},
|
|
50
|
+
// @ts-expect-error using possible symbol as index type
|
|
51
|
+
}[name],
|
|
52
|
+
]),
|
|
53
|
+
),
|
|
54
|
+
...extraMethods,
|
|
55
|
+
});
|
|
56
|
+
harden(wrapperMethods);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @template [U=any]
|
|
60
|
+
* @callback MakeAttenuator
|
|
61
|
+
* @param {U} underlying
|
|
62
|
+
* @returns {Partial<U>}
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @template [U=any]
|
|
67
|
+
* @typedef {object} AttenuatorThis
|
|
68
|
+
* @property {Partial<U>} self
|
|
69
|
+
* @property {{ underlying: U }} state
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @template [U=any]
|
|
74
|
+
* @typedef {object} AttenuatorOptions
|
|
75
|
+
* @property {string} [uInterfaceName]
|
|
76
|
+
* The `interfaceName` of the underlying interface guard.
|
|
77
|
+
* Defaults to the `uKindName`.
|
|
78
|
+
* @property {Record<
|
|
79
|
+
* PropertyKey,
|
|
80
|
+
* import('@endo/patterns').MethodGuard
|
|
81
|
+
* >} [extraMethodGuards]
|
|
82
|
+
* For guarding the `extraMethods`, if you include them below. These appear
|
|
83
|
+
* only on the synthesized interface guard for the attenuator, and
|
|
84
|
+
* do not necessarily correspond to any method of the underlying.
|
|
85
|
+
* @property {Record<
|
|
86
|
+
* PropertyKey,
|
|
87
|
+
* (this: AttenuatorThis<U>, ...args: any[]) => any
|
|
88
|
+
* >} [extraMethods]
|
|
89
|
+
* Extra methods adding behavior only to the attenuator, and
|
|
90
|
+
* do not necessarily correspond to any methods of the underlying.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Make an exo class for wrapping an underlying exo class,
|
|
95
|
+
* where the wrapper is an attenuator of the underlying.
|
|
96
|
+
*
|
|
97
|
+
* @template [U=any]
|
|
98
|
+
* @param {import('@agoric/base-zone').Zone} zone
|
|
99
|
+
* @param {string} uKindName
|
|
100
|
+
* The `kindName` of the underlying exo class
|
|
101
|
+
* @param {(keyof U)[]} uMethodNames
|
|
102
|
+
* The method names of the underlying exo class that should be represented
|
|
103
|
+
* by transparently-forwarding methods of the attenuator.
|
|
104
|
+
* @param {AttenuatorOptions<U>} [options]
|
|
105
|
+
* @returns {MakeAttenuator}
|
|
106
|
+
*/
|
|
107
|
+
export const prepareAttenuatorMaker = (
|
|
108
|
+
zone,
|
|
109
|
+
uKindName,
|
|
110
|
+
uMethodNames,
|
|
111
|
+
options = {},
|
|
112
|
+
) => {
|
|
113
|
+
const {
|
|
114
|
+
uInterfaceName = uKindName,
|
|
115
|
+
extraMethodGuards = {},
|
|
116
|
+
extraMethods = {},
|
|
117
|
+
} = options;
|
|
118
|
+
const AttenuatorI = M.interface(
|
|
119
|
+
`${uInterfaceName}_attenuator`,
|
|
120
|
+
extraMethodGuards,
|
|
121
|
+
{ defaultGuards: 'raw' },
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const attenuatorKindName = `${uKindName}_attenuator`;
|
|
125
|
+
|
|
126
|
+
return zone.exoClass(
|
|
127
|
+
attenuatorKindName,
|
|
128
|
+
AttenuatorI,
|
|
129
|
+
underlying => ({ underlying }),
|
|
130
|
+
wrapperMethods(attenuatorKindName, uMethodNames, extraMethods),
|
|
131
|
+
{
|
|
132
|
+
stateShape: { underlying: M.opt(M.remotable('underlying')) },
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
harden(prepareAttenuatorMaker);
|
|
137
|
+
|
|
138
|
+
// cf. https://endojs.github.io/endo/functions/_endo_pass_style.Remotable.html
|
|
139
|
+
const RemotablePrefixRE = /^(Alleged: |DebugName: )/;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A convenience above `prepareAttenuatorMaker` for doing a singleton
|
|
143
|
+
* ephemeral attenuator, i.e., a new attenuator instance (and hidden class)
|
|
144
|
+
* that is allocated in the heap zone, and therefore ephemeral. The underlying
|
|
145
|
+
* can be durable or not, with no conflict with the attenuator being
|
|
146
|
+
* ephemeral. The price of this convenience is the allocation of the hidden
|
|
147
|
+
* class and wrapper methods per `attenuateOne` call, i.e., per attenuator
|
|
148
|
+
* instance.
|
|
149
|
+
*
|
|
150
|
+
* @template {any} U
|
|
151
|
+
* @param {U} underlying
|
|
152
|
+
* @param {(keyof U)[]} uMethodNames
|
|
153
|
+
* @param {AttenuatorOptions<U>} [options]
|
|
154
|
+
* @returns {Partial<U>}
|
|
155
|
+
*/
|
|
156
|
+
export const attenuateOne = (underlying, uMethodNames, options = undefined) => {
|
|
157
|
+
const heapZone = makeHeapZone();
|
|
158
|
+
const uKindName = (underlying[Symbol.toStringTag] || 'Underlying').replace(
|
|
159
|
+
RemotablePrefixRE,
|
|
160
|
+
'',
|
|
161
|
+
);
|
|
162
|
+
const makeAttenuator = prepareAttenuatorMaker(
|
|
163
|
+
heapZone,
|
|
164
|
+
uKindName,
|
|
165
|
+
uMethodNames,
|
|
166
|
+
options,
|
|
167
|
+
);
|
|
168
|
+
return makeAttenuator(underlying);
|
|
169
|
+
};
|
|
170
|
+
harden(attenuateOne);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export function prepareRevocableMakerKit<U = any>(zone: import("@agoric/base-zone").Zone, uKindName: string, uMethodNames:
|
|
1
|
+
export function prepareRevocableMakerKit<U = any>(zone: import("@agoric/base-zone").Zone, uKindName: string, uMethodNames: PropertyKey[], options?: RevocableKitOptions<U>): RevocableMakerKit<U>;
|
|
2
|
+
export type MakeRevocable<U = any> = (underlying: U) => Partial<U>;
|
|
3
|
+
export type MakeRevocableKit<U = any> = (underlying: U) => RevocableKit<U>;
|
|
2
4
|
export type RevocableMakerKit<U = any> = {
|
|
3
5
|
revoke: (revocable: U) => boolean;
|
|
4
6
|
/**
|
|
5
7
|
* Forwards to the underlying exo object, until revoked
|
|
6
8
|
*/
|
|
7
|
-
makeRevocable:
|
|
9
|
+
makeRevocable: MakeRevocable<U>;
|
|
10
|
+
makeRevocableKit: MakeRevocableKit<U>;
|
|
8
11
|
};
|
|
9
12
|
export type RevokerFacet = {
|
|
10
13
|
revoke: () => boolean;
|
|
@@ -14,7 +17,7 @@ export type RevocableKit<U = any> = {
|
|
|
14
17
|
/**
|
|
15
18
|
* Forwards to the underlying exo object, until revoked
|
|
16
19
|
*/
|
|
17
|
-
revocable: U
|
|
20
|
+
revocable: Partial<U>;
|
|
18
21
|
};
|
|
19
22
|
export type RevocableKitThis<U = any> = {
|
|
20
23
|
facets: RevocableKit<U>;
|
|
@@ -33,11 +36,11 @@ export type RevocableKitOptions<U = any> = {
|
|
|
33
36
|
* only on the synthesized interface guard for the revocable caretaker, and
|
|
34
37
|
* do not necessarily correspond to any method of the underlying.
|
|
35
38
|
*/
|
|
36
|
-
extraMethodGuards?: Record<
|
|
39
|
+
extraMethodGuards?: Record<PropertyKey, import("@endo/patterns").MethodGuard> | undefined;
|
|
37
40
|
/**
|
|
38
41
|
* Extra methods adding behavior only to the revocable caretaker, and
|
|
39
42
|
* do not necessarily correspond to any methods of the underlying.
|
|
40
43
|
*/
|
|
41
|
-
extraMethods?: Record<
|
|
44
|
+
extraMethods?: Record<PropertyKey, (this: RevocableKitThis<U>, ...args: any[]) => any> | undefined;
|
|
42
45
|
};
|
|
43
46
|
//# sourceMappingURL=prepare-revocable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepare-revocable.d.ts","sourceRoot":"","sources":["prepare-revocable.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prepare-revocable.d.ts","sourceRoot":"","sources":["prepare-revocable.js"],"names":[],"mappings":"AAyFO,yCAVO,CAAC,cACJ,OAAO,mBAAmB,EAAE,IAAI,aAChC,MAAM,gBAEN,WAAW,EAAE,YAGb,mBAAmB,CAAC,CAAC,CAAC,GACpB,iBAAiB,CAAC,CAAC,CAAC,CA4EhC;0BAvJa,CAAC,uBAEJ,CAAC,KACC,OAAO,CAAC,CAAC,CAAC;6BAIT,CAAC,uBAEJ,CAAC,KACC,YAAY,CAAC,CAAC,CAAC;8BAId,CAAC;YAED,CAAC,SAAS,EAAE,CAAC,KAAK,OAAO;;;;mBACzB,aAAa,CAAC,CAAC,CAAC;sBAEhB,gBAAgB,CAAC,CAAC,CAAC;;;YAKnB,MAAM,OAAO;;yBAIb,CAAC;aAED,YAAY;;;;eACZ,OAAO,CAAC,CAAC,CAAC;;6BAKV,CAAC;YAED,YAAY,CAAC,CAAC,CAAC;WACf;QAAE,UAAU,EAAE,CAAC,CAAA;KAAE;;gCAIjB,CAAC;;;;;;;;;;;;;;;;8CAcH,gBAAgB,CAAC,CAAC,CAAC,WAAW,GAAG,EAAE,KAAK,GAAG"}
|
package/src/prepare-revocable.js
CHANGED
|
@@ -1,15 +1,35 @@
|
|
|
1
|
-
import { Fail, q } from '@endo/errors';
|
|
2
|
-
import { fromUniqueEntries } from '@endo/common/from-unique-entries.js';
|
|
3
1
|
import { M } from '@endo/patterns';
|
|
2
|
+
import { wrapperMethods } from './prepare-attenuator.js';
|
|
4
3
|
|
|
5
4
|
/** @import {Amplify} from '@endo/exo'; */
|
|
6
5
|
|
|
6
|
+
// This revocable kit implementation provides for both attenuation and
|
|
7
|
+
// revocation, since both can be provided at the cost of one level of
|
|
8
|
+
// indirection. But if you just need attenuation without revocation, better to
|
|
9
|
+
// use the attenuator implementation in prepare-attenuator.js.
|
|
10
|
+
// Please co-maintain these two modules.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @template [U=any]
|
|
14
|
+
* @callback MakeRevocable
|
|
15
|
+
* @param {U} underlying
|
|
16
|
+
* @returns {Partial<U>}
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @template [U=any]
|
|
21
|
+
* @callback MakeRevocableKit
|
|
22
|
+
* @param {U} underlying
|
|
23
|
+
* @returns {RevocableKit<U>}
|
|
24
|
+
*/
|
|
25
|
+
|
|
7
26
|
/**
|
|
8
27
|
* @template [U=any]
|
|
9
28
|
* @typedef {object} RevocableMakerKit
|
|
10
29
|
* @property {(revocable: U) => boolean} revoke
|
|
11
|
-
* @property {
|
|
30
|
+
* @property {MakeRevocable<U>} makeRevocable
|
|
12
31
|
* Forwards to the underlying exo object, until revoked
|
|
32
|
+
* @property {MakeRevocableKit<U>} makeRevocableKit
|
|
13
33
|
*/
|
|
14
34
|
|
|
15
35
|
/**
|
|
@@ -21,7 +41,7 @@ import { M } from '@endo/patterns';
|
|
|
21
41
|
* @template [U=any]
|
|
22
42
|
* @typedef {object} RevocableKit
|
|
23
43
|
* @property {RevokerFacet} revoker
|
|
24
|
-
* @property {U} revocable
|
|
44
|
+
* @property {Partial<U>} revocable
|
|
25
45
|
* Forwards to the underlying exo object, until revoked
|
|
26
46
|
*/
|
|
27
47
|
|
|
@@ -39,14 +59,14 @@ import { M } from '@endo/patterns';
|
|
|
39
59
|
* The `interfaceName` of the underlying interface guard.
|
|
40
60
|
* Defaults to the `uKindName`.
|
|
41
61
|
* @property {Record<
|
|
42
|
-
*
|
|
62
|
+
* PropertyKey,
|
|
43
63
|
* import('@endo/patterns').MethodGuard
|
|
44
64
|
* >} [extraMethodGuards]
|
|
45
65
|
* For guarding the `extraMethods`, if you include them below. These appear
|
|
46
66
|
* only on the synthesized interface guard for the revocable caretaker, and
|
|
47
67
|
* do not necessarily correspond to any method of the underlying.
|
|
48
68
|
* @property {Record<
|
|
49
|
-
*
|
|
69
|
+
* PropertyKey,
|
|
50
70
|
* (this: RevocableKitThis<U>, ...args: any[]) => any
|
|
51
71
|
* >} [extraMethods]
|
|
52
72
|
* Extra methods adding behavior only to the revocable caretaker, and
|
|
@@ -61,10 +81,10 @@ import { M } from '@endo/patterns';
|
|
|
61
81
|
* @param {import('@agoric/base-zone').Zone} zone
|
|
62
82
|
* @param {string} uKindName
|
|
63
83
|
* The `kindName` of the underlying exo class
|
|
64
|
-
* @param {
|
|
84
|
+
* @param {PropertyKey[]} uMethodNames
|
|
65
85
|
* The method names of the underlying exo class that should be represented
|
|
66
86
|
* by transparently-forwarding methods of the revocable caretaker.
|
|
67
|
-
* @param {RevocableKitOptions} [options]
|
|
87
|
+
* @param {RevocableKitOptions<U>} [options]
|
|
68
88
|
* @returns {RevocableMakerKit<U>}
|
|
69
89
|
*/
|
|
70
90
|
export const prepareRevocableMakerKit = (
|
|
@@ -75,22 +95,16 @@ export const prepareRevocableMakerKit = (
|
|
|
75
95
|
) => {
|
|
76
96
|
const {
|
|
77
97
|
uInterfaceName = uKindName,
|
|
78
|
-
extraMethodGuards =
|
|
79
|
-
extraMethods =
|
|
98
|
+
extraMethodGuards = {},
|
|
99
|
+
extraMethods = {},
|
|
80
100
|
} = options;
|
|
81
101
|
const RevocableIKit = harden({
|
|
82
102
|
revoker: M.interface(`${uInterfaceName}_revoker`, {
|
|
83
103
|
revoke: M.call().returns(M.boolean()),
|
|
84
104
|
}),
|
|
85
|
-
revocable: M.interface(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
...extraMethodGuards,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
defaultGuards: 'raw',
|
|
92
|
-
},
|
|
93
|
-
),
|
|
105
|
+
revocable: M.interface(`${uInterfaceName}_revocable`, extraMethodGuards, {
|
|
106
|
+
defaultGuards: 'raw',
|
|
107
|
+
}),
|
|
94
108
|
});
|
|
95
109
|
|
|
96
110
|
const revocableKindName = `${uKindName}_caretaker`;
|
|
@@ -101,9 +115,7 @@ export const prepareRevocableMakerKit = (
|
|
|
101
115
|
const makeRevocableKit = zone.exoClassKit(
|
|
102
116
|
revocableKindName,
|
|
103
117
|
RevocableIKit,
|
|
104
|
-
underlying => ({
|
|
105
|
-
underlying,
|
|
106
|
-
}),
|
|
118
|
+
underlying => ({ underlying }),
|
|
107
119
|
{
|
|
108
120
|
revoker: {
|
|
109
121
|
revoke() {
|
|
@@ -115,30 +127,10 @@ export const prepareRevocableMakerKit = (
|
|
|
115
127
|
return true;
|
|
116
128
|
},
|
|
117
129
|
},
|
|
118
|
-
revocable:
|
|
119
|
-
...fromUniqueEntries(
|
|
120
|
-
uMethodNames.map(name => [
|
|
121
|
-
name,
|
|
122
|
-
{
|
|
123
|
-
// Use concise method syntax for exo methods
|
|
124
|
-
[name](...args) {
|
|
125
|
-
// @ts-expect-error normal exo-this typing confusion
|
|
126
|
-
const { underlying } = this.state;
|
|
127
|
-
underlying !== undefined ||
|
|
128
|
-
Fail`${q(revocableKindName)} revoked`;
|
|
129
|
-
return underlying[name](...args);
|
|
130
|
-
},
|
|
131
|
-
// @ts-expect-error using possible symbol as index type
|
|
132
|
-
}[name],
|
|
133
|
-
]),
|
|
134
|
-
),
|
|
135
|
-
...extraMethods,
|
|
136
|
-
},
|
|
130
|
+
revocable: wrapperMethods(revocableKindName, uMethodNames, extraMethods),
|
|
137
131
|
},
|
|
138
132
|
{
|
|
139
|
-
stateShape: {
|
|
140
|
-
underlying: M.opt(M.remotable('underlying')),
|
|
141
|
-
},
|
|
133
|
+
stateShape: { underlying: M.opt(M.remotable('underlying')) },
|
|
142
134
|
receiveAmplifier: amp => {
|
|
143
135
|
amplifier = amp;
|
|
144
136
|
},
|
|
@@ -146,12 +138,9 @@ export const prepareRevocableMakerKit = (
|
|
|
146
138
|
);
|
|
147
139
|
|
|
148
140
|
/**
|
|
149
|
-
* @
|
|
150
|
-
* @returns {U}
|
|
141
|
+
* @type {MakeRevocable}
|
|
151
142
|
*/
|
|
152
|
-
const makeRevocable = underlying =>
|
|
153
|
-
// @ts-expect-error some confusion about UU vs Guarded<U> I think
|
|
154
|
-
makeRevocableKit(underlying).revocable;
|
|
143
|
+
const makeRevocable = underlying => makeRevocableKit(underlying).revocable;
|
|
155
144
|
|
|
156
145
|
/**
|
|
157
146
|
* @param {U} revocable
|
|
@@ -169,6 +158,8 @@ export const prepareRevocableMakerKit = (
|
|
|
169
158
|
return harden({
|
|
170
159
|
revoke,
|
|
171
160
|
makeRevocable,
|
|
161
|
+
/** @type {MakeRevocableKit} */
|
|
162
|
+
makeRevocableKit,
|
|
172
163
|
});
|
|
173
164
|
};
|
|
174
165
|
harden(prepareRevocableMakerKit);
|
|
@@ -0,0 +1,110 @@
|
|
|
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 { prepareAttenuatorMaker } from '../src/prepare-attenuator.js';
|
|
9
|
+
|
|
10
|
+
const CounterI = M.interface('Counter', {
|
|
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
|
+
decr: M.call()
|
|
16
|
+
// TODO M.number() should not be needed to get a better error message
|
|
17
|
+
.optional(M.and(M.number(), M.gte(0)))
|
|
18
|
+
.returns(M.number()),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('test attenuate defineVirtualExoClass', t => {
|
|
22
|
+
const zone = makeHeapZone('rootZone');
|
|
23
|
+
|
|
24
|
+
const makeUnderlyingCounter = zone.exoClass(
|
|
25
|
+
'Counter',
|
|
26
|
+
CounterI,
|
|
27
|
+
/** @param {number} [x] */
|
|
28
|
+
(x = 0) => ({ x }),
|
|
29
|
+
{
|
|
30
|
+
incr(y = 1) {
|
|
31
|
+
const { state } = this;
|
|
32
|
+
state.x += y;
|
|
33
|
+
return state.x;
|
|
34
|
+
},
|
|
35
|
+
decr(y = 1) {
|
|
36
|
+
const { state } = this;
|
|
37
|
+
state.x -= y;
|
|
38
|
+
return state.x;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const makeUpAttenuator = prepareAttenuatorMaker(zone, 'UpCounter', ['incr']);
|
|
44
|
+
|
|
45
|
+
const makeUpCounter = x => makeUpAttenuator(makeUnderlyingCounter(x));
|
|
46
|
+
|
|
47
|
+
const upCounter = makeUpCounter(3);
|
|
48
|
+
t.is(upCounter.incr(5), 8);
|
|
49
|
+
t.throws(() => upCounter.decr(1), {
|
|
50
|
+
message: 'upCounter.decr is not a function',
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('test revoke defineVirtualExoClassKit', t => {
|
|
55
|
+
const zone = makeHeapZone('rootZone');
|
|
56
|
+
|
|
57
|
+
const makeUnderlyingCounter = zone.exoClass(
|
|
58
|
+
'Counter',
|
|
59
|
+
CounterI,
|
|
60
|
+
/** @param {number} [x] */
|
|
61
|
+
(x = 0) => ({ x }),
|
|
62
|
+
{
|
|
63
|
+
incr(y = 1) {
|
|
64
|
+
const { state } = this;
|
|
65
|
+
state.x += y;
|
|
66
|
+
return state.x;
|
|
67
|
+
},
|
|
68
|
+
decr(y = 1) {
|
|
69
|
+
const { state } = this;
|
|
70
|
+
state.x -= y;
|
|
71
|
+
return state.x;
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const makeDownAttenuator = prepareAttenuatorMaker(
|
|
77
|
+
zone,
|
|
78
|
+
'DownCounter',
|
|
79
|
+
['decr'],
|
|
80
|
+
{
|
|
81
|
+
extraMethodGuards: {
|
|
82
|
+
selfRevoke: M.call().returns(M.boolean()),
|
|
83
|
+
},
|
|
84
|
+
extraMethods: {
|
|
85
|
+
selfRevoke() {
|
|
86
|
+
const { state } = this;
|
|
87
|
+
if (state.underlying === undefined) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
// @ts-expect-error Setting this to `undefined` is purposely outside
|
|
91
|
+
// the type system.
|
|
92
|
+
state.underlying = undefined;
|
|
93
|
+
return true;
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const makeDownCounter = x => makeDownAttenuator(makeUnderlyingCounter(x));
|
|
100
|
+
|
|
101
|
+
const downCounter = makeDownCounter(3);
|
|
102
|
+
t.throws(() => downCounter.incr(1), {
|
|
103
|
+
message: 'downCounter.incr is not a function',
|
|
104
|
+
});
|
|
105
|
+
t.is(downCounter.decr(), 2);
|
|
106
|
+
t.is(downCounter.selfRevoke(), true);
|
|
107
|
+
t.throws(() => downCounter.decr(3), {
|
|
108
|
+
message: '"DownCounter_attenuator" revoked',
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./heap.js","./zone-helpers.js","./src/heap.js","./src/index.js","./src/is-passable.js","./src/keys.js","./src/make-once.js","./src/prepare-revocable.js","./src/types.js","./src/watch-promise.js"],"version":"5.8.
|
|
1
|
+
{"root":["./heap.js","./zone-helpers.js","./src/heap.js","./src/index.js","./src/is-passable.js","./src/keys.js","./src/make-once.js","./src/prepare-attenuator.js","./src/prepare-revocable.js","./src/types.js","./src/watch-promise.js"],"version":"5.8.3"}
|
package/zone-helpers.d.ts
CHANGED
package/zone-helpers.js
CHANGED