@peers-app/peers-sdk 0.25.2 → 0.25.4
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/dist/contracts/__tests__/contract-resolvers.test.js +35 -2
- package/dist/contracts/__tests__/host-contract-consumer.test.d.ts +1 -0
- package/dist/contracts/__tests__/host-contract-consumer.test.js +163 -0
- package/dist/contracts/__tests__/system-contracts.test.js +14 -0
- package/dist/contracts/contract-resolvers.d.ts +14 -2
- package/dist/contracts/contract-resolvers.js +69 -6
- package/dist/contracts/host-contract-consumer.d.ts +40 -0
- package/dist/contracts/host-contract-consumer.js +105 -0
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.js +7 -2
- package/dist/contracts/system/identity-contract.d.ts +43 -0
- package/dist/contracts/system/identity-contract.js +133 -0
- package/dist/contracts/system/index.d.ts +2 -0
- package/dist/contracts/system/index.js +5 -1
- package/dist/contracts/types.d.ts +6 -0
- package/dist/data/devices.d.ts +4 -4
- package/dist/data/group-share.js +0 -38
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.js +1 -0
- package/dist/data/invites.d.ts +40 -40
- package/dist/data/invites.js +4 -2
- package/dist/data/peers-services-registration.pvar.d.ts +37 -0
- package/dist/data/peers-services-registration.pvar.js +57 -0
- package/dist/data/tool-access-approvals.d.ts +3 -3
- package/dist/device/connection.d.ts +1 -1
- package/dist/device/device.d.ts +1 -1
- package/dist/device-pairing/constants.d.ts +0 -2
- package/dist/device-pairing/constants.js +1 -3
- package/dist/device-pairing/types.d.ts +80 -80
- package/dist/invites/invite-messages.d.ts +12 -12
- package/dist/rpc-types.d.ts +0 -14
- package/dist/rpc-types.js +0 -13
- package/dist/system-ids.d.ts +17 -0
- package/dist/system-ids.js +18 -1
- package/package.json +1 -1
|
@@ -164,7 +164,11 @@ describe("Self-trusted contract caller authorization", () => {
|
|
|
164
164
|
})).rejects.toThrow("Permission denied");
|
|
165
165
|
router.dispose();
|
|
166
166
|
});
|
|
167
|
-
it(
|
|
167
|
+
it.each([
|
|
168
|
+
["Tool Access Control", system_ids_1.toolAccessControlContractId],
|
|
169
|
+
["Identity", system_ids_1.identityContractId],
|
|
170
|
+
["Invites", system_ids_1.invitesContractId],
|
|
171
|
+
])("rejects remote %s as local-only", async (_name, contractId) => {
|
|
168
172
|
const { remoteIdentity, user } = makeIdentity();
|
|
169
173
|
const records = new Map();
|
|
170
174
|
const { personalContext, userContext } = setupUserContext(remoteIdentity.userId, records);
|
|
@@ -177,7 +181,7 @@ describe("Self-trusted contract caller authorization", () => {
|
|
|
177
181
|
});
|
|
178
182
|
await expect(consumerTransport.emit(index_1.CONTRACT_REQUEST_CHANNEL, {
|
|
179
183
|
...call,
|
|
180
|
-
contractId
|
|
184
|
+
contractId,
|
|
181
185
|
member: "tool",
|
|
182
186
|
name: "approve",
|
|
183
187
|
operation: "run",
|
|
@@ -381,6 +385,35 @@ describe("local user-context contract routing", () => {
|
|
|
381
385
|
await expect(result).resolves.toBe(setup.userId);
|
|
382
386
|
router.dispose();
|
|
383
387
|
});
|
|
388
|
+
it("resolves a signed-out contract without waiting for a user context", async () => {
|
|
389
|
+
const contractId = (0, utils_1.newid)();
|
|
390
|
+
(0, index_1.registerContractResolver)(contractId, 1, () => ({
|
|
391
|
+
contractId,
|
|
392
|
+
version: 1,
|
|
393
|
+
tools: {
|
|
394
|
+
inspect: {
|
|
395
|
+
tool: {
|
|
396
|
+
toolId: (0, utils_1.newid)(),
|
|
397
|
+
name: "inspect",
|
|
398
|
+
usageDescription: "Report that no user was required.",
|
|
399
|
+
inputSchema: { type: "simple", fields: [] },
|
|
400
|
+
outputSchema: { type: "simple", fields: [] },
|
|
401
|
+
code: "",
|
|
402
|
+
packageId: (0, utils_1.newid)(),
|
|
403
|
+
accessLevel: 0,
|
|
404
|
+
},
|
|
405
|
+
toolFn: () => "signed-out",
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
}), { requiresUserContext: false });
|
|
409
|
+
const [consumerTransport, providerTransport] = (0, index_1.inProcessTransportPair)();
|
|
410
|
+
const router = (0, index_1.createLocalUserContextContractProviderRouter)(providerTransport, {
|
|
411
|
+
getUserContext: () => new Promise(() => undefined),
|
|
412
|
+
peekUserContext: () => undefined,
|
|
413
|
+
});
|
|
414
|
+
await expect(consumerTransport.emit(index_1.CONTRACT_REQUEST_CHANNEL, makeToolCall(contractId))).resolves.toBe("signed-out");
|
|
415
|
+
router.dispose();
|
|
416
|
+
});
|
|
384
417
|
it("routes known groups and rejects unknown contexts without creating them", async () => {
|
|
385
418
|
const setup = setupLocalRouter();
|
|
386
419
|
const router = (0, index_1.createLocalUserContextContractProviderRouter)(setup.providerTransport, {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const field_type_1 = require("../../types/field-type");
|
|
4
|
+
const contract_transport_1 = require("../contract-transport");
|
|
5
|
+
const index_1 = require("../index");
|
|
6
|
+
const definition = {
|
|
7
|
+
contractId: "hostconsumercontract00001",
|
|
8
|
+
version: 1,
|
|
9
|
+
devTag: "dev",
|
|
10
|
+
name: "Host consumer test",
|
|
11
|
+
description: "Exercises hostContractConsumer caching.",
|
|
12
|
+
tables: [],
|
|
13
|
+
tools: [
|
|
14
|
+
{
|
|
15
|
+
name: "ping",
|
|
16
|
+
usageDescription: "Echo the provider label.",
|
|
17
|
+
inputFields: [],
|
|
18
|
+
outputFields: [{ name: "label", type: field_type_1.FieldType.string, description: "Provider label." }],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
observables: [
|
|
22
|
+
{
|
|
23
|
+
name: "state",
|
|
24
|
+
description: "Snapshot withheld until the test releases it.",
|
|
25
|
+
valueType: field_type_1.FieldType.object,
|
|
26
|
+
writable: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
events: [],
|
|
30
|
+
};
|
|
31
|
+
function setupHost(label, opts = {}) {
|
|
32
|
+
const [consumerTransport, providerTransport] = (0, index_1.inProcessTransportPair)();
|
|
33
|
+
let releaseSnapshot;
|
|
34
|
+
const snapshot = opts.holdSnapshot
|
|
35
|
+
? new Promise((resolve) => {
|
|
36
|
+
releaseSnapshot = resolve;
|
|
37
|
+
})
|
|
38
|
+
: Promise.resolve({ phase: "idle" });
|
|
39
|
+
const calls = [];
|
|
40
|
+
providerTransport.on(contract_transport_1.CONTRACT_REQUEST_CHANNEL, (call) => {
|
|
41
|
+
calls.push(call);
|
|
42
|
+
if (call.member === "observable" && call.operation === "get") {
|
|
43
|
+
return snapshot;
|
|
44
|
+
}
|
|
45
|
+
if (call.member === "tool" && call.name === "ping") {
|
|
46
|
+
return { label };
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
transport: consumerTransport,
|
|
52
|
+
calls,
|
|
53
|
+
release: () => releaseSnapshot?.({ phase: "idle" }),
|
|
54
|
+
dispose: (0, index_1.registerHostContractTransport)(consumerTransport),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function waitForCall(host, match) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const started = Date.now();
|
|
60
|
+
const timer = setInterval(() => {
|
|
61
|
+
if (host.calls.some(match)) {
|
|
62
|
+
clearInterval(timer);
|
|
63
|
+
resolve();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (Date.now() - started > 1000) {
|
|
67
|
+
clearInterval(timer);
|
|
68
|
+
reject(new Error(`timed out waiting for a host call; saw ${JSON.stringify(host.calls)}`));
|
|
69
|
+
}
|
|
70
|
+
}, 5);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
describe("hostContractConsumer", () => {
|
|
74
|
+
const handles = [];
|
|
75
|
+
const hosts = [];
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
while (handles.length > 0) {
|
|
78
|
+
handles.pop()?.dispose();
|
|
79
|
+
}
|
|
80
|
+
while (hosts.length > 0) {
|
|
81
|
+
hosts.pop()?.dispose();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
function open() {
|
|
85
|
+
const handle = (0, index_1.hostContractConsumer)(definition, {
|
|
86
|
+
dataContextId: async () => "user-context",
|
|
87
|
+
});
|
|
88
|
+
handles.push(handle);
|
|
89
|
+
return handle;
|
|
90
|
+
}
|
|
91
|
+
it("waits for a transport, then returns the same consumer", async () => {
|
|
92
|
+
const handle = open();
|
|
93
|
+
const pending = handle.get();
|
|
94
|
+
const host = setupHost("first");
|
|
95
|
+
hosts.push(host);
|
|
96
|
+
const consumer = await pending;
|
|
97
|
+
expect(await handle.get()).toBe(consumer);
|
|
98
|
+
await expect(consumer.tools.ping()).resolves.toEqual({ label: "first" });
|
|
99
|
+
expect(host.calls).toContainEqual(expect.objectContaining({
|
|
100
|
+
member: "tool",
|
|
101
|
+
name: "ping",
|
|
102
|
+
dataContextId: "user-context",
|
|
103
|
+
}));
|
|
104
|
+
});
|
|
105
|
+
it("shares one in-flight load across concurrent callers", async () => {
|
|
106
|
+
const host = setupHost("shared");
|
|
107
|
+
hosts.push(host);
|
|
108
|
+
const handle = open();
|
|
109
|
+
const [first, second] = await Promise.all([handle.get(), handle.get()]);
|
|
110
|
+
expect(second).toBe(first);
|
|
111
|
+
const snapshots = host.calls.filter((call) => call.member === "observable" && call.operation === "get");
|
|
112
|
+
expect(snapshots).toHaveLength(1);
|
|
113
|
+
});
|
|
114
|
+
it("disposes the cached consumer when the transport is replaced", async () => {
|
|
115
|
+
const first = setupHost("first");
|
|
116
|
+
hosts.push(first);
|
|
117
|
+
const handle = open();
|
|
118
|
+
const firstConsumer = await handle.get();
|
|
119
|
+
const second = setupHost("second");
|
|
120
|
+
hosts.push(second);
|
|
121
|
+
const secondConsumer = await handle.get();
|
|
122
|
+
expect(secondConsumer).not.toBe(firstConsumer);
|
|
123
|
+
await expect(firstConsumer.tools.ping()).rejects.toThrow("disposed");
|
|
124
|
+
await expect(secondConsumer.tools.ping()).resolves.toEqual({ label: "second" });
|
|
125
|
+
});
|
|
126
|
+
it("drops an in-flight consumer when the transport changes during load", async () => {
|
|
127
|
+
const first = setupHost("first", { holdSnapshot: true });
|
|
128
|
+
hosts.push(first);
|
|
129
|
+
const handle = open();
|
|
130
|
+
const pending = handle.get();
|
|
131
|
+
await waitForCall(first, (call) => call.member === "observable" && call.operation === "get");
|
|
132
|
+
const second = setupHost("second");
|
|
133
|
+
hosts.push(second);
|
|
134
|
+
first.release();
|
|
135
|
+
const consumer = await pending;
|
|
136
|
+
await expect(consumer.tools.ping()).resolves.toEqual({ label: "second" });
|
|
137
|
+
await waitForCall(first, (call) => call.operation === "unsubscribe");
|
|
138
|
+
});
|
|
139
|
+
it("rejects get after dispose and disposes the cached consumer", async () => {
|
|
140
|
+
const host = setupHost("first");
|
|
141
|
+
hosts.push(host);
|
|
142
|
+
const handle = open();
|
|
143
|
+
const consumer = await handle.get();
|
|
144
|
+
handle.dispose();
|
|
145
|
+
handle.dispose();
|
|
146
|
+
await expect(consumer.tools.ping()).rejects.toThrow("disposed");
|
|
147
|
+
await expect(handle.get()).rejects.toThrow("is disposed");
|
|
148
|
+
});
|
|
149
|
+
it("rejects when no transport arrives before timeoutMs", async () => {
|
|
150
|
+
jest.useFakeTimers();
|
|
151
|
+
try {
|
|
152
|
+
const handle = (0, index_1.hostContractConsumer)(definition, { timeoutMs: 25 });
|
|
153
|
+
handles.push(handle);
|
|
154
|
+
const pending = handle.get();
|
|
155
|
+
const expectation = expect(pending).rejects.toThrow("Timed out waiting for host contract transport after 25ms");
|
|
156
|
+
await jest.advanceTimersByTimeAsync(25);
|
|
157
|
+
await expectation;
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
jest.useRealTimers();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -4,6 +4,7 @@ const system_ids_1 = require("../../system-ids");
|
|
|
4
4
|
const utils_1 = require("../../utils");
|
|
5
5
|
const contract_resolvers_1 = require("../contract-resolvers");
|
|
6
6
|
const registry_1 = require("../registry");
|
|
7
|
+
const identity_contract_1 = require("../system/identity-contract");
|
|
7
8
|
const system_contracts_1 = require("../system-contracts");
|
|
8
9
|
// Populate the catalog with built-in system contracts.
|
|
9
10
|
require("../system");
|
|
@@ -15,6 +16,7 @@ describe("system contract catalog", () => {
|
|
|
15
16
|
expect(ids).toEqual(expect.arrayContaining([
|
|
16
17
|
system_ids_1.logsContractId,
|
|
17
18
|
system_ids_1.deviceOperationsContractId,
|
|
19
|
+
system_ids_1.identityContractId,
|
|
18
20
|
system_ids_1.networkDiagnosticsContractId,
|
|
19
21
|
system_ids_1.networkControlContractId,
|
|
20
22
|
system_ids_1.toolAccessControlContractId,
|
|
@@ -31,6 +33,8 @@ describe("system contract catalog", () => {
|
|
|
31
33
|
expect(registry.getProviderPackageId(system_ids_1.logsContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
32
34
|
expect(registry.resolve(system_ids_1.deviceOperationsContractId, 1)?.name).toBe("System Device Operations");
|
|
33
35
|
expect(registry.getProviderPackageId(system_ids_1.deviceOperationsContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
36
|
+
expect(registry.resolve(system_ids_1.identityContractId, 1)?.name).toBe("System Identity");
|
|
37
|
+
expect(registry.getProviderPackageId(system_ids_1.identityContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
34
38
|
expect(registry.resolve(system_ids_1.networkDiagnosticsContractId, 1)?.name).toBe("System Network Diagnostics");
|
|
35
39
|
expect(registry.getProviderPackageId(system_ids_1.networkDiagnosticsContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
36
40
|
expect(registry.resolve(system_ids_1.networkControlContractId, 1)?.name).toBe("System Network Control");
|
|
@@ -38,6 +42,16 @@ describe("system contract catalog", () => {
|
|
|
38
42
|
expect(registry.resolve(system_ids_1.toolAccessControlContractId, 1)?.name).toBe("System Tool Access Control");
|
|
39
43
|
expect(registry.getProviderPackageId(system_ids_1.toolAccessControlContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
40
44
|
});
|
|
45
|
+
it("declares DevicePairingState as the identity ceremony result", async () => {
|
|
46
|
+
// Loaded lazily. A top-level import cycles through device tables before they finish initializing.
|
|
47
|
+
const { devicePairingStateSchema } = await Promise.resolve().then(() => require("../../device-pairing/types"));
|
|
48
|
+
const stateFields = Object.keys(devicePairingStateSchema.shape).sort();
|
|
49
|
+
const definition = (0, identity_contract_1.getIdentityContractDefinition)();
|
|
50
|
+
for (const name of ["startSource", "startDestination", "approve", "reject", "cancel"]) {
|
|
51
|
+
const tool = definition.tools.find((entry) => entry.name === name);
|
|
52
|
+
expect(tool?.outputFields.map((entry) => entry.name).sort()).toEqual(stateFields);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
41
55
|
it("registerSystemContract makes new contracts installable without loader changes", () => {
|
|
42
56
|
const fakeContractId = (0, utils_1.newid)();
|
|
43
57
|
const providerPackageId = (0, utils_1.newid)();
|
|
@@ -30,6 +30,12 @@ export interface IContractResolverRegistrationOptions {
|
|
|
30
30
|
* resolver used as a compatibility fallback.
|
|
31
31
|
*/
|
|
32
32
|
providerPackageId?: string;
|
|
33
|
+
/**
|
|
34
|
+
* When `false`, the local host router resolves this contract without waiting
|
|
35
|
+
* for a signed-in user. The resolver must not read `dataContext` in that case.
|
|
36
|
+
* Defaults to requiring a user.
|
|
37
|
+
*/
|
|
38
|
+
requiresUserContext?: boolean;
|
|
33
39
|
}
|
|
34
40
|
/** Connection-authenticated identity fields used to authorize remote contract calls. */
|
|
35
41
|
export type ContractRemoteIdentity = Pick<IDeviceInfo, "deviceId" | "userId" | "publicKey" | "publicBoxKey">;
|
|
@@ -72,8 +78,7 @@ export declare function isSelfTrustedContractCaller(userContext: UserContext, re
|
|
|
72
78
|
*
|
|
73
79
|
* Layered policy, evaluated in order:
|
|
74
80
|
* 1. Calls that still carry a `targetDeviceId` are denied: a remote caller may not use
|
|
75
|
-
* this device as a relay under its identity.
|
|
76
|
-
* local-only.
|
|
81
|
+
* this device as a relay under its identity. Host-only system contracts are local-only.
|
|
77
82
|
* 2. The caller's keys must match a valid signed Users record known to the provider.
|
|
78
83
|
* 3. A Self-trusted caller (the provider's own account, or an account granted exact
|
|
79
84
|
* `TrustLevel.Self`) keeps full access; an omitted data context selects the provider's
|
|
@@ -99,16 +104,23 @@ export declare function authorizeRemoteContractCall(call: IContractCall, userCon
|
|
|
99
104
|
* currently known group context; arbitrary valid IDs are rejected before
|
|
100
105
|
* `UserContext.getDataContext` can create a new context.
|
|
101
106
|
*
|
|
107
|
+
* Contracts registered with `requiresUserContext: false` resolve before sign-in
|
|
108
|
+
* when `peekUserContext` reports that no user is loaded. Every other call still
|
|
109
|
+
* waits for `getUserContext`.
|
|
110
|
+
*
|
|
102
111
|
* This helper trusts the host transport's session authentication. It does not
|
|
103
112
|
* infer package-level authority from renderer-supplied data.
|
|
104
113
|
*
|
|
105
114
|
* @param transport Contract transport backed by an authenticated local session.
|
|
106
115
|
* @param opts.getUserContext Lazy accessor for the host's loaded user context.
|
|
116
|
+
* @param opts.peekUserContext Non-blocking view of the same user context. Required
|
|
117
|
+
* for contracts that resolve on the signed-out setup screen.
|
|
107
118
|
* @param opts.allowedContractIds Optional contract allowlist for this session.
|
|
108
119
|
* @param opts.resolveVariable Optional host-only pvar resolver exposed to local package tools.
|
|
109
120
|
*/
|
|
110
121
|
export declare function createLocalUserContextContractProviderRouter(transport: IContractTransport, opts: {
|
|
111
122
|
getUserContext: () => Promise<UserContext>;
|
|
123
|
+
peekUserContext?: () => UserContext | undefined;
|
|
112
124
|
allowedContractIds?: ReadonlySet<string>;
|
|
113
125
|
resolveVariable?: LocalContractVariableResolver;
|
|
114
126
|
}): IContractProviderRouter;
|
|
@@ -20,6 +20,8 @@ const contract_proxy_1 = require("./contract-proxy");
|
|
|
20
20
|
const remote_contract_transport_1 = require("./remote-contract-transport");
|
|
21
21
|
const types_1 = require("./types");
|
|
22
22
|
const resolvers = new Map();
|
|
23
|
+
/** Resolver keys that may run before a user context exists. */
|
|
24
|
+
const resolversWithoutUser = new Set();
|
|
23
25
|
const resolverKey = (contractId, version, providerPackageId) => providerPackageId
|
|
24
26
|
? `${(0, types_1.contractKey)(contractId, version)}#provider:${providerPackageId}`
|
|
25
27
|
: (0, types_1.contractKey)(contractId, version);
|
|
@@ -29,7 +31,32 @@ const resolverKey = (contractId, version, providerPackageId) => providerPackageI
|
|
|
29
31
|
* registry's "latest provider wins for dev contracts" behavior).
|
|
30
32
|
*/
|
|
31
33
|
function registerContractResolver(contractId, version, resolver, opts = {}) {
|
|
32
|
-
|
|
34
|
+
const key = resolverKey(contractId, version, opts.providerPackageId);
|
|
35
|
+
resolvers.set(key, resolver);
|
|
36
|
+
if (opts.requiresUserContext === false) {
|
|
37
|
+
resolversWithoutUser.add(key);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
resolversWithoutUser.delete(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Whether the local host must wait for a signed-in user before resolving this contract.
|
|
45
|
+
*
|
|
46
|
+
* A contract is user-optional when any of its resolvers registered with
|
|
47
|
+
* `requiresUserContext: false`.
|
|
48
|
+
*/
|
|
49
|
+
function contractRequiresUserContext(contractId, version) {
|
|
50
|
+
if (resolversWithoutUser.has(resolverKey(contractId, version))) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const providerPrefix = `${(0, types_1.contractKey)(contractId, version)}#provider:`;
|
|
54
|
+
for (const key of resolversWithoutUser) {
|
|
55
|
+
if (key.startsWith(providerPrefix)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
33
60
|
}
|
|
34
61
|
/**
|
|
35
62
|
* Look up a resolver for a contract version.
|
|
@@ -119,13 +146,17 @@ async function isSelfTrustedContractCaller(userContext, remoteIdentity) {
|
|
|
119
146
|
return hasSelfTrust(userContext, remoteIdentity);
|
|
120
147
|
}
|
|
121
148
|
const permissionDenied = (call, reason) => new Error(`Permission denied: ${call.member} '${call.name}'.${call.operation} on contract ${call.contractId}${reason ? ` (${reason})` : ""}`);
|
|
149
|
+
const localOnlyContractIds = new Set([
|
|
150
|
+
system_ids_1.identityContractId,
|
|
151
|
+
system_ids_1.invitesContractId,
|
|
152
|
+
system_ids_1.toolAccessControlContractId,
|
|
153
|
+
]);
|
|
122
154
|
/**
|
|
123
155
|
* Authorize one inbound contract call from **another device**.
|
|
124
156
|
*
|
|
125
157
|
* Layered policy, evaluated in order:
|
|
126
158
|
* 1. Calls that still carry a `targetDeviceId` are denied: a remote caller may not use
|
|
127
|
-
* this device as a relay under its identity.
|
|
128
|
-
* local-only.
|
|
159
|
+
* this device as a relay under its identity. Host-only system contracts are local-only.
|
|
129
160
|
* 2. The caller's keys must match a valid signed Users record known to the provider.
|
|
130
161
|
* 3. A Self-trusted caller (the provider's own account, or an account granted exact
|
|
131
162
|
* `TrustLevel.Self`) keeps full access; an omitted data context selects the provider's
|
|
@@ -145,8 +176,8 @@ async function authorizeRemoteContractCall(call, userContext, remoteIdentity) {
|
|
|
145
176
|
if (call.targetDeviceId) {
|
|
146
177
|
throw permissionDenied(call, "remote callers cannot relay through this device");
|
|
147
178
|
}
|
|
148
|
-
if (call.contractId
|
|
149
|
-
throw
|
|
179
|
+
if (localOnlyContractIds.has(call.contractId)) {
|
|
180
|
+
throw permissionDenied(call, "host system contract is local-only");
|
|
150
181
|
}
|
|
151
182
|
if (!(await verifyRemoteContractIdentity(userContext, remoteIdentity))) {
|
|
152
183
|
throw permissionDenied(call, "unverified caller identity");
|
|
@@ -247,6 +278,23 @@ async function resolveDataContextEndpoint(dataContext, request, resolveVariable)
|
|
|
247
278
|
notify: request.notify,
|
|
248
279
|
});
|
|
249
280
|
}
|
|
281
|
+
async function resolveSignedOutEndpoint(request) {
|
|
282
|
+
const resolver = getContractResolver(request.contractId, request.version, request.providerPackageId);
|
|
283
|
+
if (!resolver) {
|
|
284
|
+
throw new Error(`No contract provider registered for ${request.contractId} v${request.version}`);
|
|
285
|
+
}
|
|
286
|
+
// Signed-out contracts declare that they do not read dataContext.
|
|
287
|
+
const resolution = await resolver({ dataContext: undefined });
|
|
288
|
+
return (0, contract_proxy_1.createContractProviderEndpoint)(resolution, {
|
|
289
|
+
context: request.context,
|
|
290
|
+
notify: request.notify,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function resolvesWithoutUser(contractId, version, peekUserContext) {
|
|
294
|
+
return (!contractRequiresUserContext(contractId, version) &&
|
|
295
|
+
peekUserContext !== undefined &&
|
|
296
|
+
!peekUserContext());
|
|
297
|
+
}
|
|
250
298
|
/**
|
|
251
299
|
* Create a provider router for one authenticated UI-to-local-host session.
|
|
252
300
|
*
|
|
@@ -256,21 +304,30 @@ async function resolveDataContextEndpoint(dataContext, request, resolveVariable)
|
|
|
256
304
|
* currently known group context; arbitrary valid IDs are rejected before
|
|
257
305
|
* `UserContext.getDataContext` can create a new context.
|
|
258
306
|
*
|
|
307
|
+
* Contracts registered with `requiresUserContext: false` resolve before sign-in
|
|
308
|
+
* when `peekUserContext` reports that no user is loaded. Every other call still
|
|
309
|
+
* waits for `getUserContext`.
|
|
310
|
+
*
|
|
259
311
|
* This helper trusts the host transport's session authentication. It does not
|
|
260
312
|
* infer package-level authority from renderer-supplied data.
|
|
261
313
|
*
|
|
262
314
|
* @param transport Contract transport backed by an authenticated local session.
|
|
263
315
|
* @param opts.getUserContext Lazy accessor for the host's loaded user context.
|
|
316
|
+
* @param opts.peekUserContext Non-blocking view of the same user context. Required
|
|
317
|
+
* for contracts that resolve on the signed-out setup screen.
|
|
264
318
|
* @param opts.allowedContractIds Optional contract allowlist for this session.
|
|
265
319
|
* @param opts.resolveVariable Optional host-only pvar resolver exposed to local package tools.
|
|
266
320
|
*/
|
|
267
321
|
function createLocalUserContextContractProviderRouter(transport, opts) {
|
|
268
|
-
const { getUserContext, allowedContractIds, resolveVariable } = opts;
|
|
322
|
+
const { getUserContext, peekUserContext, allowedContractIds, resolveVariable } = opts;
|
|
269
323
|
return (0, contract_provider_router_1.createContractProviderRouter)(transport, {
|
|
270
324
|
authorizeCall: async (call) => {
|
|
271
325
|
if (allowedContractIds && !allowedContractIds.has(call.contractId)) {
|
|
272
326
|
throw new Error(`Permission denied: contract ${call.contractId} is not available locally`);
|
|
273
327
|
}
|
|
328
|
+
if (resolvesWithoutUser(call.contractId, call.version, peekUserContext)) {
|
|
329
|
+
return { context: {} };
|
|
330
|
+
}
|
|
274
331
|
const userContext = await getUserContext();
|
|
275
332
|
const dataContext = getAuthorizedLocalDataContext(userContext, call.dataContextId);
|
|
276
333
|
const context = {
|
|
@@ -284,11 +341,17 @@ function createLocalUserContextContractProviderRouter(transport, opts) {
|
|
|
284
341
|
};
|
|
285
342
|
},
|
|
286
343
|
resolveProviderPackageId: async ({ contractId, version, dataContextId }) => {
|
|
344
|
+
if (resolvesWithoutUser(contractId, version, peekUserContext)) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
287
347
|
const userContext = await getUserContext();
|
|
288
348
|
const dataContext = getAuthorizedLocalDataContext(userContext, dataContextId);
|
|
289
349
|
return getActiveProviderPackageId(dataContext, contractId, version);
|
|
290
350
|
},
|
|
291
351
|
resolveEndpoint: async (request) => {
|
|
352
|
+
if (resolvesWithoutUser(request.contractId, request.version, peekUserContext)) {
|
|
353
|
+
return resolveSignedOutEndpoint(request);
|
|
354
|
+
}
|
|
292
355
|
const userContext = await getUserContext();
|
|
293
356
|
const dataContext = getAuthorizedLocalDataContext(userContext, request.dataContextId);
|
|
294
357
|
return resolveDataContextEndpoint(dataContext, request, resolveVariable);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type IContractConsumer, type IContractConsumerBase } from "./contract-proxy";
|
|
2
|
+
import type { IContractDefinition } from "./types";
|
|
3
|
+
/** Options for {@link hostContractConsumer}. */
|
|
4
|
+
export interface IHostContractConsumerOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Data context bound into every call. Resolved when a consumer is built, not when
|
|
7
|
+
* the handle is created. Omit it for contracts that resolve before sign-in.
|
|
8
|
+
*/
|
|
9
|
+
dataContextId?: () => Promise<string | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* How long `get` waits for a host transport on the first call. Later calls reuse
|
|
12
|
+
* the cached consumer and do not wait again.
|
|
13
|
+
*/
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Cached handle for one contract consumed over the UI-to-host transport.
|
|
18
|
+
*
|
|
19
|
+
* `get` returns the same consumer until the transport is replaced (socket reconnect).
|
|
20
|
+
* The stale consumer is disposed. A load that is in flight when the transport changes
|
|
21
|
+
* disposes its half-built consumer and resolves with one bound to the new transport.
|
|
22
|
+
*/
|
|
23
|
+
export interface IHostContractConsumer<T extends IContractConsumerBase> {
|
|
24
|
+
/** Return the live consumer, building or rebuilding it when the transport changed. */
|
|
25
|
+
get(): Promise<T>;
|
|
26
|
+
/** Dispose the cached consumer and ignore later transport changes. Idempotent. */
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cache one local-host contract consumer per active UI-to-host transport.
|
|
31
|
+
*
|
|
32
|
+
* UI modules that talk to a system contract should share one handle instead of
|
|
33
|
+
* reimplementing transport wait, swap, and dispose. The CLI does not use this;
|
|
34
|
+
* it opens a socket per invocation and has no transport swaps.
|
|
35
|
+
*
|
|
36
|
+
* @param definition Contract shape to consume.
|
|
37
|
+
* @param options Data-context resolver and transport wait.
|
|
38
|
+
* @returns A handle whose `get` is safe to call concurrently.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hostContractConsumer<T extends IContractConsumerBase = IContractConsumer>(definition: IContractDefinition, options?: IHostContractConsumerOptions): IHostContractConsumer<T>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hostContractConsumer = hostContractConsumer;
|
|
4
|
+
const contract_proxy_1 = require("./contract-proxy");
|
|
5
|
+
const host_contract_transport_1 = require("./host-contract-transport");
|
|
6
|
+
/** Default wait for a UI-to-host transport before `get` rejects. */
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
8
|
+
/**
|
|
9
|
+
* Cache one local-host contract consumer per active UI-to-host transport.
|
|
10
|
+
*
|
|
11
|
+
* UI modules that talk to a system contract should share one handle instead of
|
|
12
|
+
* reimplementing transport wait, swap, and dispose. The CLI does not use this;
|
|
13
|
+
* it opens a socket per invocation and has no transport swaps.
|
|
14
|
+
*
|
|
15
|
+
* @param definition Contract shape to consume.
|
|
16
|
+
* @param options Data-context resolver and transport wait.
|
|
17
|
+
* @returns A handle whose `get` is safe to call concurrently.
|
|
18
|
+
*/
|
|
19
|
+
function hostContractConsumer(definition, options = {}) {
|
|
20
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
21
|
+
let cached;
|
|
22
|
+
let pending;
|
|
23
|
+
let disposed = false;
|
|
24
|
+
const disposedMessage = () => `Host contract consumer ${definition.contractId} v${definition.version} is disposed`;
|
|
25
|
+
const dropCached = () => {
|
|
26
|
+
cached?.consumer.dispose();
|
|
27
|
+
cached = undefined;
|
|
28
|
+
};
|
|
29
|
+
const unsubscribe = (0, host_contract_transport_1.subscribeHostContractTransport)((transport) => {
|
|
30
|
+
if (cached && cached.transport !== transport) {
|
|
31
|
+
dropCached();
|
|
32
|
+
}
|
|
33
|
+
if (pending && pending.transport !== transport) {
|
|
34
|
+
pending = undefined;
|
|
35
|
+
}
|
|
36
|
+
}, { emitCurrent: false });
|
|
37
|
+
async function load(transport) {
|
|
38
|
+
const dataContextId = options.dataContextId ? await options.dataContextId() : undefined;
|
|
39
|
+
if (disposed) {
|
|
40
|
+
throw new Error(disposedMessage());
|
|
41
|
+
}
|
|
42
|
+
if ((0, host_contract_transport_1.getHostContractTransport)() !== transport) {
|
|
43
|
+
return get();
|
|
44
|
+
}
|
|
45
|
+
const consumer = (0, contract_proxy_1.createContractConsumer)(definition, transport, dataContextId ? { dataContextId } : {});
|
|
46
|
+
try {
|
|
47
|
+
await consumer.loadingPromise;
|
|
48
|
+
if (disposed || (0, host_contract_transport_1.getHostContractTransport)() !== transport) {
|
|
49
|
+
consumer.dispose();
|
|
50
|
+
if (disposed) {
|
|
51
|
+
throw new Error(disposedMessage());
|
|
52
|
+
}
|
|
53
|
+
return get();
|
|
54
|
+
}
|
|
55
|
+
cached = { transport, consumer };
|
|
56
|
+
return consumer;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
consumer.dispose();
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function get() {
|
|
64
|
+
if (disposed) {
|
|
65
|
+
throw new Error(disposedMessage());
|
|
66
|
+
}
|
|
67
|
+
const transport = (0, host_contract_transport_1.getHostContractTransport)() ?? (await (0, host_contract_transport_1.waitForHostContractTransport)({ timeoutMs }));
|
|
68
|
+
if (disposed) {
|
|
69
|
+
throw new Error(disposedMessage());
|
|
70
|
+
}
|
|
71
|
+
if ((0, host_contract_transport_1.getHostContractTransport)() !== transport) {
|
|
72
|
+
return get();
|
|
73
|
+
}
|
|
74
|
+
if (cached?.transport === transport) {
|
|
75
|
+
return cached.consumer;
|
|
76
|
+
}
|
|
77
|
+
if (pending?.transport === transport) {
|
|
78
|
+
return pending.promise;
|
|
79
|
+
}
|
|
80
|
+
const flight = { transport, promise: undefined };
|
|
81
|
+
const promise = load(transport);
|
|
82
|
+
flight.promise = promise;
|
|
83
|
+
pending = flight;
|
|
84
|
+
try {
|
|
85
|
+
return await promise;
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
if (pending === flight) {
|
|
89
|
+
pending = undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
get,
|
|
95
|
+
dispose() {
|
|
96
|
+
if (disposed) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
disposed = true;
|
|
100
|
+
unsubscribe();
|
|
101
|
+
pending = undefined;
|
|
102
|
+
dropCached();
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -7,11 +7,13 @@ export { CONTRACT_NOTIFY_CHANNEL, CONTRACT_REQUEST_CHANNEL, connectionContractTr
|
|
|
7
7
|
export { Contracts, ContractsTable, contractsMetaData, contractsSchema, type IContractRecord, } from "./contracts.table";
|
|
8
8
|
export { createLocalContractConsumer } from "./create-local-consumer";
|
|
9
9
|
export { extractTableShape, extractToolShape, type IExtractableTableDef, type IExtractableToolInstance, setSchemaToFieldsFn, } from "./extract";
|
|
10
|
+
export { hostContractConsumer, type IHostContractConsumer, type IHostContractConsumerOptions, } from "./host-contract-consumer";
|
|
10
11
|
export { getHostContractTransport, type HostContractTransportListener, registerHostContractTransport, subscribeHostContractTransport, waitForHostContractTransport, } from "./host-contract-transport";
|
|
11
12
|
export { PersistentContractRegistry } from "./persistent-registry";
|
|
12
13
|
export { ContractRegistry, type IContractProviderRegistration } from "./registry";
|
|
13
14
|
export { createRemoteContractEndpoint, getRemoteContractTransportFactory, type IRemoteContractHost, type IRemoteContractTarget, type RemoteContractTransportFactory, resolveRemoteTargetEndpoint, setRemoteContractTransportFactory, } from "./remote-contract-transport";
|
|
14
15
|
export { DEVICE_OPERATIONS_CONTRACT_VERSION, DEVICE_OPERATIONS_MAX_QUERY_LENGTH, DEVICE_OPERATIONS_MAX_RESULT_BYTES, DEVICE_OPERATIONS_MAX_ROWS, DEVICE_OPERATIONS_MAX_TOOLS, type DeviceOperationCapability, deviceOperationsProviderPackageId, deviceOperationsToolIds, getDeviceOperationsContractDefinition, type IDeviceDatabaseQueryInput, type IDeviceDatabaseQueryResult, type IDeviceOperationStatus, type IDeviceToolListInput, type IDeviceToolLookupInput, type IDeviceToolLookupResult, type IDeviceToolRunInput, } from "./system/device-operations-contract";
|
|
16
|
+
export { getIdentityContractDefinition, IDENTITY_CONTRACT_VERSION, type IIdentityConsumer, type IIdentitySessionInfo, } from "./system/identity-contract";
|
|
15
17
|
export { getInvitesContractDefinition, type IAcceptInviteResult, type ICreatedInvite, type IInvitePreview, type IInvitesConsumer, INVITES_CONTRACT_VERSION, } from "./system/invites-contract";
|
|
16
18
|
export { getLogsContractDefinition, type ILogsConsumer, LOGS_CONTRACT_VERSION, resolveLogsContract, } from "./system/logs-contract";
|
|
17
19
|
export * from "./system/network-contracts";
|
package/dist/contracts/index.js
CHANGED
|
@@ -14,8 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
18
|
-
exports.validateProviderSatisfiesContract = exports.validateImmutability = exports.validateAlsoImplements = exports.contractKey = exports.registerSystemContract = exports.listSystemContracts = exports.installSystemContracts = exports.TOOL_ACCESS_CONTROL_CONTRACT_VERSION = exports.getToolAccessControlContractDefinition = exports.resolveLogsContract = exports.LOGS_CONTRACT_VERSION = exports.getLogsContractDefinition = exports.INVITES_CONTRACT_VERSION = exports.getInvitesContractDefinition = exports.getDeviceOperationsContractDefinition = exports.deviceOperationsToolIds = exports.deviceOperationsProviderPackageId = void 0;
|
|
17
|
+
exports.DEVICE_OPERATIONS_MAX_ROWS = exports.DEVICE_OPERATIONS_MAX_RESULT_BYTES = exports.DEVICE_OPERATIONS_MAX_QUERY_LENGTH = exports.DEVICE_OPERATIONS_CONTRACT_VERSION = exports.setRemoteContractTransportFactory = exports.resolveRemoteTargetEndpoint = exports.getRemoteContractTransportFactory = exports.createRemoteContractEndpoint = exports.ContractRegistry = exports.PersistentContractRegistry = exports.waitForHostContractTransport = exports.subscribeHostContractTransport = exports.registerHostContractTransport = exports.getHostContractTransport = exports.hostContractConsumer = exports.setSchemaToFieldsFn = exports.extractToolShape = exports.extractTableShape = exports.createLocalContractConsumer = exports.contractsSchema = exports.contractsMetaData = exports.ContractsTable = exports.Contracts = exports.inProcessTransportPair = exports.connectionContractTransport = exports.CONTRACT_REQUEST_CHANNEL = exports.CONTRACT_NOTIFY_CHANNEL = exports.verifyRemoteContractIdentity = exports.registerContractResolver = exports.isSelfTrustedContractCaller = exports.handleContractCall = exports.getContractResolver = exports.createUserContextContractProviderRouter = exports.createLocalUserContextContractProviderRouter = exports.authorizeRemoteContractCall = exports.sameUserContractPermissionCheck = exports.createRemoteContractConsumer = exports.createContractProviderSession = exports.createContractProviderEndpoint = exports.createContractProvider = exports.createContractConsumer = exports.CONTRACT_CALL_RPC = exports.contractProvidersSchema = exports.contractProvidersMetaData = exports.ContractProvidersTable = exports.ContractProviders = exports.createContractProviderRouter = exports.PackageBuilder = exports.definePackage = exports.ContractBuilder = void 0;
|
|
18
|
+
exports.validateProviderSatisfiesContract = exports.validateImmutability = exports.validateAlsoImplements = exports.contractKey = exports.registerSystemContract = exports.listSystemContracts = exports.installSystemContracts = exports.TOOL_ACCESS_CONTROL_CONTRACT_VERSION = exports.getToolAccessControlContractDefinition = exports.resolveLogsContract = exports.LOGS_CONTRACT_VERSION = exports.getLogsContractDefinition = exports.INVITES_CONTRACT_VERSION = exports.getInvitesContractDefinition = exports.IDENTITY_CONTRACT_VERSION = exports.getIdentityContractDefinition = exports.getDeviceOperationsContractDefinition = exports.deviceOperationsToolIds = exports.deviceOperationsProviderPackageId = exports.DEVICE_OPERATIONS_MAX_TOOLS = void 0;
|
|
19
19
|
var builder_1 = require("./builder");
|
|
20
20
|
Object.defineProperty(exports, "ContractBuilder", { enumerable: true, get: function () { return builder_1.ContractBuilder; } });
|
|
21
21
|
Object.defineProperty(exports, "definePackage", { enumerable: true, get: function () { return builder_1.definePackage; } });
|
|
@@ -60,6 +60,8 @@ var extract_1 = require("./extract");
|
|
|
60
60
|
Object.defineProperty(exports, "extractTableShape", { enumerable: true, get: function () { return extract_1.extractTableShape; } });
|
|
61
61
|
Object.defineProperty(exports, "extractToolShape", { enumerable: true, get: function () { return extract_1.extractToolShape; } });
|
|
62
62
|
Object.defineProperty(exports, "setSchemaToFieldsFn", { enumerable: true, get: function () { return extract_1.setSchemaToFieldsFn; } });
|
|
63
|
+
var host_contract_consumer_1 = require("./host-contract-consumer");
|
|
64
|
+
Object.defineProperty(exports, "hostContractConsumer", { enumerable: true, get: function () { return host_contract_consumer_1.hostContractConsumer; } });
|
|
63
65
|
var host_contract_transport_1 = require("./host-contract-transport");
|
|
64
66
|
Object.defineProperty(exports, "getHostContractTransport", { enumerable: true, get: function () { return host_contract_transport_1.getHostContractTransport; } });
|
|
65
67
|
Object.defineProperty(exports, "registerHostContractTransport", { enumerable: true, get: function () { return host_contract_transport_1.registerHostContractTransport; } });
|
|
@@ -83,6 +85,9 @@ Object.defineProperty(exports, "DEVICE_OPERATIONS_MAX_TOOLS", { enumerable: true
|
|
|
83
85
|
Object.defineProperty(exports, "deviceOperationsProviderPackageId", { enumerable: true, get: function () { return device_operations_contract_1.deviceOperationsProviderPackageId; } });
|
|
84
86
|
Object.defineProperty(exports, "deviceOperationsToolIds", { enumerable: true, get: function () { return device_operations_contract_1.deviceOperationsToolIds; } });
|
|
85
87
|
Object.defineProperty(exports, "getDeviceOperationsContractDefinition", { enumerable: true, get: function () { return device_operations_contract_1.getDeviceOperationsContractDefinition; } });
|
|
88
|
+
var identity_contract_1 = require("./system/identity-contract");
|
|
89
|
+
Object.defineProperty(exports, "getIdentityContractDefinition", { enumerable: true, get: function () { return identity_contract_1.getIdentityContractDefinition; } });
|
|
90
|
+
Object.defineProperty(exports, "IDENTITY_CONTRACT_VERSION", { enumerable: true, get: function () { return identity_contract_1.IDENTITY_CONTRACT_VERSION; } });
|
|
86
91
|
var invites_contract_1 = require("./system/invites-contract");
|
|
87
92
|
Object.defineProperty(exports, "getInvitesContractDefinition", { enumerable: true, get: function () { return invites_contract_1.getInvitesContractDefinition; } });
|
|
88
93
|
Object.defineProperty(exports, "INVITES_CONTRACT_VERSION", { enumerable: true, get: function () { return invites_contract_1.INVITES_CONTRACT_VERSION; } });
|