@sonicjs-cms/core 3.0.0-beta.22 → 3.0.0-beta.23

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.
Files changed (106) hide show
  1. package/dist/adapters.cjs +512 -0
  2. package/dist/adapters.cjs.map +1 -0
  3. package/dist/adapters.d.cts +342 -0
  4. package/dist/adapters.d.ts +342 -0
  5. package/dist/adapters.js +501 -0
  6. package/dist/adapters.js.map +1 -0
  7. package/dist/admin-documents-form.template-55V2ZGWT.js +6 -0
  8. package/dist/{admin-documents-form.template-DDSH6ROU.js.map → admin-documents-form.template-55V2ZGWT.js.map} +1 -1
  9. package/dist/admin-documents-form.template-YNAVMJCV.cjs +19 -0
  10. package/dist/{admin-layout-catalyst.template-YQ4EMF2J.js.map → admin-documents-form.template-YNAVMJCV.cjs.map} +1 -1
  11. package/dist/admin-layout-catalyst.template-3XZXDGF7.cjs +21 -0
  12. package/dist/{admin-layout-catalyst.template-KDHKVLXR.cjs.map → admin-layout-catalyst.template-3XZXDGF7.cjs.map} +1 -1
  13. package/dist/admin-layout-catalyst.template-6UQGLCKI.js +7 -0
  14. package/dist/{admin-documents-form.template-LSZKGA5J.cjs.map → admin-layout-catalyst.template-6UQGLCKI.js.map} +1 -1
  15. package/dist/{chunk-I2DM27HP.js → chunk-2MIYYAO4.js} +49 -4
  16. package/dist/chunk-2MIYYAO4.js.map +1 -0
  17. package/dist/{chunk-EUJAEB6W.cjs → chunk-4WMZGHIC.cjs} +53 -4
  18. package/dist/chunk-4WMZGHIC.cjs.map +1 -0
  19. package/dist/{chunk-2FCKKOA5.js → chunk-A46IRC5A.js} +3 -3
  20. package/dist/chunk-A46IRC5A.js.map +1 -0
  21. package/dist/{chunk-PYZYU7EK.cjs → chunk-AT4ZQGEZ.cjs} +17 -2
  22. package/dist/chunk-AT4ZQGEZ.cjs.map +1 -0
  23. package/dist/{chunk-CRGUD4KC.cjs → chunk-BBEKHLTW.cjs} +9 -9
  24. package/dist/{chunk-CRGUD4KC.cjs.map → chunk-BBEKHLTW.cjs.map} +1 -1
  25. package/dist/{chunk-RMRJGMDE.js → chunk-BHND5PEV.js} +3 -3
  26. package/dist/{chunk-RMRJGMDE.js.map → chunk-BHND5PEV.js.map} +1 -1
  27. package/dist/{chunk-OBA2RYZN.js → chunk-BJROXF4N.js} +3 -3
  28. package/dist/{chunk-OBA2RYZN.js.map → chunk-BJROXF4N.js.map} +1 -1
  29. package/dist/{chunk-QWSR4THH.cjs → chunk-C3ZQP5NW.cjs} +12 -5
  30. package/dist/chunk-C3ZQP5NW.cjs.map +1 -0
  31. package/dist/{chunk-JFLZHOQP.cjs → chunk-CTYLCWMO.cjs} +486 -409
  32. package/dist/chunk-CTYLCWMO.cjs.map +1 -0
  33. package/dist/{chunk-CHWT5KBK.js → chunk-DIBCKCUE.js} +6 -6
  34. package/dist/{chunk-CHWT5KBK.js.map → chunk-DIBCKCUE.js.map} +1 -1
  35. package/dist/{chunk-73O4GPJ6.js → chunk-DPLJYTHE.js} +12 -5
  36. package/dist/chunk-DPLJYTHE.js.map +1 -0
  37. package/dist/{chunk-PXNTCCPE.cjs → chunk-E66UVONV.cjs} +8 -8
  38. package/dist/{chunk-PXNTCCPE.cjs.map → chunk-E66UVONV.cjs.map} +1 -1
  39. package/dist/{chunk-FTXGZ3BF.cjs → chunk-IS54FPB7.cjs} +18 -18
  40. package/dist/{chunk-FTXGZ3BF.cjs.map → chunk-IS54FPB7.cjs.map} +1 -1
  41. package/dist/{chunk-N32OWET6.cjs → chunk-JMJM7DMM.cjs} +5 -5
  42. package/dist/{chunk-N32OWET6.cjs.map → chunk-JMJM7DMM.cjs.map} +1 -1
  43. package/dist/{chunk-DOMXI5PM.js → chunk-KS24NPPL.js} +17 -2
  44. package/dist/chunk-KS24NPPL.js.map +1 -0
  45. package/dist/{chunk-MGHHPNJN.cjs → chunk-RFBW5RRH.cjs} +11 -59
  46. package/dist/chunk-RFBW5RRH.cjs.map +1 -0
  47. package/dist/{chunk-6KNTZ67K.js → chunk-SK5CHFJP.js} +192 -116
  48. package/dist/chunk-SK5CHFJP.js.map +1 -0
  49. package/dist/{chunk-5V62WT6M.js → chunk-THFZSDIU.js} +4 -2
  50. package/dist/chunk-THFZSDIU.js.map +1 -0
  51. package/dist/{chunk-JQISFW6U.js → chunk-URIBTX27.js} +3 -3
  52. package/dist/{chunk-JQISFW6U.js.map → chunk-URIBTX27.js.map} +1 -1
  53. package/dist/{chunk-K623Q6WD.cjs → chunk-UYJQDDEE.cjs} +4 -2
  54. package/dist/chunk-UYJQDDEE.cjs.map +1 -0
  55. package/dist/{chunk-3CT3SUR4.cjs → chunk-VGHEKIJL.cjs} +3 -3
  56. package/dist/chunk-VGHEKIJL.cjs.map +1 -0
  57. package/dist/{chunk-GB2Q2DNK.js → chunk-XXRR54AB.js} +5 -49
  58. package/dist/chunk-XXRR54AB.js.map +1 -0
  59. package/dist/{define-plugin-DsKiGu1q.d.cts → define-plugin-DMcvEy_C.d.cts} +33 -0
  60. package/dist/{define-plugin-BYIey6sp.d.ts → define-plugin-D_6D9vVZ.d.ts} +33 -0
  61. package/dist/index.cjs +1758 -326
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +2 -2
  64. package/dist/index.d.ts +2 -2
  65. package/dist/index.js +1453 -21
  66. package/dist/index.js.map +1 -1
  67. package/dist/middleware.cjs +34 -34
  68. package/dist/middleware.js +5 -5
  69. package/dist/migrations-XQ6EX6HX.js +4 -0
  70. package/dist/{migrations-HLK2QF4T.js.map → migrations-XQ6EX6HX.js.map} +1 -1
  71. package/dist/migrations-ZD7Y5NUC.cjs +13 -0
  72. package/dist/{migrations-UIMA6ZL6.cjs.map → migrations-ZD7Y5NUC.cjs.map} +1 -1
  73. package/dist/plugins.cjs +40 -40
  74. package/dist/plugins.d.cts +1 -1
  75. package/dist/plugins.d.ts +1 -1
  76. package/dist/plugins.js +3 -3
  77. package/dist/routes.cjs +29 -29
  78. package/dist/routes.js +9 -9
  79. package/dist/services.cjs +18 -18
  80. package/dist/services.js +2 -2
  81. package/dist/templates.cjs +17 -17
  82. package/dist/templates.js +3 -3
  83. package/dist/utils.cjs +3 -3
  84. package/dist/utils.js +1 -1
  85. package/migrations/0001_core.sql +7 -1
  86. package/package.json +10 -3
  87. package/dist/admin-documents-form.template-DDSH6ROU.js +0 -6
  88. package/dist/admin-documents-form.template-LSZKGA5J.cjs +0 -19
  89. package/dist/admin-layout-catalyst.template-KDHKVLXR.cjs +0 -21
  90. package/dist/admin-layout-catalyst.template-YQ4EMF2J.js +0 -7
  91. package/dist/chunk-2FCKKOA5.js.map +0 -1
  92. package/dist/chunk-3CT3SUR4.cjs.map +0 -1
  93. package/dist/chunk-5V62WT6M.js.map +0 -1
  94. package/dist/chunk-6KNTZ67K.js.map +0 -1
  95. package/dist/chunk-73O4GPJ6.js.map +0 -1
  96. package/dist/chunk-DOMXI5PM.js.map +0 -1
  97. package/dist/chunk-EUJAEB6W.cjs.map +0 -1
  98. package/dist/chunk-GB2Q2DNK.js.map +0 -1
  99. package/dist/chunk-I2DM27HP.js.map +0 -1
  100. package/dist/chunk-JFLZHOQP.cjs.map +0 -1
  101. package/dist/chunk-K623Q6WD.cjs.map +0 -1
  102. package/dist/chunk-MGHHPNJN.cjs.map +0 -1
  103. package/dist/chunk-PYZYU7EK.cjs.map +0 -1
  104. package/dist/chunk-QWSR4THH.cjs.map +0 -1
  105. package/dist/migrations-HLK2QF4T.js +0 -4
  106. package/dist/migrations-UIMA6ZL6.cjs +0 -13
