@pmate/chat-sdk 0.12.0-next.51 → 0.12.0-next.53

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.
@@ -264,7 +264,8 @@ export class ChatSession {
264
264
  this.rememberHistoryEdge(threadId, page);
265
265
  return page;
266
266
  }
267
- confirmMessages(messages) {
267
+ async confirmMessages(messages) {
268
+ await this.sender.initialize();
268
269
  // Only this profile can create its outgoing wire hashes. Keep the legacy
269
270
  // fallback when a generic host has not supplied an author on stored rows.
270
271
  const hashes = messages.filter(message => {
@@ -127,6 +127,9 @@ export declare class IndexedDbReadIndex implements ReadIndexPersistence {
127
127
  constructor(factory?: IDBFactory, name?: string, storageScope?: string | undefined, migrateLegacy?: boolean, metadataOnly?: boolean);
128
128
  private transaction;
129
129
  private key;
130
+ /** Keyset pages avoid materializing a second, unbounded array of durable rows.
131
+ * Keep the caller's transaction so every page belongs to one consistent view. */
132
+ private readPages;
130
133
  private hydrate;
131
134
  private loadCurrent;
132
135
  private loadStored;
@@ -269,22 +269,33 @@ export class IndexedDbReadIndex {
269
269
  throw new Error("READ_STORAGE_UNAVAILABLE");
270
270
  }
271
271
  key(appId, profileId) { return this.storageScope === undefined ? [appId, profileId] : [appId, profileId, this.storageScope]; }
272
+ /** Keyset pages avoid materializing a second, unbounded array of durable rows.
273
+ * Keep the caller's transaction so every page belongs to one consistent view. */
274
+ async readPages(table, owner, key, visit) {
275
+ let after;
276
+ for (;;) {
277
+ const rows = await table.where(":id").between(after ?? [owner, Dexie.minKey], [owner, Dexie.maxKey], !after, true).limit(128).toArray();
278
+ for (const row of rows)
279
+ visit(row);
280
+ if (rows.length < 128)
281
+ return;
282
+ after = key(rows[rows.length - 1]);
283
+ }
284
+ }
272
285
  async hydrate(current, key) {
273
286
  if (current.normalized) {
274
287
  const owner = JSON.stringify(key);
275
- const [threads, messages, positions] = await Promise.all([
276
- this.threadRows.where("owner").equals(owner).toArray(),
277
- this.messageRows.where("owner").equals(owner).toArray(),
278
- this.positionRows.where("owner").equals(owner).toArray(),
279
- ]);
280
288
  const byThread = new Map();
281
- for (const row of messages) {
282
- if (!byThread.has(row.thread))
283
- byThread.set(row.thread, new Map());
284
- byThread.get(row.thread).set(row.id, row.value);
285
- }
286
- const byPosition = new Map(positions.map(row => [row.id, row]));
287
- const byId = new Map(threads.map(row => [row.id, row]));
289
+ const byPosition = new Map(), byId = new Map();
290
+ await Promise.all([
291
+ this.readPages(this.threadRows, owner, row => [row.owner, row.id], row => byId.set(row.id, row)),
292
+ this.readPages(this.messageRows, owner, row => [row.owner, row.thread, row.id], row => {
293
+ if (!byThread.has(row.thread))
294
+ byThread.set(row.thread, new Map());
295
+ byThread.get(row.thread).set(row.id, row.value);
296
+ }),
297
+ this.readPages(this.positionRows, owner, row => [row.owner, row.id], row => byPosition.set(row.id, row)),
298
+ ]);
288
299
  const { normalized: _normalized, threadOrder, ...state } = current;
289
300
  const result = { ...state, threads: (threadOrder ?? []).map(id => {
290
301
  const row = byId.get(id);
@@ -494,8 +505,8 @@ export class IndexedDbReadIndex {
494
505
  // Cursors are the mutable cross-document view. Refresh their small rows,
495
506
  // while message metadata remains targeted; otherwise a later batch based
496
507
  // on an older snapshot could regress a different conversation's progress.
497
- const allPositions = await this.positionRows.where("owner").equals(owner).toArray();
498
- const positionById = new Map(allPositions.map(position => [position.id, position]));
508
+ const positionById = new Map();
509
+ await this.readPages(this.positionRows, owner, row => [row.owner, row.id], row => positionById.set(row.id, row));
499
510
  if (base.threads.some(thread => !positionById.has(thread.id)))
500
511
  return undefined;
501
512
  const positions = sourceIds.map(id => positionById.get(id));
@@ -538,7 +549,7 @@ export class IndexedDbReadIndex {
538
549
  await this.positionRows.bulkPut(writes);
539
550
  if (JSON.stringify(header.pending) !== JSON.stringify(result.pending) || JSON.stringify(header.observedReadIntents) !== JSON.stringify(result.observedReadIntents))
540
551
  await this.profiles.put({ ...header, pending: result.pending, observedReadIntents: result.observedReadIntents }, key);
541
- const byId = new Map(allPositions.map(position => [position.id, position]));
552
+ const byId = new Map(positionById);
542
553
  result.threads.forEach(thread => byId.set(thread.id, thread));
543
554
  let changed = false;
544
555
  const threads = base.threads.map(thread => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/chat-sdk",
3
- "version": "0.12.0-next.51",
3
+ "version": "0.12.0-next.53",
4
4
  "description": "Cross-platform PMate chat input components, voice input primitives, and ASR helpers",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",