@kyro-cms/core 0.9.0 → 0.9.2

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 (265) hide show
  1. package/README.md +55 -593
  2. package/dist/{WebhookService-AefJfqX0.d.cts → WebhookService-BKszZlG0.d.cts} +1 -1
  3. package/dist/{WebhookService-118ZTFis.d.ts → WebhookService-Ccf1j-IN.d.ts} +1 -1
  4. package/dist/api-handler-graphql.cjs +44 -0
  5. package/dist/api-handler-graphql.cjs.map +1 -0
  6. package/dist/api-handler-graphql.d.cts +6 -0
  7. package/dist/api-handler-graphql.d.ts +6 -0
  8. package/dist/api-handler-graphql.js +41 -0
  9. package/dist/api-handler-graphql.js.map +1 -0
  10. package/dist/api-handler-trpc.cjs +38 -0
  11. package/dist/api-handler-trpc.cjs.map +1 -0
  12. package/dist/api-handler-trpc.d.cts +5 -0
  13. package/dist/api-handler-trpc.d.ts +5 -0
  14. package/dist/api-handler-trpc.js +36 -0
  15. package/dist/api-handler-trpc.js.map +1 -0
  16. package/dist/api-handler.cjs +33 -99
  17. package/dist/api-handler.cjs.map +1 -1
  18. package/dist/api-handler.d.cts +2 -1
  19. package/dist/api-handler.d.ts +2 -1
  20. package/dist/api-handler.js +21 -97
  21. package/dist/api-handler.js.map +1 -1
  22. package/dist/{tenant-B1YB0Jy8.d.ts → base-CIuXkrH4.d.cts} +7 -15
  23. package/dist/{tenant-Cpeveji6.d.cts → base-fFo4lqER.d.ts} +7 -15
  24. package/dist/bootstrap-3PV3GJ3S.js +7 -0
  25. package/dist/{bootstrap-JCML6NFO.js.map → bootstrap-3PV3GJ3S.js.map} +1 -1
  26. package/dist/bootstrap-4CELFLJO.cjs +32 -0
  27. package/dist/{bootstrap-AKAUP6F6.cjs.map → bootstrap-4CELFLJO.cjs.map} +1 -1
  28. package/dist/{chunk-VJT6P4N6.cjs → chunk-3HR772HI.cjs} +199 -32
  29. package/dist/chunk-3HR772HI.cjs.map +1 -0
  30. package/dist/chunk-3KTWGODI.cjs +178 -0
  31. package/dist/chunk-3KTWGODI.cjs.map +1 -0
  32. package/dist/{chunk-QXIQWPAP.js → chunk-3UK5XBVJ.js} +4 -134
  33. package/dist/chunk-3UK5XBVJ.js.map +1 -0
  34. package/dist/{chunk-FXYP2HA6.js → chunk-4AO3A3JM.js} +48 -4
  35. package/dist/chunk-4AO3A3JM.js.map +1 -0
  36. package/dist/{chunk-Z6ZWNWWR.js → chunk-4CV4JOE5.js} +3 -9
  37. package/dist/{chunk-Z6ZWNWWR.js.map → chunk-4CV4JOE5.js.map} +1 -1
  38. package/dist/chunk-4M7X5HAB.cjs +173 -0
  39. package/dist/chunk-4M7X5HAB.cjs.map +1 -0
  40. package/dist/chunk-53NYVYVX.js +3243 -0
  41. package/dist/chunk-53NYVYVX.js.map +1 -0
  42. package/dist/{chunk-35U3FROB.js → chunk-5H3MWQJS.js} +714 -184
  43. package/dist/chunk-5H3MWQJS.js.map +1 -0
  44. package/dist/{chunk-YVUJBEXE.cjs → chunk-5PMQQFRE.cjs} +16 -7
  45. package/dist/chunk-5PMQQFRE.cjs.map +1 -0
  46. package/dist/{chunk-57P6MJKC.js → chunk-6UNONDW7.js} +94 -10
  47. package/dist/chunk-6UNONDW7.js.map +1 -0
  48. package/dist/{chunk-Y3N7UUDO.js → chunk-7OGPN7MP.js} +5 -2
  49. package/dist/chunk-7OGPN7MP.js.map +1 -0
  50. package/dist/{chunk-2OL4O2TH.cjs → chunk-7OS7TX2Q.cjs} +68 -62
  51. package/dist/chunk-7OS7TX2Q.cjs.map +1 -0
  52. package/dist/{chunk-3TPQ2BU6.js → chunk-BYBMTIMT.js} +2 -6
  53. package/dist/chunk-BYBMTIMT.js.map +1 -0
  54. package/dist/{chunk-ES5HNFFT.js → chunk-CF7OL6HR.js} +4 -2
  55. package/dist/chunk-CF7OL6HR.js.map +1 -0
  56. package/dist/chunk-CJONKRHJ.js +162 -0
  57. package/dist/chunk-CJONKRHJ.js.map +1 -0
  58. package/dist/{chunk-OHVB4AJ7.js → chunk-CJX74IYK.js} +24 -18
  59. package/dist/chunk-CJX74IYK.js.map +1 -0
  60. package/dist/{chunk-5KVM3WEY.cjs → chunk-CNKT4PME.cjs} +1592 -868
  61. package/dist/chunk-CNKT4PME.cjs.map +1 -0
  62. package/dist/{chunk-G7VZBCD6.cjs → chunk-CZLDE2OZ.cjs} +2 -9
  63. package/dist/{chunk-G7VZBCD6.cjs.map → chunk-CZLDE2OZ.cjs.map} +1 -1
  64. package/dist/{chunk-WQBRWOQT.cjs → chunk-DPA3KWPY.cjs} +4 -3
  65. package/dist/chunk-DPA3KWPY.cjs.map +1 -0
  66. package/dist/{chunk-LINKCEG4.cjs → chunk-E2763JUP.cjs} +726 -196
  67. package/dist/chunk-E2763JUP.cjs.map +1 -0
  68. package/dist/chunk-E5UJBLQ7.js +220 -0
  69. package/dist/chunk-E5UJBLQ7.js.map +1 -0
  70. package/dist/{chunk-DVD5P72E.cjs → chunk-EEJUFDMF.cjs} +2 -6
  71. package/dist/chunk-EEJUFDMF.cjs.map +1 -0
  72. package/dist/chunk-FSKONGCX.cjs +253 -0
  73. package/dist/chunk-FSKONGCX.cjs.map +1 -0
  74. package/dist/{chunk-Y3QQN7PN.js → chunk-GAAHG2Z4.js} +13 -4
  75. package/dist/chunk-GAAHG2Z4.js.map +1 -0
  76. package/dist/chunk-GAOXD3XT.js +175 -0
  77. package/dist/chunk-GAOXD3XT.js.map +1 -0
  78. package/dist/{chunk-SA7NSSIQ.cjs → chunk-GUUB5EAG.cjs} +13 -187
  79. package/dist/chunk-GUUB5EAG.cjs.map +1 -0
  80. package/dist/{chunk-4DA7QPLA.cjs → chunk-GXFOGU7N.cjs} +5 -2
  81. package/dist/chunk-GXFOGU7N.cjs.map +1 -0
  82. package/dist/{chunk-I7HHI6QV.cjs → chunk-IDVRRRAK.cjs} +17 -9
  83. package/dist/chunk-IDVRRRAK.cjs.map +1 -0
  84. package/dist/{chunk-HXRD4B37.js → chunk-IPTZM3VE.js} +1423 -704
  85. package/dist/chunk-IPTZM3VE.js.map +1 -0
  86. package/dist/chunk-KC2GDBLS.cjs +84 -0
  87. package/dist/chunk-KC2GDBLS.cjs.map +1 -0
  88. package/dist/{chunk-QUW2RZTM.cjs → chunk-L46ROHUS.cjs} +51 -7
  89. package/dist/chunk-L46ROHUS.cjs.map +1 -0
  90. package/dist/chunk-L4EZKIEX.js +185 -0
  91. package/dist/chunk-L4EZKIEX.js.map +1 -0
  92. package/dist/{chunk-REK7AYOC.js → chunk-L5UKKZQN.js} +199 -32
  93. package/dist/chunk-L5UKKZQN.js.map +1 -0
  94. package/dist/chunk-NKPKR5BW.cjs +188 -0
  95. package/dist/chunk-NKPKR5BW.cjs.map +1 -0
  96. package/dist/chunk-NWUEVLQT.cjs +99 -0
  97. package/dist/chunk-NWUEVLQT.cjs.map +1 -0
  98. package/dist/{chunk-3AJE4SEG.js → chunk-OHC6UHFY.js} +208 -76
  99. package/dist/chunk-OHC6UHFY.js.map +1 -0
  100. package/dist/chunk-PHJRNPHY.cjs +3291 -0
  101. package/dist/chunk-PHJRNPHY.cjs.map +1 -0
  102. package/dist/{chunk-DXHRBMGB.js → chunk-PQ72Z6WC.js} +67 -112
  103. package/dist/chunk-PQ72Z6WC.js.map +1 -0
  104. package/dist/{chunk-K7JPTH3G.cjs → chunk-PV2I2KMI.cjs} +214 -82
  105. package/dist/chunk-PV2I2KMI.cjs.map +1 -0
  106. package/dist/{chunk-PDYFVNUX.cjs → chunk-Q23GAMLE.cjs} +71 -116
  107. package/dist/chunk-Q23GAMLE.cjs.map +1 -0
  108. package/dist/{chunk-H727JIG7.js → chunk-Q72BOAPK.js} +16 -8
  109. package/dist/chunk-Q72BOAPK.js.map +1 -0
  110. package/dist/{chunk-IBG6V56E.cjs → chunk-QFLB4EIJ.cjs} +2 -139
  111. package/dist/chunk-QFLB4EIJ.cjs.map +1 -0
  112. package/dist/{chunk-2KVHZE6O.cjs → chunk-RFFSZSCL.cjs} +282 -190
  113. package/dist/chunk-RFFSZSCL.cjs.map +1 -0
  114. package/dist/{chunk-V3LKPM3O.cjs → chunk-SHTTJMLT.cjs} +4 -2
  115. package/dist/chunk-SHTTJMLT.cjs.map +1 -0
  116. package/dist/{chunk-WOWUL7ZY.js → chunk-UUDTPZX6.js} +5 -4
  117. package/dist/chunk-UUDTPZX6.js.map +1 -0
  118. package/dist/{chunk-QPPDLRNR.js → chunk-V7KZQIZ6.js} +277 -185
  119. package/dist/chunk-V7KZQIZ6.js.map +1 -0
  120. package/dist/{chunk-3ZFYL34R.js → chunk-WXVB364T.js} +12 -185
  121. package/dist/chunk-WXVB364T.js.map +1 -0
  122. package/dist/chunk-XEB7PH2E.js +81 -0
  123. package/dist/chunk-XEB7PH2E.js.map +1 -0
  124. package/dist/{chunk-IA6AU5PI.cjs → chunk-Y7AQK4R4.cjs} +94 -10
  125. package/dist/chunk-Y7AQK4R4.cjs.map +1 -0
  126. package/dist/chunk-YFAVQQTU.js +92 -0
  127. package/dist/chunk-YFAVQQTU.js.map +1 -0
  128. package/dist/cli/index.cjs +6 -6
  129. package/dist/cli/index.cjs.map +1 -1
  130. package/dist/cli/index.js +6 -6
  131. package/dist/cli/index.js.map +1 -1
  132. package/dist/client.cjs +4 -4
  133. package/dist/client.d.cts +3 -3
  134. package/dist/client.d.ts +3 -3
  135. package/dist/client.js +2 -2
  136. package/dist/drizzle/index.cjs +15 -14
  137. package/dist/drizzle/index.d.cts +10 -14
  138. package/dist/drizzle/index.d.ts +10 -14
  139. package/dist/drizzle/index.js +6 -5
  140. package/dist/fields/index.cjs +22 -38
  141. package/dist/fields/index.d.cts +2 -22
  142. package/dist/fields/index.d.ts +2 -22
  143. package/dist/fields/index.js +2 -2
  144. package/dist/graphql/index.cjs +6 -5
  145. package/dist/graphql/index.d.cts +5 -3
  146. package/dist/graphql/index.d.ts +5 -3
  147. package/dist/graphql/index.js +4 -3
  148. package/dist/index-BKta3cBH.d.cts +277 -0
  149. package/dist/index-ClOqnkTO.d.ts +277 -0
  150. package/dist/index.cjs +310 -168
  151. package/dist/index.cjs.map +1 -1
  152. package/dist/index.d.cts +130 -211
  153. package/dist/index.d.ts +130 -211
  154. package/dist/index.js +174 -35
  155. package/dist/index.js.map +1 -1
  156. package/dist/integration.cjs +3 -3
  157. package/dist/integration.js +2 -2
  158. package/dist/media-7WDX4BDJ.js +4 -0
  159. package/dist/{media-GPPTZ43E.js.map → media-7WDX4BDJ.js.map} +1 -1
  160. package/dist/{media-XNTUFJZR.cjs → media-TUSLVRQ6.cjs} +3 -3
  161. package/dist/{media-XNTUFJZR.cjs.map → media-TUSLVRQ6.cjs.map} +1 -1
  162. package/dist/mongo-auth-adapter-GT4S7SCU.cjs +17 -0
  163. package/dist/{mongo-auth-adapter-NHHUJHVH.cjs.map → mongo-auth-adapter-GT4S7SCU.cjs.map} +1 -1
  164. package/dist/mongo-auth-adapter-M7VV4LNB.js +4 -0
  165. package/dist/{mongo-auth-adapter-NJQUUCTP.js.map → mongo-auth-adapter-M7VV4LNB.js.map} +1 -1
  166. package/dist/mongodb/index.cjs +9 -8
  167. package/dist/mongodb/index.d.cts +6 -13
  168. package/dist/mongodb/index.d.ts +6 -13
  169. package/dist/mongodb/index.js +5 -4
  170. package/dist/postgres-auth-adapter-AFAPISH7.js +5 -0
  171. package/dist/{postgres-auth-adapter-3T2NKTSE.js.map → postgres-auth-adapter-AFAPISH7.js.map} +1 -1
  172. package/dist/postgres-auth-adapter-SFDTLONT.cjs +14 -0
  173. package/dist/{postgres-auth-adapter-7IEENCKQ.cjs.map → postgres-auth-adapter-SFDTLONT.cjs.map} +1 -1
  174. package/dist/redis-adapter-UQX4EE3B.cjs +13 -0
  175. package/dist/{redis-adapter-D2E2S3GB.cjs.map → redis-adapter-UQX4EE3B.cjs.map} +1 -1
  176. package/dist/redis-adapter-XALOGWY3.js +4 -0
  177. package/dist/{redis-adapter-VQXD7ESY.js.map → redis-adapter-XALOGWY3.js.map} +1 -1
  178. package/dist/rest/index.cjs +16 -15
  179. package/dist/rest/index.d.cts +4 -4
  180. package/dist/rest/index.d.ts +4 -4
  181. package/dist/rest/index.js +14 -13
  182. package/dist/{schema-37SE2F4B.cjs → schema-6QL3USNB.cjs} +15 -15
  183. package/dist/{schema-37SE2F4B.cjs.map → schema-6QL3USNB.cjs.map} +1 -1
  184. package/dist/{schema-5PHL5IVB.js → schema-FNNWEAAW.js} +4 -4
  185. package/dist/{schema-5PHL5IVB.js.map → schema-FNNWEAAW.js.map} +1 -1
  186. package/dist/sqlite-adapter-AQB5TCGV.cjs +13 -0
  187. package/dist/{sqlite-adapter-LVK5PS4T.cjs.map → sqlite-adapter-AQB5TCGV.cjs.map} +1 -1
  188. package/dist/sqlite-adapter-N5H6IM2X.js +4 -0
  189. package/dist/{sqlite-adapter-TR3U3W6Q.js.map → sqlite-adapter-N5H6IM2X.js.map} +1 -1
  190. package/dist/templates/index.cjs +134 -32
  191. package/dist/templates/index.d.cts +52 -9
  192. package/dist/templates/index.d.ts +52 -9
  193. package/dist/templates/index.js +4 -2
  194. package/dist/trpc/index.cjs +14 -13
  195. package/dist/trpc/index.d.cts +55 -49
  196. package/dist/trpc/index.d.ts +55 -49
  197. package/dist/trpc/index.js +5 -4
  198. package/dist/{types-D6ZLRGbH.d.cts → types-CpjuXbe7.d.cts} +2 -0
  199. package/dist/{types-D6ZLRGbH.d.ts → types-CpjuXbe7.d.ts} +2 -0
  200. package/dist/{types-VtjUxIMp.d.cts → types-DeSApf9T.d.cts} +36 -14
  201. package/dist/{types-VtjUxIMp.d.ts → types-DeSApf9T.d.ts} +36 -14
  202. package/dist/{types-J3R9nVsZ.d.cts → types-Dgzlftb7.d.ts} +32 -28
  203. package/dist/{types-Bs1up4yP.d.ts → types-Ds0tCA3L.d.cts} +32 -28
  204. package/dist/ws/index.cjs +6 -6
  205. package/dist/ws/index.js +2 -2
  206. package/package.json +22 -4
  207. package/dist/bootstrap-AKAUP6F6.cjs +0 -32
  208. package/dist/bootstrap-JCML6NFO.js +0 -7
  209. package/dist/chunk-2KVHZE6O.cjs.map +0 -1
  210. package/dist/chunk-2OL4O2TH.cjs.map +0 -1
  211. package/dist/chunk-35U3FROB.js.map +0 -1
  212. package/dist/chunk-3AJE4SEG.js.map +0 -1
  213. package/dist/chunk-3J4MFTI3.js +0 -3872
  214. package/dist/chunk-3J4MFTI3.js.map +0 -1
  215. package/dist/chunk-3TPQ2BU6.js.map +0 -1
  216. package/dist/chunk-3ZFYL34R.js.map +0 -1
  217. package/dist/chunk-4DA7QPLA.cjs.map +0 -1
  218. package/dist/chunk-57P6MJKC.js.map +0 -1
  219. package/dist/chunk-5KVM3WEY.cjs.map +0 -1
  220. package/dist/chunk-6IMPH6WV.cjs +0 -3897
  221. package/dist/chunk-6IMPH6WV.cjs.map +0 -1
  222. package/dist/chunk-ATBOUGQP.cjs +0 -513
  223. package/dist/chunk-ATBOUGQP.cjs.map +0 -1
  224. package/dist/chunk-DVD5P72E.cjs.map +0 -1
  225. package/dist/chunk-DXHRBMGB.js.map +0 -1
  226. package/dist/chunk-ES5HNFFT.js.map +0 -1
  227. package/dist/chunk-FXYP2HA6.js.map +0 -1
  228. package/dist/chunk-H727JIG7.js.map +0 -1
  229. package/dist/chunk-HXRD4B37.js.map +0 -1
  230. package/dist/chunk-I7HHI6QV.cjs.map +0 -1
  231. package/dist/chunk-IA6AU5PI.cjs.map +0 -1
  232. package/dist/chunk-IBG6V56E.cjs.map +0 -1
  233. package/dist/chunk-K7JPTH3G.cjs.map +0 -1
  234. package/dist/chunk-LINKCEG4.cjs.map +0 -1
  235. package/dist/chunk-OHVB4AJ7.js.map +0 -1
  236. package/dist/chunk-PDYFVNUX.cjs.map +0 -1
  237. package/dist/chunk-Q23JB3KL.js +0 -488
  238. package/dist/chunk-Q23JB3KL.js.map +0 -1
  239. package/dist/chunk-QPPDLRNR.js.map +0 -1
  240. package/dist/chunk-QUW2RZTM.cjs.map +0 -1
  241. package/dist/chunk-QXIQWPAP.js.map +0 -1
  242. package/dist/chunk-R3XIBBAW.cjs +0 -34
  243. package/dist/chunk-R3XIBBAW.cjs.map +0 -1
  244. package/dist/chunk-REK7AYOC.js.map +0 -1
  245. package/dist/chunk-SA7NSSIQ.cjs.map +0 -1
  246. package/dist/chunk-SDMNUYVU.js +0 -30
  247. package/dist/chunk-SDMNUYVU.js.map +0 -1
  248. package/dist/chunk-V3LKPM3O.cjs.map +0 -1
  249. package/dist/chunk-VJT6P4N6.cjs.map +0 -1
  250. package/dist/chunk-WOWUL7ZY.js.map +0 -1
  251. package/dist/chunk-WQBRWOQT.cjs.map +0 -1
  252. package/dist/chunk-Y3N7UUDO.js.map +0 -1
  253. package/dist/chunk-Y3QQN7PN.js.map +0 -1
  254. package/dist/chunk-YVUJBEXE.cjs.map +0 -1
  255. package/dist/index-CLp-DRKA.d.ts +0 -64
  256. package/dist/index-DfO7G4kN.d.cts +0 -64
  257. package/dist/media-GPPTZ43E.js +0 -4
  258. package/dist/mongo-auth-adapter-NHHUJHVH.cjs +0 -17
  259. package/dist/mongo-auth-adapter-NJQUUCTP.js +0 -4
  260. package/dist/postgres-auth-adapter-3T2NKTSE.js +0 -5
  261. package/dist/postgres-auth-adapter-7IEENCKQ.cjs +0 -14
  262. package/dist/redis-adapter-D2E2S3GB.cjs +0 -13
  263. package/dist/redis-adapter-VQXD7ESY.js +0 -4
  264. package/dist/sqlite-adapter-LVK5PS4T.cjs +0 -13
  265. package/dist/sqlite-adapter-TR3U3W6Q.js +0 -4
