@peers-app/peers-sdk 0.19.14 → 0.20.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.
Files changed (59) hide show
  1. package/dist/contracts/__tests__/builder.test.js +24 -0
  2. package/dist/contracts/__tests__/contract-events.test.d.ts +1 -0
  3. package/dist/contracts/__tests__/contract-events.test.js +199 -0
  4. package/dist/contracts/__tests__/contract-observables.test.d.ts +1 -0
  5. package/dist/contracts/__tests__/contract-observables.test.js +290 -0
  6. package/dist/contracts/__tests__/contract-provider-router.test.d.ts +1 -0
  7. package/dist/contracts/__tests__/contract-provider-router.test.js +193 -0
  8. package/dist/contracts/__tests__/contract-proxy.test.d.ts +1 -0
  9. package/dist/contracts/__tests__/contract-proxy.test.js +424 -0
  10. package/dist/contracts/__tests__/contract-resolvers.test.d.ts +1 -0
  11. package/dist/contracts/__tests__/contract-resolvers.test.js +148 -0
  12. package/dist/contracts/__tests__/contract-subscription-lifecycle.test.d.ts +1 -0
  13. package/dist/contracts/__tests__/contract-subscription-lifecycle.test.js +353 -0
  14. package/dist/contracts/__tests__/contract-table-events.test.d.ts +1 -0
  15. package/dist/contracts/__tests__/contract-table-events.test.js +209 -0
  16. package/dist/contracts/__tests__/contract-transport.test.d.ts +1 -0
  17. package/dist/contracts/__tests__/contract-transport.test.js +163 -0
  18. package/dist/contracts/__tests__/persistent-registry.test.d.ts +1 -0
  19. package/dist/contracts/__tests__/persistent-registry.test.js +109 -0
  20. package/dist/contracts/__tests__/playground.test.d.ts +1 -0
  21. package/dist/contracts/__tests__/playground.test.js +213 -0
  22. package/dist/contracts/__tests__/validate.test.js +122 -0
  23. package/dist/contracts/builder.d.ts +9 -1
  24. package/dist/contracts/builder.js +13 -0
  25. package/dist/contracts/contract-provider-router.d.ts +72 -0
  26. package/dist/contracts/contract-provider-router.js +164 -0
  27. package/dist/contracts/contract-proxy.d.ts +333 -0
  28. package/dist/contracts/contract-proxy.js +663 -0
  29. package/dist/contracts/contract-resolvers.d.ts +66 -0
  30. package/dist/contracts/contract-resolvers.js +138 -0
  31. package/dist/contracts/contract-transport.d.ts +94 -0
  32. package/dist/contracts/contract-transport.js +159 -0
  33. package/dist/contracts/contracts.table.d.ts +1 -1
  34. package/dist/contracts/contracts.table.js +2 -2
  35. package/dist/contracts/index.d.ts +6 -1
  36. package/dist/contracts/index.js +25 -1
  37. package/dist/contracts/persistent-registry.js +3 -1
  38. package/dist/contracts/system/logs-contract.d.ts +24 -0
  39. package/dist/contracts/system/logs-contract.js +60 -0
  40. package/dist/contracts/types.d.ts +27 -4
  41. package/dist/contracts/validate.d.ts +2 -2
  42. package/dist/contracts/validate.js +44 -2
  43. package/dist/data/orm/decorators.d.ts +6 -0
  44. package/dist/data/orm/decorators.js +21 -0
  45. package/dist/data/user-trust-levels.d.ts +2 -0
  46. package/dist/data/user-trust-levels.js +2 -1
  47. package/dist/data/user-trust-levels.test.d.ts +1 -0
  48. package/dist/data/user-trust-levels.test.js +37 -0
  49. package/dist/device/binary-peer-connection-v2.d.ts +2 -0
  50. package/dist/device/binary-peer-connection-v2.js +12 -2
  51. package/dist/device/binary-peer-connection-v2.test.js +23 -0
  52. package/dist/device/get-trust-level-fn.d.ts +6 -0
  53. package/dist/device/get-trust-level-fn.js +12 -7
  54. package/dist/device/get-trust-level-fn.test.js +16 -2
  55. package/dist/logging/console-logs.table.d.ts +3 -0
  56. package/dist/logging/console-logs.table.js +2 -1
  57. package/dist/system-ids.d.ts +11 -0
  58. package/dist/system-ids.js +12 -1
  59. package/package.json +1 -1
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LOGS_CONTRACT_VERSION = void 0;
4
+ exports.getLogsContractDefinition = getLogsContractDefinition;
5
+ exports.resolveLogsContract = resolveLogsContract;
6
+ const user_context_singleton_1 = require("../../context/user-context-singleton");
7
+ const types_1 = require("../../data/orm/types");
8
+ const console_logs_table_1 = require("../../logging/console-logs.table");
9
+ const system_ids_1 = require("../../system-ids");
10
+ const contract_resolvers_1 = require("../contract-resolvers");
11
+ /** Version of the built-in System Logs contract. */
12
+ exports.LOGS_CONTRACT_VERSION = 1;
13
+ let _definition;
14
+ /**
15
+ * The first built-in **system contract**: read and write the local device's console logs.
16
+ *
17
+ * It declares only the `ConsoleLogs` table — the table's own methods (`list`, `count`,
18
+ * `deleteOldLogs`, …) are the entire surface we need, so no bespoke tools are required.
19
+ * The table shape is derived from {@link consoleLogSchema} so the contract stays accurate.
20
+ *
21
+ * Built lazily and memoized: reading `consoleLogsMetaData` / `consoleLogSchema` at module
22
+ * load would race the import cycle between the logging table and the SDK barrel, so we defer
23
+ * construction until first use (well after all modules have finished initializing).
24
+ */
25
+ function getLogsContractDefinition() {
26
+ if (!_definition) {
27
+ _definition = {
28
+ contractId: system_ids_1.logsContractId,
29
+ version: exports.LOGS_CONTRACT_VERSION,
30
+ devTag: "dev",
31
+ name: "System Logs",
32
+ description: "Read and write the local device's console logs.",
33
+ tables: [
34
+ {
35
+ name: console_logs_table_1.consoleLogsMetaData.name,
36
+ description: console_logs_table_1.consoleLogsMetaData.description,
37
+ primaryKeyName: console_logs_table_1.consoleLogsMetaData.primaryKeyName,
38
+ fields: (0, types_1.schemaToFields)(console_logs_table_1.consoleLogSchema),
39
+ },
40
+ ],
41
+ tools: [],
42
+ observables: [],
43
+ events: [],
44
+ };
45
+ }
46
+ return _definition;
47
+ }
48
+ /**
49
+ * Resolve the logs contract to its live implementation for a data context: the
50
+ * `ConsoleLogs` table backing that context.
51
+ */
52
+ function resolveLogsContract(ctx) {
53
+ const consoleLogs = (0, user_context_singleton_1.getTableContainer)(ctx.dataContext).getTable(console_logs_table_1.consoleLogsMetaData, console_logs_table_1.consoleLogSchema, console_logs_table_1.ConsoleLogsTable);
54
+ return {
55
+ contractId: system_ids_1.logsContractId,
56
+ version: exports.LOGS_CONTRACT_VERSION,
57
+ tables: { [console_logs_table_1.consoleLogsMetaData.name]: consoleLogs },
58
+ };
59
+ }
60
+ (0, contract_resolvers_1.registerContractResolver)(system_ids_1.logsContractId, exports.LOGS_CONTRACT_VERSION, resolveLogsContract);
@@ -1,11 +1,12 @@
1
1
  import type { Table } from "../data/orm/table";
