@peers-app/peers-sdk 0.21.1 → 0.21.3

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.
Files changed (44) hide show
  1. package/dist/context/user-context.d.ts +10 -3
  2. package/dist/context/user-context.js +12 -12
  3. package/dist/contracts/__tests__/builder.test.js +158 -4
  4. package/dist/contracts/__tests__/contract-resolvers.test.js +94 -0
  5. package/dist/contracts/__tests__/host-contract-transport.test.d.ts +1 -0
  6. package/dist/contracts/__tests__/host-contract-transport.test.js +51 -0
  7. package/dist/contracts/__tests__/registry.test.js +45 -303
  8. package/dist/contracts/__tests__/system-contracts.test.d.ts +1 -0
  9. package/dist/contracts/__tests__/system-contracts.test.js +61 -0
  10. package/dist/contracts/builder.d.ts +43 -2
  11. package/dist/contracts/builder.js +79 -5
  12. package/dist/contracts/contract-proxy.d.ts +61 -21
  13. package/dist/contracts/contract-resolvers.d.ts +20 -0
  14. package/dist/contracts/contract-resolvers.js +57 -0
  15. package/dist/contracts/create-local-consumer.d.ts +12 -0
  16. package/dist/contracts/create-local-consumer.js +19 -0
  17. package/dist/contracts/host-contract-transport.d.ts +38 -0
  18. package/dist/contracts/host-contract-transport.js +107 -0
  19. package/dist/contracts/index.d.ts +8 -4
  20. package/dist/contracts/index.js +29 -1
  21. package/dist/contracts/registry.d.ts +17 -0
  22. package/dist/contracts/registry.js +36 -0
  23. package/dist/contracts/system/device-operations-contract.js +7 -0
  24. package/dist/contracts/system/index.d.ts +10 -0
  25. package/dist/contracts/system/index.js +38 -0
  26. package/dist/contracts/system/logs-contract.d.ts +18 -1
  27. package/dist/contracts/system/logs-contract.js +8 -2
  28. package/dist/contracts/system/network-contracts.d.ts +231 -0
  29. package/dist/contracts/system/network-contracts.js +192 -0
  30. package/dist/contracts/system-contracts.d.ts +47 -0
  31. package/dist/contracts/system-contracts.js +69 -0
  32. package/dist/contracts/types.d.ts +44 -0
  33. package/dist/package-loader/contract-package-loader.test.d.ts +1 -0
  34. package/dist/package-loader/contract-package-loader.test.js +88 -0
  35. package/dist/package-loader/package-loader.d.ts +8 -0
  36. package/dist/package-loader/package-loader.js +17 -1
  37. package/dist/package-loader/package-loader.test.js +17 -0
  38. package/dist/package-loader/resolve-consumed-contracts.d.ts +13 -0
  39. package/dist/package-loader/resolve-consumed-contracts.js +55 -0
  40. package/dist/package-loader/resolve-consumed-contracts.test.d.ts +1 -0
  41. package/dist/package-loader/resolve-consumed-contracts.test.js +123 -0
  42. package/dist/system-ids.d.ts +4 -0
  43. package/dist/system-ids.js +5 -1
  44. package/package.json +1 -1
@@ -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,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
+ });
@@ -32,3 +32,7 @@ export declare const logsContractId = "000peers0logs000000000001";
32
32
  * explicit owner-authorized status, bounded read-only database, and installed-tool operations.
33
33
  */
34
34
  export declare const deviceOperationsContractId = "00mriijkhq94msfe9m2j6quwp";
35
+ /** Contract id for portable read-only network and synchronization diagnostics. */
36
+ export declare const networkDiagnosticsContractId = "00ms1yb50w29oi116uwwrg7rl";
37
+ /** Contract id for explicit local network and synchronization control operations. */
38
+ export declare const networkControlContractId = "00ms1yb50yj9w5qbytl69qfht";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deviceOperationsContractId = exports.logsContractId = exports.peersSystemPackageId = exports.peersCorePublishPublicKey = exports.peersCorePackageRepoUrl = exports.peersCorePackageId = exports.runWorkflowToolId = exports.defaultSendMessageToolId = exports.defaultAssistantRunnerToolId = exports.openCodeAssistantId = exports.defaultAssistantId = exports.peersRootUserId = void 0;
3
+ exports.networkControlContractId = exports.networkDiagnosticsContractId = exports.deviceOperationsContractId = exports.logsContractId = exports.peersSystemPackageId = exports.peersCorePublishPublicKey = exports.peersCorePackageRepoUrl = exports.peersCorePackageId = exports.runWorkflowToolId = exports.defaultSendMessageToolId = exports.defaultAssistantRunnerToolId = exports.openCodeAssistantId = exports.defaultAssistantId = exports.peersRootUserId = void 0;
4
4
  /** Stable user ID that owns built-in Peers system records. */
5
5
  exports.peersRootUserId = "000peers0user000000000001";
6
6
  /** Default assistant used when no user-selected assistant is available. */
@@ -36,3 +36,7 @@ exports.logsContractId = "000peers0logs000000000001";
36
36
  * explicit owner-authorized status, bounded read-only database, and installed-tool operations.
37
37
  */
38
38
  exports.deviceOperationsContractId = "00mriijkhq94msfe9m2j6quwp";
39
+ /** Contract id for portable read-only network and synchronization diagnostics. */
40
+ exports.networkDiagnosticsContractId = "00ms1yb50w29oi116uwwrg7rl";
41
+ /** Contract id for explicit local network and synchronization control operations. */
42
+ exports.networkControlContractId = "00ms1yb50yj9w5qbytl69qfht";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.21.1",
3
+ "version": "0.21.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"