@@ -1,12 +1,15 @@
1
1
  'use strict';
2
2
 
3
- var chunkK7JPTH3G_cjs = require('./chunk-K7JPTH3G.cjs');
4
- var chunk5KVM3WEY_cjs = require('./chunk-5KVM3WEY.cjs');
5
- var chunkIBG6V56E_cjs = require('./chunk-IBG6V56E.cjs');
6
- var chunkVJT6P4N6_cjs = require('./chunk-VJT6P4N6.cjs');
7
- var chunkDVD5P72E_cjs = require('./chunk-DVD5P72E.cjs');
8
- var chunkSA7NSSIQ_cjs = require('./chunk-SA7NSSIQ.cjs');
3
+ var chunk3KTWGODI_cjs = require('./chunk-3KTWGODI.cjs');
4
+ var chunkPV2I2KMI_cjs = require('./chunk-PV2I2KMI.cjs');
5
+ var chunk3HR772HI_cjs = require('./chunk-3HR772HI.cjs');
6
+ var chunkCNKT4PME_cjs = require('./chunk-CNKT4PME.cjs');
7
+ var chunkQFLB4EIJ_cjs = require('./chunk-QFLB4EIJ.cjs');
8
+ var chunk4M7X5HAB_cjs = require('./chunk-4M7X5HAB.cjs');
9
+ var chunkEEJUFDMF_cjs = require('./chunk-EEJUFDMF.cjs');
10
+ var chunkGUUB5EAG_cjs = require('./chunk-GUUB5EAG.cjs');
9
11
  var zod = require('zod');
