@peers-app/peers-sdk 0.26.5 → 0.26.6

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.
@@ -3,6 +3,7 @@ import type { DataContext } from "../context/data-context";
3
3
  import { GroupMemberRole } from "./group-member-roles";
4
4
  import type { ISaveOptions } from "./orm/data-query";
5
5
  import { Table } from "./orm/table";
6
+ /** Schema for a signed group membership, including its role and soft deletion state. */
6
7
  export declare const groupMemberSchema: z.ZodObject<{
7
8
  groupMemberId: z.ZodEffects<z.ZodString, string, string>;
8
9
  groupId: z.ZodEffects<z.ZodString, string, string>;
@@ -25,18 +26,22 @@ export declare const groupMemberSchema: z.ZodObject<{
25
26
  groupMemberId: string;
26
27
  deleted?: boolean | undefined;
27
28
  }>;
29
+ /** A signed association between a user and a group with an assigned role. */
28
30
  export type IGroupMember = z.infer<typeof groupMemberSchema>;
31
+ /** System table name for group membership records. */
29
32
  export declare const groupMembersTableName = "GroupMembers";
33
+ /** Group membership table with signature checks and signed soft deletion. */
30
34
  export declare class GroupMembersTable extends Table<IGroupMember> {
31
35
  static isPassthrough: boolean;
32
36
  save(groupMember: IGroupMember, opts?: ISaveOptions): Promise<IGroupMember>;
33
37
  signAndSave(groupMember: IGroupMember, opts?: ISaveOptions): Promise<IGroupMember>;
34
38
  delete(groupMemberId: string): Promise<void>;
35
- private static addSignatureToGroupMember;
39
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
36
40
  static enableGroupMemberSigning(fn: (groupMember: IGroupMember) => IGroupMember): void;
37
41
  /** @deprecated Forbidden on GroupsTable; use save() */
38
42
  insert(..._args: Parameters<Table<IGroupMember>["insert"]>): never;
39
43
  /** @deprecated Forbidden on GroupsTable; use save() */
40
44
  update(..._args: Parameters<Table<IGroupMember>["update"]>): never;
41
45
  }
46
+ /** Returns the GroupMembers table for the given context or the current default context. */
42
47
  export declare function GroupMembers(dataContext?: DataContext): GroupMembersTable;
@@ -46,6 +46,7 @@ const table_1 = require("./orm/table");
46
46
  const table_definitions_system_1 = require("./orm/table-definitions.system");
47
47
  const types_1 = require("./orm/types");
48
48
  const users_1 = require("./users");
49
+ /** Schema for a signed group membership, including its role and soft deletion state. */
49
50
  exports.groupMemberSchema = zod_1.z.object({
50
51
  groupMemberId: zod_types_1.zodPeerId,
51
52
  groupId: zod_types_1.zodPeerId.describe("The id of the group"),
@@ -59,6 +60,7 @@ exports.groupMemberSchema = zod_1.z.object({
59
60
  .optional()
60
61
  .describe("Whether this membership has been deleted (soft delete)"),
61
62
  });
63
+ /** System table name for group membership records. */
62
64
  exports.groupMembersTableName = "GroupMembers";
63
65
  const metaData = {
64
66
  name: exports.groupMembersTableName,
@@ -67,6 +69,10 @@ const metaData = {
67
69
  fields: (0, types_1.schemaToFields)(exports.groupMemberSchema),
68
70
  indexes: [{ fields: ["groupId", "userId"], unique: true }],
69
71
  };
72
+ // Symbol.for keeps signer registration shared by separately installed SDK copies.
73
+ const signingKey = Symbol.for("@peers-app/peers-sdk/group-member-signing");
74
+ const signingGlobals = globalThis;
75
+ /** Group membership table with signature checks and signed soft deletion. */
70
76
  let GroupMembersTable = (() => {
71
77
  let _classSuper = table_1.Table;
72
78
  let _instanceExtraInitializers = [];
@@ -96,10 +102,11 @@ let GroupMembersTable = (() => {
96
102
  throw new Error("Group member signature is not valid");
97
103
  }
98
104
  async signAndSave(groupMember, opts) {
99
- if (!GroupMembersTable.addSignatureToGroupMember) {
105
+ const sign = signingGlobals[signingKey];
106
+ if (!sign) {
100
107
  throw new Error("Group member signing must be enabled to sign and save group members. Call GroupMembersTable.enableGroupMemberSigning(fn) to enable it.");
101
108
  }
102
- groupMember = GroupMembersTable.addSignatureToGroupMember(groupMember);
109
+ groupMember = sign.call(GroupMembersTable, groupMember);
103
110
  return this.save(groupMember, {
104
111
  saveAsSnapshot: true,
105
112
  restoreIfDeleted: true,
@@ -118,9 +125,9 @@ let GroupMembersTable = (() => {
118
125
  await this.signAndSave(groupMember);
119
126
  }
120
127
  }
121
- static addSignatureToGroupMember = undefined;
128
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
122
129
  static enableGroupMemberSigning(fn) {
123
- GroupMembersTable.addSignatureToGroupMember = fn;
130
+ signingGlobals[signingKey] = fn;
124
131
  }
125
132
  /** @deprecated Forbidden on GroupsTable; use save() */
126
133
  insert(..._args) {
@@ -138,6 +145,7 @@ let GroupMembersTable = (() => {
138
145
  })();
139
146
  exports.GroupMembersTable = GroupMembersTable;
140
147
  (0, table_definitions_system_1.registerSystemTableDefinition)(metaData, exports.groupMemberSchema, GroupMembersTable);
148
+ /** Returns the GroupMembers table for the given context or the current default context. */
141
149
  function GroupMembers(dataContext) {
142
150
  return (0, user_context_singleton_1.getTableContainer)(dataContext).getTable(metaData, exports.groupMemberSchema, GroupMembersTable);
143
151
  }
@@ -3,6 +3,7 @@ import type { DataContext } from "../context/data-context";
3
3
  import { GroupMemberRole } from "./group-member-roles";
4
4
  import type { ISaveOptions } from "./orm/data-query";
5
5
  import { Table } from "./orm/table";
6
+ /** Schema for a signed group record, including membership policy and group public keys. */
6
7
  export declare const groupSchema: z.ZodObject<{
7
8
  groupId: z.ZodEffects<z.ZodString, string, string>;
8
9
  name: z.ZodString;
@@ -37,18 +38,22 @@ export declare const groupSchema: z.ZodObject<{
37
38
  iconClassName?: string | undefined;
38
39
  publicRole?: GroupMemberRole | undefined;
39
40
  }>;
41
+ /** A signed group record shared between personal and group contexts. */
40
42
  export type IGroup = z.infer<typeof groupSchema>;
43
+ /** System table name for group records. */
41
44
  export declare const groupsTableName = "Groups";
45
+ /** Group table with signature enforcement outside the personal context. */
42
46
  export declare class GroupsTable extends Table<IGroup> {
43
47
  static isPassthrough: boolean;
44
48
  save(group: IGroup, opts?: ISaveOptions): Promise<IGroup>;
45
49
  signAndSave(group: IGroup, opts?: ISaveOptions): Promise<IGroup>;
46
50
  delete(groupId: string): Promise<void>;
47
- private static addSignatureToGroup;
51
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
48
52
  static enableGroupSigning(fn: (group: IGroup) => IGroup): void;
49
53
  /** @deprecated Direct inserts forbidden; use save() */
50
54
  insert(..._args: Parameters<Table<IGroup>["insert"]>): never;
51
55
  /** @deprecated Direct updates forbidden; use save() */
52
56
  update(..._args: Parameters<Table<IGroup>["update"]>): never;
53
57
  }
58
+ /** Returns the Groups table for the given context or the current default context. */
54
59
  export declare function Groups(dataContext?: DataContext): GroupsTable;
@@ -45,6 +45,7 @@ const decorators_1 = require("./orm/decorators");
45
45
  const table_1 = require("./orm/table");
46
46
  const table_definitions_system_1 = require("./orm/table-definitions.system");
47
47
  const types_1 = require("./orm/types");
48
+ /** Schema for a signed group record, including membership policy and group public keys. */
48
49
  exports.groupSchema = zod_1.z.object({
49
50
  groupId: zod_types_1.zodPeerId,
50
51
  name: zod_1.z.string(),
@@ -67,6 +68,7 @@ exports.groupSchema = zod_1.z.object({
67
68
  .string()
68
69
  .describe("The public key used to encrypt data that only group members with writer permissions or higher can decrypt"),
69
70
  });
71
+ /** System table name for group records. */
70
72
  exports.groupsTableName = "Groups";
71
73
  const metaData = {
72
74
  name: exports.groupsTableName,
@@ -76,6 +78,10 @@ const metaData = {
76
78
  iconClassName: "bi bi-people-fill",
77
79
  indexes: [{ fields: ["name"] }],
78
80
  };
81
+ // Symbol.for keeps signer registration shared by separately installed SDK copies.
82
+ const signingKey = Symbol.for("@peers-app/peers-sdk/group-signing");
83
+ const signingGlobals = globalThis;
84
+ /** Group table with signature enforcement outside the personal context. */
79
85
  let GroupsTable = (() => {
80
86
  let _classSuper = table_1.Table;
81
87
  let _instanceExtraInitializers = [];
@@ -103,10 +109,11 @@ let GroupsTable = (() => {
103
109
  return super.save(group, opts);
104
110
  }
105
111
  async signAndSave(group, opts) {
106
- if (!GroupsTable.addSignatureToGroup) {
112
+ const sign = signingGlobals[signingKey];
113
+ if (!sign) {
107
114
  throw new Error("Group signing must be enabled to sign and save groups. Call GroupsTable.enableGroupSigning(fn) to enable it.");
108
115
  }
109
- group = GroupsTable.addSignatureToGroup(group);
116
+ group = sign.call(GroupsTable, group);
110
117
  return this.save(group, opts);
111
118
  }
112
119
  async delete(groupId) {
@@ -117,9 +124,9 @@ let GroupsTable = (() => {
117
124
  }
118
125
  throw new Error("Only deleting groups from the user context Groups table is allowed");
119
126
  }
120
- static addSignatureToGroup = undefined;
127
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
121
128
  static enableGroupSigning(fn) {
122
- GroupsTable.addSignatureToGroup = fn;
129
+ signingGlobals[signingKey] = fn;
123
130
  }
124
131
  /** @deprecated Direct inserts forbidden; use save() */
125
132
  insert(..._args) {
@@ -137,6 +144,7 @@ let GroupsTable = (() => {
137
144
  })();
138
145
  exports.GroupsTable = GroupsTable;
139
146
  (0, table_definitions_system_1.registerSystemTableDefinition)(metaData, exports.groupSchema, GroupsTable);
147
+ /** Returns the Groups table for the given context or the current default context. */
140
148
  function Groups(dataContext) {
141
149
  return (0, user_context_singleton_1.getTableContainer)(dataContext).getTable(metaData, exports.groupSchema, GroupsTable);
142
150
  }
@@ -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
- private static addSignatureToPackageVersion;
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
- if (!PackageVersionsTable.addSignatureToPackageVersion) {
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 PackageVersionsTable.addSignatureToPackageVersion(packageVersion);
133
+ packageVersion = await sign.call(PackageVersionsTable, packageVersion);
129
134
  return this.save(packageVersion, opts);
130
135
  }
131
- static addSignatureToPackageVersion = undefined;
136
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
132
137
  static enablePackageVersionSigning(fn) {
133
- PackageVersionsTable.addSignatureToPackageVersion = fn;
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;
@@ -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
- private static addSignatureToPackage;
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 {};
@@ -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
- if (!PackagesTable.addSignatureToPackage) {
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 PackagesTable.addSignatureToPackage(packageObj);
124
+ packageObj = await sign.call(PackagesTable, packageObj);
120
125
  return this.save(packageObj, opts);
121
126
  }
122
- static addSignatureToPackage = undefined;
127
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
123
128
  static enablePackageSigning(fn) {
124
- PackagesTable.addSignatureToPackage = fn;
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 });
@@ -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
+ });
@@ -84,8 +84,7 @@ export declare class UsersTable extends Table<IUser> {
84
84
  */
85
85
  save(user: IUser, opts?: ISaveOptions): Promise<IUser>;
86
86
  signAndSave(user: IUser, opts?: ISaveOptions): Promise<IUser>;
87
- private static addSignatureToUser;
88
- /** Enables signing for proxied `signAndSave` calls. */
87
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
89
88
  static enableUserSigning(fn: (user: IUser) => IUser): void;
90
89
  /** @deprecated Forbidden on UsersTable; use save() */
91
90
  insert(..._args: Parameters<Table<IUser>["insert"]>): never;
@@ -102,6 +102,9 @@ let userRowChangeGuard = refuseIdentityChanges;
102
102
  function setUserRowChangeGuard(guard) {
103
103
  userRowChangeGuard = guard ?? refuseIdentityChanges;
104
104
  }
105
+ // Symbol.for keeps signer registration shared by separately installed SDK copies.
106
+ const signingKey = Symbol.for("@peers-app/peers-sdk/user-signing");
107
+ const signingGlobals = globalThis;
105
108
  /** User table with owner-signature enforcement outside the personal context. */
106
109
  let UsersTable = (() => {
107
110
  let _classSuper = table_1.Table;
@@ -170,20 +173,20 @@ let UsersTable = (() => {
170
173
  return super.save(verdict.row, opts);
171
174
  }
172
175
  async signAndSave(user, opts) {
173
- if (!UsersTable.addSignatureToUser) {
176
+ const sign = signingGlobals[signingKey];
177
+ if (!sign) {
174
178
  throw new Error("User signing must be enabled to sign and save users. Call UsersTable.enableUserSigning(fn) to enable it.");
175
179
  }
176
180
  user = {
177
181
  ...user,
178
182
  name: user.name.slice(0, devices_1.DISPLAY_NAME_MAX_LENGTH),
179
183
  };
180
- user = UsersTable.addSignatureToUser(user);
184
+ user = sign.call(UsersTable, user);
181
185
  return this.save(user, opts);
182
186
  }
183
- static addSignatureToUser = undefined;
184
- /** Enables signing for proxied `signAndSave` calls. */
187
+ /** Registers the signer for all SDK copies in this JavaScript realm. Replaces any prior signer. */
185
188
  static enableUserSigning(fn) {
186
- UsersTable.addSignatureToUser = fn;
189
+ signingGlobals[signingKey] = fn;
187
190
  }
188
191
  /** @deprecated Forbidden on UsersTable; use save() */
189
192
  insert(..._args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.26.5",
3
+ "version": "0.26.6",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"