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
package/dist/index.cjs CHANGED
@@ -959,6 +959,25 @@ var init_mime_extensions = __esm({
959
959
  }
960
960
  });
961
961
 
962
+ // src/config/constants.ts
963
+ var DEFAULT_PAGE_SIZE, DETECTION_SAMPLE_CAP, MAX_FILE_SIZE, KNOWN_TYPE_SIZE_LIMIT, BINARY_CHECK_SAMPLE_SIZE, CONTENT_DETECTION_SAMPLE_SIZE, DEFAULT_MAX_PROBLEM_BYTES, INDEXEDDB_DEFAULT_DB_NAME, INDEXEDDB_DEFAULT_DB_VERSION, SQLITE_BUSY_TIMEOUT_MS, DEFAULT_SQLJS_WASM_URL;
964
+ var init_constants = __esm({
965
+ "src/config/constants.ts"() {
966
+ "use strict";
967
+ DEFAULT_PAGE_SIZE = 10;
968
+ DETECTION_SAMPLE_CAP = 8192;
969
+ MAX_FILE_SIZE = 50 * 1024 * 1024;
970
+ KNOWN_TYPE_SIZE_LIMIT = 1024 * 1024;
971
+ BINARY_CHECK_SAMPLE_SIZE = 32 * 1024;
972
+ CONTENT_DETECTION_SAMPLE_SIZE = 1024 * 1024;
973
+ DEFAULT_MAX_PROBLEM_BYTES = 2 * 1024 * 1024;
974
+ INDEXEDDB_DEFAULT_DB_NAME = "mcard-db";
975
+ INDEXEDDB_DEFAULT_DB_VERSION = 1;
976
+ SQLITE_BUSY_TIMEOUT_MS = 5e3;
977
+ DEFAULT_SQLJS_WASM_URL = "https://sql.js.org/dist/";
978
+ }
979
+ });
980
+
962
981
  // src/model/strategies/DetectionStrategy.ts
