opacacms 0.2.0 → 0.3.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.
Files changed (187) 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 +20 -2
  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-3j9zjfmn.js +376 -0
  35. package/dist/{chunk-byq8g0rd.js → chunk-48ywpd0a.js} +16 -22
  36. package/dist/{chunk-esrg9qj0.js → chunk-5422w4eq.js} +70 -54
  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-51z3x7kq.js → chunk-a3qae86h.js} +1 -1
  44. package/dist/{chunk-3rdhbedb.js → chunk-adq2b75c.js} +2 -2
  45. package/dist/chunk-d0tb1xjw.js +93 -0
  46. package/dist/chunk-d7cgd6vn.js +318 -0
  47. package/dist/{chunk-0bq155dy.js → chunk-e0g6gn7n.js} +89 -100
  48. package/dist/chunk-ec4jhybj.js +1137 -0
  49. package/dist/chunk-fatyf6f7.js +221 -0
  50. package/dist/{chunk-526a3gqx.js → chunk-fnsf1dfm.js} +1 -1
  51. package/dist/chunk-g9bxb6h0.js +205 -0
  52. package/dist/chunk-gyaf5kgf.js +10 -0
  53. package/dist/{chunk-9kxpbcb1.js → chunk-h6dhexzr.js} +16 -7
  54. package/dist/{chunk-dykn5hr6.js → chunk-j8js1y0h.js} +31 -74
  55. package/dist/{chunk-t0zg026p.js → chunk-jq1drsen.js} +12 -1
  56. package/dist/{chunk-b3kr8w41.js → chunk-m24yqkeq.js} +38 -26
  57. package/dist/chunk-m5ems3hh.js +410 -0
  58. package/dist/{chunk-8scgdznr.js → chunk-m83ybzf8.js} +15 -18
  59. package/dist/chunk-majsbncm.js +98 -0
  60. package/dist/chunk-mp2gt9yh.js +237 -0
  61. package/dist/chunk-n1twhqmf.js +54 -0
  62. package/dist/{chunk-gmee4mdc.js → chunk-naqcqj8n.js} +92 -106
  63. package/dist/chunk-q5sb5dcr.js +15 -0
  64. package/dist/{chunk-d1asgtke.js → chunk-qhdsjek6.js} +90 -121
  65. package/dist/{chunk-0gtxnxmd.js → chunk-qsh2nqz3.js} +85 -105
  66. package/dist/chunk-r0ms5tk1.js +76 -0
  67. package/dist/chunk-rwqwsanx.js +75 -0
  68. package/dist/chunk-sqsfk9p4.js +700 -0
  69. package/dist/{chunk-5gvbp2qa.js → chunk-x7bnzswh.js} +25 -18
  70. package/dist/{chunk-kc4jfnv7.js → chunk-z3ffn2b7.js} +851 -324
  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/adapter.d.ts +2 -2
  90. package/dist/db/better-sqlite.d.ts +3 -3
  91. package/dist/db/better-sqlite.js +6 -5
  92. package/dist/db/bun-sqlite.d.ts +3 -3
  93. package/dist/db/bun-sqlite.js +6 -5
  94. package/dist/db/d1.d.ts +13 -7
  95. package/dist/db/d1.js +6 -5
  96. package/dist/db/index.d.ts +2 -2
  97. package/dist/db/index.js +10 -12
  98. package/dist/db/kysely/factory.d.ts +29 -0
  99. package/dist/db/kysely/plugins/audit-logging.d.ts +48 -0
  100. package/dist/db/kysely/plugins/auto-timestamps.d.ts +38 -0
  101. package/dist/db/kysely/plugins/cursor-pagination.d.ts +42 -0
  102. package/dist/db/kysely/plugins/deadlock-handler.d.ts +47 -0
  103. package/dist/db/kysely/plugins/draft-swapper.d.ts +33 -0
  104. package/dist/db/kysely/plugins/field-masking.d.ts +45 -0
  105. package/dist/db/kysely/plugins/fts-normalizer.d.ts +38 -0
  106. package/dist/db/kysely/plugins/i18n-fallback.d.ts +48 -0
  107. package/dist/db/kysely/plugins/id-generation.d.ts +42 -0
  108. package/dist/db/kysely/plugins/index.d.ts +16 -0
  109. package/dist/db/kysely/plugins/json-flattener.d.ts +38 -0
  110. package/dist/db/kysely/plugins/relationship-preloading.d.ts +39 -0
  111. package/dist/db/kysely/plugins/slug-generation.d.ts +37 -0
  112. package/dist/db/kysely/plugins/soft-delete.d.ts +42 -0
  113. package/dist/db/kysely/plugins/tree-resolver.d.ts +39 -0
  114. package/dist/db/kysely/plugins/virtual-field-resolver.d.ts +54 -0
  115. package/dist/db/kysely/plugins/zod-coercion.d.ts +34 -0
  116. package/dist/db/kysely/snapshot/snapshot-manager.d.ts +18 -0
  117. package/dist/db/postgres.d.ts +4 -4
  118. package/dist/db/postgres.js +6 -5
  119. package/dist/db/sqlite.d.ts +3 -3
  120. package/dist/db/sqlite.js +6 -5
  121. package/dist/index.d.ts +3 -0
  122. package/dist/index.js +161 -7
  123. package/dist/runtimes/bun.js +9 -6
  124. package/dist/runtimes/cloudflare-workers.d.ts +3 -1
  125. package/dist/runtimes/cloudflare-workers.js +36 -7
  126. package/dist/runtimes/next.js +8 -5
  127. package/dist/runtimes/node.js +9 -6
  128. package/dist/schema/collection.d.ts +116 -70
  129. package/dist/schema/compiler.d.ts +6 -0
  130. package/dist/schema/global.d.ts +38 -71
  131. package/dist/schema/index.d.ts +5 -4
  132. package/dist/schema/index.js +35 -550
  133. package/dist/schema/zod.d.ts +564 -0
  134. package/dist/server/admin-router.d.ts +1 -1
  135. package/dist/server/collection-router.d.ts +1 -1
  136. package/dist/server/graphql.d.ts +6 -0
  137. package/dist/server/handlers.d.ts +25 -7
  138. package/dist/server/middlewares/auth.d.ts +1 -1
  139. package/dist/server/plugins-loader.d.ts +1 -1
  140. package/dist/server/router.d.ts +2 -2
  141. package/dist/server/routers/admin.d.ts +1 -1
  142. package/dist/server/routers/auth.d.ts +1 -1
  143. package/dist/server/routers/collections.d.ts +4 -1
  144. package/dist/server/routers/plugins.d.ts +2 -2
  145. package/dist/server/setup-middlewares.d.ts +1 -1
  146. package/dist/server/system-router.d.ts +1 -1
  147. package/dist/server.js +11 -6
  148. package/dist/storage/adapters/cloudflare-r2.d.ts +11 -2
  149. package/dist/storage/index.js +39 -30
  150. package/dist/types.d.ts +255 -44
  151. package/dist/utils/context.d.ts +14 -0
  152. package/dist/utils/logger.d.ts +2 -0
  153. package/dist/utils/string.d.ts +10 -0
  154. package/dist/utils/webhooks-engine.d.ts +24 -0
  155. package/dist/validation.d.ts +67 -1
  156. package/dist/validator.d.ts +1 -0
  157. package/package.json +36 -33
  158. package/src/cli/index.ts +117 -0
  159. package/dist/chunk-6qq3ne6b.js +0 -288
  160. package/dist/chunk-6v1fw7q7.js +0 -126
  161. package/dist/chunk-7a9kn0np.js +0 -116
  162. package/dist/chunk-bexcv7xe.js +0 -36
  163. package/dist/chunk-d3ffeqp9.js +0 -87
  164. package/dist/chunk-fj19qccp.js +0 -78
  165. package/dist/chunk-j53pz21t.js +0 -20
  166. package/dist/chunk-mkn49zmy.js +0 -102
  167. package/dist/chunk-qb6ztvw9.js +0 -17
  168. package/dist/chunk-r39em4yj.js +0 -29
  169. package/dist/chunk-rsf0tpy1.js +0 -8
  170. package/dist/chunk-srsac177.js +0 -85
  171. package/dist/chunk-swtcpvhf.js +0 -2442
  172. package/dist/chunk-twpvxfce.js +0 -64
  173. package/dist/chunk-ywm4t2gm.js +0 -19
  174. package/dist/cli/commands/plugin-sync.d.ts +0 -1
  175. package/dist/cli/commands/seed-command.d.ts +0 -2
  176. package/dist/plugins/ui-bridge.d.ts +0 -12
  177. package/dist/schema/fields/base.d.ts +0 -84
  178. package/dist/schema/fields/index.d.ts +0 -147
  179. package/dist/schema/infer.d.ts +0 -55
  180. /package/dist/admin/ui/components/{ColumnVisibilityToggle.d.ts → column-visibility-toggle.d.ts} +0 -0
  181. /package/dist/admin/ui/components/{DataDetailView.d.ts → data-detail-view.d.ts} +0 -0
  182. /package/dist/cli/{d1-mock.d.ts → core/mocks/d1-mock.d.ts} +0 -0
  183. /package/dist/cli/{r2-mock.d.ts → core/mocks/r2-mock.d.ts} +0 -0
  184. /package/dist/cli/{commands → core/plugins}/plugin-build.d.ts +0 -0
  185. /package/dist/cli/{commands → core/plugins}/plugin-init.d.ts +0 -0
  186. /package/dist/cli/{commands → core/types}/generate-types.d.ts +0 -0
  187. /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,23 +17,25 @@ 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/better-sqlite.ts
