opacacms 0.2.1 → 0.3.1

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 (185) hide show
  1. package/README.md +31 -22
  2. package/dist/admin/auth-client.d.ts +39 -39
  3. package/dist/admin/index.d.ts +2 -2
  4. package/dist/admin/index.js +15 -10520
  5. package/dist/admin/plugin-client.d.ts +65 -0
  6. package/dist/admin/react.d.ts +2 -2
  7. package/dist/admin/react.js +34 -4
  8. package/dist/admin/stores/ui.d.ts +19 -4
  9. package/dist/admin/ui/components/PluginSettingsForm.d.ts +2 -2
  10. package/dist/admin/ui/components/custom-alert.d.ts +7 -0
  11. package/dist/admin/ui/components/{DetailSheet.d.ts → detail-sheet.d.ts} +1 -2
  12. package/dist/admin/ui/components/fields/FieldLabel.d.ts +1 -1
  13. package/dist/admin/ui/components/fields/RelationshipField.d.ts +1 -1
  14. package/dist/admin/ui/components/media/AssetManagerModal.d.ts +2 -2
  15. package/dist/admin/ui/components/plugin-iframe.d.ts +7 -0
  16. package/dist/admin/ui/components/ui/accordion.d.ts +17 -7
  17. package/dist/admin/ui/components/ui/alert-dialog.d.ts +16 -12
  18. package/dist/admin/ui/components/ui/button.d.ts +11 -7
  19. package/dist/admin/ui/components/ui/relationship.d.ts +1 -1
  20. package/dist/admin/ui/components/ui/sheet.d.ts +14 -27
  21. package/dist/admin/ui/components/ui/tooltip.d.ts +7 -0
  22. package/dist/admin/ui/components/versions-sheet.d.ts +4 -5
  23. package/dist/admin/ui/views/collection-list-view.d.ts +1 -1
  24. package/dist/admin/ui/views/dashboard-view.d.ts +1 -1
  25. package/dist/admin/ui/views/media-registry-view.d.ts +3 -3
  26. package/dist/admin/ui/views/settings-view.d.ts +2 -2
  27. package/dist/admin/vue.js +27 -4
  28. package/dist/admin/webcomponent.js +66 -16
  29. package/dist/admin.css +1 -1
  30. package/dist/auth/index.d.ts +43 -43
  31. package/dist/{chunk-7y1nbmw6.js → chunk-1bd7fz7n.js} +32 -2
  32. package/dist/chunk-1qm0m8r8.js +413 -0
  33. package/dist/chunk-2k3ysje3.js +31 -0
  34. package/dist/{chunk-jdfw4v3r.js → chunk-3j9zjfmn.js} +95 -30
  35. package/dist/{chunk-byq8g0rd.js → chunk-48ywpd0a.js} +16 -22
  36. package/dist/{chunk-tfnaf41w.js → chunk-5422w4eq.js} +41 -25
  37. package/dist/chunk-56n342hs.js +95 -0
  38. package/dist/chunk-5b8r0v8c.js +47 -0
  39. package/dist/chunk-63yg00vx.js +263 -0
  40. package/dist/{chunk-8sqjbsgt.js → chunk-6bywt602.js} +26 -1
  41. package/dist/{chunk-v9z61v3g.js → chunk-6qs0g65f.js} +43 -3
  42. package/dist/chunk-7rr5p01g.js +581 -0
  43. package/dist/{chunk-2es275xs.js → chunk-941zxavt.js} +867 -322
  44. package/dist/{chunk-51z3x7kq.js → chunk-a3qae86h.js} +1 -1
  45. package/dist/{chunk-3rdhbedb.js → chunk-adq2b75c.js} +2 -2
  46. package/dist/chunk-d0tb1xjw.js +93 -0
  47. package/dist/chunk-d7cgd6vn.js +318 -0
  48. package/dist/{chunk-6d1vdfwa.js → chunk-e0g6gn7n.js} +54 -75
  49. package/dist/chunk-ec4jhybj.js +1137 -0
  50. package/dist/chunk-fatyf6f7.js +221 -0
  51. package/dist/{chunk-526a3gqx.js → chunk-fnsf1dfm.js} +1 -1
  52. package/dist/chunk-g9bxb6h0.js +205 -0
  53. package/dist/chunk-gyaf5kgf.js +10 -0
  54. package/dist/{chunk-9kxpbcb1.js → chunk-h6dhexzr.js} +16 -7
  55. package/dist/{chunk-dykn5hr6.js → chunk-j8js1y0h.js} +31 -74
  56. package/dist/{chunk-t0zg026p.js → chunk-jq1drsen.js} +12 -1
  57. package/dist/{chunk-b3kr8w41.js → chunk-m24yqkeq.js} +38 -26
  58. package/dist/chunk-m5ems3hh.js +410 -0
  59. package/dist/{chunk-8scgdznr.js → chunk-m83ybzf8.js} +15 -18
  60. package/dist/chunk-majsbncm.js +98 -0
  61. package/dist/chunk-mp2gt9yh.js +237 -0
  62. package/dist/chunk-n1twhqmf.js +54 -0
  63. package/dist/{chunk-bygjkgrx.js → chunk-naqcqj8n.js} +57 -80
  64. package/dist/chunk-q5sb5dcr.js +15 -0
  65. package/dist/{chunk-06ks4ggh.js → chunk-qhdsjek6.js} +49 -89
  66. package/dist/{chunk-n133qpsm.js → chunk-qsh2nqz3.js} +50 -81
  67. package/dist/chunk-r0ms5tk1.js +76 -0
  68. package/dist/chunk-rwqwsanx.js +75 -0
  69. package/dist/chunk-sqsfk9p4.js +700 -0
  70. package/dist/{chunk-5gvbp2qa.js → chunk-x7bnzswh.js} +25 -18
  71. package/dist/cli/commands/dev.d.ts +8 -0
  72. package/dist/cli/commands/doctor.d.ts +8 -0
  73. package/dist/cli/commands/generate.d.ts +26 -0
  74. package/dist/cli/commands/init.d.ts +13 -1
  75. package/dist/cli/commands/migrate.d.ts +33 -0
  76. package/dist/cli/commands/plugin.d.ts +13 -0
  77. package/dist/cli/commands/seed.d.ts +21 -0
  78. package/dist/cli/{commands/migrate-commands.d.ts → core/migrations/migrate-logic.d.ts} +2 -2
  79. package/dist/cli/core/migrations/schema-diff-engine.d.ts +12 -0
  80. package/dist/cli/core/migrations/schema-diff.d.ts +11 -0
  81. package/dist/cli/{seeding.d.ts → core/seeding/auto-seed.d.ts} +7 -4
  82. package/dist/cli/core/seeding/seed-logic.d.ts +2 -0
  83. package/dist/cli/index.d.ts +4 -0
  84. package/dist/cli/index.js +6 -170
  85. package/dist/client/RichText.d.ts +5 -0
  86. package/dist/client/rich-text-utils.d.ts +5 -0
  87. package/dist/client.js +3 -2
  88. package/dist/config.d.ts +3 -3
  89. package/dist/db/better-sqlite.d.ts +2 -3
  90. package/dist/db/better-sqlite.js +6 -5
  91. package/dist/db/bun-sqlite.d.ts +2 -3
  92. package/dist/db/bun-sqlite.js +6 -5
  93. package/dist/db/d1.d.ts +13 -7
  94. package/dist/db/d1.js +6 -5
  95. package/dist/db/index.d.ts +2 -2
  96. package/dist/db/index.js +10 -12
  97. package/dist/db/kysely/factory.d.ts +29 -0
  98. package/dist/db/kysely/plugins/audit-logging.d.ts +48 -0
  99. package/dist/db/kysely/plugins/auto-timestamps.d.ts +38 -0
  100. package/dist/db/kysely/plugins/cursor-pagination.d.ts +42 -0
  101. package/dist/db/kysely/plugins/deadlock-handler.d.ts +47 -0
  102. package/dist/db/kysely/plugins/draft-swapper.d.ts +33 -0
  103. package/dist/db/kysely/plugins/field-masking.d.ts +45 -0
  104. package/dist/db/kysely/plugins/fts-normalizer.d.ts +38 -0
  105. package/dist/db/kysely/plugins/i18n-fallback.d.ts +48 -0
  106. package/dist/db/kysely/plugins/id-generation.d.ts +42 -0
  107. package/dist/db/kysely/plugins/index.d.ts +16 -0
  108. package/dist/db/kysely/plugins/json-flattener.d.ts +38 -0
  109. package/dist/db/kysely/plugins/relationship-preloading.d.ts +39 -0
  110. package/dist/db/kysely/plugins/slug-generation.d.ts +37 -0
  111. package/dist/db/kysely/plugins/soft-delete.d.ts +42 -0
  112. package/dist/db/kysely/plugins/tree-resolver.d.ts +39 -0
  113. package/dist/db/kysely/plugins/virtual-field-resolver.d.ts +54 -0
  114. package/dist/db/kysely/plugins/zod-coercion.d.ts +34 -0
  115. package/dist/db/kysely/snapshot/snapshot-manager.d.ts +18 -0
  116. package/dist/db/postgres.d.ts +2 -2
  117. package/dist/db/postgres.js +6 -5
  118. package/dist/db/sqlite.d.ts +2 -3
  119. package/dist/db/sqlite.js +6 -5
  120. package/dist/index.d.ts +3 -0
  121. package/dist/index.js +161 -7
  122. package/dist/runtimes/bun.js +9 -6
  123. package/dist/runtimes/cloudflare-workers.d.ts +3 -1
  124. package/dist/runtimes/cloudflare-workers.js +36 -7
  125. package/dist/runtimes/next.js +8 -5
  126. package/dist/runtimes/node.js +9 -6
  127. package/dist/schema/collection.d.ts +116 -70
  128. package/dist/schema/compiler.d.ts +6 -0
  129. package/dist/schema/global.d.ts +38 -71
  130. package/dist/schema/index.d.ts +5 -4
  131. package/dist/schema/index.js +35 -550
  132. package/dist/schema/zod.d.ts +564 -0
  133. package/dist/server/admin-router.d.ts +1 -1
  134. package/dist/server/collection-router.d.ts +1 -1
  135. package/dist/server/graphql.d.ts +6 -0
  136. package/dist/server/handlers.d.ts +25 -7
  137. package/dist/server/middlewares/auth.d.ts +1 -1
  138. package/dist/server/plugins-loader.d.ts +1 -1
  139. package/dist/server/router.d.ts +2 -2
  140. package/dist/server/routers/admin.d.ts +1 -1
  141. package/dist/server/routers/auth.d.ts +1 -1
  142. package/dist/server/routers/collections.d.ts +4 -1
  143. package/dist/server/routers/plugins.d.ts +2 -2
  144. package/dist/server/setup-middlewares.d.ts +1 -1
  145. package/dist/server/system-router.d.ts +1 -1
  146. package/dist/server.js +11 -6
  147. package/dist/storage/adapters/cloudflare-r2.d.ts +11 -2
  148. package/dist/storage/index.js +5 -5
  149. package/dist/types.d.ts +253 -42
  150. package/dist/utils/context.d.ts +14 -0
  151. package/dist/utils/logger.d.ts +2 -0
  152. package/dist/utils/string.d.ts +10 -0
  153. package/dist/utils/webhooks-engine.d.ts +24 -0
  154. package/dist/validation.d.ts +67 -1
  155. package/dist/validator.d.ts +1 -0
  156. package/package.json +50 -11
  157. package/src/cli/index.ts +117 -0
  158. package/dist/chunk-6qq3ne6b.js +0 -288
  159. package/dist/chunk-6v1fw7q7.js +0 -126
  160. package/dist/chunk-7a9kn0np.js +0 -116
  161. package/dist/chunk-bexcv7xe.js +0 -36
  162. package/dist/chunk-d3ffeqp9.js +0 -87
  163. package/dist/chunk-fj19qccp.js +0 -78
  164. package/dist/chunk-g1jb60xd.js +0 -17
  165. package/dist/chunk-j53pz21t.js +0 -20
  166. package/dist/chunk-mkn49zmy.js +0 -102
  167. package/dist/chunk-r39em4yj.js +0 -29
  168. package/dist/chunk-rsf0tpy1.js +0 -8
  169. package/dist/chunk-srsac177.js +0 -85
  170. package/dist/chunk-twpvxfce.js +0 -64
  171. package/dist/chunk-ywm4t2gm.js +0 -19
  172. package/dist/cli/commands/plugin-sync.d.ts +0 -1
  173. package/dist/cli/commands/seed-command.d.ts +0 -2
  174. package/dist/plugins/ui-bridge.d.ts +0 -12
  175. package/dist/schema/fields/base.d.ts +0 -84
  176. package/dist/schema/fields/index.d.ts +0 -147
  177. package/dist/schema/infer.d.ts +0 -55
  178. /package/dist/admin/ui/components/{ColumnVisibilityToggle.d.ts → column-visibility-toggle.d.ts} +0 -0
  179. /package/dist/admin/ui/components/{DataDetailView.d.ts → data-detail-view.d.ts} +0 -0
  180. /package/dist/cli/{d1-mock.d.ts → core/mocks/d1-mock.d.ts} +0 -0
  181. /package/dist/cli/{r2-mock.d.ts → core/mocks/r2-mock.d.ts} +0 -0
  182. /package/dist/cli/{commands → core/plugins}/plugin-build.d.ts +0 -0
  183. /package/dist/cli/{commands → core/plugins}/plugin-init.d.ts +0 -0
  184. /package/dist/cli/{commands → core/types}/generate-types.d.ts +0 -0
  185. /package/dist/{schema/fields/validation.test.d.ts → cli/seeding.test.d.ts} +0 -0
