@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,5 +1,6 @@
1
- import { WEBHOOK_EVENTS, createWebhookService, extractApiKeyFromRequest, validateApiKey, createApiKeyContext } from './chunk-QXIQWPAP.js';
2
- import { evaluateAccess } from './chunk-SDMNUYVU.js';
1
+ import { WEBHOOK_EVENTS, createWebhookService } from './chunk-3UK5XBVJ.js';
2
+ import { extractApiKeyFromRequest, validateApiKey, createApiKeyContext, hasApiKeyPermission, evaluateAccess } from './chunk-CJONKRHJ.js';
3
+ import { hasPermission } from './chunk-L4EZKIEX.js';
3
4
 
4
5
  // src/api/trpc/context.ts
5
6
  async function createContext(options) {
@@ -15,7 +16,14 @@ async function createContext(options) {
15
16
  };
16
17
  const apiKeyRaw = extractApiKeyFromRequest(options.req);
17
18
  if (apiKeyRaw) {
18
- const result = await validateApiKey(apiKeyRaw, options.db);
19
+ const result = await validateApiKey(apiKeyRaw, options.db, async (userId) => {
20
+ try {
21
+ const user = await options.db.findByID({ collection: "users", id: userId });
22
+ return user || null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ });
19
27
  if (result.valid) {
20
28
  baseContext.user = result.user || options.user;
21
29
  baseContext.tenantID = result.tenantId || options.tenantID;
@@ -53,18 +61,85 @@ async function triggerWebhook(ctx, event, payload) {
53
61
  console.error(`[Webhook] Failed to trigger ${event}:`, err);
54
62
  }
55
63
  }
64
+ function normalizeEmptyStrings(data, fields) {
65
+ if (!data || typeof data !== "object") return;
66
+ for (const field of fields) {
67
+ if (!field.name || !(field.name in data)) continue;
68
+ const val = data[field.name];
69
+ if (val === "") {
70
+ const isTextual = field.type === "text" || field.type === "textarea" || field.type === "code" || field.type === "markdown" || field.type === "email" || field.type === "password" || field.type === "color";
71
+ if (!isTextual) data[field.name] = null;
72
+ }
73
+ if (field.type === "tabs" && field.name && Array.isArray(field.tabs) && data[field.name] && typeof data[field.name] === "object") {
74
+ for (const tab of field.tabs) {
75
+ if (Array.isArray(tab.fields)) normalizeEmptyStrings(data[field.name], tab.fields);
76
+ }
77
+ } else if ((field.type === "group" || field.type === "collapsible") && field.name && Array.isArray(field.fields) && data[field.name] && typeof data[field.name] === "object") {
78
+ normalizeEmptyStrings(data[field.name], field.fields);
79
+ } else if (field.type === "array" && field.name && Array.isArray(field.fields) && Array.isArray(data[field.name])) {
80
+ for (const item of data[field.name]) {
81
+ if (item && typeof item === "object") normalizeEmptyStrings(item, field.fields);
82
+ }
83
+ } else if (field.type === "blocks" && field.name && Array.isArray(field.blocks) && Array.isArray(data[field.name])) {
84
+ for (const item of data[field.name]) {
85
+ if (!item || typeof item !== "object") continue;
86
+ const blockTypeStr = item.type || item.blockType;
87
+ if (!blockTypeStr) continue;
88
+ const blockDef = field.blocks.find((b) => b.slug === blockTypeStr);
89
+ if (!blockDef || !Array.isArray(blockDef.fields)) continue;
90
+ const target = item.data && typeof item.data === "object" ? item.data : item;
91
+ normalizeEmptyStrings(target, blockDef.fields);
92
+ }
93
+ }
94
+ }
95
+ }
96
+ async function checkTRPCAccess(config, operation, ctx) {
97
+ const accessRule = config.access?.[operation];
98
+ const apiKey = ctx.apiKey;
99
+ if (apiKey && apiKey.permissions && apiKey.permissions.length > 0) {
100
+ const resource = config.slug;
101
+ const action = operation === "read" ? "read" : operation === "create" ? "create" : "update";
102
+ const permission = `${resource}:${action}`;
103
+ if (!hasApiKeyPermission(apiKey.permissions, permission) && !hasApiKeyPermission(apiKey.permissions, `${resource}:admin`)) {
104
+ throw new Error(`Access denied: missing API key permission ${permission}`);
105
+ }
106
+ }
107
+ if (ctx.user && !(apiKey && apiKey.permissions && apiKey.permissions.length > 0)) {
108
+ const resource = config.slug;
109
+ const action = operation === "read" ? "read" : operation === "create" ? "create" : operation === "update" ? "update" : "delete";
110
+ const permission = `${resource}:${action}`;
111
+ const userHasPermission = hasPermission(
112
+ { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role },
113
+ permission
114
+ );
115
+ if (!userHasPermission && !hasPermission(
116
+ { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role },
117
+ `${resource}:admin`
118
+ )) {
119
+ if (!accessRule) {
120
+ throw new Error(`Access denied: missing RBAC permission ${permission}`);
121
+ }
122
+ }
123
+ }
124
+ if (accessRule) {
125
+ const allowed = await evaluateAccess(accessRule, {
126
+ req: ctx.req,
127
+ user: ctx.user,
128
+ tenantID: ctx.tenantID
129
+ });
130
+ if (allowed === false) throw new Error("Access denied");
131
+ } else if (!ctx.user && !ctx.apiKey) {
132
+ throw new Error("Access denied: authentication required");
133
+ }
134
+ if (ctx.tenantID) {
135
+ ctx.db.setTenantContext({ tenantId: ctx.tenantID, userId: ctx.user?.id ?? "", role: ctx.user?.role, isSuperAdmin: ctx.user?.role === "super_admin" });
136
+ }
137
+ }
56
138
  function createFindProcedure(ctx) {
57
139
  return async (input) => {
58
- const { collection, where, sort, limit, page, depth, select } = input;
140
+ const { collection, where, sort, limit, page, depth, select, draft } = input;
59
141
  const config = ctx.registry.getCollection(collection);
60
- if (config.access?.read) {
61
- const allowed = await evaluateAccess(config.access.read, {
62
- req: ctx.req,
63
- user: ctx.user,
64
- tenantID: ctx.tenantID
65
- });
66
- if (allowed === false) throw new Error("Access denied");
67
- }
142
+ await checkTRPCAccess(config, "read", ctx);
68
143
  if (config.hooks?.beforeRead) {
69
144
  for (const hook of config.hooks.beforeRead) {
70
145
  await hook({
@@ -77,6 +152,7 @@ function createFindProcedure(ctx) {
77
152
  });
78
153
  }
79
154
  }
155
+ const isDraft = draft ?? !!ctx.user;
80
156
  const result = await ctx.db.find({
81
157
  collection,
82
158
  where: where || {},
@@ -85,7 +161,8 @@ function createFindProcedure(ctx) {
85
161
  page: page || 1,
86
162
  depth: depth || 0,
87
163
  tenantID: ctx.tenantID,
88
- select
164
+ select,
165
+ draft: isDraft
89
166
  });
90
167
  if (config.hooks?.afterRead) {
91
168
  for (const doc of result.docs) {
@@ -106,23 +183,17 @@ function createFindProcedure(ctx) {
106
183
  }
107
184
  function createFindByIDProcedure(ctx) {
108
185
  return async (input) => {
109
- const { collection, id, depth, select } = input;
186
+ const { collection, id, depth, select, draft } = input;
110
187
  const config = ctx.registry.getCollection(collection);
111
- if (config.access?.read) {
112
- const allowed = await evaluateAccess(config.access.read, {
113
- req: ctx.req,
114
- user: ctx.user,
115
- tenantID: ctx.tenantID,
116
- id
117
- });
118
- if (allowed === false) throw new Error("Access denied");
119
- }
188
+ await checkTRPCAccess(config, "read", ctx);
189
+ const isDraft = draft ?? !!ctx.user;
120
190
  const doc = await ctx.db.findByID({
121
191
  collection,
122
192
  id,
123
193
  depth: depth || 0,
124
194
  tenantID: ctx.tenantID,
125
- select
195
+ select,
196
+ draft: isDraft
126
197
  });
127
198
  if (!doc) throw new Error(`Document not found: ${collection}/${id}`);
128
199
  if (config.hooks?.afterRead) {
@@ -145,15 +216,7 @@ function createCreateProcedure(ctx) {
145
216
  return async (input) => {
146
217
  const { collection, data, depth, select } = input;
147
218
  const config = ctx.registry.getCollection(collection);
148
- if (config.access?.create) {
149
- const allowed = await evaluateAccess(config.access.create, {
150
- req: ctx.req,
151
- user: ctx.user,
152
- tenantID: ctx.tenantID,
153
- data
154
- });
155
- if (allowed === false) throw new Error("Access denied");
156
- }
219
+ await checkTRPCAccess(config, "create", ctx);
157
220
  const schema = ctx.registry.getCreateZodSchema(collection);
158
221
  const validated = schema.parse(data);
159
222
  if (config.tenantScoped && ctx.tenantID) {
@@ -215,26 +278,21 @@ function createCreateProcedure(ctx) {
215
278
  }
216
279
  function createUpdateProcedure(ctx) {
217
280
  return async (input) => {
218
- const { collection, id, data, depth, select } = input;
281
+ const { collection, id, data, depth, select, baseUpdatedAt } = input;
219
282
  const config = ctx.registry.getCollection(collection);
283
+ await checkTRPCAccess(config, "update", ctx);
220
284
  const originalDoc = await ctx.db.findByID({
221
285
  collection,
222
286
  id,
223
- tenantID: ctx.tenantID
287
+ tenantID: ctx.tenantID,
288
+ draft: true
224
289
  });
225
290
  if (!originalDoc)
226
291
  throw new Error(`Document not found: ${collection}/${id}`);
227
- if (config.access?.update) {
228
- const allowed = await evaluateAccess(config.access.update, {
229
- req: ctx.req,
230
- user: ctx.user,
231
- tenantID: ctx.tenantID,
232
- id,
233
- doc: originalDoc,
234
- data
235
- });
236
- if (allowed === false) throw new Error("Access denied");
292
+ if (baseUpdatedAt && originalDoc.updatedAt && baseUpdatedAt !== originalDoc.updatedAt) {
293
+ throw new Error(`Revision conflict: document has changed since ${baseUpdatedAt}. Current updatedAt: ${originalDoc.updatedAt}`);
237
294
  }
295
+ normalizeEmptyStrings(data, config.fields);
238
296
  const schema = ctx.registry.getUpdateZodSchema(collection);
239
297
  const validated = schema.parse(data);
240
298
  if (config.tenantScoped && ctx.tenantID) {
@@ -270,14 +328,48 @@ function createUpdateProcedure(ctx) {
270
328
  if (hookResult) Object.assign(validated, hookResult);
271
329
  }
272
330
  }
273
- const doc = await ctx.db.update({
274
- collection,
275
- id,
276
- data: validated,
277
- depth: depth || 0,
278
- tenantID: ctx.tenantID,
279
- select
280
- });
331
+ const isDraft = ctx.req && typeof ctx.req.headers?.get === "function" && ctx.req.headers.get("x-draft") === "true" || input.draft === true;
332
+ const isDraftEnabled = config.versions?.drafts === true;
333
+ const isAutosave = ctx.req?.query?.autosave === "true" || ctx.req?.url?.includes("autosave=true") || input.autosave === true;
334
+ let doc;
335
+ if (isDraftEnabled && isDraft) {
336
+ await ctx.db.createVersion({
337
+ collection,
338
+ documentId: id,
339
+ data: validated,
340
+ status: "draft",
341
+ autosave: isAutosave,
342
+ createdBy: ctx.user?.id,
343
+ tenantID: ctx.tenantID
344
+ });
345
+ doc = await ctx.db.findByID({ collection, id, tenantID: ctx.tenantID, draft: true });
346
+ } else if (isDraftEnabled) {
347
+ doc = await ctx.db.update({
348
+ collection,
349
+ id,
350
+ data: { ...validated, status: "published" },
351
+ depth: depth || 0,
352
+ tenantID: ctx.tenantID,
353
+ select
354
+ });
355
+ await ctx.db.createVersion({
356
+ collection,
357
+ documentId: id,
358
+ data: validated,
359
+ status: "published",
360
+ createdBy: ctx.user?.id,
361
+ tenantID: ctx.tenantID
362
+ });
363
+ } else {
364
+ doc = await ctx.db.update({
365
+ collection,
366
+ id,
367
+ data: validated,
368
+ depth: depth || 0,
369
+ tenantID: ctx.tenantID,
370
+ select
371
+ });
372
+ }
281
373
  if (config.hooks?.afterChange) {
282
374
  for (const hook of config.hooks.afterChange) {
283
375
  await hook({
@@ -306,23 +398,15 @@ function createDeleteProcedure(ctx) {
306
398
  return async (input) => {
307
399
  const { collection, id } = input;
308
400
  const config = ctx.registry.getCollection(collection);
401
+ await checkTRPCAccess(config, "delete", ctx);
309
402
  const originalDoc = await ctx.db.findByID({
310
403
  collection,
311
404
  id,
312
- tenantID: ctx.tenantID
405
+ tenantID: ctx.tenantID,
406
+ draft: true
313
407
  });
314
408
  if (!originalDoc)
315
409
  throw new Error(`Document not found: ${collection}/${id}`);
316
- if (config.access?.delete) {
317
- const allowed = await evaluateAccess(config.access.delete, {
318
- req: ctx.req,
319
- user: ctx.user,
320
- tenantID: ctx.tenantID,
321
- id,
322
- doc: originalDoc
323
- });
324
- if (allowed === false) throw new Error("Access denied");
325
- }
326
410
  if (config.hooks?.beforeDelete) {
327
411
  for (const hook of config.hooks.beforeDelete) {
328
412
  await hook({
@@ -367,14 +451,7 @@ function createCountProcedure(ctx) {
367
451
  return async (input) => {
368
452
  const { collection, where } = input;
369
453
  const config = ctx.registry.getCollection(collection);
370
- if (config.access?.read) {
371
- const allowed = await evaluateAccess(config.access.read, {
372
- req: ctx.req,
373
- user: ctx.user,
374
- tenantID: ctx.tenantID
375
- });
376
- if (allowed === false) throw new Error("Access denied");
377
- }
454
+ await checkTRPCAccess(config, "read", ctx);
378
455
  const totalDocs = await ctx.db.count({
379
456
  collection,
380
457
  where: where || {},
@@ -385,6 +462,44 @@ function createCountProcedure(ctx) {
385
462
  }
386
463
 
387
464
  // src/api/trpc/router.ts
465
+ async function checkGlobalAccessTRPC(global, operation, ctx) {
466
+ const accessRule = global.access?.[operation];
467
+ const apiKey = ctx.apiKey;
468
+ if (apiKey && apiKey.permissions && apiKey.permissions.length > 0) {
469
+ const permission = `globals:${operation}`;
470
+ if (!hasApiKeyPermission(apiKey.permissions, permission) && !hasApiKeyPermission(apiKey.permissions, "globals:admin")) {
471
+ throw new Error(`Access denied: missing API key permission ${permission}`);
472
+ }
473
+ }
474
+ if (ctx.user) {
475
+ const permission = `globals:${operation}`;
476
+ const userHasPermission = hasPermission(
477
+ { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role },
478
+ permission
479
+ );
480
+ if (!userHasPermission && !hasPermission(
481
+ { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role },
482
+ "globals:admin"
483
+ )) {
484
+ if (!accessRule) {
485
+ throw new Error(`Access denied: missing RBAC permission ${permission}`);
486
+ }
487
+ }
488
+ }
489
+ if (accessRule) {
490
+ const allowed = await evaluateAccess(accessRule, {
491
+ req: ctx.req,
492
+ user: ctx.user,
493
+ tenantID: ctx.tenantID
494
+ });
495
+ if (allowed === false) throw new Error("Access denied");
496
+ } else if (!ctx.user && !ctx.apiKey) {
497
+ throw new Error("Access denied: authentication required");
498
+ }
499
+ if (ctx.tenantID) {
500
+ ctx.db.setTenantContext({ tenantId: ctx.tenantID, userId: ctx.user?.id ?? "", role: ctx.user?.role, isSuperAdmin: ctx.user?.role === "super_admin" });
501
+ }
502
+ }
388
503
  function createDynamicRouter(ctx) {
389
504
  const router = {};
390
505
  const collections = ctx.registry.getCollections();
@@ -404,6 +519,7 @@ function createDynamicRouter(ctx) {
404
519
  const slug = global.slug;
405
520
  router[`_globals_${slug}`] = {
406
521
  get: async () => {
522
+ await checkGlobalAccessTRPC(global, "read", ctx);
407
523
  const doc = await ctx.db.findOne({
408
524
  collection: `_globals_${slug}`,
409
525
  where: {},
@@ -412,13 +528,29 @@ function createDynamicRouter(ctx) {
412
528
  return doc;
413
529
  },
414
530
  update: async (input) => {
531
+ await checkGlobalAccessTRPC(global, "update", ctx);
415
532
  const schema = ctx.registry.getZodSchema(slug);
416
533
  const validated = schema.parse(input.data);
417
- const doc = await ctx.db.create({
534
+ const existing = await ctx.db.findOne({
418
535
  collection: `_globals_${slug}`,
419
- data: { ...validated, id: slug },
536
+ where: {},
420
537
  tenantID: ctx.tenantID
421
538
  });
539
+ let doc;
540
+ if (existing) {
541
+ doc = await ctx.db.update({
542
+ collection: `_globals_${slug}`,
543
+ id: existing.id,
544
+ data: validated,
545
+ tenantID: ctx.tenantID
546
+ });
547
+ } else {
548
+ doc = await ctx.db.create({
549
+ collection: `_globals_${slug}`,
550
+ data: { ...validated, id: slug },
551
+ tenantID: ctx.tenantID
552
+ });
553
+ }
422
554
  return doc;
423
555
  }
424
556
  };
@@ -434,5 +566,5 @@ function createKyroServer(ctx) {
434
566
  }
435
567
 
436
568
  export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure };
437
- //# sourceMappingURL=chunk-3AJE4SEG.js.map
438
- //# sourceMappingURL=chunk-3AJE4SEG.js.map
569
+ //# sourceMappingURL=chunk-OHC6UHFY.js.map
570
+ //# sourceMappingURL=chunk-OHC6UHFY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/trpc/context.ts","../src/api/trpc/procedures.ts","../src/api/trpc/router.ts"],"names":[],"mappings":"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","file":"chunk-OHC6UHFY.js","sourcesContent":["import type { BaseAdapter } from \"../../registry/types.js\";\nimport type { User, Request } from \"../../hooks/types.js\";\nimport {\n validateApiKey,\n extractApiKeyFromRequest,\n createApiKeyContext,\n} from \"../../auth/api-key.js\";\nimport { createWebhookService } from \"../../webhooks/index.js\";\n\n// ============================================================================\n// Context Types\n// ============================================================================\n\nexport interface ApiKeyContext {\n userId: string;\n user: Partial<User>;\n permissions: string[];\n apiKeyId: string;\n tenantId?: string;\n role?: string;\n}\n\nexport interface KyroContext {\n db: BaseAdapter;\n registry: any;\n user?: User;\n tenantID?: string;\n req: Request;\n apiKey?: ApiKeyContext;\n webhookService?: ReturnType<typeof createWebhookService>;\n settings?: Record<string, any>;\n [key: string]: any;\n}\n\n// ============================================================================\n// Context Factory\n// ============================================================================\n\nexport async function createContext(options: {\n db: BaseAdapter;\n registry: any;\n req: Request;\n user?: User;\n tenantID?: string;\n settings?: Record<string, any>;\n}): Promise<KyroContext> {\n const webhookService = createWebhookService(options.db);\n\n const baseContext: KyroContext = {\n db: options.db,\n registry: options.registry,\n req: options.req,\n user: options.user,\n tenantID: options.tenantID,\n webhookService,\n settings: options.settings,\n };\n\n const apiKeyRaw = extractApiKeyFromRequest(options.req as any);\n if (apiKeyRaw) {\n const result = await validateApiKey(apiKeyRaw, options.db, async (userId) => {\n try {\n const user = await options.db.findByID({ collection: 'users', id: userId });\n return user || null;\n } catch {\n return null;\n }\n });\n if (result.valid) {\n baseContext.user = (result.user as User) || options.user;\n baseContext.tenantID = result.tenantId || options.tenantID;\n baseContext.apiKey = createApiKeyContext(result) as ApiKeyContext;\n }\n }\n\n return baseContext;\n}\n","import type {\n FindArgs,\n CreateArgs,\n UpdateArgs,\n DeleteArgs,\n} from \"../../registry/types.js\";\nimport { runHooks } from \"../../hooks/types.js\";\nimport { evaluateAccess } from \"../../access/types.js\";\nexport type { KyroContext, ApiKeyContext } from \"./context.js\";\nexport { createContext } from \"./context.js\";\nimport type { KyroContext, ApiKeyContext } from \"./context.js\";\nimport type { Field } from \"../../fields/types.js\";\nimport { WEBHOOK_EVENTS, type WebhookEvent } from \"../../webhooks/types.js\";\nimport { hasApiKeyPermission } from \"../../auth/api-key.js\";\nimport { hasPermission } from \"../../auth/rbac/checker.js\";\n\nconst COLLECTION_EVENT_MAP: Record<\n string,\n { create: WebhookEvent; update: WebhookEvent; delete: WebhookEvent }\n> = {\n _media: {\n create: WEBHOOK_EVENTS.MEDIA_UPLOAD,\n update: WEBHOOK_EVENTS.MEDIA_UPLOAD,\n delete: WEBHOOK_EVENTS.MEDIA_DELETE,\n },\n};\n\nfunction getWebhookEvent(\n collection: string,\n operation: \"create\" | \"update\" | \"delete\",\n): WebhookEvent {\n const mapped = COLLECTION_EVENT_MAP[collection];\n if (mapped) return mapped[operation];\n return `collection.${operation}` as WebhookEvent;\n}\n\nasync function triggerWebhook(\n ctx: KyroContext,\n event: WebhookEvent,\n payload: {\n collection: string;\n data: unknown;\n previousData?: unknown;\n operation: \"create\" | \"update\" | \"delete\";\n },\n) {\n if (!ctx.webhookService) return;\n try {\n await ctx.webhookService.trigger(event, {\n collection: payload.collection,\n operation: payload.operation,\n data: payload.data,\n previousData: payload.previousData,\n user: ctx.user\n ? { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role }\n : undefined,\n tenantId: ctx.tenantID,\n });\n } catch (err) {\n console.error(`[Webhook] Failed to trigger ${event}:`, err);\n }\n}\n\n// ============================================================================\n// Data normalization helpers\n// ============================================================================\n\nfunction normalizeEmptyStrings(data: any, fields: Field[]): void {\n if (!data || typeof data !== 'object') return;\n for (const field of fields) {\n if (!field.name || !(field.name in data)) continue;\n const val = data[field.name];\n if (val === \"\") {\n const isTextual = field.type === 'text' || field.type === 'textarea' || field.type === 'code' || field.type === 'markdown' || field.type === 'email' || field.type === 'password' || field.type === 'color';\n if (!isTextual) data[field.name] = null;\n }\n if (field.type === 'tabs' && field.name && Array.isArray((field as any).tabs) && data[field.name] && typeof data[field.name] === 'object') {\n for (const tab of (field as any).tabs) {\n if (Array.isArray(tab.fields)) normalizeEmptyStrings(data[field.name], tab.fields as Field[]);\n }\n } else if ((field.type === 'group' || field.type === 'collapsible') && field.name && Array.isArray((field as any).fields) && data[field.name] && typeof data[field.name] === 'object') {\n normalizeEmptyStrings(data[field.name], (field as any).fields as Field[]);\n } else if (field.type === 'array' && field.name && Array.isArray((field as any).fields) && Array.isArray(data[field.name])) {\n for (const item of data[field.name]) {\n if (item && typeof item === 'object') normalizeEmptyStrings(item, (field as any).fields as Field[]);\n }\n } else if (field.type === 'blocks' && field.name && Array.isArray((field as any).blocks) && Array.isArray(data[field.name])) {\n for (const item of data[field.name]) {\n if (!item || typeof item !== 'object') continue;\n const blockTypeStr = item.type || item.blockType;\n if (!blockTypeStr) continue;\n const blockDef = (field as any).blocks.find((b: any) => b.slug === blockTypeStr);\n if (!blockDef || !Array.isArray(blockDef.fields)) continue;\n const target = item.data && typeof item.data === 'object' ? item.data : item;\n normalizeEmptyStrings(target, blockDef.fields as Field[]);\n }\n }\n }\n}\n\n// ============================================================================\n// Access Check Helper\n// ============================================================================\n\nasync function checkTRPCAccess(\n config: { access?: any; slug: string },\n operation: \"read\" | \"create\" | \"update\" | \"delete\",\n ctx: KyroContext,\n): Promise<void> {\n const accessRule = config.access?.[operation];\n\n // API key permission check\n const apiKey = ctx.apiKey;\n if (apiKey && apiKey.permissions && apiKey.permissions.length > 0) {\n const resource = config.slug;\n const action = operation === \"read\" ? \"read\" : operation === \"create\" ? \"create\" : \"update\";\n const permission = `${resource}:${action}`;\n if (\n !hasApiKeyPermission(apiKey.permissions, permission) &&\n !hasApiKeyPermission(apiKey.permissions, `${resource}:admin`)\n ) {\n throw new Error(`Access denied: missing API key permission ${permission}`);\n }\n }\n\n // RBAC check for authenticated users (skip when API key permissions are authoritative)\n if (ctx.user && !(apiKey && apiKey.permissions && apiKey.permissions.length > 0)) {\n const resource = config.slug;\n const action = operation === \"read\" ? \"read\" : operation === \"create\" ? \"create\" : operation === \"update\" ? \"update\" : \"delete\";\n const permission = `${resource}:${action}`;\n const userHasPermission = hasPermission(\n { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role } as any,\n permission,\n );\n if (!userHasPermission && !hasPermission(\n { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role } as any,\n `${resource}:admin`,\n )) {\n if (!accessRule) {\n throw new Error(`Access denied: missing RBAC permission ${permission}`);\n }\n }\n }\n\n if (accessRule) {\n const allowed = await evaluateAccess(accessRule, {\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n });\n if (allowed === false) throw new Error(\"Access denied\");\n } else if (!ctx.user && !ctx.apiKey) {\n throw new Error(\"Access denied: authentication required\");\n }\n\n // Set tenant context\n if (ctx.tenantID) {\n ctx.db.setTenantContext({ tenantId: ctx.tenantID, userId: ctx.user?.id ?? '', role: ctx.user?.role, isSuperAdmin: ctx.user?.role === 'super_admin' });\n }\n}\n\n// ============================================================================\n// CRUD Procedure Builders\n// ============================================================================\n\nexport function createFindProcedure(ctx: KyroContext) {\n return async (input: {\n collection: string;\n where?: Record<string, any>;\n sort?: string;\n limit?: number;\n page?: number;\n depth?: number;\n select?: string[];\n draft?: boolean;\n }) => {\n const { collection, where, sort, limit, page, depth, select, draft } = input;\n const config = ctx.registry.getCollection(collection);\n\n await checkTRPCAccess(config, \"read\", ctx);\n\n // Run beforeRead hooks\n if (config.hooks?.beforeRead) {\n for (const hook of config.hooks.beforeRead) {\n await hook({\n collection,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"read\",\n where,\n });\n }\n }\n\n const isDraft = draft ?? !!ctx.user;\n\n // Execute query\n const result = await ctx.db.find({\n collection,\n where: where || {},\n sort,\n limit: limit || 10,\n page: page || 1,\n depth: depth || 0,\n tenantID: ctx.tenantID,\n select,\n draft: isDraft,\n });\n\n // Run afterRead hooks\n if (config.hooks?.afterRead) {\n for (const doc of result.docs) {\n for (const hook of config.hooks.afterRead) {\n await hook({\n collection,\n doc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"read\",\n });\n }\n }\n }\n\n return result;\n };\n}\n\nexport function createFindByIDProcedure(ctx: KyroContext) {\n return async (input: {\n collection: string;\n id: string;\n depth?: number;\n select?: string[];\n draft?: boolean;\n }) => {\n const { collection, id, depth, select, draft } = input;\n const config = ctx.registry.getCollection(collection);\n\n await checkTRPCAccess(config, \"read\", ctx);\n\n const isDraft = draft ?? !!ctx.user;\n\n const doc = await ctx.db.findByID({\n collection,\n id,\n depth: depth || 0,\n tenantID: ctx.tenantID,\n select,\n draft: isDraft,\n });\n\n if (!doc) throw new Error(`Document not found: ${collection}/${id}`);\n\n // Run afterRead hooks\n if (config.hooks?.afterRead) {\n for (const hook of config.hooks.afterRead) {\n await hook({\n collection,\n doc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"read\",\n id,\n });\n }\n }\n\n return doc;\n };\n}\n\nexport function createCreateProcedure(ctx: KyroContext) {\n return async (input: {\n collection: string;\n data: Record<string, any>;\n depth?: number;\n select?: string[];\n }) => {\n const { collection, data, depth, select } = input;\n const config = ctx.registry.getCollection(collection);\n\n await checkTRPCAccess(config, \"create\", ctx);\n\n // Validate with Zod\n const schema = ctx.registry.getCreateZodSchema(collection);\n const validated = schema.parse(data);\n\n // Add tenantID if scoped\n if (config.tenantScoped && ctx.tenantID) {\n validated.tenantID = ctx.tenantID;\n }\n\n // Run beforeValidate hooks\n if (config.hooks?.beforeValidate) {\n for (const hook of config.hooks.beforeValidate) {\n const hookResult = await hook({\n collection,\n data: validated,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"create\",\n });\n if (hookResult) Object.assign(validated, hookResult);\n }\n }\n\n // Run beforeChange hooks\n if (config.hooks?.beforeChange) {\n for (const hook of config.hooks.beforeChange) {\n const hookResult = await hook({\n collection,\n data: validated,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"create\",\n });\n if (hookResult) Object.assign(validated, hookResult);\n }\n }\n\n // Execute create\n const doc = await ctx.db.create({\n collection,\n data: validated,\n depth: depth || 0,\n tenantID: ctx.tenantID,\n select,\n });\n\n // Run afterChange hooks\n if (config.hooks?.afterChange) {\n for (const hook of config.hooks.afterChange) {\n await hook({\n collection,\n doc,\n data: validated,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"create\",\n });\n }\n }\n\n await triggerWebhook(ctx, getWebhookEvent(collection, \"create\"), {\n collection,\n data: doc,\n operation: \"create\",\n });\n\n return { doc };\n };\n}\n\nexport function createUpdateProcedure(ctx: KyroContext) {\n return async (input: {\n collection: string;\n id: string;\n data: Record<string, any>;\n depth?: number;\n select?: string[];\n baseUpdatedAt?: string;\n }) => {\n const { collection, id, data, depth, select, baseUpdatedAt } = input;\n const config = ctx.registry.getCollection(collection);\n\n await checkTRPCAccess(config, \"update\", ctx);\n\n // Get original doc for hooks + conflict detection\n const originalDoc = await ctx.db.findByID({\n collection,\n id,\n tenantID: ctx.tenantID,\n draft: true,\n });\n\n if (!originalDoc)\n throw new Error(`Document not found: ${collection}/${id}`);\n\n // Revision conflict detection\n if (baseUpdatedAt && (originalDoc as Record<string, any>).updatedAt && baseUpdatedAt !== (originalDoc as Record<string, any>).updatedAt) {\n throw new Error(`Revision conflict: document has changed since ${baseUpdatedAt}. Current updatedAt: ${(originalDoc as Record<string, any>).updatedAt}`);\n }\n\n // Normalize empty strings for non-textual field types\n normalizeEmptyStrings(data as any, config.fields as any);\n\n // Validate with Zod\n const schema = ctx.registry.getUpdateZodSchema(collection);\n const validated = schema.parse(data);\n\n // Add tenantID if scoped\n if (config.tenantScoped && ctx.tenantID) {\n validated.tenantID = ctx.tenantID;\n }\n\n // Run beforeValidate hooks\n if (config.hooks?.beforeValidate) {\n for (const hook of config.hooks.beforeValidate) {\n const hookResult = await hook({\n collection,\n data: validated,\n originalDoc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"update\",\n id,\n });\n if (hookResult) Object.assign(validated, hookResult);\n }\n }\n\n // Run beforeChange hooks\n if (config.hooks?.beforeChange) {\n for (const hook of config.hooks.beforeChange) {\n const hookResult = await hook({\n collection,\n data: validated,\n originalDoc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"update\",\n id,\n });\n if (hookResult) Object.assign(validated, hookResult);\n }\n }\n\n // Determine if this is a draft save vs publish\n const isDraft = (ctx.req && typeof (ctx.req as any).headers?.get === \"function\" && (ctx.req as any).headers.get(\"x-draft\") === \"true\") || (input as any).draft === true;\n const isDraftEnabled = config.versions?.drafts === true;\n const isAutosave = ((ctx.req as any)?.query?.autosave === \"true\") || ((ctx.req as any)?.url?.includes(\"autosave=true\")) || (input as any).autosave === true;\n\n let doc;\n if (isDraftEnabled && isDraft) {\n // Draft save: versions table only\n // Autosave reuses a single version slot; manual draft creates a new version\n await ctx.db.createVersion({\n collection,\n documentId: id,\n data: validated,\n status: 'draft',\n autosave: isAutosave,\n createdBy: ctx.user?.id,\n tenantID: ctx.tenantID,\n });\n // Refetch merged doc\n doc = await ctx.db.findByID({ collection, id, tenantID: ctx.tenantID, draft: true });\n } else if (isDraftEnabled) {\n // Publish: main doc + versions table\n doc = await ctx.db.update({\n collection,\n id,\n data: { ...validated, status: 'published' },\n depth: depth || 0,\n tenantID: ctx.tenantID,\n select,\n });\n await ctx.db.createVersion({\n collection,\n documentId: id,\n data: validated,\n status: 'published',\n createdBy: ctx.user?.id,\n tenantID: ctx.tenantID,\n });\n } else {\n // No versions: direct update\n doc = await ctx.db.update({\n collection,\n id,\n data: validated,\n depth: depth || 0,\n tenantID: ctx.tenantID,\n select,\n });\n }\n\n // Run afterChange hooks\n if (config.hooks?.afterChange) {\n for (const hook of config.hooks.afterChange) {\n await hook({\n collection,\n doc,\n data: validated,\n originalDoc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"update\",\n id,\n });\n }\n }\n\n await triggerWebhook(ctx, getWebhookEvent(collection, \"update\"), {\n collection,\n data: doc,\n previousData: originalDoc,\n operation: \"update\",\n });\n\n return { doc };\n };\n}\n\nexport function createDeleteProcedure(ctx: KyroContext) {\n return async (input: { collection: string; id: string }) => {\n const { collection, id } = input;\n const config = ctx.registry.getCollection(collection);\n\n await checkTRPCAccess(config, \"delete\", ctx);\n\n // Get original doc for hooks\n const originalDoc = await ctx.db.findByID({\n collection,\n id,\n tenantID: ctx.tenantID,\n draft: true,\n });\n\n if (!originalDoc)\n throw new Error(`Document not found: ${collection}/${id}`);\n\n // Run beforeDelete hooks\n if (config.hooks?.beforeDelete) {\n for (const hook of config.hooks.beforeDelete) {\n await hook({\n collection,\n doc: originalDoc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"delete\",\n id,\n });\n }\n }\n\n // Execute delete\n const doc = await ctx.db.delete({\n collection,\n id,\n tenantID: ctx.tenantID,\n });\n\n // Run afterDelete hooks\n if (config.hooks?.afterDelete) {\n for (const hook of config.hooks.afterDelete) {\n await hook({\n collection,\n doc,\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n operation: \"delete\",\n id,\n });\n }\n }\n\n await triggerWebhook(ctx, getWebhookEvent(collection, \"delete\"), {\n collection,\n data: doc,\n previousData: originalDoc,\n operation: \"delete\",\n });\n\n return { doc, message: \"Deleted successfully\" };\n };\n}\n\nexport function createCountProcedure(ctx: KyroContext) {\n return async (input: { collection: string; where?: Record<string, any> }) => {\n const { collection, where } = input;\n const config = ctx.registry.getCollection(collection);\n\n await checkTRPCAccess(config, \"read\", ctx);\n\n const totalDocs = await ctx.db.count({\n collection,\n where: where || {},\n tenantID: ctx.tenantID,\n });\n\n return { totalDocs };\n };\n}\n","import type { KyroContext } from \"./context.js\";\nimport {\n createFindProcedure,\n createFindByIDProcedure,\n createCreateProcedure,\n createUpdateProcedure,\n createDeleteProcedure,\n createCountProcedure,\n} from \"./procedures.js\";\nimport { evaluateAccess } from \"../../access/types.js\";\nimport { hasPermission } from \"../../auth/rbac/checker.js\";\nimport { hasApiKeyPermission } from \"../../auth/api-key.js\";\n\n// ============================================================================\n// Global Access Check Helper\n// ============================================================================\n\nasync function checkGlobalAccessTRPC(\n global: { access?: any; slug: string },\n operation: \"read\" | \"update\",\n ctx: KyroContext,\n): Promise<void> {\n const accessRule = global.access?.[operation];\n\n const apiKey = ctx.apiKey;\n if (apiKey && apiKey.permissions && apiKey.permissions.length > 0) {\n const permission = `globals:${operation}`;\n if (\n !hasApiKeyPermission(apiKey.permissions, permission) &&\n !hasApiKeyPermission(apiKey.permissions, \"globals:admin\")\n ) {\n throw new Error(`Access denied: missing API key permission ${permission}`);\n }\n }\n\n if (ctx.user) {\n const permission = `globals:${operation}`;\n const userHasPermission = hasPermission(\n { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role } as any,\n permission,\n );\n if (!userHasPermission && !hasPermission(\n { id: ctx.user.id, email: ctx.user.email, role: ctx.user.role } as any,\n \"globals:admin\",\n )) {\n if (!accessRule) {\n throw new Error(`Access denied: missing RBAC permission ${permission}`);\n }\n }\n }\n\n if (accessRule) {\n const allowed = await evaluateAccess(accessRule, {\n req: ctx.req,\n user: ctx.user,\n tenantID: ctx.tenantID,\n });\n if (allowed === false) throw new Error(\"Access denied\");\n } else if (!ctx.user && !ctx.apiKey) {\n throw new Error(\"Access denied: authentication required\");\n }\n\n if (ctx.tenantID) {\n ctx.db.setTenantContext({ tenantId: ctx.tenantID, userId: ctx.user?.id ?? '', role: ctx.user?.role, isSuperAdmin: ctx.user?.role === 'super_admin' });\n }\n}\n\n// ============================================================================\n// Dynamic Router Generator\n// ============================================================================\n\nexport function createDynamicRouter(ctx: KyroContext) {\n const router: Record<string, any> = {};\n const collections = ctx.registry.getCollections();\n\n for (const collection of collections) {\n const slug = collection.slug;\n\n router[slug] = {\n find: createFindProcedure(ctx),\n findByID: createFindByIDProcedure(ctx),\n create: createCreateProcedure(ctx),\n update: createUpdateProcedure(ctx),\n delete: createDeleteProcedure(ctx),\n count: createCountProcedure(ctx),\n };\n }\n\n // Add globals\n const globals = ctx.registry.getGlobals();\n for (const global of globals) {\n const slug = global.slug;\n\n router[`_globals_${slug}`] = {\n get: async () => {\n await checkGlobalAccessTRPC(global, \"read\", ctx);\n\n const doc = await ctx.db.findOne({\n collection: `_globals_${slug}`,\n where: {},\n tenantID: ctx.tenantID,\n });\n return doc;\n },\n update: async (input: { data: Record<string, any> }) => {\n await checkGlobalAccessTRPC(global, \"update\", ctx);\n\n const schema = ctx.registry.getZodSchema(slug);\n const validated = schema.parse(input.data);\n\n const existing = await ctx.db.findOne({\n collection: `_globals_${slug}`,\n where: {},\n tenantID: ctx.tenantID,\n });\n\n let doc;\n if (existing) {\n doc = await ctx.db.update({\n collection: `_globals_${slug}`,\n id: existing.id,\n data: validated,\n tenantID: ctx.tenantID,\n });\n } else {\n doc = await ctx.db.create({\n collection: `_globals_${slug}`,\n data: { ...validated, id: slug },\n tenantID: ctx.tenantID,\n });\n }\n\n return doc;\n },\n };\n }\n\n return router;\n}\n\n// ============================================================================\n// Typed Router Interface\n// ============================================================================\n\nexport interface KyroRouter {\n [collectionSlug: string]: {\n find: (input: {\n where?: Record<string, any>;\n sort?: string;\n limit?: number;\n page?: number;\n depth?: number;\n select?: string[];\n draft?: boolean;\n }) => Promise<{\n docs: any[];\n totalDocs: number;\n limit: number;\n totalPages: number;\n page: number;\n pagingCounter: number;\n hasPrevPage: boolean;\n hasNextPage: boolean;\n prevPage: number | null;\n nextPage: number | null;\n }>;\n findByID: (input: {\n id: string;\n depth?: number;\n select?: string[];\n draft?: boolean;\n }) => Promise<any>;\n create: (input: {\n data: Record<string, any>;\n depth?: number;\n select?: string[];\n }) => Promise<{ doc: any }>;\n update: (input: {\n id: string;\n data: Record<string, any>;\n depth?: number;\n select?: string[];\n baseUpdatedAt?: string;\n }) => Promise<{ doc: any }>;\n delete: (input: { id: string }) => Promise<{ doc: any; message: string }>;\n count: (input: {\n where?: Record<string, any>;\n }) => Promise<{ totalDocs: number }>;\n };\n}\n\n// ============================================================================\n// Server Entry\n// ============================================================================\n\nexport function createKyroServer(ctx: KyroContext): KyroRouter {\n // Check if tRPC is disabled in settings\n const apiAccess = ctx.settings?.access?.apiAccess;\n if (apiAccess?.trpcEnabled === false) {\n throw new Error(\"tRPC API is disabled\");\n }\n\n return createDynamicRouter(ctx) as KyroRouter;\n}\n"]}