@peers-app/peers-sdk 0.21.1 → 0.21.2
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/context/user-context.d.ts +10 -3
- package/dist/context/user-context.js +12 -12
- package/dist/contracts/__tests__/builder.test.js +158 -4
- package/dist/contracts/__tests__/registry.test.js +45 -303
- package/dist/contracts/__tests__/system-contracts.test.d.ts +1 -0
- package/dist/contracts/__tests__/system-contracts.test.js +52 -0
- package/dist/contracts/builder.d.ts +43 -2
- package/dist/contracts/builder.js +79 -5
- package/dist/contracts/contract-proxy.d.ts +61 -21
- package/dist/contracts/create-local-consumer.d.ts +12 -0
- package/dist/contracts/create-local-consumer.js +19 -0
- package/dist/contracts/index.d.ts +5 -3
- package/dist/contracts/index.js +8 -1
- package/dist/contracts/registry.d.ts +17 -0
- package/dist/contracts/registry.js +36 -0
- package/dist/contracts/system/device-operations-contract.js +7 -0
- package/dist/contracts/system/index.d.ts +8 -0
- package/dist/contracts/system/index.js +22 -0
- package/dist/contracts/system/logs-contract.d.ts +18 -1
- package/dist/contracts/system/logs-contract.js +8 -2
- package/dist/contracts/system-contracts.d.ts +47 -0
- package/dist/contracts/system-contracts.js +69 -0
- package/dist/contracts/types.d.ts +44 -0
- package/dist/package-loader/contract-package-loader.test.d.ts +1 -0
- package/dist/package-loader/contract-package-loader.test.js +88 -0
- package/dist/package-loader/package-loader.d.ts +8 -0
- package/dist/package-loader/package-loader.js +17 -1
- package/dist/package-loader/package-loader.test.js +17 -0
- package/dist/package-loader/resolve-consumed-contracts.d.ts +13 -0
- package/dist/package-loader/resolve-consumed-contracts.js +55 -0
- package/dist/package-loader/resolve-consumed-contracts.test.d.ts +1 -0
- package/dist/package-loader/resolve-consumed-contracts.test.js +123 -0
- package/package.json +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerSystemContract = registerSystemContract;
|
|
4
|
+
exports.installSystemContracts = installSystemContracts;
|
|
5
|
+
exports.listSystemContracts = listSystemContracts;
|
|
6
|
+
exports._clearSystemContractsForTests = _clearSystemContractsForTests;
|
|
7
|
+
const systemContracts = [];
|
|
8
|
+
function systemContractKey(contractId, version) {
|
|
9
|
+
return `${contractId}@${version}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Self-register a built-in system contract.
|
|
13
|
+
*
|
|
14
|
+
* Call once from the contract module (side effect at import). Later
|
|
15
|
+
* {@link installSystemContracts} installs every registered entry into a
|
|
16
|
+
* concrete registry. New system contracts only need this one call.
|
|
17
|
+
*/
|
|
18
|
+
function registerSystemContract(registration) {
|
|
19
|
+
if (registration.resolve) {
|
|
20
|
+
if (!registration.contractId || registration.version == null) {
|
|
21
|
+
throw new Error("registerSystemContract: contractId and version are required when resolve is provided");
|
|
22
|
+
}
|
|
23
|
+
// Defer resolver registration so self-registration during SDK barrel / package-loader
|
|
24
|
+
// import cannot re-enter contract-resolvers mid-initialization.
|
|
25
|
+
const { contractId, version, resolve } = registration;
|
|
26
|
+
queueMicrotask(() => {
|
|
27
|
+
const { registerContractResolver } = require("./contract-resolvers");
|
|
28
|
+
registerContractResolver(contractId, version, resolve);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const key = registration.contractId && registration.version != null
|
|
32
|
+
? systemContractKey(registration.contractId, registration.version)
|
|
33
|
+
: undefined;
|
|
34
|
+
if (key) {
|
|
35
|
+
const existingIdx = systemContracts.findIndex((entry) => entry.contractId &&
|
|
36
|
+
entry.version != null &&
|
|
37
|
+
systemContractKey(entry.contractId, entry.version) === key);
|
|
38
|
+
if (existingIdx >= 0) {
|
|
39
|
+
systemContracts[existingIdx] = registration;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
systemContracts.push(registration);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Register every known system contract into `registry` as a provider.
|
|
47
|
+
* Safe to call more than once for the same registry (provider updates in place).
|
|
48
|
+
*/
|
|
49
|
+
function installSystemContracts(registry) {
|
|
50
|
+
for (const entry of systemContracts) {
|
|
51
|
+
const definition = entry.getDefinition();
|
|
52
|
+
const result = registry.register(entry.providerPackageId, definition);
|
|
53
|
+
if (!result.valid) {
|
|
54
|
+
console.error(`[SystemContracts] Failed to register ${definition.name} (${definition.contractId}@${definition.version}):`, result.errors);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Snapshot of registered system contracts (for tests and diagnostics). */
|
|
59
|
+
function listSystemContracts() {
|
|
60
|
+
return systemContracts;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Clear the system-contract catalog. Test-only — production code must not call this.
|
|
64
|
+
*
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
function _clearSystemContractsForTests() {
|
|
68
|
+
systemContracts.length = 0;
|
|
69
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { IToolInstance } from "../data/tools";
|
|
|
4
4
|
import type { Event } from "../events";
|
|
5
5
|
import type { Observable } from "../observable";
|
|
6
6
|
import type { FieldType, IField } from "../types/field-type";
|
|
7
|
+
import type { IContractConsumer, IContractConsumerBase } from "./contract-proxy";
|
|
7
8
|
/**
|
|
8
9
|
* A versioned interface definition declaring the shape of tables, tools,
|
|
9
10
|
* observables, and events that a package provides. Pure data — no executable code.
|
|
@@ -58,6 +59,43 @@ export interface IContractEvent {
|
|
|
58
59
|
/** Shape of the payload delivered to subscribers when the event fires. */
|
|
59
60
|
payloadFields: IField[];
|
|
60
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Options for {@link PackageBuilder.consumes}.
|
|
64
|
+
*
|
|
65
|
+
* Provider selection (`providerPackageIds`, `fallbackToDefaultProvider`) is
|
|
66
|
+
* recorded here for the package-loader; wiring that resolves the deferred
|
|
67
|
+
* handle is a follow-up step.
|
|
68
|
+
*/
|
|
69
|
+
export interface IConsumedContractOptions {
|
|
70
|
+
/**
|
|
71
|
+
* When true, a missing provider settles the handle with `undefined` instead
|
|
72
|
+
* of rejecting. Defaults to false (required).
|
|
73
|
+
*/
|
|
74
|
+
optional?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Ordered preference of provider package IDs. The loader tries these in
|
|
77
|
+
* order before considering the active/default provider.
|
|
78
|
+
*/
|
|
79
|
+
providerPackageIds?: string[];
|
|
80
|
+
/**
|
|
81
|
+
* When true and none of {@link providerPackageIds} is available, fall back
|
|
82
|
+
* to the active/default provider for the contract. Ignored when
|
|
83
|
+
* `providerPackageIds` is omitted (default provider is used directly).
|
|
84
|
+
*/
|
|
85
|
+
fallbackToDefaultProvider?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Deferred handle returned by {@link PackageBuilder.consumes}.
|
|
89
|
+
*
|
|
90
|
+
* The handle itself is not thenable — await {@link consumer} from runtime
|
|
91
|
+
* code after `definePackage` finishes, not inside the define callback.
|
|
92
|
+
* Settlement is owned by the SDK (package-loader); package code cannot
|
|
93
|
+
* resolve or reject the promise.
|
|
94
|
+
*/
|
|
95
|
+
export interface IConsumedContractHandle<T extends IContractConsumerBase | undefined = IContractConsumer> {
|
|
96
|
+
/** Resolves to the live consumer proxy, or `undefined` when optional and unmatched. */
|
|
97
|
+
readonly consumer: Promise<T>;
|
|
98
|
+
}
|
|
61
99
|
/**
|
|
62
100
|
* A reference to a contract that a package wants to consume.
|
|
63
101
|
* Recorded during `definePackage` and checked at install time.
|
|
@@ -66,6 +104,12 @@ export interface IConsumedContract {
|
|
|
66
104
|
contractId: string;
|
|
67
105
|
version: number;
|
|
68
106
|
optional?: boolean;
|
|
107
|
+
/** Ordered preferred provider package IDs, if any. */
|
|
108
|
+
providerPackageIds?: string[];
|
|
109
|
+
/** Whether to fall back to the default provider when preferred ones are missing. */
|
|
110
|
+
fallbackToDefaultProvider?: boolean;
|
|
111
|
+
/** Deferred handle returned by `consumes()` for this dependency. */
|
|
112
|
+
handle?: IConsumedContractHandle<IContractConsumerBase | undefined>;
|
|
69
113
|
}
|
|
70
114
|
/**
|
|
71
115
|
* Declares that the owning contract's implementation also satisfies another
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
jest.mock("../data/tools", () => ({
|
|
4
|
+
Tools: () => ({
|
|
5
|
+
save: jest.fn().mockResolvedValue(undefined),
|
|
6
|
+
}),
|
|
7
|
+
}));
|
|
8
|
+
jest.mock("../tools", () => ({
|
|
9
|
+
registerTool: jest.fn(),
|
|
10
|
+
}));
|
|
11
|
+
jest.mock("../data/assistants", () => ({
|
|
12
|
+
Assistants: () => ({
|
|
13
|
+
save: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
const registry_1 = require("../contracts/registry");
|
|
17
|
+
const utils_1 = require("../utils");
|
|
18
|
+
const contract_package_loader_1 = require("./contract-package-loader");
|
|
19
|
+
describe("installContractPackage registry wiring", () => {
|
|
20
|
+
const packageId = (0, utils_1.newid)();
|
|
21
|
+
const contractId = (0, utils_1.newid)();
|
|
22
|
+
function makeDefinition(overrides = {}) {
|
|
23
|
+
return {
|
|
24
|
+
packageId,
|
|
25
|
+
version: "1.0.0",
|
|
26
|
+
contracts: [
|
|
27
|
+
{
|
|
28
|
+
contractId,
|
|
29
|
+
version: 1,
|
|
30
|
+
devTag: "dev",
|
|
31
|
+
name: "Test Contract",
|
|
32
|
+
description: "For registry install tests",
|
|
33
|
+
tables: [],
|
|
34
|
+
tools: [],
|
|
35
|
+
observables: [],
|
|
36
|
+
events: [],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
consumes: [],
|
|
40
|
+
alsoImplements: new Map(),
|
|
41
|
+
assistants: [],
|
|
42
|
+
appNavs: [],
|
|
43
|
+
toolInstances: [],
|
|
44
|
+
tableDefinitions: [],
|
|
45
|
+
...overrides,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function mockDataContext() {
|
|
49
|
+
return {
|
|
50
|
+
dataContextId: "test-ctx",
|
|
51
|
+
tableContainer: {
|
|
52
|
+
registerTableDefinition: jest.fn(),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
it("registers contract providers into the provided ContractRegistry", async () => {
|
|
57
|
+
const registry = new registry_1.ContractRegistry();
|
|
58
|
+
const dataContext = mockDataContext();
|
|
59
|
+
const definition = makeDefinition();
|
|
60
|
+
await (0, contract_package_loader_1.installContractPackage)(dataContext, definition, { registry });
|
|
61
|
+
expect(registry.resolve(contractId, 1)?.name).toBe("Test Contract");
|
|
62
|
+
expect(registry.getProviderPackageId(contractId, 1)).toBe(packageId);
|
|
63
|
+
expect(registry.getContractKeysForPackage(packageId)).toEqual([`${contractId}@1`]);
|
|
64
|
+
});
|
|
65
|
+
it("does not register providers when no registry is provided", async () => {
|
|
66
|
+
const dataContext = mockDataContext();
|
|
67
|
+
await expect((0, contract_package_loader_1.installContractPackage)(dataContext, makeDefinition())).resolves.toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("extractPackageDefinition", () => {
|
|
71
|
+
it("returns a package definition when packageId and contracts are present", () => {
|
|
72
|
+
const packageId = (0, utils_1.newid)();
|
|
73
|
+
const def = {
|
|
74
|
+
packageId,
|
|
75
|
+
contracts: [],
|
|
76
|
+
consumes: [],
|
|
77
|
+
alsoImplements: new Map(),
|
|
78
|
+
assistants: [],
|
|
79
|
+
appNavs: [],
|
|
80
|
+
toolInstances: [],
|
|
81
|
+
tableDefinitions: [],
|
|
82
|
+
};
|
|
83
|
+
expect((0, contract_package_loader_1.extractPackageDefinition)({ packageDefinition: def })).toBe(def);
|
|
84
|
+
});
|
|
85
|
+
it("returns undefined when packageDefinition is missing", () => {
|
|
86
|
+
expect((0, contract_package_loader_1.extractPackageDefinition)({})).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DataContext } from "../context/data-context";
|
|
2
|
+
import { ContractRegistry } from "../contracts/registry";
|
|
2
3
|
import { type IPackage } from "../data/packages";
|
|
3
4
|
import type { IPeersPackage } from "../types/peers-package";
|
|
5
|
+
import "../contracts/system";
|
|
4
6
|
export declare class PackageLoader {
|
|
5
7
|
readonly dataContext: DataContext;
|
|
6
8
|
static PeersSDK: any;
|
|
@@ -8,6 +10,12 @@ export declare class PackageLoader {
|
|
|
8
10
|
private packageInstances;
|
|
9
11
|
/** Packages that were installed via the contract-based path (tools/assistants/tables already saved with packageId). */
|
|
10
12
|
private contractInstalledPackages;
|
|
13
|
+
/**
|
|
14
|
+
* In-memory contract providers for this data context. Seeded with system
|
|
15
|
+
* contracts at construction; package providers are added via
|
|
16
|
+
* {@link installContractPackage}.
|
|
17
|
+
*/
|
|
18
|
+
readonly contractRegistry: ContractRegistry;
|
|
11
19
|
require: (<T>(module: string) => T) | undefined;
|
|
12
20
|
constructor(dataContext: DataContext);
|
|
13
21
|
/** Returns true if a package was installed via the contract-based definePackage() path. */
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PackageLoader = void 0;
|
|
4
4
|
exports.setDefaultRequire = setDefaultRequire;
|
|
5
|
+
const registry_1 = require("../contracts/registry");
|
|
6
|
+
const system_contracts_1 = require("../contracts/system-contracts");
|
|
5
7
|
const files_1 = require("../data/files");
|
|
6
8
|
const package_version_resolver_1 = require("../data/package-version-resolver");
|
|
7
9
|
const package_versions_1 = require("../data/package-versions");
|
|
@@ -9,6 +11,9 @@ const packages_1 = require("../data/packages");
|
|
|
9
11
|
const tools_1 = require("../data/tools");
|
|
10
12
|
const tools_2 = require("../tools");
|
|
11
13
|
const contract_package_loader_1 = require("./contract-package-loader");
|
|
14
|
+
const resolve_consumed_contracts_1 = require("./resolve-consumed-contracts");
|
|
15
|
+
// Ensure built-in system contracts are cataloged before any PackageLoader installs them.
|
|
16
|
+
require("../contracts/system");
|
|
12
17
|
class PackageLoader {
|
|
13
18
|
dataContext;
|
|
14
19
|
static PeersSDK;
|
|
@@ -16,9 +21,17 @@ class PackageLoader {
|
|
|
16
21
|
packageInstances = {};
|
|
17
22
|
/** Packages that were installed via the contract-based path (tools/assistants/tables already saved with packageId). */
|
|
18
23
|
contractInstalledPackages = new Set();
|
|
24
|
+
/**
|
|
25
|
+
* In-memory contract providers for this data context. Seeded with system
|
|
26
|
+
* contracts at construction; package providers are added via
|
|
27
|
+
* {@link installContractPackage}.
|
|
28
|
+
*/
|
|
29
|
+
contractRegistry;
|
|
19
30
|
require = undefined;
|
|
20
31
|
constructor(dataContext) {
|
|
21
32
|
this.dataContext = dataContext;
|
|
33
|
+
this.contractRegistry = new registry_1.ContractRegistry();
|
|
34
|
+
(0, system_contracts_1.installSystemContracts)(this.contractRegistry);
|
|
22
35
|
}
|
|
23
36
|
/** Returns true if a package was installed via the contract-based definePackage() path. */
|
|
24
37
|
isContractInstalled(packageId) {
|
|
@@ -208,7 +221,10 @@ class PackageLoader {
|
|
|
208
221
|
if (packageDefinition.packageId !== pkg.packageId) {
|
|
209
222
|
console.warn(`[PackageLoader] packageId mismatch: definition has "${packageDefinition.packageId}" but DB record has "${pkg.packageId}"`);
|
|
210
223
|
}
|
|
211
|
-
await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition
|
|
224
|
+
await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
|
|
225
|
+
registry: this.contractRegistry,
|
|
226
|
+
});
|
|
227
|
+
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(this.dataContext, this.contractRegistry, packageDefinition);
|
|
212
228
|
this.contractInstalledPackages.add(packageDefinition.packageId);
|
|
213
229
|
this.correctPackageVersion(pkg, packageDefinition);
|
|
214
230
|
// Build a proper IPeersPackage from the contract definition so callers
|
|
@@ -10,6 +10,8 @@ jest.mock("../data/package-versions", () => ({
|
|
|
10
10
|
}),
|
|
11
11
|
computePackageVersionHash: (version, versionTag, packageBundleFileHash, routesBundleFileHash, uiBundleFileHash) => mockComputePackageVersionHash(version, versionTag, packageBundleFileHash, routesBundleFileHash, uiBundleFileHash),
|
|
12
12
|
}));
|
|
13
|
+
const registry_1 = require("../contracts/registry");
|
|
14
|
+
const system_ids_1 = require("../system-ids");
|
|
13
15
|
const package_loader_1 = require("./package-loader");
|
|
14
16
|
describe("PackageLoader.correctPackageVersion", () => {
|
|
15
17
|
const activePvId = "pv-active";
|
|
@@ -79,3 +81,18 @@ describe("PackageLoader.correctPackageVersion", () => {
|
|
|
79
81
|
expect(mockSignAndSave).not.toHaveBeenCalled();
|
|
80
82
|
});
|
|
81
83
|
});
|
|
84
|
+
describe("PackageLoader.contractRegistry", () => {
|
|
85
|
+
it("owns an in-memory ContractRegistry per loader instance", () => {
|
|
86
|
+
const loaderA = new package_loader_1.PackageLoader({ dataContextId: "ctx-a" });
|
|
87
|
+
const loaderB = new package_loader_1.PackageLoader({ dataContextId: "ctx-b" });
|
|
88
|
+
expect(loaderA.contractRegistry).toBeInstanceOf(registry_1.ContractRegistry);
|
|
89
|
+
expect(loaderB.contractRegistry).toBeInstanceOf(registry_1.ContractRegistry);
|
|
90
|
+
expect(loaderA.contractRegistry).not.toBe(loaderB.contractRegistry);
|
|
91
|
+
});
|
|
92
|
+
it("installs system contracts into each new registry", () => {
|
|
93
|
+
const loader = new package_loader_1.PackageLoader({ dataContextId: "ctx-system" });
|
|
94
|
+
expect(loader.contractRegistry.getProviderPackageId(system_ids_1.logsContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
95
|
+
expect(loader.contractRegistry.resolve(system_ids_1.logsContractId, 1)?.name).toBe("System Logs");
|
|
96
|
+
expect(loader.contractRegistry.getProviderPackageId(system_ids_1.deviceOperationsContractId, 1)).toBe(system_ids_1.peersSystemPackageId);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DataContext } from "../context/data-context";
|
|
2
|
+
import type { ContractRegistry } from "../contracts/registry";
|
|
3
|
+
import type { IPackageDefinitionResult } from "../contracts/types";
|
|
4
|
+
/**
|
|
5
|
+
* Settle each deferred `consumes` handle on a package definition after its
|
|
6
|
+
* providers have been registered.
|
|
7
|
+
*
|
|
8
|
+
* A consume succeeds only when {@link ContractRegistry.selectProvider} finds a
|
|
9
|
+
* matching provider **and** a live {@link getContractResolver|ContractResolver}
|
|
10
|
+
* exists for that contract version. Missing optional deps settle to `undefined`;
|
|
11
|
+
* missing required deps reject the handle.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveConsumedContracts(dataContext: DataContext, registry: ContractRegistry, packageDefinition: IPackageDefinitionResult): Promise<void>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveConsumedContracts = resolveConsumedContracts;
|
|
4
|
+
const builder_1 = require("../contracts/builder");
|
|
5
|
+
const contract_resolvers_1 = require("../contracts/contract-resolvers");
|
|
6
|
+
const create_local_consumer_1 = require("../contracts/create-local-consumer");
|
|
7
|
+
/**
|
|
8
|
+
* Settle each deferred `consumes` handle on a package definition after its
|
|
9
|
+
* providers have been registered.
|
|
10
|
+
*
|
|
11
|
+
* A consume succeeds only when {@link ContractRegistry.selectProvider} finds a
|
|
12
|
+
* matching provider **and** a live {@link getContractResolver|ContractResolver}
|
|
13
|
+
* exists for that contract version. Missing optional deps settle to `undefined`;
|
|
14
|
+
* missing required deps reject the handle.
|
|
15
|
+
*/
|
|
16
|
+
async function resolveConsumedContracts(dataContext, registry, packageDefinition) {
|
|
17
|
+
for (const dep of packageDefinition.consumes) {
|
|
18
|
+
const handle = dep.handle;
|
|
19
|
+
if (!handle) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const selected = registry.selectProvider(dep);
|
|
24
|
+
if (!selected) {
|
|
25
|
+
settleMissing(dep.optional === true, handle, dep.contractId, dep.version, "no matching provider");
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const resolver = (0, contract_resolvers_1.getContractResolver)(dep.contractId, dep.version);
|
|
29
|
+
if (!resolver) {
|
|
30
|
+
settleMissing(dep.optional === true, handle, dep.contractId, dep.version, `provider ${selected.providerPackageId} has no live resolver`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const resolution = await resolver({ dataContext });
|
|
34
|
+
const consumer = (0, create_local_consumer_1.createLocalContractConsumer)(selected.definition, resolution, {
|
|
35
|
+
dataContextId: dataContext.dataContextId,
|
|
36
|
+
});
|
|
37
|
+
(0, builder_1.settleConsumedContractHandle)(handle, consumer);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
if (dep.optional === true) {
|
|
41
|
+
(0, builder_1.settleConsumedContractHandle)(handle, undefined);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
(0, builder_1.rejectConsumedContractHandle)(handle, err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function settleMissing(optional, handle, contractId, version, reason) {
|
|
50
|
+
if (optional) {
|
|
51
|
+
(0, builder_1.settleConsumedContractHandle)(handle, undefined);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
(0, builder_1.rejectConsumedContractHandle)(handle, new Error(`Required contract ${contractId}@${version} could not be resolved: ${reason}`));
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const builder_1 = require("../contracts/builder");
|
|
4
|
+
const contract_resolvers_1 = require("../contracts/contract-resolvers");
|
|
5
|
+
const registry_1 = require("../contracts/registry");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const resolve_consumed_contracts_1 = require("./resolve-consumed-contracts");
|
|
8
|
+
describe("resolveConsumedContracts", () => {
|
|
9
|
+
const contractId = (0, utils_1.newid)();
|
|
10
|
+
const providerPackageId = (0, utils_1.newid)();
|
|
11
|
+
function mockDataContext() {
|
|
12
|
+
return { dataContextId: "test-ctx" };
|
|
13
|
+
}
|
|
14
|
+
function makeProvidingRegistry() {
|
|
15
|
+
const registry = new registry_1.ContractRegistry();
|
|
16
|
+
registry.register(providerPackageId, {
|
|
17
|
+
contractId,
|
|
18
|
+
version: 1,
|
|
19
|
+
devTag: "dev",
|
|
20
|
+
name: "Fake Contract",
|
|
21
|
+
description: "test",
|
|
22
|
+
tables: [
|
|
23
|
+
{
|
|
24
|
+
name: "Items",
|
|
25
|
+
description: "Items table",
|
|
26
|
+
primaryKeyName: "itemId",
|
|
27
|
+
fields: [],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
tools: [],
|
|
31
|
+
observables: [],
|
|
32
|
+
events: [],
|
|
33
|
+
});
|
|
34
|
+
return registry;
|
|
35
|
+
}
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
const resolution = {
|
|
38
|
+
contractId,
|
|
39
|
+
version: 1,
|
|
40
|
+
tables: {
|
|
41
|
+
Items: {
|
|
42
|
+
list: jest.fn().mockResolvedValue([{ itemId: "1" }]),
|
|
43
|
+
count: jest.fn().mockResolvedValue(1),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
(0, contract_resolvers_1.registerContractResolver)(contractId, 1, () => resolution);
|
|
48
|
+
});
|
|
49
|
+
it("settles a required consume with a live local consumer proxy", async () => {
|
|
50
|
+
const registry = makeProvidingRegistry();
|
|
51
|
+
let consumeHandle;
|
|
52
|
+
const packageDefinition = (0, builder_1.definePackage)((pkg) => {
|
|
53
|
+
pkg.packageId = (0, utils_1.newid)();
|
|
54
|
+
pkg.version = "1.0.0";
|
|
55
|
+
consumeHandle = pkg.consumes(contractId, 1);
|
|
56
|
+
});
|
|
57
|
+
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(mockDataContext(), registry, packageDefinition);
|
|
58
|
+
if (!consumeHandle) {
|
|
59
|
+
throw new Error("expected consume handle");
|
|
60
|
+
}
|
|
61
|
+
const consumer = (await consumeHandle.consumer);
|
|
62
|
+
expect(consumer).toBeDefined();
|
|
63
|
+
expect(consumer?.contractId).toBe(contractId);
|
|
64
|
+
expect(consumer?.version).toBe(1);
|
|
65
|
+
await expect(consumer?.tables.Items.list()).resolves.toEqual([{ itemId: "1" }]);
|
|
66
|
+
await expect(consumer?.tables.Items.count()).resolves.toBe(1);
|
|
67
|
+
});
|
|
68
|
+
it("settles optional missing consumes to undefined", async () => {
|
|
69
|
+
const registry = new registry_1.ContractRegistry();
|
|
70
|
+
let consumeHandle;
|
|
71
|
+
const packageDefinition = (0, builder_1.definePackage)((pkg) => {
|
|
72
|
+
pkg.packageId = (0, utils_1.newid)();
|
|
73
|
+
pkg.version = "1.0.0";
|
|
74
|
+
consumeHandle = pkg.consumes(contractId, 1, { optional: true });
|
|
75
|
+
});
|
|
76
|
+
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(mockDataContext(), registry, packageDefinition);
|
|
77
|
+
if (!consumeHandle) {
|
|
78
|
+
throw new Error("expected consume handle");
|
|
79
|
+
}
|
|
80
|
+
await expect(consumeHandle.consumer).resolves.toBeUndefined();
|
|
81
|
+
});
|
|
82
|
+
it("rejects required consumes when no provider is available", async () => {
|
|
83
|
+
const registry = new registry_1.ContractRegistry();
|
|
84
|
+
let consumeHandle;
|
|
85
|
+
const packageDefinition = (0, builder_1.definePackage)((pkg) => {
|
|
86
|
+
pkg.packageId = (0, utils_1.newid)();
|
|
87
|
+
pkg.version = "1.0.0";
|
|
88
|
+
consumeHandle = pkg.consumes(contractId, 1);
|
|
89
|
+
});
|
|
90
|
+
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(mockDataContext(), registry, packageDefinition);
|
|
91
|
+
if (!consumeHandle) {
|
|
92
|
+
throw new Error("expected consume handle");
|
|
93
|
+
}
|
|
94
|
+
await expect(consumeHandle.consumer).rejects.toThrow(/could not be resolved/);
|
|
95
|
+
});
|
|
96
|
+
it("treats a registered provider without a live resolver as missing", async () => {
|
|
97
|
+
const noResolverContractId = (0, utils_1.newid)();
|
|
98
|
+
const registry = new registry_1.ContractRegistry();
|
|
99
|
+
registry.register(providerPackageId, {
|
|
100
|
+
contractId: noResolverContractId,
|
|
101
|
+
version: 1,
|
|
102
|
+
devTag: "dev",
|
|
103
|
+
name: "No Resolver",
|
|
104
|
+
description: "test",
|
|
105
|
+
tables: [],
|
|
106
|
+
tools: [],
|
|
107
|
+
observables: [],
|
|
108
|
+
events: [],
|
|
109
|
+
});
|
|
110
|
+
expect((0, contract_resolvers_1.getContractResolver)(noResolverContractId, 1)).toBeUndefined();
|
|
111
|
+
let consumeHandle;
|
|
112
|
+
const packageDefinition = (0, builder_1.definePackage)((pkg) => {
|
|
113
|
+
pkg.packageId = (0, utils_1.newid)();
|
|
114
|
+
pkg.version = "1.0.0";
|
|
115
|
+
consumeHandle = pkg.consumes(noResolverContractId, 1, { optional: true });
|
|
116
|
+
});
|
|
117
|
+
await (0, resolve_consumed_contracts_1.resolveConsumedContracts)(mockDataContext(), registry, packageDefinition);
|
|
118
|
+
if (!consumeHandle) {
|
|
119
|
+
throw new Error("expected consume handle");
|
|
120
|
+
}
|
|
121
|
+
await expect(consumeHandle.consumer).resolves.toBeUndefined();
|
|
122
|
+
});
|
|
123
|
+
});
|