@dexto/storage 1.8.12 → 1.9.1
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/database/memory-database-store.cjs +19 -0
- package/dist/database/memory-database-store.d.ts +4 -0
- package/dist/database/memory-database-store.d.ts.map +1 -1
- package/dist/database/memory-database-store.js +19 -0
- package/dist/database/postgres-store.cjs +54 -1
- package/dist/database/postgres-store.d.ts +5 -0
- package/dist/database/postgres-store.d.ts.map +1 -1
- package/dist/database/postgres-store.js +54 -1
- package/dist/database/sqlite-store.cjs +27 -0
- package/dist/database/sqlite-store.d.ts +4 -0
- package/dist/database/sqlite-store.d.ts.map +1 -1
- package/dist/database/sqlite-store.js +27 -0
- package/dist/index.d.cts +13 -0
- package/package.json +2 -2
|
@@ -108,6 +108,25 @@ class MemoryDatabaseStore {
|
|
|
108
108
|
}
|
|
109
109
|
this.lists.get(key).push(item);
|
|
110
110
|
}
|
|
111
|
+
async updateList(key, updater) {
|
|
112
|
+
this.checkConnection();
|
|
113
|
+
try {
|
|
114
|
+
const current = this.lists.get(key) ?? [];
|
|
115
|
+
const mutation = updater([...current]);
|
|
116
|
+
if (mutation.items.length === 0) {
|
|
117
|
+
this.lists.delete(key);
|
|
118
|
+
} else {
|
|
119
|
+
this.lists.set(key, [...mutation.items]);
|
|
120
|
+
}
|
|
121
|
+
return mutation.result;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
throw import_core.StorageError.writeFailed(
|
|
124
|
+
"updateList",
|
|
125
|
+
error instanceof Error ? error.message : String(error),
|
|
126
|
+
{ key }
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
111
130
|
async getRange(key, start, count) {
|
|
112
131
|
this.checkConnection();
|
|
113
132
|
const list = this.lists.get(key) || [];
|
|
@@ -21,6 +21,10 @@ export declare class MemoryDatabaseStore implements Database {
|
|
|
21
21
|
delete(key: string): Promise<void>;
|
|
22
22
|
list(prefix: string): Promise<string[]>;
|
|
23
23
|
append<T>(key: string, item: T): Promise<void>;
|
|
24
|
+
updateList<T, R>(key: string, updater: (items: T[]) => {
|
|
25
|
+
items: T[];
|
|
26
|
+
result: R;
|
|
27
|
+
}): Promise<R>;
|
|
24
28
|
getRange<T>(key: string, start: number, count: number): Promise<T[]>;
|
|
25
29
|
private checkConnection;
|
|
26
30
|
clear(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-database-store.d.ts","sourceRoot":"","sources":["../../src/database/memory-database-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,QAAQ;IAChD,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,SAAS,CAAS;IAEpB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAIhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAa3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAiB/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAsBvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAO1E,OAAO,CAAC,eAAe;IAOjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;KAAE,CAAC;CAM7F"}
|
|
1
|
+
{"version":3,"file":"memory-database-store.d.ts","sourceRoot":"","sources":["../../src/database/memory-database-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,QAAQ;IAChD,OAAO,CAAC,IAAI,CAA8B;IAC1C,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,SAAS,CAAS;IAEpB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAIhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAa3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAiB/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAsBvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C,UAAU,CAAC,CAAC,EAAE,CAAC,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK;QAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,GACnD,OAAO,CAAC,CAAC,CAAC;IAoBP,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAO1E,OAAO,CAAC,eAAe;IAOjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;KAAE,CAAC;CAM7F"}
|
|
@@ -85,6 +85,25 @@ class MemoryDatabaseStore {
|
|
|
85
85
|
}
|
|
86
86
|
this.lists.get(key).push(item);
|
|
87
87
|
}
|
|
88
|
+
async updateList(key, updater) {
|
|
89
|
+
this.checkConnection();
|
|
90
|
+
try {
|
|
91
|
+
const current = this.lists.get(key) ?? [];
|
|
92
|
+
const mutation = updater([...current]);
|
|
93
|
+
if (mutation.items.length === 0) {
|
|
94
|
+
this.lists.delete(key);
|
|
95
|
+
} else {
|
|
96
|
+
this.lists.set(key, [...mutation.items]);
|
|
97
|
+
}
|
|
98
|
+
return mutation.result;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw StorageError.writeFailed(
|
|
101
|
+
"updateList",
|
|
102
|
+
error instanceof Error ? error.message : String(error),
|
|
103
|
+
{ key }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
88
107
|
async getRange(key, start, count) {
|
|
89
108
|
this.checkConnection();
|
|
90
109
|
const list = this.lists.get(key) || [];
|
|
@@ -235,10 +235,38 @@ class PostgresStore {
|
|
|
235
235
|
);
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
+
async updateList(key, updater) {
|
|
239
|
+
try {
|
|
240
|
+
return await this.transactionOnce(async (client) => {
|
|
241
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1::text)::bigint)", [
|
|
242
|
+
key
|
|
243
|
+
]);
|
|
244
|
+
const result = await client.query(
|
|
245
|
+
"SELECT item FROM lists WHERE key = $1 ORDER BY created_at ASC, id ASC",
|
|
246
|
+
[key]
|
|
247
|
+
);
|
|
248
|
+
const mutation = updater(result.rows.map((row) => row.item));
|
|
249
|
+
await client.query("DELETE FROM lists WHERE key = $1", [key]);
|
|
250
|
+
for (const item of mutation.items) {
|
|
251
|
+
await client.query(
|
|
252
|
+
"INSERT INTO lists (key, item, created_at) VALUES ($1, $2::jsonb, $3)",
|
|
253
|
+
[key, JSON.stringify(item), /* @__PURE__ */ new Date()]
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return mutation.result;
|
|
257
|
+
});
|
|
258
|
+
} catch (error) {
|
|
259
|
+
throw import_core.StorageError.writeFailed(
|
|
260
|
+
"updateList",
|
|
261
|
+
error instanceof Error ? error.message : String(error),
|
|
262
|
+
{ key }
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
238
266
|
async getRange(key, start, count) {
|
|
239
267
|
return await this.withRetry("getRange", async (client) => {
|
|
240
268
|
const result = await client.query(
|
|
241
|
-
"SELECT item FROM lists WHERE key = $1 ORDER BY created_at ASC LIMIT $2 OFFSET $3",
|
|
269
|
+
"SELECT item FROM lists WHERE key = $1 ORDER BY created_at ASC, id ASC LIMIT $2 OFFSET $3",
|
|
242
270
|
[key, count, start]
|
|
243
271
|
);
|
|
244
272
|
return result.rows.map((row) => row.item);
|
|
@@ -339,6 +367,31 @@ class PostgresStore {
|
|
|
339
367
|
}
|
|
340
368
|
});
|
|
341
369
|
}
|
|
370
|
+
async transactionOnce(callback) {
|
|
371
|
+
this.checkConnection();
|
|
372
|
+
const client = await this.pool.connect();
|
|
373
|
+
let began = false;
|
|
374
|
+
let releaseWithError = false;
|
|
375
|
+
try {
|
|
376
|
+
await client.query("BEGIN");
|
|
377
|
+
began = true;
|
|
378
|
+
const result = await callback(client);
|
|
379
|
+
await client.query("COMMIT");
|
|
380
|
+
return result;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
releaseWithError = this.isConnectionError(error);
|
|
383
|
+
if (began && !releaseWithError) {
|
|
384
|
+
try {
|
|
385
|
+
await client.query("ROLLBACK");
|
|
386
|
+
} catch {
|
|
387
|
+
releaseWithError = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
throw error;
|
|
391
|
+
} finally {
|
|
392
|
+
client.release(releaseWithError);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
342
395
|
async getStats() {
|
|
343
396
|
return await this.withRetry("getStats", async (client) => {
|
|
344
397
|
const kvResult = await client.query("SELECT COUNT(*) as count FROM kv");
|
|
@@ -30,6 +30,10 @@ export declare class PostgresStore implements Database {
|
|
|
30
30
|
delete(key: string): Promise<void>;
|
|
31
31
|
list(prefix: string): Promise<string[]>;
|
|
32
32
|
append<T>(key: string, item: T): Promise<void>;
|
|
33
|
+
updateList<T, R>(key: string, updater: (items: T[]) => {
|
|
34
|
+
items: T[];
|
|
35
|
+
result: R;
|
|
36
|
+
}): Promise<R>;
|
|
33
37
|
getRange<T>(key: string, start: number, count: number): Promise<T[]>;
|
|
34
38
|
private createTables;
|
|
35
39
|
private checkConnection;
|
|
@@ -49,6 +53,7 @@ export declare class PostgresStore implements Database {
|
|
|
49
53
|
* Ensure callback operations are idempotent or use this only for read operations.
|
|
50
54
|
*/
|
|
51
55
|
transaction<T>(callback: (client: PoolClient) => Promise<T>): Promise<T>;
|
|
56
|
+
private transactionOnce;
|
|
52
57
|
getStats(): Promise<{
|
|
53
58
|
kvCount: number;
|
|
54
59
|
listCount: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/database/postgres-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAE,MAAM,IAAI,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;GAIG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAMtC,OAAO,CAAC,MAAM;IALlB,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;gBAGX,MAAM,EAAE,sBAAsB,EACtC,MAAM,EAAE,MAAM;IAKZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA+F9B;;OAEG;YACW,YAAY;IAmBpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAe3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5C,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IA2B/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmBvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9C,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;YAW5D,YAAY;IA4B1B,OAAO,CAAC,eAAe;IAMvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;;OAGG;YACW,SAAS;IA8CvB;;;;OAIG;IACG,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../src/database/postgres-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,UAAU,EAAE,MAAM,IAAI,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;GAIG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAMtC,OAAO,CAAC,MAAM;IALlB,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;gBAGX,MAAM,EAAE,sBAAsB,EACtC,MAAM,EAAE,MAAM;IAKZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA+F9B;;OAEG;YACW,YAAY;IAmBpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAe3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5C,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IA2B/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmBvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9C,UAAU,CAAC,CAAC,EAAE,CAAC,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK;QAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,GACnD,OAAO,CAAC,CAAC,CAAC;IA+BP,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;YAW5D,YAAY;IA4B1B,OAAO,CAAC,eAAe;IAMvB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAiBzB;;;OAGG;YACW,SAAS;IA8CvB;;;;OAIG;IACG,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;YAchE,eAAe;IA2BvB,QAAQ,IAAI,OAAO,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;IAkBI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAKhC"}
|
|
@@ -212,10 +212,38 @@ class PostgresStore {
|
|
|
212
212
|
);
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
+
async updateList(key, updater) {
|
|
216
|
+
try {
|
|
217
|
+
return await this.transactionOnce(async (client) => {
|
|
218
|
+
await client.query("SELECT pg_advisory_xact_lock(hashtext($1::text)::bigint)", [
|
|
219
|
+
key
|
|
220
|
+
]);
|
|
221
|
+
const result = await client.query(
|
|
222
|
+
"SELECT item FROM lists WHERE key = $1 ORDER BY created_at ASC, id ASC",
|
|
223
|
+
[key]
|
|
224
|
+
);
|
|
225
|
+
const mutation = updater(result.rows.map((row) => row.item));
|
|
226
|
+
await client.query("DELETE FROM lists WHERE key = $1", [key]);
|
|
227
|
+
for (const item of mutation.items) {
|
|
228
|
+
await client.query(
|
|
229
|
+
"INSERT INTO lists (key, item, created_at) VALUES ($1, $2::jsonb, $3)",
|
|
230
|
+
[key, JSON.stringify(item), /* @__PURE__ */ new Date()]
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
return mutation.result;
|
|
234
|
+
});
|
|
235
|
+
} catch (error) {
|
|
236
|
+
throw StorageError.writeFailed(
|
|
237
|
+
"updateList",
|
|
238
|
+
error instanceof Error ? error.message : String(error),
|
|
239
|
+
{ key }
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
215
243
|
async getRange(key, start, count) {
|
|
216
244
|
return await this.withRetry("getRange", async (client) => {
|
|
217
245
|
const result = await client.query(
|
|
218
|
-
"SELECT item FROM lists WHERE key = $1 ORDER BY created_at ASC LIMIT $2 OFFSET $3",
|
|
246
|
+
"SELECT item FROM lists WHERE key = $1 ORDER BY created_at ASC, id ASC LIMIT $2 OFFSET $3",
|
|
219
247
|
[key, count, start]
|
|
220
248
|
);
|
|
221
249
|
return result.rows.map((row) => row.item);
|
|
@@ -316,6 +344,31 @@ class PostgresStore {
|
|
|
316
344
|
}
|
|
317
345
|
});
|
|
318
346
|
}
|
|
347
|
+
async transactionOnce(callback) {
|
|
348
|
+
this.checkConnection();
|
|
349
|
+
const client = await this.pool.connect();
|
|
350
|
+
let began = false;
|
|
351
|
+
let releaseWithError = false;
|
|
352
|
+
try {
|
|
353
|
+
await client.query("BEGIN");
|
|
354
|
+
began = true;
|
|
355
|
+
const result = await callback(client);
|
|
356
|
+
await client.query("COMMIT");
|
|
357
|
+
return result;
|
|
358
|
+
} catch (error) {
|
|
359
|
+
releaseWithError = this.isConnectionError(error);
|
|
360
|
+
if (began && !releaseWithError) {
|
|
361
|
+
try {
|
|
362
|
+
await client.query("ROLLBACK");
|
|
363
|
+
} catch {
|
|
364
|
+
releaseWithError = true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
throw error;
|
|
368
|
+
} finally {
|
|
369
|
+
client.release(releaseWithError);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
319
372
|
async getStats() {
|
|
320
373
|
return await this.withRetry("getStats", async (client) => {
|
|
321
374
|
const kvResult = await client.query("SELECT COUNT(*) as count FROM kv");
|
|
@@ -242,6 +242,33 @@ class SQLiteStore {
|
|
|
242
242
|
);
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
|
+
async updateList(key, updater) {
|
|
246
|
+
this.checkConnection();
|
|
247
|
+
try {
|
|
248
|
+
const transaction = this.db.transaction(() => {
|
|
249
|
+
const rows = this.db.prepare("SELECT value FROM list_store WHERE key = ? ORDER BY sequence ASC").all(key);
|
|
250
|
+
const current = rows.map((row) => JSON.parse(row.value));
|
|
251
|
+
const mutation = updater(current);
|
|
252
|
+
this.db.prepare("DELETE FROM list_store WHERE key = ?").run(key);
|
|
253
|
+
if (mutation.items.length > 0) {
|
|
254
|
+
const insert = this.db.prepare(
|
|
255
|
+
"INSERT INTO list_store (key, value, sequence) VALUES (?, ?, ?)"
|
|
256
|
+
);
|
|
257
|
+
for (const [index, item] of mutation.items.entries()) {
|
|
258
|
+
insert.run(key, JSON.stringify(item), index + 1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return mutation.result;
|
|
262
|
+
});
|
|
263
|
+
return transaction();
|
|
264
|
+
} catch (error) {
|
|
265
|
+
throw import_core.StorageError.writeFailed(
|
|
266
|
+
"updateList",
|
|
267
|
+
error instanceof Error ? error.message : String(error),
|
|
268
|
+
{ key }
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
245
272
|
async getRange(key, start, count) {
|
|
246
273
|
this.checkConnection();
|
|
247
274
|
try {
|
|
@@ -25,6 +25,10 @@ export declare class SQLiteStore implements Database {
|
|
|
25
25
|
delete(key: string): Promise<void>;
|
|
26
26
|
list(prefix: string): Promise<string[]>;
|
|
27
27
|
append<T>(key: string, item: T): Promise<void>;
|
|
28
|
+
updateList<T, R>(key: string, updater: (items: T[]) => {
|
|
29
|
+
items: T[];
|
|
30
|
+
result: R;
|
|
31
|
+
}): Promise<R>;
|
|
28
32
|
getRange<T>(key: string, start: number, count: number): Promise<T[]>;
|
|
29
33
|
private checkConnection;
|
|
30
34
|
vacuum(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/database/sqlite-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKzD;;;GAGG;AACH,qBAAa,WAAY,YAAW,QAAQ;IACxC,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM;IAOxD,OAAO,CAAC,gBAAgB;IA8ClB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ExB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAgB3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5C,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAwB/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9C,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAqB1E,OAAO,CAAC,eAAe;IAOjB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvB,QAAQ,IAAI,OAAO,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;CAqBL"}
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/database/sqlite-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKzD;;;GAGG;AACH,qBAAa,WAAY,YAAW,QAAQ;IACxC,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM;IAOxD,OAAO,CAAC,gBAAgB;IA8ClB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2ExB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAgB3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5C,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAwB/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BvC,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9C,UAAU,CAAC,CAAC,EAAE,CAAC,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK;QAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,GACnD,OAAO,CAAC,CAAC,CAAC;IAiCP,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAqB1E,OAAO,CAAC,eAAe;IAOjB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAKvB,QAAQ,IAAI,OAAO,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;CAqBL"}
|
|
@@ -209,6 +209,33 @@ class SQLiteStore {
|
|
|
209
209
|
);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
+
async updateList(key, updater) {
|
|
213
|
+
this.checkConnection();
|
|
214
|
+
try {
|
|
215
|
+
const transaction = this.db.transaction(() => {
|
|
216
|
+
const rows = this.db.prepare("SELECT value FROM list_store WHERE key = ? ORDER BY sequence ASC").all(key);
|
|
217
|
+
const current = rows.map((row) => JSON.parse(row.value));
|
|
218
|
+
const mutation = updater(current);
|
|
219
|
+
this.db.prepare("DELETE FROM list_store WHERE key = ?").run(key);
|
|
220
|
+
if (mutation.items.length > 0) {
|
|
221
|
+
const insert = this.db.prepare(
|
|
222
|
+
"INSERT INTO list_store (key, value, sequence) VALUES (?, ?, ?)"
|
|
223
|
+
);
|
|
224
|
+
for (const [index, item] of mutation.items.entries()) {
|
|
225
|
+
insert.run(key, JSON.stringify(item), index + 1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return mutation.result;
|
|
229
|
+
});
|
|
230
|
+
return transaction();
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw StorageError.writeFailed(
|
|
233
|
+
"updateList",
|
|
234
|
+
error instanceof Error ? error.message : String(error),
|
|
235
|
+
{ key }
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
212
239
|
async getRange(key, start, count) {
|
|
213
240
|
this.checkConnection();
|
|
214
241
|
try {
|
package/dist/index.d.cts
CHANGED
|
@@ -79,6 +79,10 @@ declare class MemoryDatabaseStore implements Database {
|
|
|
79
79
|
delete(key: string): Promise<void>;
|
|
80
80
|
list(prefix: string): Promise<string[]>;
|
|
81
81
|
append<T>(key: string, item: T): Promise<void>;
|
|
82
|
+
updateList<T, R>(key: string, updater: (items: T[]) => {
|
|
83
|
+
items: T[];
|
|
84
|
+
result: R;
|
|
85
|
+
}): Promise<R>;
|
|
82
86
|
getRange<T>(key: string, start: number, count: number): Promise<T[]>;
|
|
83
87
|
private checkConnection;
|
|
84
88
|
clear(): Promise<void>;
|
|
@@ -112,6 +116,10 @@ declare class SQLiteStore implements Database {
|
|
|
112
116
|
delete(key: string): Promise<void>;
|
|
113
117
|
list(prefix: string): Promise<string[]>;
|
|
114
118
|
append<T>(key: string, item: T): Promise<void>;
|
|
119
|
+
updateList<T, R>(key: string, updater: (items: T[]) => {
|
|
120
|
+
items: T[];
|
|
121
|
+
result: R;
|
|
122
|
+
}): Promise<R>;
|
|
115
123
|
getRange<T>(key: string, start: number, count: number): Promise<T[]>;
|
|
116
124
|
private checkConnection;
|
|
117
125
|
vacuum(): Promise<void>;
|
|
@@ -150,6 +158,10 @@ declare class PostgresStore implements Database {
|
|
|
150
158
|
delete(key: string): Promise<void>;
|
|
151
159
|
list(prefix: string): Promise<string[]>;
|
|
152
160
|
append<T>(key: string, item: T): Promise<void>;
|
|
161
|
+
updateList<T, R>(key: string, updater: (items: T[]) => {
|
|
162
|
+
items: T[];
|
|
163
|
+
result: R;
|
|
164
|
+
}): Promise<R>;
|
|
153
165
|
getRange<T>(key: string, start: number, count: number): Promise<T[]>;
|
|
154
166
|
private createTables;
|
|
155
167
|
private checkConnection;
|
|
@@ -169,6 +181,7 @@ declare class PostgresStore implements Database {
|
|
|
169
181
|
* Ensure callback operations are idempotent or use this only for read operations.
|
|
170
182
|
*/
|
|
171
183
|
transaction<T>(callback: (client: PoolClient) => Promise<T>): Promise<T>;
|
|
184
|
+
private transactionOnce;
|
|
172
185
|
getStats(): Promise<{
|
|
173
186
|
kvCount: number;
|
|
174
187
|
listCount: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexto/storage",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "Storage backends and config schemas for Dexto agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"zod": "^4.3.6",
|
|
24
|
-
"@dexto/core": "1.
|
|
24
|
+
"@dexto/core": "1.9.1"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"better-sqlite3": "^11.10.0",
|