@airdraft/db-adapter-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 ADDED
@@ -0,0 +1,161 @@
1
+ # @airdraft/db-adapter-sqlite
2
+
3
+ SQLite storage adapter for [Airdraft](https://airdraft.io) via [`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3).
4
+
5
+ Use this adapter to store Airdraft content in a local SQLite file — great for self-hosted deployments on a single server, VPS, or edge runtimes that bundle SQLite (e.g. Cloudflare D1 with a custom driver).
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @airdraft/db-adapter @airdraft/db-adapter-sqlite better-sqlite3
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Quick start
18
+
19
+ ```ts
20
+ // airdraft.config.ts
21
+ import { defineConfig } from '@airdraft/core'
22
+ import { SQLiteAdapter } from '@airdraft/db-adapter-sqlite'
23
+
24
+ export default defineConfig({
25
+ adapter: new SQLiteAdapter({
26
+ filename: process.env.DATABASE_URL ?? '.airdraft.db',
27
+ }),
28
+ // … collections, plugins
29
+ })
30
+ ```
31
+
32
+ Add to `.env.local`:
33
+
34
+ ```
35
+ DATABASE_URL=.airdraft.db
36
+ ```
37
+
38
+ Run the migration on first deploy (or on every deploy — it is idempotent):
39
+
40
+ ```ts
41
+ // instrumentation.ts (Next.js server-start hook)
42
+ import airdraft from '@/airdraft.config'
43
+ import { BaseDatabaseAdapter } from '@airdraft/db-adapter'
44
+
45
+ export async function register() {
46
+ if (airdraft.adapter instanceof BaseDatabaseAdapter) {
47
+ await airdraft.adapter.migrate()
48
+ }
49
+ }
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Options
55
+
56
+ ```ts
57
+ new SQLiteAdapter(options)
58
+ ```
59
+
60
+ | Option | Type | Default | Description |
61
+ |---|---|---|---|
62
+ | `filename` | `string` | — | Path to the `.db` file. Pass `':memory:'` for in-memory testing. |
63
+ | `projectId` | `string` | `'default'` | Namespace for multi-tenant deployments. |
64
+ | `history` | `boolean` | `false` | Mirror every write to `airdraft_entry_history`. Enables `rollback()`. |
65
+ | `cacheTtlMs` | `number` | `0` | Per-entry read cache TTL. `0` disables caching. |
66
+ | `cacheMaxSize` | `number` | `500` | Maximum entries in the LRU cache. |
67
+
68
+ ---
69
+
70
+ ## Schema
71
+
72
+ The adapter creates two tables via `migrate()`:
73
+
74
+ ### `airdraft_entries`
75
+
76
+ | Column | Type | Notes |
77
+ |---|---|---|
78
+ | `id` | `INTEGER PRIMARY KEY` | Auto-increment |
79
+ | `project_id` | `TEXT NOT NULL` | Multi-tenancy namespace |
80
+ | `collection` | `TEXT NOT NULL` | Collection name (first path segment) |
81
+ | `slug` | `TEXT NOT NULL` | Entry slug (second path segment) |
82
+ | `sha` | `TEXT NOT NULL` | SHA-256 of the serialized content |
83
+ | `data` | `TEXT NOT NULL` | JSON-serialized entry data |
84
+ | `published` | `INTEGER` | 1 = published, 0 = draft (`NULL` = published) |
85
+ | `created_at` | `TEXT` | ISO-8601 datetime |
86
+ | `updated_at` | `TEXT` | ISO-8601 datetime |
87
+
88
+ Unique index on `(project_id, collection, slug)`.
89
+
90
+ ### `airdraft_entry_history` *(when `history: true`)*
91
+
92
+ | Column | Type | Notes |
93
+ |---|---|---|
94
+ | `id` | `INTEGER PRIMARY KEY` | |
95
+ | `project_id` | `TEXT NOT NULL` | |
96
+ | `collection` | `TEXT NOT NULL` | |
97
+ | `slug` | `TEXT NOT NULL` | |
98
+ | `sha` | `TEXT NOT NULL` | SHA at the time of the snapshot |
99
+ | `data` | `TEXT NOT NULL` | Snapshot of the entry data |
100
+ | `created_at` | `TEXT` | When the snapshot was taken |
101
+
102
+ ---
103
+
104
+ ## Features
105
+
106
+ - **Optimistic concurrency** — Every write checks the current SHA. Throws `ConflictError` on mismatch.
107
+ - **Field filters** — `queryEntries()` translates `ListOptions.filter` to `json_extract()` predicates.
108
+ - **Pagination** — `offset` + `limit` via `LIMIT ? OFFSET ?`.
109
+ - **Sort** — `json_extract()` on any dot-notation field path.
110
+ - **Atomic field ops** — `increment`, `set`, `push`, `pull` applied to any nested field with SHA re-computation.
111
+ - **History** — Opt-in snapshot table; `rollback(path, sha)` restores any previous revision.
112
+ - **Lazy `require`** — `better-sqlite3` is loaded on first use, so the module is tree-shakeable.
113
+
114
+ ---
115
+
116
+ ## Running migration as a standalone script
117
+
118
+ ```ts
119
+ // scripts/db-setup.ts
120
+ import airdraft from '../airdraft.config.js'
121
+ import { BaseDatabaseAdapter } from '@airdraft/db-adapter'
122
+
123
+ async function main() {
124
+ if (!(airdraft.adapter instanceof BaseDatabaseAdapter)) {
125
+ console.error('No database adapter configured')
126
+ process.exit(1)
127
+ }
128
+ await airdraft.adapter.migrate()
129
+ await airdraft.adapter.close()
130
+ console.log('Migration complete')
131
+ }
132
+
133
+ main().catch((err) => { console.error(err); process.exit(1) })
134
+ ```
135
+
136
+ ```bash
137
+ npx tsx scripts/db-setup.ts
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Testing
143
+
144
+ The adapter runs the shared [contract test suite](../db-adapter/README.md#contract-test-suite) against an in-memory database:
145
+
146
+ ```ts
147
+ // src/__tests__/contract.test.ts
148
+ import { describe } from 'vitest'
149
+ import { runStorageAdapterContract } from '@airdraft/db-adapter/testing'
150
+ import { SQLiteAdapter } from '../SQLiteAdapter.js'
151
+
152
+ describe('SQLiteAdapter contract', () => {
153
+ runStorageAdapterContract(() => new SQLiteAdapter({ filename: ':memory:', history: true }))
154
+ })
155
+ ```
156
+
157
+ ---
158
+
159
+ ## License
160
+
161
+ MIT
@@ -0,0 +1,26 @@
1
+ import { BaseDatabaseAdapter } from '@airdraft/db-adapter';
2
+ import type { FileResult, WriteOptions, DeleteOptions, ListOptions } from '@airdraft/core';
3
+ import type { DbAdapterOptions, QueryEntriesResult, AtomicOp } from '@airdraft/db-adapter';
4
+ import type { Database as BetterSQLite3Database } from 'better-sqlite3';
5
+ type SQLiteAdapterBaseOptions = DbAdapterOptions;
6
+ export type SQLiteAdapterOptions = ({
7
+ filename: string;
8
+ } & SQLiteAdapterBaseOptions) | ({
9
+ db: BetterSQLite3Database;
10
+ } & SQLiteAdapterBaseOptions);
11
+ export declare class SQLiteAdapter extends BaseDatabaseAdapter {
12
+ private db;
13
+ private readonly owned;
14
+ constructor(opts: SQLiteAdapterOptions);
15
+ migrate(): Promise<void>;
16
+ read(path: string): Promise<FileResult | null>;
17
+ write(path: string, content: string, options: WriteOptions): Promise<void>;
18
+ delete(path: string, options: DeleteOptions): Promise<void>;
19
+ queryEntries(collection: string, options: ListOptions): Promise<QueryEntriesResult>;
20
+ atomicUpdate(path: string, ops: AtomicOp[], sha: string): Promise<FileResult>;
21
+ rollback(path: string, sha: string): Promise<FileResult>;
22
+ close(): Promise<void>;
23
+ private _insertHistory;
24
+ }
25
+ export {};
26
+ //# sourceMappingURL=SQLiteAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SQLiteAdapter.d.ts","sourceRoot":"","sources":["../src/SQLiteAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAQ1D,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC1F,OAAO,KAAK,EAAE,QAAQ,IAAI,qBAAqB,EAAa,MAAM,gBAAgB,CAAA;AAMlF,KAAK,wBAAwB,GAAG,gBAAgB,CAAA;AAEhD,MAAM,MAAM,oBAAoB,GAC5B,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,wBAAwB,CAAC,GACjD,CAAC;IAAE,EAAE,EAAE,qBAAqB,CAAA;CAAE,GAAG,wBAAwB,CAAC,CAAA;AAqF9D,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,OAAO,CAAC,EAAE,CAAuB;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,IAAI,EAAE,oBAAoB;IAiBhC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuDxB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA+B9C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAwD1E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B3D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAqDnF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAiE7E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA0BxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,OAAO,CAAC,cAAc;CA4BvB"}
@@ -0,0 +1,371 @@
1
+ import { BaseDatabaseAdapter } from '@airdraft/db-adapter';
2
+ import { ConflictError, StorageUnavailableError, MigrationError, AirdraftError, } from '@airdraft/core';
3
+ import { serialize, deserialize } from '@airdraft/core';
4
+ // ---------------------------------------------------------------------------
5
+ // Filter helpers
6
+ // ---------------------------------------------------------------------------
7
+ function buildWhereClause(collection, projectId, options) {
8
+ const conditions = ['collection = ?', 'project_id = ?'];
9
+ const params = [collection, projectId];
10
+ if (options.status === 'published') {
11
+ conditions.push('published = 1');
12
+ }
13
+ else if (options.status === 'draft') {
14
+ conditions.push('published = 0');
15
+ }
16
+ if (options.filter) {
17
+ for (const [field, spec] of Object.entries(options.filter)) {
18
+ if (typeof spec === 'object' && spec !== null) {
19
+ for (const [op, val] of Object.entries(spec)) {
20
+ switch (op) {
21
+ case '$eq':
22
+ conditions.push(`json_extract(data, '$.${field}') = ?`);
23
+ params.push(val);
24
+ break;
25
+ case '$gt':
26
+ conditions.push(`CAST(json_extract(data, '$.${field}') AS REAL) > ?`);
27
+ params.push(val);
28
+ break;
29
+ case '$gte':
30
+ conditions.push(`CAST(json_extract(data, '$.${field}') AS REAL) >= ?`);
31
+ params.push(val);
32
+ break;
33
+ case '$lt':
34
+ conditions.push(`CAST(json_extract(data, '$.${field}') AS REAL) < ?`);
35
+ params.push(val);
36
+ break;
37
+ case '$lte':
38
+ conditions.push(`CAST(json_extract(data, '$.${field}') AS REAL) <= ?`);
39
+ params.push(val);
40
+ break;
41
+ case '$contains':
42
+ conditions.push(`json_extract(data, '$.${field}') LIKE ?`);
43
+ params.push(`%${val}%`);
44
+ break;
45
+ case '$in':
46
+ if (Array.isArray(val) && val.length > 0) {
47
+ conditions.push(`json_extract(data, '$.${field}') IN (${val.map(() => '?').join(',')})`);
48
+ params.push(...val);
49
+ }
50
+ break;
51
+ }
52
+ }
53
+ }
54
+ else {
55
+ conditions.push(`json_extract(data, '$.${field}') = ?`);
56
+ params.push(spec);
57
+ }
58
+ }
59
+ }
60
+ return { sql: conditions.join(' AND '), params };
61
+ }
62
+ // ---------------------------------------------------------------------------
63
+ // SQLiteAdapter
64
+ // ---------------------------------------------------------------------------
65
+ export class SQLiteAdapter extends BaseDatabaseAdapter {
66
+ db;
67
+ owned; // true when we created the db ourselves
68
+ constructor(opts) {
69
+ super(opts);
70
+ if ('db' in opts) {
71
+ this.db = opts.db;
72
+ this.owned = false;
73
+ }
74
+ else {
75
+ // Lazy import to avoid hard dependency at module load time
76
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
77
+ const Database = require('better-sqlite3');
78
+ const Ctor = (typeof Database === 'function' ? Database : Database.default);
79
+ this.db = new Ctor(opts.filename);
80
+ this.owned = true;
81
+ }
82
+ }
83
+ // ─── migrate() ─────────────────────────────────────────────────────────────
84
+ async migrate() {
85
+ try {
86
+ this.db.exec(`
87
+ CREATE TABLE IF NOT EXISTS airdraft_entries (
88
+ id TEXT NOT NULL,
89
+ project_id TEXT NOT NULL DEFAULT 'default',
90
+ collection TEXT NOT NULL,
91
+ slug TEXT NOT NULL,
92
+ sha TEXT NOT NULL,
93
+ data TEXT NOT NULL DEFAULT '{}',
94
+ format TEXT NOT NULL DEFAULT 'json',
95
+ published INTEGER NOT NULL DEFAULT 0,
96
+ published_at TEXT,
97
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
98
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
99
+ PRIMARY KEY (id, project_id),
100
+ UNIQUE (project_id, collection, slug)
101
+ );
102
+
103
+ CREATE INDEX IF NOT EXISTS airdraft_entries_project_collection
104
+ ON airdraft_entries (project_id, collection);
105
+
106
+ CREATE INDEX IF NOT EXISTS airdraft_entries_published
107
+ ON airdraft_entries (project_id, collection, published);
108
+ `);
109
+ if (this.history) {
110
+ this.db.exec(`
111
+ CREATE TABLE IF NOT EXISTS airdraft_entry_history (
112
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
113
+ entry_id TEXT NOT NULL,
114
+ project_id TEXT NOT NULL,
115
+ collection TEXT NOT NULL,
116
+ slug TEXT NOT NULL,
117
+ sha TEXT NOT NULL,
118
+ data TEXT NOT NULL DEFAULT '{}',
119
+ format TEXT NOT NULL DEFAULT 'json',
120
+ event TEXT NOT NULL,
121
+ message TEXT,
122
+ author_name TEXT,
123
+ author_email TEXT,
124
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
125
+ );
126
+
127
+ CREATE INDEX IF NOT EXISTS airdraft_entry_history_entry
128
+ ON airdraft_entry_history (entry_id, project_id, created_at DESC);
129
+ `);
130
+ }
131
+ }
132
+ catch (e) {
133
+ throw new MigrationError(`SQLite migration failed: ${String(e)}`);
134
+ }
135
+ }
136
+ // ─── read() ────────────────────────────────────────────────────────────────
137
+ async read(path) {
138
+ if (this.cache) {
139
+ const cached = this.cache.get(path);
140
+ if (cached !== undefined)
141
+ return cached;
142
+ }
143
+ try {
144
+ const row = this.db.prepare('SELECT * FROM airdraft_entries WHERE id = ? AND project_id = ?').get(path, this.projectId);
145
+ if (!row) {
146
+ this.cache?.set(path, null);
147
+ return null;
148
+ }
149
+ const { collection: _c, slug: _s, ext } = this.parsePath(path);
150
+ const format = (row.format || ext.replace('.', '') || 'json');
151
+ const data = JSON.parse(row.data);
152
+ const content = serialize(format, data);
153
+ const result = { path, sha: row.sha, content };
154
+ this.cache?.set(path, result);
155
+ return result;
156
+ }
157
+ catch (e) {
158
+ throw new StorageUnavailableError(`SQLite read failed: ${String(e)}`);
159
+ }
160
+ }
161
+ // ─── write() ───────────────────────────────────────────────────────────────
162
+ async write(path, content, options) {
163
+ const { collection, slug, ext } = this.parsePath(path);
164
+ const format = (ext.replace('.', '') || 'json');
165
+ const data = deserialize(format, content);
166
+ const newSha = this.computeSha(data);
167
+ const dataStr = JSON.stringify(data);
168
+ const published = data.published === true ? 1 : 0;
169
+ const publishedAt = typeof data.publishedAt === 'string' ? data.publishedAt : null;
170
+ const now = new Date().toISOString();
171
+ try {
172
+ if (options.sha) {
173
+ // UPDATE with sha check
174
+ const result = this.db.prepare(`
175
+ UPDATE airdraft_entries
176
+ SET data = ?, sha = ?, format = ?, published = ?, published_at = ?, updated_at = ?
177
+ WHERE id = ? AND project_id = ? AND sha = ?
178
+ `).run(dataStr, newSha, format, published, publishedAt, now, path, this.projectId, options.sha);
179
+ if (result.changes === 0) {
180
+ throw new ConflictError(`Conflict updating '${path}': sha mismatch or entry does not exist`);
181
+ }
182
+ if (this.history) {
183
+ this._insertHistory(path, collection, slug, newSha, dataStr, format, 'update', options);
184
+ }
185
+ }
186
+ else {
187
+ // INSERT (create)
188
+ try {
189
+ this.db.prepare(`
190
+ INSERT INTO airdraft_entries
191
+ (id, project_id, collection, slug, sha, data, format, published, published_at, created_at, updated_at)
192
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
193
+ `).run(path, this.projectId, collection, slug, newSha, dataStr, format, published, publishedAt, now, now);
194
+ if (this.history) {
195
+ this._insertHistory(path, collection, slug, newSha, dataStr, format, 'create', options);
196
+ }
197
+ }
198
+ catch (e) {
199
+ const sqliteErr = e;
200
+ if (sqliteErr?.code === 'SQLITE_CONSTRAINT_UNIQUE') {
201
+ throw new ConflictError(`An entry already exists at '${path}'`);
202
+ }
203
+ throw e;
204
+ }
205
+ }
206
+ this.cache?.delete(path);
207
+ }
208
+ catch (e) {
209
+ if (e instanceof ConflictError)
210
+ throw e;
211
+ throw new StorageUnavailableError(`SQLite write failed: ${String(e)}`);
212
+ }
213
+ }
214
+ // ─── delete() ──────────────────────────────────────────────────────────────
215
+ async delete(path, options) {
216
+ try {
217
+ if (this.history) {
218
+ const row = this.db.prepare('SELECT * FROM airdraft_entries WHERE id = ? AND project_id = ?').get(path, this.projectId);
219
+ if (row) {
220
+ const { collection, slug } = this.parsePath(path);
221
+ this._insertHistory(path, collection, slug, row.sha, row.data, row.format, 'delete', options);
222
+ }
223
+ }
224
+ const result = this.db.prepare('DELETE FROM airdraft_entries WHERE id = ? AND project_id = ? AND sha = ?').run(path, this.projectId, options.sha);
225
+ if (result.changes === 0) {
226
+ throw new ConflictError(`Conflict deleting '${path}': sha mismatch or entry does not exist`);
227
+ }
228
+ this.cache?.delete(path);
229
+ }
230
+ catch (e) {
231
+ if (e instanceof ConflictError)
232
+ throw e;
233
+ throw new StorageUnavailableError(`SQLite delete failed: ${String(e)}`);
234
+ }
235
+ }
236
+ // ─── queryEntries() ────────────────────────────────────────────────────────
237
+ async queryEntries(collection, options) {
238
+ try {
239
+ const { sql: where, params } = buildWhereClause(collection, this.projectId, options);
240
+ let orderBy = 'created_at DESC';
241
+ if (options.sort) {
242
+ const [field, dir] = Array.isArray(options.sort)
243
+ ? options.sort
244
+ : [options.sort, 'asc'];
245
+ const safeDirs = { asc: 'ASC', desc: 'DESC' };
246
+ const safeDir = safeDirs[String(dir).toLowerCase()] ?? 'ASC';
247
+ // System columns use typed path, data fields use json_extract
248
+ const systemCols = new Set(['created_at', 'updated_at', 'published_at', 'slug', 'published']);
249
+ const colExpr = systemCols.has(String(field))
250
+ ? String(field)
251
+ : `json_extract(data, '$.${field}')`;
252
+ orderBy = `${colExpr} ${safeDir}`;
253
+ }
254
+ const countRow = this.db.prepare(`SELECT COUNT(*) as cnt FROM airdraft_entries WHERE ${where}`).get(...params);
255
+ const total = countRow.cnt;
256
+ let limitSql = '';
257
+ if (options.limit !== undefined) {
258
+ limitSql = `LIMIT ${options.limit}`;
259
+ if (options.offset !== undefined)
260
+ limitSql += ` OFFSET ${options.offset}`;
261
+ }
262
+ const rows = this.db.prepare(`SELECT * FROM airdraft_entries WHERE ${where} ORDER BY ${orderBy} ${limitSql}`).all(...params);
263
+ const entries = rows.map(row => {
264
+ const { ext } = this.parsePath(row.id);
265
+ const format = (row.format || ext.replace('.', '') || 'json');
266
+ const data = JSON.parse(row.data);
267
+ return {
268
+ path: row.id,
269
+ sha: row.sha,
270
+ content: serialize(format, data),
271
+ };
272
+ });
273
+ return { entries, total };
274
+ }
275
+ catch (e) {
276
+ throw new StorageUnavailableError(`SQLite queryEntries failed: ${String(e)}`);
277
+ }
278
+ }
279
+ // ─── atomicUpdate() ────────────────────────────────────────────────────────
280
+ async atomicUpdate(path, ops, sha) {
281
+ try {
282
+ // SQLite requires a pre-read for increment/push/pull (no in-place arithmetic on JSON)
283
+ const row = this.db.prepare('SELECT * FROM airdraft_entries WHERE id = ? AND project_id = ? AND sha = ?').get(path, this.projectId, sha);
284
+ if (!row)
285
+ throw new ConflictError(`Conflict in atomicUpdate '${path}': sha mismatch or entry not found`);
286
+ const data = JSON.parse(row.data);
287
+ for (const op of ops) {
288
+ const keys = op.path.split('.');
289
+ let target = data;
290
+ for (let i = 0; i < keys.length - 1; i++) {
291
+ target = target[keys[i]] ?? {};
292
+ }
293
+ const last = keys[keys.length - 1];
294
+ switch (op.op) {
295
+ case 'increment':
296
+ target[last] = (Number(target[last]) || 0) + op.by;
297
+ break;
298
+ case 'set':
299
+ target[last] = op.value;
300
+ break;
301
+ case 'push':
302
+ if (!Array.isArray(target[last]))
303
+ target[last] = [];
304
+ target[last].push(op.value);
305
+ break;
306
+ case 'pull':
307
+ if (Array.isArray(target[last])) {
308
+ target[last] = target[last].filter(v => v !== op.value);
309
+ }
310
+ break;
311
+ }
312
+ }
313
+ const newSha = this.computeSha(data);
314
+ const dataStr = JSON.stringify(data);
315
+ const now = new Date().toISOString();
316
+ const result = this.db.prepare(`
317
+ UPDATE airdraft_entries
318
+ SET data = ?, sha = ?, updated_at = ?
319
+ WHERE id = ? AND project_id = ? AND sha = ?
320
+ `).run(dataStr, newSha, now, path, this.projectId, sha);
321
+ if (result.changes === 0) {
322
+ throw new ConflictError(`Conflict in atomicUpdate '${path}': concurrent modification`);
323
+ }
324
+ this.cache?.delete(path);
325
+ const { ext } = this.parsePath(path);
326
+ const format = (row.format || ext.replace('.', '') || 'json');
327
+ return { path, sha: newSha, content: serialize(format, data) };
328
+ }
329
+ catch (e) {
330
+ if (e instanceof ConflictError)
331
+ throw e;
332
+ throw new StorageUnavailableError(`SQLite atomicUpdate failed: ${String(e)}`);
333
+ }
334
+ }
335
+ // ─── rollback() ────────────────────────────────────────────────────────────
336
+ async rollback(path, sha) {
337
+ if (!this.history) {
338
+ throw new AirdraftError('UNSUPPORTED_OPERATION', 'rollback() requires history: true', 400);
339
+ }
340
+ try {
341
+ const row = this.db.prepare('SELECT * FROM airdraft_entry_history WHERE entry_id = ? AND project_id = ? AND sha = ? ORDER BY created_at DESC LIMIT 1').get(path, this.projectId, sha);
342
+ if (!row)
343
+ throw new StorageUnavailableError(`No history found for '${path}' with sha '${sha}'`);
344
+ const format = row.format;
345
+ const data = JSON.parse(row.data);
346
+ const content = serialize(format, data);
347
+ await this.write(path, content, { message: `rollback to ${sha}` });
348
+ return (await this.read(path));
349
+ }
350
+ catch (e) {
351
+ if (e instanceof AirdraftError)
352
+ throw e;
353
+ throw new StorageUnavailableError(`SQLite rollback failed: ${String(e)}`);
354
+ }
355
+ }
356
+ // ─── close() ───────────────────────────────────────────────────────────────
357
+ async close() {
358
+ if (this.owned) {
359
+ this.db.close();
360
+ }
361
+ }
362
+ // ─── Private helpers ───────────────────────────────────────────────────────
363
+ _insertHistory(entryId, collection, slug, sha, dataStr, format, event, options) {
364
+ this.db.prepare(`
365
+ INSERT INTO airdraft_entry_history
366
+ (entry_id, project_id, collection, slug, sha, data, format, event, message, author_name, author_email)
367
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
368
+ `).run(entryId, this.projectId, collection, slug, sha, dataStr, format, event, options.message ?? null, options.author?.name ?? null, options.author?.email ?? null);
369
+ }
370
+ }
371
+ //# sourceMappingURL=SQLiteAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SQLiteAdapter.js","sourceRoot":"","sources":["../src/SQLiteAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contract.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/contract.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import { SQLiteAdapter } from '../SQLiteAdapter.js';
2
+ import { runStorageAdapterContract } from '@airdraft/db-adapter/testing';
3
+ runStorageAdapterContract(new SQLiteAdapter({ filename: ':memory:' }));
4
+ runStorageAdapterContract(new SQLiteAdapter({ filename: ':memory:', history: true }), { skipHistory: false });
5
+ //# sourceMappingURL=contract.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.test.js","sourceRoot":"","sources":["../../src/__tests__/contract.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAA;AAExE,yBAAyB,CACvB,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAC5C,CAAA;AAED,yBAAyB,CACvB,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAC1D,EAAE,WAAW,EAAE,KAAK,EAAE,CACvB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { SQLiteAdapter } from './SQLiteAdapter.js';
2
+ export type { SQLiteAdapterOptions } from './SQLiteAdapter.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { SQLiteAdapter } from './SQLiteAdapter.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@airdraft/db-adapter-sqlite",
3
+ "version": "0.1.0",
4
+ "description": "Airdraft SQLite database adapter via better-sqlite3",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsc --watch",
21
+ "clean": "rm -rf dist",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@airdraft/core": ">=0.1.16"
33
+ },
34
+ "peerDependencies": {
35
+ "@airdraft/db-adapter": ">=0.1.0",
36
+ "better-sqlite3": ">=9.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@airdraft/db-adapter": ">=0.1.0",
40
+ "@types/better-sqlite3": "^7.6.0",
41
+ "@types/node": "^20.0.0",
42
+ "better-sqlite3": ">=9.0.0",
43
+ "typescript": "^5.4.0",
44
+ "vitest": "^2.0.0"
45
+ }
46
+ }