23
26
  import fs from "node:fs/promises";
24
27
  import path from "node:path";
25
- import { createRequire } from "node:module";
26
- import { CompiledQuery, FileMigrationProvider, Kysely, Migrator, SqliteDialect } from "kysely";
27
- var require2 = createRequire(import.meta.url);
28
- var Database = require2("better-sqlite3");
29
-
28
+ import {
29
+ CompiledQuery,
30
+ FileMigrationProvider,
31
+ Migrator,
32
+ SqliteDialect
33
+ } from "kysely";
30
34
  class BetterSQLiteAdapter extends BaseDatabaseAdapter {
35
+ path;
31
36
  name = "better-sqlite3";
32
37
  _rawDb;
33
- _db;
38
+ _db = null;
34
39
  _collections = [];
35
40
  _globals = [];
36
41
  push;
@@ -40,71 +45,50 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
40
45
  return this._rawDb;
41
46
  }
42
47
  get db() {
48
+ if (!this._db)
49
+ throw new Error("Database not connected. Call connect() first.");
43
50
  return this._db;
44
51
  }
45
52
  constructor(path2, options) {
46
53
  super();
47
- this._rawDb = new Database(path2);
48
- this._db = new Kysely({
49
- dialect: new SqliteDialect({
50
- database: this._rawDb
51
- })
52
- });
54
+ this.path = path2;
53
55
  this.push = options?.push ?? true;
54
56
  this.pushDestructive = options?.pushDestructive ?? false;
55
57
  this.migrationDir = options?.migrationDir ?? "./migrations";
56
58
  }