@@ -3,10 +3,13 @@ import {
3
3
  flattenPayload,
4
4
  pushSchema,
5
5
  unflattenRow
6
- } from "./chunk-dykn5hr6.js";
6
+ } from "./chunk-j8js1y0h.js";
7
7
  import {
8
8
  BaseDatabaseAdapter
9
9
  } from "./chunk-s8mqwnm1.js";
10
+ import {
11
+ requestContext
12
+ } from "./chunk-q5sb5dcr.js";
10
13
  import {
11
14
  flattenFields,
12
15
  getRelationalFields,
@@ -14,15 +17,15 @@ import {
14
17
  } from "./chunk-qxt9vge8.js";
15
18
  import {
16
19
  logger
17
- } from "./chunk-t0zg026p.js";
20
+ } from "./chunk-jq1drsen.js";
18
21
  import {
19
22
  __require
20
- } from "./chunk-8sqjbsgt.js";
23
+ } from "./chunk-6bywt602.js";
21
24
 
22
25
  // src/db/d1.ts
23
26
  import fs from "node:fs/promises";
24
27
  import path from "node:path";
25
- import { CompiledQuery, FileMigrationProvider, Kysely, Migrator } from "kysely";
28
+ import { CompiledQuery, FileMigrationProvider, Migrator } from "kysely";
26
29
  class D1Adapter extends BaseDatabaseAdapter {
27
30
  name = "d1";
28
31
  _rawDb;
@@ -52,8 +55,14 @@ class D1Adapter extends BaseDatabaseAdapter {
52
55
  if (this._db)
53
56
  return;
54
57
  const { D1Dialect } = await import("kysely-d1");
55
- this._db = new Kysely({
56
- dialect: new D1Dialect({ database: this._rawDb })
58
+ const { createOpacaKysely } = await import("./chunk-sqsfk9p4.js");
59
+ this._db = createOpacaKysely({
60
+ dialect: new D1Dialect({ database: this._rawDb }),
61
+ config: {
62
+ collections: this._collections,
63
+ globals: this._globals,
64
+ db: { name: "d1" }
65
+ }
57
66
  });
58
67
  }
59
68
  async disconnect() {
@@ -65,44 +74,6 @@ class D1Adapter extends BaseDatabaseAdapter {
65
74
  const result = await this.db.executeQuery(compiled);
66
75
  return result.rows;
67
76
  }
68
- async coerceData(collection, data) {
69
- const colDef = this._collections.find((c) => c.slug === collection);
70
- if (!colDef)
71
- return data;
72
- const result = { ...data };
73
- const allFields = flattenFields(colDef.fields);
74
- for (const field of allFields) {
75
- const colName = toSnakeCase(field.name);
76
- if (!(colName in result))
77
- continue;
78
- const value = result[colName];
79
- if (value === undefined || value === null)
80
- continue;
81
- switch (field.type) {
82
- case "boolean":
83
- result[colName] = value ? 1 : 0;
84
- break;
85
- case "number":
86
- result[colName] = Number(value);
87
- break;
88
- case "date":
89
- if (value instanceof Date) {
90
- result[colName] = value.toISOString();
91
- } else if (typeof value === "string") {
92
- result[colName] = new Date(value).toISOString();
93
- }
94
- break;
95
- case "richtext":
96
- case "json":
97
- case "file":
98
- if (typeof value === "object") {
99
- result[colName] = JSON.stringify(value);
100
- }
101
- break;
102
- }
103
- }
104
- return result;
105
- }
106
77
  async count(collection, query) {
107
78
  const tableName = toSnakeCase(collection);
108
79
  let qb = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
@@ -138,18 +109,14 @@ class D1Adapter extends BaseDatabaseAdapter {
138
109
  validCols.add("id");
139
110
  validCols.add(toSnakeCase("createdAt"));
140
111
  validCols.add(toSnakeCase("updatedAt"));
141
- const now = new Date().toISOString();
142
- flatData[toSnakeCase("createdAt")] = now;
143
- flatData[toSnakeCase("updatedAt")] = now;
144
112
  const filteredData = {};
145
113
  for (const col of Object.keys(flatData)) {
146
114
  if (validCols.has(col)) {
147
115
  filteredData[col] = flatData[col];
148
116
  }
149
117
  }
150
- const coercedData = await this.coerceData(collection, filteredData);
151
118
  try {
152
- await this.db.insertInto(tableName).values(coercedData).execute();
119
+ await this.db.insertInto(tableName).values(filteredData).execute();
153
120
  } catch (e) {
154
121
  logger.error(`[D1] Create failed in ${collection}: ${e.message}`);
155
122
  throw e;
@@ -181,13 +148,13 @@ class D1Adapter extends BaseDatabaseAdapter {
181
148
  }
182
149
  }
183
150
  delete blockFlatData.blockType;
184
- const coercedBlockData = await this.coerceData(blockTableName, {
151
+ const blockData = {
185
152
  ...blockFlatData,
186
153
  _parent_id: flatData.id,
187
154
  _order: i,
188
155
  block_type: block.blockType
189
- });
190
- await this.db.insertInto(blockTableName).values(coercedBlockData).execute();
156
+ };
157
+ await this.db.insertInto(blockTableName).values(blockData).execute();
191
158
  }
192
159
  }
193
160
  return this.findOne(collection, { id: flatData.id }, this.db);
@@ -258,9 +225,17 @@ class D1Adapter extends BaseDatabaseAdapter {
258
225
  const page = options?.page || 1;
259
226
  const limit = options?.limit || 10;
260
227
  const offset = (page - 1) * limit;
228
+ const cursorColumn = options?.cursorColumn || "id";
261
229
  const total = await this.count(collection, query);
262
230
  const tableName = toSnakeCase(collection);
263
- let qb = this.db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
231
+ let qb = this.db.selectFrom(tableName).selectAll().limit(limit);
232
+ if (options?.after) {
233
+ qb = qb.where(cursorColumn, ">", options.after);
234
+ } else if (options?.before) {
235
+ qb = qb.where(cursorColumn, "<", options.before);
236
+ } else {
237
+ qb = qb.offset(offset);
238
+ }
264
239
  if (query && Object.keys(query).length > 0) {
265
240
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
266
241
  }
@@ -270,11 +245,13 @@ class D1Adapter extends BaseDatabaseAdapter {
270
245
  qb = qb.orderBy(col, dir === "desc" ? "desc" : "asc");
271
246
  }
272
247
  } else {
273
- qb = qb.orderBy("created_at", "desc");
248
+ qb = qb.orderBy(cursorColumn, "desc");
274
249
  }
275
250
  const rows = await qb.execute();
276
251
  if (rows.length === 0) {
277
252
  const totalPages2 = Math.ceil(total / limit);
253
+ const afterAnchor = options?.after;
254
+ const beforeAnchor = options?.before;
278
255
  return {
279
256
  docs: [],
280
257
  totalDocs: total,
@@ -282,10 +259,12 @@ class D1Adapter extends BaseDatabaseAdapter {
282
259
  totalPages: totalPages2,
283
260
  page,
284
261
  pagingCounter: offset + 1,
285
- hasNextPage: page * limit < total,
286
- hasPrevPage: page > 1,
287
- prevPage: page > 1 ? page - 1 : null,
288
- nextPage: page < totalPages2 ? page + 1 : null
262
+ hasNextPage: false,
263
+ hasPrevPage: !!afterAnchor || page > 1,
264
+ prevPage: null,
265
+ nextPage: null,
266
+ nextCursor: null,
267
+ prevCursor: afterAnchor ? afterAnchor : null
289
268
  };
290
269
  }
291
270
  const rowIds = rows.map((r) => r.id);
@@ -363,9 +342,11 @@ class D1Adapter extends BaseDatabaseAdapter {
363
342
  page,
364
343
  pagingCounter: offset + 1,
365
344
  hasNextPage: page * limit < total,
366
- hasPrevPage: page > 1,
345
+ hasPrevPage: page > 1 || !!options?.after || !!options?.before,
367
346
  prevPage: page > 1 ? page - 1 : null,
368
- nextPage: page < totalPages ? page + 1 : null
347
+ nextPage: page < totalPages ? page + 1 : null,
348
+ nextCursor: docs.length > 0 ? docs[docs.length - 1][cursorColumn] : null,
349
+ prevCursor: docs.length > 0 && (page > 1 || !!options?.after || !!options?.before) ? docs[0][cursorColumn] : null
369
350
  };
370
351
  }
371
352
  async update(collection, query, data) {
@@ -377,6 +358,9 @@ class D1Adapter extends BaseDatabaseAdapter {
377
358
  const current = await this.findOne(collection, normalizedQuery, this.db);
378
359
  if (!current)
379
360
  throw new Error("Document not found");
361
+ const store = requestContext.getStore();
362
+ if (store)
363
+ store.previousData = current;
380
364
  const colDef = this._collections.find((c) => c.slug === collection);
381
365
  const jsonFields = colDef?.fields.filter((f) => f.name && (["richtext", "json", "file"].includes(f.type) || f.localized)).map((f) => f.name) || [];
382
366
  const flatData = flattenPayload(data, "", jsonFields);
@@ -394,30 +378,18 @@ class D1Adapter extends BaseDatabaseAdapter {
394
378
  delete flatData[key];
395
379
  }
396
380
  }
397
- const ts = colDef?.timestamps !== false;
398
- if (ts) {
399
- const config = typeof colDef?.timestamps === "object" ? colDef.timestamps : {};
400
- const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
401
- flatData[updatedField] = new Date().toISOString();
402
- }
403
381
  if (Object.keys(flatData).length > 0) {
404
382
  const dbFields = flattenFields(colDef?.fields || []);
405
383
  const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
406
384
  validCols.add("id");
407
- if (ts) {
408
- const config = typeof colDef?.timestamps === "object" ? colDef.timestamps : {};
409
- validCols.add(toSnakeCase(config.createdAt || "createdAt"));
410
- validCols.add(toSnakeCase(config.updatedAt || "updatedAt"));
411
- }
412
385
  const filteredData = {};
413
386
  for (const col of Object.keys(flatData)) {
414
387
  if (validCols.has(col)) {
415
388
  filteredData[col] = flatData[col];
416
389
  }
417
390
  }
418
- const coercedData = await this.coerceData(collection, filteredData);
419
391
  try {
420
- await this.db.updateTable(tableName).set(coercedData).where("id", "=", current.id).execute();
392
+ await this.db.updateTable(tableName).set(filteredData).where("id", "=", current.id).execute();
421
393
  } catch (e) {
422
394
  logger.error(`[D1] Update failed in ${collection}: ${e.message}`);
423
395
  throw e;
@@ -459,13 +431,13 @@ class D1Adapter extends BaseDatabaseAdapter {
459
431
  }
460
432
  }
461
433
  delete blockFlatData.blockType;
462
- const coercedBlockData = await this.coerceData(blockTableName, {
434
+ const blockData = {
463
435
  ...blockFlatData,
464
436
  _parent_id: current.id,
465
437
  _order: i,
466
438
  block_type: block.blockType
467
- });
468
- await this.db.insertInto(blockTableName).values(coercedBlockData).execute();
439
+ };
440
+ await this.db.insertInto(blockTableName).values(blockData).execute();
469
441
  }
470
442
  }
471
443
  return this.findOne(collection, { id: current.id }, this.db);
@@ -484,31 +456,19 @@ class D1Adapter extends BaseDatabaseAdapter {
484
456
  delete flatData[key];
485
457
  }
486
458
  }
487
- const ts = colDef?.timestamps !== false;
488
- if (ts) {
489
- const config = typeof colDef?.timestamps === "object" ? colDef.timestamps : {};
490
- const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
491
- flatData[updatedField] = new Date().toISOString();
492
- }
493
459
  if (Object.keys(flatData).length > 0) {
494
460
  const dbFields = flattenFields(colDef?.fields || []);
495
461
  const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
496
462
  validCols.add("id");
497
- if (ts) {
498
- const config = typeof colDef?.timestamps === "object" ? colDef.timestamps : {};
499
- validCols.add(toSnakeCase(config.createdAt || "createdAt"));
500
- validCols.add(toSnakeCase(config.updatedAt || "updatedAt"));
501
- }
502
463
  const filteredData = {};
503
464
  for (const col of Object.keys(flatData)) {
504
465
  if (validCols.has(col)) {
505
466
  filteredData[col] = flatData[col];
506
467
  }
507
468
  }
508
- const coercedData = await this.coerceData(collection, filteredData);
509
469
  logger.debug(`[D1] Bulk updating in ${collection}...`);
510
470
  try {
511
- const result = await qb.set(coercedData).executeTakeFirst();
471
+ const result = await qb.set(filteredData).executeTakeFirst();
512
472
  return Number(result.numUpdatedRows || 0);
513
473
  } catch (e) {
514
474
  logger.error(`[D1] Bulk update failed in ${collection}: ${e.message}`);
@@ -3,10 +3,13 @@ import {
3
3
  flattenPayload,
4
4
  pushSchema,
5
5
  unflattenRow
6
- } from "./chunk-dykn5hr6.js";
6
+ } from "./chunk-j8js1y0h.js";
7
7
  import {
8
8
  BaseDatabaseAdapter
9
9
  } from "./chunk-s8mqwnm1.js";
10
+ import {
11
+ requestContext
12
+ } from "./chunk-q5sb5dcr.js";
10
13
  import {
11
14
  flattenFields,
12
15
  getRelationalFields,
@@ -14,15 +17,15 @@ import {
14
17
  } from "./chunk-qxt9vge8.js";
15
18
  import {
16
19
  logger
17
- } from "./chunk-t0zg026p.js";
20
+ } from "./chunk-jq1drsen.js";
18
21
  import {
19
22
  __require
20
- } from "./chunk-8sqjbsgt.js";
23
+ } from "./chunk-6bywt602.js";
21
24
 
22
25
  // src/db/bun-sqlite.ts
23
26
  import fs from "node:fs/promises";
24
27
  import path from "node:path";
25
- import { FileMigrationProvider, Kysely, Migrator, sql } from "kysely";
28
+ import { FileMigrationProvider, Migrator, sql } from "kysely";
26
29
  class BunSQLiteAdapter extends BaseDatabaseAdapter {
27
30
  path;
28
31
  name = "bun-sqlite";
@@ -54,10 +57,16 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
54
57
  const { Database } = await import("bun:sqlite");
55
58
  const { BunSqliteDialect } = await import("kysely-bun-sqlite");
56
59
  this._rawDb = new Database(this.path);
57
- this._db = new Kysely({
60
+ const { createOpacaKysely } = await import("./chunk-sqsfk9p4.js");
61
+ this._db = createOpacaKysely({
58
62
  dialect: new BunSqliteDialect({
59
63
  database: this._rawDb
60
- })
64
+ }),
65
+ config: {
66
+ collections: this._collections,
67
+ globals: this._globals,
68
+ db: { name: "bun-sqlite" }
69
+ }
61
70
  });
62
71
  }
63
72
  async disconnect() {
@@ -69,47 +78,6 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
69
78
  const result = await compiled.execute(this.db);
70
79
  return result.rows;
71
80
  }
72
- async coerceData(collection, data) {
73
- const colDef = this._collections.find((c) => c.slug === collection);
74
- if (!colDef)
75
- return data;
76
- const result = { ...data };
77
- const allFields = flattenFields(colDef.fields);
78
- for (const field of allFields) {
79
- const colName = toSnakeCase(field.name);
80
- if (!(colName in result))
81
- continue;
82
- const value = result[colName];
83
- if (value === undefined || value === null)
84
- continue;
85
- switch (field.type) {
86
- case "boolean":
87
- result[colName] = value ? 1 : 0;
88
- break;
89
- case "number":
90
- result[colName] = Number(value);
91
- break;
92
- case "date":
93
- if (value instanceof Date) {
94
- result[colName] = value.toISOString();
95
- } else if (typeof value === "string") {
96
- const d = new Date(value);
97
- if (!isNaN(d.getTime())) {
98
- result[colName] = d.toISOString();
99
- }
100
- }
101
- break;
102
- case "richtext":
103
- case "json":
104
- case "file":
105
- if (typeof value === "object") {
106
- result[colName] = JSON.stringify(value);
107
- }
108
- break;
109
- }
110
- }
111
- return result;
112
- }
113
81
  async count(collection, query) {
114
82
  const tableName = toSnakeCase(collection);
115
83
  let qb = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
@@ -122,13 +90,7 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
122
90
  async create(collection, data) {
123
91
  const tableName = toSnakeCase(collection);
124
92
  const colDef = this._collections.find((c) => c.slug === collection);
125
- const jsonFields = colDef?.fields.filter((f) => f.name && ["richtext", "json", "file"].includes(f.type)).map((f) => toSnakeCase(f.name)) || [];
126
- const flatData = flattenPayload(data, "", jsonFields);
127
- for (const field of jsonFields) {
128
- if (flatData[field] && typeof flatData[field] === "object") {
129
- flatData[field] = JSON.stringify(flatData[field]);
130
- }
131
- }
93
+ const flatData = flattenPayload(data);
132
94
  const relationalFields = getRelationalFields(colDef?.fields || []);
133
95
  const hasManyData = {};
134
96
  const blocksData = {};
@@ -143,24 +105,18 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
143
105
  delete flatData[key];
144
106
  }
145
107
  }
146
- if (!flatData.id)
147
- flatData.id = crypto.randomUUID();
148
108
  const dbFields = flattenFields(colDef?.fields || []);
149
109
  const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
150
110
  validCols.add("id");
151
111
  validCols.add(toSnakeCase("createdAt"));
152
112
  validCols.add(toSnakeCase("updatedAt"));
153
- const now = new Date().toISOString();
154
- flatData[toSnakeCase("createdAt")] = now;
155
- flatData[toSnakeCase("updatedAt")] = now;
156
113
  const filteredData = {};
157
114
  for (const col of Object.keys(flatData)) {
158
115
  if (validCols.has(col)) {
159
116
  filteredData[col] = flatData[col];
160
117
  }
161
118
  }
162
- const coercedData = await this.coerceData(collection, filteredData);
163
- await this.db.insertInto(tableName).values(coercedData).execute();
119
+ await this.db.insertInto(tableName).values(filteredData).execute();
164
120
  for (const [key, values] of Object.entries(hasManyData)) {
165
121
  const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
166
122
  if (values.length > 0) {
@@ -188,13 +144,13 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
188
144
  }
189
145
  }
190
146
  delete blockFlatData.blockType;
191
- const coercedBlockData = await this.coerceData(blockTableName, {
147
+ const blockData = {
192
148
  ...blockFlatData,
193
149
  _parent_id: flatData.id,
194
150
  _order: i,
195
151
  block_type: block.blockType
196
- });
197
- await this.db.insertInto(blockTableName).values(coercedBlockData).execute();
152
+ };
153
+ await this.db.insertInto(blockTableName).values(blockData).execute();
198
154
  }
199
155
  }
200
156
  return this.findOne(collection, { id: flatData.id }, this.db);
@@ -265,9 +221,17 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
265
221
  const page = options?.page || 1;
266
222
  const limit = options?.limit || 10;
267
223
  const offset = (page - 1) * limit;
224
+ const cursorColumn = options?.cursorColumn || "id";
268
225
  const total = await this.count(collection, query);
269
226
  const tableName = toSnakeCase(collection);
270
- let qb = this.db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
227
+ let qb = this.db.selectFrom(tableName).selectAll().limit(limit);
228
+ if (options?.after) {
229
+ qb = qb.where(cursorColumn, ">", options.after);
230
+ } else if (options?.before) {
231
+ qb = qb.where(cursorColumn, "<", options.before);
232
+ } else {
233
+ qb = qb.offset(offset);
234
+ }
271
235
  if (query && Object.keys(query).length > 0) {
272
236
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
273
237
  }
@@ -277,11 +241,13 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
277
241
  qb = qb.orderBy(col, dir === "desc" ? "desc" : "asc");
278
242
  }
