mcard-js 2.1.39 → 2.1.41

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 (40) hide show
  1. package/dist/AbstractSqlEngine-BSfp8S_Y.d.cts +451 -0
  2. package/dist/AbstractSqlEngine-BSfp8S_Y.d.ts +451 -0
  3. package/dist/CardCollection-MXTUJV4J.js +9 -0
  4. package/dist/EventProducer-AWD6YMZR.js +47 -0
  5. package/dist/Handle-3N4QOA3U.js +13 -0
  6. package/dist/IndexedDBEngine-2G5KCISA.js +11 -0
  7. package/dist/LLMRuntime-LBWUJ7ON.js +16 -0
  8. package/dist/LambdaRuntime-B6D6IQKZ.js +18 -0
  9. package/dist/Loader-3LSJXJQG.js +11 -0
  10. package/dist/MCard-H56VOJLR.js +8 -0
  11. package/dist/NetworkRuntime-IAFHPQSX.js +1570 -0
  12. package/dist/OllamaProvider-QPX2JXL2.js +8 -0
  13. package/dist/chunk-2R4ESMZB.js +110 -0
  14. package/dist/chunk-3EIBJPNF.js +17 -0
  15. package/dist/chunk-3LPY36OG.js +355 -0
  16. package/dist/chunk-3MMMJ7NH.js +1068 -0
  17. package/dist/chunk-42VF42KH.js +273 -0
  18. package/dist/chunk-4PDYHPR6.js +297 -0
  19. package/dist/chunk-ADV52544.js +95 -0
  20. package/dist/chunk-FIE4LAJG.js +215 -0
  21. package/dist/chunk-PNKVD2UK.js +26 -0
  22. package/dist/chunk-RSTKX7WM.js +907 -0
  23. package/dist/chunk-VXV35I5J.js +2315 -0
  24. package/dist/index.browser.cjs +375 -276
  25. package/dist/index.browser.d.cts +4 -4
  26. package/dist/index.browser.d.ts +4 -4
  27. package/dist/index.browser.js +18 -13
  28. package/dist/index.cjs +382 -453
  29. package/dist/index.d.cts +2 -2
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +26 -21
  32. package/dist/storage/SqliteNodeEngine.cjs +395 -270
  33. package/dist/storage/SqliteNodeEngine.d.cts +9 -94
  34. package/dist/storage/SqliteNodeEngine.d.ts +9 -94
  35. package/dist/storage/SqliteNodeEngine.js +6 -5
  36. package/dist/storage/SqliteWasmEngine.cjs +382 -252
  37. package/dist/storage/SqliteWasmEngine.d.cts +8 -29
  38. package/dist/storage/SqliteWasmEngine.d.ts +8 -29
  39. package/dist/storage/SqliteWasmEngine.js +6 -5
  40. package/package.json +1 -1
@@ -176,6 +176,90 @@ var init_GTime = __esm({
176
176
  }
177
177
  });
178
178
 
179
+ // src/model/Handle.ts
180
+ var Handle_exports = {};
181
+ __export(Handle_exports, {
182
+ ContentHandle: () => ContentHandle,
183
+ HandleValidationError: () => HandleValidationError,
184
+ validateHandle: () => validateHandle
185
+ });
186
+ function isValidStartChar(char) {
187
+ return /^\p{L}$/u.test(char);
188
+ }
189
+ function isValidBodyChar(char) {
190
+ return /^[\p{L}\p{N}_./ -]$/u.test(char);
191
+ }
192
+ function validateHandle(handle) {
193
+ if (!handle) {
194
+ throw new HandleValidationError("Handle cannot be empty.");
195
+ }
196
+ const normalized = handle.trim().normalize("NFC");
197
+ if (normalized.length === 0) {
198
+ throw new HandleValidationError("Handle cannot be empty after normalization.");
199
+ }
200
+ if (normalized.length > MAX_HANDLE_LENGTH) {
201
+ throw new HandleValidationError(
202
+ `Handle '${handle}' is too long (${normalized.length} chars). Maximum is ${MAX_HANDLE_LENGTH}.`
203
+ );
204
+ }
205
+ if (!isValidStartChar(normalized[0])) {
206
+ throw new HandleValidationError(
207
+ `Invalid handle '${handle}'. Must start with a letter (any language).`
208
+ );
209
+ }
210
+ for (let i = 1; i < normalized.length; i++) {
211
+ if (!isValidBodyChar(normalized[i])) {
212
+ throw new HandleValidationError(
213
+ `Invalid character '${normalized[i]}' at position ${i} in handle '${handle}'.`
214
+ );
215
+ }
216
+ }
217
+ return normalized;
218
+ }
219
+ var MAX_HANDLE_LENGTH, HandleValidationError, ContentHandle;
220
+ var init_Handle = __esm({
221
+ "src/model/Handle.ts"() {
222
+ "use strict";
223
+ MAX_HANDLE_LENGTH = 255;
224
+ HandleValidationError = class extends Error {
225
+ constructor(message) {
226
+ super(message);
227
+ this.name = "HandleValidationError";
228
+ }
229
+ };
230
+ ContentHandle = class {
231
+ handle;
232
+ currentHash;
233
+ createdAt;
234
+ updatedAt;
235
+ constructor(handle, currentHash, createdAt, updatedAt) {
236
+ this.handle = validateHandle(handle);
237
+ this.currentHash = currentHash;
238
+ this.createdAt = createdAt ?? /* @__PURE__ */ new Date();
239
+ this.updatedAt = updatedAt ?? this.createdAt;
240
+ }
241
+ /**
242
+ * Update handle to point to new hash
243
+ * @returns Previous hash for history tracking
244
+ */
245
+ update(newHash) {
246
+ const previousHash = this.currentHash;
247
+ this.currentHash = newHash;
248
+ this.updatedAt = /* @__PURE__ */ new Date();
249
+ return previousHash;
250
+ }
251
+ toObject() {
252
+ return {
253
+ handle: this.handle,
254
+ currentHash: this.currentHash,
255
+ createdAt: this.createdAt.toISOString(),
256
+ updatedAt: this.updatedAt.toISOString()
257
+ };
258
+ }
259
+ };
260
+ }
261
+ });
262
+
179
263
  // src/model/constants.ts