57
- async connect() {}
59
+ async connect() {
60
+ if (this._db)
61
+ return;
62
+ const { createRequire } = await import("node:module");
63
+ const require2 = createRequire(import.meta.url);
64
+ const Database = require2("better-sqlite3");
65
+ this._rawDb = new Database(this.path);
66
+ const { createOpacaKysely } = await import("./chunk-sqsfk9p4.js");
67
+ this._db = createOpacaKysely({
68
+ dialect: new SqliteDialect({
69
+ database: this._rawDb
70
+ }),
71
+ config: {
72
+ collections: this._collections,
73
+ globals: this._globals,
74
+ db: { name: "better-sqlite3" }
75
+ }
76
+ });
77
+ }
58
78
  async disconnect() {
59
- await this._db.destroy();
79
+ if (this._db)
80
+ await this.db.destroy();
60
81
  }
61
82
  async unsafe(query, params) {
83
+ if (!this._db)
84
+ await this.connect();
62
85
  const compiled = CompiledQuery.raw(query, params || []);
63
- const result = await this._db.executeQuery(compiled);
86
+ const result = await this.db.executeQuery(compiled);
64
87
  return result.rows;
65
88
  }
66
- async coerceData(collection, data) {
67
- const colDef = this._collections.find((c) => c.slug === collection);
68
- if (!colDef)
69
- return data;
70
- const result = { ...data };
71
- const { flattenFields: flattenFields2 } = await import("./chunk-526a3gqx.js");
72
- const allFields = flattenFields2(colDef.fields);
73
- for (const field of allFields) {
74
- const colName = toSnakeCase(field.name);
75
- if (!(colName in result))
76
- continue;
77
- const value = result[colName];
78
- if (value === undefined || value === null)
79
- continue;
80
- switch (field.type) {
81
- case "boolean":
82
- result[colName] = value ? 1 : 0;
83
- break;
84
- case "number":
85
- result[colName] = Number(value);
86
- break;
87
- case "date":
88
- if (value instanceof Date) {
89
- result[colName] = value.toISOString();
90
- } else if (typeof value === "string") {
91
- result[colName] = new Date(value).toISOString();
92
- }
93
- break;
94
- case "richtext":
95
- case "json":
96
- case "file":
97
- if (typeof value === "object") {
98
- result[colName] = JSON.stringify(value);
99
- }
100
- break;
101
- }
102
- }
103
- return result;
104
- }
105
89
  async count(collection, query) {
106
90
  const tableName = toSnakeCase(collection);
107
- let qb = this._db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
91
+ let qb = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
108
92
  if (query && Object.keys(query).length > 0) {
109
93
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
110
94
  }
@@ -113,15 +97,10 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
113
97
  }
