@anuma/sdk 1.1.5 → 1.1.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.
@@ -1210,50 +1210,85 @@ async function getMessagesPageOp(ctx, convId, opts) {
1210
1210
  )
1211
1211
  );
1212
1212
  }
1213
- async function getMessageSkeletonsOp(ctx, convId) {
1214
- const results = await ctx.messagesCollection.query(Q.where("conversation_id", convId), Q.sortBy("message_id", Q.asc)).fetch();
1215
- const roleById = /* @__PURE__ */ new Map();
1216
- for (const msg of results) {
1217
- roleById.set(msg.id, msg.role);
1218
- }
1219
- const skeletons = results.map((msg) => ({
1220
- uniqueId: msg.id,
1221
- messageId: msg.messageId,
1222
- conversationId: String(msg._getRaw("conversation_id")),
1223
- role: msg.role,
1224
- createdAt: msg.createdAt,
1225
- // WatermelonDB surfaces unset text columns as null at runtime — normalize
1226
- // to undefined so `parentMessageId ?? undefined` keying works everywhere.
1227
- parentMessageId: msg.parentMessageId ?? void 0,
1228
- model: msg.model ?? void 0
1229
- }));
1230
- const artifactIndices = [];
1231
- for (let i = 0; i < results.length; i++) {
1232
- const msg = results[i];
1233
- if (msg.role !== "user" || !msg.parentMessageId) continue;
1234
- if (roleById.get(msg.parentMessageId) === "user") {
1235
- artifactIndices.push(i);
1213
+ var SKELETON_PAGE_SIZE = 2e3;
1214
+ function narrowSkeletonRow(raw) {
1215
+ return {
1216
+ id: raw.id,
1217
+ message_id: raw.message_id,
1218
+ conversation_id: raw.conversation_id,
1219
+ role: raw.role,
1220
+ created_at: raw.created_at,
1221
+ parent_message_id: raw.parent_message_id ?? null,
1222
+ model: raw.model ?? null,
1223
+ content: raw.content ?? null
1224
+ };
1225
+ }
1226
+ async function fetchThreadSkeletonRawPaged(collection, convId, pageSize = SKELETON_PAGE_SIZE) {
1227
+ const all = [];
1228
+ let cursor;
1229
+ for (; ; ) {
1230
+ const clauses = [Q.where("conversation_id", convId)];
1231
+ if (cursor) {
1232
+ clauses.push(
1233
+ Q.or(
1234
+ Q.where("message_id", Q.gt(cursor.messageId)),
1235
+ Q.and(Q.where("message_id", cursor.messageId), Q.where("id", Q.gt(cursor.id)))
1236
+ )
1237
+ );
1236
1238
  }
1239
+ const page = await collection.query(...clauses, Q.sortBy("message_id", Q.asc), Q.sortBy("id", Q.asc), Q.take(pageSize)).unsafeFetchRaw();
1240
+ if (page.length === 0) break;
1241
+ for (const raw of page) all.push(narrowSkeletonRow(raw));
1242
+ if (page.length < pageSize) break;
1243
+ const last = all[all.length - 1];
1244
+ cursor = { messageId: last.message_id, id: last.id };
1245
+ }
1246
+ return all;
1247
+ }
1248
+ function collectArtifactIndices(rows, roleById) {
1249
+ const indices = [];
1250
+ for (let i = 0; i < rows.length; i++) {
1251
+ const row = rows[i];
1252
+ if (row.role !== "user" || !row.parent_message_id) continue;
1253
+ if (roleById.get(row.parent_message_id) === "user") indices.push(i);
1237
1254
  }
1255
+ return indices;
1256
+ }
1257
+ async function decryptArtifactContent(skeletons, rows, artifactIndices, ctx) {
1238
1258
  const address = ctx.walletAddress;
1239
- if (artifactIndices.length > 0 && address) {
1240
- if (ctx.signMessage) {
1241
- try {
1242
- await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
1243
- } catch (error) {
1244
- getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
1245
- }
1246
- }
1247
- await Promise.all(
1248
- artifactIndices.map(async (i) => {
1249
- skeletons[i].content = await decryptField(results[i].content, address);
1250
- })
1251
- );
1252
- } else {
1253
- for (const i of artifactIndices) {
1254
- skeletons[i].content = results[i].content;
1259
+ if (artifactIndices.length === 0 || !address) {
1260
+ for (const i of artifactIndices) skeletons[i].content = rows[i].content ?? void 0;
1261
+ return;
1262
+ }
1263
+ if (ctx.signMessage) {
1264
+ try {
1265
+ await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
1266
+ } catch (error) {
1267
+ getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
1255
1268
  }
1256
1269
  }
1270
+ await Promise.all(
1271
+ artifactIndices.map(async (i) => {
1272
+ skeletons[i].content = await decryptField(rows[i].content ?? "", address);
1273
+ })
1274
+ );
1275
+ }
1276
+ async function getMessageSkeletonsOp(ctx, convId) {
1277
+ const rows = await fetchThreadSkeletonRawPaged(ctx.messagesCollection, convId);
1278
+ const roleById = /* @__PURE__ */ new Map();
1279
+ for (const row of rows) roleById.set(row.id, row.role);
1280
+ const skeletons = rows.map((row) => ({
1281
+ uniqueId: row.id,
1282
+ messageId: row.message_id,
1283
+ conversationId: String(row.conversation_id),
1284
+ role: row.role,
1285
+ createdAt: new Date(row.created_at),
1286
+ // WatermelonDB surfaces unset text columns as null at runtime — normalize
1287
+ // to undefined so `parentMessageId ?? undefined` keying works everywhere.
1288
+ parentMessageId: row.parent_message_id ?? void 0,
1289
+ model: row.model ?? void 0
1290
+ }));
1291
+ await decryptArtifactContent(skeletons, rows, collectArtifactIndices(rows, roleById), ctx);
1257
1292
  return skeletons;
1258
1293
  }
1259
1294
  async function clearMessagesOp(ctx, convId) {
@@ -4680,7 +4715,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
4680
4715
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
4681
4716
  );
4682
4717
  }
4683
- const { recall: recall2 } = await import("./recall-4JTNZBAW.mjs");
4718
+ const { recall: recall2 } = await import("./recall-EMFBX3WU.mjs");
4684
4719
  const result = await recall2(
4685
4720
  query,
4686
4721
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -2265,50 +2265,84 @@ async function getMessagesPageOp(ctx, convId, opts) {
2265
2265
  )
2266
2266
  );
2267
2267
  }
2268
- async function getMessageSkeletonsOp(ctx, convId) {
2269
- const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId), import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc)).fetch();
2270
- const roleById = /* @__PURE__ */ new Map();
2271
- for (const msg of results) {
2272
- roleById.set(msg.id, msg.role);
2273
- }
2274
- const skeletons = results.map((msg) => ({
2275
- uniqueId: msg.id,
2276
- messageId: msg.messageId,
2277
- conversationId: String(msg._getRaw("conversation_id")),
2278
- role: msg.role,
2279
- createdAt: msg.createdAt,
2280
- // WatermelonDB surfaces unset text columns as null at runtime — normalize
2281
- // to undefined so `parentMessageId ?? undefined` keying works everywhere.
2282
- parentMessageId: msg.parentMessageId ?? void 0,
2283
- model: msg.model ?? void 0
2284
- }));
2285
- const artifactIndices = [];
2286
- for (let i = 0; i < results.length; i++) {
2287
- const msg = results[i];
2288
- if (msg.role !== "user" || !msg.parentMessageId) continue;
2289
- if (roleById.get(msg.parentMessageId) === "user") {
2290
- artifactIndices.push(i);
2268
+ function narrowSkeletonRow(raw) {
2269
+ return {
2270
+ id: raw.id,
2271
+ message_id: raw.message_id,
2272
+ conversation_id: raw.conversation_id,
2273
+ role: raw.role,
2274
+ created_at: raw.created_at,
2275
+ parent_message_id: raw.parent_message_id ?? null,
2276
+ model: raw.model ?? null,
2277
+ content: raw.content ?? null
2278
+ };
2279
+ }
2280
+ async function fetchThreadSkeletonRawPaged(collection, convId, pageSize = SKELETON_PAGE_SIZE) {
2281
+ const all = [];
2282
+ let cursor;
2283
+ for (; ; ) {
2284
+ const clauses = [import_watermelondb.Q.where("conversation_id", convId)];
2285
+ if (cursor) {
2286
+ clauses.push(
2287
+ import_watermelondb.Q.or(
2288
+ import_watermelondb.Q.where("message_id", import_watermelondb.Q.gt(cursor.messageId)),
2289
+ import_watermelondb.Q.and(import_watermelondb.Q.where("message_id", cursor.messageId), import_watermelondb.Q.where("id", import_watermelondb.Q.gt(cursor.id)))
2290
+ )
2291
+ );
2291
2292
  }
2293
+ const page = await collection.query(...clauses, import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc), import_watermelondb.Q.sortBy("id", import_watermelondb.Q.asc), import_watermelondb.Q.take(pageSize)).unsafeFetchRaw();
2294
+ if (page.length === 0) break;
2295
+ for (const raw of page) all.push(narrowSkeletonRow(raw));
2296
+ if (page.length < pageSize) break;
2297
+ const last = all[all.length - 1];
2298
+ cursor = { messageId: last.message_id, id: last.id };
2299
+ }
2300
+ return all;
2301
+ }
2302
+ function collectArtifactIndices(rows, roleById) {
2303
+ const indices = [];
2304
+ for (let i = 0; i < rows.length; i++) {
2305
+ const row = rows[i];
2306
+ if (row.role !== "user" || !row.parent_message_id) continue;
2307
+ if (roleById.get(row.parent_message_id) === "user") indices.push(i);
2292
2308
  }
2309
+ return indices;
2310
+ }
2311
+ async function decryptArtifactContent(skeletons, rows, artifactIndices, ctx) {
2293
2312
  const address = ctx.walletAddress;
2294
- if (artifactIndices.length > 0 && address) {
2295
- if (ctx.signMessage) {
2296
- try {
2297
- await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
2298
- } catch (error) {
2299
- getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
2300
- }
2301
- }
2302
- await Promise.all(
2303
- artifactIndices.map(async (i) => {
2304
- skeletons[i].content = await decryptField(results[i].content, address);
2305
- })
2306
- );
2307
- } else {
2308
- for (const i of artifactIndices) {
2309
- skeletons[i].content = results[i].content;
2313
+ if (artifactIndices.length === 0 || !address) {
2314
+ for (const i of artifactIndices) skeletons[i].content = rows[i].content ?? void 0;
2315
+ return;
2316
+ }
2317
+ if (ctx.signMessage) {
2318
+ try {
2319
+ await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
2320
+ } catch (error) {
2321
+ getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
2310
2322
  }
2311
2323
  }
2324
+ await Promise.all(
2325
+ artifactIndices.map(async (i) => {
2326
+ skeletons[i].content = await decryptField(rows[i].content ?? "", address);
2327
+ })
2328
+ );
2329
+ }
2330
+ async function getMessageSkeletonsOp(ctx, convId) {
2331
+ const rows = await fetchThreadSkeletonRawPaged(ctx.messagesCollection, convId);
2332
+ const roleById = /* @__PURE__ */ new Map();
2333
+ for (const row of rows) roleById.set(row.id, row.role);
2334
+ const skeletons = rows.map((row) => ({
2335
+ uniqueId: row.id,
2336
+ messageId: row.message_id,
2337
+ conversationId: String(row.conversation_id),
2338
+ role: row.role,
2339
+ createdAt: new Date(row.created_at),
2340
+ // WatermelonDB surfaces unset text columns as null at runtime — normalize
2341
+ // to undefined so `parentMessageId ?? undefined` keying works everywhere.
2342
+ parentMessageId: row.parent_message_id ?? void 0,
2343
+ model: row.model ?? void 0
2344
+ }));
2345
+ await decryptArtifactContent(skeletons, rows, collectArtifactIndices(rows, roleById), ctx);
2312
2346
  return skeletons;
2313
2347
  }
2314
2348
  async function clearMessagesOp(ctx, convId) {
@@ -2646,7 +2680,7 @@ function makeSyntheticStoredConversation(opts, defaultTitle) {
2646
2680
  isDeleted: false
2647
2681
  };
2648
2682
  }
2649
- var import_watermelondb, import_uuid2;
2683
+ var import_watermelondb, import_uuid2, SKELETON_PAGE_SIZE;
2650
2684
  var init_operations = __esm({
2651
2685
  "src/lib/db/chat/operations.ts"() {
2652
2686
  "use strict";
@@ -2659,6 +2693,7 @@ var init_operations = __esm({
2659
2693
  init_conversationEncryption();
2660
2694
  init_encryption();
2661
2695
  init_types2();
2696
+ SKELETON_PAGE_SIZE = 2e3;
2662
2697
  }
2663
2698
  });
2664
2699
 
@@ -16747,6 +16782,7 @@ var DatabaseManager = class {
16747
16782
  from: this.currentWalletAddress,
16748
16783
  to: walletAddress
16749
16784
  });
16785
+ void this.disposeAdapter(this.database);
16750
16786
  this.database = null;
16751
16787
  }
16752
16788
  if (this.database) {
@@ -16776,11 +16812,37 @@ var DatabaseManager = class {
16776
16812
  async resetDatabase() {
16777
16813
  const db = this.database;
16778
16814
  if (db) {
16815
+ this.database = null;
16816
+ this.currentWalletAddress = void 0;
16779
16817
  await db.write(async () => {
16780
16818
  await db.unsafeResetDatabase();
16781
16819
  });
16782
- this.database = null;
16783
- this.currentWalletAddress = void 0;
16820
+ await this.disposeAdapter(db);
16821
+ }
16822
+ }
16823
+ /**
16824
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
16825
+ *
16826
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
16827
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
16828
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
16829
+ * type it. Client-side implementations are refcounted + idempotent, so calling
16830
+ * this is safe even when the adapter factory also disposes.
16831
+ *
16832
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
16833
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
16834
+ * completes before the caller reopens; fire-and-forget on the synchronous
16835
+ * `getDatabase` path (which cannot block).
16836
+ */
16837
+ async disposeAdapter(db) {
16838
+ try {
16839
+ const underlying = db.adapter?.underlyingAdapter;
16840
+ await underlying?.dispose?.();
16841
+ } catch (error) {
16842
+ this.logger.warn?.("Adapter dispose failed", {
16843
+ component: "DatabaseManager",
16844
+ error
16845
+ });
16784
16846
  }
16785
16847
  }
16786
16848
  /**
@@ -6568,6 +6568,21 @@ declare class DatabaseManager {
6568
6568
  * Reset the current database (useful for logout or testing).
6569
6569
  */
6570
6570
  resetDatabase(): Promise<void>;
6571
+ /**
6572
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
6573
+ *
6574
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
6575
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
6576
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
6577
+ * type it. Client-side implementations are refcounted + idempotent, so calling
6578
+ * this is safe even when the adapter factory also disposes.
6579
+ *
6580
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
6581
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
6582
+ * completes before the caller reopens; fire-and-forget on the synchronous
6583
+ * `getDatabase` path (which cannot block).
6584
+ */
6585
+ private disposeAdapter;
6571
6586
  /**
6572
6587
  * Check and handle schema migrations for a specific wallet.
6573
6588
  * Returns true if a destructive migration is in progress.
@@ -6568,6 +6568,21 @@ declare class DatabaseManager {
6568
6568
  * Reset the current database (useful for logout or testing).
6569
6569
  */
6570
6570
  resetDatabase(): Promise<void>;
6571
+ /**
6572
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
6573
+ *
6574
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
6575
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
6576
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
6577
+ * type it. Client-side implementations are refcounted + idempotent, so calling
6578
+ * this is safe even when the adapter factory also disposes.
6579
+ *
6580
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
6581
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
6582
+ * completes before the caller reopens; fire-and-forget on the synchronous
6583
+ * `getDatabase` path (which cannot block).
6584
+ */
6585
+ private disposeAdapter;
6571
6586
  /**
6572
6587
  * Check and handle schema migrations for a specific wallet.
6573
6588
  * Returns true if a destructive migration is in progress.
@@ -79,7 +79,7 @@ import {
79
79
  updateVaultMemoryOp,
80
80
  upsertMessageOp,
81
81
  useEncryption
82
- } from "./chunk-IKPPX3GY.mjs";
82
+ } from "./chunk-OZ357R4P.mjs";
83
83
  import {
84
84
  PII_PATTERNS,
85
85
  PiiRedactor,
@@ -10004,6 +10004,7 @@ var DatabaseManager = class {
10004
10004
  from: this.currentWalletAddress,
10005
10005
  to: walletAddress
10006
10006
  });
10007
+ void this.disposeAdapter(this.database);
10007
10008
  this.database = null;
10008
10009
  }
10009
10010
  if (this.database) {
@@ -10033,11 +10034,37 @@ var DatabaseManager = class {
10033
10034
  async resetDatabase() {
10034
10035
  const db = this.database;
10035
10036
  if (db) {
10037
+ this.database = null;
10038
+ this.currentWalletAddress = void 0;
10036
10039
  await db.write(async () => {
10037
10040
  await db.unsafeResetDatabase();
10038
10041
  });
10039
- this.database = null;
10040
- this.currentWalletAddress = void 0;
10042
+ await this.disposeAdapter(db);
10043
+ }
10044
+ }
10045
+ /**
10046
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
10047
+ *
10048
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
10049
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
10050
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
10051
+ * type it. Client-side implementations are refcounted + idempotent, so calling
10052
+ * this is safe even when the adapter factory also disposes.
10053
+ *
10054
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
10055
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
10056
+ * completes before the caller reopens; fire-and-forget on the synchronous
10057
+ * `getDatabase` path (which cannot block).
10058
+ */
10059
+ async disposeAdapter(db) {
10060
+ try {
10061
+ const underlying = db.adapter?.underlyingAdapter;
10062
+ await underlying?.dispose?.();
10063
+ } catch (error) {
10064
+ this.logger.warn?.("Adapter dispose failed", {
10065
+ component: "DatabaseManager",
10066
+ error
10067
+ });
10041
10068
  }
10042
10069
  }
10043
10070
  /**
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-IKPPX3GY.mjs";
3
+ } from "./chunk-OZ357R4P.mjs";
4
4
  import "./chunk-DFVCS3QQ.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -1291,50 +1291,85 @@ async function getMessagesPageOp(ctx, convId, opts) {
1291
1291
  )
1292
1292
  );
1293
1293
  }
1294
- async function getMessageSkeletonsOp(ctx, convId) {
1295
- const results = await ctx.messagesCollection.query(Q.where("conversation_id", convId), Q.sortBy("message_id", Q.asc)).fetch();
1296
- const roleById = /* @__PURE__ */ new Map();
1297
- for (const msg of results) {
1298
- roleById.set(msg.id, msg.role);
1299
- }
1300
- const skeletons = results.map((msg) => ({
1301
- uniqueId: msg.id,
1302
- messageId: msg.messageId,
1303
- conversationId: String(msg._getRaw("conversation_id")),
1304
- role: msg.role,
1305
- createdAt: msg.createdAt,
1306
- // WatermelonDB surfaces unset text columns as null at runtime — normalize
1307
- // to undefined so `parentMessageId ?? undefined` keying works everywhere.
1308
- parentMessageId: msg.parentMessageId ?? void 0,
1309
- model: msg.model ?? void 0
1310
- }));
1311
- const artifactIndices = [];
1312
- for (let i = 0; i < results.length; i++) {
1313
- const msg = results[i];
1314
- if (msg.role !== "user" || !msg.parentMessageId) continue;
1315
- if (roleById.get(msg.parentMessageId) === "user") {
1316
- artifactIndices.push(i);
1294
+ var SKELETON_PAGE_SIZE = 2e3;
1295
+ function narrowSkeletonRow(raw) {
1296
+ return {
1297
+ id: raw.id,
1298
+ message_id: raw.message_id,
1299
+ conversation_id: raw.conversation_id,
1300
+ role: raw.role,
1301
+ created_at: raw.created_at,
1302
+ parent_message_id: raw.parent_message_id ?? null,
1303
+ model: raw.model ?? null,
1304
+ content: raw.content ?? null
1305
+ };
1306
+ }
1307
+ async function fetchThreadSkeletonRawPaged(collection, convId, pageSize = SKELETON_PAGE_SIZE) {
1308
+ const all = [];
1309
+ let cursor;
1310
+ for (; ; ) {
1311
+ const clauses = [Q.where("conversation_id", convId)];
1312
+ if (cursor) {
1313
+ clauses.push(
1314
+ Q.or(
1315
+ Q.where("message_id", Q.gt(cursor.messageId)),
1316
+ Q.and(Q.where("message_id", cursor.messageId), Q.where("id", Q.gt(cursor.id)))
1317
+ )
1318
+ );
1317
1319
  }
1320
+ const page = await collection.query(...clauses, Q.sortBy("message_id", Q.asc), Q.sortBy("id", Q.asc), Q.take(pageSize)).unsafeFetchRaw();
1321
+ if (page.length === 0) break;
1322
+ for (const raw of page) all.push(narrowSkeletonRow(raw));
1323
+ if (page.length < pageSize) break;
1324
+ const last = all[all.length - 1];
1325
+ cursor = { messageId: last.message_id, id: last.id };
1326
+ }
1327
+ return all;
1328
+ }
1329
+ function collectArtifactIndices(rows, roleById) {
1330
+ const indices = [];
1331
+ for (let i = 0; i < rows.length; i++) {
1332
+ const row = rows[i];
1333
+ if (row.role !== "user" || !row.parent_message_id) continue;
1334
+ if (roleById.get(row.parent_message_id) === "user") indices.push(i);
1318
1335
  }
1336
+ return indices;
1337
+ }
1338
+ async function decryptArtifactContent(skeletons, rows, artifactIndices, ctx) {
1319
1339
  const address = ctx.walletAddress;
1320
- if (artifactIndices.length > 0 && address) {
1321
- if (ctx.signMessage) {
1322
- try {
1323
- await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
1324
- } catch (error) {
1325
- getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
1326
- }
1327
- }
1328
- await Promise.all(
1329
- artifactIndices.map(async (i) => {
1330
- skeletons[i].content = await decryptField(results[i].content, address);
1331
- })
1332
- );
1333
- } else {
1334
- for (const i of artifactIndices) {
1335
- skeletons[i].content = results[i].content;
1340
+ if (artifactIndices.length === 0 || !address) {
1341
+ for (const i of artifactIndices) skeletons[i].content = rows[i].content ?? void 0;
1342
+ return;
1343
+ }
1344
+ if (ctx.signMessage) {
1345
+ try {
1346
+ await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
1347
+ } catch (error) {
1348
+ getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
1336
1349
  }
1337
1350
  }
1351
+ await Promise.all(
1352
+ artifactIndices.map(async (i) => {
1353
+ skeletons[i].content = await decryptField(rows[i].content ?? "", address);
1354
+ })
1355
+ );
1356
+ }
1357
+ async function getMessageSkeletonsOp(ctx, convId) {
1358
+ const rows = await fetchThreadSkeletonRawPaged(ctx.messagesCollection, convId);
1359
+ const roleById = /* @__PURE__ */ new Map();
1360
+ for (const row of rows) roleById.set(row.id, row.role);
1361
+ const skeletons = rows.map((row) => ({
1362
+ uniqueId: row.id,
1363
+ messageId: row.message_id,
1364
+ conversationId: String(row.conversation_id),
1365
+ role: row.role,
1366
+ createdAt: new Date(row.created_at),
1367
+ // WatermelonDB surfaces unset text columns as null at runtime — normalize
1368
+ // to undefined so `parentMessageId ?? undefined` keying works everywhere.
1369
+ parentMessageId: row.parent_message_id ?? void 0,
1370
+ model: row.model ?? void 0
1371
+ }));
1372
+ await decryptArtifactContent(skeletons, rows, collectArtifactIndices(rows, roleById), ctx);
1338
1373
  return skeletons;
1339
1374
  }
1340
1375
  async function clearMessagesOp(ctx, convId) {
@@ -5011,7 +5046,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
5011
5046
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
5012
5047
  );
5013
5048
  }
5014
- const { recall: recall2 } = await import("./recall-Z2HUBS2Y.mjs");
5049
+ const { recall: recall2 } = await import("./recall-RGKS2OIP.mjs");
5015
5050
  const result = await recall2(
5016
5051
  query,
5017
5052
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -2468,50 +2468,84 @@ async function getMessagesPageOp(ctx, convId, opts) {
2468
2468
  )
2469
2469
  );
2470
2470
  }
2471
- async function getMessageSkeletonsOp(ctx, convId) {
2472
- const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId), import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc)).fetch();
2473
- const roleById = /* @__PURE__ */ new Map();
2474
- for (const msg of results) {
2475
- roleById.set(msg.id, msg.role);
2476
- }
2477
- const skeletons = results.map((msg) => ({
2478
- uniqueId: msg.id,
2479
- messageId: msg.messageId,
2480
- conversationId: String(msg._getRaw("conversation_id")),
2481
- role: msg.role,
2482
- createdAt: msg.createdAt,
2483
- // WatermelonDB surfaces unset text columns as null at runtime — normalize
2484
- // to undefined so `parentMessageId ?? undefined` keying works everywhere.
2485
- parentMessageId: msg.parentMessageId ?? void 0,
2486
- model: msg.model ?? void 0
2487
- }));
2488
- const artifactIndices = [];
2489
- for (let i = 0; i < results.length; i++) {
2490
- const msg = results[i];
2491
- if (msg.role !== "user" || !msg.parentMessageId) continue;
2492
- if (roleById.get(msg.parentMessageId) === "user") {
2493
- artifactIndices.push(i);
2471
+ function narrowSkeletonRow(raw) {
2472
+ return {
2473
+ id: raw.id,
2474
+ message_id: raw.message_id,
2475
+ conversation_id: raw.conversation_id,
2476
+ role: raw.role,
2477
+ created_at: raw.created_at,
2478
+ parent_message_id: raw.parent_message_id ?? null,
2479
+ model: raw.model ?? null,
2480
+ content: raw.content ?? null
2481
+ };
2482
+ }
2483
+ async function fetchThreadSkeletonRawPaged(collection, convId, pageSize = SKELETON_PAGE_SIZE) {
2484
+ const all = [];
2485
+ let cursor;
2486
+ for (; ; ) {
2487
+ const clauses = [import_watermelondb.Q.where("conversation_id", convId)];
2488
+ if (cursor) {
2489
+ clauses.push(
2490
+ import_watermelondb.Q.or(
2491
+ import_watermelondb.Q.where("message_id", import_watermelondb.Q.gt(cursor.messageId)),
2492
+ import_watermelondb.Q.and(import_watermelondb.Q.where("message_id", cursor.messageId), import_watermelondb.Q.where("id", import_watermelondb.Q.gt(cursor.id)))
2493
+ )
2494
+ );
2494
2495
  }
2496
+ const page = await collection.query(...clauses, import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc), import_watermelondb.Q.sortBy("id", import_watermelondb.Q.asc), import_watermelondb.Q.take(pageSize)).unsafeFetchRaw();
2497
+ if (page.length === 0) break;
2498
+ for (const raw of page) all.push(narrowSkeletonRow(raw));
2499
+ if (page.length < pageSize) break;
2500
+ const last = all[all.length - 1];
2501
+ cursor = { messageId: last.message_id, id: last.id };
2502
+ }
2503
+ return all;
2504
+ }
2505
+ function collectArtifactIndices(rows, roleById) {
2506
+ const indices = [];
2507
+ for (let i = 0; i < rows.length; i++) {
2508
+ const row = rows[i];
2509
+ if (row.role !== "user" || !row.parent_message_id) continue;
2510
+ if (roleById.get(row.parent_message_id) === "user") indices.push(i);
2495
2511
  }
2512
+ return indices;
2513
+ }
2514
+ async function decryptArtifactContent(skeletons, rows, artifactIndices, ctx) {
2496
2515
  const address = ctx.walletAddress;
2497
- if (artifactIndices.length > 0 && address) {
2498
- if (ctx.signMessage) {
2499
- try {
2500
- await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
2501
- } catch (error) {
2502
- getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
2503
- }
2504
- }
2505
- await Promise.all(
2506
- artifactIndices.map(async (i) => {
2507
- skeletons[i].content = await decryptField(results[i].content, address);
2508
- })
2509
- );
2510
- } else {
2511
- for (const i of artifactIndices) {
2512
- skeletons[i].content = results[i].content;
2516
+ if (artifactIndices.length === 0 || !address) {
2517
+ for (const i of artifactIndices) skeletons[i].content = rows[i].content ?? void 0;
2518
+ return;
2519
+ }
2520
+ if (ctx.signMessage) {
2521
+ try {
2522
+ await requestEncryptionKey(address, ctx.signMessage, ctx.embeddedWalletSigner);
2523
+ } catch (error) {
2524
+ getLogger().warn("Failed to request encryption key for skeleton decryption:", error);
2513
2525
  }
2514
2526
  }
2527
+ await Promise.all(
2528
+ artifactIndices.map(async (i) => {
2529
+ skeletons[i].content = await decryptField(rows[i].content ?? "", address);
2530
+ })
2531
+ );
2532
+ }
2533
+ async function getMessageSkeletonsOp(ctx, convId) {
2534
+ const rows = await fetchThreadSkeletonRawPaged(ctx.messagesCollection, convId);
2535
+ const roleById = /* @__PURE__ */ new Map();
2536
+ for (const row of rows) roleById.set(row.id, row.role);
2537
+ const skeletons = rows.map((row) => ({
2538
+ uniqueId: row.id,
2539
+ messageId: row.message_id,
2540
+ conversationId: String(row.conversation_id),
2541
+ role: row.role,
2542
+ createdAt: new Date(row.created_at),
2543
+ // WatermelonDB surfaces unset text columns as null at runtime — normalize
2544
+ // to undefined so `parentMessageId ?? undefined` keying works everywhere.
2545
+ parentMessageId: row.parent_message_id ?? void 0,
2546
+ model: row.model ?? void 0
2547
+ }));
2548
+ await decryptArtifactContent(skeletons, rows, collectArtifactIndices(rows, roleById), ctx);
2515
2549
  return skeletons;
2516
2550
  }
2517
2551
  async function clearMessagesOp(ctx, convId) {
@@ -2902,7 +2936,7 @@ function makeSyntheticStoredConversation(opts, defaultTitle) {
2902
2936
  isDeleted: false
2903
2937
  };
2904
2938
  }
2905
- var import_watermelondb, import_uuid2;
2939
+ var import_watermelondb, import_uuid2, SKELETON_PAGE_SIZE;
2906
2940
  var init_operations = __esm({
2907
2941
  "src/lib/db/chat/operations.ts"() {
2908
2942
  "use strict";
@@ -2915,6 +2949,7 @@ var init_operations = __esm({
2915
2949
  init_conversationEncryption();
2916
2950
  init_encryption();
2917
2951
  init_types2();
2952
+ SKELETON_PAGE_SIZE = 2e3;
2918
2953
  }
2919
2954
  });
2920
2955
 
@@ -19941,6 +19976,7 @@ var DatabaseManager = class {
19941
19976
  from: this.currentWalletAddress,
19942
19977
  to: walletAddress
19943
19978
  });
19979
+ void this.disposeAdapter(this.database);
19944
19980
  this.database = null;
19945
19981
  }
19946
19982
  if (this.database) {
@@ -19970,11 +20006,37 @@ var DatabaseManager = class {
19970
20006
  async resetDatabase() {
19971
20007
  const db = this.database;
19972
20008
  if (db) {
20009
+ this.database = null;
20010
+ this.currentWalletAddress = void 0;
19973
20011
  await db.write(async () => {
19974
20012
  await db.unsafeResetDatabase();
19975
20013
  });
19976
- this.database = null;
19977
- this.currentWalletAddress = void 0;
20014
+ await this.disposeAdapter(db);
20015
+ }
20016
+ }
20017
+ /**
20018
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
20019
+ *
20020
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
20021
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
20022
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
20023
+ * type it. Client-side implementations are refcounted + idempotent, so calling
20024
+ * this is safe even when the adapter factory also disposes.
20025
+ *
20026
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
20027
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
20028
+ * completes before the caller reopens; fire-and-forget on the synchronous
20029
+ * `getDatabase` path (which cannot block).
20030
+ */
20031
+ async disposeAdapter(db) {
20032
+ try {
20033
+ const underlying = db.adapter?.underlyingAdapter;
20034
+ await underlying?.dispose?.();
20035
+ } catch (error) {
20036
+ this.logger.warn?.("Adapter dispose failed", {
20037
+ component: "DatabaseManager",
20038
+ error
20039
+ });
19978
20040
  }
19979
20041
  }
19980
20042
  /**
@@ -8103,6 +8103,21 @@ declare class DatabaseManager {
8103
8103
  * Reset the current database (useful for logout or testing).
8104
8104
  */
8105
8105
  resetDatabase(): Promise<void>;
8106
+ /**
8107
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
8108
+ *
8109
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
8110
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
8111
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
8112
+ * type it. Client-side implementations are refcounted + idempotent, so calling
8113
+ * this is safe even when the adapter factory also disposes.
8114
+ *
8115
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
8116
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
8117
+ * completes before the caller reopens; fire-and-forget on the synchronous
8118
+ * `getDatabase` path (which cannot block).
8119
+ */
8120
+ private disposeAdapter;
8106
8121
  /**
8107
8122
  * Check and handle schema migrations for a specific wallet.
8108
8123
  * Returns true if a destructive migration is in progress.
@@ -8103,6 +8103,21 @@ declare class DatabaseManager {
8103
8103
  * Reset the current database (useful for logout or testing).
8104
8104
  */
8105
8105
  resetDatabase(): Promise<void>;
8106
+ /**
8107
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
8108
+ *
8109
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
8110
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
8111
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
8112
+ * type it. Client-side implementations are refcounted + idempotent, so calling
8113
+ * this is safe even when the adapter factory also disposes.
8114
+ *
8115
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
8116
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
8117
+ * completes before the caller reopens; fire-and-forget on the synchronous
8118
+ * `getDatabase` path (which cannot block).
8119
+ */
8120
+ private disposeAdapter;
8106
8121
  /**
8107
8122
  * Check and handle schema migrations for a specific wallet.
8108
8123
  * Returns true if a destructive migration is in progress.
@@ -121,7 +121,7 @@ import {
121
121
  updateVaultMemoryEmbeddingOp,
122
122
  updateVaultMemoryOp,
123
123
  useEncryption
124
- } from "./chunk-KQZ4KVYY.mjs";
124
+ } from "./chunk-DC6J5RX5.mjs";
125
125
  import {
126
126
  PiiRedactor,
127
127
  createStreamingDeAnonymizer,
@@ -12664,6 +12664,7 @@ var DatabaseManager = class {
12664
12664
  from: this.currentWalletAddress,
12665
12665
  to: walletAddress
12666
12666
  });
12667
+ void this.disposeAdapter(this.database);
12667
12668
  this.database = null;
12668
12669
  }
12669
12670
  if (this.database) {
@@ -12693,11 +12694,37 @@ var DatabaseManager = class {
12693
12694
  async resetDatabase() {
12694
12695
  const db = this.database;
12695
12696
  if (db) {
12697
+ this.database = null;
12698
+ this.currentWalletAddress = void 0;
12696
12699
  await db.write(async () => {
12697
12700
  await db.unsafeResetDatabase();
12698
12701
  });
12699
- this.database = null;
12700
- this.currentWalletAddress = void 0;
12702
+ await this.disposeAdapter(db);
12703
+ }
12704
+ }
12705
+ /**
12706
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
12707
+ *
12708
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
12709
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
12710
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
12711
+ * type it. Client-side implementations are refcounted + idempotent, so calling
12712
+ * this is safe even when the adapter factory also disposes.
12713
+ *
12714
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
12715
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
12716
+ * completes before the caller reopens; fire-and-forget on the synchronous
12717
+ * `getDatabase` path (which cannot block).
12718
+ */
12719
+ async disposeAdapter(db) {
12720
+ try {
12721
+ const underlying = db.adapter?.underlyingAdapter;
12722
+ await underlying?.dispose?.();
12723
+ } catch (error) {
12724
+ this.logger.warn?.("Adapter dispose failed", {
12725
+ component: "DatabaseManager",
12726
+ error
12727
+ });
12701
12728
  }
12702
12729
  }
12703
12730
  /**
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-KQZ4KVYY.mjs";
3
+ } from "./chunk-DC6J5RX5.mjs";
4
4
  import "./chunk-DFVCS3QQ.mjs";
5
5
  import "./chunk-GYKJOWEO.mjs";
6
6
  export {
@@ -18838,7 +18838,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
18838
18838
  legacy.map((r) => ({ id: r.uniqueId, content: r.content, score: r.similarity }))
18839
18839
  );
18840
18840
  }
18841
- const { recall: recall2 } = await import("./recall-IF2HX7ZA.mjs");
18841
+ const { recall: recall2 } = await import("./recall-V4763HKS.mjs");
18842
18842
  const result = await recall2(
18843
18843
  query,
18844
18844
  { vaultCtx, embeddingOptions, vaultCache: cache },
@@ -22877,6 +22877,7 @@ var DatabaseManager = class {
22877
22877
  from: this.currentWalletAddress,
22878
22878
  to: walletAddress
22879
22879
  });
22880
+ void this.disposeAdapter(this.database);
22880
22881
  this.database = null;
22881
22882
  }
22882
22883
  if (this.database) {
@@ -22906,11 +22907,37 @@ var DatabaseManager = class {
22906
22907
  async resetDatabase() {
22907
22908
  const db = this.database;
22908
22909
  if (db) {
22910
+ this.database = null;
22911
+ this.currentWalletAddress = void 0;
22909
22912
  await db.write(async () => {
22910
22913
  await db.unsafeResetDatabase();
22911
22914
  });
22912
- this.database = null;
22913
- this.currentWalletAddress = void 0;
22915
+ await this.disposeAdapter(db);
22916
+ }
22917
+ }
22918
+ /**
22919
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
22920
+ *
22921
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
22922
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
22923
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
22924
+ * type it. Client-side implementations are refcounted + idempotent, so calling
22925
+ * this is safe even when the adapter factory also disposes.
22926
+ *
22927
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
22928
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
22929
+ * completes before the caller reopens; fire-and-forget on the synchronous
22930
+ * `getDatabase` path (which cannot block).
22931
+ */
22932
+ async disposeAdapter(db) {
22933
+ try {
22934
+ const underlying = db.adapter?.underlyingAdapter;
22935
+ await underlying?.dispose?.();
22936
+ } catch (error) {
22937
+ this.logger.warn?.("Adapter dispose failed", {
22938
+ component: "DatabaseManager",
22939
+ error
22940
+ });
22914
22941
  }
22915
22942
  }
22916
22943
  /**
@@ -274,6 +274,21 @@ declare class DatabaseManager {
274
274
  * Reset the current database (useful for logout or testing).
275
275
  */
276
276
  resetDatabase(): Promise<void>;
277
+ /**
278
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
279
+ *
280
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
281
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
282
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
283
+ * type it. Client-side implementations are refcounted + idempotent, so calling
284
+ * this is safe even when the adapter factory also disposes.
285
+ *
286
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
287
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
288
+ * completes before the caller reopens; fire-and-forget on the synchronous
289
+ * `getDatabase` path (which cannot block).
290
+ */
291
+ private disposeAdapter;
277
292
  /**
278
293
  * Check and handle schema migrations for a specific wallet.
279
294
  * Returns true if a destructive migration is in progress.
@@ -274,6 +274,21 @@ declare class DatabaseManager {
274
274
  * Reset the current database (useful for logout or testing).
275
275
  */
276
276
  resetDatabase(): Promise<void>;
277
+ /**
278
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
279
+ *
280
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
281
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
282
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
283
+ * type it. Client-side implementations are refcounted + idempotent, so calling
284
+ * this is safe even when the adapter factory also disposes.
285
+ *
286
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
287
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
288
+ * completes before the caller reopens; fire-and-forget on the synchronous
289
+ * `getDatabase` path (which cannot block).
290
+ */
291
+ private disposeAdapter;
277
292
  /**
278
293
  * Check and handle schema migrations for a specific wallet.
279
294
  * Returns true if a destructive migration is in progress.
@@ -95,7 +95,7 @@ import {
95
95
  updateVaultMemoryEmbeddingOp,
96
96
  updateVaultMemoryOp,
97
97
  upsertMessageOp
98
- } from "./chunk-KLJBJWWI.mjs";
98
+ } from "./chunk-MKRSHU4S.mjs";
99
99
  import {
100
100
  createStreamingDeAnonymizer,
101
101
  resolvePiiRedactor
@@ -1984,6 +1984,7 @@ var DatabaseManager = class {
1984
1984
  from: this.currentWalletAddress,
1985
1985
  to: walletAddress
1986
1986
  });
1987
+ void this.disposeAdapter(this.database);
1987
1988
  this.database = null;
1988
1989
  }
1989
1990
  if (this.database) {
@@ -2013,11 +2014,37 @@ var DatabaseManager = class {
2013
2014
  async resetDatabase() {
2014
2015
  const db = this.database;
2015
2016
  if (db) {
2017
+ this.database = null;
2018
+ this.currentWalletAddress = void 0;
2016
2019
  await db.write(async () => {
2017
2020
  await db.unsafeResetDatabase();
2018
2021
  });
2019
- this.database = null;
2020
- this.currentWalletAddress = void 0;
2022
+ await this.disposeAdapter(db);
2023
+ }
2024
+ }
2025
+ /**
2026
+ * Best-effort dispose of the adapter backing a Database, if it exposes one.
2027
+ *
2028
+ * OPFS/SQLite adapters use this to terminate their dedicated worker and release
2029
+ * the exclusive SAHPool (+ WASM heap) they hold. The SDK only sees a generic
2030
+ * `DatabaseAdapter`, so we probe `adapter.underlyingAdapter.dispose` rather than
2031
+ * type it. Client-side implementations are refcounted + idempotent, so calling
2032
+ * this is safe even when the adapter factory also disposes.
2033
+ *
2034
+ * Awaitable and never rejects: a sync throw or async rejection from `dispose`
2035
+ * is caught + logged. Awaited on the async `resetDatabase` path so teardown
2036
+ * completes before the caller reopens; fire-and-forget on the synchronous
2037
+ * `getDatabase` path (which cannot block).
2038
+ */
2039
+ async disposeAdapter(db) {
2040
+ try {
2041
+ const underlying = db.adapter?.underlyingAdapter;
2042
+ await underlying?.dispose?.();
2043
+ } catch (error) {
2044
+ this.logger.warn?.("Adapter dispose failed", {
2045
+ component: "DatabaseManager",
2046
+ error
2047
+ });
2021
2048
  }
2022
2049
  }
2023
2050
  /**
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recall
3
- } from "./chunk-KLJBJWWI.mjs";
3
+ } from "./chunk-MKRSHU4S.mjs";
4
4
  import "./chunk-RCCKRTBI.mjs";
5
5
  import "./chunk-FMGZTVXQ.mjs";
6
6
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anuma/sdk",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",