@peers-app/peers-sdk 0.23.9 → 0.23.10

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.
@@ -135,12 +135,27 @@ function getAllTables() {
135
135
  return tableMap;
136
136
  }
137
137
  if (!rpc_types_1.isClient) {
138
+ /**
139
+ * Resolve a table for a renderer RPC call.
140
+ *
141
+ * Prefers a full (code/package-registered) instance. When no package on this
142
+ * device has registered `tableName`, falls back to a synced
143
+ * `TableDefinitions` record so a renderer whose UI bundle is ahead of the
144
+ * package the host loaded can still reach the table's data.
145
+ */
138
146
  async function getTable(dataContextId, tableName) {
139
147
  const userContext = await getUserContext();
140
- const table = userContext
141
- .getDataContext(dataContextId || userContext.userId)
142
- .tableContainer.getTable(tableName);
143
- return table;
148
+ const tableContainer = userContext.getDataContext(dataContextId || userContext.userId).tableContainer;
149
+ try {
150
+ return tableContainer.getTable(tableName);
151
+ }
152
+ catch (err) {
153
+ const fallback = await tableContainer.getFallbackTable(tableName);
154
+ if (fallback) {
155
+ return fallback;
156
+ }
157
+ throw err;
158
+ }
144
159
  }
145
160
  // TODO: add check that client has permission to make this call
146
161
  rpc_types_1.rpcServerCalls.tableMethodCall = async (dataContextId, tableName, methodName, ...args) => {
@@ -123,12 +123,20 @@ export declare function groupVar<T = string>(name: string, opts?: Omit<IPersiste
123
123
  /**
124
124
  * A persistent variable that is stored only on this device, in the user's personal db.
125
125
  * It can have a different value for each group on the device but is not synced to other devices.
126
+ *
127
+ * The record always lives in the personal db. `opts.dataContext` (or, when omitted, the
128
+ * current default group) only selects which group's value is addressed by suffixing the
129
+ * stored name with that data context id, so server-side callers that pin a group and
130
+ * renderer callers that follow the default group share one row.
126
131
  */
127
132
  export declare function groupDeviceVar<T = string>(name: string, opts: Omit<IPersistentVarOptionsWithDefault<T>, "scope">): PersistentVar<T>;
128
133
  export declare function groupDeviceVar<T = string>(name: string, opts?: Omit<IPersistentVarOptionsWithoutDefault<T>, "scope">): PersistentVar<T | undefined>;
129
134
  /**
130
135
  * A persistent variable that is stored in the user's personal db and synced across all their devices in the group.
131
136
  * It can have a different value for each user in the group and a different value for each group the user is in.
137
+ *
138
+ * As with {@link groupDeviceVar}, the record always lives in the personal db; `opts.dataContext`
139
+ * only selects the group whose value is addressed.
132
140
  */
133
141
  export declare function groupUserVar<T = string>(name: string, opts: Omit<IPersistentVarOptionsWithDefault<T>, "scope">): PersistentVar<T>;
134
142
  export declare function groupUserVar<T = string>(name: string, opts?: Omit<IPersistentVarOptionsWithoutDefault<T>, "scope">): PersistentVar<T | undefined>;
@@ -277,26 +277,71 @@ function persistentVarFactory(name, opts) {
277
277
  void (async () => {
278
278
  try {
279
279
  const userContext = opts?.userContext || (await (0, user_context_singleton_1.getUserContext)());
280
- function getDataContext() {
281
- if (opts?.dataContext) {
282
- return opts.dataContext;
283
- }
280
+ /**
281
+ * The data context whose `PersistentVars` table stores this variable.
282
+ *
283
+ * Only `group` scope lives in a group DB. Every other scope lives in the
284
+ * user's personal DB; for `groupDevice` / `groupUser` the route context
285
+ * (`opts.dataContext` or the current default group) only disambiguates
286
+ * the stored name (see {@link getVarNameInDb}). Storing the record in
287
+ * the personal DB regardless of whether a caller pinned a `dataContext`
288
+ * keeps server-side callers (which pin) and renderer callers (which
289
+ * don't) reading and writing the same row.
290
+ */
291
+ function getStorageDataContext() {
284
292
  if (scope === "group") {
285
- return userContext.defaultDataContext();
293
+ return opts?.dataContext ?? userContext.defaultDataContext();
294
+ }
295
+ return userContext.userDataContext;
296
+ }
297
+ /**
298
+ * Before storage was unified in the personal DB, a `groupDevice` /
299
+ * `groupUser` var created with an explicit group `dataContext` was
300
+ * written to that group's DB. Return that legacy location so an
301
+ * existing value can be migrated forward, or `undefined` when there is
302
+ * no distinct legacy location for this variable.
303
+ */
304
+ function getLegacyStorageDataContext() {
305
+ if (scope !== "groupDevice" && scope !== "groupUser") {
306
+ return undefined;
286
307
  }
287
- else {
288
- return userContext.userDataContext;
308
+ const pinned = opts?.dataContext;
309
+ if (!pinned || pinned.dataContextId === userContext.userDataContext.dataContextId) {
310
+ return undefined;
289
311
  }
312
+ return pinned;
290
313
  }
291
314
  function getVarNameInDb() {
292
315
  const dc = opts?.dataContext || userContext.defaultDataContext();
293
316
  return (0, persistent_var_identity_1.persistentVarStorageName)(name, scope, dc?.dataContextId);
294
317
  }
318
+ /**
319
+ * Copy a record left in a group DB by the pre-unification storage rule
320
+ * into the personal DB. Secrets are skipped because their ciphertext is
321
+ * bound to the group key and cannot be moved verbatim. The legacy row is
322
+ * left in place: older builds on other devices may still read it, and
323
+ * the personal copy is authoritative from here on.
324
+ */
325
+ async function migrateLegacyRec(table, name) {
326
+ const legacyContext = getLegacyStorageDataContext();
327
+ if (!legacyContext) {
328
+ return undefined;
329
+ }
330
+ const legacyRec = await PersistentVars(legacyContext).findOne({ name });
331
+ if (!legacyRec || legacyRec.scope !== scope || legacyRec.isSecret) {
332
+ return undefined;
333
+ }
334
+ const { persistentVarId: _legacyId, ...rest } = legacyRec;
335
+ return table.save({ ...rest, persistentVarId: "" }, { weakInsert: true });
336
+ }
295
337
  async function loadRecFromDb() {
296
- const dc = getDataContext();
338
+ const dc = getStorageDataContext();
297
339
  const table = PersistentVars(dc);
298
340
  const name = getVarNameInDb();
299
341
  let dbRec = await table.findOne({ name });
342
+ if (!dbRec) {
343
+ dbRec = await migrateLegacyRec(table, name);
344
+ }
300
345
  if (dbRec) {
301
346
  if (dbRec.scope !== scope) {
302
347
  console.warn(`${name}: deleting old persistent var record because scopes don't match. If this is unexpected there could be two different persistent variables using the same name`);
@@ -340,7 +385,7 @@ function persistentVarFactory(name, opts) {
340
385
  return;
341
386
  }
342
387
  rec.value.value = value;
343
- const dc = getDataContext();
388
+ const dc = getStorageDataContext();
344
389
  const table = PersistentVars(dc);
345
390
  try {
346
391
  rec = await table.save(rec, { weakInsert: true });
@@ -362,7 +407,7 @@ function persistentVarFactory(name, opts) {
362
407
  }
363
408
  }
364
409
  deleteVarImpl = async () => {
365
- const dc = getDataContext();
410
+ const dc = getStorageDataContext();
366
411
  const table = PersistentVars(dc);
367
412
  if (rec?.persistentVarId) {
368
413
  await table.delete(rec);
@@ -374,7 +419,7 @@ function persistentVarFactory(name, opts) {
374
419
  };
375
420
  // subscribe to db changes
376
421
  userContext.subscribeToDataChangedAcrossAllGroups(exports.persistentVarsMetaData.name, async (evt) => {
377
- const dc = getDataContext();
422
+ const dc = getStorageDataContext();
378
423
  if (dc && evt.dataContext && evt.dataContext.dataContextId !== dc.dataContextId) {
379
424
  return;
380
425
  }
@@ -50,8 +50,8 @@ export declare const toolAccessApprovalSchema: z.ZodObject<{
50
50
  userId: string;
51
51
  expiresAt: Date;
52
52
  toolId: string;
53
- createdAt: Date;
54
53
  dataContextId: string;
54
+ createdAt: Date;
55
55
  toolAccessApprovalId: string;
56
56
  toolName: string;
57
57
  argsHash: string;
@@ -106,8 +106,8 @@ export declare function ToolAccessApprovals(dataContext?: DataContext): import("
106
106
  userId: string;
107
107
  expiresAt: Date;
108
108
  toolId: string;
109
- createdAt: Date;
110
109
  dataContextId: string;
110
+ createdAt: Date;
111
111
  toolAccessApprovalId: string;
112
112
  toolName: string;
113
113
  argsHash: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.23.9",
3
+ "version": "0.23.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"