279
243
  } else {
280
- qb = qb.orderBy("created_at", "desc");
244
+ qb = qb.orderBy(cursorColumn, "desc");
281
245
  }
282
246
  const rows = await qb.execute();
283
247
  if (rows.length === 0) {
284
248
  const totalPages2 = Math.ceil(total / limit);
249
+ const afterAnchor = options?.after;
250
+ const beforeAnchor = options?.before;
285
251
  return {
286
252
  docs: [],
287
253
  totalDocs: total,
@@ -289,10 +255,12 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
289
255
  totalPages: totalPages2,
290
256
  page,
291
257
  pagingCounter: offset + 1,
292
- hasNextPage: page * limit < total,
293
- hasPrevPage: page > 1,
294
- prevPage: page > 1 ? page - 1 : null,
295
- nextPage: page < totalPages2 ? page + 1 : null
258
+ hasNextPage: false,
259
+ hasPrevPage: !!afterAnchor || page > 1,
260
+ prevPage: null,
261
+ nextPage: null,
262
+ nextCursor: null,
263
+ prevCursor: afterAnchor ? afterAnchor : null
296
264
  };
297
265
  }
298
266
  const rowIds = rows.map((r) => r.id);
@@ -370,9 +338,11 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
370
338
  page,
371
339
  pagingCounter: offset + 1,
