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
@@ -0,0 +1,237 @@
1
+ import {
2
+ require_picocolors
3
+ } from "./chunk-rwqwsanx.js";
4
+ import {
5
+ Ct,
6
+ Nt,
7
+ R,
8
+ Vt,
9
+ Zt
10
+ } from "./chunk-ec4jhybj.js";
11
+ import {
12
+ defineCommand
13
+ } from "./chunk-1qm0m8r8.js";
14
+ import {
15
+ __toESM
16
+ } from "./chunk-6bywt602.js";
17
+
18
+ // src/cli/commands/generate.ts
19
+ import fs2 from "node:fs";
20
+ import { resolve as resolve2 } from "node:path";
21
+ var import_picocolors = __toESM(require_picocolors(), 1);
22
+
23
+ // src/cli/core/types/generate-types.ts
24
+ import fs from "node:fs";
25
+ import { resolve } from "node:path";
26
+ async function generateTypesCommand(url, token, out) {
27
+ console.log(`[OpacaCMS] Fetching metadata from ${url}/api/__admin/metadata...`);
28
+ let data;
29
+ try {
30
+ const res = await fetch(`${url.replace(/\/$/, "")}/api/__admin/metadata`, {
31
+ headers: {
32
+ Authorization: `Bearer ${token}`
33
+ }
34
+ });
35
+ if (!res.ok) {
36
+ if (res.status === 401 || res.status === 403) {
37
+ throw new Error("Authentication failed. Ensure your API Key is correct and has the necessary permissions.");
38
+ }
39
+ const errorText = await res.text().catch(() => "Unknown error");
40
+ throw new Error(`Server responded with ${res.status}: ${errorText}`);
41
+ }
42
+ data = await res.json();
43
+ } catch (err) {
44
+ console.error(`[OpacaCMS] Failed to fetch metadata: ${err.message}`);
45
+ process.exit(1);
46
+ }
47
+ console.log(`[OpacaCMS] Successfully fetched metadata for ${data.collections.length} collections and ${data.globals?.length || 0} globals.`);
48
+ let dtsContent = `// Automatically generated by OpacaCMS CLI
49
+ // Do not edit this file manually
50
+
51
+ export interface OpacaAsset {
52
+ assetId: string;
53
+ url: string;
54
+ filename: string;
55
+ mime_type: string;
56
+ filesize: number;
57
+ meta?: Record<string, any>;
58
+ }
59
+
60
+ export interface OpacaCollections {
61
+ collections: {
62
+ `;
63
+ for (const collection of data.collections) {
64
+ dtsContent += ` "${collection.slug}": {
65
+ `;
66
+ dtsContent += ` id: string | number;
67
+ `;
68
+ for (const field of collection.fields) {
69
+ const typeStr = mapFieldToTsType(field.type);
70
+ const isOptional = field.required ? "" : "?";
71
+ dtsContent += ` ${field.name}${isOptional}: ${typeStr};
72
+ `;
73
+ }
74
+ if (collection.timestamps) {
75
+ dtsContent += ` createdAt: string;
76
+ `;
77
+ dtsContent += ` updatedAt: string;
78
+ `;
79
+ }
80
+ dtsContent += ` };
81
+ `;
82
+ }
83
+ dtsContent += ` };
84
+ `;
85
+ if (data.globals && data.globals.length > 0) {
86
+ dtsContent += ` globals: {
87
+ `;
88
+ for (const globalConf of data.globals) {
89
+ dtsContent += ` "${globalConf.slug}": {
90
+ `;
91
+ for (const field of globalConf.fields) {
92
+ const typeStr = mapFieldToTsType(field.type);
93
+ const isOptional = field.required ? "" : "?";
94
+ dtsContent += ` ${field.name}${isOptional}: ${typeStr};
95
+ `;
96
+ }
97
+ dtsContent += ` };
98
+ `;
99
+ }
100
+ dtsContent += ` };
101
+ `;
102
+ } else {
103
+ dtsContent += ` globals: Record<string, any>;
104
+ `;
105
+ }
106
+ dtsContent += `}
107
+ `;
108
+ const outputPath = resolve(process.cwd(), out || "opaca.d.ts");
109
+ fs.writeFileSync(outputPath, dtsContent);
110
+ console.log(`[OpacaCMS] Types successfully generated at ${outputPath}`);
111
+ }
112
+ function mapFieldToTsType(type) {
113
+ switch (type) {
114
+ case "text":
115
+ case "textarea":
116
+ case "richtext":
117
+ case "select":
118
+ case "date":
119
+ return "string";
120
+ case "number":
121
+ return "number";
122
+ case "boolean":
123
+ return "boolean";
124
+ case "json":
125
+ return "any | any[]";
126
+ case "file":
127
+ return "OpacaAsset";
128
+ case "relationship":
129
+ return "string | number | any";
130
+ default:
131
+ return "any";
132
+ }
133
+ }
134
+
135
+ // src/cli/commands/generate.ts
136
+ var generate_default = defineCommand({
137
+ meta: {
138
+ name: "generate",
139
+ description: "Generate boilerplates (collections/globals) or TypeScript definitions"
140
+ },
141
+ args: {
142
+ type: {
143
+ type: "positional",
144
+ description: "What to generate: 'types', 'collection', or 'global'",
145
+ required: true
146
+ },
147
+ name: {
148
+ type: "positional",
149
+ description: "The name of the collection or global to generate (if applicable)",
150
+ required: false
151
+ },
152
+ url: {
153
+ type: "string",
154
+ description: "The URL of the OpacaCMS instance (for 'types')"
155
+ },
156
+ token: {
157
+ type: "string",
158
+ description: "The API Key to use for fetching metadata (for 'types')"
159
+ },
160
+ out: {
161
+ type: "string",
162
+ alias: "o",
163
+ description: "The output file path (for 'types')"
164
+ }
165
+ },
166
+ async run({ args }) {
167
+ if (args.type === "types") {
168
+ if (!args.url || !args.token) {
169
+ R.error(import_picocolors.default.red("URL and Token are required to generate types from a remote instance."));
170
+ R.info("Usage: opacacms generate types --url <url> --token <token> [--out <file>]");
171
+ process.exit(1);
172
+ }
173
+ await generateTypesCommand(args.url, args.token, args.out || "opaca.d.ts");
174
+ return;
175
+ }
176
+ if (args.type === "collection" || args.type === "global") {
177
+ let name = args.name;
178
+ if (!name) {
179
+ const response = await Zt({
180
+ message: `What should we name your ${args.type}? (e.g. ${args.type === "collection" ? "posts" : "site-settings"})`
181
+ });
182
+ if (Ct(response)) {
183
+ Nt("Operation cancelled.");
184
+ process.exit(0);
185
+ }
186
+ name = response;
187
+ }
188
+ const isCollection = args.type === "collection";
189
+ const folderName = isCollection ? "collections" : "globals";
190
+ const fileName = `${name}.ts`;
191
+ const filePath = resolve2(process.cwd(), folderName, fileName);
192
+ if (fs2.existsSync(filePath)) {
193
+ R.error(import_picocolors.default.red(`File already exists at ${folderName}/${fileName}`));
194
+ process.exit(1);
195
+ }
196
+ fs2.mkdirSync(resolve2(process.cwd(), folderName), { recursive: true });
197
+ const template = isCollection ? `import { z, defineCollection } from 'opacacms/schema';
198
+
199
+ export const ${name} = defineCollection({
200
+ slug: '${name}',
201
+ label: '${name.charAt(0).toUpperCase() + name.slice(1)}',
202
+ admin: {
203
+ useAsTitle: 'title',
204
+ icon: 'FileText'
205
+ },
206
+ schema: z.object({
207
+ id: z.text().default(() => crypto.randomUUID()),
208
+ title: z.text({ label: 'Title' }).required(),
209
+ content: z.richText({ label: 'Content' }),
210
+ })
211
+ });
212
+ ` : `import { z, defineGlobal } from 'opacacms/schema';
213
+
214
+ export const ${name} = defineGlobal({
215
+ slug: '${name}',
216
+ label: '${name.charAt(0).toUpperCase() + name.slice(1)}',
217
+ admin: {
218
+ icon: 'Settings'
219
+ },
220
+ schema: z.object({
221
+ siteName: z.text({ label: 'Site Name' }).required(),
222
+ description: z.textarea({ label: 'Description' }),
223
+ })
224
+ });
225
+ `;
226
+ fs2.writeFileSync(filePath, template);
227
+ R.success(import_picocolors.default.green(`Successfully generated ${args.type} at ./${folderName}/${fileName}`));
228
+ Vt(`Don't forget to import it into your opacacms.config.ts and add it to the '${folderName}' array!`, "Reminder");
229
+ return;
230
+ }
231
+ R.error(import_picocolors.default.red(`Unknown generation type: ${args.type}. Use 'types', 'collection', or 'global'.`));
232
+ process.exit(1);
233
+ }
234
+ });
235
+ export {
236
+ generate_default as default
237
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ init_compiler,
3
+ init_zod,
4
+ zodToOpacaFields
5
+ } from "./chunk-m5ems3hh.js";
6
+
7
+ // src/schema/collection.ts
8
+ init_compiler();
9
+ function defineCollection(config) {
10
+ const compiledSchema = config.schema.meta({
11
+ opaca: {
12
+ collectionConfig: {
13
+ slug: config.slug,
14
+ label: config.label,
15
+ useAsTitle: config.admin?.useAsTitle
16
+ }
17
+ }
18
+ });
19
+ return {
20
+ ...config,
21
+ schema: compiledSchema,
22
+ fields: zodToOpacaFields(compiledSchema),
23
+ seed: config.seed,
24
+ __type: null
25
+ };
26
+ }
27
+
28
+ // src/schema/index.ts
29
+ init_compiler();
30
+
31
+ // src/schema/global.ts
32
+ init_compiler();
33
+ function defineGlobal(config) {
34
+ const compiledSchema = config.schema.meta({
35
+ opaca: {
36
+ globalConfig: {
37
+ slug: config.slug,
38
+ label: config.label
39
+ }
40
+ }
41
+ });
42
+ return {
43
+ ...config,
44
+ schema: compiledSchema,
45
+ fields: zodToOpacaFields(compiledSchema),
46
+ seed: config.seed,
47
+ __type: null
48
+ };
49
+ }
50
+
51
+ // src/schema/index.ts
52
+ init_zod();
53
+
54
+ export { defineCollection, defineGlobal };
@@ -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,20 @@ 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 { CompiledQuery, FileMigrationProvider, Kysely, Migrator, SqliteDialect } from "kysely";
28
+ import {
29
+ CompiledQuery,
30
+ FileMigrationProvider,
31
+ Migrator,
32
+ SqliteDialect
33
+ } from "kysely";
26
34
  class BetterSQLiteAdapter extends BaseDatabaseAdapter {
27
35
  path;
28
36
  name = "better-sqlite3";
@@ -55,10 +63,16 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
55
63
  const require2 = createRequire(import.meta.url);
56
64
  const Database = require2("better-sqlite3");
57
65
  this._rawDb = new Database(this.path);
58
- this._db = new Kysely({
66
+ const { createOpacaKysely } = await import("./chunk-sqsfk9p4.js");
67
+ this._db = createOpacaKysely({
59
68
  dialect: new SqliteDialect({
60
69
  database: this._rawDb
61
- })
70
+ }),
71
+ config: {
72
+ collections: this._collections,
73
+ globals: this._globals,
74
+ db: { name: "better-sqlite3" }
75
+ }
62
76
  });
63
77
  }
