@storyshelf/db-sqlite 0.1.0
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 +43 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.mjs +71 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @storyshelf/db-sqlite
|
|
2
|
+
|
|
3
|
+
The default database adapter for self-hosted StoryShelf: SQLite backed by better-sqlite3 and Drizzle ORM, in WAL mode. Zero configuration for a single-node deployment.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
nub add @storyshelf/db-sqlite
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
or
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @storyshelf/db-sqlite
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createSqliteDatabase } from "@storyshelf/db-sqlite";
|
|
21
|
+
import { createShelfRouter } from "@storyshelf/core";
|
|
22
|
+
import { createLocalStorage } from "@storyshelf/storage-local";
|
|
23
|
+
|
|
24
|
+
const database = createSqliteDatabase("./data/shelf.db");
|
|
25
|
+
await database.migrate();
|
|
26
|
+
|
|
27
|
+
const storage = createLocalStorage("./data");
|
|
28
|
+
const app = createShelfRouter({ database, storage });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
### `createSqliteDatabase(path: string): DatabaseAdapter`
|
|
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. Call `migrate()` before use to apply the schema (shared DDL from `@storyshelf/core`).
|
|
36
|
+
|
|
37
|
+
The returned adapter implements every method of the `DatabaseAdapter` interface (`insert`, `update`, `get`, `remove`, `list`, `count`, `all`, `migrate`, `close`).
|
|
38
|
+
|
|
39
|
+
## How it fits in
|
|
40
|
+
|
|
41
|
+
`db-sqlite` is the default `database` option for `createShelfRouter` in single-node self-hosted deployments. It implements the same `DatabaseAdapter` interface and shares the same Drizzle schema as `@storyshelf/db-turso`, so switching to a serverless database only means swapping this adapter.
|
|
42
|
+
|
|
43
|
+
See `docs/architecture.md` and ADR 0002.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create a SQLite-backed DatabaseAdapter using better-sqlite3 and Drizzle ORM.
|
|
7
|
+
*
|
|
8
|
+
* @param path - Filesystem path to the SQLite database file.
|
|
9
|
+
* @returns A DatabaseAdapter backed by the given SQLite file (WAL mode).
|
|
10
|
+
*/
|
|
11
|
+
declare function createSqliteDatabase(path: string): DatabaseAdapter;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { createSqliteDatabase };
|
|
14
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { eq, getTableColumns } from "drizzle-orm";
|
|
5
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
6
|
+
import Database from "better-sqlite3";
|
|
7
|
+
import { schema } from "@storyshelf/core/schema";
|
|
8
|
+
import { DDL } from "@storyshelf/core/ddl";
|
|
9
|
+
//#region src/index.ts
|
|
10
|
+
function idOf(table) {
|
|
11
|
+
return getTableColumns(table)["id"];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a SQLite-backed DatabaseAdapter using better-sqlite3 and Drizzle ORM.
|
|
15
|
+
*
|
|
16
|
+
* @param path - Filesystem path to the SQLite database file.
|
|
17
|
+
* @returns A DatabaseAdapter backed by the given SQLite file (WAL mode).
|
|
18
|
+
*/
|
|
19
|
+
function createSqliteDatabase(path) {
|
|
20
|
+
const sqlite = new Database(path);
|
|
21
|
+
sqlite.pragma("journal_mode = WAL");
|
|
22
|
+
sqlite.pragma("busy_timeout = 5000");
|
|
23
|
+
const db = drizzle(sqlite, { schema });
|
|
24
|
+
return {
|
|
25
|
+
metadata: {
|
|
26
|
+
name: "SQLite",
|
|
27
|
+
version: "0.1.0",
|
|
28
|
+
description: "SQLite database adapter (better-sqlite3 + Drizzle)",
|
|
29
|
+
kind: "sqlite"
|
|
30
|
+
},
|
|
31
|
+
async insert(table, values) {
|
|
32
|
+
return db.insert(table).values(values).returning().get();
|
|
33
|
+
},
|
|
34
|
+
async update(table, id, values) {
|
|
35
|
+
return db.update(table).set(values).where(eq(idOf(table), id)).returning().get();
|
|
36
|
+
},
|
|
37
|
+
async get(table, id) {
|
|
38
|
+
return db.select().from(table).where(eq(idOf(table), id)).limit(1).get() ?? null;
|
|
39
|
+
},
|
|
40
|
+
async remove(table, id) {
|
|
41
|
+
db.delete(table).where(eq(idOf(table), id)).run();
|
|
42
|
+
},
|
|
43
|
+
async list(table, opts = {}) {
|
|
44
|
+
const query = db.select().from(table);
|
|
45
|
+
if (opts.where) query.where(opts.where);
|
|
46
|
+
if (opts.orderBy) query.orderBy(opts.orderBy);
|
|
47
|
+
if (opts.limit !== void 0) query.limit(opts.limit);
|
|
48
|
+
if (opts.offset !== void 0) query.offset(opts.offset);
|
|
49
|
+
return query.all();
|
|
50
|
+
},
|
|
51
|
+
async count(table, where) {
|
|
52
|
+
return db.$count(table, where);
|
|
53
|
+
},
|
|
54
|
+
async all(query) {
|
|
55
|
+
return db.all(query);
|
|
56
|
+
},
|
|
57
|
+
async migrate() {
|
|
58
|
+
sqlite.exec(DDL);
|
|
59
|
+
try {
|
|
60
|
+
sqlite.exec("ALTER TABLE projects ADD COLUMN storybook_meta TEXT");
|
|
61
|
+
} catch {}
|
|
62
|
+
},
|
|
63
|
+
async close() {
|
|
64
|
+
sqlite.close();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { createSqliteDatabase };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { eq, getTableColumns } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { AnySQLiteTable, SQLiteColumn } from \"drizzle-orm/sqlite-core\";\nimport Database from \"better-sqlite3\";\n\nimport type { DatabaseAdapter, ListOptions } from \"@storyshelf/core/adapter/database\";\nimport { schema } from \"@storyshelf/core/schema\";\nimport { DDL } from \"@storyshelf/core/ddl\";\n\ndeclare const __PKG_VERSION__: string;\n\nfunction idOf(table: AnySQLiteTable): SQLiteColumn {\n // eslint-disable-next-line no-non-null-assertion -- every table has an `id` column\n return getTableColumns(table)[\"id\"]!;\n}\n\n/**\n * Create a SQLite-backed DatabaseAdapter using better-sqlite3 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 Database(path);\n sqlite.pragma(\"journal_mode = WAL\");\n sqlite.pragma(\"busy_timeout = 5000\");\n const db = drizzle(sqlite, { schema });\n\n // better-sqlite3 is synchronous, so `async` is required only by the\n // DatabaseAdapter interface (no `await`), and drizzle's `.get()`/`[\"id\"]`\n // are guaranteed present despite the undefined-able types. Both rules are\n // false positives here.\n /* eslint-disable require-await, no-non-null-assertion, no-unnecessary-type-assertion */\n return {\n metadata: {\n name: \"SQLite\",\n version: typeof __PKG_VERSION__ === \"undefined\" ? \"0.0.0\" : __PKG_VERSION__, // oxlint-disable-line unicorn/no-typeof-undefined\n description: \"SQLite database adapter (better-sqlite3 + Drizzle)\",\n kind: \"sqlite\",\n },\n async insert(table, values) {\n const row = db.insert(table).values(values).returning().get();\n return row!;\n },\n async update(table, id, values) {\n const row = db.update(table).set(values).where(eq(idOf(table), id)).returning().get();\n return row!;\n },\n async get(table, id) {\n const row = db.select().from(table).where(eq(idOf(table), id)).limit(1).get();\n return row ?? null;\n },\n async remove(table, id) {\n db.delete(table).where(eq(idOf(table), id)).run();\n },\n async list(table, opts: ListOptions = {}) {\n const query = db.select().from(table);\n if (opts.where) query.where(opts.where);\n if (opts.orderBy) query.orderBy(opts.orderBy);\n if (opts.limit !== undefined) query.limit(opts.limit);\n if (opts.offset !== undefined) query.offset(opts.offset);\n return query.all();\n },\n async count(table, where) {\n return db.$count(table, where);\n },\n async all(query) {\n return db.all(query);\n },\n async migrate() {\n sqlite.exec(DDL);\n try {\n sqlite.exec(\"ALTER TABLE projects ADD COLUMN storybook_meta TEXT\");\n } catch {\n // column already exists or other error — ignore for idempotency\n }\n },\n async close() {\n sqlite.close();\n },\n };\n /* eslint-enable require-await, no-non-null-assertion, no-unnecessary-type-assertion */\n}\n"],"mappings":";;;;;;;;;AAWA,SAAS,KAAK,OAAqC;CAEjD,OAAO,gBAAgB,KAAK,CAAC,CAAC;AAChC;;;;;;;AAQA,SAAgB,qBAAqB,MAA+B;CAClE,MAAM,SAAS,IAAI,SAAS,IAAI;CAChC,OAAO,OAAO,oBAAoB;CAClC,OAAO,OAAO,qBAAqB;CACnC,MAAM,KAAK,QAAQ,QAAQ,EAAE,OAAO,CAAC;CAOrC,OAAO;EACL,UAAU;GACR,MAAM;GACN,SAAA;GACA,aAAa;GACb,MAAM;EACR;EACA,MAAM,OAAO,OAAO,QAAQ;GAE1B,OADY,GAAG,OAAO,KAAK,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,IAC/C;EACX;EACA,MAAM,OAAO,OAAO,IAAI,QAAQ;GAE9B,OADY,GAAG,OAAO,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,GAAG,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IACvE;EACX;EACA,MAAM,IAAI,OAAO,IAAI;GAEnB,OADY,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,GAAG,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAC/D,KAAK;EAChB;EACA,MAAM,OAAO,OAAO,IAAI;GACtB,GAAG,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI;EAClD;EACA,MAAM,KAAK,OAAO,OAAoB,CAAC,GAAG;GACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK;GACpC,IAAI,KAAK,OAAO,MAAM,MAAM,KAAK,KAAK;GACtC,IAAI,KAAK,SAAS,MAAM,QAAQ,KAAK,OAAO;GAC5C,IAAI,KAAK,UAAU,KAAA,GAAW,MAAM,MAAM,KAAK,KAAK;GACpD,IAAI,KAAK,WAAW,KAAA,GAAW,MAAM,OAAO,KAAK,MAAM;GACvD,OAAO,MAAM,IAAI;EACnB;EACA,MAAM,MAAM,OAAO,OAAO;GACxB,OAAO,GAAG,OAAO,OAAO,KAAK;EAC/B;EACA,MAAM,IAAI,OAAO;GACf,OAAO,GAAG,IAAI,KAAK;EACrB;EACA,MAAM,UAAU;GACd,OAAO,KAAK,GAAG;GACf,IAAI;IACF,OAAO,KAAK,qDAAqD;GACnE,QAAQ,CAER;EACF;EACA,MAAM,QAAQ;GACZ,OAAO,MAAM;EACf;CACF;AAEF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@storyshelf/db-sqlite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "SQLite database adapter for StoryShelf (better-sqlite3 + Drizzle).",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Siddhant Gupta",
|
|
8
|
+
"url": "https://guptasiddhant.com"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/GuptaSiddhant/storyshelf.git",
|
|
15
|
+
"directory": "packages/db-sqlite"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/GuptaSiddhant/storyshelf#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/GuptaSiddhant/storyshelf/issues"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./dist/index.mjs",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsdown",
|
|
33
|
+
"dev": "tsdown -w",
|
|
34
|
+
"fmt": "oxfmt -c ../../.oxfmtrc.json ./src",
|
|
35
|
+
"lint": "oxlint --type-aware --type-check ./src",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"prepublishOnly": "nub run build"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@storyshelf/core": "workspace:*",
|
|
41
|
+
"better-sqlite3": "^12.4.1",
|
|
42
|
+
"drizzle-orm": "^0.44.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
46
|
+
"@types/node": "catalog:",
|
|
47
|
+
"@vitest/coverage-v8": "catalog:",
|
|
48
|
+
"oxfmt": "catalog:",
|
|
49
|
+
"oxlint": "catalog:",
|
|
50
|
+
"oxlint-tsgolint": "catalog:",
|
|
51
|
+
"tsdown": "catalog:",
|
|
52
|
+
"typescript": "catalog:",
|
|
53
|
+
"vitest": "catalog:"
|
|
54
|
+
},
|
|
55
|
+
"types": "./dist/index.d.mts",
|
|
56
|
+
"exports": {
|
|
57
|
+
".": {
|
|
58
|
+
"source": "./src/index.ts",
|
|
59
|
+
"default": "./dist/index.mjs"
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json"
|
|
62
|
+
}
|
|
63
|
+
}
|