963
982
  function loadSharedMimeData() {
964
983
  return {
@@ -975,6 +994,7 @@ var init_DetectionStrategy = __esm({
975
994
  "use strict";
976
995
  init_registry();
977
996
  init_mime_extensions();
997
+ init_constants();
978
998
  _sharedData = loadSharedMimeData();
979
999
  DefaultDetectionStrategy = class _DefaultDetectionStrategy {
980
1000
  constructor(customRegistry) {
@@ -997,7 +1017,7 @@ var init_DetectionStrategy = __esm({
997
1017
  return this.extensionsRegistry[mimeType] || "";
998
1018
  }
999
1019
  detect(content, fileExtension) {
1000
- const contentSample = typeof content === "string" ? content.slice(0, 8192) : content.slice(0, 8192);
1020
+ const contentSample = typeof content === "string" ? content.slice(0, DETECTION_SAMPLE_CAP) : content.slice(0, DETECTION_SAMPLE_CAP);
1001
1021
  const textSample = this.getTextSample(contentSample);
1002
1022
  const lines = textSample.split("\n").slice(0, 20);
1003
1023
  const firstLine = lines[0] || "";
@@ -1031,9 +1051,9 @@ var init_DetectionStrategy = __esm({
1031
1051
  }
1032
1052
  getTextSample(content) {
1033
1053
  if (typeof content === "string") {
1034
- return content.slice(0, 8192);
1054
+ return content.slice(0, DETECTION_SAMPLE_CAP);
1035
1055
  }
1036
- return new TextDecoder("utf-8", { fatal: false }).decode(content.slice(0, 8192));
1056
+ return new TextDecoder("utf-8", { fatal: false }).decode(content.slice(0, DETECTION_SAMPLE_CAP));
1037
1057
  }
1038
1058
  getExtension(mimeType) {
1039
1059
  return _DefaultDetectionStrategy.getExtension(mimeType);
@@ -1303,6 +1323,12 @@ var init_MCard = __esm({
1303
1323
  });
1304
1324
 
1305
1325
  // src/model/Handle.ts
1326
+ var Handle_exports = {};
1327
+ __export(Handle_exports, {
1328
+ ContentHandle: () => ContentHandle,
1329
+ HandleValidationError: () => HandleValidationError,
1330
+ validateHandle: () => validateHandle
1331
+ });
1306
1332
  function isValidStartChar(char) {
1307
1333
  return /^\p{L}$/u.test(char);
1308
1334
  }
@@ -1313,7 +1339,7 @@ function validateHandle(handle) {
1313
1339
  if (!handle) {
1314
1340
  throw new HandleValidationError("Handle cannot be empty.");
1315
1341
  }
1316
- const normalized = handle.trim().normalize("NFC").toLowerCase();
1342
+ const normalized = handle.trim().normalize("NFC");
1317
1343
  if (normalized.length === 0) {
1318
1344
  throw new HandleValidationError("Handle cannot be empty after normalization.");
1319
1345
  }
@@ -1454,7 +1480,7 @@ var init_Maybe = __esm({
1454
1480
 
1455
1481
  // src/model/constants.ts
1456
1482
  var EVENT_CONSTANTS, ALGORITHM_HIERARCHY;
1457
- var init_constants = __esm({
1483
+ var init_constants2 = __esm({
1458
1484
  "src/model/constants.ts"() {
1459
1485
  "use strict";
1460
1486
  EVENT_CONSTANTS = {
@@ -1521,7 +1547,7 @@ function nextHashFunction(current) {
1521
1547
  var init_EventProducer = __esm({
1522
1548
  "src/model/EventProducer.ts"() {
1523
1549
  "use strict";
1524
- init_constants();
1550
+ init_constants2();
1525
1551
  init_GTime();
1526
1552
  init_HashValidator();
1527
1553
  }
@@ -1537,6 +1563,7 @@ var init_CardCollection = __esm({
1537
1563
  "src/model/CardCollection.ts"() {
1538
1564
  "use strict";
1539
1565
  init_MCard();
1566
+ init_constants();
1540
1567
  init_Maybe();
1541
1568
  CardCollection = class {
1542
1569
  engine;
@@ -1596,7 +1623,7 @@ var init_CardCollection = __esm({
1596
1623
  /**
1597
1624
  * Get a page of cards
1598
1625
  */
1599
- async getPage(pageNumber = 1, pageSize = 10) {
1626
+ async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
1600
1627
  return this.engine.getPage(pageNumber, pageSize);
1601
1628
  }
1602
1629
  /**
@@ -1686,10 +1713,10 @@ var init_CardCollection = __esm({
1686
1713
  async clear() {
1687
1714
  return this.engine.clear();
1688
1715
  }
1689
- async searchByString(query, pageNumber = 1, pageSize = 10) {
1716
+ async searchByString(query, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
1690
1717
  return this.engine.search(query, pageNumber, pageSize);
1691
1718
  }
1692
- async searchByContent(query, pageNumber = 1, pageSize = 10) {
1719
+ async searchByContent(query, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
1693
1720
  return this.engine.search(query, pageNumber, pageSize);
1694
1721
  }
1695
1722
  async searchByHash(hashPrefix) {
@@ -1698,7 +1725,7 @@ var init_CardCollection = __esm({
1698
1725
  async getAllMCardsRaw() {
1699
1726
  return this.engine.getAll();
1700
1727
  }
1701
- async getAllCards(pageSize = 10, processCallback) {
1728
+ async getAllCards(pageSize = DEFAULT_PAGE_SIZE, processCallback) {
1702
1729
  const cards = [];
1703
1730
  let pageNumber = 1;
1704
1731
  let total = 0;
@@ -1789,17 +1816,18 @@ var init_IndexedDBEngine = __esm({
1789
1816
  init_MCard();
1790
1817
  init_Handle();
1791
1818
  init_StorageAdapter();
1819
+ init_constants();
1792
1820
  IndexedDBEngine = class {
1793
1821
  db = null;
1794
1822
  dbName;
1795
- constructor(dbName = "mcard-db") {
1823
+ constructor(dbName = INDEXEDDB_DEFAULT_DB_NAME) {
1796
1824
  this.dbName = dbName;
1797
1825
  }
1798
1826
  /**
1799
1827
  * Initialize the database connection
1800
1828
  */
1801
1829
  async init() {
1802
- this.db = await (0, import_idb.openDB)(this.dbName, 1, {
1830
+ this.db = await (0, import_idb.openDB)(this.dbName, INDEXEDDB_DEFAULT_DB_VERSION, {
1803
1831
  upgrade(db) {
1804
1832
  if (!db.objectStoreNames.contains("cards")) {
1805
1833
  db.createObjectStore("cards", { keyPath: "hash" });
@@ -1844,7 +1872,7 @@ var init_IndexedDBEngine = __esm({
1844
1872
  const db = this.ensureDb();
1845
1873
  await db.delete("cards", hash);
1846
1874
  }
1847
- async getPage(pageNumber, pageSize) {
1875
+ async getPage(pageNumber, pageSize = DEFAULT_PAGE_SIZE) {
1848
1876
  const db = this.ensureDb();
1849
1877
  const totalItems = await db.count("cards");
1850
1878
  const allCards = await db.getAll("cards");
@@ -1865,7 +1893,7 @@ var init_IndexedDBEngine = __esm({
1865
1893
  const records = await db.getAll("cards", range);
1866
1894
  return records.map((r) => MCard.fromData(r.content, r.hash, r.g_time));
1867
1895
  }
1868
- async search(query, pageNumber, pageSize) {
1896
+ async search(query, pageNumber, pageSize = DEFAULT_PAGE_SIZE) {
1869
1897
  const db = this.ensureDb();
1870
1898
  const records = await db.getAll("cards");
1871
1899
  const decoder = new TextDecoder();
@@ -2707,167 +2735,135 @@ var init_schema = __esm({
2707
2735
  }
2708
2736
  });
2709
2737
 
2710
- // src/storage/engines/SqliteWasmEngine.ts
2711
- var SqliteWasmEngine;
2712
- var init_SqliteWasmEngine = __esm({
2713
- "src/storage/engines/SqliteWasmEngine.ts"() {
2738
+ // src/storage/engines/AbstractSqlEngine.ts
2739
+ var AbstractSqlEngine;
2740
+ var init_AbstractSqlEngine = __esm({
2741
+ "src/storage/engines/AbstractSqlEngine.ts"() {
2714
2742
  "use strict";
2715
- init_MCard();
2716
2743
  init_Handle();
2717
2744
  init_StorageAdapter();
2718
- init_schema();
2719
- SqliteWasmEngine = class {
2720
- db = null;
2721
- SQL = null;
2722
- /**
2723
- * Initialize the database
2724
- * @param wasmUrl URL to sql-wasm.wasm file (optional, defaults to CDN)
2725
- * @param existingData Optional existing database as Uint8Array
2726
- */
2727
- async init(wasmUrl, existingData) {
2728
- const initSqlJs = (await import("sql.js")).default;
2729
- const SQL = await initSqlJs({
2730
- locateFile: (file) => wasmUrl || `https://sql.js.org/dist/${file}`
2731
- });
2732
- this.SQL = SQL;
2733
- this.db = existingData ? new SQL.Database(existingData) : new SQL.Database();
2734
- this.db.run(CORE_SCHEMAS.card);
2735
- this.db.run(CORE_SCHEMAS.handleRegistry);
2736
- this.db.run(CORE_SCHEMAS.handleHistory);
2737
- this.db.run(CORE_SCHEMAS.handleIndex);
2738
- }
2739
- ensureDb() {
2740
- if (!this.db) throw new Error("Database not initialized. Call init() first.");
2741
- return this.db;
2742
- }
2745
+ init_constants();
2746
+ AbstractSqlEngine = class {
2743
2747
  /**
2744
- * Export database as Uint8Array (for persistence)
2748
+ * SQL expression for casting content to text in search queries.
2749
+ * Override in subclasses: SQLite uses 'TEXT', DuckDB uses 'VARCHAR'.
2745
2750
  */
2746
- export() {
2747
- return this.ensureDb().export();
2751
+ get castContentAs() {
2752
+ return "TEXT";
2748
2753
  }
2754
+ // ======================================================================
2755
+ // Concrete: Card Operations (shared across all SQL engines)
2756
+ // ======================================================================
2749
2757
  /**
2750
- * Get raw sql.js Database for use with VectorStore adapter.
2751
- *
2752
- * Example:
2753
- * const vecStore = createWasmVectorStore(engine.getRawDb());
2758
+ * Add a card. Default uses INSERT OR REPLACE (SQLite).
2759
+ * DuckDB overrides with DELETE + INSERT.
2754
2760
  */
2755
- getRawDb() {
2756
- return this.ensureDb();
2757
- }
2758
- // =========== Card Operations ===========
2759
2761
  async add(card) {
2760
- const db = this.ensureDb();
2761
- db.run(
2762
+ const contentBytes = card.content instanceof Uint8Array ? card.content : new TextEncoder().encode(String(card.content));
2763
+ await this.execSql(
2762
2764
  "INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)",
2763
- [card.hash, card.content, card.g_time]
2765
+ card.hash,
2766
+ contentBytes,
2767
+ card.g_time
2764
2768
  );
2765
2769
  return card.hash;
2766
2770
  }
2767
2771
  async get(hash) {
2768
- const db = this.ensureDb();
2769
- const stmt = db.prepare("SELECT hash, content, g_time FROM card WHERE hash = ?");
2770
- stmt.bind([hash]);
2771
- if (!stmt.step()) {
2772
- stmt.free();
2773
- return null;
2774
- }
2775
- const row = stmt.get();
2776
- stmt.free();
2777
- return MCard.fromData(new Uint8Array(row[1]), row[0], row[2]);
2772
+ const rows = await this.queryRows(
2773
+ "SELECT hash, content, g_time FROM card WHERE hash = ?",
2774
+ hash
2775
+ );
2776
+ if (rows.length === 0) return null;
2777
+ return this.rowToCard(rows[0]);
2778
2778
  }
2779
2779
  async delete(hash) {
2780
- this.ensureDb().run("DELETE FROM card WHERE hash = ?", [hash]);
2780
+ await this.execSql("DELETE FROM card WHERE hash = ?", hash);
2781
2781
  }
2782
- async getPage(pageNumber, pageSize) {
2783
- const db = this.ensureDb();
2782
+ async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
2783
+ if (pageNumber < 1) throw new Error("Page number must be >= 1");
2784
+ if (pageSize < 1) throw new Error("Page size must be >= 1");
2784
2785
  const totalItems = await this.count();
2785
2786
  const offset = (pageNumber - 1) * pageSize;
2786
- const stmt = db.prepare("SELECT hash, content, g_time FROM card LIMIT ? OFFSET ?");
2787
- stmt.bind([pageSize, offset]);
2788
- const items = [];
2789
- while (stmt.step()) {
2790
- const row = stmt.get();
2791
- items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
2792
- }
2793
- stmt.free();
2787
+ const rows = await this.queryRows(
2788
+ "SELECT hash, content, g_time FROM card ORDER BY g_time DESC LIMIT ? OFFSET ?",
2789
+ pageSize,
2790
+ offset
2791
+ );
2792
+ const items = rows.map((r) => this.rowToCard(r));
2794
2793
  return createPage(items, totalItems, pageNumber, pageSize);
2795
2794
  }
2796
2795
  async count() {
2797
- const result = this.ensureDb().exec("SELECT COUNT(*) FROM card");
2798
- return result[0]?.values[0]?.[0] || 0;
2796
+ const rows = await this.queryRows("SELECT COUNT(*) as cnt FROM card");
2797
+ return Number(rows[0]?.cnt ?? 0);
2799
2798
  }
2800
2799
  async searchByHash(hashPrefix) {
2801
- const db = this.ensureDb();
2802
- const stmt = db.prepare("SELECT hash, content, g_time FROM card WHERE hash LIKE ?");
2803
- stmt.bind([`${hashPrefix}%`]);
2804
- const items = [];
2805
- while (stmt.step()) {
2806
- const row = stmt.get();
2807
- items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
2808
- }
2809
- stmt.free();
2810
- return items;
2800
+ const rows = await this.queryRows(
2801
+ "SELECT hash, content, g_time FROM card WHERE hash LIKE ?",
2802
+ `${hashPrefix}%`
2803
+ );
2804
+ return rows.map((r) => this.rowToCard(r));
2811
2805
  }
2812
- async search(query, pageNumber, pageSize) {
2813
- const db = this.ensureDb();
2806
+ async search(queryStr, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
2807
+ if (pageNumber < 1) throw new Error("Page number must be >= 1");
2808
+ if (pageSize < 1) throw new Error("Page size must be >= 1");
2814
2809
  const offset = (pageNumber - 1) * pageSize;
2815
- const pattern = `%${query}%`;
2816
- let stmt = db.prepare("SELECT COUNT(*) FROM card WHERE CAST(content AS TEXT) LIKE ?");
2817
- stmt.bind([pattern]);
2818
- stmt.step();
2819
- const totalItems = stmt.get()[0];
2820
- stmt.free();
2821
- stmt = db.prepare("SELECT hash, content, g_time FROM card WHERE CAST(content AS TEXT) LIKE ? LIMIT ? OFFSET ?");
2822
- stmt.bind([pattern, pageSize, offset]);
2823
- const items = [];
2824
- while (stmt.step()) {
2825
- const row = stmt.get();
2826
- items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
2827
- }
2828
- stmt.free();
2810
+ const pattern = `%${queryStr}%`;
2811
+ const cast = this.castContentAs;
2812
+ const countRows = await this.queryRows(
2813
+ `SELECT COUNT(*) as cnt FROM card WHERE CAST(content AS ${cast}) LIKE ?`,
2814
+ pattern
2815
+ );
2816
+ const totalItems = Number(countRows[0]?.cnt ?? 0);
2817
+ const rows = await this.queryRows(
2818
+ `SELECT hash, content, g_time FROM card WHERE CAST(content AS ${cast}) LIKE ? ORDER BY g_time DESC LIMIT ? OFFSET ?`,
2819
+ pattern,
2820
+ pageSize,
2821
+ offset
2822
+ );
2823
+ const items = rows.map((r) => this.rowToCard(r));
2829
2824
  return createPage(items, totalItems, pageNumber, pageSize);
2830
2825
  }
2831
2826
  async getAll() {
2832
- const result = this.ensureDb().exec("SELECT hash, content, g_time FROM card");
2833
- return (result[0]?.values || []).map(
2834
- ([h, content, g_time]) => MCard.fromData(new Uint8Array(content), h, g_time)
2835
- );
2827
+ const rows = await this.queryRows("SELECT hash, content, g_time FROM card ORDER BY g_time DESC");
2828
+ return rows.map((r) => this.rowToCard(r));
2836
2829
  }
2830
+ // ======================================================================
2831
+ // Concrete: clear (FK-safe delete order)
2832
+ // ======================================================================
2837
2833
  async clear() {
2838
- const db = this.ensureDb();
2839
- db.run("DELETE FROM card");
2840
- db.run("DELETE FROM handle_registry");
2841
- db.run("DELETE FROM handle_history");
2834
+ await this.execSql("DELETE FROM handle_history");
2835
+ await this.execSql("DELETE FROM handle_registry");
2836
+ await this.execSql("DELETE FROM card");
2842
2837
  }
2843
- // =========== Handle Operations ===========
2838
+ // ======================================================================
2839
+ // Concrete: Handle Operations
2840
+ // ======================================================================
2844
2841
  async registerHandle(handle, hash) {
2845
- const db = this.ensureDb();
2846
2842
  const normalized = validateHandle(handle);
2847
- const checkStmt = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
2848
- checkStmt.bind([normalized]);
2849
- const exists = checkStmt.step();
2850
- checkStmt.free();
2851
- if (exists) {
2843
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2844
+ const existing = await this.queryRows(
2845
+ "SELECT handle FROM handle_registry WHERE handle = ?",
2846
+ normalized
2847
+ );
2848
+ if (existing.length > 0) {
2852
2849
  throw new Error(`Handle '${handle}' already exists.`);
2853
2850
  }
2854
- const now = (/* @__PURE__ */ new Date()).toISOString();
2855
- db.run(
2851
+ await this.execSql(
2856
2852
  "INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
2857
- [normalized, hash, now, now]
2853
+ normalized,
2854
+ hash,
2855
+ now,
2856
+ now
2858
2857
  );
2859
2858
  }
2860
2859
  async resolveHandle(handle) {
2861
2860
  const normalized = validateHandle(handle);
2862
- const stmt = this.ensureDb().prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
2863
- stmt.bind([normalized]);
2864
- if (!stmt.step()) {
2865
- stmt.free();
2866
- return null;
2867
- }
2868
- const hash = stmt.get()[0];
2869
- stmt.free();
2870
- return hash || null;
2861
+ const rows = await this.queryRows(
2862
+ "SELECT current_hash FROM handle_registry WHERE handle = ?",
2863
+ normalized
2864
+ );
2865
+ if (rows.length === 0) return null;
2866
+ return String(rows[0].current_hash);
2871
2867
  }
2872
2868
  async getByHandle(handle) {
2873
2869
  const hash = await this.resolveHandle(handle);
@@ -2875,42 +2871,216 @@ var init_SqliteWasmEngine = __esm({
2875
2871
  return this.get(hash);
2876
2872
  }
2877
2873
  async updateHandle(handle, newHash) {
2878
- const db = this.ensureDb();
2879
2874
  const normalized = validateHandle(handle);
2880
- const getStmt = db.prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
2881
- getStmt.bind([normalized]);
2882
- if (!getStmt.step()) {
2883
- getStmt.free();
2875
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2876
+ const rows = await this.queryRows(
2877
+ "SELECT current_hash FROM handle_registry WHERE handle = ?",
2878
+ normalized
2879
+ );
2880
+ if (rows.length === 0) {
2884
2881
  throw new Error(`Handle '${handle}' not found.`);
2885
2882
  }
2886
- const previousHash = getStmt.get()[0];
2887
- getStmt.free();
2888
- const now = (/* @__PURE__ */ new Date()).toISOString();
2889
- db.run(
2883
+ const previousHash = String(rows[0].current_hash);
2884
+ await this.execSql(
2890
2885
  "INSERT INTO handle_history (handle, previous_hash, changed_at) VALUES (?, ?, ?)",
2891
- [normalized, previousHash, now]
2886
+ normalized,
2887
+ previousHash,
2888
+ now
2892
2889
  );
2893
- db.run(
2890
+ await this.execSql(
2894
2891
  "UPDATE handle_registry SET current_hash = ?, updated_at = ? WHERE handle = ?",
2895
- [newHash, now, normalized]
2892
+ newHash,
2893
+ now,
2894
+ normalized
2896
2895
  );
2897
2896
  return previousHash;
2898
2897
  }
2899
2898
  async getHandleHistory(handle) {
2900
2899
  const normalized = validateHandle(handle);
2901
- const stmt = this.ensureDb().prepare("SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC");
2902
- stmt.bind([normalized]);
2900
+ const rows = await this.queryRows(
2901
+ "SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC",
2902
+ normalized
2903
+ );
2904
+ return rows.map((r) => ({
2905
+ previousHash: String(r.previous_hash),
2906
+ changedAt: String(r.changed_at)
2907
+ }));
2908
+ }
2909
+ async pruneHandleHistory(handle, options = {}) {
2910
+ const normalized = validateHandle(handle);
2911
+ if (options.deleteAll) {
2912
+ return this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
2913
+ } else if (options.olderThan) {
2914
+ return this.execSql(
2915
+ "DELETE FROM handle_history WHERE handle = ? AND changed_at < ?",
2916
+ normalized,
2917
+ options.olderThan
2918
+ );
2919
+ }
2920
+ return 0;
2921
+ }
2922
+ // ======================================================================
2923
+ // Concrete: Handle Management Operations
2924
+ // ======================================================================
2925
+ async getAllHandles() {
2926
+ const rows = await this.queryRows("SELECT handle, current_hash FROM handle_registry");
2927
+ return rows.map((r) => ({
2928
+ handle: String(r.handle),
2929
+ hash: String(r.current_hash)
2930
+ }));
2931
+ }
2932
+ async removeHandle(handle) {
2933
+ const normalized = validateHandle(handle);
2934
+ await this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
2935
+ await this.execSql("DELETE FROM handle_registry WHERE handle = ?", normalized);
2936
+ }
2937
+ async renameHandle(oldHandle, newHandle) {
2938
+ const normalizedOld = validateHandle(oldHandle);
2939
+ const normalizedNew = validateHandle(newHandle);
2940
+ if (normalizedOld === normalizedNew) return;
2941
+ const existsOld = await this.queryRows(
2942
+ "SELECT handle FROM handle_registry WHERE handle = ?",
2943
+ normalizedOld
2944
+ );
2945
+ if (existsOld.length === 0) throw new Error(`Handle '${oldHandle}' not found.`);
2946
+ const existsNew = await this.queryRows(
2947
+ "SELECT handle FROM handle_registry WHERE handle = ?",
2948
+ normalizedNew
2949
+ );
2950
+ if (existsNew.length > 0) throw new Error(`Handle '${newHandle}' already exists.`);
2951
+ const oldRow = await this.queryRows(
2952
+ "SELECT current_hash, created_at, updated_at FROM handle_registry WHERE handle = ?",
2953
+ normalizedOld
2954
+ );
2955
+ await this.execSql(
2956
+ "INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
2957
+ normalizedNew,
2958
+ String(oldRow[0].current_hash),
2959
+ String(oldRow[0].created_at),
2960
+ String(oldRow[0].updated_at)
2961
+ );
2962
+ await this.execSql(
2963
+ "UPDATE handle_history SET handle = ? WHERE handle = ?",
2964
+ normalizedNew,
2965
+ normalizedOld
2966
+ );
2967
+ await this.execSql(
2968
+ "DELETE FROM handle_registry WHERE handle = ?",
2969
+ normalizedOld
2970
+ );
2971
+ }
2972
+ async deleteHistoryEntry(handle, previousHash) {
2973
+ const normalized = validateHandle(handle);
2974
+ const rOutRows = await this.queryRows(
2975
+ "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1",
2976
+ normalized,
2977
+ previousHash
2978
+ );
2979
+ if (rOutRows.length === 0) return;
2980
+ const rOutId = Number(rOutRows[0].id);
2981
+ const rInRows = await this.queryRows(
2982
+ "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1",
2983
+ normalized,
2984
+ rOutId
2985
+ );
2986
+ if (rInRows.length > 0) {
2987
+ const rInId = Number(rInRows[0].id);
2988
+ const rInPrevHash = String(rInRows[0].previous_hash);
2989
+ await this.execSql("UPDATE handle_history SET previous_hash = ? WHERE id = ?", rInPrevHash, rOutId);
2990
+ await this.execSql("DELETE FROM handle_history WHERE id = ?", rInId);
2991
+ } else {
2992
+ await this.execSql("DELETE FROM handle_history WHERE id = ?", rOutId);
2993
+ }
2994
+ }
2995
+ };
2996
+ }
2997
+ });
2998
+
2999
+ // src/storage/engines/SqliteWasmEngine.ts
3000
+ var SqliteWasmEngine;
3001
+ var init_SqliteWasmEngine = __esm({
3002
+ "src/storage/engines/SqliteWasmEngine.ts"() {
3003
+ "use strict";
3004
+ init_MCard();
3005
+ init_schema();
3006
+ init_AbstractSqlEngine();
3007
+ init_constants();
3008
+ SqliteWasmEngine = class extends AbstractSqlEngine {
3009
+ db = null;
3010
+ SQL = null;
3011
+ /**
3012
+ * Initialize the database
3013
+ * @param wasmUrl URL to sql-wasm.wasm file (optional, defaults to CDN)
3014
+ * @param existingData Optional existing database as Uint8Array
3015
+ */
3016
+ async init(wasmUrl, existingData) {
3017
+ const initSqlJs = (await import("sql.js")).default;
3018
+ const SQL = await initSqlJs({
3019
+ locateFile: (file) => wasmUrl || `${DEFAULT_SQLJS_WASM_URL}${file}`
3020
+ });
3021
+ this.SQL = SQL;
3022
+ this.db = existingData ? new SQL.Database(existingData) : new SQL.Database();
3023
+ this.db.run(CORE_SCHEMAS.card);
3024
+ this.db.run(CORE_SCHEMAS.handleRegistry);
3025
+ this.db.run(CORE_SCHEMAS.handleHistory);
3026
+ this.db.run(CORE_SCHEMAS.handleIndex);
3027
+ }
3028
+ ensureDb() {
3029
+ if (!this.db) throw new Error("Database not initialized. Call init() first.");
3030
+ return this.db;
3031
+ }
3032
+ // ======================================================================
3033
+ // AbstractSqlEngine primitives
3034
+ // ======================================================================
3035
+ async queryRows(sql, ...params) {
3036
+ const db = this.ensureDb();
3037
+ const stmt = db.prepare(sql);
3038
+ if (params.length > 0) stmt.bind(params);
2903
3039
  const results = [];
2904
3040
  while (stmt.step()) {
2905
- const row = stmt.get();
2906
- results.push({ previousHash: row[0], changedAt: row[1] });
3041
+ const row = stmt.getAsObject();
3042
+ results.push(row);
2907
3043
  }
2908
3044
  stmt.free();
2909
3045
  return results;
2910
3046
  }
3047
+ async execSql(sql, ...params) {
3048
+ const db = this.ensureDb();
3049
+ if (params.length === 0) {
3050
+ db.run(sql);
3051
+ } else {
3052
+ db.run(sql, params);
3053
+ }
3054
+ return 0;
3055
+ }
3056
+ // ======================================================================
3057
+ // Row → MCard conversion (sql.js returns Uint8Array for BLOBs)
3058
+ // ======================================================================
3059
+ rowToCard(row) {
3060
+ const rawContent = row.content;
3061
+ const content = rawContent instanceof Uint8Array ? rawContent : typeof rawContent === "string" ? new TextEncoder().encode(rawContent) : new Uint8Array(0);
3062
+ return MCard.fromData(content, String(row.hash), String(row.g_time));
3063
+ }
3064
+ // ======================================================================
3065
+ // sql.js-specific helpers
3066
+ // ======================================================================
3067
+ /**
3068
+ * Export database as Uint8Array (for persistence)
3069
+ */
3070
+ export() {
3071
+ return this.ensureDb().export();
3072
+ }
3073
+ /**
3074
+ * Get raw sql.js Database for use with VectorStore adapter.
3075
+ */
3076
+ getRawDb() {
3077
+ return this.ensureDb();
3078
+ }
3079
+ // =========== pruneHandleHistory override (needs count before delete) ===========
2911
3080
  async pruneHandleHistory(handle, options = {}) {
3081
+ const { validateHandle: validateHandle2 } = await Promise.resolve().then(() => (init_Handle(), Handle_exports));
2912
3082
  const db = this.ensureDb();
2913
- const normalized = validateHandle(handle);
3083
+ const normalized = validateHandle2(handle);
2914
3084
  if (options.deleteAll) {
2915
3085
  const stmt = db.prepare("SELECT COUNT(*) FROM handle_history WHERE handle = ?");
2916
3086
  stmt.bind([normalized]);
@@ -2930,67 +3100,6 @@ var init_SqliteWasmEngine = __esm({
2930
3100
  }
2931
3101
  return 0;
2932
3102
  }
2933
- // =========== Handle Management Operations ===========
2934
- async getAllHandles() {
2935
- const result = this.ensureDb().exec("SELECT handle, current_hash FROM handle_registry");
2936
- return (result[0]?.values || []).map(([handle, hash]) => ({
2937
- handle,
2938
- hash
2939
- }));
2940
- }
2941
- async removeHandle(handle) {
2942
- const db = this.ensureDb();
2943
- const normalized = validateHandle(handle);
2944
- db.run("DELETE FROM handle_history WHERE handle = ?", [normalized]);
2945
- db.run("DELETE FROM handle_registry WHERE handle = ?", [normalized]);
2946
- }
2947
- async renameHandle(oldHandle, newHandle) {
2948
- const db = this.ensureDb();
2949
- const normalizedOld = validateHandle(oldHandle);
2950
- const normalizedNew = validateHandle(newHandle);
2951
- if (normalizedOld === normalizedNew) return;
2952
- const checkOld = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
2953
- checkOld.bind([normalizedOld]);
2954
- const oldExists = checkOld.step();
2955
- checkOld.free();
2956
- if (!oldExists) throw new Error(`Handle '${oldHandle}' not found.`);
2957
- const checkNew = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
2958
- checkNew.bind([normalizedNew]);
2959
- const newExists = checkNew.step();
2960
- checkNew.free();
2961
- if (newExists) throw new Error(`Handle '${newHandle}' already exists.`);
2962
- db.run("UPDATE handle_registry SET handle = ? WHERE handle = ?", [normalizedNew, normalizedOld]);
2963
- db.run("UPDATE handle_history SET handle = ? WHERE handle = ?", [normalizedNew, normalizedOld]);
2964
- }
2965
- async deleteHistoryEntry(handle, previousHash) {
2966
- const db = this.ensureDb();
2967
- const normalized = validateHandle(handle);
2968
- const rOutStmt = db.prepare(
2969
- "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1"
2970
- );
2971
- rOutStmt.bind([normalized, previousHash]);
2972
- if (!rOutStmt.step()) {
2973
- rOutStmt.free();
2974
- return;
2975
- }
2976
- const rOutId = rOutStmt.get()[0];
2977
- rOutStmt.free();
2978
- const rInStmt = db.prepare(
2979
- "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1"
2980
- );
2981
- rInStmt.bind([normalized, rOutId]);
2982
- const hasRIn = rInStmt.step();
2983
- if (hasRIn) {
2984
- const rInPrevHash = rInStmt.get()[1];
2985
- const rInId = rInStmt.get()[0];
2986
- rInStmt.free();
2987
- db.run("UPDATE handle_history SET previous_hash = ? WHERE id = ?", [rInPrevHash, rOutId]);
2988
- db.run("DELETE FROM handle_history WHERE id = ?", [rInId]);
2989
- } else {
2990
- rInStmt.free();
2991
- db.run("DELETE FROM handle_history WHERE id = ?", [rOutId]);
2992
- }
2993
- }
2994
3103
  };
2995
3104
  }
2996
3105
  });
@@ -3008,24 +3117,25 @@ var init_SqliteWasmEngine2 = __esm({
3008
3117
  });
3009
3118
 
3010
3119
  // src/storage/engines/SqliteNodeEngine.ts
3011
- var DEFAULT_PAGE_SIZE, SqliteNodeEngine;
3120
+ var SqliteNodeEngine;
3012
3121
  var init_SqliteNodeEngine = __esm({
3013
3122
  "src/storage/engines/SqliteNodeEngine.ts"() {
3014
3123
  "use strict";
3015
3124
  init_MCard();
3016
- init_Handle();
3017
3125
  init_StorageAdapter();
3018
3126
  init_schema();
3019
- DEFAULT_PAGE_SIZE = 10;
3020
- SqliteNodeEngine = class _SqliteNodeEngine {
3127
+ init_AbstractSqlEngine();
3128
+ init_constants();
3129
+ SqliteNodeEngine = class _SqliteNodeEngine extends AbstractSqlEngine {
3021
3130
  db;
3022
3131
  dbPath;
3023
3132
  /**
3024
3133
  * Convert a database row into an MCard instance.
3025
3134
  */
3026
- static rowToCard(row) {
3027
- const content = row.content instanceof Buffer ? new Uint8Array(row.content) : new Uint8Array(row.content);
3028
- return MCard.fromData(content, row.hash, row.g_time);
3135
+ rowToCard(row) {
3136
+ const rawContent = row.content;
3137
+ const content = rawContent instanceof Buffer ? new Uint8Array(rawContent) : new Uint8Array(rawContent);
3138
+ return MCard.fromData(content, String(row.hash), String(row.g_time));
3029
3139
  }
3030
3140
  /**
3031
3141
  * Create a new SqliteNodeEngine via async factory.
@@ -3037,6 +3147,7 @@ var init_SqliteNodeEngine = __esm({
3037
3147
  return engine;
3038
3148
  }
3039
3149
  constructor(dbPath) {
3150
+ super();
3040
3151
  this.dbPath = dbPath;
3041
3152
  }
3042
3153
  async init() {
@@ -3055,10 +3166,26 @@ var init_SqliteNodeEngine = __esm({
3055
3166
  */
3056
3167
  setupDatabase() {
3057
3168
  this.db.pragma("journal_mode = WAL");
3058
- this.db.pragma("busy_timeout = 5000");
3169
+ this.db.pragma(`busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`);
3059
3170
  this.db.pragma("synchronous = NORMAL");
3060
3171
  initCoreSchemas(this.db);
3061
3172
  }
3173
+ // ======================================================================
3174
+ // AbstractSqlEngine primitives
3175
+ // ======================================================================
3176
+ async queryRows(sql, ...params) {
3177
+ const stmt = this.db.prepare(sql);
3178
+ return stmt.all(...params);
3179
+ }
3180
+ async execSql(sql, ...params) {
3181
+ if (params.length === 0) {
3182
+ this.db.exec(sql);
3183
+ return 0;
3184
+ }
3185
+ const stmt = this.db.prepare(sql);
3186
+ const result = stmt.run(...params);
3187
+ return result.changes;
3188
+ }
3062
3189
  /**
3063
3190
  * Close the database connection
3064
3191
  */
@@ -3071,7 +3198,7 @@ var init_SqliteNodeEngine = __esm({
3071
3198
  getDbPath() {
3072
3199
  return this.dbPath;
3073
3200
  }
3074
- // =========== Card Operations ===========
3201
+ // =========== Card Operations (overrides to use Buffer.from for better-sqlite3) ===========
3075
3202
  async add(card) {
3076
3203
  const stmt = this.db.prepare(
3077
3204
  "INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)"
@@ -3079,9 +3206,7 @@ var init_SqliteNodeEngine = __esm({
3079
3206
  stmt.run(card.hash, Buffer.from(card.content), card.g_time);
3080
3207
  return card.hash;
3081
3208
  }
3082
- /**
3083
- * Add a card synchronously (for performance-critical paths)
3084
- */
3209
+ // =========== Sync card operations (for performance-critical callers) ===========
3085
3210
  addSync(card) {
3086
3211
  const stmt = this.db.prepare(
3087
3212
  "INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)"
@@ -3089,35 +3214,17 @@ var init_SqliteNodeEngine = __esm({
3089
3214
  stmt.run(card.hash, Buffer.from(card.content), card.g_time);
3090
3215
  return card.hash;
3091
3216
  }
3092
- async get(hash) {
3093
- return this.getSync(hash);
3094
- }
3095
- /**
3096
- * Get a card synchronously
3097
- */
3098
3217
  getSync(hash) {
3099
3218
  const stmt = this.db.prepare("SELECT hash, content, g_time FROM card WHERE hash = ?");
3100
3219
  const row = stmt.get(hash);
3101
3220
  if (!row) return null;
3102
- return _SqliteNodeEngine.rowToCard(row);
3103
- }
3104
- async delete(hash) {
3105
- this.deleteSync(hash);
3221
+ return this.rowToCard(row);
3106
3222
  }
3107
- /**
3108
- * Delete a card synchronously
3109
- */
3110
3223
  deleteSync(hash) {
3111
3224
  const stmt = this.db.prepare("DELETE FROM card WHERE hash = ?");
3112
3225
  const result = stmt.run(hash);
3113
3226
  return result.changes > 0;
3114
3227
  }
3115
- async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
3116
- return this.getPageSync(pageNumber, pageSize);
3117
- }
3118
- /**
3119
- * Get a page of cards synchronously
3120
- */
3121
3228
  getPageSync(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
3122
3229
  if (pageNumber < 1) throw new Error("Page number must be >= 1");
3123
3230
  if (pageSize < 1) throw new Error("Page size must be >= 1");
@@ -3127,76 +3234,40 @@ var init_SqliteNodeEngine = __esm({
3127
3234
  "SELECT hash, content, g_time FROM card ORDER BY g_time DESC LIMIT ? OFFSET ?"
3128
3235
  );
3129
3236
  const rows = stmt.all(pageSize, offset);
3130
- const items = rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3237
+ const items = rows.map((row) => this.rowToCard(row));
3131
3238
  return createPage(items, totalItems, pageNumber, pageSize);
3132
3239
  }
3133
- async count() {
3134
- return this.countSync();
3135
- }
3136
- /**
3137
- * Count cards synchronously
3138
- */
3139
3240
  countSync() {
3140
- const stmt = this.db.prepare("SELECT COUNT(*) as count FROM card");
3241
+ const stmt = this.db.prepare("SELECT COUNT(*) as cnt FROM card");
3141
3242
  const row = stmt.get();
3142
- return row.count;
3243
+ return row.cnt;
3143
3244
  }
3144
- async searchByHash(hashPrefix) {
3145
- const rows = this.db.prepare("SELECT hash, content, g_time FROM card WHERE hash LIKE ?").all(`${hashPrefix}%`);
3146
- return rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3147
- }
3148
- async search(query, pageNumber, pageSize) {
3149
- const offset = (pageNumber - 1) * pageSize;
3150
- const searchPattern = `%${query}%`;
3151
- const countResult = this.db.prepare("SELECT COUNT(*) as count FROM card WHERE CAST(content AS TEXT) LIKE ?").get(searchPattern);
3152
- const totalItems = countResult.count;
3153
- const rows = this.db.prepare("SELECT hash, content, g_time FROM card WHERE CAST(content AS TEXT) LIKE ? LIMIT ? OFFSET ?").all(searchPattern, pageSize, offset);
3154
- const items = rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3155
- return createPage(items, totalItems, pageNumber, pageSize);
3156
- }
3157
- async getAll() {
3158
- const rows = this.db.prepare("SELECT hash, content, g_time FROM card").all();
3159
- return rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3160
- }
3161
- async clear() {
3162
- this.clearSync();
3163
- }
3164
- /**
3165
- * Clear all data synchronously
3166
- * Delete in FK-safe order: children first, then parents
3167
- */
3168
3245
  clearSync() {
3169
3246
  this.db.exec("DELETE FROM handle_history");
3170
3247
  this.db.exec("DELETE FROM handle_registry");
3171
3248
  this.db.exec("DELETE FROM card");
3172
3249
  }
3173
- // =========== Search Operations ===========
3174
- /**
3175
- * Search cards by string in content, hash, or g_time
3176
- */
3250
+ // =========== Additional sync search operations ===========
3177
3251
  searchByString(searchString, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
3178
3252
  if (pageNumber < 1) throw new Error("Page number must be >= 1");
3179
3253
  if (pageSize < 1) throw new Error("Page size must be >= 1");
3180
3254
  const pattern = `%${searchString}%`;
3181
3255
  const offset = (pageNumber - 1) * pageSize;
3182
3256
  const countStmt = this.db.prepare(`
3183
- SELECT COUNT(*) as count FROM card
3257
+ SELECT COUNT(*) as cnt FROM card
3184
3258
  WHERE hash LIKE ? OR g_time LIKE ? OR CAST(content AS TEXT) LIKE ?
3185
3259
  `);
3186
3260
  const countRow = countStmt.get(pattern, pattern, pattern);
3187
- const totalItems = countRow.count;
3261
+ const totalItems = countRow.cnt;
3188
3262
  const stmt = this.db.prepare(`
3189
- SELECT hash, content, g_time FROM card
3263
+ SELECT hash, content, g_time FROM card
3190
3264
  WHERE hash LIKE ? OR g_time LIKE ? OR CAST(content AS TEXT) LIKE ?
3191
3265
  ORDER BY g_time DESC LIMIT ? OFFSET ?
3192
3266
  `);
3193
3267
  const rows = stmt.all(pattern, pattern, pattern, pageSize, offset);
3194
- const items = rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3268
+ const items = rows.map((row) => this.rowToCard(row));
3195
3269
  return createPage(items, totalItems, pageNumber, pageSize);
3196
3270
  }
3197
- /**
3198
- * Search cards by content pattern (binary-safe)
3199
- */
3200
3271
  searchByContent(searchPattern, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
3201
3272
  if (pageNumber < 1) throw new Error("Page number must be >= 1");
3202
3273
  if (pageSize < 1) throw new Error("Page size must be >= 1");
@@ -3205,172 +3276,28 @@ var init_SqliteNodeEngine = __esm({
3205
3276
  }
3206
3277
  const searchBytes = typeof searchPattern === "string" ? Buffer.from(searchPattern, "utf-8") : Buffer.from(searchPattern);
3207
3278
  const offset = (pageNumber - 1) * pageSize;
3208
- const countStmt = this.db.prepare("SELECT COUNT(*) as count FROM card WHERE INSTR(content, ?) > 0");
3279
+ const countStmt = this.db.prepare("SELECT COUNT(*) as cnt FROM card WHERE INSTR(content, ?) > 0");
3209
3280
  const countRow = countStmt.get(searchBytes);
3210
- const totalItems = countRow.count;
3281
+ const totalItems = countRow.cnt;
3211
3282
  const stmt = this.db.prepare(`
3212
- SELECT hash, content, g_time FROM card
3283
+ SELECT hash, content, g_time FROM card
3213
3284
  WHERE INSTR(content, ?) > 0
3214
3285
  ORDER BY g_time DESC LIMIT ? OFFSET ?
3215
3286
  `);
3216
3287
  const rows = stmt.all(searchBytes, pageSize, offset);
3217
- const items = rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3288
+ const items = rows.map((row) => this.rowToCard(row));
3218
3289
  return createPage(items, totalItems, pageNumber, pageSize);
3219
3290
  }
3220
- /**
3221
- * Get all cards (single page with all items)
3222
- */
3223
3291
  getAllCards() {
3224
3292
  const stmt = this.db.prepare("SELECT hash, content, g_time FROM card ORDER BY g_time DESC");
3225
3293
  const rows = stmt.all();
3226
- const items = rows.map((row) => _SqliteNodeEngine.rowToCard(row));
3294
+ const items = rows.map((row) => this.rowToCard(row));
3227
3295
  return createPage(items, items.length, 1, items.length || 1);
3228
3296
  }
3229
- // =========== Handle Operations ===========
3230
- async registerHandle(handle, hash) {
3231
- this.registerHandleSync(handle, hash);
3232
- }
3233
- /**
3234
- * Register a handle synchronously
3235
- */
3236
- registerHandleSync(handle, hash) {
3237
- const normalized = validateHandle(handle);
3238
- const now = (/* @__PURE__ */ new Date()).toISOString();
3239
- const checkStmt = this.db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
3240
- const existing = checkStmt.get(normalized);
3241
- if (existing) {
3242
- throw new Error(`Handle '${handle}' already exists.`);
3243
- }
3244
- const stmt = this.db.prepare(
3245
- "INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)"
3246
- );
3247
- stmt.run(normalized, hash, now, now);
3248
- }
3249
- async resolveHandle(handle) {
3250
- return this.resolveHandleSync(handle);
3251
- }
3252
- /**
3253
- * Resolve a handle synchronously
3254
- */
3255
- resolveHandleSync(handle) {
3256
- const normalized = validateHandle(handle);
3257
- const stmt = this.db.prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
3258
- const row = stmt.get(normalized);
3259
- return row?.current_hash || null;
3260
- }
3261
- async getByHandle(handle) {
3262
- return this.getByHandleSync(handle);
3263
- }
3264
- /**
3265
- * Get card by handle synchronously
3266
- */
3267
- getByHandleSync(handle) {
3268
- const hash = this.resolveHandleSync(handle);
3269
- if (!hash) return null;
3270
- return this.getSync(hash);
3271
- }
3272
- async updateHandle(handle, newHash) {
3273
- return this.updateHandleSync(handle, newHash);
3274
- }
3275
- /**
3276
- * Update a handle synchronously
3277
- */
3278
- updateHandleSync(handle, newHash) {
3279
- const normalized = validateHandle(handle);
3280
- const now = (/* @__PURE__ */ new Date()).toISOString();
3281
- const getStmt = this.db.prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
3282
- const row = getStmt.get(normalized);
3283
- if (!row) {
3284
- throw new Error(`Handle '${handle}' not found.`);
3285
- }
3286
- const previousHash = row.current_hash;
3287
- const historyStmt = this.db.prepare(
3288
- "INSERT INTO handle_history (handle, previous_hash, changed_at) VALUES (?, ?, ?)"
3289
- );
3290
- historyStmt.run(normalized, previousHash, now);
3291
- const updateStmt = this.db.prepare(
3292
- "UPDATE handle_registry SET current_hash = ?, updated_at = ? WHERE handle = ?"
3293
- );
3294
- updateStmt.run(newHash, now, normalized);
3295
- return previousHash;
3296
- }
3297
- async getHandleHistory(handle) {
3298
- return this.getHandleHistorySync(handle);
3299
- }
3300
- /**
3301
- * Get handle history synchronously
3302
- */
3303
- getHandleHistorySync(handle) {
3304
- const normalized = validateHandle(handle);
3305
- const stmt = this.db.prepare(
3306
- "SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC"
3307
- );
3308
- const rows = stmt.all(normalized);
3309
- return rows.map((row) => ({
3310
- previousHash: row.previous_hash,
3311
- changedAt: row.changed_at
3312
- }));
3313
- }
3314
- async pruneHandleHistory(handle, options = {}) {
3315
- return this.pruneHandleHistorySync(handle, options);
3316
- }
3317
- /**
3318
- * Prune handle history synchronously
3319
- */
3320
- pruneHandleHistorySync(handle, options = {}) {
3321
- const normalized = validateHandle(handle);
3322
- let stmt;
3323
- if (options.deleteAll) {
3324
- stmt = this.db.prepare("DELETE FROM handle_history WHERE handle = ?");
3325
- const result = stmt.run(normalized);
3326
- return result.changes;
3327
- } else if (options.olderThan) {
3328
- stmt = this.db.prepare("DELETE FROM handle_history WHERE handle = ? AND changed_at < ?");
3329
- const result = stmt.run(normalized, options.olderThan);
3330
- return result.changes;
3331
- }
3332
- return 0;
3333
- }
3334
- // =========== Handle Management Operations ===========
3335
- async getAllHandles() {
3336
- return this.getAllHandlesSync();
3337
- }
3338
- getAllHandlesSync() {
3339
- const stmt = this.db.prepare("SELECT handle, current_hash FROM handle_registry");
3340
- const rows = stmt.all();
3341
- return rows.map((row) => ({ handle: row.handle, hash: row.current_hash }));
3342
- }
3343
- async removeHandle(handle) {
3344
- this.removeHandleSync(handle);
3345
- }
3346
- removeHandleSync(handle) {
3347
- const normalized = validateHandle(handle);
3348
- this.db.prepare("DELETE FROM handle_history WHERE handle = ?").run(normalized);
3349
- this.db.prepare("DELETE FROM handle_registry WHERE handle = ?").run(normalized);
3350
- }
3351
- async renameHandle(oldHandle, newHandle) {
3352
- this.renameHandleSync(oldHandle, newHandle);
3353
- }
3354
- renameHandleSync(oldHandle, newHandle) {
3355
- const normalizedOld = validateHandle(oldHandle);
3356
- const normalizedNew = validateHandle(newHandle);
3357
- if (normalizedOld === normalizedNew) return;
3358
- const existsOld = this.db.prepare("SELECT handle FROM handle_registry WHERE handle = ?").get(normalizedOld);
3359
- if (!existsOld) throw new Error(`Handle '${oldHandle}' not found.`);
3360
- const existsNew = this.db.prepare("SELECT handle FROM handle_registry WHERE handle = ?").get(normalizedNew);
3361
- if (existsNew) throw new Error(`Handle '${newHandle}' already exists.`);
3362
- const performRename = this.db.transaction(() => {
3363
- this.db.pragma("defer_foreign_keys = ON");
3364
- this.db.prepare("UPDATE handle_registry SET handle = ? WHERE handle = ?").run(normalizedNew, normalizedOld);
3365
- this.db.prepare("UPDATE handle_history SET handle = ? WHERE handle = ?").run(normalizedNew, normalizedOld);
3366
- });
3367
- performRename();
3368
- }
3369
- async deleteHistoryEntry(handle, previousHash) {
3370
- this.deleteHistoryEntrySync(handle, previousHash);
3371
- }
3297
+ // =========== Unique sync method (not in AbstractSqlEngine) ===========
3372
3298
  deleteHistoryEntrySync(handle, previousHash) {
3373
- const normalized = validateHandle(handle);
3299
+ const { validateHandle: validateHandle2 } = (init_Handle(), __toCommonJS(Handle_exports));
3300
+ const normalized = validateHandle2(handle);
3374
3301
  const performStitch = this.db.transaction(() => {
3375
3302
  const rOut = this.db.prepare(
3376
3303
  "SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1"
@@ -3621,7 +3548,7 @@ async function isProblematicFile(filePath) {
3621
3548
  const stats = await fs.stat(filePath);
3622
3549
  if (stats.size === 0) return false;
3623
3550
  if (path.basename(filePath).startsWith(".")) return true;
3624
- if (stats.size > MAX_FILE_SIZE) return true;
3551
+ if (stats.size > MAX_FILE_SIZE2) return true;
3625
3552
  const ext = path.extname(filePath);
3626
3553
  const isKnownType = ContentTypeInterpreter.isKnownLongLineExtension(ext);
3627
3554
  if (isKnownType && stats.size > 1024 * 1024) return true;
@@ -3642,7 +3569,7 @@ async function isProblematicFile(filePath) {
3642
3569
  }
3643
3570
  async function readFileSafely(filePath, options = {}) {
3644
3571
  const stats = await fs.stat(filePath);
3645
- if (stats.size > MAX_FILE_SIZE) throw new Error(`File too large: ${stats.size}`);
3572
+ if (stats.size > MAX_FILE_SIZE2) throw new Error(`File too large: ${stats.size}`);
3646
3573
  const controller = new AbortController();
3647
3574
  const timeout = setTimeout(() => controller.abort(), READ_TIMEOUT_MS);
3648
3575
  try {
@@ -3707,7 +3634,7 @@ async function processFileContent(filePath, options = {}) {
3707
3634
  size: rawContent.length
3708
3635
  };
3709
3636
  }
3710
- var crypto2, fs, path, MAX_FILE_SIZE, READ_TIMEOUT_MS;
3637
+ var crypto2, fs, path, MAX_FILE_SIZE2, READ_TIMEOUT_MS;
3711
3638
  var init_FileIO = __esm({
3712
3639
  "src/FileIO.ts"() {
3713
3640
  "use strict";
@@ -3715,7 +3642,7 @@ var init_FileIO = __esm({
3715
3642
  fs = __toESM(require("fs/promises"), 1);
3716
3643
  path = __toESM(require("path"), 1);
3717
3644
  init_ContentTypeInterpreter();
3718
- MAX_FILE_SIZE = 50 * 1024 * 1024;
3645
+ MAX_FILE_SIZE2 = 50 * 1024 * 1024;
3719
3646
  READ_TIMEOUT_MS = 5e3;
3720
3647
  }
3721
3648
  });
@@ -3729,7 +3656,7 @@ __export(Loader_exports, {
3729
3656
  async function processAndStoreFile(filePath, collection, options = {}) {
3730
3657
  const {
3731
3658
  allowProblematic = false,
3732
- maxBytesOnProblem = DEFAULT_MAX_PROBLEM_BYTES,
3659
+ maxBytesOnProblem = DEFAULT_MAX_PROBLEM_BYTES2,
3733
3660
  metadataOnly = false,
3734
3661
  rootPath
3735
3662
  } = options;
@@ -3843,7 +3770,7 @@ async function loadFileToCollection(targetPath, collection, options = {}) {
3843
3770
  const {
3844
3771
  recursive = false,
3845
3772
  includeProblematic = false,
3846
- maxBytesOnProblem = DEFAULT_MAX_PROBLEM_BYTES,
3773
+ maxBytesOnProblem = DEFAULT_MAX_PROBLEM_BYTES2,
3847
3774
  metadataOnly = false
3848
3775
  } = options;
3849
3776
  const resolvedPath = path2.resolve(targetPath);
@@ -3889,7 +3816,7 @@ async function loadFileToCollection(targetPath, collection, options = {}) {
3889
3816
  }
3890
3817
  return { metrics, results };
3891
3818
  }
3892
- var fs2, path2, DEFAULT_MAX_PROBLEM_BYTES, WRAP_WIDTH_KNOWN, WRAP_WIDTH_DEFAULT;
3819
+ var fs2, path2, DEFAULT_MAX_PROBLEM_BYTES2, WRAP_WIDTH_KNOWN, WRAP_WIDTH_DEFAULT;
3893
3820
  var init_Loader = __esm({
3894
3821
  "src/Loader.ts"() {
3895
3822
  "use strict";
@@ -3898,7 +3825,7 @@ var init_Loader = __esm({
3898
3825
  init_MCard();
3899
3826
  init_FileIO();
3900
3827
  init_ContentTypeInterpreter();
3901
- DEFAULT_MAX_PROBLEM_BYTES = 2 * 1024 * 1024;
3828
+ DEFAULT_MAX_PROBLEM_BYTES2 = 2 * 1024 * 1024;
3902
3829
  WRAP_WIDTH_KNOWN = 1e3;
3903
3830
  WRAP_WIDTH_DEFAULT = 80;
3904
3831
  }
@@ -6007,8 +5934,8 @@ var init_lean = __esm({
6007
5934
  async execute(code, context, config) {
6008
5935
  const contextStr = typeof context === "string" ? context : JSON.stringify(context);
6009
5936
  const sourcePath = await getCachedLeanSource(code);
6010
- const os = await import("os");
6011
- const elanPath = path8.join(os.homedir(), ".elan", "bin", "elan");
5937
+ const os2 = await import("os");
5938
+ const elanPath = path8.join(os2.homedir(), ".elan", "bin", "elan");
6012
5939
  let stdout;
6013
5940
  if (fs6.existsSync(elanPath)) {
6014
5941
  const result = await execFile2(elanPath, [
@@ -6031,12 +5958,13 @@ var init_lean = __esm({
6031
5958
  });
6032
5959
 
6033
5960
  // src/ptr/node/runtimes/loader.ts
6034
- var fs7, path9, LoaderRuntime, CollectionLoaderRuntime;
5961
+ var fs7, path9, os, LoaderRuntime, CollectionLoaderRuntime;
6035
5962
  var init_loader = __esm({
6036
5963
  "src/ptr/node/runtimes/loader.ts"() {
6037
5964
  "use strict";
6038
5965
  fs7 = __toESM(require("fs"), 1);
6039
5966
  path9 = __toESM(require("path"), 1);
5967
+ os = __toESM(require("os"), 1);
6040
5968
  init_MCard();
6041
5969
  init_SqliteNodeEngine2();
6042
5970
  init_CardCollection();
@@ -6111,11 +6039,12 @@ var init_loader = __esm({
6111
6039
  */
6112
6040
  async execute(_code, context, config) {
6113
6041
  const ctx = context;
6042
+ const defaultDbPath = path9.join(os.tmpdir(), "loader_clm.db");
6114
6043
  const loaderParams = extractLoaderParams(ctx, {
6115
6044
  sourceDir: "chapters/chapter_04_load_dir/test_data",
6116
- dbPath: "data/loader_clm.db"
6045
+ dbPath: defaultDbPath
6117
6046
  });
6118
- const { sourceDir, recursive, dbPath = "data/loader_clm.db" } = loaderParams;
6047
+ const { sourceDir, recursive, dbPath = defaultDbPath } = loaderParams;
6119
6048
  const projectRoot = findProjectRoot();
6120
6049
  const sourcePath = path9.isAbsolute(sourceDir) ? sourceDir : path9.join(projectRoot, sourceDir);
6121
6050
  if (!fs7.existsSync(sourcePath)) {
@@ -8770,7 +8699,7 @@ init_GTime();
8770
8699
  init_Handle();
8771
8700
  init_CardCollection();
8772
8701
  init_ContentTypeInterpreter();
8773
- init_constants();
8702
+ init_constants2();
8774
8703
  init_IndexedDBEngine2();
8775
8704
  init_SqliteWasmEngine2();
8776
8705
  init_SqliteNodeEngine2();