64
78
  async disconnect() {
@@ -72,45 +86,6 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
72
86
  const result = await this.db.executeQuery(compiled);
73
87
  return result.rows;
74
88
  }
75
- async coerceData(collection, data) {
76
- const colDef = this._collections.find((c) => c.slug === collection);
77
- if (!colDef)
78
- return data;
79
- const result = { ...data };
80
- const { flattenFields: flattenFields2 } = await import("./chunk-526a3gqx.js");
81
- const allFields = flattenFields2(colDef.fields);
82
- for (const field of allFields) {
83
- const colName = toSnakeCase(field.name);
84
- if (!(colName in result))
85
- continue;
86
- const value = result[colName];
87
- if (value === undefined || value === null)
88
- continue;
89
- switch (field.type) {
90
- case "boolean":
91
- result[colName] = value ? 1 : 0;
92
- break;
93
- case "number":
94
- result[colName] = Number(value);
95
- break;
96
- case "date":
97
- if (value instanceof Date) {
98
- result[colName] = value.toISOString();
99
- } else if (typeof value === "string") {
100
- result[colName] = new Date(value).toISOString();
101
- }
102
- break;
103
- case "richtext":
104
- case "json":
105
- case "file":
106
- if (typeof value === "object") {
107
- result[colName] = JSON.stringify(value);
108
- }
109
- break;
110
- }
111
- }
112
- return result;
113
- }
114
89
  async count(collection, query) {
115
90
  const tableName = toSnakeCase(collection);
116
91
  let qb = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count"));
@@ -124,13 +99,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
124
99
  const tableName = toSnakeCase(collection);
125
100
  return this.db.transaction().execute(async (tx) => {
126
101
  const colDef = this._collections.find((c) => c.slug === collection);
127
- const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
128
- const flatData = flattenPayload(data, "", jsonFields);
129
- for (const field of jsonFields) {
130
- if (flatData[field] && typeof flatData[field] === "object") {
131
- flatData[field] = JSON.stringify(flatData[field]);
132
- }
133
- }
102
+ const flatData = flattenPayload(data);
103
+ const relationalFields = getRelationalFields(colDef?.fields || []);
134
104
  const hasManyData = {};
135
105
  const blocksData = {};
136
106
  for (const key in flatData) {
@@ -144,24 +114,18 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
144
114
  delete flatData[key];
145
115
  }
146
116
  }
147
- if (!flatData.id)
148
- flatData.id = crypto.randomUUID();
149
117
  const dbFields = flattenFields(colDef?.fields || []);
150
118
  const validCols = new Set(dbFields.map((f) => toSnakeCase(f.name)));
151
119
  validCols.add("id");
152
120
  validCols.add(toSnakeCase("createdAt"));
153
121
  validCols.add(toSnakeCase("updatedAt"));
154
- const now = new Date().toISOString();
155
- flatData[toSnakeCase("createdAt")] = now;
156
- flatData[toSnakeCase("updatedAt")] = now;
157
122
  const filteredData = {};
158
123
  for (const col of Object.keys(flatData)) {
159
124
  if (validCols.has(col)) {
160
125
  filteredData[col] = flatData[col];
161
126
  }
162
127
  }
163
- const coercedData = await this.coerceData(collection, filteredData);
164
- await tx.insertInto(tableName).values(coercedData).execute();
128
+ await tx.insertInto(tableName).values(filteredData).execute();
165
129
  for (const [key, values] of Object.entries(hasManyData)) {
166
130
  const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
167
131
  if (values.length > 0) {
@@ -189,13 +153,13 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
189
153
  }
190
154
  }
191
155
  delete blockFlatData.blockType;
192
- const coercedBlockData = await this.coerceData(blockTableName, {
156
+ const blockData = {
193
157
  ...blockFlatData,
194
158
  _parent_id: flatData.id,
195
159
  _order: i,
196
160
  block_type: block.blockType
197
- });
198
- await tx.insertInto(blockTableName).values(coercedBlockData).execute();
161
+ };
162
+ await tx.insertInto(blockTableName).values(blockData).execute();
199
163
  }
200
164
  }
