@fedify/sqlite 2.0.0-dev.1604 → 2.0.0-dev.161
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/LICENSE +1 -1
- package/README.md +3 -3
- package/deno.json +2 -3
- package/dist/_virtual/{rolldown_runtime.js → rolldown_runtime.cjs} +9 -6
- package/dist/adapter.d.cts +45 -0
- package/dist/dist/sqlite.node.d.cts +2 -0
- package/dist/kv.cjs +218 -0
- package/dist/kv.d.cts +81 -0
- package/dist/kv.d.ts +6 -1
- package/dist/kv.js +41 -12
- package/dist/mod.cjs +6 -0
- package/dist/mod.d.cts +2 -0
- package/dist/mod.js +2 -2
- package/dist/sqlite.bun.cjs +42 -0
- package/dist/sqlite.bun.d.cts +23 -0
- package/dist/sqlite.bun.js +2 -2
- package/dist/sqlite.node.cjs +44 -0
- package/dist/sqlite.node.d.cts +23 -0
- package/dist/sqlite.node.js +2 -2
- package/package.json +18 -8
- package/src/kv.test.ts +96 -3
- package/src/kv.ts +56 -7
- package/tsdown.config.ts +12 -4
- package/dist/node_modules/.pnpm/@js-temporal_polyfill@0.5.1/node_modules/@js-temporal/polyfill/dist/index.esm.js +0 -5795
- package/dist/node_modules/.pnpm/jsbi@4.3.2/node_modules/jsbi/dist/jsbi-cjs.js +0 -1139
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
3
|
+
|
|
4
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
5
|
+
const node_sqlite = require_rolldown_runtime.__toESM(require("node:sqlite"));
|
|
6
|
+
|
|
7
|
+
//#region src/sqlite.node.ts
|
|
8
|
+
var SqliteDatabase = class {
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.db = db;
|
|
11
|
+
}
|
|
12
|
+
prepare(sql) {
|
|
13
|
+
return new SqliteStatement(this.db.prepare(sql));
|
|
14
|
+
}
|
|
15
|
+
exec(sql) {
|
|
16
|
+
this.db.exec(sql);
|
|
17
|
+
}
|
|
18
|
+
close() {
|
|
19
|
+
this.db.close();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var SqliteStatement = class {
|
|
23
|
+
constructor(stmt) {
|
|
24
|
+
this.stmt = stmt;
|
|
25
|
+
}
|
|
26
|
+
run(...params) {
|
|
27
|
+
const result = this.stmt.run(...params);
|
|
28
|
+
return {
|
|
29
|
+
changes: Number(result.changes),
|
|
30
|
+
lastInsertRowid: Number(result.lastInsertRowid)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
get(...params) {
|
|
34
|
+
return this.stmt.get(...params);
|
|
35
|
+
}
|
|
36
|
+
all(...params) {
|
|
37
|
+
return this.stmt.all(...params);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
exports.PlatformDatabase = node_sqlite.DatabaseSync;
|
|
43
|
+
exports.SqliteDatabase = SqliteDatabase;
|
|
44
|
+
exports.SqliteStatement = SqliteStatement;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SqliteDatabaseAdapter, SqliteStatementAdapter } from "./adapter.cjs";
|
|
2
|
+
import { DatabaseSync, StatementSync } from "node:sqlite";
|
|
3
|
+
|
|
4
|
+
//#region src/sqlite.node.d.ts
|
|
5
|
+
declare class SqliteDatabase implements SqliteDatabaseAdapter {
|
|
6
|
+
private readonly db;
|
|
7
|
+
constructor(db: DatabaseSync);
|
|
8
|
+
prepare(sql: string): SqliteStatementAdapter;
|
|
9
|
+
exec(sql: string): void;
|
|
10
|
+
close(): void;
|
|
11
|
+
}
|
|
12
|
+
declare class SqliteStatement implements SqliteStatementAdapter {
|
|
13
|
+
private readonly stmt;
|
|
14
|
+
constructor(stmt: StatementSync);
|
|
15
|
+
run(...params: unknown[]): {
|
|
16
|
+
changes: number;
|
|
17
|
+
lastInsertRowid: number;
|
|
18
|
+
};
|
|
19
|
+
get(...params: unknown[]): unknown | undefined;
|
|
20
|
+
all(...params: unknown[]): unknown[];
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { DatabaseSync as PlatformDatabase, StatementSync as PlatformStatement, SqliteDatabase, SqliteStatement };
|
package/dist/sqlite.node.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/sqlite",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.161+b505ad7a",
|
|
4
4
|
"description": "SQLite drivers for Fedify",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fedify",
|
|
@@ -22,18 +22,28 @@
|
|
|
22
22
|
"https://github.com/sponsors/dahlia"
|
|
23
23
|
],
|
|
24
24
|
"type": "module",
|
|
25
|
-
"main": "./dist/mod.
|
|
25
|
+
"main": "./dist/mod.cjs",
|
|
26
26
|
"module": "./dist/mod.js",
|
|
27
27
|
"types": "./dist/mod.d.ts",
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
30
|
-
"types":
|
|
30
|
+
"types": {
|
|
31
|
+
"import": "./dist/mod.d.ts",
|
|
32
|
+
"require": "./dist/mod.d.cts",
|
|
33
|
+
"default": "./dist/mod.d.ts"
|
|
34
|
+
},
|
|
31
35
|
"import": "./dist/mod.js",
|
|
36
|
+
"require": "./dist/mod.cjs",
|
|
32
37
|
"default": "./dist/mod.js"
|
|
33
38
|
},
|
|
34
39
|
"./kv": {
|
|
35
|
-
"types":
|
|
40
|
+
"types": {
|
|
41
|
+
"import": "./dist/kv.d.ts",
|
|
42
|
+
"require": "./dist/kv.d.cts",
|
|
43
|
+
"default": "./dist/kv.d.ts"
|
|
44
|
+
},
|
|
36
45
|
"import": "./dist/kv.js",
|
|
46
|
+
"require": "./dist/kv.cjs",
|
|
37
47
|
"default": "./dist/kv.js"
|
|
38
48
|
},
|
|
39
49
|
"./package.json": "./package.json"
|
|
@@ -47,17 +57,17 @@
|
|
|
47
57
|
}
|
|
48
58
|
},
|
|
49
59
|
"dependencies": {
|
|
50
|
-
"@
|
|
60
|
+
"@js-temporal/polyfill": "^0.5.1",
|
|
61
|
+
"@logtape/logtape": "^1.3.5",
|
|
51
62
|
"es-toolkit": "^1.31.0"
|
|
52
63
|
},
|
|
53
64
|
"peerDependencies": {
|
|
54
|
-
"@fedify/fedify": "^2.0.0-dev.
|
|
65
|
+
"@fedify/fedify": "^2.0.0-dev.161+b505ad7a"
|
|
55
66
|
},
|
|
56
67
|
"devDependencies": {
|
|
57
|
-
"@js-temporal/polyfill": "^0.5.1",
|
|
58
68
|
"@std/async": "npm:@jsr/std__async@^1.0.13",
|
|
59
69
|
"tsdown": "^0.12.9",
|
|
60
|
-
"typescript": "^5.9.
|
|
70
|
+
"typescript": "^5.9.3"
|
|
61
71
|
},
|
|
62
72
|
"scripts": {
|
|
63
73
|
"build": "tsdown",
|
package/src/kv.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { PlatformDatabase } from "#sqlite";
|
|
2
|
+
import { SqliteKvStore } from "@fedify/sqlite/kv";
|
|
2
3
|
import * as temporal from "@js-temporal/polyfill";
|
|
3
4
|
import { delay } from "@std/async/delay";
|
|
4
5
|
import assert from "node:assert/strict";
|
|
5
6
|
import { test } from "node:test";
|
|
6
|
-
import { SqliteKvStore } from "./kv.ts";
|
|
7
7
|
|
|
8
8
|
let Temporal: typeof temporal.Temporal;
|
|
9
9
|
if ("Temporal" in globalThis) {
|
|
@@ -31,7 +31,7 @@ test("SqliteKvStore.initialize()", async () => {
|
|
|
31
31
|
try {
|
|
32
32
|
await store.initialize();
|
|
33
33
|
const result = await db.prepare(`
|
|
34
|
-
SELECT name FROM sqlite_master
|
|
34
|
+
SELECT name FROM sqlite_master
|
|
35
35
|
WHERE type='table' AND name=?
|
|
36
36
|
`).get(tableName);
|
|
37
37
|
assert(result !== undefined);
|
|
@@ -180,7 +180,7 @@ test("SqliteKvStore.drop()", async () => {
|
|
|
180
180
|
try {
|
|
181
181
|
await store.drop();
|
|
182
182
|
const result = await db.prepare(`
|
|
183
|
-
SELECT name FROM sqlite_master
|
|
183
|
+
SELECT name FROM sqlite_master
|
|
184
184
|
WHERE type='table' AND name=?
|
|
185
185
|
`).get(tableName);
|
|
186
186
|
// Bun returns null, Node returns undefined
|
|
@@ -310,3 +310,96 @@ test("SqliteKvStore.set() - preserves created timestamp on update", async () =>
|
|
|
310
310
|
await db.close();
|
|
311
311
|
}
|
|
312
312
|
});
|
|
313
|
+
|
|
314
|
+
test("SqliteKvStore.list()", async () => {
|
|
315
|
+
const { db, store } = getStore();
|
|
316
|
+
try {
|
|
317
|
+
await store.set(["prefix", "a"], "value-a");
|
|
318
|
+
await store.set(["prefix", "b"], "value-b");
|
|
319
|
+
await store.set(["prefix", "nested", "c"], "value-c");
|
|
320
|
+
await store.set(["other", "x"], "value-x");
|
|
321
|
+
|
|
322
|
+
const entries: { key: readonly string[]; value: unknown }[] = [];
|
|
323
|
+
for await (const entry of store.list(["prefix"])) {
|
|
324
|
+
entries.push({ key: entry.key, value: entry.value });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
assert.strictEqual(entries.length, 3);
|
|
328
|
+
assert(
|
|
329
|
+
entries.some((e) => e.key[1] === "a" && e.value === "value-a"),
|
|
330
|
+
);
|
|
331
|
+
assert(entries.some((e) => e.key[1] === "b"));
|
|
332
|
+
assert(entries.some((e) => e.key[1] === "nested"));
|
|
333
|
+
} finally {
|
|
334
|
+
await store.drop();
|
|
335
|
+
await db.close();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("SqliteKvStore.list() - excludes expired", async () => {
|
|
340
|
+
const { db, tableName, store } = getStore();
|
|
341
|
+
try {
|
|
342
|
+
await store.initialize();
|
|
343
|
+
const now = Temporal.Now.instant().epochMilliseconds;
|
|
344
|
+
|
|
345
|
+
// Insert expired entry directly
|
|
346
|
+
db.prepare(`
|
|
347
|
+
INSERT INTO "${tableName}" (key, value, created, expires)
|
|
348
|
+
VALUES (?, ?, ?, ?)
|
|
349
|
+
`).run(
|
|
350
|
+
JSON.stringify(["list-test", "expired"]),
|
|
351
|
+
JSON.stringify("expired-value"),
|
|
352
|
+
now - 1000,
|
|
353
|
+
now - 500,
|
|
354
|
+
);
|
|
355
|
+
await store.set(["list-test", "valid"], "valid-value");
|
|
356
|
+
|
|
357
|
+
const entries: { key: readonly string[]; value: unknown }[] = [];
|
|
358
|
+
for await (const entry of store.list(["list-test"])) {
|
|
359
|
+
entries.push({ key: entry.key, value: entry.value });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
assert.strictEqual(entries.length, 1);
|
|
363
|
+
assert.deepStrictEqual(entries[0].key, ["list-test", "valid"]);
|
|
364
|
+
} finally {
|
|
365
|
+
await store.drop();
|
|
366
|
+
await db.close();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("SqliteKvStore.list() - single element key", async () => {
|
|
371
|
+
const { db, store } = getStore();
|
|
372
|
+
try {
|
|
373
|
+
await store.set(["a"], "value-a");
|
|
374
|
+
await store.set(["b"], "value-b");
|
|
375
|
+
|
|
376
|
+
const entries: { key: readonly string[]; value: unknown }[] = [];
|
|
377
|
+
for await (const entry of store.list(["a"])) {
|
|
378
|
+
entries.push({ key: entry.key, value: entry.value });
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
assert.strictEqual(entries.length, 1);
|
|
382
|
+
} finally {
|
|
383
|
+
await store.drop();
|
|
384
|
+
await db.close();
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test("SqliteKvStore.list() - empty prefix", async () => {
|
|
389
|
+
const { db, store } = getStore();
|
|
390
|
+
try {
|
|
391
|
+
await store.set(["a"], "value-a");
|
|
392
|
+
await store.set(["b", "c"], "value-bc");
|
|
393
|
+
await store.set(["d", "e", "f"], "value-def");
|
|
394
|
+
|
|
395
|
+
const entries: { key: readonly string[]; value: unknown }[] = [];
|
|
396
|
+
for await (const entry of store.list()) {
|
|
397
|
+
entries.push({ key: entry.key, value: entry.value });
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
assert.strictEqual(entries.length, 3);
|
|
401
|
+
} finally {
|
|
402
|
+
await store.drop();
|
|
403
|
+
await db.close();
|
|
404
|
+
}
|
|
405
|
+
});
|
package/src/kv.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type PlatformDatabase, SqliteDatabase } from "#sqlite";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
KvKey,
|
|
4
|
+
KvStore,
|
|
5
|
+
KvStoreListEntry,
|
|
6
|
+
KvStoreSetOptions,
|
|
7
|
+
} from "@fedify/fedify";
|
|
4
8
|
import { getLogger } from "@logtape/logtape";
|
|
5
9
|
import { isEqual } from "es-toolkit";
|
|
6
10
|
import type { SqliteDatabaseAdapter } from "./adapter.ts";
|
|
@@ -81,8 +85,8 @@ export class SqliteKvStore implements KvStore {
|
|
|
81
85
|
|
|
82
86
|
const result = this.#db
|
|
83
87
|
.prepare(`
|
|
84
|
-
SELECT value
|
|
85
|
-
FROM "${this.#tableName}"
|
|
88
|
+
SELECT value
|
|
89
|
+
FROM "${this.#tableName}"
|
|
86
90
|
WHERE key = ? AND (expires IS NULL OR expires > ?)
|
|
87
91
|
`)
|
|
88
92
|
.get(encodedKey, now);
|
|
@@ -170,8 +174,8 @@ export class SqliteKvStore implements KvStore {
|
|
|
170
174
|
|
|
171
175
|
const currentResult = this.#db
|
|
172
176
|
.prepare(`
|
|
173
|
-
SELECT value
|
|
174
|
-
FROM "${this.#tableName}"
|
|
177
|
+
SELECT value
|
|
178
|
+
FROM "${this.#tableName}"
|
|
175
179
|
WHERE key = ? AND (expires IS NULL OR expires > ?)
|
|
176
180
|
`)
|
|
177
181
|
.get(encodedKey, now) as { value: string } | undefined;
|
|
@@ -213,6 +217,51 @@ export class SqliteKvStore implements KvStore {
|
|
|
213
217
|
}
|
|
214
218
|
}
|
|
215
219
|
|
|
220
|
+
/**
|
|
221
|
+
* {@inheritDoc KvStore.list}
|
|
222
|
+
* @since 1.10.0
|
|
223
|
+
*/
|
|
224
|
+
async *list(prefix?: KvKey): AsyncIterable<KvStoreListEntry> {
|
|
225
|
+
this.initialize();
|
|
226
|
+
|
|
227
|
+
const now = Temporal.Now.instant().epochMilliseconds;
|
|
228
|
+
|
|
229
|
+
let results: { key: string; value: string }[];
|
|
230
|
+
|
|
231
|
+
if (prefix == null || prefix.length === 0) {
|
|
232
|
+
// Empty prefix: return all entries
|
|
233
|
+
results = this.#db
|
|
234
|
+
.prepare(`
|
|
235
|
+
SELECT key, value
|
|
236
|
+
FROM "${this.#tableName}"
|
|
237
|
+
WHERE expires IS NULL OR expires > ?
|
|
238
|
+
ORDER BY key
|
|
239
|
+
`)
|
|
240
|
+
.all(now) as { key: string; value: string }[];
|
|
241
|
+
} else {
|
|
242
|
+
// JSON pattern: '["prefix","' matches keys starting with prefix
|
|
243
|
+
const pattern = JSON.stringify(prefix).slice(0, -1) + ",%";
|
|
244
|
+
const exactKey = JSON.stringify(prefix);
|
|
245
|
+
|
|
246
|
+
results = this.#db
|
|
247
|
+
.prepare(`
|
|
248
|
+
SELECT key, value
|
|
249
|
+
FROM "${this.#tableName}"
|
|
250
|
+
WHERE (key LIKE ? ESCAPE '\\' OR key = ?)
|
|
251
|
+
AND (expires IS NULL OR expires > ?)
|
|
252
|
+
ORDER BY key
|
|
253
|
+
`)
|
|
254
|
+
.all(pattern, exactKey, now) as { key: string; value: string }[];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (const row of results) {
|
|
258
|
+
yield {
|
|
259
|
+
key: this.#decodeKey(row.key),
|
|
260
|
+
value: this.#decodeValue(row.value),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
216
265
|
/**
|
|
217
266
|
* Creates the table used by the key–value store if it does not already exist.
|
|
218
267
|
* Does nothing if the table already exists.
|
|
@@ -236,7 +285,7 @@ export class SqliteKvStore implements KvStore {
|
|
|
236
285
|
`);
|
|
237
286
|
|
|
238
287
|
this.#db.exec(`
|
|
239
|
-
CREATE INDEX IF NOT EXISTS "idx_${this.#tableName}_expires"
|
|
288
|
+
CREATE INDEX IF NOT EXISTS "idx_${this.#tableName}_expires"
|
|
240
289
|
ON "${this.#tableName}" (expires)
|
|
241
290
|
`);
|
|
242
291
|
|
package/tsdown.config.ts
CHANGED
|
@@ -4,6 +4,7 @@ export default defineConfig({
|
|
|
4
4
|
entry: ["src/mod.ts", "src/kv.ts", "src/sqlite.node.ts", "src/sqlite.bun.ts"],
|
|
5
5
|
dts: true,
|
|
6
6
|
unbundle: true,
|
|
7
|
+
format: ["esm", "cjs"],
|
|
7
8
|
platform: "node",
|
|
8
9
|
inputOptions: {
|
|
9
10
|
onwarn(warning, defaultHandler) {
|
|
@@ -16,9 +17,16 @@ export default defineConfig({
|
|
|
16
17
|
defaultHandler(warning);
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
|
-
outputOptions
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
outputOptions(outputOptions, format) {
|
|
21
|
+
if (format === "cjs") {
|
|
22
|
+
outputOptions.intro = `
|
|
23
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
24
|
+
`;
|
|
25
|
+
} else {
|
|
26
|
+
outputOptions.intro = `
|
|
27
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
return outputOptions;
|
|
23
31
|
},
|
|
24
32
|
});
|