@dyrected/core 2.6.3 → 2.7.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 (354) hide show
  1. package/dist/__tests__/access-features.test.d.ts +2 -0
  2. package/dist/__tests__/access-features.test.d.ts.map +1 -0
  3. package/dist/__tests__/access-features.test.js +159 -0
  4. package/dist/__tests__/access-features.test.js.map +1 -0
  5. package/dist/__tests__/access-parity.test.d.ts +2 -0
  6. package/dist/__tests__/access-parity.test.d.ts.map +1 -0
  7. package/dist/__tests__/access-parity.test.js +338 -0
  8. package/dist/__tests__/access-parity.test.js.map +1 -0
  9. package/dist/__tests__/access.test.d.ts +2 -0
  10. package/dist/__tests__/access.test.d.ts.map +1 -0
  11. package/dist/__tests__/access.test.js +137 -0
  12. package/dist/__tests__/access.test.js.map +1 -0
  13. package/dist/__tests__/admin-auth.test.d.ts +2 -0
  14. package/dist/__tests__/admin-auth.test.d.ts.map +1 -0
  15. package/dist/__tests__/admin-auth.test.js +440 -0
  16. package/dist/__tests__/admin-auth.test.js.map +1 -0
  17. package/dist/__tests__/app.test.d.ts +2 -0
  18. package/dist/__tests__/app.test.d.ts.map +1 -0
  19. package/dist/__tests__/app.test.js +112 -0
  20. package/dist/__tests__/app.test.js.map +1 -0
  21. package/dist/__tests__/audit.test.d.ts +2 -0
  22. package/dist/__tests__/audit.test.d.ts.map +1 -0
  23. package/dist/__tests__/audit.test.js +125 -0
  24. package/dist/__tests__/audit.test.js.map +1 -0
  25. package/dist/__tests__/auth-hydration.test.d.ts +2 -0
  26. package/dist/__tests__/auth-hydration.test.d.ts.map +1 -0
  27. package/dist/__tests__/auth-hydration.test.js +90 -0
  28. package/dist/__tests__/auth-hydration.test.js.map +1 -0
  29. package/dist/__tests__/auth-invite.test.d.ts +2 -0
  30. package/dist/__tests__/auth-invite.test.d.ts.map +1 -0
  31. package/dist/__tests__/auth-invite.test.js +152 -0
  32. package/dist/__tests__/auth-invite.test.js.map +1 -0
  33. package/dist/__tests__/auth-lockout.test.d.ts +2 -0
  34. package/dist/__tests__/auth-lockout.test.d.ts.map +1 -0
  35. package/dist/__tests__/auth-lockout.test.js +154 -0
  36. package/dist/__tests__/auth-lockout.test.js.map +1 -0
  37. package/dist/__tests__/auth-sessions.test.d.ts +2 -0
  38. package/dist/__tests__/auth-sessions.test.d.ts.map +1 -0
  39. package/dist/__tests__/auth-sessions.test.js +118 -0
  40. package/dist/__tests__/auth-sessions.test.js.map +1 -0
  41. package/dist/__tests__/config.test.d.ts +2 -0
  42. package/dist/__tests__/config.test.d.ts.map +1 -0
  43. package/dist/__tests__/config.test.js +402 -0
  44. package/dist/__tests__/config.test.js.map +1 -0
  45. package/dist/__tests__/define-field.test.d.ts +2 -0
  46. package/dist/__tests__/define-field.test.d.ts.map +1 -0
  47. package/dist/__tests__/define-field.test.js +121 -0
  48. package/dist/__tests__/define-field.test.js.map +1 -0
  49. package/dist/__tests__/deleteMany.test.d.ts +2 -0
  50. package/dist/__tests__/deleteMany.test.d.ts.map +1 -0
  51. package/dist/__tests__/deleteMany.test.js +75 -0
  52. package/dist/__tests__/deleteMany.test.js.map +1 -0
  53. package/dist/__tests__/depth.test.d.ts +2 -0
  54. package/dist/__tests__/depth.test.d.ts.map +1 -0
  55. package/dist/__tests__/depth.test.js +115 -0
  56. package/dist/__tests__/depth.test.js.map +1 -0
  57. package/dist/__tests__/dynamic-options.test.d.ts +2 -0
  58. package/dist/__tests__/dynamic-options.test.d.ts.map +1 -0
  59. package/dist/__tests__/dynamic-options.test.js +168 -0
  60. package/dist/__tests__/dynamic-options.test.js.map +1 -0
  61. package/dist/__tests__/email.service.test.d.ts +2 -0
  62. package/dist/__tests__/email.service.test.d.ts.map +1 -0
  63. package/dist/__tests__/email.service.test.js +52 -0
  64. package/dist/__tests__/email.service.test.js.map +1 -0
  65. package/dist/__tests__/field-inference.test-types.d.ts +161 -0
  66. package/dist/__tests__/field-inference.test-types.d.ts.map +1 -0
  67. package/dist/__tests__/field-inference.test-types.js +87 -0
  68. package/dist/__tests__/field-inference.test-types.js.map +1 -0
  69. package/dist/__tests__/fixtures/editorial-workflow.d.ts +14 -0
  70. package/dist/__tests__/fixtures/editorial-workflow.d.ts.map +1 -0
  71. package/dist/__tests__/fixtures/editorial-workflow.js +12 -0
  72. package/dist/__tests__/fixtures/editorial-workflow.js.map +1 -0
  73. package/dist/__tests__/hooks.test.d.ts +2 -0
  74. package/dist/__tests__/hooks.test.d.ts.map +1 -0
  75. package/dist/__tests__/hooks.test.js +320 -0
  76. package/dist/__tests__/hooks.test.js.map +1 -0
  77. package/dist/__tests__/mocks.d.ts +75 -0
  78. package/dist/__tests__/mocks.d.ts.map +1 -0
  79. package/dist/__tests__/mocks.js +170 -0
  80. package/dist/__tests__/mocks.js.map +1 -0
  81. package/dist/__tests__/observability.test.d.ts +2 -0
  82. package/dist/__tests__/observability.test.d.ts.map +1 -0
  83. package/dist/__tests__/observability.test.js +233 -0
  84. package/dist/__tests__/observability.test.js.map +1 -0
  85. package/dist/__tests__/openapi.test.d.ts +2 -0
  86. package/dist/__tests__/openapi.test.d.ts.map +1 -0
  87. package/dist/__tests__/openapi.test.js +105 -0
  88. package/dist/__tests__/openapi.test.js.map +1 -0
  89. package/dist/__tests__/parse-sort.test.d.ts +2 -0
  90. package/dist/__tests__/parse-sort.test.d.ts.map +1 -0
  91. package/dist/__tests__/parse-sort.test.js +24 -0
  92. package/dist/__tests__/parse-sort.test.js.map +1 -0
  93. package/dist/__tests__/preferences.test.d.ts +2 -0
  94. package/dist/__tests__/preferences.test.d.ts.map +1 -0
  95. package/dist/__tests__/preferences.test.js +168 -0
  96. package/dist/__tests__/preferences.test.js.map +1 -0
  97. package/dist/__tests__/router.test.d.ts +2 -0
  98. package/dist/__tests__/router.test.d.ts.map +1 -0
  99. package/dist/__tests__/router.test.js +70 -0
  100. package/dist/__tests__/router.test.js.map +1 -0
  101. package/dist/__tests__/search.test.d.ts +2 -0
  102. package/dist/__tests__/search.test.d.ts.map +1 -0
  103. package/dist/__tests__/search.test.js +174 -0
  104. package/dist/__tests__/search.test.js.map +1 -0
  105. package/dist/__tests__/upload-validation.test.d.ts +2 -0
  106. package/dist/__tests__/upload-validation.test.d.ts.map +1 -0
  107. package/dist/__tests__/upload-validation.test.js +41 -0
  108. package/dist/__tests__/upload-validation.test.js.map +1 -0
  109. package/dist/__tests__/where.test.d.ts +2 -0
  110. package/dist/__tests__/where.test.d.ts.map +1 -0
  111. package/dist/__tests__/where.test.js +105 -0
  112. package/dist/__tests__/where.test.js.map +1 -0
  113. package/dist/__tests__/workflow.test.d.ts +2 -0
  114. package/dist/__tests__/workflow.test.d.ts.map +1 -0
  115. package/dist/__tests__/workflow.test.js +684 -0
  116. package/dist/__tests__/workflow.test.js.map +1 -0
  117. package/dist/app-config-C9VJ6rTv.d.cts +2092 -0
  118. package/dist/app-config-C9VJ6rTv.d.ts +2092 -0
  119. package/dist/app-config-CYV70eoF.d.cts +2100 -0
  120. package/dist/app-config-CYV70eoF.d.ts +2100 -0
  121. package/dist/app-config-ChsrBB7p.d.cts +2100 -0
  122. package/dist/app-config-ChsrBB7p.d.ts +2100 -0
  123. package/dist/app.d.ts +35 -0
  124. package/dist/app.d.ts.map +1 -0
  125. package/dist/app.js +164 -0
  126. package/dist/app.js.map +1 -0
  127. package/dist/auth/access.d.ts +5 -0
  128. package/dist/auth/access.d.ts.map +1 -0
  129. package/dist/auth/access.js +59 -0
  130. package/dist/auth/access.js.map +1 -0
  131. package/dist/auth/jexl.d.ts +13 -0
  132. package/dist/auth/jexl.d.ts.map +1 -0
  133. package/dist/auth/jexl.js +30 -0
  134. package/dist/auth/jexl.js.map +1 -0
  135. package/dist/auth/lockout.d.ts +11 -0
  136. package/dist/auth/lockout.d.ts.map +1 -0
  137. package/dist/auth/lockout.js +37 -0
  138. package/dist/auth/lockout.js.map +1 -0
  139. package/dist/auth/password.d.ts +10 -0
  140. package/dist/auth/password.d.ts.map +1 -0
  141. package/dist/auth/password.js +28 -0
  142. package/dist/auth/password.js.map +1 -0
  143. package/dist/auth/sessions.d.ts +39 -0
  144. package/dist/auth/sessions.d.ts.map +1 -0
  145. package/dist/auth/sessions.js +126 -0
  146. package/dist/auth/sessions.js.map +1 -0
  147. package/dist/auth/token.d.ts +23 -0
  148. package/dist/auth/token.d.ts.map +1 -0
  149. package/dist/auth/token.js +40 -0
  150. package/dist/auth/token.js.map +1 -0
  151. package/dist/chunk-23NM4LDU.js +2883 -0
  152. package/dist/chunk-2TKDCCIR.js +2622 -0
  153. package/dist/chunk-3SFITPX4.js +2628 -0
  154. package/dist/chunk-CQVTQ67D.js +2632 -0
  155. package/dist/chunk-ELHRRPKX.js +2863 -0
  156. package/dist/chunk-HRLSFTLR.js +3061 -0
  157. package/dist/chunk-QTIAQBNH.js +3097 -0
  158. package/dist/chunk-T2DGC4MD.js +3008 -0
  159. package/dist/chunk-TLU77TLE.js +3203 -0
  160. package/dist/chunk-YEBCT77K.js +3269 -0
  161. package/dist/chunk-YXEKNN5X.js +3085 -0
  162. package/dist/controllers/admin-auth.controller.d.ts +71 -0
  163. package/dist/controllers/admin-auth.controller.d.ts.map +1 -0
  164. package/dist/controllers/admin-auth.controller.js +450 -0
  165. package/dist/controllers/admin-auth.controller.js.map +1 -0
  166. package/dist/controllers/audit.controller.d.ts +42 -0
  167. package/dist/controllers/audit.controller.d.ts.map +1 -0
  168. package/dist/controllers/audit.controller.js +149 -0
  169. package/dist/controllers/audit.controller.js.map +1 -0
  170. package/dist/controllers/auth.controller.d.ts +143 -0
  171. package/dist/controllers/auth.controller.d.ts.map +1 -0
  172. package/dist/controllers/auth.controller.js +572 -0
  173. package/dist/controllers/auth.controller.js.map +1 -0
  174. package/dist/controllers/collection.controller.d.ts +170 -0
  175. package/dist/controllers/collection.controller.d.ts.map +1 -0
  176. package/dist/controllers/collection.controller.js +1033 -0
  177. package/dist/controllers/collection.controller.js.map +1 -0
  178. package/dist/controllers/global.controller.d.ts +20 -0
  179. package/dist/controllers/global.controller.d.ts.map +1 -0
  180. package/dist/controllers/global.controller.js +182 -0
  181. package/dist/controllers/global.controller.js.map +1 -0
  182. package/dist/controllers/media.controller.d.ts +39 -0
  183. package/dist/controllers/media.controller.d.ts.map +1 -0
  184. package/dist/controllers/media.controller.js +178 -0
  185. package/dist/controllers/media.controller.js.map +1 -0
  186. package/dist/controllers/preview.controller.d.ts +37 -0
  187. package/dist/controllers/preview.controller.d.ts.map +1 -0
  188. package/dist/controllers/preview.controller.js +48 -0
  189. package/dist/controllers/preview.controller.js.map +1 -0
  190. package/dist/index.cjs +707 -27
  191. package/dist/index.d.cts +77 -5
  192. package/dist/index.d.ts +77 -5
  193. package/dist/index.d.ts.map +1 -0
  194. package/dist/index.js +37 -1
  195. package/dist/index.js.map +1 -0
  196. package/dist/middleware/auth.d.ts +19 -0
  197. package/dist/middleware/auth.d.ts.map +1 -0
  198. package/dist/middleware/auth.js +157 -0
  199. package/dist/middleware/auth.js.map +1 -0
  200. package/dist/middleware/rate-limit.d.ts +9 -0
  201. package/dist/middleware/rate-limit.d.ts.map +1 -0
  202. package/dist/middleware/rate-limit.js +75 -0
  203. package/dist/middleware/rate-limit.js.map +1 -0
  204. package/dist/network.d.ts +6 -0
  205. package/dist/network.d.ts.map +1 -0
  206. package/dist/network.js +70 -0
  207. package/dist/network.js.map +1 -0
  208. package/dist/observability.d.ts +113 -0
  209. package/dist/observability.d.ts.map +1 -0
  210. package/dist/observability.js +497 -0
  211. package/dist/observability.js.map +1 -0
  212. package/dist/router.d.ts +8 -0
  213. package/dist/router.d.ts.map +1 -0
  214. package/dist/router.js +704 -0
  215. package/dist/router.js.map +1 -0
  216. package/dist/server.cjs +861 -54
  217. package/dist/server.d.cts +9 -1
  218. package/dist/server.d.ts +9 -1
  219. package/dist/server.d.ts.map +1 -0
  220. package/dist/server.js +189 -26
  221. package/dist/server.js.map +1 -0
  222. package/dist/services/audit.service.d.ts +23 -0
  223. package/dist/services/audit.service.d.ts.map +1 -0
  224. package/dist/services/audit.service.js +39 -0
  225. package/dist/services/audit.service.js.map +1 -0
  226. package/dist/services/defaults.service.d.ts +8 -0
  227. package/dist/services/defaults.service.d.ts.map +1 -0
  228. package/dist/services/defaults.service.js +55 -0
  229. package/dist/services/defaults.service.js.map +1 -0
  230. package/dist/services/email-template.d.ts +46 -0
  231. package/dist/services/email-template.d.ts.map +1 -0
  232. package/dist/services/email-template.js +88 -0
  233. package/dist/services/email-template.js.map +1 -0
  234. package/dist/services/email.service.d.ts +34 -0
  235. package/dist/services/email.service.d.ts.map +1 -0
  236. package/dist/services/email.service.js +129 -0
  237. package/dist/services/email.service.js.map +1 -0
  238. package/dist/services/media.service.d.ts +20 -0
  239. package/dist/services/media.service.d.ts.map +1 -0
  240. package/dist/services/media.service.js +49 -0
  241. package/dist/services/media.service.js.map +1 -0
  242. package/dist/services/population.service.d.ts +20 -0
  243. package/dist/services/population.service.d.ts.map +1 -0
  244. package/dist/services/population.service.js +224 -0
  245. package/dist/services/population.service.js.map +1 -0
  246. package/dist/types/access.d.ts +48 -0
  247. package/dist/types/access.d.ts.map +1 -0
  248. package/dist/types/access.js +2 -0
  249. package/dist/types/access.js.map +1 -0
  250. package/dist/types/adapters.d.ts +152 -0
  251. package/dist/types/adapters.d.ts.map +1 -0
  252. package/dist/types/adapters.js +2 -0
  253. package/dist/types/adapters.js.map +1 -0
  254. package/dist/types/admin-auth.d.ts +132 -0
  255. package/dist/types/admin-auth.d.ts.map +1 -0
  256. package/dist/types/admin-auth.js +2 -0
  257. package/dist/types/admin-auth.js.map +1 -0
  258. package/dist/types/admin.d.ts +86 -0
  259. package/dist/types/admin.d.ts.map +1 -0
  260. package/dist/types/admin.js +2 -0
  261. package/dist/types/admin.js.map +1 -0
  262. package/dist/types/app-config.d.ts +279 -0
  263. package/dist/types/app-config.d.ts.map +1 -0
  264. package/dist/types/app-config.js +2 -0
  265. package/dist/types/app-config.js.map +1 -0
  266. package/dist/types/documents.d.ts +65 -0
  267. package/dist/types/documents.d.ts.map +1 -0
  268. package/dist/types/documents.js +2 -0
  269. package/dist/types/documents.js.map +1 -0
  270. package/dist/types/hooks.d.ts +122 -0
  271. package/dist/types/hooks.d.ts.map +1 -0
  272. package/dist/types/hooks.js +2 -0
  273. package/dist/types/hooks.js.map +1 -0
  274. package/dist/types/index.d.ts +13 -0
  275. package/dist/types/index.d.ts.map +1 -0
  276. package/dist/types/index.js +13 -0
  277. package/dist/types/index.js.map +1 -0
  278. package/dist/types/request.d.ts +42 -0
  279. package/dist/types/request.d.ts.map +1 -0
  280. package/dist/types/request.js +2 -0
  281. package/dist/types/request.js.map +1 -0
  282. package/dist/types/schema-config.d.ts +443 -0
  283. package/dist/types/schema-config.d.ts.map +1 -0
  284. package/dist/types/schema-config.js +2 -0
  285. package/dist/types/schema-config.js.map +1 -0
  286. package/dist/types/schema-core.d.ts +628 -0
  287. package/dist/types/schema-core.d.ts.map +1 -0
  288. package/dist/types/schema-core.js +2 -0
  289. package/dist/types/schema-core.js.map +1 -0
  290. package/dist/types/schema-inference.d.ts +73 -0
  291. package/dist/types/schema-inference.d.ts.map +1 -0
  292. package/dist/types/schema-inference.js +2 -0
  293. package/dist/types/schema-inference.js.map +1 -0
  294. package/dist/types/workflows.d.ts +121 -0
  295. package/dist/types/workflows.d.ts.map +1 -0
  296. package/dist/types/workflows.js +7 -0
  297. package/dist/types/workflows.js.map +1 -0
  298. package/dist/utils/access-control.d.ts +35 -0
  299. package/dist/utils/access-control.d.ts.map +1 -0
  300. package/dist/utils/access-control.js +161 -0
  301. package/dist/utils/access-control.js.map +1 -0
  302. package/dist/utils/admin-auth.d.ts +5 -0
  303. package/dist/utils/admin-auth.d.ts.map +1 -0
  304. package/dist/utils/admin-auth.js +39 -0
  305. package/dist/utils/admin-auth.js.map +1 -0
  306. package/dist/utils/block-references.d.ts +10 -0
  307. package/dist/utils/block-references.d.ts.map +1 -0
  308. package/dist/utils/block-references.js +100 -0
  309. package/dist/utils/block-references.js.map +1 -0
  310. package/dist/utils/collection-search.d.ts +11 -0
  311. package/dist/utils/collection-search.d.ts.map +1 -0
  312. package/dist/utils/collection-search.js +204 -0
  313. package/dist/utils/collection-search.js.map +1 -0
  314. package/dist/utils/config.d.ts +8 -0
  315. package/dist/utils/config.d.ts.map +1 -0
  316. package/dist/utils/config.js +336 -0
  317. package/dist/utils/config.js.map +1 -0
  318. package/dist/utils/hooks.d.ts +41 -0
  319. package/dist/utils/hooks.d.ts.map +1 -0
  320. package/dist/utils/hooks.js +177 -0
  321. package/dist/utils/hooks.js.map +1 -0
  322. package/dist/utils/openapi.d.ts +6 -0
  323. package/dist/utils/openapi.d.ts.map +1 -0
  324. package/dist/utils/openapi.js +981 -0
  325. package/dist/utils/openapi.js.map +1 -0
  326. package/dist/utils/parse-sort.d.ts +43 -0
  327. package/dist/utils/parse-sort.d.ts.map +1 -0
  328. package/dist/utils/parse-sort.js +70 -0
  329. package/dist/utils/parse-sort.js.map +1 -0
  330. package/dist/utils/parse-where.d.ts +69 -0
  331. package/dist/utils/parse-where.d.ts.map +1 -0
  332. package/dist/utils/parse-where.js +202 -0
  333. package/dist/utils/parse-where.js.map +1 -0
  334. package/dist/utils/readonly-db.d.ts +9 -0
  335. package/dist/utils/readonly-db.d.ts.map +1 -0
  336. package/dist/utils/readonly-db.js +21 -0
  337. package/dist/utils/readonly-db.js.map +1 -0
  338. package/dist/utils/swagger.d.ts +11 -0
  339. package/dist/utils/swagger.d.ts.map +1 -0
  340. package/dist/utils/swagger.js +62 -0
  341. package/dist/utils/swagger.js.map +1 -0
  342. package/dist/utils/upload-validation.d.ts +23 -0
  343. package/dist/utils/upload-validation.d.ts.map +1 -0
  344. package/dist/utils/upload-validation.js +46 -0
  345. package/dist/utils/upload-validation.js.map +1 -0
  346. package/dist/utils/where-sanitizer.d.ts +10 -0
  347. package/dist/utils/where-sanitizer.d.ts.map +1 -0
  348. package/dist/utils/where-sanitizer.js +67 -0
  349. package/dist/utils/where-sanitizer.js.map +1 -0
  350. package/dist/workflows.d.ts +86 -0
  351. package/dist/workflows.d.ts.map +1 -0
  352. package/dist/workflows.js +405 -0
  353. package/dist/workflows.js.map +1 -0
  354. package/package.json +1 -1