372
340
  hasNextPage: page * limit < total,
373
- hasPrevPage: page > 1,
341
+ hasPrevPage: page > 1 || !!options?.after || !!options?.before,
374
342
  prevPage: page > 1 ? page - 1 : null,
375
- nextPage: page < totalPages ? page + 1 : null
343
+ nextPage: page < totalPages ? page + 1 : null,
344
+ nextCursor: docs.length > 0 ? docs[docs.length - 1][cursorColumn] : null,
345
+ prevCursor: docs.length > 0 && (page > 1 || !!options?.after || !!options?.before) ? docs[0][cursorColumn] : null
376
346
  };
377
347
  }
378
348
  async update(collection, query, data) {
@@ -384,6 +354,9 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
384
354
  const current = await this.findOne(collection, normalizedQuery, tx);
385
355
  if (!current)
386
356
  throw new Error("Document not found");
357
+ const store = requestContext.getStore();
358
+ if (store)
359
+ store.previousData = current;
387
360
  const colDef = this._collections.find((c) => c.slug === collection);
388
361
  const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
389
362
  const flatData = flattenPayload(data, "", jsonFields);
@@ -406,10 +379,8 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
406
379
  delete flatData[key];
407
380
  }
408
381
  }
409
- flatData.updated_at = new Date().toISOString();
410
382
  if (Object.keys(flatData).length > 0) {
411
- const coercedData = await this.coerceData(collection, flatData);
412
- await tx.updateTable(toSnakeCase(collection)).set(coercedData).where("id", "=", current.id).execute();
383
+ await tx.updateTable(toSnakeCase(collection)).set(flatData).where("id", "=", current.id).execute();
413
384
  }