114
98
  async create(collection, data) {
115
99
  const tableName = toSnakeCase(collection);
116
- return this._db.transaction().execute(async (tx) => {
100
+ return this.db.transaction().execute(async (tx) => {
117
101
  const colDef = this._collections.find((c) => c.slug === collection);
118
- const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
119
- const flatData = flattenPayload(data, "", jsonFields);
120
- for (const field of jsonFields) {
121
- if (flatData[field] && typeof flatData[field] === "object") {
122
- flatData[field] = JSON.stringify(flatData[field]);
123
- }
124
- }
102
+ const flatData = flattenPayload(data);
103
+ const relationalFields = getRelationalFields(colDef?.fields || []);
125
104
  const hasManyData = {};
126
105
  const blocksData = {};
127
106
  for (const key in flatData) {
@@ -135,24 +114,18 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
135
114
  delete flatData[key];
136
115
  }
137
116
  }
138
- if (!flatData.id)
139
- flatData.id = crypto.randomUUID();
140
117
  const dbFields = flattenFields(colDef?.fields || []);
141
118
  const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
142
119
  validCols.add("id");
143
120
  validCols.add(toSnakeCase("createdAt"));
144
121
  validCols.add(toSnakeCase("updatedAt"));
145
- const now = new Date().toISOString();
146
- flatData[toSnakeCase("createdAt")] = now;
147
- flatData[toSnakeCase("updatedAt")] = now;
148
122
  const filteredData = {};
149
123
  for (const col of Object.keys(flatData)) {
150
124
  if (validCols.has(col)) {
151
125
  filteredData[col] = flatData[col];
152
126
  }
153
127
  }
154
- const coercedData = await this.coerceData(collection, filteredData);
155
- await tx.insertInto(tableName).values(coercedData).execute();
128
+ await tx.insertInto(tableName).values(filteredData).execute();
156
129
  for (const [key, values] of Object.entries(hasManyData)) {
157
130
  const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
158
131
  if (values.length > 0) {
@@ -180,13 +153,13 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
180
153
  }
181
154
  }
182
155
  delete blockFlatData.blockType;
183
- const coercedBlockData = await this.coerceData(blockTableName, {
156
+ const blockData = {
184
157
  ...blockFlatData,
185
158
  _parent_id: flatData.id,
186
159
  _order: i,
187
160
  block_type: block.blockType
188
- });
189
- await tx.insertInto(blockTableName).values(coercedBlockData).execute();
161
+ };
162
+ await tx.insertInto(blockTableName).values(blockData).execute();
190
163
  }
191
164
  }
192
165
  return this.findOne(collection, { id: flatData.id }, tx);
@@ -194,7 +167,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
194
167
  }
195
168
  async findOne(collection, query, tx) {
196
169
  const tableName = toSnakeCase(collection);
197
- const executor = tx || this._db;
170
+ const executor = tx || this.db;
198
171
  let qb = executor.selectFrom(tableName).selectAll();
199
172
  if (query && Object.keys(query).length > 0) {
200
173
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
@@ -205,7 +178,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
205
178
  const unflattened = unflattenRow(row);
206
179
  const colDef = this._collections.find((c) => c.slug === collection);
207
180
  if (colDef) {
208
- const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
181
+ const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-fnsf1dfm.js");
209
182
  const relationalFields = getRelationalFields2(colDef.fields);
210
183
  for (const field of relationalFields) {
211
184
  if (!field.name)
@@ -245,9 +218,17 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
245
218
  const page = options?.page || 1;
246
219
  const limit = options?.limit || 10;
247
220
  const offset = (page - 1) * limit;
221
+ const cursorColumn = options?.cursorColumn || "id";
248
222
  const total = await this.count(collection, query);
249
223
  const tableName = toSnakeCase(collection);
250
- let qb = this._db.selectFrom(tableName).selectAll().limit(limit).offset(offset);
224
+ let qb = this.db.selectFrom(tableName).selectAll().limit(limit);
225
+ if (options?.after) {
226
+ qb = qb.where(cursorColumn, ">", options.after);
227
+ } else if (options?.before) {
228
+ qb = qb.where(cursorColumn, "<", options.before);
229
+ } else {
230
+ qb = qb.offset(offset);
231
+ }
251
232
  if (query && Object.keys(query).length > 0) {
252
233
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
253
234
  }
@@ -256,11 +237,13 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
256
237
  const col = isDesc ? options.sort.substring(1) : options.sort;
257
238
  qb = qb.orderBy(col, isDesc ? "desc" : "asc");
258
239
  } else {
259
- qb = qb.orderBy("created_at", "desc");
240
+ qb = qb.orderBy(cursorColumn, "desc");
260
241
  }
261
242
  const rows = await qb.execute();
262
243
  if (rows.length === 0) {
263
244
  const totalPages2 = Math.ceil(total / limit);
245
+ const afterAnchor = options?.after;
246
+ const beforeAnchor = options?.before;
264
247
  return {
265
248
  docs: [],
266
249
  totalDocs: total,
@@ -268,10 +251,12 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
268
251
  totalPages: totalPages2,
269
252
  page,
270
253
  pagingCounter: offset + 1,
271
- hasNextPage: page * limit < total,
272
- hasPrevPage: page > 1,
273
- prevPage: page > 1 ? page - 1 : null,
274
- nextPage: page < totalPages2 ? page + 1 : null
254
+ hasNextPage: false,
255
+ hasPrevPage: !!afterAnchor || page > 1,
256
+ prevPage: null,
257
+ nextPage: null,
258
+ nextCursor: null,
259
+ prevCursor: afterAnchor ? afterAnchor : null
275
260
  };
276
261
  }
277
262
  const rowIds = rows.map((r) => r.id);
@@ -286,7 +271,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
286
271
  if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
287
272
  const joinTableName = `${toSnakeCase(collection)}_${snakeName}_relations`.toLowerCase();
288
273
  try {
289
- const allRelations = await this._db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
274
+ const allRelations = await this.db.selectFrom(joinTableName).selectAll().where("source_id", "in", rowIds).orderBy("order", "asc").execute();
290
275
  const relationsBySource = allRelations.reduce((acc, r) => {
291
276
  if (!acc[r.source_id])
292
277
  acc[r.source_id] = [];
@@ -310,7 +295,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
310
295
  for (const b of field.blocks) {
311
296
  const blockTableName = `${collection}_${snakeName}_${toSnakeCase(b.slug)}`.toLowerCase();
312
297
  try {
313
- const allBlocks = await this._db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
298
+ const allBlocks = await this.db.selectFrom(blockTableName).selectAll().where("_parent_id", "in", rowIds).execute();
314
299
  for (const blk of allBlocks) {
315
300
  const uf = unflattenRow(blk);
316
301
  uf.blockType = blk.block_type;
@@ -349,14 +334,16 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
349
334
  page,
350
335
  pagingCounter: offset + 1,
351
336
  hasNextPage: page * limit < total,
352
- hasPrevPage: page > 1,
337
+ hasPrevPage: page > 1 || !!options?.after || !!options?.before,
353
338
  prevPage: page > 1 ? page - 1 : null,
354
- nextPage: page < totalPages ? page + 1 : null
339
+ nextPage: page < totalPages ? page + 1 : null,
340
+ nextCursor: docs.length > 0 ? docs[docs.length - 1][cursorColumn] : null,
341
+ prevCursor: docs.length > 0 && (page > 1 || !!options?.after || !!options?.before) ? docs[0][cursorColumn] : null
355
342
  };
356
343
  }
357
344
  async update(collection, query, data) {
358
345
  const tableName = toSnakeCase(collection);
359
- return this._db.transaction().execute(async (tx) => {
346
+ return this.db.transaction().execute(async (tx) => {
360
347
  let normalizedQuery = query;
361
348
  if (typeof query !== "object" || query === null) {
362
349
  normalizedQuery = { id: query };
@@ -364,6 +351,9 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
364
351
  const current = await this.findOne(collection, normalizedQuery, tx);
365
352
  if (!current)
366
353
  throw new Error("Document not found");
354
+ const store = requestContext.getStore();
355
+ if (store)
356
+ store.previousData = current;
367
357
  const colDef = this._collections.find((c) => c.slug === collection);
368
358
  const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
369
359
  const flatData = flattenPayload(data, "", jsonFields);
@@ -385,10 +375,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
385
375
  delete flatData[key];
386
376
  }
387
377
  }
388
- flatData.updated_at = new Date().toISOString();
389
378
  if (Object.keys(flatData).length > 0) {
390
- const coercedData = await this.coerceData(collection, flatData);
391
- await tx.updateTable(tableName).set(coercedData).where("id", "=", current.id).execute();
379
+ await tx.updateTable(tableName).set(flatData).where("id", "=", current.id).execute();
392
380
  }
393
381
  for (const [key, values] of Object.entries(hasManyData)) {
394
382
  const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
@@ -426,13 +414,13 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
426
414
  }
427
415
  }
428
416
  delete blockFlatData.blockType;
429
- const coercedBlockData = await this.coerceData(blockTableName, {
417
+ const blockData = {
430
418
  ...blockFlatData,
431
419
  _parent_id: current.id,
432
420
  _order: i,
433
421
  block_type: block.blockType
434
- });
435
- await tx.insertInto(blockTableName).values(coercedBlockData).execute();
422
+ };
423
+ await tx.insertInto(blockTableName).values(blockData).execute();
436
424
  }
437
425
  }
438
426
  return this.findOne(collection, { id: current.id }, tx);
@@ -447,7 +435,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
447
435
  const current = await this.findOne(collection, normalizedQuery);
448
436
  if (!current)
449
437
  return false;
450
- await this._db.transaction().execute(async (tx) => {
438
+ await this.db.transaction().execute(async (tx) => {
451
439
  const colDef = this._collections.find((c) => c.slug === collection);
452
440
  if (colDef) {
453
441
  for (const field of colDef.fields) {
@@ -473,7 +461,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
473
461
  }
474
462
  async updateMany(collection, query, data) {
475
463
  const tableName = toSnakeCase(collection);
476
- return this._db.transaction().execute(async (tx) => {
464
+ return this.db.transaction().execute(async (tx) => {
477
465
  let qb = tx.updateTable(tableName);
478
466
  if (query && Object.keys(query).length > 0) {
479
467
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
@@ -491,10 +479,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
491
479
  delete flatData[key];
492
480
  }
493
481
  }
494
- flatData.updated_at = new Date().toISOString();
495
482
  if (Object.keys(flatData).length > 0) {
496
- const coercedData = await this.coerceData(collection, flatData);
497
- const result = await qb.set(coercedData).executeTakeFirst();
483
+ const result = await qb.set(flatData).executeTakeFirst();
498
484
  return Number(result.numUpdatedRows || 0);
499
485
  }
500
486
  return 0;
@@ -502,7 +488,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
502
488
  }
503
489
  async deleteMany(collection, query) {
504
490
  const tableName = toSnakeCase(collection);
505
- return this._db.transaction().execute(async (tx) => {
491
+ return this.db.transaction().execute(async (tx) => {
506
492
  let selectQb = tx.selectFrom(tableName).select("id");
507
493
  if (query && Object.keys(query).length > 0) {
508
494
  selectQb = selectQb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
@@ -536,7 +522,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
536
522
  }
537
523
  async findGlobal(slug) {
538
524
  const tableName = toSnakeCase(slug);
539
- const row = await this._db.selectFrom(tableName).selectAll().limit(1).executeTakeFirst();
525
+ const row = await this.db.selectFrom(tableName).selectAll().limit(1).executeTakeFirst();
540
526
  return row ? unflattenRow(row) : null;
541
527
  }
542
528
  async updateGlobal(slug, data) {
@@ -550,15 +536,15 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
550
536
  if (!flatData.id)
551
537
  flatData.id = "global";
552
538
  flatData[toSnakeCase("createdAt")] = now;
553
- await this._db.insertInto(tableName).values(flatData).execute();
539
+ await this.db.insertInto(tableName).values(flatData).execute();
554
540
  } else {
555
- await this._db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
541
+ await this.db.updateTable(tableName).set(flatData).where("id", "=", existing.id).execute();
556
542
  }
557
543
  return this.findGlobal(slug);
558
544
  }
559
545
  async runMigrations() {
560
546
  const migrator = new Migrator({
561
- db: this._db,
547
+ db: this.db,
562
548
  provider: new FileMigrationProvider({
563
549
  fs,
564
550
  path,
@@ -581,7 +567,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
581
567
  this._collections = collections;
582
568
  this._globals = globals;
583
569
  if (this.push) {
584
- await pushSchema(this._db, "sqlite", collections, globals, {
570
+ await pushSchema(this.db, "sqlite", collections, globals, {
585
571
  pushDestructive: this.pushDestructive
586
572
  });
587
573
  }
@@ -0,0 +1,15 @@
1
+ // src/utils/context.ts
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ var GLOBAL_CONTEXT_KEY = Symbol.for("opacacms.requestContext");
4
+ if (!globalThis[GLOBAL_CONTEXT_KEY]) {
5
+ globalThis[GLOBAL_CONTEXT_KEY] = new AsyncLocalStorage;
6
+ }
7
+ var requestContext = globalThis[GLOBAL_CONTEXT_KEY];
8
+ function getUserIdFromContext() {
9
+ return requestContext.getStore()?.userId || null;
10
+ }
11
+ function getPreviousDataFromContext() {
12
+ return requestContext.getStore()?.previousData || null;
13
+ }
14
+
15
+ export { requestContext, getUserIdFromContext, getPreviousDataFromContext };