@peers-app/peers-sdk 0.26.5 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/bin/peers-package.js +13 -0
- package/define/package.json +4 -0
- package/dist/contracts/__tests__/action-dispatch.test.js +56 -76
- package/dist/contracts/__tests__/also-implements.test.d.ts +1 -0
- package/dist/contracts/__tests__/also-implements.test.js +110 -0
- package/dist/contracts/__tests__/builder.test.js +45 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +134 -4
- package/dist/contracts/__tests__/frozen-contracts.test.d.ts +1 -0
- package/dist/contracts/__tests__/frozen-contracts.test.js +191 -0
- package/dist/contracts/__tests__/generic-tool-dispatch.test.js +13 -56
- package/dist/contracts/__tests__/host-contract-client.test.d.ts +1 -0
- package/dist/contracts/__tests__/host-contract-client.test.js +793 -0
- package/dist/contracts/__tests__/host-contract-transport.test.js +15 -0
- package/dist/contracts/__tests__/registry.test.js +68 -0
- package/dist/contracts/__tests__/target-dispatch.d.ts +11 -0
- package/dist/contracts/__tests__/target-dispatch.js +24 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +37 -1
- package/dist/contracts/__tests__/tool-catalog.test.js +12 -3
- package/dist/contracts/__tests__/validate.test.js +177 -7
- package/dist/contracts/__tests__/workflow-context.test.js +0 -11
- package/dist/contracts/builder.d.ts +16 -2
- package/dist/contracts/builder.js +44 -3
- package/dist/contracts/contract-definition-schema.d.ts +251 -0
- package/dist/contracts/contract-definition-schema.js +48 -0
- package/dist/contracts/contract-provider-router.d.ts +2 -0
- package/dist/contracts/contract-provider-router.js +5 -2
- package/dist/contracts/contract-proxy.d.ts +52 -18
- package/dist/contracts/contract-proxy.js +144 -203
- package/dist/contracts/contract-resolvers.d.ts +6 -4
- package/dist/contracts/contract-resolvers.js +32 -9
- package/dist/contracts/frozen-contracts.d.ts +59 -0
- package/dist/contracts/frozen-contracts.js +86 -0
- package/dist/contracts/host-contract-client.d.ts +134 -0
- package/dist/contracts/host-contract-client.js +396 -0
- package/dist/contracts/host-contract-transport.d.ts +2 -0
- package/dist/contracts/host-contract-transport.js +8 -0
- package/dist/contracts/index.d.ts +6 -4
- package/dist/contracts/index.js +11 -5
- package/dist/contracts/registry.d.ts +31 -1
- package/dist/contracts/registry.js +100 -6
- package/dist/contracts/system/contract-catalog-contract.d.ts +61 -6
- package/dist/contracts/system/contract-catalog-contract.js +17 -5
- package/dist/contracts/tool-catalog-service.js +26 -6
- package/dist/contracts/tool-catalog.d.ts +1 -1
- package/dist/contracts/tool-catalog.js +4 -1
- package/dist/contracts/types.d.ts +32 -2
- package/dist/contracts/validate.d.ts +16 -1
- package/dist/contracts/validate.js +139 -25
- package/dist/data/change-tracking.d.ts +17 -5
- package/dist/data/change-tracking.js +22 -6
- package/dist/data/change-tracking.test.d.ts +1 -0
- package/dist/data/change-tracking.test.js +62 -0
- package/dist/data/group-members.d.ts +6 -1
- package/dist/data/group-members.js +12 -4
- package/dist/data/groups.d.ts +6 -1
- package/dist/data/groups.js +12 -4
- package/dist/data/orm/sql.data-source.d.ts +9 -0
- package/dist/data/orm/sql.data-source.js +47 -2
- package/dist/data/orm/sql.data-source.test.js +142 -0
- package/dist/data/orm/table-container.js +9 -9
- package/dist/data/orm/table-container.test.js +44 -0
- package/dist/data/orm/types.d.ts +41 -1
- package/dist/data/orm/types.js +159 -28
- package/dist/data/orm/types.test.js +129 -0
- package/dist/data/package-versions.d.ts +5 -1
- package/dist/data/package-versions.js +11 -4
- package/dist/data/packages.d.ts +7 -1
- package/dist/data/packages.js +13 -4
- package/dist/data/peer-types.d.ts +8 -62
- package/dist/data/persistent-vars.d.ts +4 -1
- package/dist/data/persistent-vars.js +7 -2
- package/dist/data/table-signing.test.d.ts +1 -0
- package/dist/data/table-signing.test.js +118 -0
- package/dist/data/tools.d.ts +29 -221
- package/dist/data/users.d.ts +1 -2
- package/dist/data/users.js +8 -5
- package/dist/define/also-implements.test.d.ts +1 -0
- package/dist/define/also-implements.test.js +123 -0
- package/dist/define/context.d.ts +77 -0
- package/dist/define/context.js +2 -0
- package/dist/define/contract-ref.d.ts +177 -0
- package/dist/define/contract-ref.js +67 -0
- package/dist/define/declare-contract.d.ts +167 -0
- package/dist/define/declare-contract.js +220 -0
- package/dist/define/declare-contract.test.d.ts +1 -0
- package/dist/define/declare-contract.test.js +573 -0
- package/dist/define/define-package.d.ts +203 -0
- package/dist/define/define-package.js +289 -0
- package/dist/define/define.test.d.ts +1 -0
- package/dist/define/define.test.js +1010 -0
- package/dist/define/empty-fields.d.ts +15 -0
- package/dist/define/empty-fields.js +134 -0
- package/dist/define/empty-fields.test.d.ts +1 -0
- package/dist/define/empty-fields.test.js +55 -0
- package/dist/define/events-observables.test.d.ts +1 -0
- package/dist/define/events-observables.test.js +1013 -0
- package/dist/define/events.d.ts +81 -0
- package/dist/define/events.js +81 -0
- package/dist/define/generated-field.d.ts +20 -0
- package/dist/define/generated-field.js +25 -0
- package/dist/define/index.d.ts +28 -0
- package/dist/define/index.js +62 -0
- package/dist/define/lazy-members.d.ts +8 -0
- package/dist/define/lazy-members.js +38 -0
- package/dist/define/manifest.d.ts +63 -0
- package/dist/define/manifest.js +653 -0
- package/dist/define/observables.d.ts +146 -0
- package/dist/define/observables.js +187 -0
- package/dist/define/serve-members.d.ts +21 -0
- package/dist/define/serve-members.js +152 -0
- package/dist/define/serve-members.test.d.ts +1 -0
- package/dist/define/serve-members.test.js +630 -0
- package/dist/define/serve.d.ts +22 -0
- package/dist/define/serve.js +58 -0
- package/dist/define/system-contracts.d.ts +41 -0
- package/dist/define/system-contracts.js +21 -0
- package/dist/define/table-handle.d.ts +13 -0
- package/dist/define/table-handle.js +36 -0
- package/dist/define/table.d.ts +128 -0
- package/dist/define/table.js +122 -0
- package/dist/define/tool.d.ts +129 -0
- package/dist/define/tool.js +91 -0
- package/dist/define/vars.d.ts +82 -0
- package/dist/define/vars.js +256 -0
- package/dist/define/vars.test.d.ts +1 -0
- package/dist/define/vars.test.js +39 -0
- package/dist/define/wire-value.d.ts +12 -0
- package/dist/define/wire-value.js +76 -0
- package/dist/define/worker-state.d.ts +15 -0
- package/dist/define/worker-state.js +45 -0
- package/dist/device/tx-encoding.test.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/observable.d.ts +42 -2
- package/dist/observable.js +25 -7
- package/dist/package-build/build-package.d.ts +60 -0
- package/dist/package-build/build-package.js +312 -0
- package/dist/package-build/build-package.test.d.ts +1 -0
- package/dist/package-build/build-package.test.js +204 -0
- package/dist/package-build/cli.d.ts +7 -0
- package/dist/package-build/cli.js +218 -0
- package/dist/package-build/contract-add.d.ts +35 -0
- package/dist/package-build/contract-add.js +97 -0
- package/dist/package-build/contract-add.test.d.ts +1 -0
- package/dist/package-build/contract-add.test.js +467 -0
- package/dist/package-build/contract-source.d.ts +59 -0
- package/dist/package-build/contract-source.js +471 -0
- package/dist/package-build/freeze-contracts.d.ts +56 -0
- package/dist/package-build/freeze-contracts.js +198 -0
- package/dist/package-build/freeze-contracts.test.d.ts +1 -0
- package/dist/package-build/freeze-contracts.test.js +355 -0
- package/dist/package-installer/package-creator.d.ts +4 -0
- package/dist/package-installer/package-creator.js +57 -16
- package/dist/package-installer/package-creator.test.js +86 -0
- package/dist/package-loader/contract-members.d.ts +53 -0
- package/dist/package-loader/contract-members.js +31 -0
- package/dist/package-loader/contract-package-loader.js +5 -1
- package/dist/package-loader/contract-package-loader.test.js +11 -0
- package/dist/package-loader/host-authority-loading.test.js +9 -5
- package/dist/package-loader/index.d.ts +1 -0
- package/dist/package-loader/index.js +1 -0
- package/dist/package-loader/isolated-artifact.d.ts +80 -17
- package/dist/package-loader/isolated-artifact.js +257 -51
- package/dist/package-loader/isolated-artifact.test.js +294 -21
- package/dist/package-loader/isolated-runtime.d.ts +22 -7
- package/dist/package-loader/isolated-table-gateway.js +30 -11
- package/dist/package-loader/isolated-table-gateway.test.js +243 -0
- package/dist/package-loader/package-loader.d.ts +8 -1
- package/dist/package-loader/package-loader.js +39 -20
- package/dist/package-loader/package-loader.test.js +47 -0
- package/dist/peer-id.d.ts +25 -0
- package/dist/peer-id.js +99 -0
- package/dist/serial-json.compat.test.d.ts +1 -0
- package/dist/serial-json.compat.test.js +369 -0
- package/dist/serial-json.d.ts +41 -5
- package/dist/serial-json.js +216 -157
- package/dist/serial-json.oracle.d.ts +21 -0
- package/dist/serial-json.oracle.js +222 -0
- package/dist/serial-json.test.js +315 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/mock-host-contract.d.ts +123 -0
- package/dist/testing/mock-host-contract.js +434 -0
- package/dist/testing/mock-host-contract.test.d.ts +1 -0
- package/dist/testing/mock-host-contract.test.js +782 -0
- package/dist/types/field-type.d.ts +35 -18
- package/dist/types/field-type.js +4 -1
- package/dist/types/zod-types.d.ts +9 -0
- package/dist/types/zod-types.js +8 -2
- package/dist/utils.d.ts +1 -25
- package/dist/utils.js +10 -90
- package/package.json +9 -2
- package/testing/package.json +4 -0
|
@@ -277,4 +277,133 @@ describe("types", () => {
|
|
|
277
277
|
expect(() => schema.parse({ recordId: validId, label: "hello" })).not.toThrow();
|
|
278
278
|
expect(() => schema.parse({ recordId: "", label: "hello" })).toThrow();
|
|
279
279
|
});
|
|
280
|
+
describe("the richer IR", () => {
|
|
281
|
+
const fields = [
|
|
282
|
+
{ name: "mode", type: field_type_1.FieldType.string, enumValues: ["on", "off"] },
|
|
283
|
+
{ name: "note", type: field_type_1.FieldType.string, optional: true },
|
|
284
|
+
{
|
|
285
|
+
name: "where",
|
|
286
|
+
type: field_type_1.FieldType.object,
|
|
287
|
+
fields: [
|
|
288
|
+
{ name: "city", type: field_type_1.FieldType.string },
|
|
289
|
+
{ name: "unit", type: field_type_1.FieldType.string, enumValues: ["F", "C"], optional: true },
|
|
290
|
+
],
|
|
291
|
+
},
|
|
292
|
+
{ name: "tags", type: field_type_1.FieldType.object, isArray: true },
|
|
293
|
+
];
|
|
294
|
+
const valid = {
|
|
295
|
+
mode: "on",
|
|
296
|
+
where: { city: "Austin", unit: "F", extra: 1 },
|
|
297
|
+
tags: [{ a: 1 }],
|
|
298
|
+
};
|
|
299
|
+
it("fieldsToSchema checks types and keeps an object's keys", () => {
|
|
300
|
+
const schema = (0, types_1.fieldsToSchema)(fields);
|
|
301
|
+
expect(schema.parse(valid)).toEqual(valid);
|
|
302
|
+
// An empty optional field is removed first (withoutEmptyFields); null itself isn't a value.
|
|
303
|
+
expect(schema.safeParse({ ...valid, note: null }).success).toBe(false);
|
|
304
|
+
// Enum values and nested fields describe; the Runtime doesn't enforce them.
|
|
305
|
+
expect(schema.safeParse({ ...valid, mode: "dim", where: { city: 3 } }).success).toBe(true);
|
|
306
|
+
expect(schema.safeParse({ ...valid, mode: 1 }).success).toBe(false);
|
|
307
|
+
expect(schema.safeParse({ ...valid, mode: null }).success).toBe(false);
|
|
308
|
+
expect(schema.safeParse({ ...valid, where: "Austin" }).success).toBe(false);
|
|
309
|
+
});
|
|
310
|
+
it("fieldsToSchema still defers an object to an intersected author schema", () => {
|
|
311
|
+
const codeSchema = zod_1.z.object({ when: zod_1.z.object({ at: zod_1.z.coerce.date() }) });
|
|
312
|
+
const schema = (0, types_1.fieldsToSchema)([{ name: "when", type: field_type_1.FieldType.object }], codeSchema);
|
|
313
|
+
expect(schema.parse({ when: { at: "2026-01-01" } }).when.at).toBeInstanceOf(Date);
|
|
314
|
+
});
|
|
315
|
+
it("fieldsToShapeSchema checks enum values and nested fields", () => {
|
|
316
|
+
const schema = (0, types_1.fieldsToShapeSchema)(fields);
|
|
317
|
+
expect(schema.parse(valid)).toEqual(valid);
|
|
318
|
+
expect(schema.safeParse({ ...valid, mode: "dim" }).success).toBe(false);
|
|
319
|
+
expect(schema.safeParse({ ...valid, where: { city: 3 } }).success).toBe(false);
|
|
320
|
+
expect(schema.safeParse({ ...valid, where: { city: "Austin", unit: "K" } }).success).toBe(false);
|
|
321
|
+
const mixed = (0, types_1.fieldsToShapeSchema)([
|
|
322
|
+
{ name: "size", type: field_type_1.FieldType.any, enumValues: [1, "large", true] },
|
|
323
|
+
{ name: "one", type: field_type_1.FieldType.number, enumValues: [3] },
|
|
324
|
+
]);
|
|
325
|
+
expect(mixed.safeParse({ size: true, one: 3 }).success).toBe(true);
|
|
326
|
+
expect(mixed.safeParse({ size: false, one: 3 }).success).toBe(false);
|
|
327
|
+
expect(mixed.safeParse({ size: 1, one: 4 }).success).toBe(false);
|
|
328
|
+
});
|
|
329
|
+
it("comparableTableFields keeps only the keys a table version is compared on", () => {
|
|
330
|
+
expect((0, types_1.comparableTableFields)([
|
|
331
|
+
{
|
|
332
|
+
type: field_type_1.FieldType.object,
|
|
333
|
+
name: "where",
|
|
334
|
+
defaultValue: {},
|
|
335
|
+
fields: [],
|
|
336
|
+
enumValues: ["x"],
|
|
337
|
+
optional: true,
|
|
338
|
+
future: 1,
|
|
339
|
+
},
|
|
340
|
+
])).toEqual([{ name: "where", type: field_type_1.FieldType.object, optional: true }]);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
describe("withoutEmptyFields", () => {
|
|
344
|
+
const fields = [
|
|
345
|
+
{ name: "title", type: field_type_1.FieldType.string },
|
|
346
|
+
{ name: "note", type: field_type_1.FieldType.string, optional: true },
|
|
347
|
+
{
|
|
348
|
+
name: "where",
|
|
349
|
+
type: field_type_1.FieldType.object,
|
|
350
|
+
optional: true,
|
|
351
|
+
fields: [
|
|
352
|
+
{ name: "city", type: field_type_1.FieldType.string },
|
|
353
|
+
{ name: "unit", type: field_type_1.FieldType.string, optional: true },
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "stops",
|
|
358
|
+
type: field_type_1.FieldType.object,
|
|
359
|
+
isArray: true,
|
|
360
|
+
fields: [{ name: "at", type: field_type_1.FieldType.string, optional: true }],
|
|
361
|
+
},
|
|
362
|
+
{ name: "data", type: field_type_1.FieldType.any, optional: true },
|
|
363
|
+
];
|
|
364
|
+
it("removes null and undefined optional fields, in declared nested fields too", () => {
|
|
365
|
+
const value = {
|
|
366
|
+
title: "a",
|
|
367
|
+
note: null,
|
|
368
|
+
where: { city: "Austin", unit: null },
|
|
369
|
+
stops: [{ at: null }, { at: "noon" }],
|
|
370
|
+
data: { inner: null },
|
|
371
|
+
extra: null,
|
|
372
|
+
};
|
|
373
|
+
expect((0, types_1.withoutEmptyFields)(fields, value)).toEqual({
|
|
374
|
+
title: "a",
|
|
375
|
+
where: { city: "Austin" },
|
|
376
|
+
stops: [{}, { at: "noon" }],
|
|
377
|
+
// Values the fields don't describe, and keys they don't list, are left as they are.
|
|
378
|
+
data: { inner: null },
|
|
379
|
+
extra: null,
|
|
380
|
+
});
|
|
381
|
+
expect(value.note).toBeNull();
|
|
382
|
+
expect(value.where.unit).toBeNull();
|
|
383
|
+
expect((0, types_1.withoutEmptyFields)(fields, { ...value, note: undefined, where: null })).not.toHaveProperty("where");
|
|
384
|
+
});
|
|
385
|
+
it("keeps an empty required field, and returns the value itself when nothing is removed", () => {
|
|
386
|
+
const required = { title: null, stops: [] };
|
|
387
|
+
expect((0, types_1.withoutEmptyFields)(fields, required)).toBe(required);
|
|
388
|
+
const clean = { title: "a", where: { city: "Austin" }, stops: [{ at: "noon" }] };
|
|
389
|
+
expect((0, types_1.withoutEmptyFields)(fields, clean)).toBe(clean);
|
|
390
|
+
expect((0, types_1.withoutEmptyFields)(fields, null)).toBeNull();
|
|
391
|
+
expect((0, types_1.withoutEmptyFields)(fields, ["a"])).toEqual(["a"]);
|
|
392
|
+
});
|
|
393
|
+
it("deletes own undefined keys at every described depth", () => {
|
|
394
|
+
const value = { note: undefined, where: { unit: undefined }, stops: [{ at: undefined }] };
|
|
395
|
+
const cleaned = (0, types_1.withoutEmptyFields)(fields, value);
|
|
396
|
+
expect(cleaned).toStrictEqual({ where: {}, stops: [{}] });
|
|
397
|
+
expect(value).toHaveProperty("note");
|
|
398
|
+
expect(value.where).toHaveProperty("unit");
|
|
399
|
+
});
|
|
400
|
+
it("lets parsing fill a default for null just as for a missing field", () => {
|
|
401
|
+
const defaulted = [{ name: "rank", type: field_type_1.FieldType.number, defaultValue: 5 }];
|
|
402
|
+
for (const value of [{ rank: null }, { rank: undefined }, {}]) {
|
|
403
|
+
const cleaned = (0, types_1.withoutEmptyFields)(defaulted, value);
|
|
404
|
+
expect(cleaned).toStrictEqual({});
|
|
405
|
+
expect((0, types_1.fieldsToSchema)(defaulted).parse(cleaned)).toStrictEqual({ rank: 5 });
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
});
|
|
280
409
|
});
|
|
@@ -107,16 +107,19 @@ declare const schema: z.ZodObject<{
|
|
|
107
107
|
uiBundleFileHash?: string | undefined;
|
|
108
108
|
packageAuthorSignature?: string | undefined;
|
|
109
109
|
}>;
|
|
110
|
+
/** A signed package build and its bundle hashes, tag, and lifecycle history. */
|
|
110
111
|
export type IPackageVersion = z.infer<typeof schema>;
|
|
112
|
+
/** Package version table with signature and group promotion permission checks. */
|
|
111
113
|
export declare class PackageVersionsTable extends Table<IPackageVersion> {
|
|
112
114
|
static isPassthrough: boolean;
|
|
113
115
|
insert(packageVersion: IPackageVersion, opts?: ISaveOptions): Promise<IPackageVersion>;
|
|
114
116
|
update(packageVersion: IPackageVersion, opts?: ISaveOptions): Promise<IPackageVersion>;
|
|
115
117
|
save(packageVersion: IPackageVersion, opts?: ISaveOptions): Promise<IPackageVersion>;
|
|
116
118
|
signAndSave(packageVersion: IPackageVersion, opts?: ISaveOptions): Promise<IPackageVersion>;
|
|
117
|
-
|
|
119
|
+
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
|
118
120
|
static enablePackageVersionSigning(fn: (packageVersion: IPackageVersion) => IPackageVersion): void;
|
|
119
121
|
}
|
|
122
|
+
/** Returns the PackageVersions table for the given context or the current default context. */
|
|
120
123
|
export declare function PackageVersions(dataContext?: DataContext): PackageVersionsTable;
|
|
121
124
|
/**
|
|
122
125
|
* Compute a content hash from bundle file hashes. The hash represents code
|
|
@@ -134,6 +137,7 @@ export declare function PackageVersions(dataContext?: DataContext): PackageVersi
|
|
|
134
137
|
export declare function computePackageVersionHash(version: string,
|
|
135
138
|
/** @deprecated Ignored — will be removed in a future release. */
|
|
136
139
|
_versionTag: string, packageBundleFileHash: string, routesBundleFileHash?: string, uiBundleFileHash?: string): string;
|
|
140
|
+
/** Checks whether an incoming version falls within the selected automatic update range. */
|
|
137
141
|
export declare function isVersionInRange(activeVersion: string, incomingVersion: string, range: "pinned" | "patch" | "minor" | "latest"): boolean;
|
|
138
142
|
/**
|
|
139
143
|
* Returns true if incomingVersion is strictly newer than activeVersion (semver comparison).
|
|
@@ -92,6 +92,10 @@ const metaData = {
|
|
|
92
92
|
fields: (0, types_1.schemaToFields)(schema),
|
|
93
93
|
indexes: [{ fields: ["packageId"] }],
|
|
94
94
|
};
|
|
95
|
+
// Symbol.for keeps signer registration shared by separately installed SDK copies.
|
|
96
|
+
const signingKey = Symbol.for("@peers-app/peers-sdk/package-version-signing");
|
|
97
|
+
const signingGlobals = globalThis;
|
|
98
|
+
/** Package version table with signature and group promotion permission checks. */
|
|
95
99
|
let PackageVersionsTable = (() => {
|
|
96
100
|
let _classSuper = table_1.Table;
|
|
97
101
|
let _instanceExtraInitializers = [];
|
|
@@ -122,15 +126,16 @@ let PackageVersionsTable = (() => {
|
|
|
122
126
|
return super.save(packageVersion, opts);
|
|
123
127
|
}
|
|
124
128
|
async signAndSave(packageVersion, opts) {
|
|
125
|
-
|
|
129
|
+
const sign = signingGlobals[signingKey];
|
|
130
|
+
if (!sign) {
|
|
126
131
|
throw new Error("Package version signing is not enabled. Call PackageVersionsTable.enablePackageVersionSigning(fn) to enable it.");
|
|
127
132
|
}
|
|
128
|
-
packageVersion = await
|
|
133
|
+
packageVersion = await sign.call(PackageVersionsTable, packageVersion);
|
|
129
134
|
return this.save(packageVersion, opts);
|
|
130
135
|
}
|
|
131
|
-
|
|
136
|
+
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
|
132
137
|
static enablePackageVersionSigning(fn) {
|
|
133
|
-
|
|
138
|
+
signingGlobals[signingKey] = fn;
|
|
134
139
|
}
|
|
135
140
|
constructor() {
|
|
136
141
|
super(...arguments);
|
|
@@ -140,6 +145,7 @@ let PackageVersionsTable = (() => {
|
|
|
140
145
|
})();
|
|
141
146
|
exports.PackageVersionsTable = PackageVersionsTable;
|
|
142
147
|
(0, table_definitions_system_1.registerSystemTableDefinition)(metaData, schema, PackageVersionsTable);
|
|
148
|
+
/** Returns the PackageVersions table for the given context or the current default context. */
|
|
143
149
|
function PackageVersions(dataContext) {
|
|
144
150
|
return (0, context_1.getTableContainer)(dataContext).getTable(metaData, schema, PackageVersionsTable);
|
|
145
151
|
}
|
|
@@ -161,6 +167,7 @@ function computePackageVersionHash(version,
|
|
|
161
167
|
_versionTag, packageBundleFileHash, routesBundleFileHash, uiBundleFileHash) {
|
|
162
168
|
return (0, keys_1.hashValue)([version, packageBundleFileHash, routesBundleFileHash ?? "", uiBundleFileHash ?? ""].join(":"));
|
|
163
169
|
}
|
|
170
|
+
/** Checks whether an incoming version falls within the selected automatic update range. */
|
|
164
171
|
function isVersionInRange(activeVersion, incomingVersion, range) {
|
|
165
172
|
if (range === "pinned")
|
|
166
173
|
return false;
|
package/dist/data/packages.d.ts
CHANGED
|
@@ -42,19 +42,25 @@ declare const schema: z.ZodObject<{
|
|
|
42
42
|
followVersionTags?: string | undefined;
|
|
43
43
|
updateUrl?: string | undefined;
|
|
44
44
|
}>;
|
|
45
|
+
/** A signed package record with publisher identity and version selection policy. */
|
|
45
46
|
export type IPackage = z.infer<typeof schema>;
|
|
47
|
+
/** Package table with signature enforcement outside personal contexts. */
|
|
46
48
|
export declare class PackagesTable extends Table<IPackage> {
|
|
47
49
|
static isPassthrough: boolean;
|
|
48
50
|
insert(packageObj: IPackage, opts?: ISaveOptions): Promise<IPackage>;
|
|
49
51
|
update(packageObj: IPackage, opts?: ISaveOptions): Promise<IPackage>;
|
|
50
52
|
save(packageObj: IPackage, opts?: ISaveOptions): Promise<IPackage>;
|
|
51
53
|
signAndSave(packageObj: IPackage, opts?: ISaveOptions): Promise<IPackage>;
|
|
52
|
-
|
|
54
|
+
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
|
53
55
|
static enablePackageSigning(fn: (packageObj: IPackage) => IPackage): void;
|
|
54
56
|
delete(packageId: string): Promise<void>;
|
|
55
57
|
}
|
|
58
|
+
/** Returns the Packages table for the given context or the current default context. */
|
|
56
59
|
export declare function Packages(dataContext?: DataContext): PackagesTable;
|
|
60
|
+
/** Default directory for locally stored package files. */
|
|
57
61
|
export declare const packagesRootDir = "~/peers/packages";
|
|
62
|
+
/** Device preference controlling package reloads when the UI refreshes. */
|
|
58
63
|
export declare const reloadPackagesOnPageRefresh: import("./persistent-vars").PersistentVar<boolean>;
|
|
64
|
+
/** Device preference enabling automatic peers-core updates. */
|
|
59
65
|
export declare const autoUpdatePeersCore: import("./persistent-vars").PersistentVar<boolean>;
|
|
60
66
|
export {};
|
package/dist/data/packages.js
CHANGED
|
@@ -83,6 +83,10 @@ const metaData = {
|
|
|
83
83
|
iconClassName: "bi bi-box-fill",
|
|
84
84
|
fields: (0, types_1.schemaToFields)(schema),
|
|
85
85
|
};
|
|
86
|
+
// Symbol.for keeps signer registration shared by separately installed SDK copies.
|
|
87
|
+
const signingKey = Symbol.for("@peers-app/peers-sdk/package-signing");
|
|
88
|
+
const signingGlobals = globalThis;
|
|
89
|
+
/** Package table with signature enforcement outside personal contexts. */
|
|
86
90
|
let PackagesTable = (() => {
|
|
87
91
|
let _classSuper = table_1.Table;
|
|
88
92
|
let _instanceExtraInitializers = [];
|
|
@@ -113,15 +117,16 @@ let PackagesTable = (() => {
|
|
|
113
117
|
return super.save(packageObj, opts);
|
|
114
118
|
}
|
|
115
119
|
async signAndSave(packageObj, opts) {
|
|
116
|
-
|
|
120
|
+
const sign = signingGlobals[signingKey];
|
|
121
|
+
if (!sign) {
|
|
117
122
|
throw new Error("Package signing is not enabled. Call PackagesTable.enablePackageSigning(fn) to enable it.");
|
|
118
123
|
}
|
|
119
|
-
packageObj = await
|
|
124
|
+
packageObj = await sign.call(PackagesTable, packageObj);
|
|
120
125
|
return this.save(packageObj, opts);
|
|
121
126
|
}
|
|
122
|
-
|
|
127
|
+
/** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
|
|
123
128
|
static enablePackageSigning(fn) {
|
|
124
|
-
|
|
129
|
+
signingGlobals[signingKey] = fn;
|
|
125
130
|
}
|
|
126
131
|
async delete(packageId) {
|
|
127
132
|
// anyone (with write permissions) can delete packages since that isn't a security risk (unless something else is relying on a package to be present?)
|
|
@@ -141,11 +146,15 @@ let PackagesTable = (() => {
|
|
|
141
146
|
})();
|
|
142
147
|
exports.PackagesTable = PackagesTable;
|
|
143
148
|
(0, table_definitions_system_1.registerSystemTableDefinition)(metaData, schema, PackagesTable);
|
|
149
|
+
/** Returns the Packages table for the given context or the current default context. */
|
|
144
150
|
function Packages(dataContext) {
|
|
145
151
|
return (0, context_1.getTableContainer)(dataContext).getTable(metaData, schema, PackagesTable);
|
|
146
152
|
}
|
|
153
|
+
/** Default directory for locally stored package files. */
|
|
147
154
|
exports.packagesRootDir = "~/peers/packages";
|
|
155
|
+
/** Device preference controlling package reloads when the UI refreshes. */
|
|
148
156
|
exports.reloadPackagesOnPageRefresh = (0, persistent_vars_1.deviceVar)("reloadPackagesOnPageRefresh", {
|
|
149
157
|
defaultValue: true,
|
|
150
158
|
});
|
|
159
|
+
/** Device preference enabling automatic peers-core updates. */
|
|
151
160
|
exports.autoUpdatePeersCore = (0, persistent_vars_1.deviceVar)("autoUpdatePeersCore", { defaultValue: true });
|
|
@@ -14,60 +14,22 @@ declare const valueTypeSchema: z.ZodObject<{
|
|
|
14
14
|
optional: z.ZodOptional<z.ZodBoolean>;
|
|
15
15
|
isArray: z.ZodOptional<z.ZodBoolean>;
|
|
16
16
|
subType: z.ZodOptional<z.ZodString>;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
optional?: boolean | undefined;
|
|
21
|
-
description?: string | undefined;
|
|
22
|
-
defaultValue?: any;
|
|
23
|
-
isArray?: boolean | undefined;
|
|
24
|
-
subType?: string | undefined;
|
|
25
|
-
}, {
|
|
26
|
-
name: string;
|
|
27
|
-
type: import("..").FieldType;
|
|
28
|
-
optional?: boolean | undefined;
|
|
29
|
-
description?: string | undefined;
|
|
30
|
-
defaultValue?: any;
|
|
31
|
-
isArray?: boolean | undefined;
|
|
32
|
-
subType?: string | undefined;
|
|
33
|
-
}>, "many">>;
|
|
17
|
+
fields: z.ZodOptional<z.ZodLazy<z.ZodArray<z.ZodType<import("..").IField>>>>;
|
|
18
|
+
enumValues: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, import("..").IField, import("..").IField>, "many">>;
|
|
34
20
|
}, "strip", z.ZodTypeAny, {
|
|
35
21
|
type: import("./tools").IOSchemaType;
|
|
36
|
-
fields:
|
|
37
|
-
name: string;
|
|
38
|
-
type: import("..").FieldType;
|
|
39
|
-
optional?: boolean | undefined;
|
|
40
|
-
description?: string | undefined;
|
|
41
|
-
defaultValue?: any;
|
|
42
|
-
isArray?: boolean | undefined;
|
|
43
|
-
subType?: string | undefined;
|
|
44
|
-
}[];
|
|
22
|
+
fields: import("..").IField[];
|
|
45
23
|
}, {
|
|
46
24
|
type: import("./tools").IOSchemaType;
|
|
47
|
-
fields?:
|
|
48
|
-
name: string;
|
|
49
|
-
type: import("..").FieldType;
|
|
50
|
-
optional?: boolean | undefined;
|
|
51
|
-
description?: string | undefined;
|
|
52
|
-
defaultValue?: any;
|
|
53
|
-
isArray?: boolean | undefined;
|
|
54
|
-
subType?: string | undefined;
|
|
55
|
-
}[] | undefined;
|
|
25
|
+
fields?: import("..").IField[] | undefined;
|
|
56
26
|
}>;
|
|
57
27
|
iconClass: z.ZodOptional<z.ZodString>;
|
|
58
28
|
}, "strip", z.ZodTypeAny, {
|
|
59
29
|
name: string;
|
|
60
30
|
schema: {
|
|
61
31
|
type: import("./tools").IOSchemaType;
|
|
62
|
-
fields:
|
|
63
|
-
name: string;
|
|
64
|
-
type: import("..").FieldType;
|
|
65
|
-
optional?: boolean | undefined;
|
|
66
|
-
description?: string | undefined;
|
|
67
|
-
defaultValue?: any;
|
|
68
|
-
isArray?: boolean | undefined;
|
|
69
|
-
subType?: string | undefined;
|
|
70
|
-
}[];
|
|
32
|
+
fields: import("..").IField[];
|
|
71
33
|
};
|
|
72
34
|
peerTypeId: string;
|
|
73
35
|
description?: string | undefined;
|
|
@@ -76,15 +38,7 @@ declare const valueTypeSchema: z.ZodObject<{
|
|
|
76
38
|
name: string;
|
|
77
39
|
schema: {
|
|
78
40
|
type: import("./tools").IOSchemaType;
|
|
79
|
-
fields?:
|
|
80
|
-
name: string;
|
|
81
|
-
type: import("..").FieldType;
|
|
82
|
-
optional?: boolean | undefined;
|
|
83
|
-
description?: string | undefined;
|
|
84
|
-
defaultValue?: any;
|
|
85
|
-
isArray?: boolean | undefined;
|
|
86
|
-
subType?: string | undefined;
|
|
87
|
-
}[] | undefined;
|
|
41
|
+
fields?: import("..").IField[] | undefined;
|
|
88
42
|
};
|
|
89
43
|
peerTypeId: string;
|
|
90
44
|
description?: string | undefined;
|
|
@@ -95,15 +49,7 @@ export declare function PeerTypes(dataContext?: DataContext): import("..").Table
|
|
|
95
49
|
name: string;
|
|
96
50
|
schema: {
|
|
97
51
|
type: import("./tools").IOSchemaType;
|
|
98
|
-
fields:
|
|
99
|
-
name: string;
|
|
100
|
-
type: import("..").FieldType;
|
|
101
|
-
optional?: boolean | undefined;
|
|
102
|
-
description?: string | undefined;
|
|
103
|
-
defaultValue?: any;
|
|
104
|
-
isArray?: boolean | undefined;
|
|
105
|
-
subType?: string | undefined;
|
|
106
|
-
}[];
|
|
52
|
+
fields: import("..").IField[];
|
|
107
53
|
};
|
|
108
54
|
peerTypeId: string;
|
|
109
55
|
description?: string | undefined;
|
|
@@ -61,7 +61,10 @@ export declare class PersistentVarsTable extends Table<IPersistentVar> {
|
|
|
61
61
|
insert(persistentVar: IPersistentVar): Promise<IPersistentVar>;
|
|
62
62
|
/** Update a persistent variable through the canonical save path. */
|
|
63
63
|
update(persistentVar: IPersistentVar): Promise<IPersistentVar>;
|
|
64
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Save a variable, assigning its deterministic ID and encrypting new secret plaintext. Saves as
|
|
66
|
+
* a snapshot and restores a deleted row unless `opts` says otherwise.
|
|
67
|
+
*/
|
|
65
68
|
save(persistentVar: IPersistentVar, opts?: ISaveOptions): Promise<IPersistentVar>;
|
|
66
69
|
/**
|
|
67
70
|
* Replace a secret variable's stored ciphertext as-is, without passing it through
|
|
@@ -128,7 +128,10 @@ let PersistentVarsTable = (() => {
|
|
|
128
128
|
async update(persistentVar) {
|
|
129
129
|
return this.save(persistentVar);
|
|
130
130
|
}
|
|
131
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Save a variable, assigning its deterministic ID and encrypting new secret plaintext. Saves as
|
|
133
|
+
* a snapshot and restores a deleted row unless `opts` says otherwise.
|
|
134
|
+
*/
|
|
132
135
|
async save(persistentVar, opts) {
|
|
133
136
|
if (!persistentVar.persistentVarId) {
|
|
134
137
|
persistentVar.persistentVarId = (0, utils_1.deterministicPvarId)(persistentVar.name);
|
|
@@ -145,7 +148,9 @@ let PersistentVarsTable = (() => {
|
|
|
145
148
|
}
|
|
146
149
|
persistentVar.modifiedAt = Date.now();
|
|
147
150
|
opts ??= {};
|
|
148
|
-
|
|
151
|
+
// The ID comes from the name, so setting a deleted pvar again, or re-creating it under a new
|
|
152
|
+
// scope, reuses a deleted ID; change tracking refuses that save unless it restores the row.
|
|
153
|
+
opts = { saveAsSnapshot: true, restoreIfDeleted: true, ...opts };
|
|
149
154
|
return super.save(persistentVar, opts);
|
|
150
155
|
}
|
|
151
156
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tableNames = ["package-version", "package", "group", "group-member", "user"];
|
|
4
|
+
// These key strings are the contract between installed SDK versions; do not rename them.
|
|
5
|
+
const signingKey = (name) => Symbol.for(`@peers-app/peers-sdk/${name}-signing`);
|
|
6
|
+
// Restore process-global registrations so these tests never leave test signers installed.
|
|
7
|
+
const priorSigners = tableNames.map((name) => {
|
|
8
|
+
const key = signingKey(name);
|
|
9
|
+
return { key, descriptor: Object.getOwnPropertyDescriptor(globalThis, key) };
|
|
10
|
+
});
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
for (const { key } of priorSigners) {
|
|
13
|
+
Reflect.deleteProperty(globalThis, key);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
for (const { key, descriptor } of priorSigners) {
|
|
18
|
+
if (descriptor) {
|
|
19
|
+
Object.defineProperty(globalThis, key, descriptor);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
Reflect.deleteProperty(globalThis, key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
function loadSigningTables() {
|
|
27
|
+
let tables;
|
|
28
|
+
jest.isolateModules(() => {
|
|
29
|
+
const { PackageVersionsTable } = require("./package-versions");
|
|
30
|
+
const { PackagesTable } = require("./packages");
|
|
31
|
+
const { GroupsTable } = require("./groups");
|
|
32
|
+
const { GroupMembersTable } = require("./group-members");
|
|
33
|
+
const { UsersTable } = require("./users");
|
|
34
|
+
tables = {
|
|
35
|
+
"package-version": {
|
|
36
|
+
prototype: PackageVersionsTable.prototype,
|
|
37
|
+
enable: PackageVersionsTable.enablePackageVersionSigning,
|
|
38
|
+
},
|
|
39
|
+
package: { prototype: PackagesTable.prototype, enable: PackagesTable.enablePackageSigning },
|
|
40
|
+
group: { prototype: GroupsTable.prototype, enable: GroupsTable.enableGroupSigning },
|
|
41
|
+
"group-member": {
|
|
42
|
+
prototype: GroupMembersTable.prototype,
|
|
43
|
+
enable: GroupMembersTable.enableGroupMemberSigning,
|
|
44
|
+
},
|
|
45
|
+
user: { prototype: UsersTable.prototype, enable: UsersTable.enableUserSigning },
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
return tables;
|
|
49
|
+
}
|
|
50
|
+
describe("table signing across SDK module instances", () => {
|
|
51
|
+
it.each(tableNames)("shares and replaces the %s signer across copies", async (name) => {
|
|
52
|
+
const first = loadSigningTables()[name];
|
|
53
|
+
const second = loadSigningTables()[name];
|
|
54
|
+
expect(first.prototype).not.toBe(second.prototype);
|
|
55
|
+
// Stub persistence only: exercise the real signAndSave and registration methods.
|
|
56
|
+
const receiver = { save: jest.fn(async (record) => record) };
|
|
57
|
+
const row = { name: "Example", signature: "" };
|
|
58
|
+
const opts = { saveAsSnapshot: false };
|
|
59
|
+
await expect(second.prototype.signAndSave.call(receiver, row, opts)).rejects.toThrow(/signing.*enabled/i);
|
|
60
|
+
expect(receiver.save).not.toHaveBeenCalled();
|
|
61
|
+
const sign = jest.fn((record) => ({ ...record, signature: "first" }));
|
|
62
|
+
first.enable(sign);
|
|
63
|
+
expect(Reflect.get(globalThis, signingKey(name))).toBe(sign);
|
|
64
|
+
await expect(second.prototype.signAndSave.call(receiver, row, opts)).resolves.toEqual({
|
|
65
|
+
...row,
|
|
66
|
+
signature: "first",
|
|
67
|
+
});
|
|
68
|
+
expect(sign).toHaveBeenCalledWith(row);
|
|
69
|
+
expect(sign.mock.contexts.at(-1)).toBe(second.prototype.constructor);
|
|
70
|
+
expect(receiver.save).toHaveBeenLastCalledWith({ ...row, signature: "first" }, name === "group-member" ? { ...opts, restoreIfDeleted: true } : opts);
|
|
71
|
+
// Loading another copy must preserve a signer that is already registered.
|
|
72
|
+
const third = loadSigningTables()[name];
|
|
73
|
+
expect(third.prototype).not.toBe(first.prototype);
|
|
74
|
+
await expect(third.prototype.signAndSave.call(receiver, row)).resolves.toEqual({
|
|
75
|
+
...row,
|
|
76
|
+
signature: "first",
|
|
77
|
+
});
|
|
78
|
+
expect(sign.mock.contexts.at(-1)).toBe(third.prototype.constructor);
|
|
79
|
+
second.enable((record) => ({ ...record, signature: "replacement" }));
|
|
80
|
+
for (const copy of [first, third]) {
|
|
81
|
+
await expect(copy.prototype.signAndSave.call(receiver, row)).resolves.toEqual({
|
|
82
|
+
...row,
|
|
83
|
+
signature: "replacement",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
receiver.save.mockClear();
|
|
87
|
+
const failsOnce = jest
|
|
88
|
+
.fn((record) => ({ ...record, signature: "recovered" }))
|
|
89
|
+
.mockImplementationOnce(() => {
|
|
90
|
+
throw new Error("signing failed");
|
|
91
|
+
});
|
|
92
|
+
first.enable(failsOnce);
|
|
93
|
+
await expect(second.prototype.signAndSave.call(receiver, row)).rejects.toThrow("signing failed");
|
|
94
|
+
expect(receiver.save).not.toHaveBeenCalled();
|
|
95
|
+
await expect(third.prototype.signAndSave.call(receiver, row)).resolves.toEqual({
|
|
96
|
+
...row,
|
|
97
|
+
signature: "recovered",
|
|
98
|
+
});
|
|
99
|
+
expect(failsOnce).toHaveBeenCalledTimes(2);
|
|
100
|
+
receiver.save.mockRejectedValueOnce(new Error("save failed"));
|
|
101
|
+
await expect(second.prototype.signAndSave.call(receiver, row)).rejects.toThrow("save failed");
|
|
102
|
+
await expect(first.prototype.signAndSave.call(receiver, row)).resolves.toEqual({
|
|
103
|
+
...row,
|
|
104
|
+
signature: "recovered",
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it("keeps the five signer registrations independent", async () => {
|
|
108
|
+
const first = loadSigningTables();
|
|
109
|
+
const second = loadSigningTables();
|
|
110
|
+
for (const name of tableNames) {
|
|
111
|
+
first[name].enable((record) => ({ ...record, signature: name }));
|
|
112
|
+
}
|
|
113
|
+
const receiver = { save: jest.fn(async (record) => record) };
|
|
114
|
+
for (const name of tableNames) {
|
|
115
|
+
await expect(second[name].prototype.signAndSave.call(receiver, { name: "Example", signature: "" })).resolves.toEqual({ name: "Example", signature: name });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|