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/SqliteNodeEngine.ts
|
|
31
118
|
var SqliteNodeEngine_exports = {};
|
|
32
119
|
__export(SqliteNodeEngine_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 SQLITE_BUSY_TIMEOUT_MS = 5e3;
|
|
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,48 +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
1291
|
// src/storage/StorageAdapter.ts
|
|
1237
1292
|
function createPage(items, totalItems, pageNumber, pageSize) {
|
|
1238
1293
|
const totalPages = Math.ceil(totalItems / pageSize) || 1;
|
|
@@ -1879,17 +1934,270 @@ function initCoreSchemas(db) {
|
|
|
1879
1934
|
schema.initHandleTables(db);
|
|
1880
1935
|
}
|
|
1881
1936
|
|
|
1937
|
+
// src/storage/engines/AbstractSqlEngine.ts
|
|
1938
|
+
init_Handle();
|
|
1939
|
+
var AbstractSqlEngine = class {
|
|
1940
|
+
/**
|
|
1941
|
+
* SQL expression for casting content to text in search queries.
|
|
1942
|
+
* Override in subclasses: SQLite uses 'TEXT', DuckDB uses 'VARCHAR'.
|
|
1943
|
+
*/
|
|
1944
|
+
get castContentAs() {
|
|
1945
|
+
return "TEXT";
|
|
1946
|
+
}
|
|
1947
|
+
// ======================================================================
|
|
1948
|
+
// Concrete: Card Operations (shared across all SQL engines)
|
|
1949
|
+
// ======================================================================
|
|
1950
|
+
/**
|
|
1951
|
+
* Add a card. Default uses INSERT OR REPLACE (SQLite).
|
|
1952
|
+
* DuckDB overrides with DELETE + INSERT.
|
|
1953
|
+
*/
|
|
1954
|
+
async add(card) {
|
|
1955
|
+
const contentBytes = card.content instanceof Uint8Array ? card.content : new TextEncoder().encode(String(card.content));
|
|
1956
|
+
await this.execSql(
|
|
1957
|
+
"INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)",
|
|
1958
|
+
card.hash,
|
|
1959
|
+
contentBytes,
|
|
1960
|
+
card.g_time
|
|
1961
|
+
);
|
|
1962
|
+
return card.hash;
|
|
1963
|
+
}
|
|
1964
|
+
async get(hash) {
|
|
1965
|
+
const rows = await this.queryRows(
|
|
1966
|
+
"SELECT hash, content, g_time FROM card WHERE hash = ?",
|
|
1967
|
+
hash
|
|
1968
|
+
);
|
|
1969
|
+
if (rows.length === 0) return null;
|
|
1970
|
+
return this.rowToCard(rows[0]);
|
|
1971
|
+
}
|
|
1972
|
+
async delete(hash) {
|
|
1973
|
+
await this.execSql("DELETE FROM card WHERE hash = ?", hash);
|
|
1974
|
+
}
|
|
1975
|
+
async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
1976
|
+
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
1977
|
+
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
1978
|
+
const totalItems = await this.count();
|
|
1979
|
+
const offset = (pageNumber - 1) * pageSize;
|
|
1980
|
+
const rows = await this.queryRows(
|
|
1981
|
+
"SELECT hash, content, g_time FROM card ORDER BY g_time DESC LIMIT ? OFFSET ?",
|
|
1982
|
+
pageSize,
|
|
1983
|
+
offset
|
|
1984
|
+
);
|
|
1985
|
+
const items = rows.map((r) => this.rowToCard(r));
|
|
1986
|
+
return createPage(items, totalItems, pageNumber, pageSize);
|
|
1987
|
+
}
|
|
1988
|
+
async count() {
|
|
1989
|
+
const rows = await this.queryRows("SELECT COUNT(*) as cnt FROM card");
|
|
1990
|
+
return Number(rows[0]?.cnt ?? 0);
|
|
1991
|
+
}
|
|
1992
|
+
async searchByHash(hashPrefix) {
|
|
1993
|
+
const rows = await this.queryRows(
|
|
1994
|
+
"SELECT hash, content, g_time FROM card WHERE hash LIKE ?",
|
|
1995
|
+
`${hashPrefix}%`
|
|
1996
|
+
);
|
|
1997
|
+
return rows.map((r) => this.rowToCard(r));
|
|
1998
|
+
}
|
|
1999
|
+
async search(queryStr, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
2000
|
+
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
2001
|
+
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
2002
|
+
const offset = (pageNumber - 1) * pageSize;
|
|
2003
|
+
const pattern = `%${queryStr}%`;
|
|
2004
|
+
const cast = this.castContentAs;
|
|
2005
|
+
const countRows = await this.queryRows(
|
|
2006
|
+
`SELECT COUNT(*) as cnt FROM card WHERE CAST(content AS ${cast}) LIKE ?`,
|
|
2007
|
+
pattern
|
|
2008
|
+
);
|
|
2009
|
+
const totalItems = Number(countRows[0]?.cnt ?? 0);
|
|
2010
|
+
const rows = await this.queryRows(
|
|
2011
|
+
`SELECT hash, content, g_time FROM card WHERE CAST(content AS ${cast}) LIKE ? ORDER BY g_time DESC LIMIT ? OFFSET ?`,
|
|
2012
|
+
pattern,
|
|
2013
|
+
pageSize,
|
|
2014
|
+
offset
|
|
2015
|
+
);
|
|
2016
|
+
const items = rows.map((r) => this.rowToCard(r));
|
|
2017
|
+
return createPage(items, totalItems, pageNumber, pageSize);
|
|
2018
|
+
}
|
|
2019
|
+
async getAll() {
|
|
2020
|
+
const rows = await this.queryRows("SELECT hash, content, g_time FROM card ORDER BY g_time DESC");
|
|
2021
|
+
return rows.map((r) => this.rowToCard(r));
|
|
2022
|
+
}
|
|
2023
|
+
// ======================================================================
|
|
2024
|
+
// Concrete: clear (FK-safe delete order)
|
|
2025
|
+
// ======================================================================
|
|
2026
|
+
async clear() {
|
|
2027
|
+
await this.execSql("DELETE FROM handle_history");
|
|
2028
|
+
await this.execSql("DELETE FROM handle_registry");
|
|
2029
|
+
await this.execSql("DELETE FROM card");
|
|
2030
|
+
}
|
|
2031
|
+
// ======================================================================
|
|
2032
|
+
// Concrete: Handle Operations
|
|
2033
|
+
// ======================================================================
|
|
2034
|
+
async registerHandle(handle, hash) {
|
|
2035
|
+
const normalized = validateHandle(handle);
|
|
2036
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2037
|
+
const existing = await this.queryRows(
|
|
2038
|
+
"SELECT handle FROM handle_registry WHERE handle = ?",
|
|
2039
|
+
normalized
|
|
2040
|
+
);
|
|
2041
|
+
if (existing.length > 0) {
|
|
2042
|
+
throw new Error(`Handle '${handle}' already exists.`);
|
|
2043
|
+
}
|
|
2044
|
+
await this.execSql(
|
|
2045
|
+
"INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
|
|
2046
|
+
normalized,
|
|
2047
|
+
hash,
|
|
2048
|
+
now,
|
|
2049
|
+
now
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
async resolveHandle(handle) {
|
|
2053
|
+
const normalized = validateHandle(handle);
|
|
2054
|
+
const rows = await this.queryRows(
|
|
2055
|
+
"SELECT current_hash FROM handle_registry WHERE handle = ?",
|
|
2056
|
+
normalized
|
|
2057
|
+
);
|
|
2058
|
+
if (rows.length === 0) return null;
|
|
2059
|
+
return String(rows[0].current_hash);
|
|
2060
|
+
}
|
|
2061
|
+
async getByHandle(handle) {
|
|
2062
|
+
const hash = await this.resolveHandle(handle);
|
|
2063
|
+
if (!hash) return null;
|
|
2064
|
+
return this.get(hash);
|
|
2065
|
+
}
|
|
2066
|
+
async updateHandle(handle, newHash) {
|
|
2067
|
+
const normalized = validateHandle(handle);
|
|
2068
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2069
|
+
const rows = await this.queryRows(
|
|
2070
|
+
"SELECT current_hash FROM handle_registry WHERE handle = ?",
|
|
2071
|
+
normalized
|
|
2072
|
+
);
|
|
2073
|
+
if (rows.length === 0) {
|
|
2074
|
+
throw new Error(`Handle '${handle}' not found.`);
|
|
2075
|
+
}
|
|
2076
|
+
const previousHash = String(rows[0].current_hash);
|
|
2077
|
+
await this.execSql(
|
|
2078
|
+
"INSERT INTO handle_history (handle, previous_hash, changed_at) VALUES (?, ?, ?)",
|
|
2079
|
+
normalized,
|
|
2080
|
+
previousHash,
|
|
2081
|
+
now
|
|
2082
|
+
);
|
|
2083
|
+
await this.execSql(
|
|
2084
|
+
"UPDATE handle_registry SET current_hash = ?, updated_at = ? WHERE handle = ?",
|
|
2085
|
+
newHash,
|
|
2086
|
+
now,
|
|
2087
|
+
normalized
|
|
2088
|
+
);
|
|
2089
|
+
return previousHash;
|
|
2090
|
+
}
|
|
2091
|
+
async getHandleHistory(handle) {
|
|
2092
|
+
const normalized = validateHandle(handle);
|
|
2093
|
+
const rows = await this.queryRows(
|
|
2094
|
+
"SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC",
|
|
2095
|
+
normalized
|
|
2096
|
+
);
|
|
2097
|
+
return rows.map((r) => ({
|
|
2098
|
+
previousHash: String(r.previous_hash),
|
|
2099
|
+
changedAt: String(r.changed_at)
|
|
2100
|
+
}));
|
|
2101
|
+
}
|
|
2102
|
+
async pruneHandleHistory(handle, options = {}) {
|
|
2103
|
+
const normalized = validateHandle(handle);
|
|
2104
|
+
if (options.deleteAll) {
|
|
2105
|
+
return this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
|
|
2106
|
+
} else if (options.olderThan) {
|
|
2107
|
+
return this.execSql(
|
|
2108
|
+
"DELETE FROM handle_history WHERE handle = ? AND changed_at < ?",
|
|
2109
|
+
normalized,
|
|
2110
|
+
options.olderThan
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
return 0;
|
|
2114
|
+
}
|
|
2115
|
+
// ======================================================================
|
|
2116
|
+
// Concrete: Handle Management Operations
|
|
2117
|
+
// ======================================================================
|
|
2118
|
+
async getAllHandles() {
|
|
2119
|
+
const rows = await this.queryRows("SELECT handle, current_hash FROM handle_registry");
|
|
2120
|
+
return rows.map((r) => ({
|
|
2121
|
+
handle: String(r.handle),
|
|
2122
|
+
hash: String(r.current_hash)
|
|
2123
|
+
}));
|
|
2124
|
+
}
|
|
2125
|
+
async removeHandle(handle) {
|
|
2126
|
+
const normalized = validateHandle(handle);
|
|
2127
|
+
await this.execSql("DELETE FROM handle_history WHERE handle = ?", normalized);
|
|
2128
|
+
await this.execSql("DELETE FROM handle_registry WHERE handle = ?", normalized);
|
|
2129
|
+
}
|
|
2130
|
+
async renameHandle(oldHandle, newHandle) {
|
|
2131
|
+
const normalizedOld = validateHandle(oldHandle);
|
|
2132
|
+
const normalizedNew = validateHandle(newHandle);
|
|
2133
|
+
if (normalizedOld === normalizedNew) return;
|
|
2134
|
+
const existsOld = await this.queryRows(
|
|
2135
|
+
"SELECT handle FROM handle_registry WHERE handle = ?",
|
|
2136
|
+
normalizedOld
|
|
2137
|
+
);
|
|
2138
|
+
if (existsOld.length === 0) throw new Error(`Handle '${oldHandle}' not found.`);
|
|
2139
|
+
const existsNew = await this.queryRows(
|
|
2140
|
+
"SELECT handle FROM handle_registry WHERE handle = ?",
|
|
2141
|
+
normalizedNew
|
|
2142
|
+
);
|
|
2143
|
+
if (existsNew.length > 0) throw new Error(`Handle '${newHandle}' already exists.`);
|
|
2144
|
+
const oldRow = await this.queryRows(
|
|
2145
|
+
"SELECT current_hash, created_at, updated_at FROM handle_registry WHERE handle = ?",
|
|
2146
|
+
normalizedOld
|
|
2147
|
+
);
|
|
2148
|
+
await this.execSql(
|
|
2149
|
+
"INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)",
|
|
2150
|
+
normalizedNew,
|
|
2151
|
+
String(oldRow[0].current_hash),
|
|
2152
|
+
String(oldRow[0].created_at),
|
|
2153
|
+
String(oldRow[0].updated_at)
|
|
2154
|
+
);
|
|
2155
|
+
await this.execSql(
|
|
2156
|
+
"UPDATE handle_history SET handle = ? WHERE handle = ?",
|
|
2157
|
+
normalizedNew,
|
|
2158
|
+
normalizedOld
|
|
2159
|
+
);
|
|
2160
|
+
await this.execSql(
|
|
2161
|
+
"DELETE FROM handle_registry WHERE handle = ?",
|
|
2162
|
+
normalizedOld
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
async deleteHistoryEntry(handle, previousHash) {
|
|
2166
|
+
const normalized = validateHandle(handle);
|
|
2167
|
+
const rOutRows = await this.queryRows(
|
|
2168
|
+
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1",
|
|
2169
|
+
normalized,
|
|
2170
|
+
previousHash
|
|
2171
|
+
);
|
|
2172
|
+
if (rOutRows.length === 0) return;
|
|
2173
|
+
const rOutId = Number(rOutRows[0].id);
|
|
2174
|
+
const rInRows = await this.queryRows(
|
|
2175
|
+
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND id < ? ORDER BY id DESC LIMIT 1",
|
|
2176
|
+
normalized,
|
|
2177
|
+
rOutId
|
|
2178
|
+
);
|
|
2179
|
+
if (rInRows.length > 0) {
|
|
2180
|
+
const rInId = Number(rInRows[0].id);
|
|
2181
|
+
const rInPrevHash = String(rInRows[0].previous_hash);
|
|
2182
|
+
await this.execSql("UPDATE handle_history SET previous_hash = ? WHERE id = ?", rInPrevHash, rOutId);
|
|
2183
|
+
await this.execSql("DELETE FROM handle_history WHERE id = ?", rInId);
|
|
2184
|
+
} else {
|
|
2185
|
+
await this.execSql("DELETE FROM handle_history WHERE id = ?", rOutId);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
|
|
1882
2190
|
// src/storage/engines/SqliteNodeEngine.ts
|
|
1883
|
-
var
|
|
1884
|
-
var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
2191
|
+
var SqliteNodeEngine = class _SqliteNodeEngine extends AbstractSqlEngine {
|
|
1885
2192
|
db;
|
|
1886
2193
|
dbPath;
|
|
1887
2194
|
/**
|
|
1888
2195
|
* Convert a database row into an MCard instance.
|
|
1889
2196
|
*/
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1892
|
-
|
|
2197
|
+
rowToCard(row) {
|
|
2198
|
+
const rawContent = row.content;
|
|
2199
|
+
const content = rawContent instanceof Buffer ? new Uint8Array(rawContent) : new Uint8Array(rawContent);
|
|
2200
|
+
return MCard.fromData(content, String(row.hash), String(row.g_time));
|
|
1893
2201
|
}
|
|
1894
2202
|
/**
|
|
1895
2203
|
* Create a new SqliteNodeEngine via async factory.
|
|
@@ -1901,6 +2209,7 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
1901
2209
|
return engine;
|
|
1902
2210
|
}
|
|
1903
2211
|
constructor(dbPath) {
|
|
2212
|
+
super();
|
|
1904
2213
|
this.dbPath = dbPath;
|
|
1905
2214
|
}
|
|
1906
2215
|
async init() {
|
|
@@ -1919,10 +2228,26 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
1919
2228
|
*/
|
|
1920
2229
|
setupDatabase() {
|
|
1921
2230
|
this.db.pragma("journal_mode = WAL");
|
|
1922
|
-
this.db.pragma(
|
|
2231
|
+
this.db.pragma(`busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`);
|
|
1923
2232
|
this.db.pragma("synchronous = NORMAL");
|
|
1924
2233
|
initCoreSchemas(this.db);
|
|
1925
2234
|
}
|
|
2235
|
+
// ======================================================================
|
|
2236
|
+
// AbstractSqlEngine primitives
|
|
2237
|
+
// ======================================================================
|
|
2238
|
+
async queryRows(sql, ...params) {
|
|
2239
|
+
const stmt = this.db.prepare(sql);
|
|
2240
|
+
return stmt.all(...params);
|
|
2241
|
+
}
|
|
2242
|
+
async execSql(sql, ...params) {
|
|
2243
|
+
if (params.length === 0) {
|
|
2244
|
+
this.db.exec(sql);
|
|
2245
|
+
return 0;
|
|
2246
|
+
}
|
|
2247
|
+
const stmt = this.db.prepare(sql);
|
|
2248
|
+
const result = stmt.run(...params);
|
|
2249
|
+
return result.changes;
|
|
2250
|
+
}
|
|
1926
2251
|
/**
|
|
1927
2252
|
* Close the database connection
|
|
1928
2253
|
*/
|
|
@@ -1935,7 +2260,7 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
1935
2260
|
getDbPath() {
|
|
1936
2261
|
return this.dbPath;
|
|
1937
2262
|
}
|
|
1938
|
-
// =========== Card Operations ===========
|
|
2263
|
+
// =========== Card Operations (overrides to use Buffer.from for better-sqlite3) ===========
|
|
1939
2264
|
async add(card) {
|
|
1940
2265
|
const stmt = this.db.prepare(
|
|
1941
2266
|
"INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)"
|
|
@@ -1943,9 +2268,7 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
1943
2268
|
stmt.run(card.hash, Buffer.from(card.content), card.g_time);
|
|
1944
2269
|
return card.hash;
|
|
1945
2270
|
}
|
|
1946
|
-
|
|
1947
|
-
* Add a card synchronously (for performance-critical paths)
|
|
1948
|
-
*/
|
|
2271
|
+
// =========== Sync card operations (for performance-critical callers) ===========
|
|
1949
2272
|
addSync(card) {
|
|
1950
2273
|
const stmt = this.db.prepare(
|
|
1951
2274
|
"INSERT OR REPLACE INTO card (hash, content, g_time) VALUES (?, ?, ?)"
|
|
@@ -1953,35 +2276,17 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
1953
2276
|
stmt.run(card.hash, Buffer.from(card.content), card.g_time);
|
|
1954
2277
|
return card.hash;
|
|
1955
2278
|
}
|
|
1956
|
-
async get(hash) {
|
|
1957
|
-
return this.getSync(hash);
|
|
1958
|
-
}
|
|
1959
|
-
/**
|
|
1960
|
-
* Get a card synchronously
|
|
1961
|
-
*/
|
|
1962
2279
|
getSync(hash) {
|
|
1963
2280
|
const stmt = this.db.prepare("SELECT hash, content, g_time FROM card WHERE hash = ?");
|
|
1964
2281
|
const row = stmt.get(hash);
|
|
1965
2282
|
if (!row) return null;
|
|
1966
|
-
return
|
|
2283
|
+
return this.rowToCard(row);
|
|
1967
2284
|
}
|
|
1968
|
-
async delete(hash) {
|
|
1969
|
-
this.deleteSync(hash);
|
|
1970
|
-
}
|
|
1971
|
-
/**
|
|
1972
|
-
* Delete a card synchronously
|
|
1973
|
-
*/
|
|
1974
2285
|
deleteSync(hash) {
|
|
1975
2286
|
const stmt = this.db.prepare("DELETE FROM card WHERE hash = ?");
|
|
1976
2287
|
const result = stmt.run(hash);
|
|
1977
2288
|
return result.changes > 0;
|
|
1978
2289
|
}
|
|
1979
|
-
async getPage(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
1980
|
-
return this.getPageSync(pageNumber, pageSize);
|
|
1981
|
-
}
|
|
1982
|
-
/**
|
|
1983
|
-
* Get a page of cards synchronously
|
|
1984
|
-
*/
|
|
1985
2290
|
getPageSync(pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
1986
2291
|
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
1987
2292
|
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
@@ -1991,76 +2296,40 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
1991
2296
|
"SELECT hash, content, g_time FROM card ORDER BY g_time DESC LIMIT ? OFFSET ?"
|
|
1992
2297
|
);
|
|
1993
2298
|
const rows = stmt.all(pageSize, offset);
|
|
1994
|
-
const items = rows.map((row) =>
|
|
2299
|
+
const items = rows.map((row) => this.rowToCard(row));
|
|
1995
2300
|
return createPage(items, totalItems, pageNumber, pageSize);
|
|
1996
2301
|
}
|
|
1997
|
-
async count() {
|
|
1998
|
-
return this.countSync();
|
|
1999
|
-
}
|
|
2000
|
-
/**
|
|
2001
|
-
* Count cards synchronously
|
|
2002
|
-
*/
|
|
2003
2302
|
countSync() {
|
|
2004
|
-
const stmt = this.db.prepare("SELECT COUNT(*) as
|
|
2303
|
+
const stmt = this.db.prepare("SELECT COUNT(*) as cnt FROM card");
|
|
2005
2304
|
const row = stmt.get();
|
|
2006
|
-
return row.
|
|
2007
|
-
}
|
|
2008
|
-
async searchByHash(hashPrefix) {
|
|
2009
|
-
const rows = this.db.prepare("SELECT hash, content, g_time FROM card WHERE hash LIKE ?").all(`${hashPrefix}%`);
|
|
2010
|
-
return rows.map((row) => _SqliteNodeEngine.rowToCard(row));
|
|
2011
|
-
}
|
|
2012
|
-
async search(query, pageNumber, pageSize) {
|
|
2013
|
-
const offset = (pageNumber - 1) * pageSize;
|
|
2014
|
-
const searchPattern = `%${query}%`;
|
|
2015
|
-
const countResult = this.db.prepare("SELECT COUNT(*) as count FROM card WHERE CAST(content AS TEXT) LIKE ?").get(searchPattern);
|
|
2016
|
-
const totalItems = countResult.count;
|
|
2017
|
-
const rows = this.db.prepare("SELECT hash, content, g_time FROM card WHERE CAST(content AS TEXT) LIKE ? LIMIT ? OFFSET ?").all(searchPattern, pageSize, offset);
|
|
2018
|
-
const items = rows.map((row) => _SqliteNodeEngine.rowToCard(row));
|
|
2019
|
-
return createPage(items, totalItems, pageNumber, pageSize);
|
|
2020
|
-
}
|
|
2021
|
-
async getAll() {
|
|
2022
|
-
const rows = this.db.prepare("SELECT hash, content, g_time FROM card").all();
|
|
2023
|
-
return rows.map((row) => _SqliteNodeEngine.rowToCard(row));
|
|
2305
|
+
return row.cnt;
|
|
2024
2306
|
}
|
|
2025
|
-
async clear() {
|
|
2026
|
-
this.clearSync();
|
|
2027
|
-
}
|
|
2028
|
-
/**
|
|
2029
|
-
* Clear all data synchronously
|
|
2030
|
-
* Delete in FK-safe order: children first, then parents
|
|
2031
|
-
*/
|
|
2032
2307
|
clearSync() {
|
|
2033
2308
|
this.db.exec("DELETE FROM handle_history");
|
|
2034
2309
|
this.db.exec("DELETE FROM handle_registry");
|
|
2035
2310
|
this.db.exec("DELETE FROM card");
|
|
2036
2311
|
}
|
|
2037
|
-
// ===========
|
|
2038
|
-
/**
|
|
2039
|
-
* Search cards by string in content, hash, or g_time
|
|
2040
|
-
*/
|
|
2312
|
+
// =========== Additional sync search operations ===========
|
|
2041
2313
|
searchByString(searchString, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
2042
2314
|
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
2043
2315
|
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
2044
2316
|
const pattern = `%${searchString}%`;
|
|
2045
2317
|
const offset = (pageNumber - 1) * pageSize;
|
|
2046
2318
|
const countStmt = this.db.prepare(`
|
|
2047
|
-
SELECT COUNT(*) as
|
|
2319
|
+
SELECT COUNT(*) as cnt FROM card
|
|
2048
2320
|
WHERE hash LIKE ? OR g_time LIKE ? OR CAST(content AS TEXT) LIKE ?
|
|
2049
2321
|
`);
|
|
2050
2322
|
const countRow = countStmt.get(pattern, pattern, pattern);
|
|
2051
|
-
const totalItems = countRow.
|
|
2323
|
+
const totalItems = countRow.cnt;
|
|
2052
2324
|
const stmt = this.db.prepare(`
|
|
2053
|
-
SELECT hash, content, g_time FROM card
|
|
2325
|
+
SELECT hash, content, g_time FROM card
|
|
2054
2326
|
WHERE hash LIKE ? OR g_time LIKE ? OR CAST(content AS TEXT) LIKE ?
|
|
2055
2327
|
ORDER BY g_time DESC LIMIT ? OFFSET ?
|
|
2056
2328
|
`);
|
|
2057
2329
|
const rows = stmt.all(pattern, pattern, pattern, pageSize, offset);
|
|
2058
|
-
const items = rows.map((row) =>
|
|
2330
|
+
const items = rows.map((row) => this.rowToCard(row));
|
|
2059
2331
|
return createPage(items, totalItems, pageNumber, pageSize);
|
|
2060
2332
|
}
|
|
2061
|
-
/**
|
|
2062
|
-
* Search cards by content pattern (binary-safe)
|
|
2063
|
-
*/
|
|
2064
2333
|
searchByContent(searchPattern, pageNumber = 1, pageSize = DEFAULT_PAGE_SIZE) {
|
|
2065
2334
|
if (pageNumber < 1) throw new Error("Page number must be >= 1");
|
|
2066
2335
|
if (pageSize < 1) throw new Error("Page size must be >= 1");
|
|
@@ -2069,172 +2338,28 @@ var SqliteNodeEngine = class _SqliteNodeEngine {
|
|
|
2069
2338
|
}
|
|
2070
2339
|
const searchBytes = typeof searchPattern === "string" ? Buffer.from(searchPattern, "utf-8") : Buffer.from(searchPattern);
|
|
2071
2340
|
const offset = (pageNumber - 1) * pageSize;
|
|
2072
|
-
const countStmt = this.db.prepare("SELECT COUNT(*) as
|
|
2341
|
+
const countStmt = this.db.prepare("SELECT COUNT(*) as cnt FROM card WHERE INSTR(content, ?) > 0");
|
|
2073
2342
|
const countRow = countStmt.get(searchBytes);
|
|
2074
|
-
const totalItems = countRow.
|
|
2343
|
+
const totalItems = countRow.cnt;
|
|
2075
2344
|
const stmt = this.db.prepare(`
|
|
2076
|
-
SELECT hash, content, g_time FROM card
|
|
2345
|
+
SELECT hash, content, g_time FROM card
|
|
2077
2346
|
WHERE INSTR(content, ?) > 0
|
|
2078
2347
|
ORDER BY g_time DESC LIMIT ? OFFSET ?
|
|
2079
2348
|
`);
|
|
2080
2349
|
const rows = stmt.all(searchBytes, pageSize, offset);
|
|
2081
|
-
const items = rows.map((row) =>
|
|
2350
|
+
const items = rows.map((row) => this.rowToCard(row));
|
|
2082
2351
|
return createPage(items, totalItems, pageNumber, pageSize);
|
|
2083
2352
|
}
|
|
2084
|
-
/**
|
|
2085
|
-
* Get all cards (single page with all items)
|
|
2086
|
-
*/
|
|
2087
2353
|
getAllCards() {
|
|
2088
2354
|
const stmt = this.db.prepare("SELECT hash, content, g_time FROM card ORDER BY g_time DESC");
|
|
2089
2355
|
const rows = stmt.all();
|
|
2090
|
-
const items = rows.map((row) =>
|
|
2356
|
+
const items = rows.map((row) => this.rowToCard(row));
|
|
2091
2357
|
return createPage(items, items.length, 1, items.length || 1);
|
|
2092
2358
|
}
|
|
2093
|
-
// ===========
|
|
2094
|
-
async registerHandle(handle, hash) {
|
|
2095
|
-
this.registerHandleSync(handle, hash);
|
|
2096
|
-
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Register a handle synchronously
|
|
2099
|
-
*/
|
|
2100
|
-
registerHandleSync(handle, hash) {
|
|
2101
|
-
const normalized = validateHandle(handle);
|
|
2102
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2103
|
-
const checkStmt = this.db.prepare("SELECT handle FROM handle_registry WHERE handle = ?");
|
|
2104
|
-
const existing = checkStmt.get(normalized);
|
|
2105
|
-
if (existing) {
|
|
2106
|
-
throw new Error(`Handle '${handle}' already exists.`);
|
|
2107
|
-
}
|
|
2108
|
-
const stmt = this.db.prepare(
|
|
2109
|
-
"INSERT INTO handle_registry (handle, current_hash, created_at, updated_at) VALUES (?, ?, ?, ?)"
|
|
2110
|
-
);
|
|
2111
|
-
stmt.run(normalized, hash, now, now);
|
|
2112
|
-
}
|
|
2113
|
-
async resolveHandle(handle) {
|
|
2114
|
-
return this.resolveHandleSync(handle);
|
|
2115
|
-
}
|
|
2116
|
-
/**
|
|
2117
|
-
* Resolve a handle synchronously
|
|
2118
|
-
*/
|
|
2119
|
-
resolveHandleSync(handle) {
|
|
2120
|
-
const normalized = validateHandle(handle);
|
|
2121
|
-
const stmt = this.db.prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
|
|
2122
|
-
const row = stmt.get(normalized);
|
|
2123
|
-
return row?.current_hash || null;
|
|
2124
|
-
}
|
|
2125
|
-
async getByHandle(handle) {
|
|
2126
|
-
return this.getByHandleSync(handle);
|
|
2127
|
-
}
|
|
2128
|
-
/**
|
|
2129
|
-
* Get card by handle synchronously
|
|
2130
|
-
*/
|
|
2131
|
-
getByHandleSync(handle) {
|
|
2132
|
-
const hash = this.resolveHandleSync(handle);
|
|
2133
|
-
if (!hash) return null;
|
|
2134
|
-
return this.getSync(hash);
|
|
2135
|
-
}
|
|
2136
|
-
async updateHandle(handle, newHash) {
|
|
2137
|
-
return this.updateHandleSync(handle, newHash);
|
|
2138
|
-
}
|
|
2139
|
-
/**
|
|
2140
|
-
* Update a handle synchronously
|
|
2141
|
-
*/
|
|
2142
|
-
updateHandleSync(handle, newHash) {
|
|
2143
|
-
const normalized = validateHandle(handle);
|
|
2144
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2145
|
-
const getStmt = this.db.prepare("SELECT current_hash FROM handle_registry WHERE handle = ?");
|
|
2146
|
-
const row = getStmt.get(normalized);
|
|
2147
|
-
if (!row) {
|
|
2148
|
-
throw new Error(`Handle '${handle}' not found.`);
|
|
2149
|
-
}
|
|
2150
|
-
const previousHash = row.current_hash;
|
|
2151
|
-
const historyStmt = this.db.prepare(
|
|
2152
|
-
"INSERT INTO handle_history (handle, previous_hash, changed_at) VALUES (?, ?, ?)"
|
|
2153
|
-
);
|
|
2154
|
-
historyStmt.run(normalized, previousHash, now);
|
|
2155
|
-
const updateStmt = this.db.prepare(
|
|
2156
|
-
"UPDATE handle_registry SET current_hash = ?, updated_at = ? WHERE handle = ?"
|
|
2157
|
-
);
|
|
2158
|
-
updateStmt.run(newHash, now, normalized);
|
|
2159
|
-
return previousHash;
|
|
2160
|
-
}
|
|
2161
|
-
async getHandleHistory(handle) {
|
|
2162
|
-
return this.getHandleHistorySync(handle);
|
|
2163
|
-
}
|
|
2164
|
-
/**
|
|
2165
|
-
* Get handle history synchronously
|
|
2166
|
-
*/
|
|
2167
|
-
getHandleHistorySync(handle) {
|
|
2168
|
-
const normalized = validateHandle(handle);
|
|
2169
|
-
const stmt = this.db.prepare(
|
|
2170
|
-
"SELECT previous_hash, changed_at FROM handle_history WHERE handle = ? ORDER BY id DESC"
|
|
2171
|
-
);
|
|
2172
|
-
const rows = stmt.all(normalized);
|
|
2173
|
-
return rows.map((row) => ({
|
|
2174
|
-
previousHash: row.previous_hash,
|
|
2175
|
-
changedAt: row.changed_at
|
|
2176
|
-
}));
|
|
2177
|
-
}
|
|
2178
|
-
async pruneHandleHistory(handle, options = {}) {
|
|
2179
|
-
return this.pruneHandleHistorySync(handle, options);
|
|
2180
|
-
}
|
|
2181
|
-
/**
|
|
2182
|
-
* Prune handle history synchronously
|
|
2183
|
-
*/
|
|
2184
|
-
pruneHandleHistorySync(handle, options = {}) {
|
|
2185
|
-
const normalized = validateHandle(handle);
|
|
2186
|
-
let stmt;
|
|
2187
|
-
if (options.deleteAll) {
|
|
2188
|
-
stmt = this.db.prepare("DELETE FROM handle_history WHERE handle = ?");
|
|
2189
|
-
const result = stmt.run(normalized);
|
|
2190
|
-
return result.changes;
|
|
2191
|
-
} else if (options.olderThan) {
|
|
2192
|
-
stmt = this.db.prepare("DELETE FROM handle_history WHERE handle = ? AND changed_at < ?");
|
|
2193
|
-
const result = stmt.run(normalized, options.olderThan);
|
|
2194
|
-
return result.changes;
|
|
2195
|
-
}
|
|
2196
|
-
return 0;
|
|
2197
|
-
}
|
|
2198
|
-
// =========== Handle Management Operations ===========
|
|
2199
|
-
async getAllHandles() {
|
|
2200
|
-
return this.getAllHandlesSync();
|
|
2201
|
-
}
|
|
2202
|
-
getAllHandlesSync() {
|
|
2203
|
-
const stmt = this.db.prepare("SELECT handle, current_hash FROM handle_registry");
|
|
2204
|
-
const rows = stmt.all();
|
|
2205
|
-
return rows.map((row) => ({ handle: row.handle, hash: row.current_hash }));
|
|
2206
|
-
}
|
|
2207
|
-
async removeHandle(handle) {
|
|
2208
|
-
this.removeHandleSync(handle);
|
|
2209
|
-
}
|
|
2210
|
-
removeHandleSync(handle) {
|
|
2211
|
-
const normalized = validateHandle(handle);
|
|
2212
|
-
this.db.prepare("DELETE FROM handle_history WHERE handle = ?").run(normalized);
|
|
2213
|
-
this.db.prepare("DELETE FROM handle_registry WHERE handle = ?").run(normalized);
|
|
2214
|
-
}
|
|
2215
|
-
async renameHandle(oldHandle, newHandle) {
|
|
2216
|
-
this.renameHandleSync(oldHandle, newHandle);
|
|
2217
|
-
}
|
|
2218
|
-
renameHandleSync(oldHandle, newHandle) {
|
|
2219
|
-
const normalizedOld = validateHandle(oldHandle);
|
|
2220
|
-
const normalizedNew = validateHandle(newHandle);
|
|
2221
|
-
if (normalizedOld === normalizedNew) return;
|
|
2222
|
-
const existsOld = this.db.prepare("SELECT handle FROM handle_registry WHERE handle = ?").get(normalizedOld);
|
|
2223
|
-
if (!existsOld) throw new Error(`Handle '${oldHandle}' not found.`);
|
|
2224
|
-
const existsNew = this.db.prepare("SELECT handle FROM handle_registry WHERE handle = ?").get(normalizedNew);
|
|
2225
|
-
if (existsNew) throw new Error(`Handle '${newHandle}' already exists.`);
|
|
2226
|
-
const performRename = this.db.transaction(() => {
|
|
2227
|
-
this.db.pragma("defer_foreign_keys = ON");
|
|
2228
|
-
this.db.prepare("UPDATE handle_registry SET handle = ? WHERE handle = ?").run(normalizedNew, normalizedOld);
|
|
2229
|
-
this.db.prepare("UPDATE handle_history SET handle = ? WHERE handle = ?").run(normalizedNew, normalizedOld);
|
|
2230
|
-
});
|
|
2231
|
-
performRename();
|
|
2232
|
-
}
|
|
2233
|
-
async deleteHistoryEntry(handle, previousHash) {
|
|
2234
|
-
this.deleteHistoryEntrySync(handle, previousHash);
|
|
2235
|
-
}
|
|
2359
|
+
// =========== Unique sync method (not in AbstractSqlEngine) ===========
|
|
2236
2360
|
deleteHistoryEntrySync(handle, previousHash) {
|
|
2237
|
-
const
|
|
2361
|
+
const { validateHandle: validateHandle2 } = (init_Handle(), __toCommonJS(Handle_exports));
|
|
2362
|
+
const normalized = validateHandle2(handle);
|
|
2238
2363
|
const performStitch = this.db.transaction(() => {
|
|
2239
2364
|
const rOut = this.db.prepare(
|
|
2240
2365
|
"SELECT id, previous_hash FROM handle_history WHERE handle = ? AND previous_hash = ? ORDER BY id DESC LIMIT 1"
|