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.
- package/dist/AbstractSqlEngine-BSfp8S_Y.d.cts +451 -0
- package/dist/AbstractSqlEngine-BSfp8S_Y.d.ts +451 -0
- package/dist/CardCollection-MXTUJV4J.js +9 -0
- package/dist/EventProducer-AWD6YMZR.js +47 -0
- package/dist/Handle-3N4QOA3U.js +13 -0
- package/dist/IndexedDBEngine-2G5KCISA.js +11 -0
- package/dist/LLMRuntime-LBWUJ7ON.js +16 -0
- package/dist/LambdaRuntime-B6D6IQKZ.js +18 -0
- package/dist/Loader-3LSJXJQG.js +11 -0
- package/dist/MCard-H56VOJLR.js +8 -0
- package/dist/NetworkRuntime-IAFHPQSX.js +1570 -0
- package/dist/OllamaProvider-QPX2JXL2.js +8 -0
- package/dist/chunk-2R4ESMZB.js +110 -0
- package/dist/chunk-3EIBJPNF.js +17 -0
- package/dist/chunk-3LPY36OG.js +355 -0
- package/dist/chunk-3MMMJ7NH.js +1068 -0
- package/dist/chunk-42VF42KH.js +273 -0
- package/dist/chunk-4PDYHPR6.js +297 -0
- package/dist/chunk-ADV52544.js +95 -0
- package/dist/chunk-FIE4LAJG.js +215 -0
- package/dist/chunk-PNKVD2UK.js +26 -0
- package/dist/chunk-RSTKX7WM.js +907 -0
- package/dist/chunk-VXV35I5J.js +2315 -0
- package/dist/index.browser.cjs +375 -276
- package/dist/index.browser.d.cts +4 -4
- package/dist/index.browser.d.ts +4 -4
- package/dist/index.browser.js +18 -13
- package/dist/index.cjs +382 -453
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +26 -21
- package/dist/storage/SqliteNodeEngine.cjs +395 -270
- package/dist/storage/SqliteNodeEngine.d.cts +9 -94
- package/dist/storage/SqliteNodeEngine.d.ts +9 -94
- package/dist/storage/SqliteNodeEngine.js +6 -5
- package/dist/storage/SqliteWasmEngine.cjs +382 -252
- package/dist/storage/SqliteWasmEngine.d.cts +8 -29
- package/dist/storage/SqliteWasmEngine.d.ts +8 -29
- package/dist/storage/SqliteWasmEngine.js +6 -5
- package/package.json +1 -1
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +30,90 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// src/model/Handle.ts
|
|
34
|
+
var Handle_exports = {};
|
|
35
|
+
__export(Handle_exports, {
|
|
36
|
+
ContentHandle: () => ContentHandle,
|
|
37
|
+
HandleValidationError: () => HandleValidationError,
|
|
38
|
+
validateHandle: () => validateHandle
|
|
39
|
+
});
|
|
40
|
+
function isValidStartChar(char) {
|
|
41
|
+
return /^\p{L}$/u.test(char);
|
|
42
|
+
}
|
|
43
|
+
function isValidBodyChar(char) {
|
|
44
|
+
return /^[\p{L}\p{N}_./ -]$/u.test(char);
|
|
45
|
+
}
|
|
46
|
+
function validateHandle(handle) {
|
|
47
|
+
if (!handle) {
|
|
48
|
+
throw new HandleValidationError("Handle cannot be empty.");
|
|
49
|
+
}
|
|
50
|
+
const normalized = handle.trim().normalize("NFC");
|
|
51
|
+
if (normalized.length === 0) {
|
|
52
|
+
throw new HandleValidationError("Handle cannot be empty after normalization.");
|
|
53
|
+
}
|
|
54
|
+
if (normalized.length > MAX_HANDLE_LENGTH) {
|
|
55
|
+
throw new HandleValidationError(
|
|
56
|
+
`Handle '${handle}' is too long (${normalized.length} chars). Maximum is ${MAX_HANDLE_LENGTH}.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (!isValidStartChar(normalized[0])) {
|
|
60
|
+
throw new HandleValidationError(
|
|
61
|
+
`Invalid handle '${handle}'. Must start with a letter (any language).`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
for (let i = 1; i < normalized.length; i++) {
|
|
65
|
+
if (!isValidBodyChar(normalized[i])) {
|
|
66
|
+
throw new HandleValidationError(
|
|
67
|
+
`Invalid character '${normalized[i]}' at position ${i} in handle '${handle}'.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
var MAX_HANDLE_LENGTH, HandleValidationError, ContentHandle;
|
|
74
|
+
var init_Handle = __esm({
|
|
75
|
+
"src/model/Handle.ts"() {
|
|
76
|
+
"use strict";
|
|
77
|
+
MAX_HANDLE_LENGTH = 255;
|
|
78
|
+
HandleValidationError = class extends Error {
|
|
79
|
+
constructor(message) {
|
|
80
|
+
super(message);
|
|
81
|
+
this.name = "HandleValidationError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
ContentHandle = class {
|
|
85
|
+
handle;
|
|
86
|
+
currentHash;
|
|
87
|
+
createdAt;
|
|
88
|
+
updatedAt;
|
|
89
|
+
constructor(handle, currentHash, createdAt, updatedAt) {
|
|
90
|
+
this.handle = validateHandle(handle);
|
|
91
|
+
this.currentHash = currentHash;
|
|
92
|
+
this.createdAt = createdAt ?? /* @__PURE__ */ new Date();
|
|
93
|
+
this.updatedAt = updatedAt ?? this.createdAt;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Update handle to point to new hash
|
|
97
|
+
* @returns Previous hash for history tracking
|
|
98
|
+
*/
|
|
99
|
+
update(newHash) {
|
|
100
|
+
const previousHash = this.currentHash;
|
|
101
|
+
this.currentHash = newHash;
|
|
102
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
103
|
+
return previousHash;
|
|
104
|
+
}
|
|
105
|
+
toObject() {
|
|
106
|
+
return {
|
|
107
|
+
handle: this.handle,
|
|
108
|
+
currentHash: this.currentHash,
|
|
109
|
+
createdAt: this.createdAt.toISOString(),
|
|
110
|
+
updatedAt: this.updatedAt.toISOString()
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
30
117
|
// src/storage/SqliteWasmEngine.ts
|
|
31
118
|
var SqliteWasmEngine_exports = {};
|
|
32
119
|
__export(SqliteWasmEngine_exports, {
|
|
@@ -905,6 +992,16 @@ var mime_extensions_default = {
|
|
|
905
992
|
}
|
|
906
993
|
};
|
|
907
994
|
|
|
995
|
+
// src/config/constants.ts
|
|
996
|
+
var DEFAULT_PAGE_SIZE = 10;
|
|
997
|
+
var DETECTION_SAMPLE_CAP = 8192;
|
|
998
|
+
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
999
|
+
var KNOWN_TYPE_SIZE_LIMIT = 1024 * 1024;
|
|
1000
|
+
var BINARY_CHECK_SAMPLE_SIZE = 32 * 1024;
|
|
1001
|
+
var CONTENT_DETECTION_SAMPLE_SIZE = 1024 * 1024;
|
|
1002
|
+
var DEFAULT_MAX_PROBLEM_BYTES = 2 * 1024 * 1024;
|
|
1003
|
+
var DEFAULT_SQLJS_WASM_URL = "https://sql.js.org/dist/";
|
|
1004
|
+
|
|
908
1005
|
// src/model/strategies/DetectionStrategy.ts
|
|
909
1006
|
function loadSharedMimeData() {
|
|
910
1007
|
return {
|
|
@@ -937,7 +1034,7 @@ var DefaultDetectionStrategy = class _DefaultDetectionStrategy {
|
|
|
937
1034
|
return this.extensionsRegistry[mimeType] || "";
|
|
938
1035
|
}
|
|
939
1036
|
detect(content, fileExtension) {
|
|
940
|
-
const contentSample = typeof content === "string" ? content.slice(0,
|
|
1037
|
+
const contentSample = typeof content === "string" ? content.slice(0, DETECTION_SAMPLE_CAP) : content.slice(0, DETECTION_SAMPLE_CAP);
|
|
941
1038
|
const textSample = this.getTextSample(contentSample);
|
|
942
1039
|
const lines = textSample.split("\n").slice(0, 20);
|
|
943
1040
|
const firstLine = lines[0] || "";
|
|
@@ -971,9 +1068,9 @@ var DefaultDetectionStrategy = class _DefaultDetectionStrategy {
|
|
|
971
1068
|
}
|
|
972
1069
|
getTextSample(content) {
|
|
973
1070
|
if (typeof content === "string") {
|
|
974
|
-
return content.slice(0,
|
|
1071
|
+
return content.slice(0, DETECTION_SAMPLE_CAP);
|
|
975
1072
|
}
|
|
976
|
-
return new TextDecoder("utf-8", { fatal: false }).decode(content.slice(0,
|
|
1073
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(content.slice(0, DETECTION_SAMPLE_CAP));
|
|
977
1074
|
}
|
|
978
1075
|
getExtension(mimeType) {
|
|
979
1076
|
return _DefaultDetectionStrategy.getExtension(mimeType);
|
|
@@ -1191,62 +1288,6 @@ var MCard = class _MCard {
|
|
|
1191
1288
|
}
|
|
1192
1289
|
};
|
|
1193
1290
|
|
|
1194
|
-
// src/model/Handle.ts
|
|
1195
|
-
var MAX_HANDLE_LENGTH = 255;
|
|
1196
|
-
function isValidStartChar(char) {
|
|
1197
|
-
return /^\p{L}$/u.test(char);
|
|
1198
|
-
}
|
|
1199
|
-
function isValidBodyChar(char) {
|
|
1200
|
-
return /^[\p{L}\p{N}_./ -]$/u.test(char);
|
|
1201
|
-
}
|
|
1202
|
-
var HandleValidationError = class extends Error {
|
|
1203
|
-
constructor(message) {
|
|
1204
|
-
super(message);
|
|
1205
|
-
this.name = "HandleValidationError";
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
function validateHandle(handle) {
|
|
1209
|
-
if (!handle) {
|
|
1210
|
-
throw new HandleValidationError("Handle cannot be empty.");
|
|
1211
|
-
}
|
|
1212
|
-
const normalized = handle.trim().normalize("NFC").toLowerCase();
|
|
1213
|
-
if (normalized.length === 0) {
|
|
1214
|
-
throw new HandleValidationError("Handle cannot be empty after normalization.");
|
|
1215
|
-
}
|
|
1216
|
-
if (normalized.length > MAX_HANDLE_LENGTH) {
|
|
1217
|
-
throw new HandleValidationError(
|
|
1218
|
-
`Handle '${handle}' is too long (${normalized.length} chars). Maximum is ${MAX_HANDLE_LENGTH}.`
|
|
1219
|
-
);
|
|
1220
|
-
}
|
|
1221
|
-
if (!isValidStartChar(normalized[0])) {
|
|
1222
|
-
throw new HandleValidationError(
|
|
1223
|
-
`Invalid handle '${handle}'. Must start with a letter (any language).`
|
|
1224
|
-
);
|
|
1225
|
-
}
|
|
1226
|
-
for (let i = 1; i < normalized.length; i++) {
|
|
1227
|
-
if (!isValidBodyChar(normalized[i])) {
|
|
1228
|
-
throw new HandleValidationError(
|
|
1229
|
-
`Invalid character '${normalized[i]}' at position ${i} in handle '${handle}'.`
|
|
1230
|
-
);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return normalized;
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
// src/storage/StorageAdapter.ts
|
|
1237
|
-
function createPage(items, totalItems, pageNumber, pageSize) {
|
|
1238
|
-
const totalPages = Math.ceil(totalItems / pageSize) || 1;
|
|
1239
|
-
return {
|
|
1240
|
-
items,
|
|
1241
|
-
totalItems,
|
|
1242
|
-
pageNumber,
|
|
1243
|
-
pageSize,
|
|
1244
|
-
totalPages,
|
|
1245
|
-
hasNext: totalItems > (pageNumber - 1) * pageSize + items.length,
|
|
1246
|
-
hasPrevious: pageNumber > 1
|
|
1247
|
-
};
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
1291
|
// src/storage/schema_constants.ts
|
|
1251
1292
|
var MCARD_SCHEMA_SQL = `-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
1252
1293
|
-- MCard Unified Database Schema (Monadic Core)
|
|
@@ -1880,159 +1921,145 @@ var CORE_SCHEMAS = {
|
|
|
1880
1921
|
handleIndex: HANDLE_INDEX_SCHEMA
|
|
1881
1922
|
};
|
|
1882
1923
|
|
|
1883
|
-
// src/storage/engines/
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
this.db.run(CORE_SCHEMAS.handleIndex);
|
|
1903
|
-
}
|
|
1904
|
-
ensureDb() {
|
|
1905
|
-
if (!this.db) throw new Error("Database not initialized. Call init() first.");
|
|
1906
|
-
return this.db;
|
|
1907
|
-
}
|
|
1924
|
+
// src/storage/engines/AbstractSqlEngine.ts
|
|
1925
|
+
init_Handle();
|
|
1926
|
+
|
|
1927
|
+
// src/storage/StorageAdapter.ts
|
|
1928
|
+
function createPage(items, totalItems, pageNumber, pageSize) {
|
|
1929
|
+
const totalPages = Math.ceil(totalItems / pageSize) || 1;
|
|
1930
|
+
return {
|
|
1931
|
+
items,
|
|
1932
|
+
totalItems,
|
|
1933
|
+
pageNumber,
|
|
1934
|
+
pageSize,
|
|
1935
|
+
totalPages,
|
|
1936
|
+
hasNext: totalItems > (pageNumber - 1) * pageSize + items.length,
|
|
1937
|
+
hasPrevious: pageNumber > 1
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// src/storage/engines/AbstractSqlEngine.ts
|
|
1942
|
+
var AbstractSqlEngine = class {
|
|
1908
1943
|
/**
|
|
1909
|
-
*
|
|
1944
|
+
* SQL expression for casting content to text in search queries.
|
|
1945
|
+
* Override in subclasses: SQLite uses 'TEXT', DuckDB uses 'VARCHAR'.
|
|
1910
1946
|
*/
|
|
1911
|
-
|
|
1912
|
-
return
|
|
1947
|
+
get castContentAs() {
|
|
1948
|
+
return "TEXT";
|
|
1913
1949
|
}
|
|
1950
|
+
// ======================================================================
|
|
1951
|
+
// Concrete: Card Operations (shared across all SQL engines)
|
|
1952
|
+
// ======================================================================
|
|
1914
1953
|
/**
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
1917
|
-
* Example:
|
|
1918
|
-
* const vecStore = createWasmVectorStore(engine.getRawDb());
|
|
1954
|
+
* Add a card. Default uses INSERT OR REPLACE (SQLite).
|
|
1955
|
+
* DuckDB overrides with DELETE + INSERT.
|
|
1919
1956
|
*/
|
|
1920
|
-
getRawDb() {
|
|
1921
|
-
return this.ensureDb();
|
|
1922
|
-
}
|
|
1923
|
-
// =========== Card Operations ===========
|
|
1924
1957
|
async add(card) {
|
|
1925
|
-
const
|
|
1926
|
-
|
|
1958
|
+
const contentBytes = card.content instanceof Uint8Array ? card.content : new TextEncoder().encode(String(card.content));
|
|
1959
|
+
await this.execSql(
|
|
1927
1960
|
"INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)",
|
|
1928
|
-
|
|
1961
|
+
card.hash,
|
|
1962
|
+
contentBytes,
|
|
1963
|
+
card.g_time
|
|
1929
1964
|
);
|
|
1930
1965
|
return card.hash;
|
|
1931
1966
|
}
|
|
1932
1967
|
async get(hash) {
|
|
1933
|
-
const
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}
|
|
1940
|
-
const row = stmt.get();
|
|
1941
|
-
stmt.free();
|
|
1942
|
-
return MCard.fromData(new Uint8Array(row[1]), row[0], row[2]);
|
|
1968
|
+
const rows = await this.queryRows(
|
|
1969
|
+
"SELECT hash, content, g_time FROM card WHERE hash = ?",
|
|
1970
|
+
hash
|
|
1971
|
+
);
|
|
1972
|
+
if (rows.length === 0) return null;
|
|
1973
|
+
return this.rowToCard(rows[0]);
|
|
1943
1974
|
}
|
|
1944
1975
|
async delete(hash) {
|
|
1945
|
-
this.
|
|
1976
|
+
await this.execSql("DELETE FROM card WHERE hash = ?", hash);
|
|
1946
1977
|
}
|
|
1947
|
-
async getPage(pageNumber, pageSize) {
|
|
1948
|
-
|
|
1978
|
+
async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
1979
|
+
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
1980
|
+
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
1949
1981
|
const totalItems = await this.count();
|
|
1950
1982
|
const offset = (pageNumber - 1) * pageSize;
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
}
|
|
1958
|
-
stmt.free();
|
|
1983
|
+
const rows = await this.queryRows(
|
|
1984
|
+
"SELECT hash, content, g_time FROM card ORDER BY g_time DESC LIMIT ? OFFSET ?",
|
|
1985
|
+
pageSize,
|
|
1986
|
+
offset
|
|
1987
|
+
);
|
|
1988
|
+
const items = rows.map((r) => this.rowToCard(r));
|
|
1959
1989
|
return createPage(items, totalItems, pageNumber, pageSize);
|
|
1960
1990
|
}
|
|
1961
1991
|
async count() {
|
|
1962
|
-
const
|
|
1963
|
-
return
|
|
1992
|
+
const rows = await this.queryRows("SELECT COUNT(*) as cnt FROM card");
|
|
1993
|
+
return Number(rows[0]?.cnt ?? 0);
|
|
1964
1994
|
}
|
|
1965
1995
|
async searchByHash(hashPrefix) {
|
|
1966
|
-
const
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const row = stmt.get();
|
|
1972
|
-
items.push(MCard.fromData(new Uint8Array(row[1]), row[0], row[2]));
|
|
1973
|
-
}
|
|
1974
|
-
stmt.free();
|
|
1975
|
-
return items;
|
|
1996
|
+
const rows = await this.queryRows(
|
|
1997
|
+
"SELECT hash, content, g_time FROM card WHERE hash LIKE ?",
|
|
1998
|
+
`${hashPrefix}%`
|
|
1999
|
+
);
|
|
2000
|
+
return rows.map((r) => this.rowToCard(r));
|
|
1976
2001
|
}
|
|
1977
|
-
async search(
|
|
1978
|
-
|
|
2002
|
+
async search(queryStr, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
2003
|
+
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
2004
|
+
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
1979
2005
|
const offset = (pageNumber - 1) * pageSize;
|
|
1980
|
-
const pattern = `%${
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2006
|
+
const pattern = `%${queryStr}%`;
|
|
2007
|
+
const cast = this.castContentAs;
|
|
2008
|
+
const countRows = await this.queryRows(
|
|
2009
|
+
`SELECT COUNT(*) as cnt FROM card WHERE CAST(content AS ${cast}) LIKE ?`,
|
|
2010
|
+
pattern
|
|
2011
|
+
);
|
|
2012
|
+
const totalItems = Number(countRows[0]?.cnt ?? 0);
|
|
2013
|
+
const rows = await this.queryRows(
|
|
2014
|
+
`SELECT hash, content, g_time FROM card WHERE CAST(content AS ${cast}) LIKE ? ORDER BY g_time DESC LIMIT ? OFFSET ?`,
|
|
2015
|
+
pattern,
|
|
2016
|
+
pageSize,
|
|
2017
|
+
offset
|
|
2018
|
+
);
|
|
2019
|
+
const items = rows.map((r) => this.rowToCard(r));
|
|
1994
2020
|
return createPage(items, totalItems, pageNumber, pageSize);
|
|
1995
2021
|
}
|
|
1996
2022
|
async getAll() {
|
|
1997
|
-
const
|
|
1998
|
-
return (
|
|
1999
|
-
([h, content, g_time]) => MCard.fromData(new Uint8Array(content), h, g_time)
|
|
2000
|
-
);
|
|
2023
|
+
const rows = await this.queryRows("SELECT hash, content, g_time FROM card ORDER BY g_time DESC");
|
|
2024
|
+
return rows.map((r) => this.rowToCard(r));
|
|
2001
2025
|
}
|
|
2026
|
+
// ======================================================================
|
|
2027
|
+
// Concrete: clear (FK-safe delete order)
|
|
2028
|
+
// ======================================================================
|
|
2002
2029
|
async clear() {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
db.run("DELETE FROM handle_history");
|
|
2030
|
+
await this.execSql("DELETE FROM handle_history");
|
|
2031
|
+
await this.execSql("DELETE FROM handle_registry");
|
|
2032
|
+
await this.execSql("DELETE FROM card");
|
|
2007
2033
|
}
|
|
2008
|
-
//
|
|
2034
|
+
// ======================================================================
|
|
2035
|
+
// Concrete: Handle Operations
|
|
2036
|
+
// ======================================================================
|
|
2009
2037
|
async registerHandle(handle, hash) {
|
|
2010
|
-
const db = this.ensureDb();
|
|
2011
2038
|
const normalized = validateHandle(handle);
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2039
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2040
|
+
const existing = await this.queryRows(
|
|
2041
|
+
"SELECT handle FROM handle_registry WHERE handle = ?",
|
|
2042
|
+
normalized
|
|
2043
|
+
);
|
|
2044
|
+
if (existing.length > 0) {
|
|
2017
2045
|
throw new Error(`Handle '${handle}' already exists.`);
|
|
2018
2046
|
}
|
|
2019
|
-
|
|
2020
|
-
db.run(
|
|
2047
|
+
await this.execSql(
|
|
2021
2048
|
"INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
|
|
2022
|
-
|
|
2049
|
+
normalized,
|
|
2050
|
+
hash,
|
|
2051
|
+
now,
|
|
2052
|
+
now
|
|
2023
2053
|
);
|
|
2024
2054
|
}
|
|
2025
2055
|
async resolveHandle(handle) {
|
|
2026
2056
|
const normalized = validateHandle(handle);
|
|
2027
|
-
const
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
const hash = stmt.get()[0];
|
|
2034
|
-
stmt.free();
|
|
2035
|
-
return hash || null;
|
|
2057
|
+
const rows = await this.queryRows(
|
|
2058
|
+
"SELECT current_hash FROM handle_registry WHERE handle = ?",
|
|
2059
|
+
normalized
|
|
2060
|
+
);
|
|
2061
|
+
if (rows.length === 0) return null;
|
|
2062
|
+
return String(rows[0].current_hash);
|
|
2036
2063
|
}
|
|
2037
2064
|
async getByHandle(handle) {
|
|
2038
2065
|
const hash = await this.resolveHandle(handle);
|
|
@@ -2040,42 +2067,206 @@ var SqliteWasmEngine = class {
|
|
|
2040
2067
|
return this.get(hash);
|
|
2041
2068
|
}
|
|
2042
2069
|
async updateHandle(handle, newHash) {
|
|
2043
|
-
const db = this.ensureDb();
|
|
2044
2070
|
const normalized = validateHandle(handle);
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2071
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2072
|
+
const rows = await this.queryRows(
|
|
2073
|
+
"SELECT current_hash FROM handle_registry WHERE handle = ?",
|
|
2074
|
+
normalized
|
|
2075
|
+
);
|
|
2076
|
+
if (rows.length === 0) {
|
|
2049
2077
|
throw new Error(`Handle '${handle}' not found.`);
|
|
2050
2078
|
}
|
|
2051
|
-
const previousHash =
|
|
2052
|
-
|
|
2053
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2054
|
-
db.run(
|
|
2079
|
+
const previousHash = String(rows[0].current_hash);
|
|
2080
|
+
await this.execSql(
|
|
2055
2081
|
"INSERT INTO handle_history (handle, previous_hash, changed_at) VALUES (?, ?, ?)",
|
|
2056
|
-
|
|
2082
|
+
normalized,
|
|
2083
|
+
previousHash,
|
|
2084
|
+
now
|
|
2057
2085
|
);
|
|
2058
|
-
|
|
2086
|
+
await this.execSql(
|
|
2059
2087
|
"UPDATE handle_registry SET current_hash = ?, updated_at = ? WHERE handle = ?",
|
|
2060
|
-
|
|
2088
|
+
newHash,
|
|
2089
|
+
now,
|
|
2090
|
+
normalized
|
|
2061
2091
|
);
|
|
2062
2092
|
return previousHash;
|
|
2063
2093
|
}
|
|
2064
2094
|
async getHandleHistory(handle) {
|
|
2065
2095
|
const normalized = validateHandle(handle);
|
|
2066
|
-
const
|
|
2067
|
-
|
|
2096
|
+
const rows = await this.queryRows(
|
|
2097
|
+
"SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC",
|
|
2098
|
+
normalized
|
|
2099
|
+
);
|
|
2100
|
+
return rows.map((r) => ({
|
|
2101
|
+
previousHash: String(r.previous_hash),
|
|
2102
|
+
changedAt: String(r.changed_at)
|
|
2103
|
+
}));
|
|
2104
|
+
}
|
|
2105
|
+
async pruneHandleHistory(handle, options = {}) {
|
|
2106
|
+
const normalized = validateHandle(handle);
|
|
2107
|
+
if (options.deleteAll) {
|
|
2108
|
+
return this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
|
|
2109
|
+
} else if (options.olderThan) {
|
|
2110
|
+
return this.execSql(
|
|
2111
|
+
"DELETE FROM handle_history WHERE handle = ? AND changed_at < ?",
|
|
2112
|
+
normalized,
|
|
2113
|
+
options.olderThan
|
|
2114
|
+
);
|
|
2115
|
+
}
|
|
2116
|
+
return 0;
|
|
2117
|
+
}
|
|
2118
|
+
// ======================================================================
|
|
2119
|
+
// Concrete: Handle Management Operations
|
|
2120
|
+
// ======================================================================
|
|
2121
|
+
async getAllHandles() {
|
|
2122
|
+
const rows = await this.queryRows("SELECT handle, current_hash FROM handle_registry");
|
|
2123
|
+
return rows.map((r) => ({
|
|
2124
|
+
handle: String(r.handle),
|
|
2125
|
+
hash: String(r.current_hash)
|
|
2126
|
+
}));
|
|
2127
|
+
}
|
|
2128
|
+
async removeHandle(handle) {
|
|
2129
|
+
const normalized = validateHandle(handle);
|
|
2130
|
+
await this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
|
|
2131
|
+
await this.execSql("DELETE FROM handle_registry WHERE handle = ?", normalized);
|
|
2132
|
+
}
|
|
2133
|
+
async renameHandle(oldHandle, newHandle) {
|
|
2134
|
+
const normalizedOld = validateHandle(oldHandle);
|
|
2135
|
+
const normalizedNew = validateHandle(newHandle);
|
|
2136
|
+
if (normalizedOld === normalizedNew) return;
|
|
2137
|
+
const existsOld = await this.queryRows(
|
|
2138
|
+
"SELECT handle FROM handle_registry WHERE handle = ?",
|
|
2139
|
+
normalizedOld
|
|
2140
|
+
);
|
|
2141
|
+
if (existsOld.length === 0) throw new Error(`Handle '${oldHandle}' not found.`);
|
|
2142
|
+
const existsNew = await this.queryRows(
|
|
2143
|
+
"SELECT handle FROM handle_registry WHERE handle = ?",
|
|
2144
|
+
normalizedNew
|
|
2145
|
+
);
|
|
2146
|
+
if (existsNew.length > 0) throw new Error(`Handle '${newHandle}' already exists.`);
|
|
2147
|
+
const oldRow = await this.queryRows(
|
|
2148
|
+
"SELECT current_hash, created_at, updated_at FROM handle_registry WHERE handle = ?",
|
|
2149
|
+
normalizedOld
|
|
2150
|
+
);
|
|
2151
|
+
await this.execSql(
|
|
2152
|
+
"INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
|
|
2153
|
+
normalizedNew,
|
|
2154
|
+
String(oldRow[0].current_hash),
|
|
2155
|
+
String(oldRow[0].created_at),
|
|
2156
|
+
String(oldRow[0].updated_at)
|
|
2157
|
+
);
|
|
2158
|
+
await this.execSql(
|
|
2159
|
+
"UPDATE handle_history SET handle = ? WHERE handle = ?",
|
|
2160
|
+
normalizedNew,
|
|
2161
|
+
normalizedOld
|
|
2162
|
+
);
|
|
2163
|
+
await this.execSql(
|
|
2164
|
+
"DELETE FROM handle_registry WHERE handle = ?",
|
|
2165
|
+
normalizedOld
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
async deleteHistoryEntry(handle, previousHash) {
|
|
2169
|
+
const normalized = validateHandle(handle);
|
|
2170
|
+
const rOutRows = await this.queryRows(
|
|
2171
|
+
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1",
|
|
2172
|
+
normalized,
|
|
2173
|
+
previousHash
|
|
2174
|
+
);
|
|
2175
|
+
if (rOutRows.length === 0) return;
|
|
2176
|
+
const rOutId = Number(rOutRows[0].id);
|
|
2177
|
+
const rInRows = await this.queryRows(
|
|
2178
|
+
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1",
|
|
2179
|
+
normalized,
|
|
2180
|
+
rOutId
|
|
2181
|
+
);
|
|
2182
|
+
if (rInRows.length > 0) {
|
|
2183
|
+
const rInId = Number(rInRows[0].id);
|
|
2184
|
+
const rInPrevHash = String(rInRows[0].previous_hash);
|
|
2185
|
+
await this.execSql("UPDATE handle_history SET previous_hash = ? WHERE id = ?", rInPrevHash, rOutId);
|
|
2186
|
+
await this.execSql("DELETE FROM handle_history WHERE id = ?", rInId);
|
|
2187
|
+
} else {
|
|
2188
|
+
await this.execSql("DELETE FROM handle_history WHERE id = ?", rOutId);
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
|
|
2193
|
+
// src/storage/engines/SqliteWasmEngine.ts
|
|
2194
|
+
var SqliteWasmEngine = class extends AbstractSqlEngine {
|
|
2195
|
+
db = null;
|
|
2196
|
+
SQL = null;
|
|
2197
|
+
/**
|
|
2198
|
+
* Initialize the database
|
|
2199
|
+
* @param wasmUrl URL to sql-wasm.wasm file (optional, defaults to CDN)
|
|
2200
|
+
* @param existingData Optional existing database as Uint8Array
|
|
2201
|
+
*/
|
|
2202
|
+
async init(wasmUrl, existingData) {
|
|
2203
|
+
const initSqlJs = (await import("sql.js")).default;
|
|
2204
|
+
const SQL = await initSqlJs({
|
|
2205
|
+
locateFile: (file) => wasmUrl || `${DEFAULT_SQLJS_WASM_URL}${file}`
|
|
2206
|
+
});
|
|
2207
|
+
this.SQL = SQL;
|
|
2208
|
+
this.db = existingData ? new SQL.Database(existingData) : new SQL.Database();
|
|
2209
|
+
this.db.run(CORE_SCHEMAS.card);
|
|
2210
|
+
this.db.run(CORE_SCHEMAS.handleRegistry);
|
|
2211
|
+
this.db.run(CORE_SCHEMAS.handleHistory);
|
|
2212
|
+
this.db.run(CORE_SCHEMAS.handleIndex);
|
|
2213
|
+
}
|
|
2214
|
+
ensureDb() {
|
|
2215
|
+
if (!this.db) throw new Error("Database not initialized. Call init() first.");
|
|
2216
|
+
return this.db;
|
|
2217
|
+
}
|
|
2218
|
+
// ======================================================================
|
|
2219
|
+
// AbstractSqlEngine primitives
|
|
2220
|
+
// ======================================================================
|
|
2221
|
+
async queryRows(sql, ...params) {
|
|
2222
|
+
const db = this.ensureDb();
|
|
2223
|
+
const stmt = db.prepare(sql);
|
|
2224
|
+
if (params.length > 0) stmt.bind(params);
|
|
2068
2225
|
const results = [];
|
|
2069
2226
|
while (stmt.step()) {
|
|
2070
|
-
const row = stmt.
|
|
2071
|
-
results.push(
|
|
2227
|
+
const row = stmt.getAsObject();
|
|
2228
|
+
results.push(row);
|
|
2072
2229
|
}
|
|
2073
2230
|
stmt.free();
|
|
2074
2231
|
return results;
|
|
2075
2232
|
}
|
|
2233
|
+
async execSql(sql, ...params) {
|
|
2234
|
+
const db = this.ensureDb();
|
|
2235
|
+
if (params.length === 0) {
|
|
2236
|
+
db.run(sql);
|
|
2237
|
+
} else {
|
|
2238
|
+
db.run(sql, params);
|
|
2239
|
+
}
|
|
2240
|
+
return 0;
|
|
2241
|
+
}
|
|
2242
|
+
// ======================================================================
|
|
2243
|
+
// Row → MCard conversion (sql.js returns Uint8Array for BLOBs)
|
|
2244
|
+
// ======================================================================
|
|
2245
|
+
rowToCard(row) {
|
|
2246
|
+
const rawContent = row.content;
|
|
2247
|
+
const content = rawContent instanceof Uint8Array ? rawContent : typeof rawContent === "string" ? new TextEncoder().encode(rawContent) : new Uint8Array(0);
|
|
2248
|
+
return MCard.fromData(content, String(row.hash), String(row.g_time));
|
|
2249
|
+
}
|
|
2250
|
+
// ======================================================================
|
|
2251
|
+
// sql.js-specific helpers
|
|
2252
|
+
// ======================================================================
|
|
2253
|
+
/**
|
|
2254
|
+
* Export database as Uint8Array (for persistence)
|
|
2255
|
+
*/
|
|
2256
|
+
export() {
|
|
2257
|
+
return this.ensureDb().export();
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Get raw sql.js Database for use with VectorStore adapter.
|
|
2261
|
+
*/
|
|
2262
|
+
getRawDb() {
|
|
2263
|
+
return this.ensureDb();
|
|
2264
|
+
}
|
|
2265
|
+
// =========== pruneHandleHistory override (needs count before delete) ===========
|
|
2076
2266
|
async pruneHandleHistory(handle, options = {}) {
|
|
2267
|
+
const { validateHandle: validateHandle2 } = await Promise.resolve().then(() => (init_Handle(), Handle_exports));
|
|
2077
2268
|
const db = this.ensureDb();
|
|
2078
|
-
const normalized =
|
|
2269
|
+
const normalized = validateHandle2(handle);
|
|
2079
2270
|
if (options.deleteAll) {
|
|
2080
2271
|
const stmt = db.prepare("SELECT COUNT(*) FROM handle_history WHERE handle = ?");
|
|
2081
2272
|
stmt.bind([normalized]);
|
|
@@ -2095,67 +2286,6 @@ var SqliteWasmEngine = class {
|
|
|
2095
2286
|
}
|
|
2096
2287
|
return 0;
|
|
2097
2288
|
}
|
|
2098
|
-
// =========== Handle Management Operations ===========
|
|
2099
|
-
async getAllHandles() {
|
|
2100
|
-
const result = this.ensureDb().exec("SELECT handle, current_hash FROM handle_registry");
|
|
2101
|
-
return (result[0]?.values || []).map(([handle, hash]) => ({
|
|
2102
|
-
handle,
|
|
2103
|
-
hash
|
|
2104
|
-
}));
|
|
2105
|
-
}
|
|
2106
|
-
async removeHandle(handle) {
|
|
2107
|
-
const db = this.ensureDb();
|
|
2108
|
-
const normalized = validateHandle(handle);
|
|
2109
|
-
db.run("DELETE FROM handle_history WHERE handle = ?", [normalized]);
|
|
2110
|
-
db.run("DELETE FROM handle_registry WHERE handle = ?", [normalized]);
|
|
2111
|
-
}
|
|
2112
|
-
async renameHandle(oldHandle, newHandle) {
|
|
2113
|
-
const db = this.ensureDb();
|
|
2114
|
-
const normalizedOld = validateHandle(oldHandle);
|
|
2115
|
-
const normalizedNew = validateHandle(newHandle);
|
|
2116
|
-
if (normalizedOld === normalizedNew) return;
|
|
2117
|
-
const checkOld = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
|
|
2118
|
-
checkOld.bind([normalizedOld]);
|
|
2119
|
-
const oldExists = checkOld.step();
|
|
2120
|
-
checkOld.free();
|
|
2121
|
-
if (!oldExists) throw new Error(`Handle '${oldHandle}' not found.`);
|
|
2122
|
-
const checkNew = db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
|
|
2123
|
-
checkNew.bind([normalizedNew]);
|
|
2124
|
-
const newExists = checkNew.step();
|
|
2125
|
-
checkNew.free();
|
|
2126
|
-
if (newExists) throw new Error(`Handle '${newHandle}' already exists.`);
|
|
2127
|
-
db.run("UPDATE handle_registry SET handle = ? WHERE handle = ?", [normalizedNew, normalizedOld]);
|
|
2128
|
-
db.run("UPDATE handle_history SET handle = ? WHERE handle = ?", [normalizedNew, normalizedOld]);
|
|
2129
|
-
}
|
|
2130
|
-
async deleteHistoryEntry(handle, previousHash) {
|
|
2131
|
-
const db = this.ensureDb();
|
|
2132
|
-
const normalized = validateHandle(handle);
|
|
2133
|
-
const rOutStmt = db.prepare(
|
|
2134
|
-
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1"
|
|
2135
|
-
);
|
|
2136
|
-
rOutStmt.bind([normalized, previousHash]);
|
|
2137
|
-
if (!rOutStmt.step()) {
|
|
2138
|
-
rOutStmt.free();
|
|
2139
|
-
return;
|
|
2140
|
-
}
|
|
2141
|
-
const rOutId = rOutStmt.get()[0];
|
|
2142
|
-
rOutStmt.free();
|
|
2143
|
-
const rInStmt = db.prepare(
|
|
2144
|
-
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1"
|
|
2145
|
-
);
|
|
2146
|
-
rInStmt.bind([normalized, rOutId]);
|
|
2147
|
-
const hasRIn = rInStmt.step();
|
|
2148
|
-
if (hasRIn) {
|
|
2149
|
-
const rInPrevHash = rInStmt.get()[1];
|
|
2150
|
-
const rInId = rInStmt.get()[0];
|
|
2151
|
-
rInStmt.free();
|
|
2152
|
-
db.run("UPDATE handle_history SET previous_hash = ? WHERE id = ?", [rInPrevHash, rOutId]);
|
|
2153
|
-
db.run("DELETE FROM handle_history WHERE id = ?", [rInId]);
|
|
2154
|
-
} else {
|
|
2155
|
-
rInStmt.free();
|
|
2156
|
-
db.run("DELETE FROM handle_history WHERE id = ?", [rOutId]);
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
2289
|
};
|
|
2160
2290
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2161
2291
|
0 && (module.exports = {
|