@@ -0,0 +1,981 @@
1
+ function getCollectionLabels(collection) {
2
+ return (collection.labels || { singular: collection.slug, plural: collection.slug });
3
+ }
4
+ function getCollectionTag(collection) {
5
+ return `Collection: ${getCollectionLabels(collection).plural}`;
6
+ }
7
+ function getGlobalTag(global) {
8
+ return `Global: ${global.label || global.slug}`;
9
+ }
10
+ /**
11
+ * Generate an OpenAPI 3.0 specification based on the Dyrected configuration.
12
+ */
13
+ export function generateOpenApi(config) {
14
+ const spec = {
15
+ openapi: "3.0.0",
16
+ info: {
17
+ title: "Dyrected API",
18
+ version: "1.0.0",
19
+ description: "Automatically generated OpenAPI specification for the Dyrected project.",
20
+ },
21
+ components: {
22
+ schemas: {
23
+ WorkflowMetadata: {
24
+ type: "object",
25
+ required: ["state", "revision"],
26
+ properties: {
27
+ state: { type: "string" },
28
+ revision: { type: "integer", minimum: 1 },
29
+ publishedRevision: { type: "integer", minimum: 1 },
30
+ publishedAt: { type: "string", format: "date-time" },
31
+ publishedBy: { type: "string" },
32
+ availableTransitions: { type: "array", items: { type: "string" } },
33
+ },
34
+ },
35
+ WorkflowTransitionRequest: {
36
+ type: "object",
37
+ properties: {
38
+ expectedRevision: { type: "integer", minimum: 1 },
39
+ comment: { type: "string" },
40
+ },
41
+ },
42
+ WorkflowHistoryEntry: {
43
+ type: "object",
44
+ required: [
45
+ "collection",
46
+ "documentId",
47
+ "transition",
48
+ "from",
49
+ "to",
50
+ "revision",
51
+ "createdAt",
52
+ ],
53
+ properties: {
54
+ id: { type: "string" },
55
+ collection: { type: "string" },
56
+ documentId: { type: "string" },
57
+ transition: { type: "string" },
58
+ from: { type: "string" },
59
+ to: { type: "string" },
60
+ revision: { type: "integer" },
61
+ comment: { type: "string", nullable: true },
62
+ actorId: { type: "string", nullable: true },
63
+ createdAt: { type: "string", format: "date-time" },
64
+ },
65
+ },
66
+ AuditEntry: {
67
+ type: "object",
68
+ required: ["collection", "operation", "timestamp"],
69
+ properties: {
70
+ id: { type: "string" },
71
+ collection: { type: "string" },
72
+ documentId: { type: "string", nullable: true },
73
+ operation: { type: "string" },
74
+ user: { type: "string", nullable: true },
75
+ timestamp: { type: "string", format: "date-time" },
76
+ changes: {
77
+ oneOf: [
78
+ { type: "string" },
79
+ { type: "object", additionalProperties: true },
80
+ { type: "null" },
81
+ ],
82
+ },
83
+ },
84
+ },
85
+ Error: {
86
+ type: "object",
87
+ properties: {
88
+ message: { type: "string" },
89
+ errors: {
90
+ type: "array",
91
+ items: { type: "object", additionalProperties: true },
92
+ },
93
+ },
94
+ },
95
+ AuthCredentials: {
96
+ type: "object",
97
+ required: ["email", "password"],
98
+ properties: {
99
+ email: { type: "string", format: "email" },
100
+ password: { type: "string" },
101
+ },
102
+ },
103
+ TokenResponse: {
104
+ type: "object",
105
+ properties: {
106
+ token: { type: "string" },
107
+ user: { type: "object", additionalProperties: true },
108
+ },
109
+ },
110
+ },
111
+ securitySchemes: {
112
+ ApiKeyAuth: {
113
+ type: "apiKey",
114
+ in: "header",
115
+ name: "x-api-key",
116
+ },
117
+ },
118
+ },
119
+ paths: {},
120
+ security: [{ ApiKeyAuth: [] }],
121
+ };
122
+ spec.paths["/api/schemas"] = {
123
+ get: {
124
+ tags: ["System"],
125
+ summary: "Get the serialized Dyrected schema",
126
+ security: [],
127
+ responses: {
128
+ 200: { description: "Collection and global schema definitions" },
129
+ },
130
+ },
131
+ };
132
+ spec.paths["/api/openapi.json"] = {
133
+ get: {
134
+ tags: ["System"],
135
+ summary: "Get the OpenAPI specification",
136
+ security: [],
137
+ responses: { 200: { description: "OpenAPI 3.0 document" } },
138
+ },
139
+ };
140
+ spec.paths["/api/docs"] = {
141
+ get: {
142
+ tags: ["System"],
143
+ summary: "Open interactive API documentation",
144
+ security: [],
145
+ responses: { 200: { description: "Swagger UI HTML" } },
146
+ },
147
+ };
148
+ spec.paths["/api/dyrected/options/{collection}/{field}"] = {
149
+ get: {
150
+ tags: ["System"],
151
+ summary: "Resolve dynamic field options",
152
+ parameters: [
153
+ {
154
+ name: "collection",
155
+ in: "path",
156
+ required: true,
157
+ schema: { type: "string" },
158
+ },
159
+ {
160
+ name: "field",
161
+ in: "path",
162
+ required: true,
163
+ schema: { type: "string" },
164
+ },
165
+ ],
166
+ responses: { 200: { description: "Resolved option items" } },
167
+ },
168
+ };
169
+ spec.paths["/api/preferences/{key}"] = {
170
+ get: {
171
+ tags: ["Preferences"],
172
+ summary: "Get an authenticated user preference",
173
+ parameters: [
174
+ { name: "key", in: "path", required: true, schema: { type: "string" } },
175
+ ],
176
+ responses: { 200: { description: "Preference value" } },
177
+ },
178
+ put: {
179
+ tags: ["Preferences"],
180
+ summary: "Set an authenticated user preference",
181
+ parameters: [
182
+ { name: "key", in: "path", required: true, schema: { type: "string" } },
183
+ ],
184
+ requestBody: {
185
+ required: true,
186
+ content: { "application/json": { schema: { type: "object" } } },
187
+ },
188
+ responses: { 200: { description: "Updated preference" } },
189
+ },
190
+ };
191
+ spec.paths["/api/preview-token"] = {
192
+ post: {
193
+ tags: ["Preview"],
194
+ summary: "Create a preview token",
195
+ responses: { 200: { description: "Short-lived preview token" } },
196
+ },
197
+ };
198
+ spec.paths["/api/preview-data"] = {
199
+ get: {
200
+ tags: ["Preview"],
201
+ summary: "Resolve preview data from a token",
202
+ security: [],
203
+ parameters: [
204
+ {
205
+ name: "token",
206
+ in: "query",
207
+ required: true,
208
+ schema: { type: "string" },
209
+ },
210
+ ],
211
+ responses: { 200: { description: "Preview document data" } },
212
+ },
213
+ };
214
+ spec.paths["/api/audit"] = {
215
+ get: {
216
+ tags: ["Audit"],
217
+ summary: "Get audit entries across all readable audited collections",
218
+ parameters: [
219
+ {
220
+ name: "limit",
221
+ in: "query",
222
+ schema: { type: "integer", default: 50, maximum: 100 },
223
+ },
224
+ { name: "page", in: "query", schema: { type: "integer", default: 1 } },
225
+ {
226
+ name: "where",
227
+ in: "query",
228
+ schema: { type: "string" },
229
+ description: "JSON filter",
230
+ },
231
+ {
232
+ name: "sort",
233
+ in: "query",
234
+ schema: { type: "string", default: "-timestamp" },
235
+ },
236
+ ],
237
+ responses: {
238
+ 200: {
239
+ description: "Audit entries",
240
+ content: {
241
+ "application/json": {
242
+ schema: {
243
+ type: "object",
244
+ properties: {
245
+ docs: {
246
+ type: "array",
247
+ items: { $ref: "#/components/schemas/AuditEntry" },
248
+ },
249
+ total: { type: "integer" },
250
+ limit: { type: "integer" },
251
+ page: { type: "integer" },
252
+ },
253
+ },
254
+ },
255
+ },
256
+ },
257
+ },
258
+ },
259
+ };
260
+ // 1. Generate Schemas for Collections
261
+ for (const collection of config.collections) {
262
+ spec.components.schemas[collection.slug] = collectionToSchema(collection);
263
+ }
264
+ // 2. Generate Schemas for Globals
265
+ for (const global of config.globals) {
266
+ spec.components.schemas[global.slug] = globalToSchema(global);
267
+ }
268
+ // 3. Generate Paths for Collections
269
+ for (const collection of config.collections) {
270
+ const slug = collection.slug;
271
+ const path = `/api/collections/${slug}`;
272
+ const labels = getCollectionLabels(collection);
273
+ const collectionTag = getCollectionTag(collection);
274
+ spec.paths[path] = {
275
+ get: {
276
+ tags: [collectionTag],
277
+ summary: `Find ${labels.plural}`,
278
+ parameters: [
279
+ {
280
+ name: "limit",
281
+ in: "query",
282
+ schema: { type: "integer", default: 10 },
283
+ },
284
+ {
285
+ name: "page",
286
+ in: "query",
287
+ schema: { type: "integer", default: 1 },
288
+ },
289
+ {
290
+ name: "where",
291
+ in: "query",
292
+ schema: { type: "string" },
293
+ description: "JSON filter",
294
+ },
295
+ {
296
+ name: "search",
297
+ in: "query",
298
+ schema: { type: "string" },
299
+ description: "Free-text search across configured searchable fields",
300
+ },
301
+ {
302
+ name: "sort",
303
+ in: "query",
304
+ schema: { type: "string" },
305
+ description: "Sort field (e.g. -createdAt)",
306
+ },
307
+ ],
308
+ responses: {
309
+ 200: {
310
+ description: "Success",
311
+ content: {
312
+ "application/json": {
313
+ schema: {
314
+ type: "object",
315
+ properties: {
316
+ docs: {
317
+ type: "array",
318
+ items: { $ref: `#/components/schemas/${slug}` },
319
+ },
320
+ total: { type: "integer" },
321
+ limit: { type: "integer" },
322
+ page: { type: "integer" },
323
+ },
324
+ },
325
+ },
326
+ },
327
+ },
328
+ },
329
+ },
330
+ post: {
331
+ tags: [collectionTag],
332
+ summary: `Create ${labels.singular}`,
333
+ requestBody: {
334
+ required: true,
335
+ content: {
336
+ "application/json": {
337
+ schema: { $ref: `#/components/schemas/${slug}` },
338
+ },
339
+ },
340
+ },
341
+ responses: {
342
+ 201: {
343
+ description: "Created",
344
+ content: {
345
+ "application/json": {
346
+ schema: { $ref: `#/components/schemas/${slug}` },
347
+ },
348
+ },
349
+ },
350
+ },
351
+ },
352
+ };
353
+ spec.paths[`${path}/{id}`] = {
354
+ get: {
355
+ tags: [collectionTag],
356
+ summary: `Get a single ${labels.singular}`,
357
+ parameters: [
358
+ {
359
+ name: "id",
360
+ in: "path",
361
+ required: true,
362
+ schema: { type: "string" },
363
+ },
364
+ ],
365
+ responses: {
366
+ 200: {
367
+ description: "Success",
368
+ content: {
369
+ "application/json": {
370
+ schema: { $ref: `#/components/schemas/${slug}` },
371
+ },
372
+ },
373
+ },
374
+ },
375
+ },
376
+ patch: {
377
+ tags: [collectionTag],
378
+ summary: `Update ${labels.singular}`,
379
+ parameters: [
380
+ {
381
+ name: "id",
382
+ in: "path",
383
+ required: true,
384
+ schema: { type: "string" },
385
+ },
386
+ ],
387
+ requestBody: {
388
+ required: true,
389
+ content: {
390
+ "application/json": {
391
+ schema: { $ref: `#/components/schemas/${slug}` },
392
+ },
393
+ },
394
+ },
395
+ responses: {
396
+ 200: {
397
+ description: "Updated",
398
+ content: {
399
+ "application/json": {
400
+ schema: { $ref: `#/components/schemas/${slug}` },
401
+ },
402
+ },
403
+ },
404
+ },
405
+ },
406
+ delete: {
407
+ tags: [collectionTag],
408
+ summary: `Delete ${labels.singular}`,
409
+ parameters: [
410
+ {
411
+ name: "id",
412
+ in: "path",
413
+ required: true,
414
+ schema: { type: "string" },
415
+ },
416
+ ],
417
+ responses: {
418
+ 204: { description: "Deleted" },
419
+ },
420
+ },
421
+ };
422
+ spec.paths[`${path}/delete-many`] = {
423
+ delete: {
424
+ tags: [collectionTag],
425
+ summary: `Delete multiple ${labels.plural}`,
426
+ requestBody: {
427
+ required: true,
428
+ content: {
429
+ "application/json": {
430
+ schema: {
431
+ type: "object",
432
+ required: ["ids"],
433
+ properties: {
434
+ ids: { type: "array", items: { type: "string" } },
435
+ },
436
+ },
437
+ },
438
+ },
439
+ },
440
+ responses: { 200: { description: "Deleted and failed document IDs" } },
441
+ },
442
+ };
443
+ if (collection.audit) {
444
+ spec.paths[`${path}/__audit`] = {
445
+ get: {
446
+ tags: [collectionTag],
447
+ summary: `Get ${labels.singular} audit entries`,
448
+ parameters: [
449
+ {
450
+ name: "limit",
451
+ in: "query",
452
+ schema: { type: "integer", default: 50, maximum: 100 },
453
+ },
454
+ {
455
+ name: "page",
456
+ in: "query",
457
+ schema: { type: "integer", default: 1 },
458
+ },
459
+ {
460
+ name: "where",
461
+ in: "query",
462
+ schema: { type: "string" },
463
+ description: "JSON filter",
464
+ },
465
+ {
466
+ name: "sort",
467
+ in: "query",
468
+ schema: { type: "string", default: "-timestamp" },
469
+ },
470
+ ],
471
+ responses: {
472
+ 200: {
473
+ description: "Audit entries for this collection",
474
+ content: {
475
+ "application/json": {
476
+ schema: {
477
+ type: "object",
478
+ properties: {
479
+ docs: {
480
+ type: "array",
481
+ items: { $ref: "#/components/schemas/AuditEntry" },
482
+ },
483
+ total: { type: "integer" },
484
+ limit: { type: "integer" },
485
+ page: { type: "integer" },
486
+ },
487
+ },
488
+ },
489
+ },
490
+ },
491
+ 403: { description: "Collection read access denied" },
492
+ 404: { description: "Audit is not enabled for this collection" },
493
+ },
494
+ },
495
+ };
496
+ }
497
+ if (collection.upload) {
498
+ spec.paths[`${path}/media`] = {
499
+ get: {
500
+ tags: [collectionTag],
501
+ summary: `List ${labels.plural}`,
502
+ responses: { 200: { description: "Paginated media documents" } },
503
+ },
504
+ post: {
505
+ tags: [collectionTag],
506
+ summary: `Upload ${labels.singular}`,
507
+ requestBody: {
508
+ required: true,
509
+ content: {
510
+ "multipart/form-data": {
511
+ schema: {
512
+ type: "object",
513
+ required: ["file"],
514
+ properties: { file: { type: "string", format: "binary" } },
515
+ },
516
+ },
517
+ },
518
+ },
519
+ responses: { 201: { description: "Uploaded media document" } },
520
+ },
521
+ };
522
+ spec.paths[`${path}/media/{filename}`] = {
523
+ get: {
524
+ tags: [collectionTag],
525
+ summary: `Serve ${labels.singular} bytes`,
526
+ parameters: [
527
+ {
528
+ name: "filename",
529
+ in: "path",
530
+ required: true,
531
+ schema: { type: "string" },
532
+ },
533
+ ],
534
+ security: [],
535
+ responses: {
536
+ 200: { description: "Stored file" },
537
+ 404: { description: "File not found" },
538
+ },
539
+ },
540
+ };
541
+ }
542
+ if (collection.auth) {
543
+ const publicAuthPost = (summary) => ({
544
+ tags: [collectionTag],
545
+ summary,
546
+ security: [],
547
+ requestBody: {
548
+ required: true,
549
+ content: {
550
+ "application/json": {
551
+ schema: { type: "object", additionalProperties: true },
552
+ },
553
+ },
554
+ },
555
+ responses: {
556
+ 200: { description: "Success" },
557
+ 400: { description: "Invalid request" },
558
+ },
559
+ });
560
+ spec.paths[`${path}/login`] = {
561
+ post: {
562
+ ...publicAuthPost(`Log in to ${labels.plural}`),
563
+ requestBody: {
564
+ required: true,
565
+ content: {
566
+ "application/json": {
567
+ schema: { $ref: "#/components/schemas/AuthCredentials" },
568
+ },
569
+ },
570
+ },
571
+ responses: {
572
+ 200: {
573
+ description: "Authenticated",
574
+ content: {
575
+ "application/json": {
576
+ schema: { $ref: "#/components/schemas/TokenResponse" },
577
+ },
578
+ },
579
+ },
580
+ 401: { description: "Invalid credentials" },
581
+ },
582
+ },
583
+ };
584
+ spec.paths[`${path}/logout`] = {
585
+ post: {
586
+ tags: [collectionTag],
587
+ summary: `Log out of ${labels.plural}`,
588
+ parameters: [
589
+ {
590
+ name: "allSessions",
591
+ in: "query",
592
+ required: false,
593
+ schema: { type: "boolean" },
594
+ description: "Revoke every active session for this account instead of only the current one.",
595
+ },
596
+ ],
597
+ responses: { 200: { description: "Logged out" } },
598
+ },
599
+ };
600
+ spec.paths[`${path}/init`] = {
601
+ get: {
602
+ tags: [collectionTag],
603
+ summary: `Get ${labels.plural} initialization state`,
604
+ security: [],
605
+ responses: { 200: { description: "Initialization state" } },
606
+ },
607
+ };
608
+ spec.paths[`${path}/first-user`] = {
609
+ post: publicAuthPost(`Register the first ${labels.singular}`),
610
+ };
611
+ spec.paths[`${path}/me`] = {
612
+ get: {
613
+ tags: [collectionTag],
614
+ summary: `Get the current ${labels.singular}`,
615
+ responses: { 200: { description: "Authenticated user" } },
616
+ },
617
+ };
618
+ spec.paths[`${path}/refresh-token`] = {
619
+ post: {
620
+ tags: [collectionTag],
621
+ summary: "Refresh an authentication token",
622
+ responses: {
623
+ 200: {
624
+ description: "Refreshed token for the current active session",
625
+ },
626
+ },
627
+ },
628
+ };
629
+ spec.paths[`${path}/forgot-password`] = {
630
+ post: publicAuthPost("Request a password reset"),
631
+ };
632
+ spec.paths[`${path}/reset-password`] = {
633
+ post: publicAuthPost("Reset a password"),
634
+ };
635
+ spec.paths[`${path}/invite`] = {
636
+ post: {
637
+ tags: [collectionTag],
638
+ summary: `Invite a ${labels.singular}`,
639
+ responses: { 200: { description: "Invitation sent" } },
640
+ },
641
+ };
642
+ spec.paths[`${path}/accept-invite`] = {
643
+ post: publicAuthPost("Accept an invitation"),
644
+ };
645
+ spec.paths[`${path}/{id}/change-password`] = {
646
+ post: {
647
+ tags: [collectionTag],
648
+ summary: `Change a ${labels.singular} password`,
649
+ parameters: [
650
+ {
651
+ name: "id",
652
+ in: "path",
653
+ required: true,
654
+ schema: { type: "string" },
655
+ },
656
+ ],
657
+ responses: {
658
+ 200: {
659
+ description: "Password changed and active sessions revoked",
660
+ },
661
+ },
662
+ },
663
+ };
664
+ }
665
+ if (collection.workflow) {
666
+ spec.paths[`${path}/{id}/transitions/{transition}`] = {
667
+ post: {
668
+ tags: [collectionTag],
669
+ summary: `Transition ${labels.singular} workflow`,
670
+ parameters: [
671
+ {
672
+ name: "id",
673
+ in: "path",
674
+ required: true,
675
+ schema: { type: "string" },
676
+ },
677
+ {
678
+ name: "transition",
679
+ in: "path",
680
+ required: true,
681
+ schema: {
682
+ type: "string",
683
+ enum: collection.workflow.transitions.map((item) => item.name),
684
+ },
685
+ },
686
+ ],
687
+ requestBody: {
688
+ content: {
689
+ "application/json": {
690
+ schema: {
691
+ $ref: "#/components/schemas/WorkflowTransitionRequest",
692
+ },
693
+ },
694
+ },
695
+ },
696
+ responses: {
697
+ 200: {
698
+ description: "Transition applied",
699
+ content: {
700
+ "application/json": {
701
+ schema: { $ref: `#/components/schemas/${slug}` },
702
+ },
703
+ },
704
+ },
705
+ 400: { description: "Required transition input is missing" },
706
+ 403: { description: "Transition capability denied" },
707
+ 409: { description: "Invalid state or stale revision" },
708
+ },
709
+ },
710
+ };
711
+ spec.paths[`${path}/{id}/workflow-history`] = {
712
+ get: {
713
+ tags: [collectionTag],
714
+ summary: `Get ${labels.singular} workflow history`,
715
+ parameters: [
716
+ {
717
+ name: "id",
718
+ in: "path",
719
+ required: true,
720
+ schema: { type: "string" },
721
+ },
722
+ {
723
+ name: "limit",
724
+ in: "query",
725
+ schema: { type: "integer", default: 50, maximum: 100 },
726
+ },
727
+ ],
728
+ responses: {
729
+ 200: {
730
+ description: "Workflow transition history",
731
+ content: {
732
+ "application/json": {
733
+ schema: {
734
+ type: "object",
735
+ properties: {
736
+ docs: {
737
+ type: "array",
738
+ items: {
739
+ $ref: "#/components/schemas/WorkflowHistoryEntry",
740
+ },
741
+ },
742
+ total: { type: "integer" },
743
+ limit: { type: "integer" },
744
+ page: { type: "integer" },
745
+ },
746
+ },
747
+ },
748
+ },
749
+ },
750
+ 403: { description: "Collection read access denied" },
751
+ },
752
+ },
753
+ };
754
+ }
755
+ }
756
+ // 4. Generate Paths for Globals
757
+ for (const global of config.globals) {
758
+ const slug = global.slug;
759
+ const path = `/api/globals/${slug}`;
760
+ const globalTag = getGlobalTag(global);
761
+ spec.paths[path] = {
762
+ get: {
763
+ tags: [globalTag],
764
+ summary: `Get ${global.label || slug}`,
765
+ responses: {
766
+ 200: {
767
+ description: "Success",
768
+ content: {
769
+ "application/json": {
770
+ schema: { $ref: `#/components/schemas/${slug}` },
771
+ },
772
+ },
773
+ },
774
+ },
775
+ },
776
+ patch: {
777
+ tags: [globalTag],
778
+ summary: `Update ${global.label || slug}`,
779
+ requestBody: {
780
+ required: true,
781
+ content: {
782
+ "application/json": {
783
+ schema: { $ref: `#/components/schemas/${slug}` },
784
+ },
785
+ },
786
+ },
787
+ responses: {
788
+ 200: {
789
+ description: "Updated",
790
+ content: {
791
+ "application/json": {
792
+ schema: { $ref: `#/components/schemas/${slug}` },
793
+ },
794
+ },
795
+ },
796
+ },
797
+ },
798
+ };
799
+ }
800
+ // 5. Generate the storage-backed file serving path.
801
+ if (config.storage) {
802
+ spec.paths["/api/media/{filename}"] = {
803
+ get: {
804
+ tags: ["Media"],
805
+ summary: "Serve a stored file",
806
+ security: [],
807
+ parameters: [
808
+ {
809
+ name: "filename",
810
+ in: "path",
811
+ required: true,
812
+ schema: { type: "string" },
813
+ },
814
+ ],
815
+ responses: {
816
+ 200: { description: "Stored file bytes" },
817
+ 404: { description: "File not found" },
818
+ },
819
+ },
820
+ };
821
+ }
822
+ return spec;
823
+ }
824
+ function collectionToSchema(collection) {
825
+ const { properties, required } = fieldsToProperties(collection.fields);
826
+ return {
827
+ type: "object",
828
+ properties: {
829
+ id: { type: "string" },
830
+ createdAt: { type: "string", format: "date-time" },
831
+ updatedAt: { type: "string", format: "date-time" },
832
+ ...(collection.workflow
833
+ ? { _workflow: { $ref: "#/components/schemas/WorkflowMetadata" } }
834
+ : {}),
835
+ ...properties,
836
+ },
837
+ required: ["id", ...required],
838
+ };
839
+ }
840
+ function globalToSchema(global) {
841
+ const { properties, required } = fieldsToProperties(global.fields);
842
+ return {
843
+ type: "object",
844
+ properties,
845
+ required,
846
+ };
847
+ }
848
+ function fieldsToProperties(fields) {
849
+ const props = {};
850
+ const required = [];
851
+ for (const field of fields) {
852
+ if (field.type === "row") {
853
+ const nested = fieldsToProperties(field.fields || []);
854
+ Object.assign(props, nested.properties);
855
+ required.push(...nested.required);
856
+ continue;
857
+ }
858
+ if (!field.name)
859
+ continue;
860
+ props[field.name] = fieldToSchema(field);
861
+ if (field.required) {
862
+ required.push(field.name);
863
+ }
864
+ }
865
+ return { properties: props, required };
866
+ }
867
+ function fieldToSchema(field) {
868
+ let schema;
869
+ switch (field.type) {
870
+ case "text":
871
+ case "textarea":
872
+ case "email":
873
+ schema = { type: "string" };
874
+ break;
875
+ case "url":
876
+ schema = {
877
+ oneOf: [
878
+ { type: "string" },
879
+ { type: "object", additionalProperties: true },
880
+ ],
881
+ };
882
+ break;
883
+ case "icon":
884
+ schema = { type: "string" };
885
+ break;
886
+ case "number":
887
+ schema = { type: "number" };
888
+ break;
889
+ case "boolean":
890
+ schema = { type: "boolean" };
891
+ break;
892
+ case "date":
893
+ schema = { type: "string", format: "date" };
894
+ break;
895
+ case "datetime":
896
+ schema = { type: "string", format: "date-time" };
897
+ break;
898
+ case "time":
899
+ schema = { type: "string", format: "time" };
900
+ break;
901
+ case "select":
902
+ case "radio":
903
+ schema = {
904
+ type: "string",
905
+ enum: Array.isArray(field.options)
906
+ ? field.options.map((o) => (typeof o === "string" ? o : o.value))
907
+ : undefined,
908
+ };
909
+ break;
910
+ case "multiSelect":
911
+ schema = {
912
+ type: "array",
913
+ items: {
914
+ type: "string",
915
+ enum: Array.isArray(field.options)
916
+ ? field.options.map((o) => (typeof o === "string" ? o : o.value))
917
+ : undefined,
918
+ },
919
+ };
920
+ break;
921
+ case "relationship":
922
+ case "image": {
923
+ const valueSchema = {
924
+ type: "string",
925
+ description: `ID of a ${field.relationTo} record`,
926
+ };
927
+ schema = field.hasMany
928
+ ? { type: "array", items: valueSchema }
929
+ : valueSchema;
930
+ break;
931
+ }
932
+ case "join":
933
+ schema = {
934
+ type: "array",
935
+ readOnly: true,
936
+ items: { type: "object", additionalProperties: true },
937
+ };
938
+ break;
939
+ case "object": {
940
+ const { properties, required } = fieldsToProperties(field.fields || []);
941
+ schema = { type: "object", properties, required };
942
+ break;
943
+ }
944
+ case "array": {
945
+ const { properties, required } = fieldsToProperties(field.fields || []);
946
+ schema = {
947
+ type: "array",
948
+ items: { type: "object", properties, required },
949
+ };
950
+ break;
951
+ }
952
+ case "json":
953
+ case "richText":
954
+ schema = { type: "object", additionalProperties: true };
955
+ break;
956
+ case "blocks":
957
+ schema = {
958
+ type: "array",
959
+ items: {
960
+ oneOf: field.blocks?.map((block) => {
961
+ const { properties, required } = fieldsToProperties(block.fields);
962
+ return {
963
+ type: "object",
964
+ properties: {
965
+ blockType: { type: "string", enum: [block.slug] },
966
+ ...properties,
967
+ },
968
+ required: ["blockType", ...required],
969
+ };
970
+ }),
971
+ },
972
+ };
973
+ break;
974
+ default:
975
+ schema = { type: "string" };
976
+ }
977
+ if (field.label)
978
+ schema.description = field.label;
979
+ return schema;
980
+ }
981
+ //# sourceMappingURL=openapi.js.map