414
385
  for (const [key, values] of Object.entries(hasManyData)) {
415
386
  const joinTableName = `${toSnakeCase(collection)}_${toSnakeCase(key)}_relations`.toLowerCase();
@@ -447,13 +418,13 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
447
418
  }
448
419
  }
449
420
  delete blockFlatData.blockType;
450
- const coercedBlockData = await this.coerceData(blockTableName, {
421
+ const blockData = {
451
422
  ...blockFlatData,
452
423
  _parent_id: current.id,
453
424
  _order: i,
454
425
  block_type: block.blockType
455
- });
456
- await this.db.insertInto(blockTableName).values(coercedBlockData).execute();
426
+ };
427
+ await this.db.insertInto(blockTableName).values(blockData).execute();
457
428
  }
458
429
  }
459
430
  return this.findOne(collection, { id: current.id }, tx);
@@ -478,10 +449,8 @@ class BunSQLiteAdapter extends BaseDatabaseAdapter {
478
449
  delete flatData[key];
479
450
  }
480
451
  }
481
- flatData.updated_at = new Date().toISOString();
482
452
  if (Object.keys(flatData).length > 0) {
483
- const coercedData = await this.coerceData(collection, flatData);
484
- const result = await qb.set(coercedData).executeTakeFirst();
453
+ const result = await qb.set(flatData).executeTakeFirst();
485
454
  return Number(result.numUpdatedRows || 0);
486
455
  }