2
2
  import type { ITableDefinition } from "../data/orm/table-definitions.type";
3
3
  import type { IToolInstance } from "../data/tools";
4
+ import type { Event } from "../events";
4
5
  import type { Observable } from "../observable";
5
6
  import type { FieldType, IField } from "../types/field-type";
6
7
  /**
7
- * A versioned interface definition declaring the shape of tables, tools, and
8
- * observables that a package provides. Pure data — no executable code.
8
+ * A versioned interface definition declaring the shape of tables, tools,
9
+ * observables, and events that a package provides. Pure data — no executable code.
9
10
  */
10
11
  export interface IContractDefinition {
11
12
  contractId: string;
@@ -17,6 +18,12 @@ export interface IContractDefinition {
17
18
  tables: IContractTable[];
18
19
  tools: IContractTool[];
19
20
  observables: IContractObservable[];
21
+ /**
22
+ * Events a provider may emit and consumers may subscribe to. Optional so
23
+ * existing contract definitions stay valid; the {@link ContractBuilder} always
24
+ * emits an array (possibly empty) and consumers read `contract.events ?? []`.
25
+ */
26
+ events?: IContractEvent[];
20
27
  }
21
28
  /** Table shape within a contract — derived from ITableMetaData + IField[]. */
22
29
  export interface IContractTable {
@@ -39,6 +46,18 @@ export interface IContractObservable {
39
46
  valueType: FieldType;
40
47
  writable: boolean;
41
48
  }
49
+ /**
50
+ * A generic event a contract provider may emit. Consumers subscribe by name; the
51
+ * provider only forwards payloads over the transport while at least one consumer
52
+ * has an active subscription (see the provider session). Pure shape — the live
53
+ * {@link Event} is supplied at resolution time in {@link IContractResolution.events}.
54
+ */
55
+ export interface IContractEvent {
56
+ name: string;
57
+ description: string;
58
+ /** Shape of the payload delivered to subscribers when the event fires. */
59
+ payloadFields: IField[];
60
+ }
42
61
  /**
43
62
  * A reference to a contract that a package wants to consume.
44
63
  * Recorded during `definePackage` and checked at install time.
@@ -46,6 +65,7 @@ export interface IContractObservable {
46
65
  export interface IConsumedContract {
47
66
  contractId: string;
48
67
  version: number;
68
+ optional?: boolean;
49
69
  }
50
70
  /**
51
71
  * Declares that the owning contract's implementation also satisfies another
@@ -86,15 +106,18 @@ export interface IResolvedContract {
86
106
  tables: Record<string, Table<any>>;
87
107
  tools: Record<string, IToolInstance>;
88
108
  observables: Record<string, Observable<any>>;
109
+ /** Live events the provider forwards from, keyed by contract event name. */
110
+ events?: Record<string, Event<any>>;
89
111
  }
90
112
  /** Outcome of a validation check. */
91
113
  export interface IValidationResult {
92
114
  valid: boolean;
93
115
  errors: IValidationError[];
94
116
  }
117
+ /** One incompatibility found while validating a contract definition or provider. */
95
118
  export interface IValidationError {
96
- /** Which element failed: `'table'`, `'tool'`, `'observable'`, `'contract'` */
97
- kind: "table" | "tool" | "observable" | "contract" | "field";
119
+ /** Which contract element failed validation. */
120
+ kind: "table" | "tool" | "observable" | "event" | "contract" | "field";
98
121
  /** Name of the element (table name, tool name, etc.) */
99
122
  name: string;
100
123
  message: string;
@@ -1,8 +1,8 @@
1
1
  import type { IAlsoImplementsDeclaration, IContractDefinition, IValidationResult } from "./types";
2
2
  /**
3
3
  * Validate that an implementation contract satisfies a target contract shape.
4
- * The implementation must be a superset: all contract tables, tools, and
5
- * observables must be present with compatible shapes.
4
+ * The implementation must be a superset: all contract tables, tools,
5
+ * observables, and events must be present with compatible shapes.
6
6
  */
7
7
  export declare function validateProviderSatisfiesContract(impl: IContractDefinition, contract: IContractDefinition): IValidationResult;
8
8
  /**
@@ -114,10 +114,36 @@ function validateToolFields(contractTool, implTool, direction) {
114
114
  }
115
115
  return errors;
116
116
  }
117
+ function validateEventFields(contractEvent, implEvent) {
118
+ const errors = [];
119
+ const implFieldMap = new Map(implEvent.payloadFields.map((field) => [field.name, field]));
120
+ for (const contractField of contractEvent.payloadFields) {
121
+ const implField = implFieldMap.get(contractField.name);
122
+ if (!implField) {
123
+ if (!contractField.optional) {
124
+ errors.push({
125
+ kind: "field",
126
+ name: `${contractEvent.name}.payload.${contractField.name}`,
127
+ message: `Required payload field '${contractField.name}' missing from implementation event '${implEvent.name}'`,
128
+ });
129
+ }
130
+ continue;
131
+ }
132
+ const compat = isFieldCompatible(contractField, implField);
133
+ if (compat) {
134
+ errors.push({
135
+ kind: "field",
136
+ name: `${contractEvent.name}.payload.${contractField.name}`,
137
+ message: `Payload field '${contractField.name}' in event '${implEvent.name}': ${compat}`,
138
+ });
139
+ }
140
+ }
141
+ return errors;
142
+ }
117
143
  /**
118
144
  * Validate that an implementation contract satisfies a target contract shape.
119
- * The implementation must be a superset: all contract tables, tools, and
120
- * observables must be present with compatible shapes.
145
+ * The implementation must be a superset: all contract tables, tools,
146
+ * observables, and events must be present with compatible shapes.
121
147
  */
122
148
  function validateProviderSatisfiesContract(impl, contract) {
123
149
  const errors = [];
@@ -184,6 +210,20 @@ function validateProviderSatisfiesContract(impl, contract) {
184
210
  });
185
211
  }
186
212
  }
