@fedify/sqlite 2.0.0-dev.1690 → 2.0.0-dev.170

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright 2024–2025 Hong Minhee
3
+ Copyright 2024–2026 Hong Minhee
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -6,12 +6,12 @@
6
6
 
7
7
  This package provides a SQLite-based [`KvStore`] implementation for [Fedify].
8
8
 
9
- [JSR]: https://jsr.io/@fedify/sqlite
10
9
  [JSR badge]: https://jsr.io/badges/@fedify/sqlite
11
- [npm]: https://www.npmjs.com/package/@fedify/sqlite
10
+ [JSR]: https://jsr.io/@fedify/sqlite
12
11
  [npm badge]: https://img.shields.io/npm/v/@fedify/sqlite?logo=npm
13
- [Fedify]: https://fedify.dev/
12
+ [npm]: https://www.npmjs.com/package/@fedify/sqlite
14
13
  [`KvStore`]: https://jsr.io/@fedify/fedify/doc/federation/~/KvStore
14
+ [Fedify]: https://fedify.dev/
15
15
 
16
16
 
17
17
  Usage
package/deno.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "@fedify/sqlite",
3
- "version": "2.0.0-dev.1690+304896b8",
3
+ "version": "2.0.0-dev.170+a03fedcd",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
7
7
  "./kv": "./src/kv.ts"
8
8
  },
9
9
  "imports": {
10
- "@fedify/sqlite": "./src/mod.ts",
11
- "@fedify/sqlite/": "./src/",
12
10
  "#sqlite": "./src/sqlite.node.ts"
13
11
  },
14
12
  "exclude": [
@@ -18,6 +16,7 @@
18
16
  ],
19
17
  "publish": {
20
18
  "exclude": [
19
+ "**/*.test.ts",
21
20
  "!dist/"
22
21
  ]
23
22
  },
package/dist/kv.cjs CHANGED
@@ -3,7 +3,6 @@
3
3
 
4
4
  const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
5
5
  const __sqlite = require_rolldown_runtime.__toESM(require("#sqlite"));
6
- const __js_temporal_polyfill = require_rolldown_runtime.__toESM(require("@js-temporal/polyfill"));
7
6
  const __logtape_logtape = require_rolldown_runtime.__toESM(require("@logtape/logtape"));
8
7
  const es_toolkit = require_rolldown_runtime.__toESM(require("es-toolkit"));
9
8
 