201
165
  return this.findOne(collection, { id: flatData.id }, tx);
@@ -214,7 +178,7 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
214
178
  const unflattened = unflattenRow(row);
215
179
  const colDef = this._collections.find((c) => c.slug === collection);
216
180
  if (colDef) {
217
- const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-526a3gqx.js");
181
+ const { getRelationalFields: getRelationalFields2, toSnakeCase: toSnakeCase2 } = await import("./chunk-fnsf1dfm.js");
218
182
  const relationalFields = getRelationalFields2(colDef.fields);
219
183
  for (const field of relationalFields) {
220
184
  if (!field.name)
@@ -254,9 +218,17 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
254
218
  const page = options?.page || 1;
255
219
  const limit = options?.limit || 10;
256
220
  const offset = (page - 1) * limit;
221
+ const cursorColumn = options?.cursorColumn || "id";
257
222
  const total = await this.count(collection, query);
258
223
  const tableName = toSnakeCase(collection);
259
- 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
+ }
260
232
  if (query && Object.keys(query).length > 0) {
261
233
  qb = qb.where((eb) => buildKyselyWhere(eb, query) || eb.val(true));
262
234
  }
@@ -265,11 +237,13 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
265
237
  const col = isDesc ? options.sort.substring(1) : options.sort;
266
238
  qb = qb.orderBy(col, isDesc ? "desc" : "asc");
267
239
  } else {
268
- qb = qb.orderBy("created_at", "desc");
240
+ qb = qb.orderBy(cursorColumn, "desc");
269
241
  }