213
+ // Events
214
+ const implEventMap = new Map((impl.events ?? []).map((event) => [event.name, event]));
215
+ for (const contractEvent of contract.events ?? []) {
216
+ const implEvent = implEventMap.get(contractEvent.name);
217
+ if (!implEvent) {
218
+ errors.push({
219
+ kind: "event",
220
+ name: contractEvent.name,
221
+ message: `Event '${contractEvent.name}' required by contract but not provided by implementation`,
222
+ });
223
+ continue;
224
+ }
225
+ errors.push(...validateEventFields(contractEvent, implEvent));
226
+ }
187
227
  return errors.length === 0 ? ok() : fail(errors);
188
228
  }
189
229
  /**
@@ -212,11 +252,13 @@ function validateImmutability(existing, incoming) {
212
252
  tables: existing.tables,
213
253
  tools: existing.tools,
214
254
  observables: existing.observables,
255
+ events: existing.events ?? [],
215
256
  });
216
257
  const incomingShape = JSON.stringify({
217
258
  tables: incoming.tables,
218
259
  tools: incoming.tools,
219
260
  observables: incoming.observables,
261
+ events: incoming.events ?? [],
220
262
  });
221
263
  if (existingShape !== incomingShape) {
222
264
  return fail([
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Return whether a table method was explicitly marked for remote invocation by
3
+ * {@link ProxyClientTableMethodCalls}. Client proxies are recognized through their
4
+ * marked `__original` implementation as well.
5
+ */
6
+ export declare function isContractRemoteTableMethod(method: unknown): boolean;
1
7
  /**
2
8
  * Decorator that marks a table method as server-side only.
3
9
  * When used on the client side, these methods will be automatically proxied
@@ -1,7 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isContractRemoteTableMethod = isContractRemoteTableMethod;
3
4
  exports.ProxyClientTableMethodCalls = ProxyClientTableMethodCalls;
4
5
  const rpc_types_1 = require("../../rpc-types");
6
+ const contractRemoteTableMethod = Symbol("contractRemoteTableMethod");
7
+ function markContractRemoteTableMethod(method) {
8
+ Object.defineProperty(method, contractRemoteTableMethod, { value: true });
9
+ }
10
+ /**
11
+ * Return whether a table method was explicitly marked for remote invocation by
12
+ * {@link ProxyClientTableMethodCalls}. Client proxies are recognized through their
13
+ * marked `__original` implementation as well.
14
+ */
15
+ function isContractRemoteTableMethod(method) {
16
+ if (typeof method !== "function") {
17
+ return false;
18
+ }
19
+ const candidate = method;
20
+ return Boolean(candidate[contractRemoteTableMethod] || candidate.__original?.[contractRemoteTableMethod]);
21
+ }
5
22
  /**
6
23
  * Decorator that marks a table method as server-side only.
7
24
  * When used on the client side, these methods will be automatically proxied
@@ -13,6 +30,9 @@ const rpc_types_1 = require("../../rpc-types");
13
30
  */