@@ -54,10 +53,10 @@ var SqliteKvStore = class SqliteKvStore {
54
53
  async get(key) {
55
54
  this.initialize();
56
55
  const encodedKey = this.#encodeKey(key);
57
- const now = __js_temporal_polyfill.Temporal.Now.instant().epochMilliseconds;
56
+ const now = Temporal.Now.instant().epochMilliseconds;
58
57
  const result = this.#db.prepare(`
59
- SELECT value
60
- FROM "${this.#tableName}"
58
+ SELECT value
59
+ FROM "${this.#tableName}"
61
60
  WHERE key = ? AND (expires IS NULL OR expires > ?)
62
61
  `).get(encodedKey, now);
63
62
  if (!result) return void 0;
@@ -71,7 +70,7 @@ var SqliteKvStore = class SqliteKvStore {
71
70
  if (value === void 0) return;
72
71
  const encodedKey = this.#encodeKey(key);
73
72
  const encodedValue = this.#encodeValue(value);
74
- const now = __js_temporal_polyfill.Temporal.Now.instant().epochMilliseconds;
73
+ const now = Temporal.Now.instant().epochMilliseconds;
75
74
  const expiresAt = options?.ttl !== void 0 ? now + options.ttl.total({ unit: "milliseconds" }) : null;
76
75
  this.#db.prepare(`INSERT INTO "${this.#tableName}" (key, value, created, expires)
77
76
  VALUES (?, ?, ?, ?)
@@ -99,13 +98,13 @@ var SqliteKvStore = class SqliteKvStore {
99
98
  async cas(key, expectedValue, newValue, options) {
100
99
  this.initialize();
101
100
  const encodedKey = this.#encodeKey(key);
102
- const now = __js_temporal_polyfill.Temporal.Now.instant().epochMilliseconds;
101
+ const now = Temporal.Now.instant().epochMilliseconds;
103
102
  const expiresAt = options?.ttl !== void 0 ? now + options.ttl.total({ unit: "milliseconds" }) : null;
104
103
  try {
105
104
  this.#db.exec("BEGIN IMMEDIATE");
106
105
  const currentResult = this.#db.prepare(`
107
- SELECT value
108
- FROM "${this.#tableName}"
106
+ SELECT value
107
+ FROM "${this.#tableName}"
109
108
  WHERE key = ? AND (expires IS NULL OR expires > ?)
110
109
  `).get(encodedKey, now);
111
110
  const currentValue = currentResult === void 0 ? void 0 : this.#decodeValue(currentResult.value);
@@ -135,6 +134,36 @@ var SqliteKvStore = class SqliteKvStore {
135
134
  }
136
135
  }
137
136
  /**
137
+ * {@inheritDoc KvStore.list}
138
+ * @since 1.10.0
139
+ */
140
+ async *list(prefix) {
141
+ this.initialize();
142
+ const now = Temporal.Now.instant().epochMilliseconds;
143
+ let results;
144
+ if (prefix == null || prefix.length === 0) results = this.#db.prepare(`
145
+ SELECT key, value
146
+ FROM "${this.#tableName}"
147
+ WHERE expires IS NULL OR expires > ?
148
+ ORDER BY key
149
+ `).all(now);
150
+ else {
151
+ const pattern = JSON.stringify(prefix).slice(0, -1) + ",%";
152
+ const exactKey = JSON.stringify(prefix);
153
+ results = this.#db.prepare(`
154
+ SELECT key, value
155
+ FROM "${this.#tableName}"
156
+ WHERE (key LIKE ? ESCAPE '\\' OR key = ?)
157
+ AND (expires IS NULL OR expires > ?)
158
+ ORDER BY key
159
+ `).all(pattern, exactKey, now);
160
+ }
161
+ for (const row of results) yield {
162
+ key: this.#decodeKey(row.key),
163
+ value: this.#decodeValue(row.value)
164
+ };
165
+ }
166
+ /**
138
167
  * Creates the table used by the key–value store if it does not already exist.
139
168
  * Does nothing if the table already exists.
140
169
  */
@@ -150,14 +179,14 @@ var SqliteKvStore = class SqliteKvStore {
150
179
  )
151
180
  `);
152
181
  this.#db.exec(`
153
- CREATE INDEX IF NOT EXISTS "idx_${this.#tableName}_expires"
182
+ CREATE INDEX IF NOT EXISTS "idx_${this.#tableName}_expires"
154
183
  ON "${this.#tableName}" (expires)
155
184
  `);
156
185
  this.#initialized = true;
157
186
  logger.debug("Initialized the key–value store table {tableName}.", { tableName: this.#tableName });
158
187
  }
159
188
  #expire() {
160
- const now = __js_temporal_polyfill.Temporal.Now.instant().epochMilliseconds;
189
+ const now = Temporal.Now.instant().epochMilliseconds;
161
190
  this.#db.prepare(`
162
191
  DELETE FROM "${this.#tableName}"
163
192
  WHERE expires IS NOT NULL AND expires <= ?
package/dist/kv.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DatabaseSync } from "./dist/sqlite.node.cjs";
2
- import { KvKey, KvStore, KvStoreSetOptions } from "@fedify/fedify";
2
+ import { KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions } from "@fedify/fedify";
3
3
 
4
4
  //#region src/kv.d.ts
5
5
  /**
@@ -12,12 +12,12 @@ interface SqliteKvStoreOptions {
12
12
  * `"fedify_kv"` by default.
13
13
  * @default `"fedify_kv"`
14
14
  */
15
- tableName?: string;
15
+ readonly tableName?: string;
16
16
  /**
17
17
  * Whether the table has been initialized. `false` by default.
18
18
  * @default `false`
19
19
  */
20
- initialized?: boolean;
20
+ readonly initialized?: boolean;
21
21
  }
22
22
  /**
23
23
  * A key–value store that uses SQLite as the underlying storage.
@@ -61,6 +61,11 @@ declare class SqliteKvStore implements KvStore {
61
61
  * {@inheritDoc KvStore.cas}
62
62
  */
63
63
  cas(key: KvKey, expectedValue: unknown, newValue: unknown, options?: KvStoreSetOptions): Promise<boolean>;
64
+ /**
65
+ * {@inheritDoc KvStore.list}
66
+ * @since 1.10.0
67
+ */
68
+ list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>;
64
69
  /**
65
70
  * Creates the table used by the key–value store if it does not already exist.
66
71
  * Does nothing if the table already exists.
package/dist/kv.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { PlatformDatabase } from "#sqlite";
3
- import { KvKey, KvStore, KvStoreSetOptions } from "@fedify/fedify";
3
+ import { KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions } from "@fedify/fedify";
4
4
 
5
5
  //#region src/kv.d.ts
6
6
  /**
@@ -13,12 +13,12 @@ interface SqliteKvStoreOptions {
13
13
  * `"fedify_kv"` by default.
14
14
  * @default `"fedify_kv"`
15
15
  */
16
- tableName?: string;
16
+ readonly tableName?: string;
17
17
  /**
18
18
  * Whether the table has been initialized. `false` by default.
19
19
  * @default `false`
20
20
  */
21
- initialized?: boolean;
21
+ readonly initialized?: boolean;
22
22
  }
23
23
  /**
24
24
  * A key–value store that uses SQLite as the underlying storage.
@@ -62,6 +62,11 @@ declare class SqliteKvStore implements KvStore {
62
62
  * {@inheritDoc KvStore.cas}
63
63
  */
64
64
  cas(key: KvKey, expectedValue: unknown, newValue: unknown, options?: KvStoreSetOptions): Promise<boolean>;
65
+ /**
66
+ * {@inheritDoc KvStore.list}
67
+ * @since 1.10.0
68
+ */
69
+ list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>;
65
70
  /**
66
71
  * Creates the table used by the key–value store if it does not already exist.
67
72
  * Does nothing if the table already exists.
package/dist/kv.js CHANGED
@@ -2,7 +2,6 @@
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
 
4
4
  import { SqliteDatabase } from "#sqlite";
5
- import { Temporal } from "@js-temporal/polyfill";
6
5
  import { getLogger } from "@logtape/logtape";
7
6
  import { isEqual } from "es-toolkit";
8
7
 
@@ -55,8 +54,8 @@ var SqliteKvStore = class SqliteKvStore {
55
54
  const encodedKey = this.#encodeKey(key);
56
55
  const now = Temporal.Now.instant().epochMilliseconds;
57
56
  const result = this.#db.prepare(`
58
- SELECT value
59
- FROM "${this.#tableName}"
57
+ SELECT value
58
+ FROM "${this.#tableName}"
60
59
  WHERE key = ? AND (expires IS NULL OR expires > ?)
61
60
  `).get(encodedKey, now);
62
61
  if (!result) return void 0;
@@ -103,8 +102,8 @@ var SqliteKvStore = class SqliteKvStore {
103
102
  try {
104
103
  this.#db.exec("BEGIN IMMEDIATE");
105
104
  const currentResult = this.#db.prepare(`
106
- SELECT value
107
- FROM "${this.#tableName}"
105
+ SELECT value
106
+ FROM "${this.#tableName}"
108
107
  WHERE key = ? AND (expires IS NULL OR expires > ?)
109
108
  `).get(encodedKey, now);
110
109
  const currentValue = currentResult === void 0 ? void 0 : this.#decodeValue(currentResult.value);
@@ -134,6 +133,36 @@ var SqliteKvStore = class SqliteKvStore {
134
133
  }
135
134
  }
136
135
  /**
136
+ * {@inheritDoc KvStore.list}
137
+ * @since 1.10.0
138
+ */
139
+ async *list(prefix) {
140
+ this.initialize();
141
+ const now = Temporal.Now.instant().epochMilliseconds;
142
+ let results;
143
+ if (prefix == null || prefix.length === 0) results = this.#db.prepare(`
144
+ SELECT key, value
145
+ FROM "${this.#tableName}"
146
+ WHERE expires IS NULL OR expires > ?
147
+ ORDER BY key
148
+ `).all(now);
149
+ else {
150
+ const pattern = JSON.stringify(prefix).slice(0, -1) + ",%";
151
+ const exactKey = JSON.stringify(prefix);
152
+ results = this.#db.prepare(`
153
+ SELECT key, value
154
+ FROM "${this.#tableName}"
155
+ WHERE (key LIKE ? ESCAPE '\\' OR key = ?)
156
+ AND (expires IS NULL OR expires > ?)
157
+ ORDER BY key
158
+ `).all(pattern, exactKey, now);
159
+ }
160
+ for (const row of results) yield {
161
+ key: this.#decodeKey(row.key),
162
+ value: this.#decodeValue(row.value)
163
+ };
164
+ }
165
+ /**
137
166
  * Creates the table used by the key–value store if it does not already exist.
138
167
  * Does nothing if the table already exists.
139
168
  */
@@ -149,7 +178,7 @@ var SqliteKvStore = class SqliteKvStore {
149
178
  )
150
179
  `);
151
180
  this.#db.exec(`
152
- CREATE INDEX IF NOT EXISTS "idx_${this.#tableName}_expires"
181
+ CREATE INDEX IF NOT EXISTS "idx_${this.#tableName}_expires"
153
182
  ON "${this.#tableName}" (expires)
154
183
  `);
155
184
  this.#initialized = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/sqlite",
3
- "version": "2.0.0-dev.1690+304896b8",
3
+ "version": "2.0.0-dev.170+a03fedcd",
4
4
  "description": "SQLite drivers for Fedify",
5
5
  "keywords": [
6
6
  "fedify",
@@ -58,16 +58,16 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@js-temporal/polyfill": "^0.5.1",
61
- "@logtape/logtape": "^1.0.0",
61
+ "@logtape/logtape": "^2.0.0",
62
62
  "es-toolkit": "^1.31.0"
63
63
  },
64
64
  "peerDependencies": {
65
- "@fedify/fedify": "^2.0.0-dev.1690+304896b8"
65
+ "@fedify/fedify": "^2.0.0-dev.170+a03fedcd"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@std/async": "npm:@jsr/std__async@^1.0.13",
69
69
  "tsdown": "^0.12.9",
70
- "typescript": "^5.9.2"
70
+ "typescript": "^5.9.3"
71
71
  },
72
72
  "scripts": {
73
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 { KvKey, KvStore, KvStoreSetOptions } from "@fedify/fedify";
3
- import { Temporal } from "@js-temporal/polyfill";
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";
@@ -17,13 +21,13 @@ export interface SqliteKvStoreOptions {
17
21
  * `"fedify_kv"` by default.
18
22
  * @default `"fedify_kv"`
19
23
  */
20
- tableName?: string;
24
+ readonly tableName?: string;
21
25
 
22
26
  /**
23
27
  * Whether the table has been initialized. `false` by default.
24
28
  * @default `false`
25
29
  */
26
- initialized?: boolean;
30
+ readonly initialized?: boolean;
27
31
  }
28
32
 
29
33
  /**
@@ -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