@dyrected/core 2.5.53 → 2.5.56

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 (409) hide show
  1. package/dist/{app-config-oOarB6Fw.d.cts → app-config-Bxg_DVxv.d.cts} +3 -1
  2. package/dist/{app-config-oOarB6Fw.d.ts → app-config-Bxg_DVxv.d.ts} +3 -1
  3. package/dist/index.d.cts +2 -2
  4. package/dist/index.d.ts +2 -2
  5. package/dist/server.d.cts +1 -1
  6. package/dist/server.d.ts +1 -1
  7. package/package.json +1 -1
  8. package/dist/__tests__/admin-auth.test.d.ts +0 -2
  9. package/dist/__tests__/admin-auth.test.d.ts.map +0 -1
  10. package/dist/__tests__/admin-auth.test.js +0 -440
  11. package/dist/__tests__/admin-auth.test.js.map +0 -1
  12. package/dist/__tests__/app.test.d.ts +0 -2
  13. package/dist/__tests__/app.test.d.ts.map +0 -1
  14. package/dist/__tests__/app.test.js +0 -27
  15. package/dist/__tests__/app.test.js.map +0 -1
  16. package/dist/__tests__/config.test.d.ts +0 -2
  17. package/dist/__tests__/config.test.d.ts.map +0 -1
  18. package/dist/__tests__/config.test.js +0 -91
  19. package/dist/__tests__/config.test.js.map +0 -1
  20. package/dist/__tests__/deleteMany.test.d.ts +0 -2
  21. package/dist/__tests__/deleteMany.test.d.ts.map +0 -1
  22. package/dist/__tests__/deleteMany.test.js +0 -75
  23. package/dist/__tests__/deleteMany.test.js.map +0 -1
  24. package/dist/__tests__/depth.test.d.ts +0 -2
  25. package/dist/__tests__/depth.test.d.ts.map +0 -1
  26. package/dist/__tests__/depth.test.js +0 -81
  27. package/dist/__tests__/depth.test.js.map +0 -1
  28. package/dist/__tests__/dynamic-options.test.d.ts +0 -2
  29. package/dist/__tests__/dynamic-options.test.d.ts.map +0 -1
  30. package/dist/__tests__/dynamic-options.test.js +0 -132
  31. package/dist/__tests__/dynamic-options.test.js.map +0 -1
  32. package/dist/__tests__/email.service.test.d.ts +0 -2
  33. package/dist/__tests__/email.service.test.d.ts.map +0 -1
  34. package/dist/__tests__/email.service.test.js +0 -46
  35. package/dist/__tests__/email.service.test.js.map +0 -1
  36. package/dist/__tests__/field-inference.test-types.d.ts +0 -24
  37. package/dist/__tests__/field-inference.test-types.d.ts.map +0 -1
  38. package/dist/__tests__/field-inference.test-types.js +0 -87
  39. package/dist/__tests__/field-inference.test-types.js.map +0 -1
  40. package/dist/__tests__/fixtures/editorial-workflow.d.ts +0 -9
  41. package/dist/__tests__/fixtures/editorial-workflow.d.ts.map +0 -1
  42. package/dist/__tests__/fixtures/editorial-workflow.js +0 -12
  43. package/dist/__tests__/fixtures/editorial-workflow.js.map +0 -1
  44. package/dist/__tests__/hooks.test.d.ts +0 -2
  45. package/dist/__tests__/hooks.test.d.ts.map +0 -1
  46. package/dist/__tests__/hooks.test.js +0 -320
  47. package/dist/__tests__/hooks.test.js.map +0 -1
  48. package/dist/__tests__/mocks.d.ts +0 -74
  49. package/dist/__tests__/mocks.d.ts.map +0 -1
  50. package/dist/__tests__/mocks.js +0 -159
  51. package/dist/__tests__/mocks.js.map +0 -1
  52. package/dist/__tests__/openapi.test.d.ts +0 -2
  53. package/dist/__tests__/openapi.test.d.ts.map +0 -1
  54. package/dist/__tests__/openapi.test.js +0 -95
  55. package/dist/__tests__/openapi.test.js.map +0 -1
  56. package/dist/__tests__/parse-sort.test.d.ts +0 -2
  57. package/dist/__tests__/parse-sort.test.d.ts.map +0 -1
  58. package/dist/__tests__/parse-sort.test.js +0 -24
  59. package/dist/__tests__/parse-sort.test.js.map +0 -1
  60. package/dist/__tests__/preferences.test.d.ts +0 -2
  61. package/dist/__tests__/preferences.test.d.ts.map +0 -1
  62. package/dist/__tests__/preferences.test.js +0 -168
  63. package/dist/__tests__/preferences.test.js.map +0 -1
  64. package/dist/__tests__/router.test.d.ts +0 -2
  65. package/dist/__tests__/router.test.d.ts.map +0 -1
  66. package/dist/__tests__/router.test.js +0 -52
  67. package/dist/__tests__/router.test.js.map +0 -1
  68. package/dist/__tests__/where.test.d.ts +0 -2
  69. package/dist/__tests__/where.test.d.ts.map +0 -1
  70. package/dist/__tests__/where.test.js +0 -105
  71. package/dist/__tests__/where.test.js.map +0 -1
  72. package/dist/__tests__/workflow.test.d.ts +0 -2
  73. package/dist/__tests__/workflow.test.d.ts.map +0 -1
  74. package/dist/__tests__/workflow.test.js +0 -497
  75. package/dist/__tests__/workflow.test.js.map +0 -1
  76. package/dist/app-B2tg7Djj.d.cts +0 -1575
  77. package/dist/app-B2tg7Djj.d.ts +0 -1575
  78. package/dist/app-BElen1tP.d.cts +0 -1690
  79. package/dist/app-BElen1tP.d.ts +0 -1690
  80. package/dist/app-BOrsS7Tz.d.cts +0 -1771
  81. package/dist/app-BOrsS7Tz.d.ts +0 -1771
  82. package/dist/app-Bh4_Opv0.d.cts +0 -1522
  83. package/dist/app-Bh4_Opv0.d.ts +0 -1522
  84. package/dist/app-BibuoHQG.d.cts +0 -1764
  85. package/dist/app-BibuoHQG.d.ts +0 -1764
  86. package/dist/app-Bv9gaDAN.d.cts +0 -561
  87. package/dist/app-Bv9gaDAN.d.ts +0 -561
  88. package/dist/app-BvG3bRc8.d.cts +0 -419
  89. package/dist/app-BvG3bRc8.d.ts +0 -419
  90. package/dist/app-C-HgyliB.d.cts +0 -1694
  91. package/dist/app-C-HgyliB.d.ts +0 -1694
  92. package/dist/app-C3B9N1KR.d.cts +0 -1522
  93. package/dist/app-C3B9N1KR.d.ts +0 -1522
  94. package/dist/app-Cc2mG3zi.d.cts +0 -373
  95. package/dist/app-Cc2mG3zi.d.ts +0 -373
  96. package/dist/app-Ctfmo2Re.d.cts +0 -1692
  97. package/dist/app-Ctfmo2Re.d.ts +0 -1692
  98. package/dist/app-D0ffogDd.d.cts +0 -1690
  99. package/dist/app-D0ffogDd.d.ts +0 -1690
  100. package/dist/app-D2uR47gK.d.cts +0 -1690
  101. package/dist/app-D2uR47gK.d.ts +0 -1690
  102. package/dist/app-DDJJa0ep.d.cts +0 -1621
  103. package/dist/app-DDJJa0ep.d.ts +0 -1621
  104. package/dist/app-DO1s9YW1.d.cts +0 -1621
  105. package/dist/app-DO1s9YW1.d.ts +0 -1621
  106. package/dist/app-DTP3-9PJ.d.cts +0 -561
  107. package/dist/app-DTP3-9PJ.d.ts +0 -561
  108. package/dist/app-DZt5nslu.d.cts +0 -374
  109. package/dist/app-DZt5nslu.d.ts +0 -377
  110. package/dist/app-DbKDGYTI.d.cts +0 -566
  111. package/dist/app-DbKDGYTI.d.ts +0 -566
  112. package/dist/app-Dk3SzV1a.d.cts +0 -374
  113. package/dist/app-Dk3SzV1a.d.ts +0 -374
  114. package/dist/app-DnX9mNsh.d.cts +0 -374
  115. package/dist/app-DnX9mNsh.d.ts +0 -374
  116. package/dist/app-DqRO-CMi.d.cts +0 -1457
  117. package/dist/app-DqRO-CMi.d.ts +0 -1457
  118. package/dist/app-DvaFpOtj.d.cts +0 -398
  119. package/dist/app-DvaFpOtj.d.ts +0 -398
  120. package/dist/app-FGzip4XM.d.cts +0 -1563
  121. package/dist/app-FGzip4XM.d.ts +0 -1563
  122. package/dist/app-KApbf4XL.d.cts +0 -362
  123. package/dist/app-KApbf4XL.d.ts +0 -362
  124. package/dist/app-T0alZAE0.d.cts +0 -383
  125. package/dist/app-T0alZAE0.d.ts +0 -383
  126. package/dist/app-aW2FMuYM.d.cts +0 -1759
  127. package/dist/app-aW2FMuYM.d.ts +0 -1759
  128. package/dist/app-config-2vF6W56z.d.cts +0 -1335
  129. package/dist/app-config-2vF6W56z.d.ts +0 -1335
  130. package/dist/app-config-3XBnu9Xz.d.cts +0 -1379
  131. package/dist/app-config-3XBnu9Xz.d.ts +0 -1379
  132. package/dist/app-config-BLMX-9MN.d.cts +0 -1045
  133. package/dist/app-config-BLMX-9MN.d.ts +0 -1045
  134. package/dist/app-config-CaEQH0Kb.d.cts +0 -1383
  135. package/dist/app-config-CaEQH0Kb.d.ts +0 -1383
  136. package/dist/app-config-DLK6bCaP.d.cts +0 -1390
  137. package/dist/app-config-DLK6bCaP.d.ts +0 -1390
  138. package/dist/app-oQt5-9MU.d.cts +0 -1560
  139. package/dist/app-oQt5-9MU.d.ts +0 -1560
  140. package/dist/app-rZj1VFer.d.cts +0 -1621
  141. package/dist/app-rZj1VFer.d.ts +0 -1621
  142. package/dist/app-wo82JRHl.d.cts +0 -445
  143. package/dist/app-wo82JRHl.d.ts +0 -445
  144. package/dist/app.d.ts +0 -21
  145. package/dist/app.d.ts.map +0 -1
  146. package/dist/app.js +0 -56
  147. package/dist/app.js.map +0 -1
  148. package/dist/auth/jexl.d.ts +0 -10
  149. package/dist/auth/jexl.d.ts.map +0 -1
  150. package/dist/auth/jexl.js +0 -22
  151. package/dist/auth/jexl.js.map +0 -1
  152. package/dist/auth/password.d.ts +0 -10
  153. package/dist/auth/password.d.ts.map +0 -1
  154. package/dist/auth/password.js +0 -28
  155. package/dist/auth/password.js.map +0 -1
  156. package/dist/auth/token.d.ts +0 -22
  157. package/dist/auth/token.d.ts.map +0 -1
  158. package/dist/auth/token.js +0 -40
  159. package/dist/auth/token.js.map +0 -1
  160. package/dist/chunk-22JTWD74.js +0 -1784
  161. package/dist/chunk-23URSKPI.js +0 -2371
  162. package/dist/chunk-2JMA3M5S.js +0 -2475
  163. package/dist/chunk-3FZEUK36.js +0 -2470
  164. package/dist/chunk-3VUH2MNW.js +0 -1903
  165. package/dist/chunk-44O2LDPT.js +0 -1513
  166. package/dist/chunk-45JDQDA2.js +0 -1899
  167. package/dist/chunk-4C4L6SD2.js +0 -1524
  168. package/dist/chunk-4EDMZAM5.js +0 -2692
  169. package/dist/chunk-52T4RCFU.js +0 -1972
  170. package/dist/chunk-6ZXEGKT6.js +0 -619
  171. package/dist/chunk-7GGHK75W.js +0 -1835
  172. package/dist/chunk-ALNQINX7.js +0 -2496
  173. package/dist/chunk-B6KDKYXB.js +0 -2502
  174. package/dist/chunk-BEONZV6M.js +0 -1951
  175. package/dist/chunk-C4GEQ5RP.js +0 -1993
  176. package/dist/chunk-CQDVPMEU.js +0 -613
  177. package/dist/chunk-DOJHZ7XN.js +0 -2394
  178. package/dist/chunk-EH7G3LLL.js +0 -2065
  179. package/dist/chunk-EXXOPW3I.js +0 -2483
  180. package/dist/chunk-F4ZC6TXR.js +0 -2065
  181. package/dist/chunk-FCXE5CVC.js +0 -1528
  182. package/dist/chunk-FDQYPPG3.js +0 -2698
  183. package/dist/chunk-FURYJ254.mjs +0 -33
  184. package/dist/chunk-G2QQLBHW.js +0 -24
  185. package/dist/chunk-GM4WW6IE.js +0 -66
  186. package/dist/chunk-GZODLJ3C.js +0 -1861
  187. package/dist/chunk-IIY3E7Q7.js +0 -1996
  188. package/dist/chunk-JJN4J5NS.js +0 -2069
  189. package/dist/chunk-MRUHCNDM.js +0 -1940
  190. package/dist/chunk-NKDX67AW.js +0 -2698
  191. package/dist/chunk-NYUB4AHZ.js +0 -1951
  192. package/dist/chunk-OD4SLW7H.js +0 -1930
  193. package/dist/chunk-OE5MH3NB.js +0 -2070
  194. package/dist/chunk-PH36WQYS.js +0 -1875
  195. package/dist/chunk-PKNFV7KE.js +0 -2469
  196. package/dist/chunk-SUGK7UYL.js +0 -311
  197. package/dist/chunk-TIJHR54M.js +0 -1879
  198. package/dist/chunk-TUUHGLB5.js +0 -2609
  199. package/dist/chunk-UBTRANFX.js +0 -2476
  200. package/dist/chunk-VJWUG2XK.js +0 -1951
  201. package/dist/chunk-W6KURRMW.js +0 -2471
  202. package/dist/chunk-Y4AYI52W.js +0 -631
  203. package/dist/chunk-YDOBB7MY.js +0 -1457
  204. package/dist/chunk-YNJ3YC4N.js +0 -2483
  205. package/dist/chunk-ZFAOBRHT.js +0 -2709
  206. package/dist/controllers/admin-auth.controller.d.ts +0 -68
  207. package/dist/controllers/admin-auth.controller.d.ts.map +0 -1
  208. package/dist/controllers/admin-auth.controller.js +0 -414
  209. package/dist/controllers/admin-auth.controller.js.map +0 -1
  210. package/dist/controllers/auth.controller.d.ts +0 -125
  211. package/dist/controllers/auth.controller.d.ts.map +0 -1
  212. package/dist/controllers/auth.controller.js +0 -323
  213. package/dist/controllers/auth.controller.js.map +0 -1
  214. package/dist/controllers/collection.controller.d.ts +0 -157
  215. package/dist/controllers/collection.controller.d.ts.map +0 -1
  216. package/dist/controllers/collection.controller.js +0 -779
  217. package/dist/controllers/collection.controller.js.map +0 -1
  218. package/dist/controllers/global.controller.d.ts +0 -17
  219. package/dist/controllers/global.controller.d.ts.map +0 -1
  220. package/dist/controllers/global.controller.js +0 -132
  221. package/dist/controllers/global.controller.js.map +0 -1
  222. package/dist/controllers/media.controller.d.ts +0 -38
  223. package/dist/controllers/media.controller.d.ts.map +0 -1
  224. package/dist/controllers/media.controller.js +0 -162
  225. package/dist/controllers/media.controller.js.map +0 -1
  226. package/dist/controllers/preview.controller.d.ts +0 -37
  227. package/dist/controllers/preview.controller.d.ts.map +0 -1
  228. package/dist/controllers/preview.controller.js +0 -48
  229. package/dist/controllers/preview.controller.js.map +0 -1
  230. package/dist/index-BKYugiYp.d.cts +0 -1988
  231. package/dist/index-BKYugiYp.d.ts +0 -1988
  232. package/dist/index-BSWCiAen.d.cts +0 -1983
  233. package/dist/index-BSWCiAen.d.ts +0 -1983
  234. package/dist/index-BfLDb3b0.d.cts +0 -1467
  235. package/dist/index-BfLDb3b0.d.ts +0 -1467
  236. package/dist/index-Bi4sX_eC.d.cts +0 -1491
  237. package/dist/index-Bi4sX_eC.d.ts +0 -1491
  238. package/dist/index-Bp7PDOYG.d.cts +0 -1750
  239. package/dist/index-Bp7PDOYG.d.ts +0 -1750
  240. package/dist/index-ByXooLy-.d.cts +0 -1762
  241. package/dist/index-ByXooLy-.d.ts +0 -1762
  242. package/dist/index-C9Vp1DCT.d.cts +0 -1563
  243. package/dist/index-C9Vp1DCT.d.ts +0 -1563
  244. package/dist/index-CFezd8p1.d.cts +0 -1986
  245. package/dist/index-CFezd8p1.d.ts +0 -1986
  246. package/dist/index-CdQwnbfh.d.cts +0 -1889
  247. package/dist/index-CdQwnbfh.d.ts +0 -1889
  248. package/dist/index-D38_E0gf.d.cts +0 -336
  249. package/dist/index-D38_E0gf.d.ts +0 -336
  250. package/dist/index-DR2z1rrf.d.cts +0 -1503
  251. package/dist/index-DR2z1rrf.d.ts +0 -1503
  252. package/dist/index-DTrpTru7.d.cts +0 -1881
  253. package/dist/index-DTrpTru7.d.ts +0 -1881
  254. package/dist/index-DfAmTZXk.d.cts +0 -1749
  255. package/dist/index-DfAmTZXk.d.ts +0 -1749
  256. package/dist/index-DfkPT4NR.d.cts +0 -1584
  257. package/dist/index-DfkPT4NR.d.ts +0 -1584
  258. package/dist/index-DzUlE5Hc.d.cts +0 -1751
  259. package/dist/index-DzUlE5Hc.d.ts +0 -1751
  260. package/dist/index-M_m6dXZV.d.cts +0 -1889
  261. package/dist/index-M_m6dXZV.d.ts +0 -1889
  262. package/dist/index-RylhgOwj.d.cts +0 -340
  263. package/dist/index-RylhgOwj.d.ts +0 -340
  264. package/dist/index-qhLus8ZO.d.cts +0 -1881
  265. package/dist/index-qhLus8ZO.d.ts +0 -1881
  266. package/dist/index-xB1lMg79.d.cts +0 -1456
  267. package/dist/index-xB1lMg79.d.ts +0 -1456
  268. package/dist/index.d.mts +0 -417
  269. package/dist/index.d.ts.map +0 -1
  270. package/dist/index.js.map +0 -1
  271. package/dist/index.mjs +0 -1974
  272. package/dist/middleware/auth.d.ts +0 -18
  273. package/dist/middleware/auth.d.ts.map +0 -1
  274. package/dist/middleware/auth.js +0 -45
  275. package/dist/middleware/auth.js.map +0 -1
  276. package/dist/password-ACI2YUGZ.js +0 -8
  277. package/dist/router.d.ts +0 -8
  278. package/dist/router.d.ts.map +0 -1
  279. package/dist/router.js +0 -625
  280. package/dist/router.js.map +0 -1
  281. package/dist/server.d.ts.map +0 -1
  282. package/dist/server.js.map +0 -1
  283. package/dist/services/audit.service.d.ts +0 -23
  284. package/dist/services/audit.service.d.ts.map +0 -1
  285. package/dist/services/audit.service.js +0 -28
  286. package/dist/services/audit.service.js.map +0 -1
  287. package/dist/services/defaults.service.d.ts +0 -8
  288. package/dist/services/defaults.service.d.ts.map +0 -1
  289. package/dist/services/defaults.service.js +0 -55
  290. package/dist/services/defaults.service.js.map +0 -1
  291. package/dist/services/email-template.d.ts +0 -46
  292. package/dist/services/email-template.d.ts.map +0 -1
  293. package/dist/services/email-template.js +0 -88
  294. package/dist/services/email-template.js.map +0 -1
  295. package/dist/services/email.service.d.ts +0 -33
  296. package/dist/services/email.service.d.ts.map +0 -1
  297. package/dist/services/email.service.js +0 -96
  298. package/dist/services/email.service.js.map +0 -1
  299. package/dist/services/media.service.d.ts +0 -20
  300. package/dist/services/media.service.d.ts.map +0 -1
  301. package/dist/services/media.service.js +0 -49
  302. package/dist/services/media.service.js.map +0 -1
  303. package/dist/services/population.service.d.ts +0 -20
  304. package/dist/services/population.service.d.ts.map +0 -1
  305. package/dist/services/population.service.js +0 -194
  306. package/dist/services/population.service.js.map +0 -1
  307. package/dist/token-7QG5DBME.mjs +0 -10
  308. package/dist/types/access.d.ts +0 -38
  309. package/dist/types/access.d.ts.map +0 -1
  310. package/dist/types/access.js +0 -2
  311. package/dist/types/access.js.map +0 -1
  312. package/dist/types/adapters.d.ts +0 -146
  313. package/dist/types/adapters.d.ts.map +0 -1
  314. package/dist/types/adapters.js +0 -2
  315. package/dist/types/adapters.js.map +0 -1
  316. package/dist/types/admin-auth.d.ts +0 -132
  317. package/dist/types/admin-auth.d.ts.map +0 -1
  318. package/dist/types/admin-auth.js +0 -2
  319. package/dist/types/admin-auth.js.map +0 -1
  320. package/dist/types/admin.d.ts +0 -65
  321. package/dist/types/admin.d.ts.map +0 -1
  322. package/dist/types/admin.js +0 -2
  323. package/dist/types/admin.js.map +0 -1
  324. package/dist/types/app-config.d.ts +0 -143
  325. package/dist/types/app-config.d.ts.map +0 -1
  326. package/dist/types/app-config.js +0 -2
  327. package/dist/types/app-config.js.map +0 -1
  328. package/dist/types/documents.d.ts +0 -65
  329. package/dist/types/documents.d.ts.map +0 -1
  330. package/dist/types/documents.js +0 -2
  331. package/dist/types/documents.js.map +0 -1
  332. package/dist/types/hooks.d.ts +0 -122
  333. package/dist/types/hooks.d.ts.map +0 -1
  334. package/dist/types/hooks.js +0 -2
  335. package/dist/types/hooks.js.map +0 -1
  336. package/dist/types/index.d.ts +0 -13
  337. package/dist/types/index.d.ts.map +0 -1
  338. package/dist/types/index.js +0 -13
  339. package/dist/types/index.js.map +0 -1
  340. package/dist/types/request.d.ts +0 -42
  341. package/dist/types/request.d.ts.map +0 -1
  342. package/dist/types/request.js +0 -2
  343. package/dist/types/request.js.map +0 -1
  344. package/dist/types/schema-config.d.ts +0 -303
  345. package/dist/types/schema-config.d.ts.map +0 -1
  346. package/dist/types/schema-config.js +0 -2
  347. package/dist/types/schema-config.js.map +0 -1
  348. package/dist/types/schema-core.d.ts +0 -294
  349. package/dist/types/schema-core.d.ts.map +0 -1
  350. package/dist/types/schema-core.js +0 -2
  351. package/dist/types/schema-core.js.map +0 -1
  352. package/dist/types/schema-inference.d.ts +0 -73
  353. package/dist/types/schema-inference.d.ts.map +0 -1
  354. package/dist/types/schema-inference.js +0 -2
  355. package/dist/types/schema-inference.js.map +0 -1
  356. package/dist/types/workflows.d.ts +0 -84
  357. package/dist/types/workflows.d.ts.map +0 -1
  358. package/dist/types/workflows.js +0 -7
  359. package/dist/types/workflows.js.map +0 -1
  360. package/dist/utils/admin-auth.d.ts +0 -5
  361. package/dist/utils/admin-auth.d.ts.map +0 -1
  362. package/dist/utils/admin-auth.js +0 -39
  363. package/dist/utils/admin-auth.js.map +0 -1
  364. package/dist/utils/config.d.ts +0 -8
  365. package/dist/utils/config.d.ts.map +0 -1
  366. package/dist/utils/config.js +0 -238
  367. package/dist/utils/config.js.map +0 -1
  368. package/dist/utils/hooks.d.ts +0 -41
  369. package/dist/utils/hooks.d.ts.map +0 -1
  370. package/dist/utils/hooks.js +0 -173
  371. package/dist/utils/hooks.js.map +0 -1
  372. package/dist/utils/openapi.d.ts +0 -6
  373. package/dist/utils/openapi.d.ts.map +0 -1
  374. package/dist/utils/openapi.js +0 -842
  375. package/dist/utils/openapi.js.map +0 -1
  376. package/dist/utils/parse-sort.d.ts +0 -7
  377. package/dist/utils/parse-sort.d.ts.map +0 -1
  378. package/dist/utils/parse-sort.js +0 -23
  379. package/dist/utils/parse-sort.js.map +0 -1
  380. package/dist/utils/parse-where.d.ts +0 -63
  381. package/dist/utils/parse-where.d.ts.map +0 -1
  382. package/dist/utils/parse-where.js +0 -196
  383. package/dist/utils/parse-where.js.map +0 -1
  384. package/dist/utils/readonly-db.d.ts +0 -9
  385. package/dist/utils/readonly-db.d.ts.map +0 -1
  386. package/dist/utils/readonly-db.js +0 -21
  387. package/dist/utils/readonly-db.js.map +0 -1
  388. package/dist/utils/setup-prompt.d.ts +0 -11
  389. package/dist/utils/setup-prompt.d.ts.map +0 -1
  390. package/dist/utils/setup-prompt.js +0 -863
  391. package/dist/utils/setup-prompt.js.map +0 -1
  392. package/dist/utils/swagger.d.ts +0 -11
  393. package/dist/utils/swagger.d.ts.map +0 -1
  394. package/dist/utils/swagger.js +0 -62
  395. package/dist/utils/swagger.js.map +0 -1
  396. package/dist/utils/upload-validation.d.ts +0 -23
  397. package/dist/utils/upload-validation.d.ts.map +0 -1
  398. package/dist/utils/upload-validation.js +0 -44
  399. package/dist/utils/upload-validation.js.map +0 -1
  400. package/dist/utils/where-sanitizer.d.ts +0 -10
  401. package/dist/utils/where-sanitizer.d.ts.map +0 -1
  402. package/dist/utils/where-sanitizer.js +0 -67
  403. package/dist/utils/where-sanitizer.js.map +0 -1
  404. package/dist/where-sanitizer-7Q4JXMX6.js +0 -57
  405. package/dist/where-sanitizer-DQIWTQZW.js +0 -50
  406. package/dist/workflows.d.ts +0 -54
  407. package/dist/workflows.d.ts.map +0 -1
  408. package/dist/workflows.js +0 -282
  409. package/dist/workflows.js.map +0 -1
