@storyshelf/db-sqlite 0.1.3 → 0.3.2
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/README.md +4 -4
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +44 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @storyshelf/db-sqlite
|
|
2
2
|
|
|
3
|
-
The default database adapter for self-hosted StoryShelf: SQLite backed by
|
|
3
|
+
The default database adapter for self-hosted StoryShelf: SQLite backed by node:sqlite (zero-dependency builtin) and Drizzle ORM, in WAL mode. Zero configuration for a single-node deployment.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -22,19 +22,19 @@ import { createShelfRouter } from "@storyshelf/core";
|
|
|
22
22
|
import { createLocalStorage } from "@storyshelf/storage-local";
|
|
23
23
|
|
|
24
24
|
const database = createSqliteDatabase("./data/shelf.db");
|
|
25
|
-
await database.migrate();
|
|
26
25
|
|
|
27
26
|
const storage = createLocalStorage("./data");
|
|
28
27
|
const app = createShelfRouter({ database, storage });
|
|
28
|
+
await app.lifecycle.init();
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## API
|
|
32
32
|
|
|
33
33
|
### `createSqliteDatabase(path: string): DatabaseAdapter`
|
|
34
34
|
|
|
35
|
-
Opens (or creates) a SQLite database at `path` and returns a `DatabaseAdapter`. The connection enables WAL journal mode and a 5s busy timeout.
|
|
35
|
+
Opens (or creates) a SQLite database at `path` and returns a `DatabaseAdapter`. The connection enables WAL journal mode and a 5s busy timeout. Schema migrations run inside the adapter's `lifecycle.init` (via `await app.lifecycle.init()`); teardown via `lifecycle.close` (idempotent).
|
|
36
36
|
|
|
37
|
-
The returned adapter implements every method of the `DatabaseAdapter` interface (`insert`, `update`, `get`, `remove`, `list`, `count`, `all
|
|
37
|
+
The returned adapter implements every method of the `DatabaseAdapter` interface (`insert`, `update`, `get`, `remove`, `list`, `count`, `all`) plus `metadata`/`lifecycle` from the shared `Adapter` base.
|
|
38
38
|
|
|
39
39
|
## How it fits in
|
|
40
40
|
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
4
4
|
//#region src/index.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* Create a SQLite-backed DatabaseAdapter using
|
|
6
|
+
* Create a SQLite-backed DatabaseAdapter using node:sqlite and Drizzle ORM.
|
|
7
7
|
*
|
|
8
8
|
* @param path - Filesystem path to the SQLite database file.
|
|
9
9
|
* @returns A DatabaseAdapter backed by the given SQLite file (WAL mode).
|
package/dist/index.mjs
CHANGED
|
@@ -1,72 +1,69 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import Database from "better-sqlite3";
|
|
7
|
-
import { schema } from "@storyshelf/core/schema";
|
|
4
|
+
import { DatabaseSync } from "node:sqlite";
|
|
5
|
+
import { createDrizzleAdapter } from "@storyshelf/core/adapter/database";
|
|
8
6
|
import { DDL } from "@storyshelf/core/ddl";
|
|
7
|
+
import { schema } from "@storyshelf/core/schema";
|
|
8
|
+
import { drizzle } from "drizzle-orm/sqlite-proxy";
|
|
9
9
|
//#region src/index.ts
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
const STORYBOOK_META_ALTER = "ALTER TABLE projects ADD COLUMN storybook_meta TEXT";
|
|
11
|
+
const WEBHOOK_SECRET_ALTER = "ALTER TABLE webhooks ADD COLUMN secret_encrypted TEXT NOT NULL DEFAULT ''";
|
|
12
|
+
const WEBHOOK_SECRET_DROP = "ALTER TABLE webhooks DROP COLUMN secret";
|
|
13
|
+
/**
|
|
14
|
+
* Execute one Drizzle statement against DatabaseSync, returning proxy-shaped rows.
|
|
15
|
+
* Drizzle builds the SQL and params; only the transport is hand-written.
|
|
16
|
+
* `values` shares the `all` shape (array rows); the factory never emits it.
|
|
17
|
+
* Note: for `get`, the proxy passes `result.rows` straight to the row mapper,
|
|
18
|
+
* so `rows` is the single row (or undefined on a miss) — not a one-row array.
|
|
19
|
+
*/
|
|
20
|
+
function executeStatement(sqlite, sql, params, method) {
|
|
21
|
+
const stmt = sqlite.prepare(sql);
|
|
22
|
+
const args = params;
|
|
23
|
+
if (method === "run") {
|
|
24
|
+
stmt.run(...args);
|
|
25
|
+
return { rows: [] };
|
|
26
|
+
}
|
|
27
|
+
if (method === "get") return { rows: stmt.get(...args) };
|
|
28
|
+
return { rows: stmt.all(...args) };
|
|
18
29
|
}
|
|
19
30
|
/**
|
|
20
|
-
* Create a SQLite-backed DatabaseAdapter using
|
|
31
|
+
* Create a SQLite-backed DatabaseAdapter using node:sqlite and Drizzle ORM.
|
|
21
32
|
*
|
|
22
33
|
* @param path - Filesystem path to the SQLite database file.
|
|
23
34
|
* @returns A DatabaseAdapter backed by the given SQLite file (WAL mode).
|
|
24
35
|
*/
|
|
25
36
|
function createSqliteDatabase(path) {
|
|
26
|
-
const sqlite = new
|
|
27
|
-
sqlite.
|
|
28
|
-
sqlite.
|
|
29
|
-
const
|
|
30
|
-
|
|
37
|
+
const sqlite = new DatabaseSync(path, { returnArrays: true });
|
|
38
|
+
sqlite.exec("PRAGMA journal_mode = WAL");
|
|
39
|
+
sqlite.exec("PRAGMA busy_timeout = 5000");
|
|
40
|
+
const callback = (async (sql, params, method) => {
|
|
41
|
+
await Promise.resolve();
|
|
42
|
+
return executeStatement(sqlite, sql, params, method);
|
|
43
|
+
});
|
|
44
|
+
const db = drizzle(callback, { schema });
|
|
45
|
+
return createDrizzleAdapter(db, {
|
|
31
46
|
metadata: {
|
|
32
47
|
name: "SQLite",
|
|
33
48
|
version: globalThis.__PKG_VERSION__ ?? "0.0.0",
|
|
34
|
-
description: "SQLite database adapter (
|
|
35
|
-
kind: "sqlite"
|
|
36
|
-
|
|
37
|
-
async insert(table, values) {
|
|
38
|
-
return db.insert(table).values(values).returning().get();
|
|
39
|
-
},
|
|
40
|
-
async update(table, id, values) {
|
|
41
|
-
return db.update(table).set(values).where(eq(idOf(table), id)).returning().get();
|
|
49
|
+
description: "SQLite database adapter (node:sqlite + Drizzle)",
|
|
50
|
+
kind: "sqlite",
|
|
51
|
+
category: "database"
|
|
42
52
|
},
|
|
43
|
-
|
|
44
|
-
return db.select().from(table).where(eq(idOf(table), id)).limit(1).get() ?? null;
|
|
45
|
-
},
|
|
46
|
-
async remove(table, id) {
|
|
47
|
-
db.delete(table).where(eq(idOf(table), id)).run();
|
|
48
|
-
},
|
|
49
|
-
async list(table, opts = {}) {
|
|
50
|
-
const query = db.select().from(table);
|
|
51
|
-
applyListOptions(query, opts);
|
|
52
|
-
return query.all();
|
|
53
|
-
},
|
|
54
|
-
async count(table, where) {
|
|
55
|
-
return db.$count(table, where);
|
|
56
|
-
},
|
|
57
|
-
async all(query) {
|
|
58
|
-
return db.all(query);
|
|
59
|
-
},
|
|
60
|
-
async migrate() {
|
|
53
|
+
migrate: () => {
|
|
61
54
|
sqlite.exec(DDL);
|
|
62
55
|
try {
|
|
63
|
-
sqlite.exec(
|
|
56
|
+
sqlite.exec(STORYBOOK_META_ALTER);
|
|
57
|
+
} catch {}
|
|
58
|
+
try {
|
|
59
|
+
sqlite.exec(WEBHOOK_SECRET_ALTER);
|
|
60
|
+
sqlite.exec(WEBHOOK_SECRET_DROP);
|
|
64
61
|
} catch {}
|
|
65
62
|
},
|
|
66
|
-
|
|
63
|
+
close: () => {
|
|
67
64
|
sqlite.close();
|
|
68
65
|
}
|
|
69
|
-
};
|
|
66
|
+
});
|
|
70
67
|
}
|
|
71
68
|
//#endregion
|
|
72
69
|
export { createSqliteDatabase };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { DatabaseSync, type SQLInputValue } from \"node:sqlite\";\nimport type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { createDrizzleAdapter } from \"@storyshelf/core/adapter/database\";\nimport { DDL } from \"@storyshelf/core/ddl\";\nimport { schema } from \"@storyshelf/core/schema\";\nimport { drizzle, type AsyncRemoteCallback } from \"drizzle-orm/sqlite-proxy\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\nconst STORYBOOK_META_ALTER = \"ALTER TABLE projects ADD COLUMN storybook_meta TEXT\";\nconst WEBHOOK_SECRET_ALTER = \"ALTER TABLE webhooks ADD COLUMN secret_encrypted TEXT NOT NULL DEFAULT ''\";\nconst WEBHOOK_SECRET_DROP = \"ALTER TABLE webhooks DROP COLUMN secret\";\n\ntype ProxyMethod = \"run\" | \"all\" | \"values\" | \"get\";\n\n/**\n * Execute one Drizzle statement against DatabaseSync, returning proxy-shaped rows.\n * Drizzle builds the SQL and params; only the transport is hand-written.\n * `values` shares the `all` shape (array rows); the factory never emits it.\n * Note: for `get`, the proxy passes `result.rows` straight to the row mapper,\n * so `rows` is the single row (or undefined on a miss) — not a one-row array.\n */\nfunction executeStatement(\n sqlite: DatabaseSync,\n sql: string,\n params: unknown[],\n method: ProxyMethod,\n): { rows: unknown } {\n const stmt = sqlite.prepare(sql);\n // Single contained cast: Drizzle's params are SQLite scalars by construction.\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- tsc requires the narrowing; the rule misfires on the variadic overloads\n const args = params as SQLInputValue[];\n if (method === \"run\") {\n stmt.run(...args);\n return { rows: [] };\n }\n if (method === \"get\") {\n return { rows: stmt.get(...args) };\n }\n return { rows: stmt.all(...args) };\n}\n\n/**\n * Create a SQLite-backed DatabaseAdapter using node:sqlite and Drizzle ORM.\n *\n * @param path - Filesystem path to the SQLite database file.\n * @returns A DatabaseAdapter backed by the given SQLite file (WAL mode).\n */\nexport function createSqliteDatabase(path: string): DatabaseAdapter {\n const sqlite = new DatabaseSync(path, { returnArrays: true });\n sqlite.exec(\"PRAGMA journal_mode = WAL\");\n sqlite.exec(\"PRAGMA busy_timeout = 5000\");\n // The declared callback type claims `{ rows: any[] }`, but the session\n // passes `result.rows` straight to the row mapper for `get` — so `rows`\n // must be the single row (or undefined), not a one-row array. The cast\n // quarantines drizzle's inaccurate declaration at this boundary.\n const callback = (async (sql: string, params: unknown[], method: ProxyMethod) => {\n await Promise.resolve();\n return executeStatement(sqlite, sql, params, method);\n }) as AsyncRemoteCallback;\n const db = drizzle(callback, { schema });\n\n return createDrizzleAdapter(db, {\n metadata: {\n name: \"SQLite\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"SQLite database adapter (node:sqlite + Drizzle)\",\n kind: \"sqlite\",\n category: \"database\",\n },\n migrate: () => {\n sqlite.exec(DDL);\n try {\n sqlite.exec(STORYBOOK_META_ALTER);\n } catch {\n // Column already exists or other error — ignore for idempotency\n }\n try {\n sqlite.exec(WEBHOOK_SECRET_ALTER);\n sqlite.exec(WEBHOOK_SECRET_DROP);\n } catch {\n // Already migrated (or fresh schema) — ignore for idempotency\n }\n },\n close: () => {\n sqlite.close();\n },\n });\n}\n"],"mappings":";;;;;;;;;AASA,MAAM,uBAAuB;AAC7B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;;;;;;;;AAW5B,SAAS,iBACP,QACA,KACA,QACA,QACmB;CACnB,MAAM,OAAO,OAAO,QAAQ,GAAG;CAG/B,MAAM,OAAO;CACb,IAAI,WAAW,OAAO;EACpB,KAAK,IAAI,GAAG,IAAI;EAChB,OAAO,EAAE,MAAM,CAAC,EAAE;CACpB;CACA,IAAI,WAAW,OACb,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE;CAEnC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE;AACnC;;;;;;;AAQA,SAAgB,qBAAqB,MAA+B;CAClE,MAAM,SAAS,IAAI,aAAa,MAAM,EAAE,cAAc,KAAK,CAAC;CAC5D,OAAO,KAAK,2BAA2B;CACvC,OAAO,KAAK,4BAA4B;CAKxC,MAAM,YAAY,OAAO,KAAa,QAAmB,WAAwB;EAC/E,MAAM,QAAQ,QAAQ;EACtB,OAAO,iBAAiB,QAAQ,KAAK,QAAQ,MAAM;CACrD;CACA,MAAM,KAAK,QAAQ,UAAU,EAAE,OAAO,CAAC;CAEvC,OAAO,qBAAqB,IAAI;EAC9B,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EACA,eAAe;GACb,OAAO,KAAK,GAAG;GACf,IAAI;IACF,OAAO,KAAK,oBAAoB;GAClC,QAAQ,CAER;GACA,IAAI;IACF,OAAO,KAAK,oBAAoB;IAChC,OAAO,KAAK,mBAAmB;GACjC,QAAQ,CAER;EACF;EACA,aAAa;GACX,OAAO,MAAM;EACf;CACF,CAAC;AACH"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyshelf/db-sqlite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "SQLite database adapter for StoryShelf (
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "SQLite database adapter for StoryShelf (node:sqlite + Drizzle).",
|
|
5
5
|
"homepage": "https://github.com/GuptaSiddhant/StoryShelf#readme",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/GuptaSiddhant/StoryShelf/issues"
|
|
@@ -45,12 +45,10 @@
|
|
|
45
45
|
"prepublishOnly": "nub run build"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@storyshelf/core": "^0.
|
|
49
|
-
"better-sqlite3": "^12.4.1",
|
|
48
|
+
"@storyshelf/core": "^0.3.2",
|
|
50
49
|
"drizzle-orm": "^0.44.0"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
53
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
54
52
|
"@types/node": "catalog:",
|
|
55
53
|
"@vitest/coverage-v8": "catalog:",
|
|
56
54
|
"oxfmt": "catalog:",
|