487
456
  return 0;
@@ -0,0 +1,76 @@
1
+ import {
2
+ require_picocolors
3
+ } from "./chunk-rwqwsanx.js";
4
+ import {
5
+ Gt,
6
+ R,
7
+ Vt,
8
+ Wt
9
+ } from "./chunk-ec4jhybj.js";
10
+ import {
11
+ defineCommand
12
+ } from "./chunk-1qm0m8r8.js";
13
+ import {
14
+ __toESM
15
+ } from "./chunk-6bywt602.js";
16
+
17
+ // src/cli/commands/dev.ts
18
+ import { spawn } from "node:child_process";
19
+ import fs from "node:fs";
20
+ import { resolve } from "node:path";
21
+ var import_picocolors = __toESM(require_picocolors(), 1);
22
+ var dev_default = defineCommand({
23
+ meta: {
24
+ name: "dev",
25
+ description: "Start the OpacaCMS development server"
26
+ },
27
+ args: {
28
+ entry: {
29
+ type: "string",
30
+ description: "Path to your entry point (default: index.ts)",
31
+ default: "index.ts"
32
+ }
33
+ },
34
+ async run({ args }) {
35
+ console.log();
36
+ Wt(import_picocolors.default.bgBlue(import_picocolors.default.white(" OpacaCMS Dev Server ")));
37
+ const entryPath = resolve(process.cwd(), args.entry);
38
+ if (!fs.existsSync(entryPath)) {
39
+ R.error(import_picocolors.default.red(`Entry file not found at ${entryPath}`));
40
+ R.info("Try specifying the entry file: opacacms dev --entry src/index.ts");
41
+ process.exit(1);
42
+ }
43
+ Vt(`Watching for changes...`, "Status");
44
+ const isD1 = fs.readFileSync(entryPath, "utf-8").includes("createCloudflareWorkersHandler");
45
+ let cmd = "bun";
46
+ let cmdArgs = ["run", "--watch", args.entry];
47
+ if (isD1) {
48
+ cmd = "bun";
49
+ cmdArgs = ["x", "wrangler", "dev", args.entry];
50
+ }
51
+ const child = spawn(cmd, cmdArgs, {
52
+ stdio: "inherit",
53
+ cwd: process.cwd(),
54
+ env: process.env
55
+ });
56
+ child.on("error", (err) => {
57
+ R.error(import_picocolors.default.red(`Failed to start server: ${err.message}`));
58
+ });
59
+ child.on("exit", (code) => {
60
+ if (code !== 0) {
61
+ R.warn(`Server exited with code ${code}`);
62
+ }
63
+ Gt("Goodbye!");
64
+ process.exit(code || 0);
65
+ });
66
+ process.on("SIGINT", () => {
67
+ child.kill("SIGINT");
68
+ });
69
+ process.on("SIGTERM", () => {
70
+ child.kill("SIGTERM");
71
+ });
72
+ }
73
+ });
74
+ export {
75
+ dev_default as default
76
+ };