@@ -1,1889 +0,0 @@
1
- import { icons } from 'lucide-react';
2
-
3
- /** A valid icon component name from the Lucide icon set bundled with Dyrected Admin. */
4
- type AdminIconName = keyof typeof icons;
5
- /** Ordered custom component keys rendered around the built-in Admin dashboard. */
6
- interface AdminDashboardComponentSlots {
7
- beforeDashboard?: string[];
8
- afterDashboard?: string[];
9
- }
10
- /** Ordered custom component keys rendered around a collection's list content. */
11
- interface CollectionListComponentSlots {
12
- beforeList?: string[];
13
- beforeListTable?: string[];
14
- afterListTable?: string[];
15
- afterList?: string[];
16
- }
17
- /**
18
- * Minimum HTTP request context passed to every server-side hook and resolver.
19
- *
20
- * The full Web Standard `Request` is available as `raw` when you need it, but
21
- * most hooks only need `query` (URL search parameters).
22
- */
23
- interface HookRequestContext {
24
- /** Parsed URL query-string parameters, e.g. `{ page: '2', search: 'hello' }`. */
25
- query: Record<string, string>;
26
- /** Incoming HTTP headers, lowercased. */
27
- headers: Record<string, string>;
28
- /** The raw Web Standard `Request` object. Useful for streaming or advanced header inspection. */
29
- raw?: Request;
30
- }
31
- /**
32
- * Base shape of an authenticated user as decoded from the JWT.
33
- *
34
- * The actual shape will include every field on your auth collection — this
35
- * interface only guarantees the properties that Dyrected always stamps on the
36
- * token. Extend it in your own codebase for stronger typing:
37
- *
38
- * @example
39
- * declare module '@dyrected/core' {
40
- * interface AuthenticatedUser {
41
- * role: 'admin' | 'editor'
42
- * organizationId: string
43
- * }
44
- * }
45
- */
46
- interface AuthenticatedUser {
47
- /** The user's document ID in the database. */
48
- sub: string;
49
- /** The user's email address. */
50
- email?: string;
51
- /** Slug of the collection this user was authenticated against. */
52
- collection: string;
53
- /** Array of role strings, if your auth collection has a `roles` field. */
54
- roles?: string[];
55
- /** Any additional fields from the auth collection document. */
56
- [key: string]: unknown;
57
- }
58
- declare const LIFECYCLE_EVENT_NAMES: readonly ["revision.created", "workflow.transitioned", "entry.published", "entry.unpublished"];
59
- type LifecycleEventName = (typeof LIFECYCLE_EVENT_NAMES)[number];
60
- interface WorkflowState {
61
- /** Stable machine-readable state key. */
62
- name: string;
63
- /** Label rendered in the Admin UI. */
64
- label: string;
65
- /** Marks the state whose revision is visible to public readers. */
66
- published?: boolean;
67
- /** Optional visual tone used by the Admin UI. */
68
- color?: "neutral" | "warning" | "success" | "danger" | "info";
69
- }
70
- interface WorkflowTransition {
71
- /** Stable transition key used by the REST and SDK APIs. */
72
- name: string;
73
- label: string;
74
- from: string | string[];
75
- to: string;
76
- /** Every listed capability is required. */
77
- requiredCapabilities?: string[];
78
- /** Require a non-empty comment when performing the transition. */
79
- requireComment?: boolean;
80
- /** Remove the public snapshot after this transition commits. */
81
- unpublish?: boolean;
82
- }
83
- interface WorkflowRole {
84
- /** Existing user role value, for example `editor` or `publisher`. */
85
- role: string;
86
- capabilities: string[];
87
- }
88
- interface WorkflowConfig<TDoc extends object = Record<string, unknown>> {
89
- initialState: string;
90
- /** State used for a new working revision created from published content. */
91
- draftState?: string;
92
- states: WorkflowState[];
93
- transitions: WorkflowTransition[];
94
- /** Maps values in `user.roles` to workflow capabilities. */
95
- roles?: WorkflowRole[];
96
- hooks?: {
97
- beforeTransition?: CollectionBeforeTransitionHook<TDoc>[];
98
- afterTransition?: CollectionAfterTransitionHook<TDoc>[];
99
- };
100
- }
101
- interface WorkflowMetadata {
102
- state: string;
103
- revision: number;
104
- publishedRevision?: number;
105
- publishedAt?: string;
106
- publishedBy?: string;
107
- /** Transitions currently allowed for the requesting user. Response-only. */
108
- availableTransitions?: string[];
109
- }
110
- interface WorkflowTransitionContext<TDoc extends object = Record<string, unknown>> {
111
- transition: WorkflowTransition;
112
- from: string;
113
- to: string;
114
- doc: TDoc;
115
- user?: AuthenticatedUser;
116
- comment?: string;
117
- req: HookRequestContext;
118
- db: DatabaseAdapter;
119
- }
120
- type CollectionBeforeTransitionHook<TDoc extends object = Record<string, unknown>> = (args: WorkflowTransitionContext<TDoc>) => void | Promise<void>;
121
- type CollectionAfterTransitionHook<TDoc extends object = Record<string, unknown>> = (args: WorkflowTransitionContext<TDoc> & {
122
- event: LifecycleEvent;
123
- }) => void | Promise<void>;
124
- interface LifecycleEvent<TPayload = Record<string, unknown>> {
125
- id: string;
126
- name: LifecycleEventName;
127
- collection: string;
128
- documentId: string;
129
- occurredAt: string;
130
- actorId?: string;
131
- payload: TPayload;
132
- attempts: number;
133
- status: "pending" | "processing" | "delivered" | "failed";
134
- nextAttemptAt?: string;
135
- deliveredAt?: string;
136
- lastError?: string;
137
- }
138
- type LifecycleEventHandler = (event: LifecycleEvent) => void | Promise<void>;
139
- /**
140
- * Every field type supported by Dyrected.
141
- *
142
- * - Text group: `text`, `textarea`, `richText`, `email`, `url`, `icon`
143
- * - Number/Bool: `number`, `boolean`
144
- * - Date: `date`, `datetime`, `time`
145
- * - Selection: `select`, `multiSelect`, `radio`
146
- * - Relationship: `relationship`, `join`
147
- * - Structural: `array`, `object`, `blocks`, `json`
148
- * - Layout: `row`
149
- * - Media: `image`
150
- */
151
- type FieldType = "text" | "textarea" | "richText" | "number" | "boolean" | "date" | "datetime" | "time" | "select" | "multiSelect" | "radio" | "relationship" | "array" | "object" | "json" | "blocks" | "image" | "email" | "url" | "icon" | "join" | "row";
152
- /**
153
- * Arguments passed to a server-side dynamic options resolver.
154
- *
155
- * All properties are optional so simple resolvers that fetch from an external
156
- * API without needing any context (`async () => fetch(...)`) still compile
157
- * without errors.
158
- */
159
- interface DynamicOptionsResolverArgs {
160
- /**
161
- * The configured database adapter. Use it to query any collection.
162
- *
163
- * @example
164
- * options: async ({ db }) => {
165
- * const result = await db.find({ collection: 'categories' })
166
- * return result.docs.map(c => ({ label: c.name, value: c.id }))
167
- * }
168
- */
169
- db?: DatabaseAdapter;
170
- /**
171
- * The authenticated user making the request, or `undefined` for
172
- * unauthenticated requests. Use it to filter options per user.
173
- *
174
- * @example
175
- * options: async ({ user, db }) => {
176
- * if (!user) return []
177
- * const teams = await db.find({ collection: 'teams', where: { owner: { equals: user.sub } } })
178
- * return teams.docs.map(t => ({ label: t.name, value: t.id }))
179
- * }
180
- */
181
- user?: AuthenticatedUser;
182
- /**
183
- * HTTP request context. Sibling field values selected in the Admin UI are
184
- * forwarded as query parameters and accessible via `req.query`.
185
- *
186
- * @example
187
- * // Cascading dropdown: the Admin UI appends ?country=ca
188
- * options: async ({ req }) => {
189
- * const country = req.query.country ?? 'us'
190
- * const regions = await fetch(`/api/regions?country=${country}`)
191
- * return regions.json()
192
- * }
193
- */
194
- req: HookRequestContext;
195
- }
196
- /** A function that returns option items for a `select`, `multiSelect`, or `radio` field. */
197
- type DynamicOptionsResolver = (args: DynamicOptionsResolverArgs) => Promise<DynamicOptionItem[]> | DynamicOptionItem[];
198
- /**
199
- * Config-object form for a dynamic options resolver. Use this when you also
200
- * want server-side caching via `cacheTTL`.
201
- *
202
- * @example
203
- * options: {
204
- * resolve: async () => fetchCountriesFromApi(),
205
- * cacheTTL: 300, // cache for 5 minutes
206
- * }
207
- */
208
- interface DynamicOptionsConfig {
209
- /** The resolver function. Receives the same args as a bare function resolver. */
210
- resolve: DynamicOptionsResolver;
211
- /**
212
- * Time-to-live in **seconds** for caching the resolver result on the server.
213
- *
214
- * Useful for resolvers that call rate-limited external APIs. When set, the
215
- * result is reused for all identical requests within the TTL window.
216
- *
217
- * Not applied when the resolver uses query parameters (`req.query`), since
218
- * parameterised results are request-specific.
219
- */
220
- cacheTTL?: number;
221
- }
222
- /** A single option item — either a plain string or a label/value pair. */
223
- type DynamicOptionItem = string | {
224
- label: string;
225
- value: unknown;
226
- };
227
- /**
228
- * A single block type for use inside a `blocks` field.
229
- *
230
- * Each block has its own field schema. At runtime, every block item in the
231
- * array carries a `blockType` property that matches the block's `slug`.
232
- *
233
- * @example
234
- * const HeroBlock: Block = {
235
- * slug: 'hero',
236
- * labels: { singular: 'Hero', plural: 'Heroes' },
237
- * fields: [
238
- * { name: 'heading', type: 'text', required: true },
239
- * { name: 'image', type: 'relationship', relationTo: 'media' },
240
- * ],
241
- * }
242
- */
243
- interface Block {
244
- /** Unique identifier for this block type. Stored as `blockType` on each item. */
245
- slug: string;
246
- /** Human-readable labels shown in the Admin UI block picker. */
247
- labels?: {
248
- singular: string;
249
- plural: string;
250
- };
251
- /** Fields that make up this block's data shape. */
252
- fields: Field[];
253
- }
254
- /**
255
- * A hook that runs **before a field value is saved** to the database.
256
- *
257
- * Return the transformed value to persist. Return `undefined` to leave the
258
- * value unchanged (same as returning the original `value`).
259
- *
260
- * Field `beforeChange` hooks run recursively inside `array`, `object`, and
261
- * `blocks` fields — every nested item is processed automatically.
262
- *
263
- * @template TValue The TypeScript type of this field's value.
264
- * @template TDoc The document shape of the parent collection/global.
265
- *
266
- * @example
267
- * // Normalise email to lowercase
268
- * const normaliseEmail: FieldBeforeChangeHook<string> = ({ value }) =>
269
- * value?.toLowerCase().trim()
270
- *
271
- * @example
272
- * // Hash a password with bcrypt
273
- * const hashPassword: FieldBeforeChangeHook<string> = async ({ value }) =>
274
- * value ? bcrypt.hash(value, 10) : value
275
- */
276
- type FieldBeforeChangeHook<TValue = unknown, TDoc extends object = Record<string, unknown>> = (args: {
277
- /** The current value of this field (after any previous hooks in the chain). */
278
- value: TValue;
279
- /** The full document as it existed before this update. `undefined` on create. */
280
- originalDoc?: TDoc;
281
- /** The full incoming data payload being written. */
282
- data: Partial<TDoc>;
283
- /** The authenticated user, or `undefined` for unauthenticated requests. */
284
- user?: AuthenticatedUser;
285
- /**
286
- * Database adapter for cross-collection reads. Write operations (create,
287
- * update, delete) will throw — use `afterChange`/`afterDelete` for writes.
288
- */
289
- db: ReadonlyDatabaseAdapter;
290
- }) => TValue | undefined | Promise<TValue | undefined>;
291
- /**
292
- * A hook that runs **after a field value is read** from the database, before
293
- * the response is sent to the client.
294
- *
295
- * Return the transformed value to return to the client. Use this for masking,
296
- * formatting, or adding computed properties.
297
- *
298
- * @template TValue The TypeScript type of this field's value.
299
- * @template TDoc The document shape of the parent collection/global.
300
- *
301
- * @example
302
- * // Mask sensitive value for non-admins
303
- * const maskForPublic: FieldAfterReadHook<string> = ({ value, user }) =>
304
- * user?.role === 'admin' ? value : '••••••••'
305
- */
306
- type FieldAfterReadHook<TValue = unknown, TDoc extends object = Record<string, unknown>> = (args: {
307
- /** The raw field value as stored in the database. */
308
- value: TValue;
309
- /** The full document being returned (with defaults applied). */
310
- doc: TDoc;
311
- /** The authenticated user, or `undefined` for unauthenticated requests. */
312
- user?: AuthenticatedUser;
313
- /**
314
- * Database adapter for cross-collection reads. Write operations (create,
315
- * update, delete) will throw — use `afterChange`/`afterDelete` for writes.
316
- */
317
- db: ReadonlyDatabaseAdapter;
318
- }) => TValue | undefined | Promise<TValue | undefined>;
319
- /**
320
- * @deprecated Use {@link FieldBeforeChangeHook} or {@link FieldAfterReadHook} instead.
321
- * This alias remains for backwards compatibility.
322
- */
323
- type FieldHook<TDoc extends object = Record<string, unknown>, TValue = unknown> = FieldBeforeChangeHook<TValue, TDoc>;
324
- /**
325
- * Runs before Dyrected queries the database for a list or single-document fetch.
326
- *
327
- * Return a new `where` query object to override or extend the current filter.
328
- * Return `undefined` (or nothing) to leave the query unchanged.
329
- *
330
- * @example
331
- * // Scope all reads to the current user's own documents
332
- * const scopeToUser: CollectionBeforeReadHook = ({ user, query }) => ({
333
- * ...query,
334
- * owner: { equals: user?.sub },
335
- * })
336
- */
337
- type CollectionBeforeReadHook = (args: {
338
- /** The HTTP request context. */
339
- req: HookRequestContext;
340
- /** The current `where` query filter. Modify and return to override. */
341
- query?: Record<string, unknown>;
342
- /** The authenticated user, or `undefined` for unauthenticated requests. */
343
- user?: AuthenticatedUser;
344
- /**
345
- * Database adapter for cross-collection reads. Write operations (create,
346
- * update, delete) will throw — use `afterChange`/`afterDelete` for writes.
347
- */
348
- db: ReadonlyDatabaseAdapter;
349
- }) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;
350
- /**
351
- * Runs after a document (or list of documents) is fetched from the database,
352
- * before the response is sent to the client.
353
- *
354
- * Return a modified document to send instead. Useful for adding computed
355
- * virtual fields or transforming the shape of the response.
356
- *
357
- * @template TDoc The shape of the collection's document.
358
- *
359
- * @example
360
- * // Add a computed fullName field
361
- * const addFullName: CollectionAfterReadHook<User> = ({ doc }) => ({
362
- * ...doc,
363
- * fullName: `${doc.firstName} ${doc.lastName}`.trim(),
364
- * })
365
- */
366
- type CollectionAfterReadHook<TDoc extends object = Record<string, unknown>> = (args: {
367
- /** The document as fetched from the database (with defaults applied). */
368
- doc: TDoc;
369
- /** The HTTP request context. */
370
- req: HookRequestContext;
371
- /** The authenticated user, or `undefined` for unauthenticated requests. */
372
- user?: AuthenticatedUser;
373
- /**
374
- * Database adapter for cross-collection reads. Write operations (create,
375
- * update, delete) will throw — use `afterChange`/`afterDelete` for writes.
376
- */
377
- db: ReadonlyDatabaseAdapter;
378
- }) => TDoc | Promise<TDoc>;
379
- /**
380
- * Runs **before** a document is created or updated in the database.
381
- *
382
- * Return a modified data object to write instead of the original. This is the
383
- * right place for data transformation, normalisation, slug generation, and
384
- * validation (throw to abort the write).
385
- *
386
- * @template TDoc The shape of the collection's document.
387
- *
388
- * @example
389
- * // Auto-generate a slug on create
390
- * const generateSlug: CollectionBeforeChangeHook<Post> = ({ data, operation }) => {
391
- * if (operation === 'create' || data.title !== undefined) {
392
- * return { ...data, slug: slugify(data.title ?? '') }
393
- * }
394
- * return data
395
- * }
396
- *
397
- * @example
398
- * // Abort the write with a validation error
399
- * const validateStock: CollectionBeforeChangeHook<Product> = ({ data }) => {
400
- * if ((data.stock ?? 0) < 0) throw new Error('Stock cannot be negative.')
401
- * return data
402
- * }
403
- */
404
- type CollectionBeforeChangeHook<TDoc extends object = Record<string, unknown>> = (args: {
405
- /** The incoming data payload being written. */
406
- data: Partial<TDoc>;
407
- /**
408
- * The existing document before this update. Only present on `'update'`
409
- * operations; `undefined` on `'create'`.
410
- */
411
- doc?: TDoc;
412
- /** The HTTP request context. */
413
- req: HookRequestContext;
414
- /** The authenticated user, or `undefined` for unauthenticated requests. */
415
- user?: AuthenticatedUser;
416
- /** Whether this is a new document or an update to an existing one. */
417
- operation: "create" | "update";
418
- /**
419
- * Database adapter for cross-collection reads. Write operations (create,
420
- * update, delete) will throw — use `afterChange`/`afterDelete` for writes.
421
- */
422
- db: ReadonlyDatabaseAdapter;
423
- }) => Partial<TDoc> | void | Promise<Partial<TDoc> | void>;
424
- /**
425
- * Runs **after** a document is created or updated in the database.
426
- *
427
- * **Isolation**: errors thrown inside this hook are caught by the framework,
428
- * logged to the console, and then discarded. The HTTP response returns the
429
- * saved document as if nothing went wrong — because from the database's
430
- * perspective, nothing did. This means a transient email failure or webhook
431
- * timeout will never turn a successful write into a 500 for the caller.
432
- *
433
- * The return value is ignored — this hook is for side-effects only (emails,
434
- * webhooks, cache revalidation, search index updates, etc.).
435
- *
436
- * **Awaiting vs fire-and-forget**: `await`ing inside this hook is fine for
437
- * fast, reliable calls. For slow or unreliable external services prefer
438
- * fire-and-forget with your own `.catch()` so the response doesn't block:
439
- *
440
- * ```ts
441
- * // ✓ fast & reliable — safe to await
442
- * afterChange: [async ({ doc }) => {
443
- * await revalidatePath(`/posts/${doc.slug}`)
444
- * }]
445
- *
446
- * // ✓ slow or unreliable — fire-and-forget
447
- * afterChange: [({ doc }) => {
448
- * sendEmail({ to: doc.email, ... }).catch(console.error)
449
- * }]
450
- * ```
451
- *
452
- * @template TDoc The shape of the collection's document.
453
- *
454
- * @example
455
- * // Send a webhook after every save
456
- * const sendWebhook: CollectionAfterChangeHook<Post> = async ({ doc, operation }) => {
457
- * await fetch('https://hooks.example.com/content', {
458
- * method: 'POST',
459
- * body: JSON.stringify({ event: operation, doc }),
460
- * })
461
- * }
462
- *
463
- * @example
464
- * // Only act when a specific field changed
465
- * const onStatusChange: CollectionAfterChangeHook<Post> = async ({ doc, previousDoc, operation }) => {
466
- * if (operation === 'update' && doc.status !== previousDoc?.status) {
467
- * await notifySubscribers(doc)
468
- * }
469
- * }
470
- */
471
- type CollectionAfterChangeHook<TDoc extends object = Record<string, unknown>> = (args: {
472
- /** The document as it was written to the database. */
473
- doc: TDoc;
474
- /**
475
- * Snapshot of the document before the write. Only present on `'update'`
476
- * operations; `undefined` on `'create'`.
477
- */
478
- previousDoc?: TDoc;
479
- /** The HTTP request context. */
480
- req: HookRequestContext;
481
- /** The authenticated user, or `undefined` for unauthenticated requests. */
482
- user?: AuthenticatedUser;
483
- /** Whether this was a new document or an update. */
484
- operation: "create" | "update";
485
- /**
486
- * Database adapter with full read/write access. The DB write for this
487
- * operation has already committed — safe for side-effect writes.
488
- */
489
- db: DatabaseAdapter;
490
- }) => void | Promise<void>;
491
- /**
492
- * Runs **before** a document is deleted from the database.
493
- *
494
- * Throw an error to cancel the deletion — the document will not be removed
495
- * and the API will return a `500` with your error message.
496
- *
497
- * @template TDoc The shape of the collection's document.
498
- *
499
- * @example
500
- * // Prevent deletion when other documents reference this one
501
- * const guardReferences: CollectionBeforeDeleteHook<Category> = async ({ id, doc }) => {
502
- * const refs = await db.find({ collection: 'posts', where: { category: { equals: id } } })
503
- * if (refs.total > 0) throw new Error(`${refs.total} post(s) still reference this category.`)
504
- * }
505
- */
506
- type CollectionBeforeDeleteHook<TDoc extends object = Record<string, unknown>> = (args: {
507
- /** The ID of the document about to be deleted. */
508
- id: string;
509
- /** The full document about to be deleted. */
510
- doc: TDoc;
511
- /** The HTTP request context. */
512
- req: HookRequestContext;
513
- /** The authenticated user, or `undefined` for unauthenticated requests. */
514
- user?: AuthenticatedUser;
515
- /**
516
- * Database adapter for cross-collection reads. Write operations (create,
517
- * update, delete) will throw — use `afterDelete` for post-deletion writes.
518
- */
519
- db: ReadonlyDatabaseAdapter;
520
- }) => void | Promise<void>;
521
- /**
522
- * Runs **after** a document has been deleted from the database.
523
- *
524
- * **Isolation**: same as {@link CollectionAfterChangeHook} — errors are caught,
525
- * logged, and discarded. The deletion is already committed; a hook failure will
526
- * never cause the deleted ID to appear in the `failed` list of a bulk delete or
527
- * turn a successful single delete into a 500.
528
- *
529
- * Use for cleanup side-effects — removing related media, invalidating caches,
530
- * notifying downstream services, etc.
531
- *
532
- * @template TDoc The shape of the collection's document.
533
- */
534
- type CollectionAfterDeleteHook<TDoc extends object = Record<string, unknown>> = (args: {
535
- /** The ID of the deleted document. */
536
- id: string;
537
- /** The document as it was just before deletion. */
538
- doc: TDoc;
539
- /** The HTTP request context. */
540
- req: HookRequestContext;
541
- /** The authenticated user, or `undefined` for unauthenticated requests. */
542
- user?: AuthenticatedUser;
543
- /**
544
- * Database adapter with full read/write access. The deletion has already
545
- * committed — safe for cascade deletes or cleanup writes.
546
- */
547
- db: DatabaseAdapter;
548
- }) => void | Promise<void>;
549
- /** @see {@link CollectionBeforeReadHook} */
550
- type GlobalBeforeReadHook = CollectionBeforeReadHook;
551
- /**
552
- * Runs after the global document is fetched, before the response is sent.
553
- * @see {@link CollectionAfterReadHook}
554
- */
555
- type GlobalAfterReadHook<TDoc extends object = Record<string, unknown>> = (args: {
556
- doc: TDoc;
557
- req: HookRequestContext;
558
- user?: AuthenticatedUser;
559
- /**
560
- * Database adapter for cross-collection reads. Write operations (create,
561
- * update, delete) will throw — use `afterChange` for writes.
562
- */
563
- db: ReadonlyDatabaseAdapter;
564
- }) => TDoc | Promise<TDoc>;
565
- /**
566
- * Runs before the global document is updated.
567
- * Operation is always `'update'` (globals cannot be created or deleted).
568
- * @see {@link CollectionBeforeChangeHook}
569
- */
570
- type GlobalBeforeChangeHook<TDoc extends object = Record<string, unknown>> = (args: {
571
- data: Partial<TDoc>;
572
- doc?: TDoc;
573
- req: HookRequestContext;
574
- user?: AuthenticatedUser;
575
- operation: "update";
576
- /**
577
- * Database adapter for cross-collection reads. Write operations (create,
578
- * update, delete) will throw — use `afterChange` for writes.
579
- */
580
- db: ReadonlyDatabaseAdapter;
581
- }) => Partial<TDoc> | void | Promise<Partial<TDoc> | void>;
582
- /**
583
- * Runs after the global document is updated. Side-effects only.
584
- *
585
- * **Isolation**: errors are caught, logged, and discarded — same behaviour as
586
- * {@link CollectionAfterChangeHook}. The update is already committed.
587
- *
588
- * @see {@link CollectionAfterChangeHook}
589
- */
590
- type GlobalAfterChangeHook<TDoc extends object = Record<string, unknown>> = (args: {
591
- doc: TDoc;
592
- previousDoc?: TDoc;
593
- req: HookRequestContext;
594
- user?: AuthenticatedUser;
595
- operation: "update";
596
- /**
597
- * Database adapter with full read/write access. The DB write for this
598
- * operation has already committed — safe for side-effect writes.
599
- */
600
- db: DatabaseAdapter;
601
- }) => void | Promise<void>;
602
- /**
603
- * @deprecated Use the specific hook types instead:
604
- * {@link CollectionBeforeChangeHook}, {@link CollectionAfterReadHook}, etc.
605
- *
606
- * This broad type remains for backwards compatibility with the internal hook runner.
607
- */
608
- type HookFunction<TDoc extends object = Record<string, unknown>> = (args: {
609
- data?: Partial<TDoc>;
610
- doc?: TDoc;
611
- user?: AuthenticatedUser;
612
- req?: HookRequestContext;
613
- operation?: "create" | "update" | "delete";
614
- db?: DatabaseAdapter;
615
- [key: string]: unknown;
616
- }) => unknown | Promise<unknown>;
617
- /**
618
- * A function that determines whether the current user can perform an operation.
619
- *
620
- * Return `true` to allow, `false` to deny.
621
- * Return a `where`-style object to allow access only to matching documents
622
- * (useful for multi-tenant setups where users can only see their own data).
623
- *
624
- * Can also be expressed as a Jexl expression **string** for simple role checks
625
- * that need to be serialised (e.g. stored in the database or sent to the Admin UI).
626
- *
627
- * @template TDoc The shape of the collection's document.
628
- *
629
- * @example
630
- * // Simple role check
631
- * access: {
632
- * delete: ({ user }) => user?.roles?.includes('admin') ?? false,
633
- * }
634
- *
635
- * @example
636
- * // Row-level: users can only read their own documents
637
- * access: {
638
- * read: ({ user }) => ({ owner: { equals: user?.sub } }),
639
- * }
640
- *
641
- * @example
642
- * // Jexl string — evaluated server-side
643
- * access: {
644
- * update: "user.roles contains 'editor'",
645
- * }
646
- */
647
- type AccessFunction<TDoc extends object = Record<string, unknown>> = (args: {
648
- user: AuthenticatedUser | undefined;
649
- doc?: TDoc;
650
- data?: Partial<TDoc>;
651
- req: HookRequestContext;
652
- }) => boolean | Record<string, unknown> | Promise<boolean | Record<string, unknown>>;
653
- /**
654
- * Shared base properties present on every field type.
655
- * The exported `Field` union type extends this with a typed `hooks` block.
656
- */
657
- interface FieldBase {
658
- /**
659
- * The database column / JSON key for this field.
660
- * Use camelCase. Omit for layout-only fields (`row`, `join`).
661
- */
662
- name?: string;
663
- /**
664
- * Human-readable label shown next to the field in the Admin UI.
665
- * Defaults to a title-cased version of `name` if omitted.
666
- */
667
- label?: string;
668
- /**
669
- * If `true`, the field must have a non-empty value.
670
- * Enforced on both client (Admin UI) and server (API).
671
- */
672
- required?: boolean;
673
- /**
674
- * If `true`, the database adapter enforces a unique constraint for this
675
- * field across the collection.
676
- */
677
- unique?: boolean;
678
- /**
679
- * The initial value written to new documents when no value is provided.
680
- * The type should match the field type.
681
- */
682
- defaultValue?: unknown;
683
- /**
684
- * Available choices for `select`, `multiSelect`, and `radio` fields.
685
- *
686
- * Three forms are accepted:
687
- *
688
- * **1. Static array** — fixed list known at build time:
689
- * ```ts
690
- * options: [{ label: 'Draft', value: 'draft' }, { label: 'Published', value: 'published' }]
691
- * ```
692
- *
693
- * **2. Server-side resolver** — runs on the server (never in the browser).
694
- * Use for DB queries, secret API keys, or user-filtered lists.
695
- * The Admin UI fetches results from `GET /api/dyrected/options/:slug/:field`.
696
- * ```ts
697
- * options: async ({ db, user }) => {
698
- * const cats = await db.find({ collection: 'categories' })
699
- * return cats.docs.map(c => ({ label: c.name, value: c.id }))
700
- * }
701
- * ```
702
- *
703
- * **3. With caching** (`{ resolve, cacheTTL }`) — same as above but the
704
- * result is cached on the server for `cacheTTL` seconds:
705
- * ```ts
706
- * options: { resolve: async () => fetchFromSlowApi(), cacheTTL: 300 }
707
- * ```
708
- *
709
- * For **instant client-side cascading dropdowns** driven by another field's
710
- * value, use `admin.hooks.options` instead (no network round-trip).
711
- */
712
- options?: string[] | {
713
- label: string;
714
- value: unknown;
715
- }[] | DynamicOptionsResolver | DynamicOptionsConfig;
716
- /** For `relationship` fields — the slug of the collection to link to. */
717
- relationTo?: string;
718
- /**
719
- * For `relationship` and `image` fields — if `true`, stores an array of
720
- * IDs instead of a single ID.
721
- */
722
- hasMany?: boolean;
723
- /** Sub-fields for `array` and `object` field types. */
724
- fields?: Field[];
725
- /** Block type definitions for `blocks` fields. */
726
- blocks?: Block[];
727
- /**
728
- * For `join` fields — the slug of the target collection to show linked
729
- * documents from. Paired with `on`.
730
- */
731
- collection?: string;
732
- /**
733
- * For `join` fields — the name of the `relationship` field on the target
734
- * collection that points back to this collection.
735
- */
736
- on?: string;
737
- /**
738
- * For `join` fields — maximum number of related documents to return.
739
- * Defaults to 10. Set to 0 to return all.
740
- */
741
- limit?: number;
742
- /**
743
- * Field-level read/update access control.
744
- *
745
- * - `read`: if it returns `false`, the field is stripped from API responses.
746
- * - `update`: if it returns `false`, incoming values for this field are silently ignored.
747
- */
748
- access?: {
749
- read?: AccessFunction | string;
750
- update?: AccessFunction | string;
751
- };
752
- /**
753
- * Admin UI options for this field. None of these properties affect the API
754
- * or the database — they are purely presentational.
755
- */
756
- admin?: {
757
- /** Placeholder text shown when the input is empty. */
758
- placeholder?: string;
759
- /** String key referencing a custom React component to render this input field. */
760
- component?: string;
761
- /** Help text rendered below the field in the Admin UI. */
762
- description?: string;
763
- /**
764
- * If `true`, the field is hidden from the Admin form entirely.
765
- * Its value is preserved in the database and still returned by the API.
766
- */
767
- hidden?: boolean;
768
- /**
769
- * If `false`, this field cannot be used in list filters and will be stripped from incoming `where` queries.
770
- * Defaults to `true` for supported field types.
771
- */
772
- filterable?: boolean;
773
- /**
774
- * If `true`, the field is rendered as non-editable in the Admin UI.
775
- * The value is still included in form submissions.
776
- */
777
- readOnly?: boolean;
778
- /**
779
- * A function (or Jexl expression string) evaluated reactively as the
780
- * editor types. Return `true` to show the field, `false` to hide it.
781
- *
782
- * Receives the current form values as `data` and sibling-level values
783
- * as `siblingData`.
784
- *
785
- * @example
786
- * // Only show the discount field when a coupon code is entered
787
- * condition: (data) => !!data.couponCode
788
- */
789
- condition?: ((data: Record<string, unknown>, siblingData: Record<string, unknown>) => boolean) | string;
790
- /**
791
- * Presentation style for fields with multiple built-in admin renderers.
792
- * - `select`: use `radio` for 2–5 options, otherwise defaults to `select`
793
- * - `boolean`: use `checkbox` (default) or `switch`
794
- */
795
- layout?: "checkbox" | "switch" | "radio" | "select" | string;
796
- /**
797
- * For `select` fields with `layout: 'radio'` — orientation of the radio buttons.
798
- * @default 'vertical'
799
- */
800
- direction?: "horizontal" | "vertical";
801
- /**
802
- * Assigns this field to a named tab in the Admin form.
803
- * When any field in the collection has a `tab`, the form switches to a
804
- * tabbed layout. Fields without a `tab` are grouped under "General".
805
- */
806
- tab?: string;
807
- /**
808
- * CSS width of this field when it is a child of a `row` field.
809
- * Accepts any CSS length value, e.g. `'50%'`, `'200px'`, `'1fr'`.
810
- */
811
- width?: string;
812
- /**
813
- * Client-side Admin UI hooks for this field.
814
- *
815
- * These run **in the browser** every time any sibling field value changes.
816
- * They are completely separate from the server-side `hooks` property.
817
- *
818
- * `onChange` is typed per-field via the discriminated `Field` union —
819
- * see `FieldAdminHooks<TValue>` below.
820
- */
821
- hooks?: {
822
- /**
823
- * For `select`, `multiSelect`, and `radio` fields only.
824
- *
825
- * Computes the **available choices** for this field based on other
826
- * field values — instant, zero-latency cascading dropdowns.
827
- *
828
- * Runs in the browser every time any sibling field changes. When the
829
- * returned list changes, the field's current value is automatically
830
- * cleared if it is no longer a valid choice.
831
- *
832
- * Use the top-level `options` resolver (server-side) when:
833
- * - Your option list requires a DB query or a secret API key
834
- * - You want server-side caching (`cacheTTL`)
835
- *
836
- * @example
837
- * // Country → State/Province cascading dropdown
838
- * options: ({ siblingData }) => {
839
- * if (siblingData.country === 'us') {
840
- * return [{ label: 'California', value: 'CA' }, { label: 'New York', value: 'NY' }]
841
- * }
842
- * return []
843
- * }
844
- */
845
- options?: (args: {
846
- /** Current values of all fields at the same nesting level. */
847
- siblingData: Record<string, unknown>;
848
- /** Current values of the entire form. */
849
- data: Record<string, unknown>;
850
- }) => Array<string | {
851
- label: string;
852
- value: unknown;
853
- }> | Promise<Array<string | {
854
- label: string;
855
- value: unknown;
856
- }>>;
857
- };
858
- };
859
- /**
860
- * For database migrations: if set, data stored under this key in the database
861
- * will be migrated to the current field `name` on next schema sync.
862
- */
863
- renameTo?: string;
864
- /**
865
- * For SQL adapters: if `true`, this field is extracted to a real column
866
- * instead of being stored inside a JSON blob. Improves query performance
867
- * for frequently-filtered fields.
868
- */
869
- promoted?: boolean;
870
- }
871
- /**
872
- * Typed hooks block for a field whose stored value is `TValue`.
873
- *
874
- * The return type of the callbacks is kept as `unknown` here so that TypeScript
875
- * can narrow the contextual type from the discriminated `Field` union without
876
- * bidirectional inference conflicts. Use the explicit `FieldBeforeChangeHook<T>`
877
- * and `FieldAfterReadHook<T>` types when you need checked return types.
878
- */
879
- type FieldHooks<TValue> = {
880
- /**
881
- * Field-level lifecycle hooks.
882
- *
883
- * - `beforeChange` — transform or validate the value before it is saved.
884
- * `value` is typed to the field's own value type (e.g. `string` for
885
- * `text`, `number` for `number`, `boolean` for `boolean`).
886
- * - `afterRead` — transform the value after it is read, before the API response.
887
- *
888
- * Both hook types run **recursively** inside `array`, `object`, and `blocks` fields.
889
- *
890
- * @example
891
- * hooks: {
892
- * beforeChange: [({ value }) => value.trim()], // value: string ✓
893
- * afterRead: [({ value, user }) => user?.role === 'admin' ? value : '****'],
894
- * }
895
- */
896
- hooks?: {
897
- beforeChange?: Array<(args: {
898
- value: TValue;
899
- originalDoc?: Record<string, unknown>;
900
- data: Record<string, unknown>;
901
- user?: AuthenticatedUser;
902
- }) => unknown>;
903
- afterRead?: Array<(args: {
904
- value: TValue;
905
- doc: Record<string, unknown>;
906
- user?: AuthenticatedUser;
907
- }) => unknown>;
908
- };
909
- };
910
- /**
911
- * Typed client-side admin hook block for a field whose value type is `TValue`.
912
- *
913
- * Intersected into each discriminated `Field` union member so that
914
- * `admin.hooks.onChange` receives a `value` typed to the field's own type.
915
- * The return type is `unknown` for the same reason as `FieldHooks` — to
916
- * prevent bidirectional inference from conflicting with union discrimination.
917
- */
918
- type FieldAdminHooks<TValue> = {
919
- admin?: {
920
- hooks?: {
921
- /**
922
- * Derives or computes this field's **value** from other field values in
923
- * real time — without any network request.
924
- *
925
- * Return a new value to update this field. Return `undefined` to leave
926
- * it unchanged.
927
- *
928
- * Use `setValue` for imperative updates inside async callbacks.
929
- *
930
- * ⚠️ Never return an options array here — use `admin.hooks.options` for that.
931
- *
932
- * @example
933
- * // Auto-generate a URL slug from the title field
934
- * onChange: ({ value, siblingData }) => {
935
- * const base = (siblingData.title ?? '').toLowerCase().replace(/[^a-z0-9]+/g, '-');
936
- * return base.includes(value) ? base : value;
937
- * }
938
- */
939
- onChange?: (args: {
940
- /** The current value of this field — typed to match the field's `type`. */
941
- value: TValue;
942
- /** Current values of all fields at the same nesting level. */
943
- siblingData: Record<string, unknown>;
944
- /** Current values of the entire form. */
945
- data: Record<string, unknown>;
946
- /** Imperative setter — use inside async callbacks when you can't return a value. */
947
- setValue: (value: unknown) => void;
948
- }) => unknown;
949
- };
950
- };
951
- };
952
- /**
953
- * Defines a single field on a collection or global.
954
- *
955
- * ## Typed `value` in hook callbacks
956
- *
957
- * Three hook callbacks automatically receive a `value` typed to the field's
958
- * own value type — no manual annotations needed:
959
- *
960
- * | Hook | When it runs |
961
- * |------|-------------|
962
- * | `hooks.beforeChange` | Server — before the value is written to the DB |
963
- * | `hooks.afterRead` | Server — after the value is read, before the API response |
964
- * | `admin.hooks.onChange` | Browser — whenever a sibling field changes in the Admin UI |
965
- *
966
- * Type mapping by `type` property:
967
- * - `text / textarea / email / url / icon / date / select / radio` → `string`
968
- * - `number` → `number`
969
- * - `boolean` → `boolean`
970
- * - `multiSelect` → `string[]`
971
- * - `relationship / image` → `string | string[]`
972
- * - `richText / json` → `Record<string, unknown>`
973
- * - `object / array / blocks` → `unknown`
974
- *
975
- * ```ts
976
- * {
977
- * name: 'slug', type: 'text',
978
- * hooks: {
979
- * beforeChange: [({ value }) => value.toLowerCase()], // value: string ✓
980
- * afterRead: [({ value }) => value.trim()], // value: string ✓
981
- * },
982
- * admin: {
983
- * hooks: {
984
- * onChange: ({ value, siblingData }) => // value: string ✓
985
- * (siblingData.title as string ?? '').toLowerCase().replace(/\s+/g, '-'),
986
- * },
987
- * },
988
- * }
989
- * ```
990
- *
991
- * **Important**: write `type` as a plain string literal — do **not** use `as const`.
992
- * TypeScript's `const` generic inference already preserves literal types, and
993
- * adding `as const` to the discriminant property prevents the contextual type
994
- * from flowing into the hook callbacks.
995
- *
996
- * ```ts
997
- * // ✓ correct
998
- * { name: 'slug', type: 'text', hooks: { beforeChange: [({ value }) => value.toLowerCase()] } }
999
- *
1000
- * // ✗ breaks value typing
1001
- * { name: 'slug', type: 'text' as const, hooks: { beforeChange: [({ value }) => value.toLowerCase()] } }
1002
- * ```
1003
- */
1004
- type Field = FieldBase & (({
1005
- type: "text" | "textarea" | "email" | "url" | "icon" | "date" | "datetime" | "select" | "radio";
1006
- } & FieldHooks<string> & FieldAdminHooks<string>) | ({
1007
- type: "number";
1008
- } & FieldHooks<number> & FieldAdminHooks<number>) | ({
1009
- type: "boolean";
1010
- } & FieldHooks<boolean> & FieldAdminHooks<boolean>) | ({
1011
- type: "multiSelect";
1012
- } & FieldHooks<string[]> & FieldAdminHooks<string[]>) | ({
1013
- type: "relationship" | "image";
1014
- } & FieldHooks<string | string[]> & FieldAdminHooks<string | string[]>) | ({
1015
- type: "richText" | "json";
1016
- } & FieldHooks<Record<string, unknown>> & FieldAdminHooks<Record<string, unknown>>) | ({
1017
- type: "object" | "array" | "blocks" | "join" | "row";
1018
- } & FieldHooks<unknown> & FieldAdminHooks<unknown>));
1019
- /**
1020
- * Defines a Dyrected collection — a named set of documents with a shared schema.
1021
- *
1022
- * Pass your document's TypeScript type as the generic parameter `TDoc` to get
1023
- * fully typed hooks and access functions:
1024
- *
1025
- * ```ts
1026
- * interface Post {
1027
- * id: string
1028
- * title: string
1029
- * slug: string
1030
- * status: 'draft' | 'published'
1031
- * publishedAt?: string
1032
- * }
1033
- *
1034
- * export const Posts = defineCollection<Post>({
1035
- * slug: 'posts',
1036
- * hooks: {
1037
- * beforeChange: [({ data, operation }) => {
1038
- * // `data` is typed as Partial<Post>
1039
- * if (operation === 'create') return { ...data, status: 'draft' }
1040
- * return data
1041
- * }],
1042
- * afterChange: [({ doc, previousDoc }) => {
1043
- * // `doc` and `previousDoc` are typed as Post
1044
- * if (doc.status !== previousDoc?.status) notifySubscribers(doc)
1045
- * }],
1046
- * },
1047
- * fields: [...],
1048
- * })
1049
- * ```
1050
- *
1051
- * @template TDoc The TypeScript shape of a document in this collection.
1052
- * Defaults to `Record<string, unknown>` for untyped usage.
1053
- */
1054
- interface CollectionConfig<TDoc extends object = Record<string, unknown>> {
1055
- /**
1056
- * Unique identifier for this collection.
1057
- * Used as the URL segment (`/api/collections/:slug`) and the database table/collection name.
1058
- * Use kebab-case, e.g. `'blog-posts'`.
1059
- */
1060
- slug: string;
1061
- /**
1062
- * Restricts this collection to a specific site in a multi-tenant deployment.
1063
- * When set, only requests bearing a matching `X-Site-Id` header can access it.
1064
- */
1065
- siteId?: string;
1066
- /**
1067
- * If `true`, this collection is shared across all sites in a multi-tenant
1068
- * deployment and accessible regardless of the `X-Site-Id` header.
1069
- */
1070
- shared?: boolean;
1071
- /** Human-readable names for documents in this collection, shown in the Admin UI. */
1072
- labels?: {
1073
- singular: string;
1074
- plural: string;
1075
- };
1076
- /**
1077
- * If `true`, this collection is an **auth collection** — it gains
1078
- * `POST /api/collections/:slug/login` and `POST /api/collections/:slug/logout`
1079
- * endpoints, and documents are expected to have a `password` field.
1080
- */
1081
- auth?: boolean;
1082
- /**
1083
- * If `true` (or a config object), this collection supports **file uploads**.
1084
- * Documents gain file-related fields (`url`, `filename`, `mimeType`, etc.)
1085
- * and the create endpoint accepts `multipart/form-data`.
1086
- */
1087
- upload?: boolean | UploadConfig;
1088
- /** Field definitions that make up the document schema for this collection. */
1089
- fields: Field[];
1090
- /**
1091
- * If `true`, Dyrected automatically adds `createdAt` and `updatedAt`
1092
- * timestamp fields to every document. Defaults to `true`.
1093
- */
1094
- timestamps?: boolean;
1095
- /**
1096
- * Initial documents to seed into this collection the first time it is
1097
- * fetched and found to be empty (e.g. for demo data or defaults).
1098
- */
1099
- initialData?: Partial<TDoc>[];
1100
- /**
1101
- * If `true`, every create, update, and delete operation on this collection
1102
- * is logged to the `__audit` collection with before/after snapshots and the
1103
- * acting user's identity.
1104
- */
1105
- audit?: boolean;
1106
- /**
1107
- * Optional state-machine workflow for this collection. Workflow-enabled
1108
- * entries keep an editable working revision and an independent public
1109
- * snapshot, so editing published content never changes the live response.
1110
- */
1111
- workflow?: WorkflowConfig<TDoc>;
1112
- /**
1113
- * Collection-level access control.
1114
- *
1115
- * Each key is an operation; the value is a function (or Jexl string) that
1116
- * returns `true` to allow or `false` to deny. Returning a `where`-style
1117
- * object grants access only to matching documents.
1118
- *
1119
- * @example
1120
- * access: {
1121
- * read: () => true, // public read
1122
- * create: ({ user }) => !!user, // logged-in users only
1123
- * update: ({ user }) => user?.roles?.includes('editor') ?? false,
1124
- * delete: ({ user }) => user?.roles?.includes('admin') ?? false,
1125
- * }
1126
- */
1127
- access?: {
1128
- read?: AccessFunction<TDoc> | string;
1129
- create?: AccessFunction<TDoc> | string;
1130
- update?: AccessFunction<TDoc> | string;
1131
- delete?: AccessFunction<TDoc> | string;
1132
- };
1133
- /**
1134
- * Collection-level lifecycle hooks.
1135
- *
1136
- * Hooks run in the order they appear in the array. The return value of each
1137
- * hook is passed as the input to the next. Throwing inside any hook aborts
1138
- * the operation and returns a `500` error.
1139
- *
1140
- * See the [Hooks reference](/docs/concepts/hooks) for the full lifecycle diagram.
1141
- */
1142
- hooks?: {
1143
- /**
1144
- * Runs before the database is queried. Return a modified `where` object
1145
- * to override the query filter.
1146
- */
1147
- beforeRead?: CollectionBeforeReadHook[];
1148
- /**
1149
- * Runs after documents are fetched. Return a modified doc to change what
1150
- * the client receives. Runs on every document in a list response.
1151
- */
1152
- afterRead?: CollectionAfterReadHook<TDoc>[];
1153
- /**
1154
- * Runs before create or update. Return modified data to change what is
1155
- * written to the database. Throw to abort the write entirely.
1156
- */
1157
- beforeChange?: CollectionBeforeChangeHook<TDoc>[];
1158
- /**
1159
- * Runs after create or update is committed. For side-effects only —
1160
- * webhooks, cache busting, notifications. Return value is ignored.
1161
- *
1162
- * Errors are **isolated**: caught, logged, and discarded so a failing
1163
- * side-effect never turns a successful write into an HTTP 500.
1164
- * See {@link CollectionAfterChangeHook} for the await-vs-fire-and-forget guidance.
1165
- */
1166
- afterChange?: CollectionAfterChangeHook<TDoc>[];
1167
- /**
1168
- * Runs before a document is deleted. Throw to cancel the deletion.
1169
- */
1170
- beforeDelete?: CollectionBeforeDeleteHook<TDoc>[];
1171
- /**
1172
- * Runs after a document has been deleted. For cleanup side-effects only.
1173
- *
1174
- * Errors are **isolated**: caught, logged, and discarded — the deletion is
1175
- * already committed and will not be undone.
1176
- */
1177
- afterDelete?: CollectionAfterDeleteHook<TDoc>[];
1178
- };
1179
- /** Admin UI configuration for this collection. */
1180
- admin?: {
1181
- /**
1182
- * Lucide icon displayed beside this collection in the Admin sidebar.
1183
- * Uses Lucide component names, e.g. `'Newspaper'` or `'ShoppingBag'`.
1184
- */
1185
- icon?: AdminIconName;
1186
- /** Custom component slots for this collection's list view. */
1187
- components?: CollectionListComponentSlots;
1188
- /**
1189
- * The field name used as the document's display title in the Admin list
1190
- * view and breadcrumbs. Defaults to `'title'` if the field exists.
1191
- */
1192
- useAsTitle?: string;
1193
- /**
1194
- * Field names to show as columns in the Admin list view.
1195
- * Defaults to a sensible set of the first few non-structural fields.
1196
- */
1197
- defaultColumns?: string[];
1198
- /**
1199
- * Groups this collection under a named section in the Admin sidebar.
1200
- * Collections with the same `group` are visually grouped together.
1201
- */
1202
- group?: string;
1203
- /** If `true`, this collection is not shown in the Admin UI sidebar. */
1204
- hidden?: boolean;
1205
- /** If `false`, disables the filter UI entirely for this collection. Defaults to `true`. */
1206
- filterable?: boolean;
1207
- /**
1208
- * URL to open in the Live Preview pane when editing a document.
1209
- * Pass a function to derive the URL from the document's fields.
1210
- *
1211
- * @example
1212
- * previewUrl: (doc) => `https://mysite.com/blog/${doc.slug}`
1213
- */
1214
- previewUrl?: string | ((doc: TDoc, opts: {
1215
- locale?: string;
1216
- }) => string | null);
1217
- /**
1218
- * How the Live Preview pane communicates with the frontend.
1219
- * - `'postMessage'` (default) — sends a `postMessage` with the current doc data.
1220
- * - `'token'` — passes a short-lived preview token as a query parameter.
1221
- */
1222
- previewMode?: "postMessage" | "token";
1223
- /**
1224
- * Frontend URL pattern for this collection, used by `url` fields to
1225
- * resolve internal links. Use `{fieldName}` placeholders.
1226
- *
1227
- * @example
1228
- * urlPattern: '/blog/{slug}' // → /blog/my-post
1229
- * urlPattern: '/{slug}' // → /about
1230
- */
1231
- urlPattern?: string;
1232
- };
1233
- }
1234
- /**
1235
- * Upload configuration for collections that store files.
1236
- * Set `upload: true` on the collection to use all defaults, or pass this
1237
- * object to customise allowed types, size limits, and image processing.
1238
- */
1239
- interface UploadConfig {
1240
- /**
1241
- * Allowed MIME types. Requests with other MIME types are rejected with `400`.
1242
- * @example ['image/jpeg', 'image/png', 'image/webp', 'application/pdf']
1243
- */
1244
- allowedMimeTypes?: string[];
1245
- /**
1246
- * Maximum file size in **bytes**.
1247
- * @example 10 * 1024 * 1024 // 10 MB
1248
- */
1249
- maxFileSize?: number;
1250
- /**
1251
- * Local filesystem path where files are stored.
1252
- * Only used by the `LocalStorage` adapter.
1253
- */
1254
- staticDir?: string;
1255
- /**
1256
- * Public URL prefix prepended to filenames when generating download URLs.
1257
- * Only used by the `LocalStorage` adapter.
1258
- * @example '/uploads'
1259
- */
1260
- staticURL?: string;
1261
- /**
1262
- * The `imageSizes` entry name to use as the thumbnail in the Admin media grid.
1263
- * @example 'thumbnail'
1264
- */
1265
- adminThumbnail?: string;
1266
- /**
1267
- * Additional image sizes to generate when an image is uploaded.
1268
- * Requires an `ImageService` to be configured (e.g. `@dyrected/image-sharp`).
1269
- *
1270
- * @example
1271
- * imageSizes: [
1272
- * { name: 'thumbnail', width: 300, height: 300, fit: 'cover' },
1273
- * { name: 'card', width: 800 },
1274
- * ]
1275
- */
1276
- imageSizes?: {
1277
- /** Identifier used to access this size, e.g. `doc.sizes.thumbnail`. */
1278
- name: string;
1279
- /** Target width in pixels. */
1280
- width?: number;
1281
- /** Target height in pixels. */
1282
- height?: number;
1283
- /** sharp crop strategy (`'entropy'`, `'attention'`, etc.). */
1284
- crop?: string;
1285
- /**
1286
- * sharp fit strategy.
1287
- * @see https://sharp.pixelplumbing.com/api-resize#parameters
1288
- */
1289
- fit?: string;
1290
- /**
1291
- * If `true`, images smaller than the target size are not upscaled.
1292
- * @default true
1293
- */
1294
- withoutEnlargement?: boolean;
1295
- /** Additional sharp format options forwarded to the output pipeline. */
1296
- formatOptions?: Record<string, unknown>;
1297
- }[];
1298
- }
1299
- /**
1300
- * Defines a Dyrected global — a singleton document without pagination or IDs.
1301
- *
1302
- * Globals are ideal for site-wide settings, feature flags, or any data where
1303
- * there is always exactly one record (e.g. `site-settings`, `navigation`, `theme`).
1304
- *
1305
- * Pass your document's TypeScript type as the generic parameter `TDoc` to get
1306
- * fully typed hooks:
1307
- *
1308
- * ```ts
1309
- * interface SiteSettings {
1310
- * siteName: string
1311
- * tagline: string
1312
- * maintenanceMode: boolean
1313
- * }
1314
- *
1315
- * export const Settings = defineGlobal<SiteSettings>({
1316
- * slug: 'site-settings',
1317
- * hooks: {
1318
- * afterChange: [({ doc }) => {
1319
- * // `doc` is typed as SiteSettings
1320
- * if (doc.maintenanceMode) alertOnCall()
1321
- * }],
1322
- * },
1323
- * fields: [...],
1324
- * })
1325
- * ```
1326
- *
1327
- * @template TDoc The TypeScript shape of this global's document.
1328
- */
1329
- interface GlobalConfig<TDoc extends object = Record<string, unknown>> {
1330
- /**
1331
- * Unique identifier for this global.
1332
- * Used as the URL segment (`/api/globals/:slug`) and the storage key.
1333
- */
1334
- slug: string;
1335
- /** Restricts this global to a specific site in a multi-tenant deployment. */
1336
- siteId?: string;
1337
- /**
1338
- * If `true`, this global is shared across all sites in a multi-tenant
1339
- * deployment.
1340
- */
1341
- shared?: boolean;
1342
- /** Human-readable label shown in the Admin UI sidebar. */
1343
- label?: string;
1344
- /** Field definitions for this global's document schema. */
1345
- fields: Field[];
1346
- /** Access control for reading and updating this global. */
1347
- access?: {
1348
- read?: AccessFunction<TDoc>;
1349
- update?: AccessFunction<TDoc>;
1350
- };
1351
- /**
1352
- * Global-level lifecycle hooks.
1353
- * Globals support `beforeRead`, `afterRead`, `beforeChange`, and `afterChange`.
1354
- * There are no delete hooks since globals cannot be deleted.
1355
- */
1356
- hooks?: {
1357
- beforeRead?: GlobalBeforeReadHook[];
1358
- afterRead?: GlobalAfterReadHook<TDoc>[];
1359
- beforeChange?: GlobalBeforeChangeHook<TDoc>[];
1360
- afterChange?: GlobalAfterChangeHook<TDoc>[];
1361
- };
1362
- /** Admin UI configuration for this global. */
1363
- admin?: {
1364
- /**
1365
- * Lucide icon displayed beside this global in the Admin sidebar.
1366
- * Uses Lucide component names, e.g. `'Settings2'` or `'Palette'`.
1367
- */
1368
- icon?: AdminIconName;
1369
- /** Groups this global under a named section in the Admin sidebar. */
1370
- group?: string;
1371
- /** If `true`, this global is not shown in the Admin UI sidebar. */
1372
- hidden?: boolean;
1373
- };
1374
- /**
1375
- * Initial data to seed this global with the first time it is fetched and
1376
- * found to be empty.
1377
- */
1378
- initialData?: Partial<TDoc>;
1379
- }
1380
- /**
1381
- * The minimum shape of every document returned by the database layer.
1382
- *
1383
- * All documents have an `id` field assigned by the adapter. Additional fields
1384
- * are stored as `unknown` until you narrow them with your own interface.
1385
- *
1386
- * Use this as the base when declaring your collection document types:
1387
- * ```ts
1388
- * interface Post extends BaseDocument {
1389
- * title: string
1390
- * slug: string
1391
- * }
1392
- * ```
1393
- */
1394
- interface BaseDocument {
1395
- /** The document's unique identifier, assigned by the database adapter. */
1396
- id: string;
1397
- [key: string]: any;
1398
- }
1399
- /**
1400
- * The envelope returned by collection list endpoints (`GET /api/collections/:slug`).
1401
- *
1402
- * @template T The document type.
1403
- */
1404
- interface PaginatedResult<T = Record<string, any>> {
1405
- /** The documents on the current page. */
1406
- docs: T[];
1407
- /** Total number of documents matching the query (across all pages). */
1408
- total: number;
1409
- /** Maximum number of documents per page as requested. */
1410
- limit: number;
1411
- /** The current page number (1-indexed). */
1412
- page: number;
1413
- /** Total number of pages given the current `limit`. */
1414
- totalPages: number;
1415
- /** Whether a next page exists. */
1416
- hasNextPage: boolean;
1417
- /** Whether a previous page exists. */
1418
- hasPrevPage: boolean;
1419
- }
1420
- /**
1421
- * The interface every database adapter must implement.
1422
- *
1423
- * Dyrected ships adapters for PostgreSQL, MySQL, SQLite, and MongoDB.
1424
- * Implement this interface to connect any other database.
1425
- */
1426
- interface DatabaseAdapter {
1427
- /** Find a paginated list of documents in a collection. */
1428
- find(args: {
1429
- collection: string;
1430
- where?: Record<string, unknown>;
1431
- limit?: number;
1432
- page?: number;
1433
- sort?: string;
1434
- }): Promise<PaginatedResult>;
1435
- /** Find a single document by its ID. Returns `null` if not found. */
1436
- findOne(args: {
1437
- collection: string;
1438
- id: string;
1439
- }): Promise<BaseDocument | null>;
1440
- /** Insert a new document and return it with its generated `id`. */
1441
- create(args: {
1442
- collection: string;
1443
- data: Record<string, unknown>;
1444
- }): Promise<BaseDocument>;
1445
- /** Update a document by ID and return the updated document. */
1446
- update(args: {
1447
- collection: string;
1448
- id: string;
1449
- data: Record<string, unknown>;
1450
- }): Promise<BaseDocument>;
1451
- /** Delete a document by ID. Return value is intentionally untyped — callers do not use it. */
1452
- delete(args: {
1453
- collection: string;
1454
- id: string;
1455
- }): Promise<unknown>;
1456
- /** Fetch the singleton document for a global. Returns an empty object if not yet initialised. */
1457
- getGlobal(args: {
1458
- slug: string;
1459
- }): Promise<Record<string, unknown>>;
1460
- /** Create or replace the singleton document for a global. */
1461
- updateGlobal(args: {
1462
- slug: string;
1463
- data: Record<string, unknown>;
1464
- }): Promise<Record<string, unknown>>;
1465
- /**
1466
- * Sync the database schema with the current collection and global configs.
1467
- * Called on startup to create tables/collections that don't exist yet.
1468
- * Not all adapters implement this (e.g. MongoDB is schema-less).
1469
- */
1470
- sync?(collections: CollectionConfig[], globals: GlobalConfig[]): Promise<void>;
1471
- /**
1472
- * Execute a raw SQL query or database command.
1473
- * Optional — not all adapters support raw access.
1474
- */
1475
- execute?(query: string, params?: unknown[]): Promise<unknown>;
1476
- /**
1477
- * Run all adapter operations in `callback` as one atomic transaction.
1478
- * Shipped adapters implement this; workflow transitions require it.
1479
- */
1480
- transaction?<T>(callback: (db: DatabaseAdapter) => Promise<T>): Promise<T>;
1481
- }
1482
- /**
1483
- * Read-only view of the database adapter. Exposes only `find`, `findOne`,
1484
- * and `getGlobal` — no write operations.
1485
- *
1486
- * Passed to `beforeChange`, `beforeDelete`, `beforeRead`, `afterRead`,
1487
- * and field-level hooks. Write operations are available in `afterChange`
1488
- * and `afterDelete` hooks where the full {@link DatabaseAdapter} is provided.
1489
- */
1490
- type ReadonlyDatabaseAdapter = Pick<DatabaseAdapter, "find" | "findOne" | "getGlobal">;
1491
- /**
1492
- * Metadata returned after a file is uploaded and stored.
1493
- * Stored on the document in upload collections.
1494
- */
1495
- interface FileData {
1496
- filename: string;
1497
- filesize?: number;
1498
- mimeType: string;
1499
- /** Public URL of the stored file. */
1500
- url: string;
1501
- width?: number;
1502
- height?: number;
1503
- focalPoint?: {
1504
- x: number;
1505
- y: number;
1506
- };
1507
- /** Base64-encoded BlurHash string for progressive image loading. */
1508
- blurhash?: string;
1509
- /** `'upload'` for server-stored files; `'external'` for provider-managed files. */
1510
- type?: "upload" | "external";
1511
- provider?: string;
1512
- provider_metadata?: unknown;
1513
- [key: string]: unknown;
1514
- }
1515
- /**
1516
- * The interface every storage adapter must implement.
1517
- *
1518
- * Dyrected ships adapters for local disk, S3, Cloudflare R2, Cloudinary, and
1519
- * Backblaze B2. Implement this interface to use any other storage provider.
1520
- */
1521
- interface StorageAdapter {
1522
- /**
1523
- * Upload a file and return its metadata (URL, dimensions, etc.).
1524
- * The `prefix` is a path prefix used for multi-tenant setups.
1525
- */
1526
- upload(args: {
1527
- filename: string;
1528
- buffer: Uint8Array;
1529
- mimeType: string;
1530
- prefix?: string;
1531
- }): Promise<FileData>;
1532
- /** Delete a file by its stored filename. */
1533
- delete(args: {
1534
- filename: string;
1535
- }): Promise<void>;
1536
- /** Return the public URL for a stored file. */
1537
- getURL(args: {
1538
- filename: string;
1539
- }): string;
1540
- /**
1541
- * Retrieve the file's raw bytes and MIME type for serving via the API.
1542
- * Only needed by adapters that serve files through the Dyrected API
1543
- * (e.g. `LocalStorage`). Cloud adapters return `null` here and rely on
1544
- * direct CDN URLs instead.
1545
- */
1546
- resolve?(args: {
1547
- filename: string;
1548
- }): Promise<{
1549
- buffer: Uint8Array;
1550
- mimeType: string;
1551
- } | null>;
1552
- }
1553
- /**
1554
- * Processes uploaded images — generates metadata (dimensions, BlurHash) and
1555
- * produces resized variants defined in `UploadConfig.imageSizes`.
1556
- *
1557
- * @example
1558
- * import { SharpImageService } from '@dyrected/image-sharp'
1559
- * defineConfig({ image: new SharpImageService(), ... })
1560
- */
1561
- interface ImageService {
1562
- process(args: {
1563
- buffer: Uint8Array;
1564
- mimeType: string;
1565
- config?: CollectionConfig["upload"];
1566
- focalPoint?: {
1567
- x: number;
1568
- y: number;
1569
- };
1570
- }): Promise<{
1571
- metadata: {
1572
- width?: number;
1573
- height?: number;
1574
- /** Base64-encoded BlurHash for progressive loading. */
1575
- blurhash?: string;
1576
- };
1577
- /** Generated image sizes keyed by their `name`. */
1578
- sizes?: Record<string, {
1579
- buffer: Uint8Array;
1580
- width: number;
1581
- height: number;
1582
- filename: string;
1583
- }>;
1584
- }>;
1585
- }
1586
- /**
1587
- * Branding and metadata options for the Dyrected Admin UI.
1588
- *
1589
- * @example
1590
- * admin: {
1591
- * branding: {
1592
- * logo: '/logo.svg',
1593
- * primaryColor: '#6366f1',
1594
- * },
1595
- * meta: {
1596
- * titleSuffix: '- My App',
1597
- * },
1598
- * }
1599
- */
1600
- interface AdminConfig {
1601
- /** Custom component slots around the built-in dashboard. */
1602
- components?: AdminDashboardComponentSlots;
1603
- branding?: {
1604
- /** Full logo image shown in the expanded sidebar. URL or imported image asset. */
1605
- logo?: string;
1606
- /** Compact logo mark used in the collapsed sidebar state. */
1607
- logoMark?: string;
1608
- /** Text alternative or addition to the logo image. */
1609
- logoText?: string;
1610
- /**
1611
- * Primary accent colour as any CSS colour value.
1612
- * @example '#6366f1'
1613
- * @example 'hsl(240 50% 60%)'
1614
- */
1615
- primaryColor?: string;
1616
- /** Browser tab favicon URL. */
1617
- favicon?: string;
1618
- /** Font family for body and UI text. Must be loaded separately. */
1619
- fontSans?: string;
1620
- /** Font family for headings. Must be loaded separately. */
1621
- fontSerif?: string;
1622
- };
1623
- meta?: {
1624
- /**
1625
- * String appended to every Admin page's `<title>`.
1626
- * @default '- Dyrected'
1627
- */
1628
- titleSuffix?: string;
1629
- };
1630
- }
1631
- /**
1632
- * Collapses intersection types into a flat object type for readable IDE tooltips.
1633
- *
1634
- * @example
1635
- * type T = Prettify<{ a: string } & { b: number }>
1636
- * // → { a: string; b: number }
1637
- */
1638
- type Prettify<T> = {
1639
- [K in keyof T]: T[K];
1640
- };
1641
- /** Maps a field's runtime type tag to its TypeScript value type. @internal */
1642
- type FieldValueType<F extends Field> = F["type"] extends "text" | "textarea" | "email" | "url" | "icon" | "date" | "datetime" | "time" | "select" | "radio" ? string : F["type"] extends "number" ? number : F["type"] extends "boolean" ? boolean : F["type"] extends "multiSelect" ? string[] : F["type"] extends "relationship" ? F extends {
1643
- hasMany: true;
1644
- } ? string[] : string : F["type"] extends "image" ? F extends {
1645
- hasMany: true;
1646
- } ? string[] : string : F["type"] extends "richText" | "json" ? Record<string, unknown> : F["type"] extends "object" ? F extends {
1647
- fields: infer SF extends readonly Field[];
1648
- } ? Prettify<InferDocShape<SF>> : Record<string, unknown> : F["type"] extends "array" ? F extends {
1649
- fields: infer SF extends readonly Field[];
1650
- } ? Array<Prettify<InferDocShape<SF>>> : unknown[] : F["type"] extends "blocks" ? F extends {
1651
- blocks: infer B extends readonly Block[];
1652
- } ? Array<InferBlocksUnion<B>> : Array<{
1653
- blockType: string;
1654
- } & Record<string, unknown>> : unknown;
1655
- /** Build a discriminated-union type covering all possible block shapes. @internal */
1656
- type InferBlocksUnion<Blocks extends readonly Block[]> = Blocks extends readonly [
1657
- infer B extends Block,
1658
- ...infer Rest extends readonly Block[]
1659
- ] ? B["fields"] extends readonly Field[] ? ({
1660
- blockType: B["slug"];
1661
- } & Prettify<InferDocShape<B["fields"]>>) | InferBlocksUnion<Rest> : ({
1662
- blockType: B["slug"];
1663
- } & Record<string, unknown>) | InferBlocksUnion<Rest> : never;
1664
- /** Converts one field definition to its corresponding document key/value pair. @internal */
1665
- type InferFieldEntry<F extends Field> = F extends {
1666
- type: "row";
1667
- fields: infer SF extends readonly Field[];
1668
- } ? InferDocShape<SF> : F extends {
1669
- type: "join";
1670
- } ? Record<never, never> : F extends {
1671
- name: infer N extends string;
1672
- required: true;
1673
- } ? {
1674
- [K in N]: FieldValueType<F>;
1675
- } : F extends {
1676
- name: infer N extends string;
1677
- } ? {
1678
- [K in N]?: FieldValueType<F>;
1679
- } : Record<never, never>;
1680
- /**
1681
- * Infer a document shape from a literal fields tuple.
1682
- *
1683
- * This is used automatically by {@link defineCollection} and {@link defineGlobal}
1684
- * when no explicit `TDoc` type argument is provided — TypeScript derives the
1685
- * document type straight from the `fields` array you write inline.
1686
- *
1687
- * For collections, the inferred type is wrapped with `{ id: string }` (the
1688
- * database adapter always stamps an `id` on created documents). Globals don't
1689
- * get an `id`.
1690
- *
1691
- * @example
1692
- * const fields = [
1693
- * { name: 'title', type: 'text', required: true },
1694
- * { name: 'published', type: 'boolean' },
1695
- * ] as const satisfies Field[];
1696
- *
1697
- * type Doc = InferDocShape<typeof fields>;
1698
- * // → { title: string; published?: boolean }
1699
- */
1700
- type InferDocShape<Fields extends readonly Field[]> = Fields extends readonly [] ? Record<never, never> : Fields extends readonly [
1701
- infer Head extends Field,
1702
- ...infer Tail extends readonly Field[]
1703
- ] ? InferFieldEntry<Head> & InferDocShape<Tail> : Record<string, unknown>;
1704
- /**
1705
- * Audit fields automatically injected into every collection document at runtime.
1706
- * They are always present in API responses but hidden in the Admin UI by default.
1707
- */
1708
- type SystemDocFields = {
1709
- createdAt?: string;
1710
- updatedAt?: string;
1711
- /** ID of the user who created the document. */
1712
- createdBy?: string;
1713
- /** ID of the user who last updated the document. */
1714
- updatedBy?: string;
1715
- };
1716
- /**
1717
- * Fields automatically injected into collections with `auth: true`.
1718
- * `password` is part of the schema but is stripped from API read responses.
1719
- */
1720
- type AuthDocFields = {
1721
- email: string;
1722
- password?: string;
1723
- roles?: string[];
1724
- };
1725
- /**
1726
- * Fields automatically added to upload/media collection documents.
1727
- * These mirror the `FileData` interface returned by storage adapters.
1728
- */
1729
- type UploadDocFields = {
1730
- filename: string;
1731
- filesize?: number;
1732
- mimeType: string;
1733
- /** Public URL of the stored file. */
1734
- url: string;
1735
- width?: number;
1736
- height?: number;
1737
- focalPoint?: {
1738
- x: number;
1739
- y: number;
1740
- };
1741
- /** Base64 BlurHash for progressive image loading. */
1742
- blurhash?: string;
1743
- sizes?: Record<string, {
1744
- filename?: string;
1745
- url?: string;
1746
- width?: number;
1747
- height?: number;
1748
- }>;
1749
- };
1750
- /**
1751
- * The root configuration object passed to `createDyrectedApp`.
1752
- *
1753
- * This is the single source of truth for your entire Dyrected instance —
1754
- * collections, globals, database adapter, storage, email, and more.
1755
- *
1756
- * @example
1757
- * import { defineConfig } from '@dyrected/core'
1758
- * import { SQLiteAdapter } from '@dyrected/db-sqlite'
1759
- *
1760
- * export default defineConfig({
1761
- * db: new SQLiteAdapter({ filename: './db.sqlite' }),
1762
- * collections: [Posts, Users],
1763
- * globals: [SiteSettings],
1764
- * })
1765
- */
1766
- interface DyrectedConfig {
1767
- /** Collection definitions. Each collection maps to a database table/collection. */
1768
- collections: CollectionConfig<any>[];
1769
- /** Global (singleton) definitions. Each global maps to a single document. */
1770
- globals: GlobalConfig<any>[];
1771
- /**
1772
- * The database adapter. Required for all data operations.
1773
- * @see {@link DatabaseAdapter}
1774
- */
1775
- db?: DatabaseAdapter;
1776
- /**
1777
- * The storage adapter for file uploads.
1778
- * Required when any collection has `upload: true`.
1779
- * @see {@link StorageAdapter}
1780
- */
1781
- storage?: StorageAdapter;
1782
- /**
1783
- * The image processing service. Required when any upload collection
1784
- * defines `imageSizes`.
1785
- * @see {@link ImageService}
1786
- */
1787
- image?: ImageService;
1788
- /** Admin UI branding and metadata. */
1789
- admin?: AdminConfig;
1790
- /**
1791
- * Email transport configuration. Required for welcome emails, password
1792
- * resets, and invite links.
1793
- *
1794
- * @example
1795
- * email: {
1796
- * from: 'no-reply@myapp.com',
1797
- * send: async ({ to, subject, html }) => {
1798
- * await resend.emails.send({ from, to, subject, html })
1799
- * },
1800
- * }
1801
- */
1802
- email?: {
1803
- /** The `From` address for all outbound emails. */
1804
- from: string;
1805
- /** The send function. Wire in any email provider (Resend, SendGrid, SES, etc.). */
1806
- send: (args: {
1807
- to: string;
1808
- subject: string;
1809
- html: string;
1810
- }) => Promise<void>;
1811
- /** Override the default email templates. */
1812
- templates?: {
1813
- welcome?: (args: {
1814
- email: string;
1815
- }) => {
1816
- subject?: string;
1817
- html: string;
1818
- };
1819
- invite?: (args: {
1820
- token: string;
1821
- invitedByEmail?: string;
1822
- }) => {
1823
- subject?: string;
1824
- html: string;
1825
- };
1826
- resetPassword?: (args: {
1827
- token: string;
1828
- url?: string;
1829
- }) => {
1830
- subject?: string;
1831
- html: string;
1832
- };
1833
- passwordChanged?: (args: {
1834
- email: string;
1835
- }) => {
1836
- subject?: string;
1837
- html: string;
1838
- };
1839
- };
1840
- };
1841
- /**
1842
- * Redis connection URL. Required for distributed caching of dynamic option
1843
- * resolvers and other server-side caches in multi-instance deployments.
1844
- *
1845
- * @example
1846
- * redis: { url: process.env.REDIS_URL }
1847
- */
1848
- redis?: {
1849
- url: string;
1850
- };
1851
- /** Durable lifecycle-event delivery configuration. */
1852
- events?: {
1853
- handlers: LifecycleEventHandler[];
1854
- /** Maximum delivery attempts before an event remains failed. Defaults to 8. */
1855
- maxAttempts?: number;
1856
- /** Initial exponential-backoff delay in milliseconds. Defaults to 1000. */
1857
- retryDelayMs?: number;
1858
- };
1859
- /**
1860
- * Cross-Origin Resource Sharing (CORS) configuration.
1861
- * List all origins that are allowed to call the Dyrected API.
1862
- *
1863
- * @example
1864
- * cors: { origins: ['https://myapp.com', 'https://www.myapp.com'] }
1865
- */
1866
- cors?: {
1867
- origins: string[];
1868
- };
1869
- /**
1870
- * Callback to dynamically fetch additional collections and globals for a
1871
- * given site ID at request time. Used in multi-tenant deployments where each
1872
- * site has its own schema stored in the database.
1873
- *
1874
- * @param siteId The `X-Site-Id` header value from the incoming request.
1875
- * @returns Extra collections and globals to merge into the config for this request.
1876
- *
1877
- * @example
1878
- * onSchemaFetch: async (siteId) => {
1879
- * const site = await db.findOne({ collection: 'sites', id: siteId })
1880
- * return buildSchemaFromSiteConfig(site)
1881
- * }
1882
- */
1883
- onSchemaFetch?: (siteId: string) => Promise<{
1884
- collections?: CollectionConfig<any>[];
1885
- globals?: GlobalConfig<any>[];
1886
- }>;
1887
- }
1888
-
1889
- export { type WorkflowRole as $, type AuthenticatedUser as A, type BaseDocument as B, type CollectionConfig as C, type DyrectedConfig as D, type FileData as E, type Field as F, type GlobalConfig as G, type HookRequestContext as H, type InferDocShape as I, type GlobalAfterChangeHook as J, type GlobalAfterReadHook as K, type LifecycleEventName as L, type GlobalBeforeChangeHook as M, type GlobalBeforeReadHook as N, type HookFunction as O, type Prettify as P, type ImageService as Q, LIFECYCLE_EVENT_NAMES as R, type SystemDocFields as S, type LifecycleEventHandler as T, type UploadDocFields as U, type PaginatedResult as V, type WorkflowConfig as W, type ReadonlyDatabaseAdapter as X, type StorageAdapter as Y, type UploadConfig as Z, type WorkflowMetadata as _, type WorkflowTransition as a, type WorkflowState as a0, type WorkflowTransitionContext as a1, type LifecycleEvent as b, type AuthDocFields as c, type AccessFunction as d, type AdminConfig as e, type AdminDashboardComponentSlots as f, type AdminIconName as g, type Block as h, type CollectionAfterChangeHook as i, type CollectionAfterDeleteHook as j, type CollectionAfterReadHook as k, type CollectionAfterTransitionHook as l, type CollectionBeforeChangeHook as m, type CollectionBeforeDeleteHook as n, type CollectionBeforeReadHook as o, type CollectionBeforeTransitionHook as p, type CollectionListComponentSlots as q, type DatabaseAdapter as r, type DynamicOptionItem as s, type DynamicOptionsConfig as t, type DynamicOptionsResolver as u, type DynamicOptionsResolverArgs as v, type FieldAfterReadHook as w, type FieldBeforeChangeHook as x, type FieldHook as y, type FieldType as z };