180
264
  var EVENT_CONSTANTS, ALGORITHM_HIERARCHY;
181
265
  var init_constants = __esm({
@@ -1025,6 +1109,18 @@ var mime_extensions_default = {
1025
1109
  }
1026
1110
  };
1027
1111
 
1112
+ // src/config/constants.ts
1113
+ var DEFAULT_PAGE_SIZE = 10;
1114
+ var DETECTION_SAMPLE_CAP = 8192;
1115
+ var MAX_FILE_SIZE = 50 * 1024 * 1024;
1116
+ var KNOWN_TYPE_SIZE_LIMIT = 1024 * 1024;
1117
+ var BINARY_CHECK_SAMPLE_SIZE = 32 * 1024;
1118
+ var CONTENT_DETECTION_SAMPLE_SIZE = 1024 * 1024;
1119
+ var DEFAULT_MAX_PROBLEM_BYTES = 2 * 1024 * 1024;
1120
+ var INDEXEDDB_DEFAULT_DB_NAME = "mcard-db";
1121
+ var INDEXEDDB_DEFAULT_DB_VERSION = 1;
1122
+ var DEFAULT_SQLJS_WASM_URL = "https://sql.js.org/dist/";
1123
+
1028
1124
  // src/model/strategies/DetectionStrategy.ts
1029
1125
  function loadSharedMimeData() {
1030
1126
  return {
@@ -1057,7 +1153,7 @@ var DefaultDetectionStrategy = class _DefaultDetectionStrategy {
1057
1153
  return this.extensionsRegistry[mimeType] || "";
1058
1154
  }
1059
1155
  detect(content, fileExtension) {
1060
- const contentSample = typeof content === "string" ? content.slice(0, 8192) : content.slice(0, 8192);
1156
+ const contentSample = typeof content === "string" ? content.slice(0, DETECTION_SAMPLE_CAP) : content.slice(0, DETECTION_SAMPLE_CAP);
1061
1157
  const textSample = this.getTextSample(contentSample);
1062
1158
  const lines = textSample.split("\n").slice(0, 20);
1063
1159
  const firstLine = lines[0] || "";
@@ -1091,9 +1187,9 @@ var DefaultDetectionStrategy = class _DefaultDetectionStrategy {
1091
1187
  }
1092
1188
  getTextSample(content) {
1093
1189
  if (typeof content === "string") {
1094
- return content.slice(0, 8192);
1190
+ return content.slice(0, DETECTION_SAMPLE_CAP);
1095
1191
  }
1096
- return new TextDecoder("utf-8", { fatal: false }).decode(content.slice(0, 8192));
1192
+ return new TextDecoder("utf-8", { fatal: false }).decode(content.slice(0, DETECTION_SAMPLE_CAP));
1097
1193
  }
1098
1194
  getExtension(mimeType) {
1099
1195
  return _DefaultDetectionStrategy.getExtension(mimeType);
@@ -1313,78 +1409,7 @@ var MCard = class _MCard {
1313
1409
 
1314
1410
  // src/index.browser.ts
1315
1411
  init_GTime();
1316
-
1317
- // src/model/Handle.ts
1318
- var MAX_HANDLE_LENGTH = 255;
1319
- function isValidStartChar(char) {
1320
- return /^\p{L}$/u.test(char);
1321
- }
1322
- function isValidBodyChar(char) {
1323
- return /^[\p{L}\p{N}_./ -]$/u.test(char);
1324
- }
1325
- var HandleValidationError = class extends Error {
1326
- constructor(message) {
1327
- super(message);
1328
- this.name = "HandleValidationError";
1329
- }
1330
- };
1331
- function validateHandle(handle) {
1332
- if (!handle) {
1333
- throw new HandleValidationError("Handle cannot be empty.");
1334
- }
1335
- const normalized = handle.trim().normalize("NFC").toLowerCase();
1336
- if (normalized.length === 0) {
1337
- throw new HandleValidationError("Handle cannot be empty after normalization.");
1338
- }
1339
- if (normalized.length > MAX_HANDLE_LENGTH) {
1340
- throw new HandleValidationError(
1341
- `Handle '${handle}' is too long (${normalized.length} chars). Maximum is ${MAX_HANDLE_LENGTH}.`
1342
- );
1343
- }
1344
- if (!isValidStartChar(normalized[0])) {
1345
- throw new HandleValidationError(
1346
- `Invalid handle '${handle}'. Must start with a letter (any language).`
1347
- );
1348
- }
1349
- for (let i = 1; i < normalized.length; i++) {
1350
- if (!isValidBodyChar(normalized[i])) {
1351
- throw new HandleValidationError(
1352
- `Invalid character '${normalized[i]}' at position ${i} in handle '${handle}'.`
1353
- );
1354
- }
1355
- }
1356
- return normalized;
1357
- }
1358
- var ContentHandle = class {
1359
- handle;
1360
- currentHash;
1361
- createdAt;
1362
- updatedAt;
1363
- constructor(handle, currentHash, createdAt, updatedAt) {
1364
- this.handle = validateHandle(handle);
1365
- this.currentHash = currentHash;
1366
- this.createdAt = createdAt ?? /* @__PURE__ */ new Date();
1367
- this.updatedAt = updatedAt ?? this.createdAt;
1368
- }
1369
- /**
1370
- * Update handle to point to new hash
1371
- * @returns Previous hash for history tracking
1372
- */
1373
- update(newHash) {
1374
- const previousHash = this.currentHash;
1375
- this.currentHash = newHash;
1376
- this.updatedAt = /* @__PURE__ */ new Date();
1377
- return previousHash;
1378
- }
1379
- toObject() {
1380
- return {
1381
- handle: this.handle,
1382
- currentHash: this.currentHash,
1383
- createdAt: this.createdAt.toISOString(),
1384
- updatedAt: this.updatedAt.toISOString()
1385
- };
1386
- }
1387
- };
1412
+ init_Handle();
1388
1413
 
1389
1414
  // src/monads/Maybe.ts
1390
1415
  var Maybe = class _Maybe {
@@ -1511,7 +1536,7 @@ var CardCollection = class {
1511
1536
  /**
1512
1537
  * Get a page of cards
1513
1538
  */
1514
- async getPage(pageNumber = 1, pageSize = 10) {
1539
+ async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
1515
1540
  return this.engine.getPage(pageNumber, pageSize);
1516
1541
  }
1517
1542
  /**
@@ -1601,10 +1626,10 @@ var CardCollection = class {
1601
1626
  async clear() {
1602
1627
  return this.engine.clear();
1603
1628
  }
1604
- async searchByString(query, pageNumber = 1, pageSize = 10) {
1629
+ async searchByString(query, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
1605
1630
  return this.engine.search(query, pageNumber, pageSize);
1606
1631
  }
1607
- async searchByContent(query, pageNumber = 1, pageSize = 10) {
1632
+ async searchByContent(query, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
1608
1633
  return this.engine.search(query, pageNumber, pageSize);
1609
1634
  }
1610
1635
  async searchByHash(hashPrefix) {
@@ -1613,7 +1638,7 @@ var CardCollection = class {
1613
1638
  async getAllMCardsRaw() {
1614
1639
  return this.engine.getAll();
1615
1640
  }
1616
- async getAllCards(pageSize = 10, processCallback) {
1641
+ async getAllCards(pageSize = DEFAULT_PAGE_SIZE, processCallback) {
1617
1642
  const cards = [];
1618
1643
  let pageNumber = 1;
1619
1644
  let total = 0;
@@ -1679,6 +1704,7 @@ init_constants();
1679
1704
 
1680
1705
  // src/storage/engines/IndexedDBEngine.ts
1681
1706
  var import_idb = require("idb");
1707
+ init_Handle();
1682
1708
 
1683
1709
  // src/storage/StorageAdapter.ts
1684
1710
  function createPage(items, totalItems, pageNumber, pageSize) {
@@ -1698,14 +1724,14 @@ function createPage(items, totalItems, pageNumber, pageSize) {
1698
1724
  var IndexedDBEngine = class {
1699
1725
  db = null;
1700
1726
  dbName;
1701
- constructor(dbName = "mcard-db") {
1727
+ constructor(dbName = INDEXEDDB_DEFAULT_DB_NAME) {
1702
1728
  this.dbName = dbName;
1703
1729
  }
1704
1730
  /**
1705
1731
  * Initialize the database connection
1706
1732
  */
1707
1733
  async init() {
1708
- this.db = await (0, import_idb.openDB)(this.dbName, 1, {
1734
+ this.db = await (0, import_idb.openDB)(this.dbName, INDEXEDDB_DEFAULT_DB_VERSION, {
1709
1735
  upgrade(db) {
1710
1736
  if (!db.objectStoreNames.contains("cards")) {
1711
1737
  db.createObjectStore("cards", { keyPath: "hash" });
@@ -1750,7 +1776,7 @@ var IndexedDBEngine = class {
1750
1776
  const db = this.ensureDb();
1751
1777
  await db.delete("cards", hash);
1752
1778
  }
1753
- async getPage(pageNumber, pageSize) {
1779
+ async getPage(pageNumber, pageSize = DEFAULT_PAGE_SIZE) {
1754
1780
  const db = this.ensureDb();
1755
1781
  const totalItems = await db.count("cards");
1756
1782
  const allCards = await db.getAll("cards");
@@ -1771,7 +1797,7 @@ var IndexedDBEngine = class {
1771
1797
  const records = await db.getAll("cards", range);
1772
1798
  return records.map((r) => MCard.fromData(r.content, r.hash, r.g_time));
1773
1799
  }
1774
- async search(query, pageNumber, pageSize) {
1800
+ async search(query, pageNumber, pageSize = DEFAULT_PAGE_SIZE) {
1775
1801
  const db = this.ensureDb();
1776
1802
  const records = await db.getAll("cards");
1777
1803
  const decoder = new TextDecoder();
@@ -2581,159 +2607,129 @@ var CORE_SCHEMAS = {
2581
2607
  handleIndex: HANDLE_INDEX_SCHEMA
2582
2608
  };
2583
2609
 
2584
- // src/storage/engines/SqliteWasmEngine.ts
2585
- var SqliteWasmEngine = class {
2586
- db = null;
2587
- SQL = null;
2610
+ // src/storage/engines/AbstractSqlEngine.ts
2611
+ init_Handle();
2612
+ var AbstractSqlEngine = class {
2588
2613
  /**
2589
- * Initialize the database
2590
- * @param wasmUrl URL to sql-wasm.wasm file (optional, defaults to CDN)
2591
- * @param existingData Optional existing database as Uint8Array
2614
+ * SQL expression for casting content to text in search queries.
2615
+ * Override in subclasses: SQLite uses 'TEXT', DuckDB uses 'VARCHAR'.
2592
2616
  */
2593
- async init(wasmUrl, existingData) {
2594
- const initSqlJs = (await import("sql.js")).default;
2595
- const SQL = await initSqlJs({
2596
- locateFile: (file) => wasmUrl || `https://sql.js.org/dist/${file}`
2597
- });
2598
- this.SQL = SQL;
2599
- this.db = existingData ? new SQL.Database(existingData) : new SQL.Database();
2600
- this.db.run(CORE_SCHEMAS.card);
2601
- this.db.run(CORE_SCHEMAS.handleRegistry);
2602
- this.db.run(CORE_SCHEMAS.handleHistory);
2603
- this.db.run(CORE_SCHEMAS.handleIndex);
2604
- }
2605
- ensureDb() {
2606
- if (!this.db) throw new Error("Database not initialized. Call init() first.");
2607
- return this.db;
2608
- }
2609
- /**
2610
- * Export database as Uint8Array (for persistence)
2611
- */
2612
- export() {
2613
- return this.ensureDb().export();
2617
+ get castContentAs() {
2618
+ return "TEXT";
2614
2619
  }
2620
+ // ======================================================================
2621
+ // Concrete: Card Operations (shared across all SQL engines)
2622
+ // ======================================================================
2615
2623
  /**
2616
- * Get raw sql.js Database for use with VectorStore adapter.
2617
- *
2618
- * Example:
2619
- * const vecStore = createWasmVectorStore(engine.getRawDb());
2624
+ * Add a card. Default uses INSERT OR REPLACE (SQLite).
2625
+ * DuckDB overrides with DELETE + INSERT.
2620
2626
  */
2621
- getRawDb() {
2622
- return this.ensureDb();
2623
- }
2624
- // =========== Card Operations ===========
2625
2627
  async add(card) {
2626
- const db = this.ensureDb();
2627
- db.run(
2628
+ const contentBytes = card.content instanceof Uint8Array ? card.content : new TextEncoder().encode(String(card.content));
2629
+ await this.execSql(
2628
2630
  "INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)",
2629
- [card.hash, card.content, card.g_time]
2631
+ card.hash,
2632
+ contentBytes,
2633
+ card.g_time
2630
2634
  );
2631
2635
  return card.hash;
2632
2636
  }
2633
2637
  async get(hash) {
2634
- const db = this.ensureDb();
2635
- const stmt = db.prepare("SELECT hash, content, g_time FROM card WHERE hash = ?");
2636
- stmt.bind([hash]);
2637
- if (!stmt.step()) {
2638
- stmt.free();
2639
- return null;
2640
- }
2641
- const row = stmt.get();
2642
- stmt.free();
2643
- return MCard.fromData(new Uint8Array(row[1]), row[0], row[2]);
2638
+ const rows = await this.queryRows(
2639
+ "SELECT hash, content, g_time FROM card WHERE hash = ?",
2640
+ hash
2641
+ );
2642
+ if (rows.length === 0) return null;
2643
+ return this.rowToCard(rows[0]);
2644
2644
  }
2645
2645
  async delete(hash) {
2646
- this.ensureDb().run("DELETE FROM card WHERE hash = ?", [hash]);
2646
+ await this.execSql("DELETE FROM card WHERE hash = ?", hash);
2647
2647
  }
2648
- async getPage(pageNumber, pageSize) {
2649
- const db = this.ensureDb();
2648
+ async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
2649
+ if (pageNumber < 1) throw new Error("Page number must be >= 1");
2650
+ if (pageSize < 1) throw new Error("Page size must be >= 1");
2650
2651
  const totalItems = await this.count();
2651
2652
  const offset = (pageNumber - 1) * pageSize;
2652
- const stmt = db.prepare("SELECT hash, content, g_time FROM card LIMIT ? OFFSET ?");
2653
- stmt.bind([pageSize, offset]);
2654
- const items = [];
2655
- while (stmt.step()) {
2656
- const row = stmt.get();
2657
- items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
2658
- }
2659
- stmt.free();
2653
+ const rows = await this.queryRows(
2654
+ "SELECT hash, content, g_time FROM card ORDER BY g_time DESC LIMIT ? OFFSET ?",
2655
+ pageSize,
2656
+ offset
2657
+ );
2658
+ const items = rows.map((r) => this.rowToCard(r));
2660
2659
  return createPage(items, totalItems, pageNumber, pageSize);
2661
2660
  }
2662
2661
  async count() {
2663
- const result = this.ensureDb().exec("SELECT COUNT(*) FROM card");
2664
- return result[0]?.values[0]?.[0] || 0;
2662
+ const rows = await this.queryRows("SELECT COUNT(*) as cnt FROM card");
2663
+ return Number(rows[0]?.cnt ?? 0);
2665
2664
  }
2666
2665
  async searchByHash(hashPrefix) {
2667
- const db = this.ensureDb();
2668
- const stmt = db.prepare("SELECT hash, content, g_time FROM card WHERE hash LIKE ?");
2669
- stmt.bind([`${hashPrefix}%`]);
2670
- const items = [];
2671
- while (stmt.step()) {
2672
- const row = stmt.get();
2673
- items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
2674
- }
2675
- stmt.free();
2676
- return items;
2666
+ const rows = await this.queryRows(
2667
+ "SELECT hash, content, g_time FROM card WHERE hash LIKE ?",
2668
+ `${hashPrefix}%`
2669
+ );
2670
+ return rows.map((r) => this.rowToCard(r));
2677
2671
  }
2678
- async search(query, pageNumber, pageSize) {
2679
- const db = this.ensureDb();
2672
+ async search(queryStr, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
2673
+ if (pageNumber < 1) throw new Error("Page number must be >= 1");
2674
+ if (pageSize < 1) throw new Error("Page size must be >= 1");
2680
2675
  const offset = (pageNumber - 1) * pageSize;
2681
- const pattern = `%${query}%`;
2682
- let stmt = db.prepare("SELECT COUNT(*) FROM card WHERE CAST(content AS TEXT) LIKE ?");
2683
- stmt.bind([pattern]);
2684
- stmt.step();
2685
- const totalItems = stmt.get()[0];
2686
- stmt.free();
2687
- stmt = db.prepare("SELECT hash, content, g_time FROM card WHERE CAST(content AS TEXT) LIKE ? LIMIT ? OFFSET ?");
2688
- stmt.bind([pattern, pageSize, offset]);
2689
- const items = [];
2690
- while (stmt.step()) {
2691
- const row = stmt.get();
2692
- items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
2693
- }
2694
- stmt.free();
2676
+ const pattern = `%${queryStr}%`;
2677
+ const cast = this.castContentAs;
2678
+ const countRows = await this.queryRows(
2679
+ `SELECT COUNT(*) as cnt FROM card WHERE CAST(content AS ${cast}) LIKE ?`,
2680
+ pattern
2681
+ );
2682
+ const totalItems = Number(countRows[0]?.cnt ?? 0);
2683
+ const rows = await this.queryRows(
2684
+ `SELECT hash, content, g_time FROM card WHERE CAST(content AS ${cast}) LIKE ? ORDER BY g_time DESC LIMIT ? OFFSET ?`,
2685
+ pattern,
2686
+ pageSize,
2687
+ offset
2688
+ );
2689
+ const items = rows.map((r) => this.rowToCard(r));
2695
2690
  return createPage(items, totalItems, pageNumber, pageSize);
2696
2691
  }
2697
2692
  async getAll() {
2698
- const result = this.ensureDb().exec("SELECT hash, content, g_time FROM card");
2699
- return (result[0]?.values || []).map(
2700
- ([h, content, g_time]) => MCard.fromData(new Uint8Array(content), h, g_time)
2701
- );
2693
+ const rows = await this.queryRows("SELECT hash, content, g_time FROM card ORDER BY g_time DESC");
2694
+ return rows.map((r) => this.rowToCard(r));
2702
2695
  }
2696
+ // ======================================================================
2697
+ // Concrete: clear (FK-safe delete order)
2698
+ // ======================================================================
2703
2699
  async clear() {
2704
- const db = this.ensureDb();
2705
- db.run("DELETE FROM card");
2706
- db.run("DELETE FROM handle_registry");
2707
- db.run("DELETE FROM handle_history");
2700
+ await this.execSql("DELETE FROM handle_history");
2701
+ await this.execSql("DELETE FROM handle_registry");
2702
+ await this.execSql("DELETE FROM card");
2708
2703
  }
2709
- // =========== Handle Operations ===========
2704
+ // ======================================================================
2705
+ // Concrete: Handle Operations
2706
+ // ======================================================================
2710
2707
  async registerHandle(handle, hash) {
2711
- const db = this.ensureDb();
2712
2708
  const normalized = validateHandle(handle);
2713
- const checkStmt = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
2714
- checkStmt.bind([normalized]);
2715
- const exists = checkStmt.step();
2716
- checkStmt.free();
2717
- if (exists) {
2709
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2710
+ const existing = await this.queryRows(
2711
+ "SELECT handle FROM handle_registry WHERE handle = ?",
2712
+ normalized
2713
+ );
2714
+ if (existing.length > 0) {
2718
2715
  throw new Error(`Handle '${handle}' already exists.`);
2719
2716
  }
2720
- const now = (/* @__PURE__ */ new Date()).toISOString();
2721
- db.run(
2717
+ await this.execSql(
2722
2718
  "INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
2723
- [normalized, hash, now, now]
2719
+ normalized,
2720
+ hash,
2721
+ now,
2722
+ now
2724
2723
  );
2725
2724
  }
2726
2725
  async resolveHandle(handle) {
2727
2726
  const normalized = validateHandle(handle);
2728
- const stmt = this.ensureDb().prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
2729
- stmt.bind([normalized]);
2730
- if (!stmt.step()) {
2731
- stmt.free();
2732
- return null;
2733
- }
2734
- const hash = stmt.get()[0];
2735
- stmt.free();
2736
- return hash || null;
2727
+ const rows = await this.queryRows(
2728
+ "SELECT current_hash FROM handle_registry WHERE handle = ?",
2729
+ normalized
2730
+ );
2731
+ if (rows.length === 0) return null;
2732
+ return String(rows[0].current_hash);
2737
2733
  }
2738
2734
  async getByHandle(handle) {
2739
2735
  const hash = await this.resolveHandle(handle);
@@ -2741,42 +2737,206 @@ var SqliteWasmEngine = class {
2741
2737
  return this.get(hash);
2742
2738
  }
2743
2739
  async updateHandle(handle, newHash) {
2744
- const db = this.ensureDb();
2745
2740
  const normalized = validateHandle(handle);
2746
- const getStmt = db.prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
2747
- getStmt.bind([normalized]);
2748
- if (!getStmt.step()) {
2749
- getStmt.free();
2741
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2742
+ const rows = await this.queryRows(
2743
+ "SELECT current_hash FROM handle_registry WHERE handle = ?",
2744
+ normalized
2745
+ );
2746
+ if (rows.length === 0) {
2750
2747
  throw new Error(`Handle '${handle}' not found.`);
2751
2748
  }
2752
- const previousHash = getStmt.get()[0];
2753
- getStmt.free();
2754
- const now = (/* @__PURE__ */ new Date()).toISOString();
2755
- db.run(
2749
+ const previousHash = String(rows[0].current_hash);
2750
+ await this.execSql(
2756
2751
  "INSERT INTO handle_history (handle, previous_hash, changed_at) VALUES (?, ?, ?)",
2757
- [normalized, previousHash, now]
2752
+ normalized,
2753
+ previousHash,
2754
+ now
2758
2755
  );
2759
- db.run(
2756
+ await this.execSql(
2760
2757
  "UPDATE handle_registry SET current_hash = ?, updated_at = ? WHERE handle = ?",
2761
- [newHash, now, normalized]
2758
+ newHash,
2759
+ now,
2760
+ normalized
2762
2761
  );
2763
2762
  return previousHash;
2764
2763
  }
2765
2764
  async getHandleHistory(handle) {
2766
2765
  const normalized = validateHandle(handle);
2767
- const stmt = this.ensureDb().prepare("SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC");
2768
- stmt.bind([normalized]);
2766
+ const rows = await this.queryRows(
2767
+ "SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC",
2768
+ normalized
2769
+ );
2770
+ return rows.map((r) => ({
2771
+ previousHash: String(r.previous_hash),
2772
+ changedAt: String(r.changed_at)
2773
+ }));
2774
+ }
2775
+ async pruneHandleHistory(handle, options = {}) {
2776
+ const normalized = validateHandle(handle);
2777
+ if (options.deleteAll) {
2778
+ return this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
2779
+ } else if (options.olderThan) {
2780
+ return this.execSql(
2781
+ "DELETE FROM handle_history WHERE handle = ? AND changed_at < ?",
2782
+ normalized,
2783
+ options.olderThan
2784
+ );
2785
+ }
2786
+ return 0;
2787
+ }
2788
+ // ======================================================================
2789
+ // Concrete: Handle Management Operations
2790
+ // ======================================================================
2791
+ async getAllHandles() {
2792
+ const rows = await this.queryRows("SELECT handle, current_hash FROM handle_registry");
2793
+ return rows.map((r) => ({
2794
+ handle: String(r.handle),
2795
+ hash: String(r.current_hash)
2796
+ }));
2797
+ }
2798
+ async removeHandle(handle) {
2799
+ const normalized = validateHandle(handle);
2800
+ await this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
2801
+ await this.execSql("DELETE FROM handle_registry WHERE handle = ?", normalized);
2802
+ }
2803
+ async renameHandle(oldHandle, newHandle) {
2804
+ const normalizedOld = validateHandle(oldHandle);
2805
+ const normalizedNew = validateHandle(newHandle);
2806
+ if (normalizedOld === normalizedNew) return;
2807
+ const existsOld = await this.queryRows(
2808
+ "SELECT handle FROM handle_registry WHERE handle = ?",
2809
+ normalizedOld
2810
+ );
2811
+ if (existsOld.length === 0) throw new Error(`Handle '${oldHandle}' not found.`);
2812
+ const existsNew = await this.queryRows(
2813
+ "SELECT handle FROM handle_registry WHERE handle = ?",
2814
+ normalizedNew
2815
+ );
2816
+ if (existsNew.length > 0) throw new Error(`Handle '${newHandle}' already exists.`);
2817
+ const oldRow = await this.queryRows(
2818
+ "SELECT current_hash, created_at, updated_at FROM handle_registry WHERE handle = ?",
2819
+ normalizedOld
2820
+ );
2821
+ await this.execSql(
2822
+ "INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
2823
+ normalizedNew,
2824
+ String(oldRow[0].current_hash),
2825
+ String(oldRow[0].created_at),
2826
+ String(oldRow[0].updated_at)
2827
+ );
2828
+ await this.execSql(
2829
+ "UPDATE handle_history SET handle = ? WHERE handle = ?",
2830
+ normalizedNew,
2831
+ normalizedOld
2832
+ );
2833
+ await this.execSql(
2834
+ "DELETE FROM handle_registry WHERE handle = ?",
2835
+ normalizedOld
2836
+ );
2837
+ }
2838
+ async deleteHistoryEntry(handle, previousHash) {
2839
+ const normalized = validateHandle(handle);
2840
+ const rOutRows = await this.queryRows(
2841
+ "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1",
2842
+ normalized,
2843
+ previousHash
2844
+ );
2845
+ if (rOutRows.length === 0) return;
2846
+ const rOutId = Number(rOutRows[0].id);
2847
+ const rInRows = await this.queryRows(
2848
+ "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1",
2849
+ normalized,
2850
+ rOutId
2851
+ );
2852
+ if (rInRows.length > 0) {
2853
+ const rInId = Number(rInRows[0].id);
2854
+ const rInPrevHash = String(rInRows[0].previous_hash);
2855
+ await this.execSql("UPDATE handle_history SET previous_hash = ? WHERE id = ?", rInPrevHash, rOutId);
2856
+ await this.execSql("DELETE FROM handle_history WHERE id = ?", rInId);
2857
+ } else {
2858
+ await this.execSql("DELETE FROM handle_history WHERE id = ?", rOutId);
2859
+ }
2860
+ }
2861
+ };
2862
+
2863
+ // src/storage/engines/SqliteWasmEngine.ts
2864
+ var SqliteWasmEngine = class extends AbstractSqlEngine {
2865
+ db = null;
2866
+ SQL = null;
2867
+ /**
2868
+ * Initialize the database
2869
+ * @param wasmUrl URL to sql-wasm.wasm file (optional, defaults to CDN)
2870
+ * @param existingData Optional existing database as Uint8Array
2871
+ */
2872
+ async init(wasmUrl, existingData) {
2873
+ const initSqlJs = (await import("sql.js")).default;
2874
+ const SQL = await initSqlJs({
2875
+ locateFile: (file) => wasmUrl || `${DEFAULT_SQLJS_WASM_URL}${file}`
2876
+ });
2877
+ this.SQL = SQL;
2878
+ this.db = existingData ? new SQL.Database(existingData) : new SQL.Database();
2879
+ this.db.run(CORE_SCHEMAS.card);
2880
+ this.db.run(CORE_SCHEMAS.handleRegistry);
2881
+ this.db.run(CORE_SCHEMAS.handleHistory);
2882
+ this.db.run(CORE_SCHEMAS.handleIndex);
2883
+ }
2884
+ ensureDb() {
2885
+ if (!this.db) throw new Error("Database not initialized. Call init() first.");
2886
+ return this.db;
2887
+ }
2888
+ // ======================================================================
2889
+ // AbstractSqlEngine primitives
2890
+ // ======================================================================
2891
+ async queryRows(sql, ...params) {
2892
+ const db = this.ensureDb();
2893
+ const stmt = db.prepare(sql);
2894
+ if (params.length > 0) stmt.bind(params);
2769
2895
  const results = [];
2770
2896
  while (stmt.step()) {
2771
- const row = stmt.get();
2772
- results.push({ previousHash: row[0], changedAt: row[1] });
2897
+ const row = stmt.getAsObject();
2898
+ results.push(row);
2773
2899
  }
2774
2900
  stmt.free();
2775
2901
  return results;
2776
2902
  }
2903
+ async execSql(sql, ...params) {
2904
+ const db = this.ensureDb();
2905
+ if (params.length === 0) {
2906
+ db.run(sql);
2907
+ } else {
2908
+ db.run(sql, params);
2909
+ }
2910
+ return 0;
2911
+ }
2912
+ // ======================================================================
2913
+ // Row → MCard conversion (sql.js returns Uint8Array for BLOBs)
2914
+ // ======================================================================
2915
+ rowToCard(row) {
2916
+ const rawContent = row.content;
2917
+ const content = rawContent instanceof Uint8Array ? rawContent : typeof rawContent === "string" ? new TextEncoder().encode(rawContent) : new Uint8Array(0);
2918
+ return MCard.fromData(content, String(row.hash), String(row.g_time));
2919
+ }
2920
+ // ======================================================================
2921
+ // sql.js-specific helpers
2922
+ // ======================================================================
2923
+ /**
2924
+ * Export database as Uint8Array (for persistence)
2925
+ */
2926
+ export() {
2927
+ return this.ensureDb().export();
2928
+ }
2929
+ /**
2930
+ * Get raw sql.js Database for use with VectorStore adapter.
2931
+ */
2932
+ getRawDb() {
2933
+ return this.ensureDb();
2934
+ }
2935
+ // =========== pruneHandleHistory override (needs count before delete) ===========
2777
2936
  async pruneHandleHistory(handle, options = {}) {
2937
+ const { validateHandle: validateHandle2 } = await Promise.resolve().then(() => (init_Handle(), Handle_exports));
2778
2938
  const db = this.ensureDb();
2779
- const normalized = validateHandle(handle);
2939
+ const normalized = validateHandle2(handle);
2780
2940
  if (options.deleteAll) {
2781
2941
  const stmt = db.prepare("SELECT COUNT(*) FROM handle_history WHERE handle = ?");
2782
2942
  stmt.bind([normalized]);
@@ -2796,67 +2956,6 @@ var SqliteWasmEngine = class {
2796
2956
  }
2797
2957
  return 0;
2798
2958
  }
2799
- // =========== Handle Management Operations ===========
2800
- async getAllHandles() {
2801
- const result = this.ensureDb().exec("SELECT handle, current_hash FROM handle_registry");
2802
- return (result[0]?.values || []).map(([handle, hash]) => ({
2803
- handle,
2804
- hash
2805
- }));
2806
- }
2807
- async removeHandle(handle) {
2808
- const db = this.ensureDb();
2809
- const normalized = validateHandle(handle);
2810
- db.run("DELETE FROM handle_history WHERE handle = ?", [normalized]);
2811
- db.run("DELETE FROM handle_registry WHERE handle = ?", [normalized]);
2812
- }
2813
- async renameHandle(oldHandle, newHandle) {
2814
- const db = this.ensureDb();
2815
- const normalizedOld = validateHandle(oldHandle);
2816
- const normalizedNew = validateHandle(newHandle);
2817
- if (normalizedOld === normalizedNew) return;
2818
- const checkOld = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
2819
- checkOld.bind([normalizedOld]);
2820
- const oldExists = checkOld.step();
2821
- checkOld.free();
2822
- if (!oldExists) throw new Error(`Handle '${oldHandle}' not found.`);
2823
- const checkNew = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
2824
- checkNew.bind([normalizedNew]);
2825
- const newExists = checkNew.step();
2826
- checkNew.free();
2827
- if (newExists) throw new Error(`Handle '${newHandle}' already exists.`);
2828
- db.run("UPDATE handle_registry SET handle = ? WHERE handle = ?", [normalizedNew, normalizedOld]);
2829
- db.run("UPDATE handle_history SET handle = ? WHERE handle = ?", [normalizedNew, normalizedOld]);
2830
- }
2831
- async deleteHistoryEntry(handle, previousHash) {
2832
- const db = this.ensureDb();
2833
- const normalized = validateHandle(handle);
2834
- const rOutStmt = db.prepare(
2835
- "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1"
2836
- );
2837
- rOutStmt.bind([normalized, previousHash]);
2838
- if (!rOutStmt.step()) {
2839
- rOutStmt.free();
2840
- return;
2841
- }
2842
- const rOutId = rOutStmt.get()[0];
2843
- rOutStmt.free();
2844
- const rInStmt = db.prepare(
2845
- "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1"
2846
- );
2847
- rInStmt.bind([normalized, rOutId]);
2848
- const hasRIn = rInStmt.step();
2849
- if (hasRIn) {
2850
- const rInPrevHash = rInStmt.get()[1];
2851
- const rInId = rInStmt.get()[0];
2852
- rInStmt.free();
2853
- db.run("UPDATE handle_history SET previous_hash = ? WHERE id = ?", [rInPrevHash, rOutId]);
2854
- db.run("DELETE FROM handle_history WHERE id = ?", [rInId]);
2855
- } else {
2856
- rInStmt.free();
2857
- db.run("DELETE FROM handle_history WHERE id = ?", [rOutId]);
2858
- }
2859
- }
2860
2959
  };
2861
2960
 
2862
2961
  // src/index.browser.ts