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
@@ -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,29 @@
1
1
  {
2
2
  "name": "opacacms",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
+ "license": "MIT",
5
+ "description": "OpacaCMS: A lightweight, type-safe, and developer-first Headless CMS for the edge and beyond.",
6
+ "keywords": [
7
+ "cms",
8
+ "headless-cms",
9
+ "typescript",
10
+ "hono",
11
+ "sqlite",
12
+ "d1",
13
+ "cloudflare-workers",
14
+ "admin-panel",
15
+ "edge"
16
+ ],
17
+ "author": "fhorray",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/fhorray/opacacms.git",
21
+ "directory": "packages/opacacms"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/fhorray/opacacms/issues"
25
+ },
26
+ "homepage": "https://opaca.francy.dev",
4
27
  "scripts": {
5
28
  "build": "bun run ../../scripts/build.ts && tsc --emitDeclarationOnly",
6
29
  "build:publish": "bun run ../../scripts/build.ts --publish && tsc --emitDeclarationOnly",
@@ -23,8 +46,9 @@
23
46
  },
24
47
  "module": "dist/index.js",
25
48
  "type": "module",
49
+ "sideEffects": false,
26
50
  "bin": {
27
- "opacacms": "./dist/cli/index.js"
51
+ "opacacms": "./src/cli/index.ts"
28
52
  },
29
53
  "exports": {
30
54
  "./styles.css": {
@@ -114,9 +138,12 @@
114
138
  "devDependencies": {
115
139
  "@aws-sdk/client-s3": "^3.1009.0",
116
140
  "@aws-sdk/s3-request-presigner": "^3.1009.0",
141
+ "@better-auth/api-key": "^1.5.5",
142
+ "@better-auth/core": "^1.5.5",
117
143
  "@cloudflare/workers-types": "^4.20260313.1",
118
144
  "@faker-js/faker": "^10.3.0",
119
145
  "@happy-dom/global-registrator": "^20.8.4",
146
+ "@hono-rate-limiter/cloudflare": "^0.2.2",
120
147
  "@hono/bun-transpiler": "^0.2.1",
121
148
  "@hono/esbuild-transpiler": "^0.1.4",
122
149
  "@hono/node-server": "^1.19.11",
@@ -145,11 +172,13 @@
145
172
  "@types/react-dom": "^19.2.3",
146
173
  "better-sqlite3": "^12.6.2",
147
174
  "bun-plugin-scss": "^1.0.0",
175
+ "class-variance-authority": "^0.7.1",
148
176
  "dotenv": "^17.3.1",
149
- "drizzle-kit": "^0.31.9",
150
177
  "esbuild": "^0.27.4",
151
178
  "esbuild-wasm": "^0.27.4",
152
179
  "happy-dom": "^20.8.4",
180
+ "hono": "^4.12.7",
181
+ "ky": "^1.14.3",
153
182
  "kysely-bun-sqlite": "^0.4.0",
154
183
  "kysely-d1": "^0.4.0",
155
184
  "kysely-postgres-js": "^3.0.0",
@@ -157,24 +186,34 @@
157
186
  "lucide-react": "^0.577.0",
158
187
  "nanostores": "^1.1.1",
159
188
  "postgres": "^3.4.8",
189
+ "radix-ui": "^1.4.3",
160
190
  "sass": "^1.98.0",
161
- "@better-auth/api-key": "^1.5.5",
162
- "@better-auth/core": "^1.5.5"
191
+ "zod": "^4.3.6"
163
192
  },
164
193
  "peerDependencies": {
194
+ "hono": "^4.0.0",
165
195
  "react": "^19.2.4",
166
196
  "react-dom": "^19.2.4",
167
- "typescript": "^5.9.3"
197
+ "typescript": "^5.9.3",
198
+ "zod": "^4.3.6"
199
+ },
200
+ "peerDependenciesMeta": {
201
+ "@hono/graphql-server": {
202
+ "optional": true
203
+ },
204
+ "graphql": {
205
+ "optional": true
206
+ },
207
+ "hono-rate-limiter": {
208
+ "optional": true
209
+ }
168
210
  },
169
211
  "dependencies": {
170
- "@hono-rate-limiter/cloudflare": "^0.2.2",
212
+ "@hono/graphql-server": "^0.7.0",
171
213
  "better-auth": "^1.5.5",
172
- "drizzle-orm": "^0.45.1",
173
- "hono": "^4.12.7",
214
+ "graphql": "^16.13.1",
174
215
  "hono-rate-limiter": "^0.5.3",
175
- "ky": "^1.14.3",
176
216
  "kysely": "^0.28.11",
177
- "zod": "^4.3.6",
178
217
  "zod-to-json-schema": "^3.25.1"
179
218
  },
180
219
  "files": [
@@ -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
- };