@@ -0,0 +1,512 @@
1
+ 'use strict';
2
+
3
+ require('./chunk-IGJUBJBW.cjs');
4
+ var Database = require('better-sqlite3');
5
+ var fs = require('fs');
6
+ var path = require('path');
7
+ var url = require('url');
8
+ var promises = require('fs/promises');
9
+ var hono = require('hono');
10
+
11
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var Database__default = /*#__PURE__*/_interopDefault(Database);
15
+
16
+ function migrationsDir() {
17
+ let dir;
18
+ try {
19
+ dir = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('adapters.cjs', document.baseURI).href))));
20
+ } catch {
21
+ dir = __dirname;
22
+ }
23
+ for (let i = 0; i < 6; i++) {
24
+ const candidate = path.join(dir, "migrations");
25
+ if (fs.existsSync(candidate)) return candidate;
26
+ dir = path.join(dir, "..");
27
+ }
28
+ throw new Error(`Cannot find migrations directory (searched from ${path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('adapters.cjs', document.baseURI).href))))})`);
29
+ }
30
+ var ORDERED_MIGRATIONS = ["0001_core.sql", "0002_documents.sql"];
31
+ function coerce(v) {
32
+ if (v === void 0 || v === null) return null;
33
+ if (typeof v === "boolean") return v ? 1 : 0;
34
+ return v;
35
+ }
36
+ var SqliteStatement = class _SqliteStatement {
37
+ constructor(sqlite, sql, binds = []) {
38
+ this.sqlite = sqlite;
39
+ this.sql = sql;
40
+ this.binds = binds;
41
+ }
42
+ sqlite;
43
+ sql;
44
+ binds;
45
+ bind(...args) {
46
+ return new _SqliteStatement(this.sqlite, this.sql, args.map(coerce));
47
+ }
48
+ async run() {
49
+ const t0 = Date.now();
50
+ const stmt = this.sqlite.prepare(this.sql);
51
+ const info = stmt.run(...this.binds);
52
+ return {
53
+ results: [],
54
+ success: true,
55
+ meta: {
56
+ duration: Date.now() - t0,
57
+ rows_read: 0,
58
+ rows_written: info.changes,
59
+ last_row_id: Number(info.lastInsertRowid),
60
+ changed_db: info.changes > 0,
61
+ changes: info.changes
62
+ }
63
+ };
64
+ }
65
+ async all() {
66
+ const t0 = Date.now();
67
+ const results = this.sqlite.prepare(this.sql).all(...this.binds);
68
+ return {
69
+ results,
70
+ success: true,
71
+ meta: {
72
+ duration: Date.now() - t0,
73
+ rows_read: results.length,
74
+ rows_written: 0,
75
+ last_row_id: 0,
76
+ changed_db: false,
77
+ changes: 0
78
+ }
79
+ };
80
+ }
81
+ async first(colName) {
82
+ const row = this.sqlite.prepare(this.sql).get(...this.binds);
83
+ if (row == null) return null;
84
+ return colName ? row[colName] : row;
85
+ }
86
+ // Returns rows as arrays (not objects) — used by drizzle-orm/d1 adapter.
87
+ // Mirrors D1PreparedStatement.raw<T = unknown[]>(): Promise<T[]>
88
+ async raw() {
89
+ return this.sqlite.prepare(this.sql).raw().all(...this.binds);
90
+ }
91
+ // Used internally by batch() to execute within a shared transaction.
92
+ execInBatch() {
93
+ this.sqlite.prepare(this.sql).run(...this.binds);
94
+ }
95
+ };
96
+ function buildDriver(sqlite, path) {
97
+ sqlite.pragma("foreign_keys = OFF");
98
+ if (path !== ":memory:") {
99
+ sqlite.pragma("journal_mode = WAL");
100
+ }
101
+ return {
102
+ path,
103
+ prepare(sql) {
104
+ return new SqliteStatement(sqlite, sql);
105
+ },
106
+ async batch(statements) {
107
+ const tx = sqlite.transaction((stmts) => {
108
+ for (const s of stmts) s.execInBatch();
109
+ });
110
+ tx(statements);
111
+ return statements.map(() => ({
112
+ results: [],
113
+ success: true,
114
+ meta: { duration: 0, rows_read: 0, rows_written: 0, last_row_id: 0, changed_db: true, changes: 0 }
115
+ }));
116
+ },
117
+ async exec(query) {
118
+ const t0 = Date.now();
119
+ sqlite.exec(query);
120
+ return { count: 1, duration: Date.now() - t0 };
121
+ },
122
+ async dump() {
123
+ const buf = sqlite.serialize();
124
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
125
+ },
126
+ close() {
127
+ sqlite.close();
128
+ }
129
+ };
130
+ }
131
+ async function createSqliteDriver(options = {}) {
132
+ const { dbPath = ":memory:", autoMigrate = true, migrationsPath } = options;
133
+ if (dbPath !== ":memory:") {
134
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
135
+ }
136
+ const sqlite = new Database__default.default(dbPath);
137
+ const driver = buildDriver(sqlite, dbPath);
138
+ if (autoMigrate && shouldRunMigrations(sqlite)) {
139
+ applyMigrations(sqlite, migrationsPath ?? migrationsDir());
140
+ }
141
+ return driver;
142
+ }
143
+ function shouldRunMigrations(sqlite) {
144
+ try {
145
+ const row = sqlite.prepare(
146
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='auth_user'"
147
+ ).get();
148
+ return row == null;
149
+ } catch {
150
+ return true;
151
+ }
152
+ }
153
+ function applyMigrations(sqlite, dir) {
154
+ for (const file of ORDERED_MIGRATIONS) {
155
+ const sql = fs.readFileSync(path.join(dir, file), "utf8");
156
+ sqlite.exec(sql);
157
+ }
158
+ }
159
+ function filePath(storageDir, key) {
160
+ const safe = key.replace(/\.\./g, "").replace(/^\/+/, "");
161
+ return path.join(storageDir, safe);
162
+ }
163
+ function metaPath(dataPath) {
164
+ return dataPath + ".meta.json";
165
+ }
166
+ function readMeta(dataPath) {
167
+ const mp = metaPath(dataPath);
168
+ if (!fs.existsSync(mp)) return {};
169
+ try {
170
+ return JSON.parse(fs.readFileSync(mp, "utf8"));
171
+ } catch {
172
+ return {};
173
+ }
174
+ }
175
+ function writeMeta(dataPath, meta) {
176
+ fs.writeFileSync(metaPath(dataPath), JSON.stringify(meta), "utf8");
177
+ }
178
+ function safeEtag(dataPath) {
179
+ try {
180
+ const s = fs.statSync(dataPath);
181
+ return `${s.size}-${s.mtimeMs}`;
182
+ } catch {
183
+ return "unknown";
184
+ }
185
+ }
186
+ function toNodeReadable(value) {
187
+ if (typeof value === "string") {
188
+ return Promise.resolve(Buffer.from(value, "utf8"));
189
+ }
190
+ if (value instanceof ArrayBuffer) {
191
+ return Promise.resolve(Buffer.from(value));
192
+ }
193
+ if (ArrayBuffer.isView(value)) {
194
+ return Promise.resolve(Buffer.from(value.buffer, value.byteOffset, value.byteLength));
195
+ }
196
+ if (value instanceof Blob) {
197
+ return value.arrayBuffer().then((ab) => Buffer.from(ab));
198
+ }
199
+ const stream = value;
200
+ return new Promise((resolve2, reject) => {
201
+ const chunks = [];
202
+ const reader = stream.getReader();
203
+ function pump() {
204
+ reader.read().then(({ done, value: chunk }) => {
205
+ if (done) {
206
+ resolve2(Buffer.concat(chunks));
207
+ return;
208
+ }
209
+ chunks.push(chunk);
210
+ pump();
211
+ }).catch(reject);
212
+ }
213
+ pump();
214
+ });
215
+ }
216
+ function nodeStreamToWebReadable(path) {
217
+ const nodeStream = fs.createReadStream(path);
218
+ return new ReadableStream({
219
+ start(controller) {
220
+ nodeStream.on("data", (chunk) => controller.enqueue(chunk instanceof Buffer ? chunk : Buffer.from(chunk)));
221
+ nodeStream.on("end", () => controller.close());
222
+ nodeStream.on("error", (err) => controller.error(err));
223
+ },
224
+ cancel() {
225
+ nodeStream.destroy();
226
+ }
227
+ });
228
+ }
229
+ function createFilesystemDriver(storageDir) {
230
+ const root = path.resolve(storageDir);
231
+ fs.mkdirSync(root, { recursive: true });
232
+ return {
233
+ async put(key, value, options = {}) {
234
+ const fp = filePath(root, key);
235
+ fs.mkdirSync(path.dirname(fp), { recursive: true });
236
+ const buf = await toNodeReadable(value);
237
+ await promises.writeFile(fp, buf);
238
+ const meta = {
239
+ httpMetadata: options.httpMetadata,
240
+ customMetadata: options.customMetadata
241
+ };
242
+ writeMeta(fp, meta);
243
+ const s = fs.statSync(fp);
244
+ return {
245
+ key,
246
+ size: s.size,
247
+ etag: `${s.size}-${s.mtimeMs}`,
248
+ httpMetadata: options.httpMetadata ?? {},
249
+ customMetadata: options.customMetadata ?? {}
250
+ };
251
+ },
252
+ async get(key) {
253
+ const fp = filePath(root, key);
254
+ if (!fs.existsSync(fp)) return null;
255
+ const meta = readMeta(fp);
256
+ const s = fs.statSync(fp);
257
+ return {
258
+ key,
259
+ size: s.size,
260
+ etag: safeEtag(fp),
261
+ httpMetadata: meta.httpMetadata ?? {},
262
+ customMetadata: meta.customMetadata ?? {},
263
+ body: nodeStreamToWebReadable(fp),
264
+ async arrayBuffer() {
265
+ const buf = await promises.readFile(fp);
266
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
267
+ },
268
+ async text() {
269
+ return promises.readFile(fp, "utf8");
270
+ }
271
+ };
272
+ },
273
+ async delete(key) {
274
+ const fp = filePath(root, key);
275
+ if (fs.existsSync(fp)) {
276
+ await promises.rm(fp, { force: true });
277
+ const mp = metaPath(fp);
278
+ if (fs.existsSync(mp)) await promises.rm(mp, { force: true });
279
+ }
280
+ },
281
+ async head(key) {
282
+ const fp = filePath(root, key);
283
+ if (!fs.existsSync(fp)) return null;
284
+ const meta = readMeta(fp);
285
+ const s = fs.statSync(fp);
286
+ return {
287
+ key,
288
+ size: s.size,
289
+ etag: safeEtag(fp),
290
+ httpMetadata: meta.httpMetadata ?? {},
291
+ customMetadata: meta.customMetadata ?? {}
292
+ };
293
+ }
294
+ };
295
+ }
296
+ function createMemoryKVDriver(options = {}) {
297
+ const { persistPath } = options;
298
+ const store = /* @__PURE__ */ new Map();
299
+ if (persistPath && fs.existsSync(persistPath)) {
300
+ try {
301
+ const raw = JSON.parse(fs.readFileSync(persistPath, "utf8"));
302
+ const now = Date.now();
303
+ for (const [k, entry] of Object.entries(raw)) {
304
+ if (entry.expiresAt == null || entry.expiresAt > now) {
305
+ store.set(k, entry);
306
+ }
307
+ }
308
+ } catch {
309
+ }
310
+ }
311
+ function persist() {
312
+ if (!persistPath) return;
313
+ const obj = {};
314
+ for (const [k, v] of store) obj[k] = v;
315
+ try {
316
+ fs.writeFileSync(persistPath, JSON.stringify(obj), "utf8");
317
+ } catch {
318
+ }
319
+ }
320
+ function isExpired(entry) {
321
+ return entry.expiresAt != null && entry.expiresAt <= Date.now();
322
+ }
323
+ function rawGet(key) {
324
+ const entry = store.get(key);
325
+ if (!entry) return null;
326
+ if (isExpired(entry)) {
327
+ store.delete(key);
328
+ return null;
329
+ }
330
+ return entry.value;
331
+ }
332
+ async function get(key, type) {
333
+ const raw = rawGet(key);
334
+ if (raw == null) return null;
335
+ if (type === "json") {
336
+ try {
337
+ return JSON.parse(raw);
338
+ } catch {
339
+ return null;
340
+ }
341
+ }
342
+ if (type === "arrayBuffer") {
343
+ const buf = Buffer.from(raw, "base64");
344
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
345
+ }
346
+ return raw;
347
+ }
348
+ async function put(key, value, options2) {
349
+ let stored;
350
+ if (typeof value === "string") {
351
+ stored = value;
352
+ } else if (value instanceof ArrayBuffer) {
353
+ stored = Buffer.from(value).toString("base64");
354
+ } else {
355
+ const chunks = [];
356
+ const reader = value.getReader();
357
+ let done = false;
358
+ while (!done) {
359
+ const result = await reader.read();
360
+ done = result.done;
361
+ if (!done) chunks.push(result.value);
362
+ }
363
+ stored = Buffer.concat(chunks).toString("base64");
364
+ }
365
+ let expiresAt;
366
+ if (options2?.expirationTtl != null) {
367
+ expiresAt = Date.now() + options2.expirationTtl * 1e3;
368
+ } else if (options2?.expiration != null) {
369
+ expiresAt = options2.expiration * 1e3;
370
+ }
371
+ store.set(key, { value: stored, expiresAt });
372
+ persist();
373
+ }
374
+ async function del(key) {
375
+ store.delete(key);
376
+ persist();
377
+ }
378
+ async function list(options2 = {}) {
379
+ const { prefix = "", limit = 1e3 } = options2;
380
+ const now = Date.now();
381
+ const keys = [];
382
+ for (const [k, entry] of store) {
383
+ if (entry.expiresAt != null && entry.expiresAt <= now) {
384
+ store.delete(k);
385
+ continue;
386
+ }
387
+ if (k.startsWith(prefix)) {
388
+ keys.push({
389
+ name: k,
390
+ expiration: entry.expiresAt != null ? Math.floor(entry.expiresAt / 1e3) : void 0
391
+ });
392
+ }
393
+ }
394
+ const sliced = keys.slice(0, limit);
395
+ return {
396
+ keys: sliced,
397
+ list_complete: sliced.length === keys.length
398
+ };
399
+ }
400
+ async function getWithMetadata(key, type) {
401
+ const value = await get(key, type);
402
+ return { value, metadata: null };
403
+ }
404
+ return {
405
+ get,
406
+ put,
407
+ delete: del,
408
+ list,
409
+ getWithMetadata
410
+ };
411
+ }
412
+
413
+ // src/adapters/queue/sync-queue-driver.ts
414
+ var _msgCounter = 0;
415
+ function makeMessage(body) {
416
+ const id = `sync-${++_msgCounter}`;
417
+ return {
418
+ id,
419
+ body,
420
+ timestamp: /* @__PURE__ */ new Date(),
421
+ ack() {
422
+ },
423
+ retry() {
424
+ }
425
+ };
426
+ }
427
+ function makeBatch(messages, queueName) {
428
+ return {
429
+ queue: queueName,
430
+ messages,
431
+ ackAll() {
432
+ messages.forEach((m) => m.ack());
433
+ },
434
+ retryAll() {
435
+ }
436
+ };
437
+ }
438
+ function createSyncQueueDriver(handler, queueName = "sync-queue") {
439
+ async function dispatch(messages) {
440
+ if (!handler) {
441
+ return;
442
+ }
443
+ try {
444
+ await handler(makeBatch(messages, queueName));
445
+ } catch (err) {
446
+ console.error(`[SyncQueue:${queueName}] Handler error:`, err);
447
+ }
448
+ }
449
+ return {
450
+ async send(body, options = {}) {
451
+ const msg = makeMessage(body);
452
+ const delay = (options.delaySeconds ?? 0) * 1e3;
453
+ if (delay > 0) {
454
+ setTimeout(() => dispatch([msg]), delay);
455
+ } else {
456
+ await dispatch([msg]);
457
+ }
458
+ },
459
+ async sendBatch(messages) {
460
+ const msgs = messages.map((m) => makeMessage(m.body));
461
+ const delays = messages.map((m) => (m.options?.delaySeconds ?? 0) * 1e3);
462
+ const maxDelay = Math.max(...delays, 0);
463
+ if (maxDelay > 0) {
464
+ setTimeout(() => dispatch(msgs), maxDelay);
465
+ } else {
466
+ await dispatch(msgs);
467
+ }
468
+ }
469
+ };
470
+ }
471
+ async function createNodeSonicApp(sonicApp, options = {}) {
472
+ const {
473
+ dbPath = "./data/sonicjs.db",
474
+ storagePath = "./data/media",
475
+ kvPersistPath,
476
+ env: extraEnv = {}
477
+ } = options;
478
+ const [db, mediaBucket] = await Promise.all([
479
+ createSqliteDriver({ dbPath }),
480
+ Promise.resolve(createFilesystemDriver(storagePath))
481
+ ]);
482
+ const cacheKV = createMemoryKVDriver({ persistPath: kvPersistPath });
483
+ const resolvedEnv = {
484
+ ...Object.fromEntries(
485
+ Object.entries(process.env).filter(([, v]) => v !== void 0)
486
+ ),
487
+ ...extraEnv
488
+ };
489
+ const wrapper = new hono.Hono();
490
+ wrapper.use("*", async (c, next) => {
491
+ const env = c.env;
492
+ env.DB = db;
493
+ env.MEDIA_BUCKET = mediaBucket;
494
+ env.CACHE_KV = cacheKV;
495
+ for (const [k, v] of Object.entries(resolvedEnv)) {
496
+ if (v !== void 0 && env[k] === void 0) {
497
+ env[k] = v;
498
+ }
499
+ }
500
+ return next();
501
+ });
502
+ wrapper.route("/", sonicApp);
503
+ return wrapper;
504
+ }
505
+
506
+ exports.createFilesystemDriver = createFilesystemDriver;
507
+ exports.createMemoryKVDriver = createMemoryKVDriver;
508
+ exports.createNodeSonicApp = createNodeSonicApp;
509
+ exports.createSqliteDriver = createSqliteDriver;
510
+ exports.createSyncQueueDriver = createSyncQueueDriver;
511
+ //# sourceMappingURL=adapters.cjs.map
512
+ //# sourceMappingURL=adapters.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/db/sqlite-driver.ts","../src/adapters/storage/filesystem-driver.ts","../src/adapters/kv/memory-kv-driver.ts","../src/adapters/queue/sync-queue-driver.ts","../src/adapters/node-server.ts"],"names":["dirname","fileURLToPath","join","existsSync","mkdirSync","Database","readFileSync","writeFileSync","statSync","resolve","createReadStream","writeFile","readFile","rm","options","Hono"],"mappings":"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","file":"adapters.cjs","sourcesContent":["/**\n * Runtime SQLite driver — D1Database-compatible adapter backed by better-sqlite3.\n *\n * Designed for Tier 1 self-hosted deployments (Docker, VPS, Node/Bun).\n * The Cloudflare Workers path never imports this file.\n *\n * Usage:\n * import { createSqliteDriver } from '@sonicjs-cms/core/adapters'\n * const db = await createSqliteDriver('./data/sonicjs.db')\n * // Pass `db` anywhere SonicJS expects a D1Database binding.\n */\n\nimport Database from 'better-sqlite3'\nimport { readFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n// Walk up from the compiled file's location until we find a migrations/ directory.\n// tsup flattens to dist/adapters.js (not dist/adapters/db/), so a fixed relative\n// path would differ between source and compiled. Walking is reliable for both.\nfunction migrationsDir(): string {\n let dir: string\n try {\n dir = dirname(fileURLToPath(import.meta.url))\n } catch {\n dir = __dirname\n }\n for (let i = 0; i < 6; i++) {\n const candidate = join(dir, 'migrations')\n if (existsSync(candidate)) return candidate\n dir = join(dir, '..')\n }\n throw new Error(`Cannot find migrations directory (searched from ${dirname(fileURLToPath(import.meta.url))})`)\n}\n\nconst ORDERED_MIGRATIONS = ['0001_core.sql', '0002_documents.sql']\n\n// D1-compatible value types accepted by better-sqlite3.\ntype BindValue = number | string | bigint | Buffer | null\n\nfunction coerce(v: unknown): BindValue {\n if (v === undefined || v === null) return null\n if (typeof v === 'boolean') return v ? 1 : 0\n return v as BindValue\n}\n\n// ---------------------------------------------------------------------------\n// D1Result / D1ExecResult shapes (mirrors @cloudflare/workers-types, locally\n// defined so this file has zero runtime dep on that package).\n// ---------------------------------------------------------------------------\n\ninterface D1Meta {\n duration: number\n size_after?: number\n rows_read: number\n rows_written: number\n last_row_id: number\n changed_db: boolean\n changes: number\n}\n\ninterface D1Result<T = unknown> {\n results: T[]\n success: boolean\n meta: D1Meta\n}\n\n// ---------------------------------------------------------------------------\n// SqliteStatement — wraps a prepared query with bound parameters.\n// Implements the D1PreparedStatement interface.\n// ---------------------------------------------------------------------------\n\nclass SqliteStatement {\n constructor(\n private readonly sqlite: Database.Database,\n private readonly sql: string,\n private readonly binds: BindValue[] = [],\n ) {}\n\n bind(...args: unknown[]): SqliteStatement {\n return new SqliteStatement(this.sqlite, this.sql, args.map(coerce))\n }\n\n async run(): Promise<D1Result<never>> {\n const t0 = Date.now()\n const stmt = this.sqlite.prepare(this.sql)\n const info = stmt.run(...this.binds)\n return {\n results: [],\n success: true,\n meta: {\n duration: Date.now() - t0,\n rows_read: 0,\n rows_written: info.changes,\n last_row_id: Number(info.lastInsertRowid),\n changed_db: info.changes > 0,\n changes: info.changes,\n },\n }\n }\n\n async all<T = unknown>(): Promise<D1Result<T>> {\n const t0 = Date.now()\n const results = this.sqlite.prepare(this.sql).all(...this.binds) as T[]\n return {\n results,\n success: true,\n meta: {\n duration: Date.now() - t0,\n rows_read: results.length,\n rows_written: 0,\n last_row_id: 0,\n changed_db: false,\n changes: 0,\n },\n }\n }\n\n async first<T = unknown>(colName?: string): Promise<T | null> {\n const row = this.sqlite.prepare(this.sql).get(...this.binds) as Record<string, unknown> | undefined\n if (row == null) return null\n return (colName ? (row[colName] as T) : (row as unknown as T))\n }\n\n // Returns rows as arrays (not objects) — used by drizzle-orm/d1 adapter.\n // Mirrors D1PreparedStatement.raw<T = unknown[]>(): Promise<T[]>\n async raw<T = unknown[]>(): Promise<T[]> {\n return this.sqlite.prepare(this.sql).raw().all(...this.binds) as T[]\n }\n\n // Used internally by batch() to execute within a shared transaction.\n execInBatch(): void {\n this.sqlite.prepare(this.sql).run(...this.binds)\n }\n}\n\n// ---------------------------------------------------------------------------\n// SqliteDriver — the public D1Database-compatible runtime adapter.\n// ---------------------------------------------------------------------------\n\nexport interface SqliteDriver {\n prepare(sql: string): SqliteStatement\n batch<T = unknown>(statements: SqliteStatement[]): Promise<Array<D1Result<T>>>\n exec(query: string): Promise<{ count: number; duration: number }>\n dump(): Promise<ArrayBuffer>\n /** Close the underlying database file handle (for graceful shutdown). */\n close(): void\n /** Path passed at creation time (`:memory:` for in-memory). */\n readonly path: string\n}\n\nfunction buildDriver(sqlite: Database.Database, path: string): SqliteDriver {\n // D1 does NOT reliably enforce foreign keys; services delete derived rows\n // explicitly. Mirror that behaviour so self-host tests match prod.\n sqlite.pragma('foreign_keys = OFF')\n // WAL mode gives much better write concurrency on a persistent file.\n if (path !== ':memory:') {\n sqlite.pragma('journal_mode = WAL')\n }\n\n return {\n path,\n\n prepare(sql: string) {\n return new SqliteStatement(sqlite, sql)\n },\n\n async batch<T = unknown>(statements: SqliteStatement[]): Promise<Array<D1Result<T>>> {\n // D1 batches are atomic and sequential under a single writer.\n const tx = sqlite.transaction((stmts: SqliteStatement[]) => {\n for (const s of stmts) s.execInBatch()\n })\n tx(statements)\n return statements.map(() => ({\n results: [] as T[],\n success: true,\n meta: { duration: 0, rows_read: 0, rows_written: 0, last_row_id: 0, changed_db: true, changes: 0 },\n }))\n },\n\n async exec(query: string) {\n const t0 = Date.now()\n sqlite.exec(query)\n return { count: 1, duration: Date.now() - t0 }\n },\n\n async dump() {\n // better-sqlite3 serialize() returns a Buffer of the full DB.\n const buf: Buffer = sqlite.serialize()\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n },\n\n close() {\n sqlite.close()\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// createSqliteDriver — public factory.\n// ---------------------------------------------------------------------------\n\nexport interface SqliteDriverOptions {\n /**\n * Absolute or relative path to the SQLite database file, or `:memory:` for\n * an in-memory database. Defaults to `:memory:`.\n */\n dbPath?: string\n /**\n * When true, applies the bundled SonicJS migrations (0001_core.sql,\n * 0002_documents.sql) before returning. Defaults to true when the DB does\n * not yet contain the `auth_user` table (i.e. first boot).\n * Set to false if you manage migrations yourself via an external tool.\n */\n autoMigrate?: boolean\n /**\n * Directory that contains the *.sql migration files. Defaults to the\n * migrations/ folder bundled with `@sonicjs-cms/core`.\n */\n migrationsPath?: string\n}\n\n/**\n * Create a D1Database-compatible SQLite driver for self-hosted deployments.\n *\n * ```ts\n * const db = await createSqliteDriver({ dbPath: './data/sonicjs.db' })\n * // db satisfies D1Database — pass it directly as c.env.DB\n * ```\n */\nexport async function createSqliteDriver(options: SqliteDriverOptions = {}): Promise<SqliteDriver> {\n const { dbPath = ':memory:', autoMigrate = true, migrationsPath } = options\n\n // Ensure parent directory exists for a file-based DB.\n if (dbPath !== ':memory:') {\n mkdirSync(dirname(dbPath), { recursive: true })\n }\n\n const sqlite = new Database(dbPath)\n const driver = buildDriver(sqlite, dbPath)\n\n if (autoMigrate && shouldRunMigrations(sqlite)) {\n applyMigrations(sqlite, migrationsPath ?? migrationsDir())\n }\n\n return driver\n}\n\n/** Returns true when the core schema hasn't been applied yet. */\nfunction shouldRunMigrations(sqlite: Database.Database): boolean {\n try {\n const row = sqlite.prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name='auth_user'\",\n ).get()\n return row == null\n } catch {\n return true\n }\n}\n\nfunction applyMigrations(sqlite: Database.Database, dir: string): void {\n for (const file of ORDERED_MIGRATIONS) {\n const sql = readFileSync(join(dir, file), 'utf8')\n sqlite.exec(sql)\n }\n}\n","/**\n * Filesystem storage driver — R2Bucket-compatible adapter for self-hosted deployments.\n *\n * Files land at `{storageDir}/{key}` preserving path structure.\n * Metadata (httpMetadata + customMetadata) is stored alongside as `{key}.meta.json`.\n *\n * Usage:\n * import { createFilesystemDriver } from '@sonicjs-cms/core/adapters'\n * const bucket = createFilesystemDriver('./data/media')\n * // Pass `bucket` anywhere SonicJS expects the MEDIA_BUCKET R2Bucket binding.\n */\n\nimport { createReadStream, existsSync, mkdirSync, rmSync, statSync, writeFileSync, readFileSync } from 'node:fs'\nimport { writeFile, rm, readFile, stat } from 'node:fs/promises'\nimport { dirname, join, resolve } from 'node:path'\nimport { Readable } from 'node:stream'\n\n// ---------------------------------------------------------------------------\n// R2 metadata shapes (locally defined — no runtime dep on workers-types).\n// ---------------------------------------------------------------------------\n\nexport interface R2HttpMetadata {\n contentType?: string\n contentDisposition?: string\n contentEncoding?: string\n contentLanguage?: string\n cacheControl?: string\n cacheExpiry?: Date\n}\n\nexport interface R2StoredMeta {\n httpMetadata?: R2HttpMetadata\n customMetadata?: Record<string, string>\n}\n\n// ---------------------------------------------------------------------------\n// R2ObjectBody — the shape callers destructure from get().\n// ---------------------------------------------------------------------------\n\nexport interface R2ObjectBody {\n key: string\n size: number\n etag: string\n httpMetadata: R2HttpMetadata\n customMetadata: Record<string, string>\n /** Web-standard ReadableStream over the file contents. */\n body: ReadableStream\n /** Convenience: consume as ArrayBuffer. */\n arrayBuffer(): Promise<ArrayBuffer>\n /** Convenience: consume as text. */\n text(): Promise<string>\n}\n\n// ---------------------------------------------------------------------------\n// R2Object — metadata-only (returned by head()).\n// ---------------------------------------------------------------------------\n\nexport interface R2ObjectInfo {\n key: string\n size: number\n etag: string\n httpMetadata: R2HttpMetadata\n customMetadata: Record<string, string>\n}\n\n// ---------------------------------------------------------------------------\n// StorageDriver — the public R2Bucket-compatible interface.\n// ---------------------------------------------------------------------------\n\nexport interface PutOptions {\n httpMetadata?: R2HttpMetadata\n customMetadata?: Record<string, string>\n}\n\nexport interface StorageDriver {\n put(key: string, value: ArrayBuffer | ReadableStream | Blob | ArrayBufferView | string, options?: PutOptions): Promise<R2ObjectInfo>\n get(key: string): Promise<R2ObjectBody | null>\n delete(key: string): Promise<void>\n head(key: string): Promise<R2ObjectInfo | null>\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction filePath(storageDir: string, key: string): string {\n // Prevent path traversal — strip any leading slashes / `..` segments.\n const safe = key.replace(/\\.\\./g, '').replace(/^\\/+/, '')\n return join(storageDir, safe)\n}\n\nfunction metaPath(dataPath: string): string {\n return dataPath + '.meta.json'\n}\n\nfunction readMeta(dataPath: string): R2StoredMeta {\n const mp = metaPath(dataPath)\n if (!existsSync(mp)) return {}\n try {\n return JSON.parse(readFileSync(mp, 'utf8')) as R2StoredMeta\n } catch {\n return {}\n }\n}\n\nfunction writeMeta(dataPath: string, meta: R2StoredMeta): void {\n writeFileSync(metaPath(dataPath), JSON.stringify(meta), 'utf8')\n}\n\nfunction safeEtag(dataPath: string): string {\n try {\n const s = statSync(dataPath)\n return `${s.size}-${s.mtimeMs}`\n } catch {\n return 'unknown'\n }\n}\n\nfunction toNodeReadable(value: ReadableStream | ArrayBuffer | Blob | ArrayBufferView | string): Promise<Buffer> {\n if (typeof value === 'string') {\n return Promise.resolve(Buffer.from(value, 'utf8'))\n }\n if (value instanceof ArrayBuffer) {\n return Promise.resolve(Buffer.from(value))\n }\n if (ArrayBuffer.isView(value)) {\n return Promise.resolve(Buffer.from(value.buffer, value.byteOffset, value.byteLength))\n }\n if (value instanceof Blob) {\n return value.arrayBuffer().then(ab => Buffer.from(ab))\n }\n // ReadableStream (web standard)\n const stream = value as ReadableStream\n return new Promise((resolve, reject) => {\n const chunks: Uint8Array[] = []\n const reader = stream.getReader()\n function pump() {\n reader.read().then(({ done, value: chunk }) => {\n if (done) { resolve(Buffer.concat(chunks)); return }\n chunks.push(chunk)\n pump()\n }).catch(reject)\n }\n pump()\n })\n}\n\nfunction nodeStreamToWebReadable(path: string): ReadableStream {\n const nodeStream = createReadStream(path)\n return new ReadableStream({\n start(controller) {\n nodeStream.on('data', (chunk) => controller.enqueue(chunk instanceof Buffer ? chunk : Buffer.from(chunk)))\n nodeStream.on('end', () => controller.close())\n nodeStream.on('error', (err) => controller.error(err))\n },\n cancel() {\n nodeStream.destroy()\n },\n })\n}\n\n// ---------------------------------------------------------------------------\n// createFilesystemDriver — public factory.\n// ---------------------------------------------------------------------------\n\n/**\n * Create an R2Bucket-compatible filesystem storage driver.\n *\n * ```ts\n * const bucket = createFilesystemDriver('./data/media')\n * // bucket satisfies R2Bucket — pass it as c.env.MEDIA_BUCKET\n * ```\n *\n * @param storageDir Absolute or relative path to the root storage directory.\n * Created automatically if it doesn't exist.\n */\nexport function createFilesystemDriver(storageDir: string): StorageDriver {\n const root = resolve(storageDir)\n mkdirSync(root, { recursive: true })\n\n return {\n async put(key, value, options = {}): Promise<R2ObjectInfo> {\n const fp = filePath(root, key)\n mkdirSync(dirname(fp), { recursive: true })\n\n const buf = await toNodeReadable(value)\n await writeFile(fp, buf)\n\n const meta: R2StoredMeta = {\n httpMetadata: options.httpMetadata,\n customMetadata: options.customMetadata,\n }\n writeMeta(fp, meta)\n\n const s = statSync(fp)\n return {\n key,\n size: s.size,\n etag: `${s.size}-${s.mtimeMs}`,\n httpMetadata: options.httpMetadata ?? {},\n customMetadata: options.customMetadata ?? {},\n }\n },\n\n async get(key): Promise<R2ObjectBody | null> {\n const fp = filePath(root, key)\n if (!existsSync(fp)) return null\n\n const meta = readMeta(fp)\n const s = statSync(fp)\n\n return {\n key,\n size: s.size,\n etag: safeEtag(fp),\n httpMetadata: meta.httpMetadata ?? {},\n customMetadata: meta.customMetadata ?? {},\n body: nodeStreamToWebReadable(fp),\n async arrayBuffer() {\n const buf = await readFile(fp)\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n },\n async text() {\n return readFile(fp, 'utf8')\n },\n }\n },\n\n async delete(key): Promise<void> {\n const fp = filePath(root, key)\n if (existsSync(fp)) {\n await rm(fp, { force: true })\n const mp = metaPath(fp)\n if (existsSync(mp)) await rm(mp, { force: true })\n }\n },\n\n async head(key): Promise<R2ObjectInfo | null> {\n const fp = filePath(root, key)\n if (!existsSync(fp)) return null\n\n const meta = readMeta(fp)\n const s = statSync(fp)\n\n return {\n key,\n size: s.size,\n etag: safeEtag(fp),\n httpMetadata: meta.httpMetadata ?? {},\n customMetadata: meta.customMetadata ?? {},\n }\n },\n }\n}\n","/**\n * In-memory KV driver — KVNamespace-compatible adapter for self-hosted deployments.\n *\n * Values survive the process lifetime. For cross-restart persistence, wire the\n * optional `persistPath` to a JSON file; entries are written on every put/delete.\n *\n * Usage:\n * import { createMemoryKVDriver } from '@sonicjs-cms/core/adapters'\n * const kv = createMemoryKVDriver()\n * // Pass `kv` anywhere SonicJS expects a KVNamespace (CACHE_KV binding).\n */\n\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\n\n// ---------------------------------------------------------------------------\n// Stored entry — value + optional expiry in epoch ms.\n// ---------------------------------------------------------------------------\n\ninterface KVEntry {\n value: string\n expiresAt?: number\n}\n\n// ---------------------------------------------------------------------------\n// KVDriver — the public KVNamespace-compatible interface.\n// ---------------------------------------------------------------------------\n\nexport interface KVPutOptions {\n expirationTtl?: number\n /** Absolute Unix timestamp (seconds) at which the key expires. */\n expiration?: number\n metadata?: unknown\n}\n\nexport interface KVListOptions {\n prefix?: string\n limit?: number\n cursor?: string\n}\n\nexport interface KVListResult {\n keys: Array<{ name: string; expiration?: number }>\n list_complete: boolean\n cursor?: string\n}\n\nexport interface KVGetWithMetadata<T> {\n value: T | null\n metadata: unknown\n}\n\nexport interface KVDriver {\n get(key: string): Promise<string | null>\n get(key: string, type: 'text'): Promise<string | null>\n get<T = unknown>(key: string, type: 'json'): Promise<T | null>\n get(key: string, type: 'arrayBuffer'): Promise<ArrayBuffer | null>\n put(key: string, value: string | ArrayBuffer | ReadableStream, options?: KVPutOptions): Promise<void>\n delete(key: string): Promise<void>\n list(options?: KVListOptions): Promise<KVListResult>\n getWithMetadata<T = unknown>(key: string, type: 'json'): Promise<KVGetWithMetadata<T>>\n}\n\n// ---------------------------------------------------------------------------\n// createMemoryKVDriver — public factory.\n// ---------------------------------------------------------------------------\n\nexport interface MemoryKVOptions {\n /**\n * Path to a JSON file used for persistence across restarts.\n * The file is read on startup and written on every put/delete.\n * Omit for a pure in-memory (ephemeral) store.\n */\n persistPath?: string\n}\n\nexport function createMemoryKVDriver(options: MemoryKVOptions = {}): KVDriver {\n const { persistPath } = options\n const store = new Map<string, KVEntry>()\n\n // Load persisted entries from disk.\n if (persistPath && existsSync(persistPath)) {\n try {\n const raw = JSON.parse(readFileSync(persistPath, 'utf8')) as Record<string, KVEntry>\n const now = Date.now()\n for (const [k, entry] of Object.entries(raw)) {\n if (entry.expiresAt == null || entry.expiresAt > now) {\n store.set(k, entry)\n }\n }\n } catch {\n // Corrupt file — start empty.\n }\n }\n\n function persist(): void {\n if (!persistPath) return\n const obj: Record<string, KVEntry> = {}\n for (const [k, v] of store) obj[k] = v\n try {\n writeFileSync(persistPath, JSON.stringify(obj), 'utf8')\n } catch {\n // Non-fatal — best-effort persistence.\n }\n }\n\n function isExpired(entry: KVEntry): boolean {\n return entry.expiresAt != null && entry.expiresAt <= Date.now()\n }\n\n function rawGet(key: string): string | null {\n const entry = store.get(key)\n if (!entry) return null\n if (isExpired(entry)) {\n store.delete(key)\n return null\n }\n return entry.value\n }\n\n async function get(key: string, type?: string): Promise<unknown> {\n const raw = rawGet(key)\n if (raw == null) return null\n if (type === 'json') {\n try { return JSON.parse(raw) } catch { return null }\n }\n if (type === 'arrayBuffer') {\n const buf = Buffer.from(raw, 'base64')\n return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)\n }\n return raw\n }\n\n async function put(key: string, value: string | ArrayBuffer | ReadableStream, options?: KVPutOptions): Promise<void> {\n let stored: string\n if (typeof value === 'string') {\n stored = value\n } else if (value instanceof ArrayBuffer) {\n stored = Buffer.from(value).toString('base64')\n } else {\n // ReadableStream — consume and base64-encode.\n const chunks: Uint8Array[] = []\n const reader = (value as ReadableStream).getReader()\n let done = false\n while (!done) {\n const result = await reader.read()\n done = result.done\n if (!done) chunks.push(result.value)\n }\n stored = Buffer.concat(chunks).toString('base64')\n }\n\n let expiresAt: number | undefined\n if (options?.expirationTtl != null) {\n expiresAt = Date.now() + options.expirationTtl * 1000\n } else if (options?.expiration != null) {\n expiresAt = options.expiration * 1000\n }\n\n store.set(key, { value: stored, expiresAt })\n persist()\n }\n\n async function del(key: string): Promise<void> {\n store.delete(key)\n persist()\n }\n\n async function list(options: KVListOptions = {}): Promise<KVListResult> {\n const { prefix = '', limit = 1000 } = options\n const now = Date.now()\n const keys: Array<{ name: string; expiration?: number }> = []\n\n for (const [k, entry] of store) {\n if (entry.expiresAt != null && entry.expiresAt <= now) {\n store.delete(k)\n continue\n }\n if (k.startsWith(prefix)) {\n keys.push({\n name: k,\n expiration: entry.expiresAt != null ? Math.floor(entry.expiresAt / 1000) : undefined,\n })\n }\n }\n\n const sliced = keys.slice(0, limit)\n return {\n keys: sliced,\n list_complete: sliced.length === keys.length,\n }\n }\n\n async function getWithMetadata<T>(key: string, type: string): Promise<KVGetWithMetadata<T>> {\n const value = (await get(key, type)) as T | null\n return { value, metadata: null }\n }\n\n return {\n get: get as KVDriver['get'],\n put,\n delete: del,\n list,\n getWithMetadata: getWithMetadata as KVDriver['getWithMetadata'],\n }\n}\n","/**\n * Synchronous Queue driver — Cloudflare Queue-compatible adapter for self-hosted deployments.\n *\n * Instead of enqueuing messages to a remote queue, `send()` immediately invokes\n * the registered handler in-process. This means \"queued\" work (e.g. scheduled\n * email jobs) runs synchronously before the HTTP response is returned — acceptable\n * for low-volume self-host deployments.\n *\n * Usage:\n * import { createSyncQueueDriver } from '@sonicjs-cms/core/adapters'\n *\n * const emailQueue = createSyncQueueDriver(async (messages) => {\n * // same body as handleEmailQueueMessage() from email-templates-plugin\n * for (const msg of messages) {\n * await processJob(msg.body)\n * msg.ack()\n * }\n * })\n * // Pass emailQueue as c.env.EMAIL_QUEUE\n */\n\n// ---------------------------------------------------------------------------\n// Minimal message shapes — locally defined, no runtime dep on workers-types.\n// ---------------------------------------------------------------------------\n\nexport interface QueueMessage<T = unknown> {\n id: string\n body: T\n timestamp: Date\n ack(): void\n retry(): void\n}\n\nexport interface MessageBatch<T = unknown> {\n readonly queue: string\n readonly messages: QueueMessage<T>[]\n ackAll(): void\n retryAll(): void\n}\n\nexport interface SendOptions {\n delaySeconds?: number\n contentType?: string\n}\n\nexport type QueueHandler<T> = (batch: MessageBatch<T>) => Promise<void>\n\nexport interface QueueDriver<T = unknown> {\n /**\n * Send a message. The registered handler is invoked synchronously (after\n * `delaySeconds` have elapsed when non-zero, via a non-blocking `setTimeout`).\n */\n send(body: T, options?: SendOptions): Promise<void>\n /**\n * Send many messages in a single batch.\n */\n sendBatch(messages: Array<{ body: T; options?: SendOptions }>): Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// createSyncQueueDriver — public factory.\n// ---------------------------------------------------------------------------\n\nlet _msgCounter = 0\n\nfunction makeMessage<T>(body: T): QueueMessage<T> {\n const id = `sync-${++_msgCounter}`\n let acked = false\n return {\n id,\n body,\n timestamp: new Date(),\n ack() { acked = true },\n retry() { /* no-op — synchronous, no retry infrastructure */ },\n }\n}\n\nfunction makeBatch<T>(messages: QueueMessage<T>[], queueName: string): MessageBatch<T> {\n return {\n queue: queueName,\n messages,\n ackAll() { messages.forEach(m => m.ack()) },\n retryAll() { /* no-op */ },\n }\n}\n\n/**\n * Create a synchronous Queue driver.\n *\n * @param handler Called for each message (or batch). When `handler` is omitted,\n * messages are logged and discarded — useful when EMAIL_QUEUE is\n * optional and no handler is wired yet.\n * @param queueName Optional queue name shown in MessageBatch (cosmetic).\n */\nexport function createSyncQueueDriver<T = unknown>(\n handler?: QueueHandler<T>,\n queueName = 'sync-queue',\n): QueueDriver<T> {\n async function dispatch(messages: QueueMessage<T>[]): Promise<void> {\n if (!handler) {\n // Silently drop — EMAIL_QUEUE is optional; no handler registered.\n return\n }\n try {\n await handler(makeBatch(messages, queueName))\n } catch (err) {\n console.error(`[SyncQueue:${queueName}] Handler error:`, err)\n }\n }\n\n return {\n async send(body: T, options: SendOptions = {}): Promise<void> {\n const msg = makeMessage(body)\n const delay = (options.delaySeconds ?? 0) * 1000\n if (delay > 0) {\n // Fire-and-forget after delay — keeps HTTP response unblocked.\n setTimeout(() => dispatch([msg]), delay)\n } else {\n await dispatch([msg])\n }\n },\n\n async sendBatch(messages): Promise<void> {\n const msgs = messages.map(m => makeMessage(m.body))\n const delays = messages.map(m => (m.options?.delaySeconds ?? 0) * 1000)\n const maxDelay = Math.max(...delays, 0)\n if (maxDelay > 0) {\n setTimeout(() => dispatch(msgs), maxDelay)\n } else {\n await dispatch(msgs)\n }\n },\n }\n}\n","/**\n * Node / Bun server adapter for self-hosted SonicJS deployments.\n *\n * Wires the local SQLite DB driver, filesystem storage, in-memory KV, and\n * optional sync queue into a SonicJS app so it can run outside Cloudflare Workers.\n *\n * ## Quick start (Node.js)\n *\n * ```ts\n * import { serve } from '@hono/node-server'\n * import { createSonicJSApp } from '@sonicjs-cms/core'\n * import { createNodeSonicApp } from '@sonicjs-cms/core/adapters'\n *\n * const app = createSonicJSApp({ ... })\n * const adapted = await createNodeSonicApp(app, {\n * dbPath: './data/sonicjs.db',\n * storagePath: './data/media',\n * })\n * serve({ fetch: adapted.fetch, port: 3000 })\n * ```\n *\n * ## Quick start (Bun)\n *\n * ```ts\n * import { createSonicJSApp } from '@sonicjs-cms/core'\n * import { createNodeSonicApp } from '@sonicjs-cms/core/adapters'\n *\n * const app = createSonicJSApp({ ... })\n * const adapted = await createNodeSonicApp(app, {\n * dbPath: './data/sonicjs.db',\n * storagePath: './data/media',\n * })\n * Bun.serve({ fetch: adapted.fetch, port: 3000 })\n * ```\n */\n\nimport { Hono } from 'hono'\nimport { createSqliteDriver } from './db/sqlite-driver'\nimport { createFilesystemDriver } from './storage/filesystem-driver'\nimport { createMemoryKVDriver } from './kv/memory-kv-driver'\n\nexport type { SqliteDriver, SqliteDriverOptions } from './db/sqlite-driver'\nexport type { StorageDriver, PutOptions, R2HttpMetadata, R2ObjectBody, R2ObjectInfo } from './storage/filesystem-driver'\nexport type { KVDriver, KVPutOptions, KVListOptions, KVListResult } from './kv/memory-kv-driver'\nexport type { QueueDriver, QueueHandler, QueueMessage, MessageBatch } from './queue/sync-queue-driver'\n\n// ---------------------------------------------------------------------------\n// NodeAdapterOptions\n// ---------------------------------------------------------------------------\n\nexport interface NodeAdapterOptions {\n /**\n * Path to the SQLite database file.\n * Defaults to `./data/sonicjs.db`.\n */\n dbPath?: string\n\n /**\n * Root directory for uploaded media files (replaces R2).\n * Defaults to `./data/media`.\n */\n storagePath?: string\n\n /**\n * Path to a JSON file used to persist KV entries across restarts.\n * Omit for ephemeral in-memory KV (TTL-based caching only).\n */\n kvPersistPath?: string\n\n /**\n * Environment variables injected as `c.env.*` (JWT_SECRET, CORS_ORIGINS, etc.).\n * Values are merged with process.env so you can also use dotenv.\n */\n env?: Record<string, string | undefined>\n}\n\n// ---------------------------------------------------------------------------\n// createNodeSonicApp\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a SonicJS Hono app with local adapter bindings injected before any\n * SonicJS middleware (bootstrap, auth, routes) executes.\n *\n * The injection MUST run first — SonicJS's bootstrap middleware accesses\n * `c.env.DB` on the very first request. If we append the middleware after\n * `createSonicJSApp()` we'd lose the ordering race. Instead we wrap the\n * sonicApp in a fresh Hono instance that injects env first, then routes\n * all requests through the sonicApp.\n *\n * @param sonicApp The SonicJS Hono app created by `createSonicJSApp()`.\n * @param options Adapter configuration.\n */\nexport async function createNodeSonicApp<E extends { Bindings: Record<string, unknown> }>(\n sonicApp: Hono<E>,\n options: NodeAdapterOptions = {},\n): Promise<Hono<E>> {\n const {\n dbPath = './data/sonicjs.db',\n storagePath = './data/media',\n kvPersistPath,\n env: extraEnv = {},\n } = options\n\n // Resolve all drivers before the first request arrives.\n const [db, mediaBucket] = await Promise.all([\n createSqliteDriver({ dbPath }),\n Promise.resolve(createFilesystemDriver(storagePath)),\n ])\n const cacheKV = createMemoryKVDriver({ persistPath: kvPersistPath })\n\n // Merge env vars: explicit options take priority over process.env.\n const resolvedEnv = {\n ...Object.fromEntries(\n Object.entries(process.env).filter(([, v]) => v !== undefined)\n ),\n ...extraEnv,\n }\n\n // Create a wrapper app so our env injection runs BEFORE sonicApp's middlewares.\n // Hono executes middleware in registration order; appending to sonicApp directly\n // would put injection AFTER bootstrap (too late). Wrapping gives us first-in ordering.\n const wrapper = new Hono<E>()\n\n wrapper.use('*', async (c, next) => {\n const env = c.env as Record<string, unknown>\n\n // DB binding (replaces D1Database)\n env.DB = db\n\n // Storage binding (replaces R2Bucket)\n env.MEDIA_BUCKET = mediaBucket\n\n // KV binding (replaces KVNamespace)\n env.CACHE_KV = cacheKV\n\n // String env vars (JWT_SECRET, CORS_ORIGINS, ENVIRONMENT, …)\n for (const [k, v] of Object.entries(resolvedEnv)) {\n if (v !== undefined && env[k] === undefined) {\n env[k] = v\n }\n }\n\n return next()\n })\n\n // Mount sonicApp under root — routes, middleware, and everything it registered.\n wrapper.route('/', sonicApp)\n\n return wrapper as unknown as Hono<E>\n}\n"]}