270
242
  const rows = await qb.execute();
271
243
  if (rows.length === 0) {
272
244
  const totalPages2 = Math.ceil(total / limit);
245
+ const afterAnchor = options?.after;
246
+ const beforeAnchor = options?.before;
273
247
  return {
274
248
  docs: [],
275
249
  totalDocs: total,
@@ -277,10 +251,12 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
277
251
  totalPages: totalPages2,
278
252
  page,
279
253
  pagingCounter: offset + 1,
280
- hasNextPage: page * limit < total,
281
- hasPrevPage: page > 1,
282
- prevPage: page > 1 ? page - 1 : null,
283
- 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
284
260
  };
285
261
  }
286
262
  const rowIds = rows.map((r) => r.id);
@@ -358,9 +334,11 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
358
334
  page,
359
335
  pagingCounter: offset + 1,
360
336
  hasNextPage: page * limit < total,
361
- hasPrevPage: page > 1,
337
+ hasPrevPage: page > 1 || !!options?.after || !!options?.before,
362
338
  prevPage: page > 1 ? page - 1 : null,
363
- 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
364
342
  };
365
343
  }
366
344
  async update(collection, query, data) {
@@ -373,6 +351,9 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
373
351
  const current = await this.findOne(collection, normalizedQuery, tx);
374
352
  if (!current)
375
353
  throw new Error("Document not found");
354
+ const store = requestContext.getStore();
355
+ if (store)
356
+ store.previousData = current;
376
357
  const colDef = this._collections.find((c) => c.slug === collection);
377
358
  const jsonFields = colDef?.fields.filter((f) => ["richtext", "json", "file"].includes(f.type)).map((f) => f.name) || [];
378
359
  const flatData = flattenPayload(data, "", jsonFields);
@@ -394,10 +375,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
394
375
  delete flatData[key];
395
376
  }