14
31
  function ProxyClientTableMethodCalls() {
15
32
  return (target, context) => {
33
+ if (context.kind === "method") {
34
+ markContractRemoteTableMethod(target);
35
+ }
16
36
  if (context.kind === "method" && rpc_types_1.isClient) {
17
37
  const methodName = context.name;
18
38
  // WARNING: Using typescript's magical `this` param
@@ -21,6 +41,7 @@ function ProxyClientTableMethodCalls() {
21
41
  return rpc_types_1.rpcServerCalls.tableMethodCall(dataContextId, this.tableName, methodName, ...args);
22
42
  };
23
43
  proxyFn.__original = target;
44
+ markContractRemoteTableMethod(proxyFn);
24
45
  return proxyFn;
25
46
  }
26
47
  return target;
@@ -24,7 +24,9 @@ export declare const userTrustLevelSchema: z.ZodObject<{
24
24
  assignedAt?: Date | undefined;
25
25
  notes?: string | undefined;
26
26
  }>;
27
+ /** A trust assignment for one user within a specific data context. */
27
28
  export type IUserTrustLevel = z.infer<typeof userTrustLevelSchema>;
29
+ /** Returns the context-scoped table containing explicit user trust assignments. */
28
30
  export declare function UserTrustLevels(dataContext?: DataContext): import("./orm").Table<{
29
31
  userId: string;
30
32
  trustLevel: TrustLevel;
@@ -33,6 +33,7 @@ const metaData = {
33
33
  indexes: [{ fields: ["trustLevel"] }],
34
34
  };
35
35
  (0, table_definitions_system_1.registerSystemTableDefinition)(metaData, exports.userTrustLevelSchema);
36
+ /** Returns the context-scoped table containing explicit user trust assignments. */
36
37
  function UserTrustLevels(dataContext) {
37
38
  return (0, user_context_singleton_1.getTableContainer)(dataContext).getTable(metaData, exports.userTrustLevelSchema);
38
39
  }
@@ -41,7 +42,7 @@ function UserTrustLevels(dataContext) {
41
42
  * Returns TrustLevel.Unknown if no trust level has been assigned.
42
43
  */
43
44
  async function getUserTrustLevel(dataContext, userId) {
44
- const userContext = await (0, user_context_singleton_1.getUserContext)();
45
+ const userContext = dataContext.userContext;
45
46
  if (dataContext === userContext.userDataContext && userContext.userId === userId) {
46
47
  return socket_type_1.TrustLevel.Self;
47
48
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const socket_type_1 = require("../device/socket.type");
4
+ const utils_1 = require("../utils");
5
+ const user_trust_levels_1 = require("./user-trust-levels");
6
+ function makeDataContext(userId, trustRecord) {
7
+ const get = jest.fn().mockResolvedValue(trustRecord);
8
+ const userContext = { userId };
9
+ const dataContext = {
10
+ userContext,
11
+ tableContainer: {
12
+ getTable: jest.fn().mockReturnValue({ get }),
13
+ },
14
+ };
15
+ Object.assign(userContext, { userDataContext: dataContext });
16
+ return { dataContext, get };
17
+ }
18
+ describe("getUserTrustLevel", () => {
19
+ it("derives Self from the supplied personal context without a global user context", async () => {
20
+ const userId = (0, utils_1.newid)();
21
+ const { dataContext, get } = makeDataContext(userId);
22
+ await expect((0, user_trust_levels_1.getUserTrustLevel)(dataContext, userId)).resolves.toBe(socket_type_1.TrustLevel.Self);
23
+ expect(get).not.toHaveBeenCalled();
24
+ });
25
+ it("reads explicit trust from the supplied context", async () => {
26
+ const remoteUserId = (0, utils_1.newid)();
27
+ const { dataContext, get } = makeDataContext((0, utils_1.newid)(), {
28
+ trustLevel: socket_type_1.TrustLevel.Trusted,
29
+ });
30
+ await expect((0, user_trust_levels_1.getUserTrustLevel)(dataContext, remoteUserId)).resolves.toBe(socket_type_1.TrustLevel.Trusted);
31
+ expect(get).toHaveBeenCalledWith(remoteUserId);
32
+ });
33
+ it("returns Unknown when the supplied context has no trust assignment", async () => {
34
+ const { dataContext } = makeDataContext((0, utils_1.newid)());
35
+ await expect((0, user_trust_levels_1.getUserTrustLevel)(dataContext, (0, utils_1.newid)())).resolves.toBe(socket_type_1.TrustLevel.Unknown);
36
+ });
37
+ });
@@ -35,7 +35,9 @@ export interface IBinaryPeer {
35
35
  */
36
36
  getBufferedAmount?(): number;
37
37
  }
38
+ /** Backwards-compatible name for a WebRTC binary peer. */
38
39
  export type IWebRTCPeer = IBinaryPeer;
40
+ /** Options for wrapping a binary transport in a verified Peers connection. */
39
41
  export interface WrapBinaryPeerOptions {
40
42
  /** Protocol identifier used for server address (e.g., 'wrtc' or 'ws') */
41
43
  protocol: "wrtc" | "ws";
@@ -324,9 +324,19 @@ function createBinaryPeerSocket(connectionId, peer, protocol = "binary") {
324
324
  return;
325
325
  }
326
326
  if (message.type === "call") {
327
- const handler = handlers[message.eventName];
327
+ const eventName = message.eventName;
328
+ const handler = eventName ? handlers[eventName] : undefined;
328
329
  if (!handler) {
329
- console.warn(`No handler for event: ${message.eventName}`);
330
+ const error = eventName
331
+ ? `No handler registered for event: ${eventName}`
332
+ : "RPC call did not include an event name";
333
+ console.warn(error);
334
+ const responseMessage = {
335
+ type: "callback",
336
+ callbackId: message.callbackId,
337
+ error,
338
+ };
339
+ sendRPC(encodeMessage(responseMessage));
330
340
  return;
331
341
  }
332
342
  try {
@@ -117,6 +117,29 @@ describe("binary-peer-connection-v2", () => {
117
117
  iPeer.destroy();
118
118
  rPeer.destroy();
119
119
  });
120
+ it("should return an error when the remote event has no handler", async () => {
121
+ const { iPeer, rPeer } = await getMockPeerPair();
122
+ const socketA = (0, binary_peer_connection_v2_1.createBinaryPeerSocket)("a", iPeer, "ws");
123
+ (0, binary_peer_connection_v2_1.createBinaryPeerSocket)("b", rPeer, "ws");
124
+ const warning = jest.spyOn(console, "warn").mockImplementation(() => { });
125
+ try {
126
+ const response = await new Promise((resolve) => {
127
+ socketA.emit("missing-event", {}, (error, result) => {
128
+ resolve({ error, result });
129
+ });
130
+ });
131
+ expect(response).toEqual({
132
+ error: "No handler registered for event: missing-event",
133
+ result: undefined,
134
+ });
135
+ expect(warning).toHaveBeenCalledWith("No handler registered for event: missing-event");
136
+ }
137
+ finally {
138
+ warning.mockRestore();
139
+ iPeer.destroy();
140
+ rPeer.destroy();
141
+ }
142
+ });
120
143
  it("should handle Uint8Array args without txEncode", async () => {
121
144
  const { iPeer, rPeer } = await getMockPeerPair();
122
145
  const socketA = (0, binary_peer_connection_v2_1.createBinaryPeerSocket)("a", iPeer, "ws");
@@ -1,3 +1,9 @@
1
1
  import { type IDeviceInfo } from "../data";
2
2
  import { TrustLevel } from "./socket.type";
3
+ /**
4
+ * Build the connection-handshake trust evaluator for a local user identity.
5
+ *
6
+ * Known user keys are authoritative even for a newly observed device: a claimed identity
7
+ * whose signing or box key differs from the stored user record is rejected as untrusted.
8
+ */
3
9
  export declare function getTrustLevelFn(me: Pick<IDeviceInfo, "userId" | "publicKey" | "publicBoxKey">, serverUrl?: string): (deviceInfo: IDeviceInfo, _registerNew?: boolean) => Promise<TrustLevel>;
@@ -4,6 +4,12 @@ exports.getTrustLevelFn = getTrustLevelFn;
4
4
  const context_1 = require("../context");
5
5
  const data_1 = require("../data");
6
6
  const socket_type_1 = require("./socket.type");
7
+ /**
8
+ * Build the connection-handshake trust evaluator for a local user identity.
9
+ *
10
+ * Known user keys are authoritative even for a newly observed device: a claimed identity
11
+ * whose signing or box key differs from the stored user record is rejected as untrusted.
12
+ */
7
13
  function getTrustLevelFn(me, serverUrl) {
8
14
  return async function getTrustLevel(deviceInfo, _registerNew) {
9
15
  const userContext = await (0, context_1.getUserContext)();
@@ -67,12 +73,11 @@ function getTrustLevelFn(me, serverUrl) {
67
73
  }
68
74
  const effectiveUser = user ?? groupUser;
69
75
  if (effectiveUser &&
70
- device &&
71
- !(deviceInfo.userId === device.userId &&
72
- deviceInfo.userId === effectiveUser.userId &&
73
- deviceInfo.publicKey === effectiveUser.publicKey &&
74
- deviceInfo.publicBoxKey === effectiveUser.publicBoxKey)) {
75
- // deviceInfo does not align with local info about user and device, return Untrusted
76
+ (deviceInfo.userId !== effectiveUser.userId ||
77
+ deviceInfo.publicKey !== effectiveUser.publicKey ||
78
+ deviceInfo.publicBoxKey !== effectiveUser.publicBoxKey ||
79
+ (device ? deviceInfo.userId !== device.userId : false))) {
80
+ // deviceInfo does not align with local info about the user or device, return Untrusted
76
81
  // TODO check if user has changed their public keys
77
82
  return socket_type_1.TrustLevel.Untrusted;
78
83
  }
@@ -151,7 +156,7 @@ function getTrustLevelFn(me, serverUrl) {
151
156
  // assignedAt: new Date(),
152
157
  // }, { restoreIfDeleted: true });
153
158
  // }
154
- if (newUser) {
159
+ if (newUser && user) {
155
160
  // TODO: I'm not sure we immediately want to save this user to the my personal db...
156
161
  await (0, data_1.Users)(userDataContext).save(user, {
157
162
  restoreIfDeleted: true,
@@ -255,7 +255,7 @@ describe("getTrustLevelFn", () => {
255
255
  const result = await getTrustLevel(other);
256
256
  expect(result).not.toBe(socket_type_1.TrustLevel.Untrusted);
257
257
  });
258
- it("does NOT return Untrusted when only user exists (no device record yet)", async () => {
258
+ it("returns Untrusted for a known user's signing-key mismatch on a new device", async () => {
259
259
  const me = makeTestIdentity();
260
260
  const other = makeTestIdentity();
261
261
  const existingUser = {
@@ -268,7 +268,21 @@ describe("getTrustLevelFn", () => {
268
268
  setupMocks(null, existingUser);
269
269
  const getTrustLevel = (0, get_trust_level_fn_1.getTrustLevelFn)(me);
270
270
  const result = await getTrustLevel(other);
271
- expect(result).not.toBe(socket_type_1.TrustLevel.Untrusted);
271
+ expect(result).toBe(socket_type_1.TrustLevel.Untrusted);
272
+ });
273
+ it("returns Untrusted for a known user's box-key mismatch on a new device", async () => {
274
+ const me = makeTestIdentity();
275
+ const other = makeTestIdentity();
276
+ const existingUser = {
277
+ userId: other.userId,
278
+ publicKey: other.publicKey,
279
+ publicBoxKey: `different_pbk_${(0, utils_1.newid)()}`,
280
+ name: "Alice",
281
+ };
282
+ setupMocks(null, existingUser);
283
+ const getTrustLevel = (0, get_trust_level_fn_1.getTrustLevelFn)(me);
284
+ const result = await getTrustLevel(other);
285
+ expect(result).toBe(socket_type_1.TrustLevel.Untrusted);
272
286
  });
273
287
  });
274
288
  // ─── Trusted fast path ──────────────────────────────────────────────────────
@@ -35,6 +35,7 @@ export declare const consoleLogSchema: z.ZodObject<{
35
35
  export type IConsoleLog = z.infer<typeof consoleLogSchema>;
36
36
  /** Cutoff within this many ms of `Date.now()` is treated as "clear all" (manual clear). */
37
37
  export declare const CLEAR_ALL_CUTOFF_SLACK_MS = 5000;
38
+ declare const metaData: ITableMetaData;
38
39
  /**
39
40
  * Returns whether a cutoff timestamp should trigger a full-table clear (drop + recreate)
40
41
  * rather than a conditional delete. Used when the UI calls `deleteOldLogs(Date.now())`.
@@ -65,3 +66,5 @@ export declare class ConsoleLogsTable extends Table<IConsoleLog> {
65
66
  * Returns the user's personal {@link ConsoleLogsTable} (logs are not synced across devices).
66
67
  */
67
68
  export declare function ConsoleLogs(): Promise<ConsoleLogsTable>;
69
+ /** Table metadata for the {@link ConsoleLogsTable} (name, primary key, fields). */
70
+ export { metaData as consoleLogsMetaData };
@@ -34,7 +34,7 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
34
34
  done = true;
35
35
  };
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.ConsoleLogsTable = exports.CLEAR_ALL_CUTOFF_SLACK_MS = exports.consoleLogSchema = void 0;
37
+ exports.consoleLogsMetaData = exports.ConsoleLogsTable = exports.CLEAR_ALL_CUTOFF_SLACK_MS = exports.consoleLogSchema = void 0;
38
38
  exports.isClearAllCutoff = isClearAllCutoff;
39
39
  exports.ConsoleLogs = ConsoleLogs;
40
40
  const zod_1 = require("zod");
@@ -73,6 +73,7 @@ const metaData = {
73
73
  { fields: ["processInstanceId"] },
74
74
  ],
75
75
  };
76
+ exports.consoleLogsMetaData = metaData;
76
77
  /**
77
78
  * Returns whether a cutoff timestamp should trigger a full-table clear (drop + recreate)
78
79
  * rather than a conditional delete. Used when the UI calls `deleteOldLogs(Date.now())`.
@@ -7,3 +7,14 @@ export declare const runWorkflowToolId = "000peers0tool0runworkflow";
7
7
  export declare const peersCorePackageId = "00mh0wlipjixk2gqmurbwee0o";
8
8
  export declare const peersCorePackageRepoUrl = "https://github.com/peers-app/peers-core";
9
9
  export declare const peersCorePublishPublicKey = "S7cQqnvVz-FVDO6Lk5zC7epSvcP_-NyBVZz9gdcj73c";
10
+ /**
11
+ * Stable id of the peers **system** itself, used as the `providerPackageId` for
12
+ * built-in system contracts (contracts the platform provides rather than a package).
13
+ */
14
+ export declare const peersSystemPackageId = "000peers0system0000000001";
15
+ /**
16
+ * Contract id for the built-in **System Logs** contract (read/write the local device's
17
+ * console logs). This is the first minimal system contract and the proof-of-concept for
18
+ * contract-consumer-based remote RPC. See `contracts/system/logs-contract.ts`.
19
+ */
20
+ export declare const logsContractId = "000peers0logs000000000001";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.peersCorePublishPublicKey = exports.peersCorePackageRepoUrl = exports.peersCorePackageId = exports.runWorkflowToolId = exports.defaultSendMessageToolId = exports.defaultAssistantRunnerToolId = exports.openCodeAssistantId = exports.defaultAssistantId = exports.peersRootUserId = void 0;
3
+ 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
  exports.peersRootUserId = "000peers0user000000000001";
5
5
  exports.defaultAssistantId = "000peers0bot00000000shell";
6
6
  // export const defaultAssistantId = '000peers0bot000000000bot1';
@@ -11,3 +11,14 @@ exports.runWorkflowToolId = "000peers0tool0runworkflow";
11
11
  exports.peersCorePackageId = "00mh0wlipjixk2gqmurbwee0o";
12
12
  exports.peersCorePackageRepoUrl = "https://github.com/peers-app/peers-core";
13
13
  exports.peersCorePublishPublicKey = "S7cQqnvVz-FVDO6Lk5zC7epSvcP_-NyBVZz9gdcj73c";
14
+ /**
15
+ * Stable id of the peers **system** itself, used as the `providerPackageId` for
16
+ * built-in system contracts (contracts the platform provides rather than a package).
17
+ */
18
+ exports.peersSystemPackageId = "000peers0system0000000001";
19
+ /**
20
+ * Contract id for the built-in **System Logs** contract (read/write the local device's
21
+ * console logs). This is the first minimal system contract and the proof-of-concept for
22
+ * contract-consumer-based remote RPC. See `contracts/system/logs-contract.ts`.
23
+ */
24
+ exports.logsContractId = "000peers0logs000000000001";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.19.14",
3
+ "version": "0.20.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"