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
@@ -0,0 +1,24 @@
1
+ export interface WebhookPayload {
2
+ event: string;
3
+ collection: string;
4
+ data: unknown;
5
+ timestamp: string;
6
+ }
7
+ /**
8
+ * Verifies an incoming webhook signature.
9
+ * Supports both string secrets (HMAC-SHA256) and custom verification functions.
10
+ */
11
+ export declare function verifyWebhookSignature(req: Request, secretOrFn: string | ((req: Request) => boolean | Promise<boolean>), headerName?: string): Promise<boolean>;
12
+ /**
13
+ * Dispatches an outgoing webhook with retries and exponential backoff.
14
+ */
15
+ export declare function dispatchWebhook(args: {
16
+ url: string;
17
+ data: unknown;
18
+ event: string;
19
+ collection: string;
20
+ headers?: Record<string, string>;
21
+ retries?: number;
22
+ timeoutMs?: number;
23
+ transform?: (data: any) => any;
24
+ }): Promise<void>;
@@ -170,6 +170,14 @@ export declare const OpacaAuthConfigSchema: z.ZodObject<{
170
170
  }, z.core.$strip>;
171
171
  export declare const ApiConfigSchema: z.ZodObject<{
172
172
  maxLimit: z.ZodOptional<z.ZodNumber>;
173
+ rest: z.ZodOptional<z.ZodObject<{
174
+ enabled: z.ZodOptional<z.ZodBoolean>;
175
+ }, z.core.$strip>>;
176
+ graphql: z.ZodOptional<z.ZodObject<{
177
+ enabled: z.ZodOptional<z.ZodBoolean>;
178
+ path: z.ZodOptional<z.ZodString>;
179
+ graphiql: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
180
+ }, z.core.$strip>>;
173
181
  rateLimit: z.ZodOptional<z.ZodObject<{
174
182
  windowMs: z.ZodOptional<z.ZodNumber>;
175
183
  limit: z.ZodOptional<z.ZodNumber>;
@@ -189,8 +197,58 @@ export declare const ApiConfigSchema: z.ZodObject<{
189
197
  }, z.core.$strip>>;
190
198
  }, z.core.$strip>;
191
199
  export type ApiConfig = z.infer<typeof ApiConfigSchema>;
200
+ export declare const OpacaPluginSchema: z.ZodObject<{
201
+ name: z.ZodString;
202
+ label: z.ZodOptional<z.ZodString>;
203
+ description: z.ZodOptional<z.ZodString>;
204
+ version: z.ZodOptional<z.ZodString>;
205
+ author: z.ZodOptional<z.ZodString>;
206
+ homepage: z.ZodOptional<z.ZodString>;
207
+ icon: z.ZodOptional<z.ZodString>;
208
+ onInit: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
209
+ onRouterInit: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
210
+ onInitComplete: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
211
+ onRequest: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
212
+ onExport: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
213
+ onDestroy: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
214
+ adminAssets: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
215
+ configSchema: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodLazy<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>, z.ZodAny]>>;
216
+ adminUI: z.ZodOptional<z.ZodObject<{
217
+ type: z.ZodEnum<{
218
+ iframe: "iframe";
219
+ ssr: "ssr";
220
+ }>;
221
+ path: z.ZodString;
222
+ }, z.core.$strip>>;
223
+ settings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
224
+ }, z.core.$strip>;
225
+ export type OpacaPlugin = z.infer<typeof OpacaPluginSchema>;
192
226
  export declare const OpacaConfigSchema: z.ZodObject<{
193
- plugins: z.ZodOptional<z.ZodArray<z.ZodAny>>;
227
+ plugins: z.ZodOptional<z.ZodArray<z.ZodObject<{
228
+ name: z.ZodString;
229
+ label: z.ZodOptional<z.ZodString>;
230
+ description: z.ZodOptional<z.ZodString>;
231
+ version: z.ZodOptional<z.ZodString>;
232
+ author: z.ZodOptional<z.ZodString>;
233
+ homepage: z.ZodOptional<z.ZodString>;
234
+ icon: z.ZodOptional<z.ZodString>;
235
+ onInit: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
236
+ onRouterInit: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
237
+ onInitComplete: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
238
+ onRequest: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
239
+ onExport: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
240
+ onDestroy: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
241
+ adminAssets: z.ZodOptional<z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>;
242
+ configSchema: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodLazy<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>>, z.ZodAny]>>;
243
+ adminUI: z.ZodOptional<z.ZodObject<{
244
+ type: z.ZodEnum<{
245
+ iframe: "iframe";
246
+ ssr: "ssr";
247
+ }>;
248
+ path: z.ZodString;
249
+ }, z.core.$strip>>;
250
+ settings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
251
+ }, z.core.$strip>>>;
194
252
  collections: z.ZodArray<z.ZodObject<{
195
253
  slug: z.ZodString;
196
254
  label: z.ZodOptional<z.ZodString>;
@@ -323,6 +381,14 @@ export declare const OpacaConfigSchema: z.ZodObject<{
323
381
  }, z.core.$strip>>;
324
382
  api: z.ZodOptional<z.ZodObject<{
325
383
  maxLimit: z.ZodOptional<z.ZodNumber>;
384
+ rest: z.ZodOptional<z.ZodObject<{
385
+ enabled: z.ZodOptional<z.ZodBoolean>;
386
+ }, z.core.$strip>>;
387
+ graphql: z.ZodOptional<z.ZodObject<{
388
+ enabled: z.ZodOptional<z.ZodBoolean>;
389
+ path: z.ZodOptional<z.ZodString>;
390
+ graphiql: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
391
+ }, z.core.$strip>>;
326
392
  rateLimit: z.ZodOptional<z.ZodObject<{
327
393
  windowMs: z.ZodOptional<z.ZodNumber>;
328
394
  limit: z.ZodOptional<z.ZodNumber>;
@@ -4,6 +4,7 @@ export declare function generateSchemaForCollection(collection: {
4
4
  fields: Field[];
5
5
  versions?: any;
6
6
  timestamps?: any;
7
+ schema?: any;
7
8
  }, isUpdate?: boolean, forDocs?: boolean): z.ZodObject<{
8
9
  [x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
9
10
  }, z.core.$strip>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opacacms",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "scripts": {
5
5
  "build": "bun run ../../scripts/build.ts && tsc --emitDeclarationOnly",
6
6
  "build:publish": "bun run ../../scripts/build.ts --publish && tsc --emitDeclarationOnly",
@@ -23,8 +23,9 @@
23
23
  },
24
24
  "module": "dist/index.js",
25
25
  "type": "module",
26
+ "sideEffects": false,
26
27
  "bin": {
27
- "opacacms": "./dist/cli/index.js"
28
+ "opacacms": "./src/cli/index.ts"
28
29
  },
29
30
  "exports": {
30
31
  "./styles.css": {
@@ -112,36 +113,16 @@
112
113
  }
113
114
  },
114
115
  "devDependencies": {
115
- "@cloudflare/workers-types": "^4.20260313.1",
116
- "@faker-js/faker": "^10.3.0",
117
- "@happy-dom/global-registrator": "^20.8.4",
118
- "@hono/bun-transpiler": "^0.2.1",
119
- "@hono/esbuild-transpiler": "^0.1.4",
120
- "@testing-library/dom": "^10.4.1",
121
- "@testing-library/react": "^16.3.2",
122
- "@types/better-sqlite3": "^7.6.13",
123
- "@types/bun": "latest",
124
- "@types/react": "^19.2.14",
125
- "@types/react-dom": "^19.2.3",
126
- "bun-plugin-scss": "^1.0.0",
127
- "dotenv": "^17.3.1",
128
- "drizzle-kit": "^0.31.9",
129
- "esbuild": "^0.27.4",
130
- "esbuild-wasm": "^0.27.4",
131
- "happy-dom": "^20.8.4",
132
- "sass": "^1.98.0"
133
- },
134
- "peerDependencies": {
135
- "react": "^19.2.4",
136
- "react-dom": "^19.2.4",
137
- "typescript": "^5.9.3"
138
- },
139
- "dependencies": {
140
116
  "@aws-sdk/client-s3": "^3.1009.0",
141
117
  "@aws-sdk/s3-request-presigner": "^3.1009.0",
142
118
  "@better-auth/api-key": "^1.5.5",
143
119
  "@better-auth/core": "^1.5.5",
120
+ "@cloudflare/workers-types": "^4.20260313.1",
121
+ "@faker-js/faker": "^10.3.0",
122
+ "@happy-dom/global-registrator": "^20.8.4",
144
123
  "@hono-rate-limiter/cloudflare": "^0.2.2",
124
+ "@hono/bun-transpiler": "^0.2.1",
125
+ "@hono/esbuild-transpiler": "^0.1.4",
145
126
  "@hono/node-server": "^1.19.11",
146
127
  "@lexical/code": "^0.41.0",
147
128
  "@lexical/history": "^0.41.0",
@@ -160,13 +141,18 @@
160
141
  "@r2wc/react-to-web-component": "^2.1.1",
161
142
  "@scalar/hono-api-reference": "^0.10.4",
162
143
  "@tanstack/react-form": "^1.28.5",
163
- "better-auth": "^1.5.5",
144
+ "@testing-library/dom": "^10.4.1",
145
+ "@testing-library/react": "^16.3.2",
146
+ "@types/better-sqlite3": "^7.6.13",
147
+ "@types/bun": "latest",
148
+ "@types/react": "^19.2.14",
149
+ "@types/react-dom": "^19.2.3",
164
150
  "better-sqlite3": "^12.6.2",
165
- "drizzle-orm": "^0.45.1",
166
- "hono": "^4.12.7",
167
- "hono-rate-limiter": "^0.5.3",
168
- "ky": "^1.14.3",
169
- "kysely": "^0.28.11",
151
+ "bun-plugin-scss": "^1.0.0",
152
+ "dotenv": "^17.3.1",
153
+ "esbuild": "^0.27.4",
154
+ "esbuild-wasm": "^0.27.4",
155
+ "happy-dom": "^20.8.4",
170
156
  "kysely-bun-sqlite": "^0.4.0",
171
157
  "kysely-d1": "^0.4.0",
172
158
  "kysely-postgres-js": "^3.0.0",
@@ -174,6 +160,23 @@
174
160
  "lucide-react": "^0.577.0",
175
161
  "nanostores": "^1.1.1",
176
162
  "postgres": "^3.4.8",
163
+ "sass": "^1.98.0"
164
+ },
165
+ "peerDependencies": {
166
+ "react": "^19.2.4",
167
+ "react-dom": "^19.2.4",
168
+ "typescript": "^5.9.3"
169
+ },
170
+ "dependencies": {
171
+ "@hono/graphql-server": "^0.7.0",
172
+ "better-auth": "^1.5.5",
173
+ "class-variance-authority": "^0.7.1",
174
+ "graphql": "^16.13.1",
175
+ "hono": "^4.12.7",
176
+ "hono-rate-limiter": "^0.5.3",
177
+ "ky": "^1.14.3",
178
+ "kysely": "^0.28.11",
179
+ "radix-ui": "^1.4.3",
177
180
  "zod": "^4.3.6",
178
181
  "zod-to-json-schema": "^3.25.1"
179
182
  },
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env bun
2
+ import fs from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { defineCommand, runMain } from "citty";
6
+
7
+ /**
8
+ * Loads and validates the OpacaCMS configuration from a file.
9
+ */
10
+ export async function loadConfig(configPath: string) {
11
+ if (!fs.existsSync(configPath)) {
12
+ throw new Error(`Config file not found at ${configPath}`);
13
+ }
14
+
15
+ const configUrl = pathToFileURL(configPath).href;
16
+ const mod = await import(configUrl);
17
+
18
+ if (!mod.default) {
19
+ throw new Error(`The configuration file at ${configPath} must have a 'default' export.`);
20
+ }
21
+
22
+ const namedExports = Object.keys(mod).filter((k) => k !== "default" && k !== "__esModule");
23
+ if (namedExports.length > 0) {
24
+ throw new Error(
25
+ `Named exports are not allowed in the configuration file. Found: ${namedExports.join(", ")}.`,
26
+ );
27
+ }
28
+
29
+ let opaca = mod.default;
30
+
31
+ if (typeof opaca === "function") {
32
+ const { createD1Mock } = await import("./core/mocks/d1-mock.js");
33
+ const { createR2Mock } = await import("./core/mocks/r2-mock.js");
34
+
35
+ const mockDb = createD1Mock();
36
+ const mockBucket = createR2Mock();
37
+ const mockSecureBucket = createR2Mock();
38
+
39
+ opaca = await opaca(
40
+ {
41
+ DB: mockDb,
42
+ BUCKET: mockBucket,
43
+ SECURE_BUCKET: mockSecureBucket,
44
+ OPACA_SECRET: "cli-secret",
45
+ },
46
+ { url: "http://localhost:3000" },
47
+ );
48
+ }
49
+
50
+ if (opaca?.fetch) {
51
+ throw new Error(
52
+ "Default export is a Hono application. The CLI requires the configuration object.",
53
+ );
54
+ }
55
+
56
+ if (!opaca.db) {
57
+ throw new Error("No database adapter found in config");
58
+ }
59
+
60
+ return opaca;
61
+ }
62
+
63
+ /**
64
+ * Tries to find the Opaca config file automatically.
65
+ */
66
+ export function resolveConfigPath(explicitPath?: string): string {
67
+ let configPath = explicitPath ? resolve(process.cwd(), explicitPath) : "";
68
+ if (!configPath) {
69
+ const defaults = [
70
+ "opaca.config.ts",
71
+ "opaca.config.js",
72
+ "opacacms.config.ts",
73
+ "opacacms.config.js",
74
+ "src/opaca.config.ts",
75
+ "src/opacacms.config.js",
76
+ ];
77
+ for (const d of defaults) {
78
+ if (fs.existsSync(resolve(process.cwd(), d))) {
79
+ configPath = resolve(process.cwd(), d);
80
+ break;
81
+ }
82
+ }
83
+ }
84
+
85
+ if (!configPath || !fs.existsSync(configPath)) {
86
+ throw new Error("Config file not found. Use -c or --config to specify path.");
87
+ }
88
+ return configPath;
89
+ }
90
+
91
+ const mainCommand = defineCommand({
92
+ meta: {
93
+ name: "opacacms",
94
+ version: "0.1.0",
95
+ description: "OpacaCMS CLI - The Headless CMS that doesn't get in your way.",
96
+ },
97
+ args: {
98
+ config: {
99
+ type: "string",
100
+ alias: "c",
101
+ description: "Path to the OpacaCMS configuration file",
102
+ },
103
+ },
104
+ subCommands: {
105
+ init: () => import("./commands/init.js").then((m) => m.default),
106
+ migrate: () => import("./commands/migrate.js").then((m) => m.default),
107
+ seed: () => import("./commands/seed.js").then((m) => m.default),
108
+ generate: () => import("./commands/generate.js").then((m) => m.default),
109
+ plugin: () => import("./commands/plugin.js").then((m) => m.default),
110
+ doctor: () => import("./commands/doctor.js").then((m) => m.default),
111
+ dev: () => import("./commands/dev.js").then((m) => m.default),
112
+ },
113
+ });
114
+
115
+ if (import.meta.main) {
116
+ runMain(mainCommand);
117
+ }
@@ -1,288 +0,0 @@
1
- import {
2
- flattenFields,
3
- mapFieldToPostgresType,
4
- mapFieldToSQLiteType,
5
- toSnakeCase
6
- } from "./chunk-qxt9vge8.js";
7
- import"./chunk-8sqjbsgt.js";
8
-
9
- // src/db/kysely/migration-generator.ts
10
- function generateMigrationCode(collections, globals = [], dialect) {
11
- const allSchemas = [...collections, ...globals];
12
- const mapType = dialect === "postgres" ? mapFieldToPostgresType : mapFieldToSQLiteType;
13
- const tsType = dialect === "postgres" ? "timestamp with time zone" : "text";
14
- let upCode = "";
15
- let downCode = "";
16
- for (const collection of allSchemas) {
17
- const slug = collection.slug;
18
- const flattenedFields = flattenFields(collection.fields);
19
- upCode += ` await db.schema.createTable('${slug}')
20
- `;
21
- upCode += ` .addColumn('id', 'text', (col) => col.primaryKey())
22
- `;
23
- for (const field of flattenedFields) {
24
- if (field.type === "relationship" && "hasMany" in field && field.hasMany)
25
- continue;
26
- const colName = toSnakeCase(field.name);
27
- if (colName === "id")
28
- continue;
29
- const colType = mapType(field);
30
- const constraints = [];
31
- if (field.required)
32
- constraints.push("notNull()");
33
- if (field.unique)
34
- constraints.push("unique()");
35
- if (field.defaultValue !== undefined) {
36
- let val = field.defaultValue;
37
- if (typeof val === "string") {
38
- val = `'${val}'`;
39
- } else if (typeof val === "boolean") {
40
- val = dialect === "sqlite" || dialect === "d1" ? val ? 1 : 0 : val;
41
- }
42
- constraints.push(`defaultTo(${val})`);
43
- }
44
- const constraintChain = constraints.length > 0 ? `.${constraints.join(".")}` : "";
45
- upCode += ` .addColumn('${colName}', '${colType}', (col) => col${constraintChain})
46
- `;
47
- }
48
- const ts = collection.timestamps !== false;
49
- if (ts) {
50
- const config = typeof collection.timestamps === "object" ? collection.timestamps : {};
51
- const createdField = toSnakeCase(config.createdAt || "createdAt");
52
- const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
53
- upCode += ` .addColumn('${createdField}', '${tsType}', (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
54
- `;
55
- upCode += ` .addColumn('${updatedField}', '${tsType}', (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
56
- `;
57
- }
58
- upCode += ` .execute();
59
-
60
- `;
61
- downCode = ` await db.schema.dropTable('${slug}').execute();
62
- ` + downCode;
63
- if ("versions" in collection && collection.versions) {
64
- const versionsTable = `${toSnakeCase(slug)}_versions`;
65
- upCode += ` await db.schema.createTable('${versionsTable}')
66
- `;
67
- upCode += ` .addColumn('id', 'text', (col) => col.primaryKey())
68
- `;
69
- upCode += ` .addColumn('_parent_id', 'text', (col) => col.notNull())
70
- `;
71
- upCode += ` .addColumn('_version_data', '${dialect === "postgres" ? "jsonb" : "text"}', (col) => col.notNull())
72
- `;
73
- upCode += ` .addColumn('_status', 'text')
74
- `;
75
- upCode += ` .addColumn('created_at', '${tsType}', (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
76
- `;
77
- upCode += ` .execute();
78
-
79
- `;
80
- downCode = ` await db.schema.dropTable('${versionsTable}').execute();
81
- ` + downCode;
82
- }
83
- for (const field of flattenedFields) {
84
- if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
85
- const colName = toSnakeCase(field.name);
86
- const joinTableName = `${slug}_${colName}_relations`.toLowerCase();
87
- upCode += ` await db.schema.createTable('${joinTableName}')
88
- `;
89
- upCode += ` .addColumn('id', 'text', (col) => col.primaryKey())
90
- `;
91
- upCode += ` .addColumn('source_id', 'text', (col) => col.notNull())
92
- `;
93
- upCode += ` .addColumn('target_id', 'text', (col) => col.notNull())
94
- `;
95
- upCode += ` .addColumn('order', 'integer', (col) => col.defaultTo(0))
96
- `;
97
- upCode += ` .execute();
98
-
99
- `;
100
- downCode = ` await db.schema.dropTable('${joinTableName}').execute();
101
- ` + downCode;
102
- }
103
- }
104
- for (const field of collection.fields) {
105
- if (field.type === "blocks" && field.blocks && Array.isArray(field.blocks)) {
106
- for (const block of field.blocks) {
107
- const blockName = toSnakeCase(field.name);
108
- const blockTableName = `${slug}_${blockName}_${block.slug}`.toLowerCase();
109
- upCode += ` await db.schema.createTable('${blockTableName}')
110
- `;
111
- upCode += ` .addColumn('id', 'text', (col) => col.primaryKey())
112
- `;
113
- upCode += ` .addColumn('_parent_id', 'text', (col) => col.notNull())
114
- `;
115
- upCode += ` .addColumn('_order', 'integer', (col) => col.defaultTo(0))
116
- `;
117
- upCode += ` .addColumn('block_type', 'text', (col) => col.notNull())
118
- `;
119
- const blockFlattened = flattenFields(block.fields);
120
- for (const bField of blockFlattened) {
121
- const bColName = toSnakeCase(bField.name);
122
- if (bColName === "id")
123
- continue;
124
- const bColType = mapType(bField);
125
- const bConstraints = [];
126
- if (bField.required)
127
- bConstraints.push("notNull()");
128
- if (bField.unique)
129
- bConstraints.push("unique()");
130
- if (bField.defaultValue !== undefined) {
131
- let bVal = bField.defaultValue;
132
- if (typeof bVal === "string")
133
- bVal = `'${bVal}'`;
134
- else if (typeof bVal === "boolean")
135
- bVal = dialect === "sqlite" || dialect === "d1" ? bVal ? 1 : 0 : bVal;
136
- bConstraints.push(`defaultTo(${bVal})`);
137
- }
138
- const bConstraintChain = bConstraints.length > 0 ? `.${bConstraints.join(".")}` : "";
139
- upCode += ` .addColumn('${bColName}', '${bColType}', (col) => col${bConstraintChain})
140
- `;
141
- }
142
- upCode += ` .addColumn('created_at', '${tsType}', (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
143
- `;
144
- upCode += ` .addColumn('updated_at', '${tsType}', (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
145
- `;
146
- upCode += ` .execute();
147
-
148
- `;
149
- downCode = ` await db.schema.dropTable('${blockTableName}').execute();
150
- ` + downCode;
151
- }
152
- }
153
- }
154
- }
155
- return `import { type OpacaMigrationDb, sql } from 'opacacms/db';
156
-
157
- export async function up(db: OpacaMigrationDb): Promise<void> {
158
- ${upCode}}
159
-
160
- export async function down(db: OpacaMigrationDb): Promise<void> {
161
- ${downCode}}
162
- `;
163
- }
164
- function generateSQLCode(collections, globals = []) {
165
- const allSchemas = [...collections, ...globals];
166
- let sql = `-- OpacaCMS Auto-generated SQL Migration
167
-
168
- `;
169
- for (const collection of allSchemas) {
170
- const slug = collection.slug;
171
- const flattenedFields = flattenFields(collection.fields);
172
- sql += `CREATE TABLE IF NOT EXISTS "${slug}" (
173
- `;
174
- sql += ` "id" TEXT PRIMARY KEY`;
175
- for (const field of flattenedFields) {
176
- if (field.type === "relationship" && "hasMany" in field && field.hasMany)
177
- continue;
178
- const colName = toSnakeCase(field.name);
179
- if (colName === "id")
180
- continue;
181
- const colType = mapFieldToSQLiteType(field);
182
- sql += `,
183
- "${colName}" ${colType.toUpperCase()}`;
184
- if (field.required)
185
- sql += " NOT NULL";
186
- if (field.unique)
187
- sql += " UNIQUE";
188
- if (field.defaultValue !== undefined) {
189
- let val = field.defaultValue;
190
- if (typeof val === "string")
191
- val = `'${val}'`;
192
- else if (typeof val === "boolean")
193
- val = val ? 1 : 0;
194
- sql += ` DEFAULT ${val}`;
195
- }
196
- }
197
- const ts = collection.timestamps !== false;
198
- if (ts) {
199
- const config = typeof collection.timestamps === "object" ? collection.timestamps : {};
200
- const createdField = toSnakeCase(config.createdAt || "createdAt");
201
- const updatedField = toSnakeCase(config.updatedAt || "updatedAt");
202
- sql += `,
203
- "${createdField}" TEXT DEFAULT CURRENT_TIMESTAMP`;
204
- sql += `,
205
- "${updatedField}" TEXT DEFAULT CURRENT_TIMESTAMP`;
206
- }
207
- sql += `
208
- );
209
-
210
- `;
211
- if ("versions" in collection && collection.versions) {
212
- const versionsTable = `${toSnakeCase(slug)}_versions`;
213
- sql += `CREATE TABLE IF NOT EXISTS "${versionsTable}" (
214
- `;
215
- sql += ` "id" TEXT PRIMARY KEY,
216
- `;
217
- sql += ` "_parent_id" TEXT NOT NULL,
218
- `;
219
- sql += ` "_version_data" TEXT NOT NULL,
220
- `;
221
- sql += ` "_status" TEXT,
222
- `;
223
- sql += ` "created_at" TEXT DEFAULT CURRENT_TIMESTAMP
224
- `;
225
- sql += `);
226
-
227
- `;
228
- }
229
- for (const field of flattenedFields) {
230
- if (field.type === "relationship" && "hasMany" in field && field.hasMany) {
231
- const colName = toSnakeCase(field.name);
232
- const joinTableName = `${slug}_${colName}_relations`.toLowerCase();
233
- sql += `CREATE TABLE IF NOT EXISTS "${joinTableName}" (
234
- `;
235
- sql += ` "id" TEXT PRIMARY KEY,
236
- `;
237
- sql += ` "source_id" TEXT NOT NULL,
238
- `;
239
- sql += ` "target_id" TEXT NOT NULL,
240
- `;
241
- sql += ` "order" INTEGER DEFAULT 0
242
- `;
243
- sql += `);
244
-
245
- `;
246
- }
247
- }
248
- for (const field of collection.fields) {
249
- if (field.type === "blocks" && field.blocks && Array.isArray(field.blocks)) {
250
- for (const block of field.blocks) {
251
- const blockName = toSnakeCase(field.name);
252
- const blockTableName = `${slug}_${blockName}_${block.slug}`.toLowerCase();
253
- sql += `CREATE TABLE IF NOT EXISTS "${blockTableName}" (
254
- `;
255
- sql += ` "id" TEXT PRIMARY KEY,
256
- `;
257
- sql += ` "_parent_id" TEXT NOT NULL,
258
- `;
259
- sql += ` "_order" INTEGER DEFAULT 0,
260
- `;
261
- sql += ` "block_type" TEXT NOT NULL`;
262
- const blockFlattened = flattenFields(block.fields);
263
- for (const bField of blockFlattened) {
264
- const bColName = toSnakeCase(bField.name);
265
- if (bColName === "id")
266
- continue;
267
- const bColType = mapFieldToSQLiteType(bField);
268
- sql += `,
269
- "${bColName}" ${bColType.toUpperCase()}`;
270
- }
271
- sql += `,
272
- "created_at" TEXT DEFAULT CURRENT_TIMESTAMP,
273
- `;
274
- sql += ` "updated_at" TEXT DEFAULT CURRENT_TIMESTAMP
275
- `;
276
- sql += `);
277
-
278
- `;
279
- }
280
- }
281
- }
282
- }
283
- return sql;
284
- }
285
- export {
286
- generateSQLCode,
287
- generateMigrationCode
288
- };