12
+ var graphql = require('graphql');
10
13
  var module$1 = require('module');
11
14
  var crypto = require('crypto');
12
15
 
@@ -177,9 +180,9 @@ function validateFields(fields, context) {
177
180
  break;
178
181
  case "select":
179
182
  case "radio":
180
- if (!field.options || field.options.length === 0) {
183
+ if ((!field.options || field.options.length === 0) && !field.dynamicOptions) {
181
184
  errors.push(`${context}: ${field.type} field "${fieldName}" has no options defined`);
182
- } else {
185
+ } else if (field.options) {
183
186
  const values = field.options.map((o) => o.value);
184
187
  const uniqueValues = new Set(values);
185
188
  if (values.length !== uniqueValues.size) {
@@ -268,7 +271,7 @@ function validateRelationships(fields, collections) {
268
271
  const targets = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
269
272
  for (const target of targets) {
270
273
  if (!collectionSlugs.has(target)) {
271
- errors.push(`Relationship field "${field.name}" references unknown collection "${target}"`);
274
+ console.warn(`[Kyro Config Warning]: Relationship field "${field.name}" references unknown collection "${target}". Select options will not be available until this collection is registered.`);
272
275
  }
273
276
  }
274
277
  }
@@ -368,7 +371,7 @@ function textToZod(field) {
368
371
  return schema;
369
372
  }
370
373
  function numberToZod(field) {
371
- let schema = field.integer ? zod.z.number().int() : zod.z.number();
374
+ let schema = field.integer ? zod.z.coerce.number().int() : zod.z.coerce.number();
372
375
  if (field.min !== void 0) schema = schema.min(field.min);
373
376
  if (field.max !== void 0) schema = schema.max(field.max);
374
377
  if (field.step) {
@@ -427,12 +430,16 @@ function textareaToZod(field) {
427
430
  return schema;
428
431
  }
429
432
  function selectToZod(field) {
430
- const values = field.options.map((opt) => opt.value);
431
433
  let schema;
432
- if (field.hasMany) {
433
- schema = zod.z.array(zod.z.enum(values));
434
+ if (field.options && field.options.length > 0) {
435
+ const values = field.options.map((opt) => opt.value);
436
+ if (field.hasMany) {
437
+ schema = zod.z.array(zod.z.enum(values));
438
+ } else {
439
+ schema = zod.z.enum(values);
440
+ }
434
441
  } else {
435
- schema = zod.z.enum(values);
442
+ schema = field.hasMany ? zod.z.array(zod.z.string()) : zod.z.string();
436
443
  }
437
444
  if (!field.required) schema = schema.optional().nullable();
438
445
  if (field.validate) schema = addCustomValidation(schema, field.validate);
@@ -462,10 +469,7 @@ function colorToZod(field) {
462
469
  return schema;
463
470
  }
464
471
  function richTextToZod(field) {
465
- let schema = zod.z.union([
466
- zod.z.array(zod.z.record(zod.z.any())),
467
- zod.z.string()
468
- ]);
472
+ let schema = zod.z.array(zod.z.record(zod.z.any()));
469
473
  if (!field.required) schema = schema.optional().nullable();
470
474
  if (field.validate) schema = addCustomValidation(schema, field.validate);
471
475
  return schema;
@@ -576,9 +580,9 @@ function blocksToZod(field) {
576
580
  const unknownSchema = zod.z.object({
577
581
  blockType: zod.z.string()
578
582
  }).catchall(zod.z.any());
579
- schema = zod.z.array(zod.z.union([knownSchema, unknownSchema]));
583
+ schema = zod.z.array(zod.z.union([knownSchema, unknownSchema, zod.z.record(zod.z.any())]));
580
584
  } else {
581
- schema = zod.z.array(zod.z.object({ blockType: zod.z.string() }).catchall(zod.z.any()));
585
+ schema = zod.z.array(zod.z.union([zod.z.object({ blockType: zod.z.string() }).catchall(zod.z.any()), zod.z.record(zod.z.any())]));
582
586
  }
583
587
  if (field.minRows) schema = schema.min(field.minRows);
584
588
  if (field.maxRows) schema = schema.max(field.maxRows);
@@ -646,6 +650,30 @@ function buildNestedShape(fields) {
646
650
  }
647
651
  return shape;
648
652
  }
653
+ function buildUpdateNestedShape(fields) {
654
+ const shape = {};
655
+ for (const field of fields) {
656
+ if (!field.name) continue;
657
+ if (field.type === "tabs" && "tabs" in field) {
658
+ const tabShape = {};
659
+ for (const tab of field.tabs) {
660
+ const nestedShape = buildUpdateNestedShape(tab.fields);
661
+ Object.assign(tabShape, nestedShape);
662
+ }
663
+ shape[field.name] = zod.z.object(tabShape).passthrough().optional().nullable();
664
+ } else if (field.type === "row" && "fields" in field) {
665
+ const rowShape = buildUpdateNestedShape(field.fields);
666
+ Object.assign(shape, rowShape);
667
+ } else if (field.type === "collapsible" && "fields" in field) {
668
+ shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).passthrough().optional().nullable();
669
+ } else if (field.type === "group" && "fields" in field) {
670
+ shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).passthrough().optional().nullable();
671
+ } else {
672
+ shape[field.name] = fieldToZod(field).optional().nullable();
673
+ }
674
+ }
675
+ return shape;
676
+ }
649
677
  function collectionToZod(collection) {
650
678
  const shape = buildNestedShape(collection.fields);
651
679
  if (collection.timestamps) {
@@ -685,7 +713,9 @@ function collectionToUpdateZod(collection) {
685
713
  }
686
714
  Object.assign(shape, rowShape);
687
715
  } else if (field.type === "collapsible" && "fields" in field) {
688
- shape[field.name] = zod.z.object(buildNestedShape(field.fields)).optional().nullable();
716
+ shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
717
+ } else if (field.type === "group" && "fields" in field) {
718
+ shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
689
719
  } else {
690
720
  shape[field.name] = fieldToZod(field).optional().nullable();
691
721
  }
@@ -721,9 +751,34 @@ function globalToZod(global) {
721
751
  shape["id"] = zod.z.string().optional();
722
752
  return zod.z.object(shape).passthrough();
723
753
  }
754
+ function globalToUpdateZod(global) {
755
+ const shape = {};
756
+ for (const field of global.fields) {
757
+ if (!field.name) continue;
758
+ if (field.type === "tabs" && "tabs" in field) {
759
+ const tabShape = {};
760
+ for (const tab of field.tabs) {
761
+ for (const tabField of tab.fields) {
762
+ if (tabField.name) {
763
+ tabShape[tabField.name] = fieldToZod(tabField).optional().nullable();
764
+ }
765
+ }
766
+ }
767
+ shape[field.name] = zod.z.object(tabShape).optional().nullable();
768
+ } else if (field.type === "collapsible" && "fields" in field) {
769
+ shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
770
+ } else if (field.type === "group" && "fields" in field) {
771
+ shape[field.name] = zod.z.object(buildUpdateNestedShape(field.fields)).optional().nullable();
772
+ } else {
773
+ shape[field.name] = fieldToZod(field).optional().nullable();
774
+ }
775
+ }
776
+ return zod.z.object(shape).passthrough();
777
+ }
724
778
 
725
779
  // src/registry/index.ts
726
780
  var Registry = class {
781
+ storageProviders = chunkCNKT4PME_cjs.getDefaultRegistry();
727
782
  collections = /* @__PURE__ */ new Map();
728
783
  globals = /* @__PURE__ */ new Map();
729
784
  plugins = [];
@@ -791,6 +846,19 @@ var Registry = class {
791
846
  if (this.initialized) {
792
847
  throw new Error("Cannot add globals after Registry has been initialized");
793
848
  }
849
+ this._addGlobalUnsafe(config);
850
+ }
851
+ /**
852
+ * Add a global after the registry is already initialized.
853
+ * Only for internal use (e.g. storage settings form built at startup).
854
+ */
855
+ addGlobalPostInit(config) {
856
+ if (this.globals.has(config.slug)) {
857
+ this.globals.delete(config.slug);
858
+ }
859
+ this._addGlobalUnsafe(config);
860
+ }
861
+ _addGlobalUnsafe(config) {
794
862
  if (this.globals.has(config.slug)) {
795
863
  console.warn(
796
864
  `[Registry] Duplicate global slug "${config.slug}" \u2014 skipping`
@@ -890,7 +958,13 @@ var Registry = class {
890
958
  this.schemaCache.set(cacheKey, schema);
891
959
  return schema;
892
960
  }
893
- throw new Error(`No collection found with slug "${slug}"`);
961
+ const global = this.globals.get(slug);
962
+ if (global) {
963
+ const schema = globalToUpdateZod(global);
964
+ this.schemaCache.set(`global:${slug}:update`, schema);
965
+ return schema;
966
+ }
967
+ throw new Error(`No collection or global found with slug "${slug}"`);
894
968
  }
895
969
  getWhereZodSchema(slug) {
896
970
  const cacheKey = `${slug}:where`;
@@ -945,6 +1019,17 @@ var Registry = class {
945
1019
  admin: { readOnly: true, hidden: true }
946
1020
  });
947
1021
  }
1022
+ if (config.versions?.drafts && !fields.some((f) => f.name === "status")) {
1023
+ fields.push({
1024
+ name: "status",
1025
+ type: "select",
1026
+ options: [
1027
+ { value: "draft", label: "Draft" },
1028
+ { value: "published", label: "Published" }
1029
+ ],
1030
+ admin: { readOnly: true, hidden: true }
1031
+ });
1032
+ }
948
1033
  if (config.auth && !fields.some((f) => f.name === "password")) {
949
1034
  fields.push({
950
1035
  name: "password",
@@ -1089,6 +1174,296 @@ function createRegistry() {
1089
1174
  instance = new Registry();
1090
1175
  return instance;
1091
1176
  }
1177
+
1178
+ // src/plugins/storage-s3.ts
1179
+ var s3Variants = {
1180
+ aws: {
1181
+ type: "aws",
1182
+ configKey: "s3",
1183
+ displayName: "S3 Compatible (AWS, Backblaze, Wasabi, etc.)",
1184
+ configFields: [
1185
+ { name: "bucket", type: "text", label: "Bucket Name", required: true },
1186
+ { name: "region", type: "text", label: "Region", defaultValue: "us-east-1", admin: { placeholder: "us-east-1" } },
1187
+ { name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
1188
+ { name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
1189
+ { name: "endpoint", type: "text", label: "Endpoint URL", admin: { placeholder: "https://s3.custom.com" } },
1190
+ { name: "cdnUrl", type: "text", label: "CDN URL", admin: { placeholder: "https://cdn.example.com" } },
1191
+ { name: "prefix", type: "text", label: "Path Prefix", admin: { placeholder: "uploads" } }
1192
+ ]
1193
+ },
1194
+ r2: {
1195
+ type: "r2",
1196
+ configKey: "r2",
1197
+ displayName: "Cloudflare R2",
1198
+ configFields: [
1199
+ { name: "accountId", type: "text", label: "Account ID", required: true, admin: { placeholder: "Your Cloudflare Account ID" } },
1200
+ { name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
1201
+ { name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
1202
+ { name: "bucket", type: "text", label: "Bucket Name", required: true },
1203
+ {
1204
+ name: "publicDevUrl",
1205
+ type: "text",
1206
+ label: "Public Dev URL ID",
1207
+ admin: {
1208
+ placeholder: "pub-xxxxxxxxxxxxxxxx",
1209
+ description: "Enter ONLY the ID (e.g., pub-b8d8c4cc8bcf4d868ddd95efc1b305aa). Do NOT include https:// or the full URL. Found in R2 Dashboard \u2192 Public Dev URL."
1210
+ }
1211
+ },
1212
+ { name: "cdnUrl", type: "text", label: "Custom CDN URL", admin: { placeholder: "https://assets.example.com (optional)" } },
1213
+ { name: "prefix", type: "text", label: "Path Prefix", admin: { placeholder: "uploads (optional)", description: "Optional prefix for all object keys. Do not use '/' as prefix." } }
1214
+ ]
1215
+ },
1216
+ gcs: {
1217
+ type: "gcs",
1218
+ configKey: "gcs",
1219
+ displayName: "Google Cloud Storage",
1220
+ configFields: [
1221
+ { name: "bucket", type: "text", label: "Bucket Name", required: true },
1222
+ { name: "projectId", type: "text", label: "Project ID" },
1223
+ { name: "clientEmail", type: "text", label: "Client Email" },
1224
+ { name: "privateKey", type: "password", label: "Private Key" },
1225
+ { name: "cdnUrl", type: "text", label: "CDN URL" },
1226
+ { name: "prefix", type: "text", label: "Path Prefix" }
1227
+ ]
1228
+ },
1229
+ digitalocean: {
1230
+ type: "digitalocean",
1231
+ configKey: "digitalocean",
1232
+ displayName: "DigitalOcean Spaces",
1233
+ configFields: [
1234
+ { name: "bucket", type: "text", label: "Bucket Name", required: true },
1235
+ { name: "region", type: "text", label: "Region", defaultValue: "nyc3" },
1236
+ { name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
1237
+ { name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
1238
+ { name: "cdnUrl", type: "text", label: "CDN URL" },
1239
+ { name: "prefix", type: "text", label: "Path Prefix" }
1240
+ ]
1241
+ },
1242
+ backblaze: {
1243
+ type: "backblaze",
1244
+ configKey: "backblaze",
1245
+ displayName: "Backblaze B2",
1246
+ configFields: [
1247
+ { name: "bucket", type: "text", label: "Bucket Name", required: true },
1248
+ { name: "accountId", type: "text", label: "Account ID" },
1249
+ { name: "applicationKeyId", type: "text", label: "Application Key ID", required: true },
1250
+ { name: "applicationKey", type: "password", label: "Application Key", required: true },
1251
+ { name: "cdnUrl", type: "text", label: "CDN URL" },
1252
+ { name: "prefix", type: "text", label: "Path Prefix" }
1253
+ ]
1254
+ },
1255
+ wasabi: {
1256
+ type: "wasabi",
1257
+ configKey: "wasabi",
1258
+ displayName: "Wasabi",
1259
+ configFields: [
1260
+ { name: "bucket", type: "text", label: "Bucket Name", required: true },
1261
+ { name: "region", type: "text", label: "Region", defaultValue: "us-east-1" },
1262
+ { name: "accessKeyId", type: "text", label: "Access Key ID", required: true },
1263
+ { name: "secretAccessKey", type: "password", label: "Secret Access Key", required: true },
1264
+ { name: "cdnUrl", type: "text", label: "CDN URL" },
1265
+ { name: "prefix", type: "text", label: "Path Prefix" }
1266
+ ]
1267
+ }
1268
+ };
1269
+ function getEndpoint(type, config) {
1270
+ switch (type) {
1271
+ case "r2":
1272
+ return config?.endpoint || `https://${config?.accountId || ""}.r2.cloudflarestorage.com`;
1273
+ case "digitalocean":
1274
+ return config?.endpoint || `https://${config?.region || "nyc3"}.digitaloceanspaces.com`;
1275
+ case "backblaze":
1276
+ return config?.endpoint || `https://s3.backblazeb2.com`;
1277
+ case "wasabi":
1278
+ return config?.endpoint || `https://s3.${config?.region || "us-east-1"}.wasabisys.com`;
1279
+ default:
1280
+ return config?.endpoint;
1281
+ }
1282
+ }
1283
+ function buildS3Config(type, c) {
1284
+ return {
1285
+ provider: type,
1286
+ bucket: c?.bucket || "",
1287
+ region: c?.region || "us-east-1",
1288
+ accessKeyId: c?.accessKeyId || c?.clientEmail || c?.applicationKeyId || "",
1289
+ secretAccessKey: c?.secretAccessKey || c?.privateKey || c?.applicationKey || "",
1290
+ endpoint: getEndpoint(type, c),
1291
+ cdnUrl: c?.cdnUrl,
1292
+ prefix: c?.prefix,
1293
+ accountId: c?.accountId,
1294
+ publicDevUrl: c?.publicDevUrl
1295
+ };
1296
+ }
1297
+ function buildS3ConfigFromStorageConfig(type, def, sc) {
1298
+ const c = sc[def.configKey] || {};
1299
+ return buildS3Config(type, c);
1300
+ }
1301
+ function buildS3ConfigFromRaw(type, def, raw) {
1302
+ const c = raw?.[def.configKey] || raw;
1303
+ return buildS3Config(type, c);
1304
+ }
1305
+ var s3StoragePlugin = {
1306
+ name: "@kyro-cms/storage-s3",
1307
+ version: "1.0.0",
1308
+ description: "S3-compatible storage (AWS R2 GCS DigitalOcean Backblaze Wasabi)",
1309
+ init: (kyro) => {
1310
+ const registry = kyro.storageProviders;
1311
+ if (!registry) return;
1312
+ const pluginName = "@kyro-cms/storage-s3";
1313
+ for (const v of Object.values(s3Variants)) {
1314
+ registry.register({
1315
+ type: v.type,
1316
+ displayName: v.displayName,
1317
+ pluginName,
1318
+ configKey: v.configKey,
1319
+ configFields: v.configFields,
1320
+ extractConfig: (sc) => buildS3ConfigFromStorageConfig(v.type, v, sc),
1321
+ extractRawConfig: (raw) => buildS3ConfigFromRaw(v.type, v, raw),
1322
+ factory: (c) => chunkCNKT4PME_cjs.createS3Storage(c)
1323
+ });
1324
+ }
1325
+ }
1326
+ };
1327
+
1328
+ // src/plugins/storage-cloudinary.ts
1329
+ var cloudinaryStoragePlugin = {
1330
+ name: "@kyro-cms/storage-cloudinary",
1331
+ version: "1.0.0",
1332
+ description: "Cloudinary image and video storage",
1333
+ init: (kyro) => {
1334
+ const registry = kyro.storageProviders;
1335
+ if (!registry) return;
1336
+ registry.register({
1337
+ type: "cloudinary",
1338
+ displayName: "Cloudinary",
1339
+ pluginName: "@kyro-cms/storage-cloudinary",
1340
+ configKey: "cloudinary",
1341
+ configFields: [
1342
+ { name: "cloudName", type: "text", label: "Cloud Name", required: true },
1343
+ { name: "apiKey", type: "text", label: "API Key", required: true },
1344
+ { name: "apiSecret", type: "password", label: "API Secret", required: true },
1345
+ { name: "folder", type: "text", label: "Folder", admin: { placeholder: "Optional folder path" } },
1346
+ {
1347
+ name: "uploadPreset",
1348
+ type: "text",
1349
+ label: "Upload Preset (optional)",
1350
+ admin: { placeholder: "Leave empty for signed uploads", description: "If not set, uploads will be signed with API Secret" }
1351
+ }
1352
+ ],
1353
+ extractConfig: (sc) => ({
1354
+ cloudName: sc.cloudinary?.cloudName || "",
1355
+ apiKey: sc.cloudinary?.apiKey || "",
1356
+ apiSecret: sc.cloudinary?.apiSecret || "",
1357
+ folder: sc.cloudinary?.folder,
1358
+ uploadPreset: sc.cloudinary?.uploadPreset
1359
+ }),
1360
+ extractRawConfig: (c) => {
1361
+ const cl = c?.cloudinary || c;
1362
+ return {
1363
+ cloudName: cl?.cloudName || "",
1364
+ apiKey: cl?.apiKey || "",
1365
+ apiSecret: cl?.apiSecret || "",
1366
+ folder: cl?.folder,
1367
+ uploadPreset: cl?.uploadPreset
1368
+ };
1369
+ },
1370
+ factory: (c) => chunkCNKT4PME_cjs.createCloudinaryStorage(c)
1371
+ });
1372
+ }
1373
+ };
1374
+
1375
+ // src/plugins/storage-ftp.ts
1376
+ var ftpStoragePlugin = {
1377
+ name: "@kyro-cms/storage-ftp",
1378
+ version: "1.0.0",
1379
+ description: "FTP/SFTP storage provider",
1380
+ init: (kyro) => {
1381
+ const registry = kyro.storageProviders;
1382
+ if (!registry) return;
1383
+ registry.register({
1384
+ type: "ftp",
1385
+ displayName: "FTP",
1386
+ pluginName: "@kyro-cms/storage-ftp",
1387
+ configKey: "ftp",
1388
+ configFields: [
1389
+ { name: "host", type: "text", label: "Host", required: true, admin: { placeholder: "ftp.example.com" } },
1390
+ { name: "port", type: "number", label: "Port", defaultValue: 21, admin: { placeholder: "21 for FTP" } },
1391
+ { name: "user", type: "text", label: "Username", required: true },
1392
+ { name: "password", type: "password", label: "Password", required: true },
1393
+ { name: "secure", type: "checkbox", label: "Use TLS/SSL", defaultValue: false, admin: { description: "Enable TLS/SSL for secure connections (FTP only)" } },
1394
+ { name: "baseUrl", type: "text", label: "Base URL", required: true, admin: { placeholder: "https://files.example.com" } },
1395
+ { name: "prefix", type: "text", label: "Path Prefix", admin: { placeholder: "uploads" } }
1396
+ ],
1397
+ extractConfig: (sc) => ({
1398
+ host: sc.ftp?.host || "",
1399
+ port: sc.ftp?.port || 21,
1400
+ user: sc.ftp?.user || "",
1401
+ password: sc.ftp?.password || "",
1402
+ secure: sc.ftp?.secure || false,
1403
+ baseUrl: sc.ftp?.baseUrl || "",
1404
+ prefix: sc.ftp?.prefix,
1405
+ type: "ftp"
1406
+ }),
1407
+ extractRawConfig: (c) => {
1408
+ const ftp = c?.ftp || c;
1409
+ return {
1410
+ host: ftp?.host || "",
1411
+ port: ftp?.port || 21,
1412
+ user: ftp?.user || "",
1413
+ password: ftp?.password || "",
1414
+ secure: ftp?.secure || false,
1415
+ baseUrl: ftp?.baseUrl || "",
1416
+ prefix: ftp?.prefix,
1417
+ type: "ftp"
1418
+ };
1419
+ },
1420
+ factory: (c) => chunkCNKT4PME_cjs.createFtpStorage(c)
1421
+ });
1422
+ }
1423
+ };
1424
+ var builtinStoragePlugins = [
1425
+ s3StoragePlugin,
1426
+ cloudinaryStoragePlugin,
1427
+ ftpStoragePlugin
1428
+ ];
1429
+ function updateFieldByPath(fields, path, updates) {
1430
+ const parts = path.split(".");
1431
+ if (parts.length === 0) return false;
1432
+ const currentPart = parts[0];
1433
+ const remainingPath = parts.slice(1).join(".");
1434
+ for (const field of fields) {
1435
+ if (field.name === currentPart) {
1436
+ if (remainingPath) {
1437
+ if (field.fields && Array.isArray(field.fields)) {
1438
+ return updateFieldByPath(field.fields, remainingPath, updates);
1439
+ }
1440
+ if (field.type === "array" && field.fields && Array.isArray(field.fields)) {
1441
+ return updateFieldByPath(field.fields, remainingPath, updates);
1442
+ }
1443
+ return false;
1444
+ } else {
1445
+ Object.assign(field, updates);
1446
+ return true;
1447
+ }
1448
+ }
1449
+ }
1450
+ return false;
1451
+ }
1452
+ function applyCollectionOverrides(collections, overrides) {
1453
+ if (!overrides) return;
1454
+ for (const col of collections) {
1455
+ const override = overrides[col.slug];
1456
+ if (override) {
1457
+ const { fields: fieldOverrides, ...adminOverrides } = override;
1458
+ col.admin = { ...col.admin, ...adminOverrides };
1459
+ if (fieldOverrides && col.fields && Array.isArray(col.fields)) {
1460
+ for (const [fieldPath, fieldUpdates] of Object.entries(fieldOverrides)) {
1461
+ updateFieldByPath(col.fields, fieldPath, fieldUpdates);
1462
+ }
1463
+ }
1464
+ }
1465
+ }
1466
+ }
1092
1467
  var Kyro = class {
1093
1468
  registry;
1094
1469
  db;
@@ -1101,14 +1476,20 @@ var Kyro = class {
1101
1476
  this.config = config;
1102
1477
  this.registry = createRegistry();
1103
1478
  this.db = config.adapter;
1104
- this.pubsub = new chunkDVD5P72E_cjs.KyroPubSub(this.registry);
1105
- this.webhookService = chunkIBG6V56E_cjs.createWebhookService(this.db);
1479
+ this.pubsub = new chunkEEJUFDMF_cjs.KyroPubSub(this.registry);
1480
+ this.webhookService = chunkQFLB4EIJ_cjs.createWebhookService(this.db);
1481
+ if (config.collections && config.admin?.collectionOverrides) {
1482
+ applyCollectionOverrides(config.collections, config.admin.collectionOverrides);
1483
+ }
1106
1484
  if (config.collections) {
1107
1485
  this.registry.addCollections(config.collections);
1108
1486
  }
1109
1487
  if (config.globals) {
1110
1488
  this.registry.addGlobals(config.globals);
1111
1489
  }
1490
+ for (const plugin of builtinStoragePlugins) {
1491
+ this.registry.addPlugin(plugin);
1492
+ }
1112
1493
  if (config.plugins) {
1113
1494
  for (const plugin of config.plugins) {
1114
1495
  this.registry.addPlugin(plugin);
@@ -1117,13 +1498,24 @@ var Kyro = class {
1117
1498
  }
1118
1499
  async init() {
1119
1500
  await this.registry.init();
1501
+ const storageGlobal = chunk3KTWGODI_cjs.createStorageSettingsGlobal(
1502
+ this.registry.storageProviders,
1503
+ (name) => this.registry.storageProviders.isPluginEnabled(name)
1504
+ );
1505
+ this.registry.addGlobalPostInit(storageGlobal);
1506
+ const pluginSettingsGlobal = {
1507
+ slug: "plugin-settings",
1508
+ admin: { hidden: true },
1509
+ fields: [{ name: "states", type: "json" }]
1510
+ };
1511
+ this.registry.addGlobalPostInit(pluginSettingsGlobal);
1120
1512
  if (!this.db) {
1121
1513
  throw new Error(
1122
1514
  `Database adapter is null \u2014 failed to load at startup. Check the server console for the exact error.`
1123
1515
  );
1124
1516
  }
1125
1517
  const systemCollection = {
1126
- slug: chunkIBG6V56E_cjs.API_KEY_COLLECTION,
1518
+ slug: chunk4M7X5HAB_cjs.API_KEY_COLLECTION,
1127
1519
  fields: [
1128
1520
  { name: "userId", type: "text", required: true },
1129
1521
  { name: "name", type: "text", required: true },
@@ -1135,7 +1527,7 @@ var Kyro = class {
1135
1527
  ]
1136
1528
  };
1137
1529
  const webhookCollection = {
1138
- slug: chunkIBG6V56E_cjs.WEBHOOK_COLLECTION,
1530
+ slug: chunkQFLB4EIJ_cjs.WEBHOOK_COLLECTION,
1139
1531
  fields: [
1140
1532
  { name: "name", type: "text", required: true },
1141
1533
  { name: "url", type: "text", required: true },
@@ -1148,7 +1540,7 @@ var Kyro = class {
1148
1540
  ]
1149
1541
  };
1150
1542
  const webhookDeliveryCollection = {
1151
- slug: chunkIBG6V56E_cjs.WEBHOOK_DELIVERY_COLLECTION,
1543
+ slug: chunkQFLB4EIJ_cjs.WEBHOOK_DELIVERY_COLLECTION,
1152
1544
  fields: [
1153
1545
  { name: "webhookId", type: "text", required: true },
1154
1546
  { name: "event", type: "text", required: true },
@@ -1163,6 +1555,7 @@ var Kyro = class {
1163
1555
  { name: "nextRetryAt", type: "date" }
1164
1556
  ]
1165
1557
  };
1558
+ const allGlobals = this.registry.getGlobals();
1166
1559
  await this.db.init(
1167
1560
  [
1168
1561
  ...this.registry.getCollections(),
@@ -1170,12 +1563,13 @@ var Kyro = class {
1170
1563
  webhookCollection,
1171
1564
  webhookDeliveryCollection
1172
1565
  ],
1173
- this.registry.getGlobals()
1566
+ allGlobals
1174
1567
  );
1568
+ await this.loadPluginState();
1175
1569
  this.pubsub.autoRegisterHooks();
1176
1570
  console.log("\u2705 Kyro CMS initialized");
1177
1571
  console.log(` Collections: ${this.registry.getCollections().length}`);
1178
- console.log(` Globals: ${this.registry.getGlobals().length}`);
1572
+ console.log(` Globals: ${allGlobals.length}`);
1179
1573
  }
1180
1574
  // ============================================================================
1181
1575
  // API Methods
@@ -1184,23 +1578,43 @@ var Kyro = class {
1184
1578
  async loadSettings() {
1185
1579
  if (this.settings) return this.settings;
1186
1580
  try {
1187
- const accessSettings = await this.db.findOne({
1188
- collection: "_globals",
1189
- where: { slug: "access-settings" }
1581
+ const doc = await this.db.findOne({
1582
+ collection: "_globals_access-settings",
1583
+ where: {}
1190
1584
  });
1191
- if (accessSettings) {
1192
- this.settings = accessSettings.data;
1585
+ if (doc) {
1586
+ this.settings = { access: doc };
1193
1587
  }
1194
1588
  } catch (e) {
1195
1589
  console.log("\u26A0\uFE0F No access-settings found, using defaults");
1196
1590
  }
1197
1591
  return this.settings || {};
1198
1592
  }
1593
+ async loadPluginState() {
1594
+ const storageRegistry = this.registry.storageProviders;
1595
+ const pluginNames = storageRegistry.getAllPluginNames();
1596
+ let pluginStates = {};
1597
+ try {
1598
+ const doc = await this.db.findOne({
1599
+ collection: "_globals_plugin-settings",
1600
+ where: {}
1601
+ });
1602
+ if (doc && doc.states) {
1603
+ pluginStates = doc.states;
1604
+ }
1605
+ } catch (e) {
1606
+ }
1607
+ for (const name of pluginNames) {
1608
+ if (pluginStates[name] !== void 0) {
1609
+ storageRegistry.setPluginEnabled(name, pluginStates[name]);
1610
+ }
1611
+ }
1612
+ }
1199
1613
  getREST(options) {
1200
1614
  const authObj = typeof this.config.auth === "object" ? this.config.auth : null;
1201
1615
  const authSecret = authObj?.secret;
1202
1616
  const checkSession = authObj?.checkSession !== false;
1203
- return chunk5KVM3WEY_cjs.createHonoApp({
1617
+ return chunkCNKT4PME_cjs.createHonoApp({
1204
1618
  registry: this.registry,
1205
1619
  db: this.db,
1206
1620
  authSecret,
@@ -1213,33 +1627,196 @@ var Kyro = class {
1213
1627
  });
1214
1628
  }
1215
1629
  getGraphQL(options) {
1216
- return chunkVJT6P4N6_cjs.buildGraphQLSchema({
1630
+ const defaultSchema = chunk3HR772HI_cjs.buildGraphQLSchema({
1217
1631
  registry: this.registry,
1218
1632
  db: this.db,
1219
- ...options,
1220
- settings: this.settings
1633
+ settings: this.settings,
1634
+ user: options?.user,
1635
+ req: options?.req,
1636
+ tenantID: options?.tenantID
1221
1637
  });
1638
+ return {
1639
+ fetch: async (request, _locals) => {
1640
+ const apiKeyRaw = chunk4M7X5HAB_cjs.extractApiKeyFromRequest(request);
1641
+ let gqlUser;
1642
+ let apiKeyCtx;
1643
+ if (apiKeyRaw && this.db) {
1644
+ const apiKeyResult = await chunk4M7X5HAB_cjs.validateApiKey(apiKeyRaw, this.db);
1645
+ if (!apiKeyResult.valid) {
1646
+ return new Response(
1647
+ JSON.stringify({ errors: [{ message: apiKeyResult.error || "Invalid API key" }] }),
1648
+ { status: 401, headers: { "Content-Type": "application/json" } }
1649
+ );
1650
+ }
1651
+ if (apiKeyResult.user) {
1652
+ gqlUser = apiKeyResult.user;
1653
+ apiKeyCtx = chunk4M7X5HAB_cjs.createApiKeyContext(apiKeyResult);
1654
+ }
1655
+ }
1656
+ const mustRebuild = gqlUser !== options?.user || apiKeyCtx !== void 0;
1657
+ const schema = mustRebuild ? chunk3HR772HI_cjs.buildGraphQLSchema({
1658
+ registry: this.registry,
1659
+ db: this.db,
1660
+ settings: this.settings,
1661
+ user: gqlUser,
1662
+ req: request,
1663
+ tenantID: options?.tenantID,
1664
+ apiKey: apiKeyCtx
1665
+ }) : defaultSchema;
1666
+ const body = request.method === "POST" ? await request.json().catch(() => ({})) : {};
1667
+ const query = body.query || "";
1668
+ const variables = body.variables || {};
1669
+ if (!query) {
1670
+ return new Response(
1671
+ JSON.stringify({ error: "No GraphQL query provided" }),
1672
+ { status: 400, headers: { "Content-Type": "application/json" } }
1673
+ );
1674
+ }
1675
+ try {
1676
+ const document = graphql.parse(query);
1677
+ const result = await graphql.execute({
1678
+ schema,
1679
+ document,
1680
+ variableValues: variables,
1681
+ contextValue: {
1682
+ db: this.db,
1683
+ registry: this.registry,
1684
+ settings: this.settings,
1685
+ user: gqlUser,
1686
+ req: request,
1687
+ tenantID: options?.tenantID
1688
+ }
1689
+ });
1690
+ return new Response(JSON.stringify(result), {
1691
+ status: 200,
1692
+ headers: { "Content-Type": "application/json" }
1693
+ });
1694
+ } catch (err) {
1695
+ return new Response(
1696
+ JSON.stringify({ errors: [{ message: err.message }] }),
1697
+ { status: 400, headers: { "Content-Type": "application/json" } }
1698
+ );
1699
+ }
1700
+ },
1701
+ schema: defaultSchema
1702
+ };
1222
1703
  }
1223
1704
  getTRPC(options) {
1224
- return chunkK7JPTH3G_cjs.createKyroServer({
1225
- registry: this.registry,
1226
- db: this.db,
1227
- req: options?.req || { headers: {} },
1228
- ...options,
1229
- settings: this.settings
1230
- });
1705
+ return {
1706
+ fetch: async (request, locals) => {
1707
+ const url = new URL(request.url);
1708
+ const path = url.pathname.replace(/^\/api\/trpc\//, "");
1709
+ const [slug, ...rest] = path.split(".");
1710
+ let procedureName = rest.join(".");
1711
+ procedureName = procedureName.replace(/\.(query|mutate|subscribe)$/, "");
1712
+ if (!slug || !procedureName) {
1713
+ return new Response(
1714
+ JSON.stringify({
1715
+ error: {
1716
+ message: "Invalid tRPC path",
1717
+ code: -32600,
1718
+ data: { code: "BAD_REQUEST", httpStatus: 400 }
1719
+ }
1720
+ }),
1721
+ { status: 400, headers: { "Content-Type": "application/json" } }
1722
+ );
1723
+ }
1724
+ const ctx = await chunkPV2I2KMI_cjs.createContext({
1725
+ db: this.db,
1726
+ registry: this.registry,
1727
+ req: request,
1728
+ user: options?.user,
1729
+ tenantID: options?.tenantID,
1730
+ settings: this.settings
1731
+ });
1732
+ const kyroRouter = chunkPV2I2KMI_cjs.createKyroServer(ctx);
1733
+ const collectionRouter = kyroRouter[slug];
1734
+ if (!collectionRouter) {
1735
+ return new Response(
1736
+ JSON.stringify({
1737
+ error: {
1738
+ message: `Collection '${slug}' not found`,
1739
+ code: -32601,
1740
+ data: { code: "NOT_FOUND", httpStatus: 404 }
1741
+ }
1742
+ }),
1743
+ { status: 404, headers: { "Content-Type": "application/json" } }
1744
+ );
1745
+ }
1746
+ const procedure = collectionRouter[procedureName];
1747
+ if (typeof procedure !== "function") {
1748
+ return new Response(
1749
+ JSON.stringify({
1750
+ error: {
1751
+ message: `Procedure '${procedureName}' not found`,
1752
+ code: -32601,
1753
+ data: { code: "NOT_FOUND", httpStatus: 404 }
1754
+ }
1755
+ }),
1756
+ { status: 404, headers: { "Content-Type": "application/json" } }
1757
+ );
1758
+ }
1759
+ try {
1760
+ let raw = {};
1761
+ if (request.method === "POST" || request.method === "PATCH") {
1762
+ raw = await request.json().catch(() => ({}));
1763
+ } else {
1764
+ const qs = new URL(request.url).searchParams.get("input");
1765
+ if (qs) {
1766
+ try {
1767
+ raw = JSON.parse(decodeURIComponent(qs));
1768
+ } catch {
1769
+ }
1770
+ }
1771
+ }
1772
+ const input = raw?.["0"] ?? raw;
1773
+ const result = await procedure({ ...input, collection: slug });
1774
+ return new Response(JSON.stringify({ result: { data: result } }), {
1775
+ status: 200,
1776
+ headers: { "Content-Type": "application/json" }
1777
+ });
1778
+ } catch (err) {
1779
+ const msg = err.message || "Internal error";
1780
+ const httpStatus = msg.includes("not found") ? 404 : msg.includes("denied") || msg.includes("authentication required") ? 403 : msg.includes("conflict") ? 409 : 500;
1781
+ const code = httpStatus === 404 ? -32601 : httpStatus === 403 ? -32001 : httpStatus === 409 ? -32002 : -32603;
1782
+ return new Response(
1783
+ JSON.stringify({
1784
+ error: {
1785
+ message: msg,
1786
+ code,
1787
+ data: {
1788
+ code: "INTERNAL_SERVER_ERROR",
1789
+ httpStatus
1790
+ }
1791
+ }
1792
+ }),
1793
+ { status: httpStatus, headers: { "Content-Type": "application/json" } }
1794
+ );
1795
+ }
1796
+ },
1797
+ router: null
1798
+ };
1799
+ }
1800
+ getWS() {
1801
+ return this.wsServer;
1231
1802
  }
1232
1803
  async startWebSocket(options) {
1233
1804
  const apiAccess = this.settings?.access?.apiAccess;
1234
- if (apiAccess?.websocketEnabled === false) {
1805
+ if (apiAccess?.wsEnabled === false) {
1235
1806
  console.log("\u26A0\uFE0F WebSocket is disabled in settings");
1236
1807
  return null;
1237
1808
  }
1238
- this.wsServer = chunkDVD5P72E_cjs.createWSServer({
1809
+ const defaultVerifyToken = async (token) => {
1810
+ const result = await chunk4M7X5HAB_cjs.validateApiKey(token, this.db);
1811
+ if (!result.valid) throw new Error(result.error || "Invalid API key");
1812
+ if (!result.user) throw new Error("API key has no associated user");
1813
+ return result.user;
1814
+ };
1815
+ this.wsServer = chunkEEJUFDMF_cjs.createWSServer({
1239
1816
  pubsub: this.pubsub,
1240
1817
  port: options?.port || 8080,
1241
1818
  requireAuth: options?.requireAuth ?? apiAccess?.requireAuth,
1242
- verifyToken: options?.verifyToken
1819
+ verifyToken: options?.verifyToken || defaultVerifyToken
1243
1820
  });
1244
1821
  console.log(`\u{1F50C} WebSocket server started on port ${options?.port || 8080}`);
1245
1822
  return this.wsServer;
@@ -1258,7 +1835,7 @@ var Kyro = class {
1258
1835
  function createKyro(config) {
1259
1836
  return new Kyro(config);
1260
1837
  }
1261
- var _require = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-LINKCEG4.cjs', document.baseURI).href)));
1838
+ var _require = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-E2763JUP.cjs', document.baseURI).href)));
1262
1839
  var modPath = "node:sqlite";
1263
1840
  var { DatabaseSync } = _require(modPath);
1264
1841
  function flattenFields(fields) {
@@ -1358,19 +1935,11 @@ function getTableColumns(db, tableName) {
1358
1935
  return [];
1359
1936
  }
1360
1937
  }
1361
- var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1938
+ var LocalAdapter = class extends chunkGUUB5EAG_cjs.AbstractBaseAdapter {
1362
1939
  db;
1363
1940
  path;
1364
1941
  migrations = /* @__PURE__ */ new Map();
1365
- draftsTableName = "kyro_drafts";
1366
1942
  versionsTableName = "kyro_versions";
1367
- tenantContext;
1368
- setTenantContext(context) {
1369
- this.tenantContext = context;
1370
- }
1371
- getTenantContext() {
1372
- return this.tenantContext;
1373
- }
1374
1943
  constructor(options) {
1375
1944
  super();
1376
1945
  this.path = options.path;
@@ -1384,6 +1953,34 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1384
1953
  if (!this.db) {
1385
1954
  this.db = new DatabaseSync(this.path || ":memory:");
1386
1955
  }
1956
+ if (this.db && typeof this.db.prepare === "function" && !this.db.prepare.__wrapped) {
1957
+ const originalPrepare = this.db.prepare.bind(this.db);
1958
+ const wrappedPrepare = (sql) => {
1959
+ const stmt = originalPrepare(sql);
1960
+ const serialize = (val) => {
1961
+ if (typeof val === "boolean") return val ? 1 : 0;
1962
+ if (val === void 0) return null;
1963
+ return val;
1964
+ };
1965
+ return new Proxy(stmt, {
1966
+ get(target, prop, receiver) {
1967
+ if (prop === "all") {
1968
+ return (...params) => target.all(...params.map(serialize));
1969
+ }
1970
+ if (prop === "get") {
1971
+ return (...params) => target.get(...params.map(serialize));
1972
+ }
1973
+ if (prop === "run") {
1974
+ return (...params) => target.run(...params.map(serialize));
1975
+ }
1976
+ const val = Reflect.get(target, prop, receiver);
1977
+ return typeof val === "function" ? val.bind(target) : val;
1978
+ }
1979
+ });
1980
+ };
1981
+ wrappedPrepare.__wrapped = true;
1982
+ this.db.prepare = wrappedPrepare;
1983
+ }
1387
1984
  this.db.exec("PRAGMA journal_mode = WAL");
1388
1985
  this.db.exec("PRAGMA foreign_keys = ON");
1389
1986
  this.connected = true;
@@ -1411,8 +2008,8 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1411
2008
  }
1412
2009
  columns.push(`${this.col("createdAt")} TEXT DEFAULT (datetime('now'))`);
1413
2010
  columns.push(`${this.col("updatedAt")} TEXT DEFAULT (datetime('now'))`);
1414
- columns.push(`_status TEXT DEFAULT 'published'`);
1415
- columns.push(`_has_draft INTEGER DEFAULT 0`);
2011
+ columns.push(`status TEXT DEFAULT 'draft'`);
2012
+ columns.push(`hasDraft INTEGER DEFAULT 0`);
1416
2013
  if (config.tenantScoped) {
1417
2014
  columns.push(`tenant_id TEXT NOT NULL`);
1418
2015
  }
@@ -1420,7 +2017,7 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1420
2017
  if (existingColumns.length === 0) {
1421
2018
  const createSQL = `CREATE TABLE IF NOT EXISTS ${name} (${columns.join(", ")})`;
1422
2019
  this.db.exec(createSQL);
1423
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${name}__status ON ${name}(_status)`);
2020
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${name}_status ON ${name}(status)`);
1424
2021
  for (const field of flattenFields(config.fields)) {
1425
2022
  if (field.name && field.indexed) {
1426
2023
  this.db.exec(
@@ -1439,9 +2036,9 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1439
2036
  const colName = colDef.split(" ")[0].replace(/^"/, "").replace(/"$/, "");
1440
2037
  if (!existingSet.has(colName) && colName !== "id") {
1441
2038
  try {
1442
- if (colName === "_status") {
2039
+ if (colName === "status") {
1443
2040
  this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} TEXT DEFAULT 'published'`);
1444
- } else if (colName === "_has_draft") {
2041
+ } else if (colName === "hasDraft") {
1445
2042
  this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} INTEGER DEFAULT 0`);
1446
2043
  } else {
1447
2044
  this.db.exec(`ALTER TABLE ${name} ADD COLUMN ${this.col(colName)} TEXT`);
@@ -1462,6 +2059,7 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1462
2059
  tenant_id TEXT,
1463
2060
  version INTEGER NOT NULL,
1464
2061
  status TEXT NOT NULL DEFAULT 'draft',
2062
+ autosave INTEGER NOT NULL DEFAULT 0,
1465
2063
  data TEXT NOT NULL,
1466
2064
  created_by TEXT,
1467
2065
  change_description TEXT,
@@ -1476,24 +2074,13 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1476
2074
  this.db.exec(
1477
2075
  `CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_status ON ${this.versionsTableName}(status)`
1478
2076
  );
1479
- }
1480
- ensureDraftsTable() {
1481
- this.db.exec(`
1482
- CREATE TABLE IF NOT EXISTS ${this.draftsTableName} (
1483
- id TEXT PRIMARY KEY,
1484
- collection_slug TEXT NOT NULL,
1485
- document_id TEXT NOT NULL,
1486
- tenant_id TEXT,
1487
- data TEXT NOT NULL,
1488
- base_updated_at TEXT,
1489
- draft_updated_at TEXT NOT NULL,
1490
- created_at TEXT DEFAULT (datetime('now')),
1491
- updated_at TEXT DEFAULT (datetime('now'))
1492
- )
1493
- `);
1494
2077
  this.db.exec(
1495
- `CREATE UNIQUE INDEX IF NOT EXISTS idx_${this.draftsTableName}_document ON ${this.draftsTableName}(collection_slug, document_id, tenant_id)`
2078
+ `CREATE INDEX IF NOT EXISTS idx_${this.versionsTableName}_autosave ON ${this.versionsTableName}(autosave)`
1496
2079
  );
2080
+ try {
2081
+ this.db.exec(`ALTER TABLE ${this.versionsTableName} ADD COLUMN autosave INTEGER NOT NULL DEFAULT 0`);
2082
+ } catch {
2083
+ }
1497
2084
  }
1498
2085
  // ========================================================================
1499
2086
  // SQL Quoting
@@ -1571,11 +2158,11 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1571
2158
  const conditions = [];
1572
2159
  let effectiveWhere = { ...where };
1573
2160
  if (this.tenantContext && config.tenantScoped) {
1574
- const rlsQuery = chunkSA7NSSIQ_cjs.applyRLS({ where: effectiveWhere }, slug, this.tenantContext, chunkSA7NSSIQ_cjs.DEFAULT_RLS_CONFIG);
2161
+ const rlsQuery = chunkGUUB5EAG_cjs.applyRLS({ where: effectiveWhere }, slug, this.tenantContext, chunkGUUB5EAG_cjs.DEFAULT_RLS_CONFIG);
1575
2162
  effectiveWhere = rlsQuery.where || {};
1576
2163
  }
1577
- if (!draft && config.versions?.drafts) {
1578
- conditions.push(`_status = ?`);
2164
+ if (!draft) {
2165
+ conditions.push(`status = ?`);
1579
2166
  params.push("published");
1580
2167
  }
1581
2168
  if (tenantID && config.tenantScoped) {
@@ -1616,20 +2203,14 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1616
2203
  const rows = this.db.prepare(sql).all(...params);
1617
2204
  let docs = rows.map((row) => this.rowToDoc(row, config));
1618
2205
  if (this.tenantContext && !this.tenantContext.isSuperAdmin) {
1619
- docs = docs.filter((doc) => chunkSA7NSSIQ_cjs.canAccessDocument(doc, slug, this.tenantContext, chunkSA7NSSIQ_cjs.DEFAULT_RLS_CONFIG));
2206
+ docs = docs.filter((doc) => chunkGUUB5EAG_cjs.canAccessDocument(doc, slug, this.tenantContext, chunkGUUB5EAG_cjs.DEFAULT_RLS_CONFIG));
1620
2207
  }
1621
2208
  if (draft) {
1622
2209
  docs = await Promise.all(docs.map(async (doc) => {
1623
- if (doc._has_draft) {
1624
- const versions = await this.findVersions({
1625
- collection: slug,
1626
- documentId: doc.id,
1627
- limit: 1,
1628
- sort: "-createdAt"
1629
- });
1630
- if (versions.docs.length > 0 && versions.docs[0].status === "draft") {
1631
- return { ...doc, ...versions.docs[0].data, _has_draft: true, _status: doc._status };
1632
- }
2210
+ const version = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND tenant_id IS NULL ORDER BY version DESC LIMIT 1`).get(slug, doc.id);
2211
+ if (version) {
2212
+ const versionData = version.data ? JSON.parse(version.data) : {};
2213
+ return { ...doc, ...versionData, status: doc.status };
1633
2214
  }
1634
2215
  return doc;
1635
2216
  }));
@@ -1657,12 +2238,12 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1657
2238
  const params = [id];
1658
2239
  if (this.tenantContext && config.tenantScoped) {
1659
2240
  const tempDoc = { id, tenant_id: this.tenantContext.tenantId };
1660
- if (!chunkSA7NSSIQ_cjs.canAccessDocument(tempDoc, slug, this.tenantContext, chunkSA7NSSIQ_cjs.DEFAULT_RLS_CONFIG)) {
2241
+ if (!chunkGUUB5EAG_cjs.canAccessDocument(tempDoc, slug, this.tenantContext, chunkGUUB5EAG_cjs.DEFAULT_RLS_CONFIG)) {
1661
2242
  return null;
1662
2243
  }
1663
2244
  }
1664
- if (!draft && config.versions?.drafts) {
1665
- sql += ` AND _status = ?`;
2245
+ if (!draft) {
2246
+ sql += ` AND status = ?`;
1666
2247
  params.push("published");
1667
2248
  }
1668
2249
  if (tenantID && config.tenantScoped) {
@@ -1672,15 +2253,11 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1672
2253
  const row = this.db.prepare(sql).get(...params);
1673
2254
  if (!row) return null;
1674
2255
  let doc = this.rowToDoc(row, config);
1675
- if (draft && doc._has_draft) {
1676
- const versions = await this.findVersions({
1677
- collection: slug,
1678
- documentId: doc.id,
1679
- limit: 1,
1680
- sort: "-createdAt"
1681
- });
1682
- if (versions.docs.length > 0 && versions.docs[0].status === "draft") {
1683
- doc = { ...doc, ...versions.docs[0].data, _has_draft: true, _status: doc._status };
2256
+ if (draft) {
2257
+ const version = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND tenant_id IS NULL ORDER BY version DESC LIMIT 1`).get(slug, doc.id);
2258
+ if (version) {
2259
+ const versionData = version.data ? JSON.parse(version.data) : {};
2260
+ doc = { ...doc, ...versionData, status: doc.status };
1684
2261
  }
1685
2262
  }
1686
2263
  return doc;
@@ -1711,7 +2288,7 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1711
2288
  const quotedColumns = filteredColumns.map((c) => this.col(c));
1712
2289
  const placeholders = filteredColumns.map(() => "?").join(", ");
1713
2290
  const values = Object.values(filteredData).map(
1714
- (v) => typeof v === "object" ? JSON.stringify(v) : v
2291
+ (v) => v !== null && typeof v === "object" ? JSON.stringify(v) : v
1715
2292
  );
1716
2293
  this.db.prepare(
1717
2294
  `INSERT OR REPLACE INTO ${tableName} (${quotedColumns.join(", ")}) VALUES (${placeholders})`
@@ -1791,7 +2368,7 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1791
2368
  const conditions = [];
1792
2369
  const params = [];
1793
2370
  if (!args.draft && globalConfig.versions) {
1794
- conditions.push("_status = 'published'");
2371
+ conditions.push("status = 'published'");
1795
2372
  }
1796
2373
  if (conditions.length > 0) {
1797
2374
  sql += ` WHERE ${conditions.join(" AND ")}`;
@@ -1800,15 +2377,11 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1800
2377
  const result2 = this.db.prepare(sql).get(...params);
1801
2378
  if (result2) {
1802
2379
  let doc = this.rowToDoc(result2, globalConfig);
1803
- if (args.draft && doc._has_draft) {
1804
- const versions = await this.findVersions({
1805
- collection: args.collection,
1806
- documentId: parsed.globalSlug,
1807
- limit: 1,
1808
- sort: "-createdAt"
1809
- });
1810
- if (versions.docs.length > 0 && versions.docs[0].status === "draft") {
1811
- doc = { ...doc, ...versions.docs[0].data, _has_draft: true, _status: doc._status };
2380
+ if (args.draft) {
2381
+ const version = this.db.prepare(`SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND tenant_id IS NULL ORDER BY version DESC LIMIT 1`).get(args.collection, parsed.globalSlug);
2382
+ if (version) {
2383
+ const versionData = version.data ? JSON.parse(version.data) : {};
2384
+ doc = { ...doc, ...versionData, status: doc.status };
1812
2385
  }
1813
2386
  }
1814
2387
  return doc;
@@ -1824,7 +2397,7 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1824
2397
  async findVersions(args) {
1825
2398
  this.ensureVersionsTable();
1826
2399
  const { collection, documentId, tenantID, limit = 20, page = 1 } = args;
1827
- const conditions = [`collection_slug = ?`, `document_id = ?`];
2400
+ const conditions = [`collection_slug = ?`, `document_id = ?`, `autosave = 0`];
1828
2401
  const params = [collection, documentId];
1829
2402
  if (tenantID) {
1830
2403
  conditions.push(`tenant_id = ?`);
@@ -1859,13 +2432,30 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1859
2432
  async createVersion(args) {
1860
2433
  this.ensureVersionsTable();
1861
2434
  const now = (/* @__PURE__ */ new Date()).toISOString();
2435
+ if (args.autosave) {
2436
+ let sql = `SELECT * FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND autosave = 1`;
2437
+ const params = [args.collection, args.documentId];
2438
+ if (args.tenantID) {
2439
+ sql += ` AND tenant_id = ?`;
2440
+ params.push(args.tenantID);
2441
+ } else {
2442
+ sql += ` AND tenant_id IS NULL`;
2443
+ }
2444
+ sql += ` LIMIT 1`;
2445
+ const existing = this.db.prepare(sql).get(...params);
2446
+ if (existing) {
2447
+ this.db.prepare(`UPDATE ${this.versionsTableName} SET data = ?, status = ?, updated_at = ? WHERE id = ?`).run(JSON.stringify(args.data), args.status, now, existing.id);
2448
+ const result = await this.findVersionByID({ collection: args.collection, versionId: existing.id });
2449
+ if (result) return result;
2450
+ }
2451
+ }
1862
2452
  const id = this.generateId();
1863
- const latestRow = this.db.prepare(`SELECT version FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? ORDER BY version DESC LIMIT 1`).get(args.collection, args.documentId);
2453
+ const latestRow = this.db.prepare(`SELECT version FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? AND autosave = 0 ORDER BY version DESC LIMIT 1`).get(args.collection, args.documentId);
1864
2454
  const nextVersion = (latestRow?.version ?? 0) + 1;
1865
2455
  this.db.prepare(
1866
2456
  `INSERT INTO ${this.versionsTableName} (
1867
- id, collection_slug, document_id, tenant_id, version, status, data, created_by, change_description, published_at, created_at, updated_at
1868
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2457
+ id, collection_slug, document_id, tenant_id, version, status, autosave, data, created_by, change_description, published_at, created_at, updated_at
2458
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
1869
2459
  ).run(
1870
2460
  id,
1871
2461
  args.collection,
@@ -1873,6 +2463,7 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1873
2463
  args.tenantID ?? null,
1874
2464
  nextVersion,
1875
2465
  args.status,
2466
+ args.autosave ? 1 : 0,
1876
2467
  JSON.stringify(args.data),
1877
2468
  args.createdBy ?? null,
1878
2469
  args.changeDescription ?? null,
@@ -1880,23 +2471,28 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1880
2471
  now,
1881
2472
  now
1882
2473
  );
1883
- const collectionConfig = this.collections.get(args.collection);
1884
- const maxPerDoc = collectionConfig?.versions?.maxPerDoc;
1885
- if (maxPerDoc && maxPerDoc > 0) {
1886
- await this.deleteVersions({ collection: args.collection, documentId: args.documentId, keepLatest: maxPerDoc, tenantID: args.tenantID });
2474
+ if (!args.autosave) {
2475
+ const collectionConfig = this.collections.get(args.collection);
2476
+ const maxPerDoc = collectionConfig?.versions?.maxPerDoc;
2477
+ if (maxPerDoc && maxPerDoc > 0) {
2478
+ await this.deleteVersions({ collection: args.collection, documentId: args.documentId, keepLatest: maxPerDoc, tenantID: args.tenantID });
2479
+ }
1887
2480
  }
1888
2481
  const saved = await this.findVersionByID({ collection: args.collection, versionId: id });
1889
2482
  return saved;
1890
2483
  }
2484
+ async updateLatestVersion(args) {
2485
+ return this.createVersion({ ...args, autosave: true });
2486
+ }
1891
2487
  async deleteVersions(args) {
1892
2488
  this.ensureVersionsTable();
1893
2489
  const { collection, documentId, keepLatest, tenantID } = args;
1894
2490
  if (keepLatest && keepLatest > 0) {
1895
- const rows = this.db.prepare(`SELECT id, status FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? ORDER BY version DESC`).all(collection, documentId);
2491
+ const rows = this.db.prepare(`SELECT id, status, autosave FROM ${this.versionsTableName} WHERE collection_slug = ? AND document_id = ? ORDER BY version DESC`).all(collection, documentId);
1896
2492
  let draftCount = 0;
1897
2493
  const toDelete = [];
1898
2494
  for (const row of rows) {
1899
- if (row.status === "published") continue;
2495
+ if (row.status === "published" || row.autosave === 1) continue;
1900
2496
  draftCount++;
1901
2497
  if (draftCount > keepLatest) toDelete.push(row.id);
1902
2498
  }
@@ -1928,60 +2524,6 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
1928
2524
  updatedAt: row.updated_at
1929
2525
  };
1930
2526
  }
1931
- async findDraft(args) {
1932
- this.ensureDraftsTable();
1933
- let sql = `SELECT * FROM ${this.draftsTableName} WHERE collection_slug = ? AND document_id = ?`;
1934
- const params = [args.collection, args.documentId];
1935
- if (args.tenantID) {
1936
- sql += ` AND tenant_id = ?`;
1937
- params.push(args.tenantID);
1938
- } else {
1939
- sql += ` AND tenant_id IS NULL`;
1940
- }
1941
- sql += ` LIMIT 1`;
1942
- const row = this.db.prepare(sql).get(...params);
1943
- if (!row) return null;
1944
- return this.rowToDraft(row);
1945
- }
1946
- async upsertDraft(args) {
1947
- this.ensureDraftsTable();
1948
- const existing = await this.findDraft(args);
1949
- const now = (/* @__PURE__ */ new Date()).toISOString();
1950
- const draftUpdatedAt = args.draftUpdatedAt || now;
1951
- const id = existing?.id || this.generateId();
1952
- this.db.prepare(
1953
- `INSERT OR REPLACE INTO ${this.draftsTableName} (
1954
- id, collection_slug, document_id, tenant_id, data, base_updated_at, draft_updated_at, created_at, updated_at
1955
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
1956
- ).run(
1957
- id,
1958
- args.collection,
1959
- args.documentId,
1960
- args.tenantID ?? null,
1961
- JSON.stringify(args.data),
1962
- args.baseUpdatedAt ?? null,
1963
- draftUpdatedAt,
1964
- existing?.createdAt || now,
1965
- now
1966
- );
1967
- const saved = await this.findDraft(args);
1968
- if (!saved) {
1969
- throw new Error("Failed to persist draft snapshot");
1970
- }
1971
- return saved;
1972
- }
1973
- async deleteDraft(args) {
1974
- this.ensureDraftsTable();
1975
- let sql = `DELETE FROM ${this.draftsTableName} WHERE collection_slug = ? AND document_id = ?`;
1976
- const params = [args.collection, args.documentId];
1977
- if (args.tenantID) {
1978
- sql += ` AND tenant_id = ?`;
1979
- params.push(args.tenantID);
1980
- } else {
1981
- sql += ` AND tenant_id IS NULL`;
1982
- }
1983
- this.db.prepare(sql).run(...params);
1984
- }
1985
2527
  // ========================================================================
1986
2528
  // Helpers
1987
2529
  // ========================================================================
@@ -2150,6 +2692,8 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
2150
2692
  if (config.tenantScoped) {
2151
2693
  doc.tenantID = row.tenant_id;
2152
2694
  }
2695
+ doc.status = row.status ?? "published";
2696
+ doc.hasDraft = row.hasDraft ? Boolean(row.hasDraft) : false;
2153
2697
  return doc;
2154
2698
  }
2155
2699
  generateId() {
@@ -2175,19 +2719,6 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
2175
2719
  getTableNameFor(slug) {
2176
2720
  return slug.replace(/-/g, "_");
2177
2721
  }
2178
- rowToDraft(row) {
2179
- return {
2180
- id: row.id,
2181
- collection: row.collection_slug,
2182
- documentId: row.document_id,
2183
- tenantID: row.tenant_id ?? void 0,
2184
- data: row.data ? JSON.parse(row.data) : {},
2185
- baseUpdatedAt: row.base_updated_at ?? null,
2186
- draftUpdatedAt: row.draft_updated_at,
2187
- createdAt: row.created_at,
2188
- updatedAt: row.updated_at
2189
- };
2190
- }
2191
2722
  // ========================================================================
2192
2723
  // Migrations
2193
2724
  // ========================================================================
@@ -2195,7 +2726,6 @@ var LocalAdapter = class extends chunkSA7NSSIQ_cjs.AbstractBaseAdapter {
2195
2726
  for (const config of this.collections.values()) {
2196
2727
  this.ensureTable(config);
2197
2728
  }
2198
- this.ensureDraftsTable();
2199
2729
  console.log("[LocalAdapter] Migrations complete");
2200
2730
  }
2201
2731
  async rollback() {
@@ -2253,5 +2783,5 @@ exports.validateCollection = validateCollection;
2253
2783
  exports.validateConfig = validateConfig;
2254
2784
  exports.validateFields = validateFields;
2255
2785
  exports.validateGlobal = validateGlobal;
2256
- //# sourceMappingURL=chunk-LINKCEG4.cjs.map
2257
- //# sourceMappingURL=chunk-LINKCEG4.cjs.map
2786
+ //# sourceMappingURL=chunk-E2763JUP.cjs.map
2787
+ //# sourceMappingURL=chunk-E2763JUP.cjs.map