396
377
  }
397
- flatData.updated_at = new Date().toISOString();
398
378
  if (Object.keys(flatData).length > 0) {
399
- const coercedData = await this.coerceData(collection, flatData);
400
- await tx.updateTable(tableName).set(coercedData).where("id", "=", current.id).execute();
379
+ await tx.updateTable(tableName).set(flatData).where("id", "=", current.id).execute();
401
380
  }
402
381
  for (const [key, values] of Object.entries(hasManyData)) {
403
382
  const joinTableName = `${tableName}_${toSnakeCase(key)}_relations`.toLowerCase();
@@ -435,13 +414,13 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
435
414
  }
436
415
  }
437
416
  delete blockFlatData.blockType;
438
- const coercedBlockData = await this.coerceData(blockTableName, {
417
+ const blockData = {
439
418
  ...blockFlatData,
440
419
  _parent_id: current.id,
441
420
  _order: i,
442
421
  block_type: block.blockType
443
- });
444
- await tx.insertInto(blockTableName).values(coercedBlockData).execute();
422
+ };
423
+ await tx.insertInto(blockTableName).values(blockData).execute();
445
424
  }
446
425
  }
447
426
  return this.findOne(collection, { id: current.id }, tx);
@@ -500,10 +479,8 @@ class BetterSQLiteAdapter extends BaseDatabaseAdapter {
500
479
  delete flatData[key];
501
480
  }
502
481
  }
503
- flatData.updated_at = new Date().toISOString();
504
482
  if (Object.keys(flatData).length > 0) {
505
- const coercedData = await this.coerceData(collection, flatData);
506
- const result = await qb.set(coercedData).executeTakeFirst();
483
+ const result = await qb.set(flatData).executeTakeFirst();
507
484
  return Number(result.numUpdatedRows || 0);
508
485
  }
509
486
  return 0;
@@ -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 };