@rakun-kit/core 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (833) hide show
  1. package/README.md +1387 -1280
  2. package/dist/cjs/api/operations/manager-contract.js +49 -0
  3. package/dist/cjs/api/operations/manager-contract.js.map +1 -1
  4. package/dist/cjs/api/operations/manager-manifest.js +30 -0
  5. package/dist/cjs/api/operations/manager-manifest.js.map +1 -1
  6. package/dist/cjs/api/operations/manager.js +20 -0
  7. package/dist/cjs/api/operations/manager.js.map +1 -1
  8. package/dist/cjs/api/realtime.js +144 -0
  9. package/dist/cjs/api/realtime.js.map +1 -0
  10. package/dist/cjs/api/routes/manager/auth/externalLogin.js +7 -5
  11. package/dist/cjs/api/routes/manager/auth/externalLogin.js.map +1 -1
  12. package/dist/cjs/api/routes/manager/auth/mfa/verifyRecoveryCode.js +2 -1
  13. package/dist/cjs/api/routes/manager/auth/mfa/verifyRecoveryCode.js.map +1 -1
  14. package/dist/cjs/api/routes/manager/auth/passwordRecovery.js +3 -3
  15. package/dist/cjs/api/routes/manager/auth/passwordRecovery.js.map +1 -1
  16. package/dist/cjs/api/routes/manager/auth/totp/enrollTotp.js +2 -2
  17. package/dist/cjs/api/routes/manager/auth/totp/enrollTotp.js.map +1 -1
  18. package/dist/cjs/api/routes/manager/auth/totp/verifyTotp.js +2 -1
  19. package/dist/cjs/api/routes/manager/auth/totp/verifyTotp.js.map +1 -1
  20. package/dist/cjs/api/routes/manager/auth/webauthn/webauthnAuthVerify.js +2 -1
  21. package/dist/cjs/api/routes/manager/auth/webauthn/webauthnAuthVerify.js.map +1 -1
  22. package/dist/cjs/api/routes/manager/auth/webauthn/webauthnRegisterOptions.js +2 -2
  23. package/dist/cjs/api/routes/manager/auth/webauthn/webauthnRegisterOptions.js.map +1 -1
  24. package/dist/cjs/api/routes/manager/auth/webauthn/webauthnRegisterVerify.js +2 -1
  25. package/dist/cjs/api/routes/manager/auth/webauthn/webauthnRegisterVerify.js.map +1 -1
  26. package/dist/cjs/api/routes/manager/collaborationBinary.js +8 -0
  27. package/dist/cjs/api/routes/manager/collaborationBinary.js.map +1 -0
  28. package/dist/cjs/api/routes/manager/comments.js +4 -0
  29. package/dist/cjs/api/routes/manager/comments.js.map +1 -1
  30. package/dist/cjs/api/routes/manager/contentCollaboration.js +102 -0
  31. package/dist/cjs/api/routes/manager/contentCollaboration.js.map +1 -0
  32. package/dist/cjs/api/routes/manager/contentVersions.js +3 -0
  33. package/dist/cjs/api/routes/manager/contentVersions.js.map +1 -1
  34. package/dist/cjs/api/routes/manager/delete.js +15 -0
  35. package/dist/cjs/api/routes/manager/delete.js.map +1 -1
  36. package/dist/cjs/api/routes/manager/duplicate.js +2 -2
  37. package/dist/cjs/api/routes/manager/duplicate.js.map +1 -1
  38. package/dist/cjs/api/routes/manager/localeVariants.js +9 -1
  39. package/dist/cjs/api/routes/manager/localeVariants.js.map +1 -1
  40. package/dist/cjs/api/routes/manager/notifications.js +2 -0
  41. package/dist/cjs/api/routes/manager/notifications.js.map +1 -1
  42. package/dist/cjs/api/routes/manager/template.js +3 -2
  43. package/dist/cjs/api/routes/manager/template.js.map +1 -1
  44. package/dist/cjs/api/routes/manager/templateCollaboration.js +101 -0
  45. package/dist/cjs/api/routes/manager/templateCollaboration.js.map +1 -0
  46. package/dist/cjs/api/routes/manager/translateDocument.js +4 -22
  47. package/dist/cjs/api/routes/manager/translateDocument.js.map +1 -1
  48. package/dist/cjs/api/routes/manager/trash.js +4 -0
  49. package/dist/cjs/api/routes/manager/trash.js.map +1 -1
  50. package/dist/cjs/api/routes/manager/uiLocales.js +13 -1
  51. package/dist/cjs/api/routes/manager/uiLocales.js.map +1 -1
  52. package/dist/cjs/api/routes/manager/update.js +4 -0
  53. package/dist/cjs/api/routes/manager/update.js.map +1 -1
  54. package/dist/cjs/api/routes/web/llms.js +5 -5
  55. package/dist/cjs/api/routes/web/llms.js.map +1 -1
  56. package/dist/cjs/api/routes/web/seo.js +2 -2
  57. package/dist/cjs/api/routes/web/seo.js.map +1 -1
  58. package/dist/cjs/api/utils/contentTemplate.js +11 -14
  59. package/dist/cjs/api/utils/contentTemplate.js.map +1 -1
  60. package/dist/cjs/api/utils/iteratorVisibility.js +5 -5
  61. package/dist/cjs/api/utils/iteratorVisibility.js.map +1 -1
  62. package/dist/cjs/api/utils/managerNotifications.js +4 -1
  63. package/dist/cjs/api/utils/managerNotifications.js.map +1 -1
  64. package/dist/cjs/api/utils/mediaUploadToken.js +3 -3
  65. package/dist/cjs/api/utils/mediaUploadToken.js.map +1 -1
  66. package/dist/cjs/api/utils/passwords.js +13 -0
  67. package/dist/cjs/api/utils/passwords.js.map +1 -1
  68. package/dist/cjs/api/utils/previewToken.js +2 -2
  69. package/dist/cjs/api/utils/previewToken.js.map +1 -1
  70. package/dist/cjs/api/utils/realtime.js +20 -0
  71. package/dist/cjs/api/utils/realtime.js.map +1 -0
  72. package/dist/cjs/api/utils/recoveryCodes.js +7 -4
  73. package/dist/cjs/api/utils/recoveryCodes.js.map +1 -1
  74. package/dist/cjs/api/utils/safeManagerQuery.js +5 -5
  75. package/dist/cjs/api/utils/safeManagerQuery.js.map +1 -1
  76. package/dist/cjs/api/utils/sanitizeManagerOutput.js +2 -2
  77. package/dist/cjs/api/utils/sanitizeManagerOutput.js.map +1 -1
  78. package/dist/cjs/auth/completePrimaryAuthentication.js +3 -3
  79. package/dist/cjs/auth/completePrimaryAuthentication.js.map +1 -1
  80. package/dist/cjs/bootstrapState.js.map +1 -1
  81. package/dist/cjs/client.js +12 -1
  82. package/dist/cjs/client.js.map +1 -1
  83. package/dist/cjs/collaboration/document.js +170 -0
  84. package/dist/cjs/collaboration/document.js.map +1 -0
  85. package/dist/cjs/collaboration/index.js +31 -0
  86. package/dist/cjs/collaboration/index.js.map +1 -0
  87. package/dist/cjs/collaboration/memory.js +38 -0
  88. package/dist/cjs/collaboration/memory.js.map +1 -0
  89. package/dist/cjs/collaboration/rooms.js +39 -0
  90. package/dist/cjs/collaboration/rooms.js.map +1 -0
  91. package/dist/cjs/collaboration/service.js +237 -0
  92. package/dist/cjs/collaboration/service.js.map +1 -0
  93. package/dist/cjs/collaboration/types.js +3 -0
  94. package/dist/cjs/collaboration/types.js.map +1 -0
  95. package/dist/cjs/contracts.js +2 -0
  96. package/dist/cjs/contracts.js.map +1 -1
  97. package/dist/cjs/index.js +19 -2
  98. package/dist/cjs/index.js.map +1 -1
  99. package/dist/cjs/lib/utils/isRecord.js +9 -0
  100. package/dist/cjs/lib/utils/isRecord.js.map +1 -0
  101. package/dist/cjs/mail/mailService.js +2 -1
  102. package/dist/cjs/mail/mailService.js.map +1 -1
  103. package/dist/cjs/media/imageOptimization.js +22 -32
  104. package/dist/cjs/media/imageOptimization.js.map +1 -1
  105. package/dist/cjs/media/mediaService.js +2 -2
  106. package/dist/cjs/media/mediaService.js.map +1 -1
  107. package/dist/cjs/media/videoOptimization.js +11 -24
  108. package/dist/cjs/media/videoOptimization.js.map +1 -1
  109. package/dist/cjs/platform/image.js +140 -0
  110. package/dist/cjs/platform/image.js.map +1 -0
  111. package/dist/cjs/platform/index.js +55 -0
  112. package/dist/cjs/platform/index.js.map +1 -0
  113. package/dist/cjs/platform/node.js +53 -0
  114. package/dist/cjs/platform/node.js.map +1 -0
  115. package/dist/cjs/platform/realtime.js +55 -0
  116. package/dist/cjs/platform/realtime.js.map +1 -0
  117. package/dist/cjs/platform/realtimeServer.js +126 -0
  118. package/dist/cjs/platform/realtimeServer.js.map +1 -0
  119. package/dist/cjs/platform/realtimeTopics.js +16 -0
  120. package/dist/cjs/platform/realtimeTopics.js.map +1 -0
  121. package/dist/cjs/platform/runtime.js +8 -0
  122. package/dist/cjs/platform/runtime.js.map +1 -0
  123. package/dist/cjs/platform/types.js +3 -0
  124. package/dist/cjs/platform/types.js.map +1 -0
  125. package/dist/cjs/schemas/manager/collaborationPresence.js +22 -0
  126. package/dist/cjs/schemas/manager/collaborationPresence.js.map +1 -0
  127. package/dist/cjs/schemas/manager/contentCollaboration.js +36 -0
  128. package/dist/cjs/schemas/manager/contentCollaboration.js.map +1 -0
  129. package/dist/cjs/schemas/manager/templateCollaboration.js +36 -0
  130. package/dist/cjs/schemas/manager/templateCollaboration.js.map +1 -0
  131. package/dist/cjs/schemas/manager/uiLocales.js +12 -0
  132. package/dist/cjs/schemas/manager/uiLocales.js.map +1 -1
  133. package/dist/cjs/translation/document.js +19 -19
  134. package/dist/cjs/translation/document.js.map +1 -1
  135. package/dist/docs/index.md +555 -424
  136. package/dist/esm/api/context.d.ts +1 -1
  137. package/dist/esm/api/context.js +3 -3
  138. package/dist/esm/api/hooks/context.d.ts +1 -1
  139. package/dist/esm/api/hooks/runContentHooks.d.ts +4 -4
  140. package/dist/esm/api/hooks/runContentHooks.js +1 -1
  141. package/dist/esm/api/operations/apiEventLog.d.ts +3 -3
  142. package/dist/esm/api/operations/apiEventLog.js +3 -3
  143. package/dist/esm/api/operations/catalog.d.ts +2 -2
  144. package/dist/esm/api/operations/catalog.js +1 -1
  145. package/dist/esm/api/operations/custom.d.ts +1 -1
  146. package/dist/esm/api/operations/custom.js +1 -1
  147. package/dist/esm/api/operations/index.d.ts +292 -200
  148. package/dist/esm/api/operations/index.d.ts.map +1 -1
  149. package/dist/esm/api/operations/index.js +9 -9
  150. package/dist/esm/api/operations/manager-contract.d.ts +257 -165
  151. package/dist/esm/api/operations/manager-contract.d.ts.map +1 -1
  152. package/dist/esm/api/operations/manager-contract.js +56 -7
  153. package/dist/esm/api/operations/manager-contract.js.map +1 -1
  154. package/dist/esm/api/operations/manager-manifest.d.ts +30 -0
  155. package/dist/esm/api/operations/manager-manifest.d.ts.map +1 -1
  156. package/dist/esm/api/operations/manager-manifest.js +30 -0
  157. package/dist/esm/api/operations/manager-manifest.js.map +1 -1
  158. package/dist/esm/api/operations/manager.d.ts +258 -166
  159. package/dist/esm/api/operations/manager.d.ts.map +1 -1
  160. package/dist/esm/api/operations/manager.js +93 -73
  161. package/dist/esm/api/operations/manager.js.map +1 -1
  162. package/dist/esm/api/operations/tracing.d.ts +1 -1
  163. package/dist/esm/api/operations/tracing.js +2 -2
  164. package/dist/esm/api/operations/types.d.ts +1 -1
  165. package/dist/esm/api/operations/web-contract.d.ts +26 -26
  166. package/dist/esm/api/operations/web-contract.js +3 -3
  167. package/dist/esm/api/operations/web.d.ts +27 -27
  168. package/dist/esm/api/operations/web.js +11 -11
  169. package/dist/esm/api/realtime.d.ts +16 -0
  170. package/dist/esm/api/realtime.d.ts.map +1 -0
  171. package/dist/esm/api/realtime.js +140 -0
  172. package/dist/esm/api/realtime.js.map +1 -0
  173. package/dist/esm/api/routes/manager/apiOperations.d.ts +3 -3
  174. package/dist/esm/api/routes/manager/apiOperations.js +5 -5
  175. package/dist/esm/api/routes/manager/auth/accountInfo.d.ts +2 -2
  176. package/dist/esm/api/routes/manager/auth/accountInfo.js +3 -3
  177. package/dist/esm/api/routes/manager/auth/deleteSession.d.ts +2 -2
  178. package/dist/esm/api/routes/manager/auth/deleteSession.js +3 -3
  179. package/dist/esm/api/routes/manager/auth/externalLogin.d.ts +2 -2
  180. package/dist/esm/api/routes/manager/auth/externalLogin.d.ts.map +1 -1
  181. package/dist/esm/api/routes/manager/auth/externalLogin.js +14 -12
  182. package/dist/esm/api/routes/manager/auth/externalLogin.js.map +1 -1
  183. package/dist/esm/api/routes/manager/auth/getSession.d.ts +7 -7
  184. package/dist/esm/api/routes/manager/auth/login.d.ts +2 -2
  185. package/dist/esm/api/routes/manager/auth/login.js +9 -9
  186. package/dist/esm/api/routes/manager/auth/logout.d.ts +1 -1
  187. package/dist/esm/api/routes/manager/auth/logout.js +3 -3
  188. package/dist/esm/api/routes/manager/auth/markTourSeen.d.ts +8 -8
  189. package/dist/esm/api/routes/manager/auth/markTourSeen.js +3 -3
  190. package/dist/esm/api/routes/manager/auth/mfa/regenerateRecoveryCodes.d.ts +2 -2
  191. package/dist/esm/api/routes/manager/auth/mfa/regenerateRecoveryCodes.js +6 -6
  192. package/dist/esm/api/routes/manager/auth/mfa/verifyRecoveryCode.d.ts +3 -3
  193. package/dist/esm/api/routes/manager/auth/mfa/verifyRecoveryCode.d.ts.map +1 -1
  194. package/dist/esm/api/routes/manager/auth/mfa/verifyRecoveryCode.js +9 -8
  195. package/dist/esm/api/routes/manager/auth/mfa/verifyRecoveryCode.js.map +1 -1
  196. package/dist/esm/api/routes/manager/auth/passwordIpBlocks.d.ts +2 -2
  197. package/dist/esm/api/routes/manager/auth/passwordIpBlocks.js +8 -8
  198. package/dist/esm/api/routes/manager/auth/passwordRecovery.d.ts +2 -2
  199. package/dist/esm/api/routes/manager/auth/passwordRecovery.d.ts.map +1 -1
  200. package/dist/esm/api/routes/manager/auth/passwordRecovery.js +13 -13
  201. package/dist/esm/api/routes/manager/auth/passwordRecovery.js.map +1 -1
  202. package/dist/esm/api/routes/manager/auth/totp/confirmTotp.d.ts +2 -2
  203. package/dist/esm/api/routes/manager/auth/totp/confirmTotp.js +4 -4
  204. package/dist/esm/api/routes/manager/auth/totp/enrollTotp.d.ts +1 -1
  205. package/dist/esm/api/routes/manager/auth/totp/enrollTotp.d.ts.map +1 -1
  206. package/dist/esm/api/routes/manager/auth/totp/enrollTotp.js +5 -5
  207. package/dist/esm/api/routes/manager/auth/totp/enrollTotp.js.map +1 -1
  208. package/dist/esm/api/routes/manager/auth/totp/verifyTotp.d.ts +2 -2
  209. package/dist/esm/api/routes/manager/auth/totp/verifyTotp.d.ts.map +1 -1
  210. package/dist/esm/api/routes/manager/auth/totp/verifyTotp.js +7 -6
  211. package/dist/esm/api/routes/manager/auth/totp/verifyTotp.js.map +1 -1
  212. package/dist/esm/api/routes/manager/auth/updateAccount.d.ts +8 -8
  213. package/dist/esm/api/routes/manager/auth/updateAccount.js +3 -3
  214. package/dist/esm/api/routes/manager/auth/updatePassword.d.ts +2 -2
  215. package/dist/esm/api/routes/manager/auth/updatePassword.js +5 -5
  216. package/dist/esm/api/routes/manager/auth/updateTutorialPreferences.d.ts +8 -8
  217. package/dist/esm/api/routes/manager/auth/updateTutorialPreferences.js +3 -3
  218. package/dist/esm/api/routes/manager/auth/webauthn/webauthnAuthOptions.js +4 -4
  219. package/dist/esm/api/routes/manager/auth/webauthn/webauthnAuthVerify.d.ts +1 -1
  220. package/dist/esm/api/routes/manager/auth/webauthn/webauthnAuthVerify.d.ts.map +1 -1
  221. package/dist/esm/api/routes/manager/auth/webauthn/webauthnAuthVerify.js +8 -7
  222. package/dist/esm/api/routes/manager/auth/webauthn/webauthnAuthVerify.js.map +1 -1
  223. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterOptions.d.ts +2 -2
  224. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterOptions.d.ts.map +1 -1
  225. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterOptions.js +5 -5
  226. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterOptions.js.map +1 -1
  227. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterVerify.d.ts +1 -1
  228. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterVerify.d.ts.map +1 -1
  229. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterVerify.js +8 -7
  230. package/dist/esm/api/routes/manager/auth/webauthn/webauthnRegisterVerify.js.map +1 -1
  231. package/dist/esm/api/routes/manager/backups/create.d.ts +2 -2
  232. package/dist/esm/api/routes/manager/backups/create.js +2 -2
  233. package/dist/esm/api/routes/manager/backups/list.d.ts +2 -2
  234. package/dist/esm/api/routes/manager/backups/list.js +2 -2
  235. package/dist/esm/api/routes/manager/backups/restore.d.ts +2 -2
  236. package/dist/esm/api/routes/manager/backups/restore.js +4 -4
  237. package/dist/esm/api/routes/manager/collaborationBinary.d.ts +3 -0
  238. package/dist/esm/api/routes/manager/collaborationBinary.d.ts.map +1 -0
  239. package/dist/esm/api/routes/manager/collaborationBinary.js +3 -0
  240. package/dist/esm/api/routes/manager/collaborationBinary.js.map +1 -0
  241. package/dist/esm/api/routes/manager/comments.d.ts +2 -2
  242. package/dist/esm/api/routes/manager/comments.d.ts.map +1 -1
  243. package/dist/esm/api/routes/manager/comments.js +12 -8
  244. package/dist/esm/api/routes/manager/comments.js.map +1 -1
  245. package/dist/esm/api/routes/manager/contentCollaboration.d.ts +21 -0
  246. package/dist/esm/api/routes/manager/contentCollaboration.d.ts.map +1 -0
  247. package/dist/esm/api/routes/manager/contentCollaboration.js +95 -0
  248. package/dist/esm/api/routes/manager/contentCollaboration.js.map +1 -0
  249. package/dist/esm/api/routes/manager/contentType.d.ts +16 -16
  250. package/dist/esm/api/routes/manager/contentType.js +3 -3
  251. package/dist/esm/api/routes/manager/contentTypes.d.ts +14 -14
  252. package/dist/esm/api/routes/manager/contentTypes.js +1 -1
  253. package/dist/esm/api/routes/manager/contentVersions.d.ts +2 -2
  254. package/dist/esm/api/routes/manager/contentVersions.d.ts.map +1 -1
  255. package/dist/esm/api/routes/manager/contentVersions.js +16 -13
  256. package/dist/esm/api/routes/manager/contentVersions.js.map +1 -1
  257. package/dist/esm/api/routes/manager/create.d.ts +5 -5
  258. package/dist/esm/api/routes/manager/create.js +11 -11
  259. package/dist/esm/api/routes/manager/delete.d.ts +2 -2
  260. package/dist/esm/api/routes/manager/delete.d.ts.map +1 -1
  261. package/dist/esm/api/routes/manager/delete.js +27 -12
  262. package/dist/esm/api/routes/manager/delete.js.map +1 -1
  263. package/dist/esm/api/routes/manager/duplicate.d.ts +5 -5
  264. package/dist/esm/api/routes/manager/duplicate.d.ts.map +1 -1
  265. package/dist/esm/api/routes/manager/duplicate.js +7 -7
  266. package/dist/esm/api/routes/manager/duplicate.js.map +1 -1
  267. package/dist/esm/api/routes/manager/favorites.d.ts +2 -2
  268. package/dist/esm/api/routes/manager/favorites.js +5 -5
  269. package/dist/esm/api/routes/manager/get.d.ts +2 -2
  270. package/dist/esm/api/routes/manager/get.js +10 -10
  271. package/dist/esm/api/routes/manager/languages.d.ts +4 -4
  272. package/dist/esm/api/routes/manager/languages.js +1 -1
  273. package/dist/esm/api/routes/manager/list.d.ts +2 -2
  274. package/dist/esm/api/routes/manager/list.js +15 -15
  275. package/dist/esm/api/routes/manager/literals/list.d.ts +2 -2
  276. package/dist/esm/api/routes/manager/literals/list.js +5 -5
  277. package/dist/esm/api/routes/manager/literals/upsert.d.ts +2 -2
  278. package/dist/esm/api/routes/manager/literals/upsert.js +6 -6
  279. package/dist/esm/api/routes/manager/localeVariants.d.ts +49 -49
  280. package/dist/esm/api/routes/manager/localeVariants.d.ts.map +1 -1
  281. package/dist/esm/api/routes/manager/localeVariants.js +28 -20
  282. package/dist/esm/api/routes/manager/localeVariants.js.map +1 -1
  283. package/dist/esm/api/routes/manager/logs/cleanup.d.ts +2 -2
  284. package/dist/esm/api/routes/manager/logs/cleanup.js +3 -3
  285. package/dist/esm/api/routes/manager/logs/list.d.ts +2 -2
  286. package/dist/esm/api/routes/manager/logs/list.js +2 -2
  287. package/dist/esm/api/routes/manager/media/createFolder.d.ts +2 -2
  288. package/dist/esm/api/routes/manager/media/createFolder.js +7 -7
  289. package/dist/esm/api/routes/manager/media/deleteFolder.d.ts +2 -2
  290. package/dist/esm/api/routes/manager/media/deleteFolder.js +7 -7
  291. package/dist/esm/api/routes/manager/media/deleteMediaStorage.js +2 -2
  292. package/dist/esm/api/routes/manager/media/finalizeUpload.d.ts +2 -2
  293. package/dist/esm/api/routes/manager/media/finalizeUpload.js +12 -12
  294. package/dist/esm/api/routes/manager/media/getMediaUrl.d.ts +2 -2
  295. package/dist/esm/api/routes/manager/media/getMediaUrl.js +6 -6
  296. package/dist/esm/api/routes/manager/media/listFolders.d.ts +2 -2
  297. package/dist/esm/api/routes/manager/media/listFolders.js +4 -4
  298. package/dist/esm/api/routes/manager/media/prepareUpload.d.ts +3 -3
  299. package/dist/esm/api/routes/manager/media/prepareUpload.js +5 -5
  300. package/dist/esm/api/routes/manager/media/reimportMedia.d.ts +5 -5
  301. package/dist/esm/api/routes/manager/media/reimportMedia.js +10 -10
  302. package/dist/esm/api/routes/manager/media/replaceMedia.d.ts +3 -3
  303. package/dist/esm/api/routes/manager/media/replaceMedia.js +12 -12
  304. package/dist/esm/api/routes/manager/media/resolveMediaRecordUrls.js +1 -1
  305. package/dist/esm/api/routes/manager/media/uploadBinary.d.ts +1 -1
  306. package/dist/esm/api/routes/manager/media/uploadBinary.js +9 -9
  307. package/dist/esm/api/routes/manager/migrations/list.d.ts +2 -2
  308. package/dist/esm/api/routes/manager/migrations/list.js +2 -2
  309. package/dist/esm/api/routes/manager/notifications.d.ts +2 -2
  310. package/dist/esm/api/routes/manager/notifications.d.ts.map +1 -1
  311. package/dist/esm/api/routes/manager/notifications.js +8 -6
  312. package/dist/esm/api/routes/manager/notifications.js.map +1 -1
  313. package/dist/esm/api/routes/manager/permissions.d.ts +1 -1
  314. package/dist/esm/api/routes/manager/permissions.js +2 -2
  315. package/dist/esm/api/routes/manager/preview/create.d.ts +2 -2
  316. package/dist/esm/api/routes/manager/preview/create.js +15 -15
  317. package/dist/esm/api/routes/manager/previewSlugRedirects.d.ts +2 -2
  318. package/dist/esm/api/routes/manager/previewSlugRedirects.js +3 -3
  319. package/dist/esm/api/routes/manager/regenerateRoutes.d.ts +1 -1
  320. package/dist/esm/api/routes/manager/regenerateRoutes.js +2 -2
  321. package/dist/esm/api/routes/manager/reviewPolicies.d.ts +2 -2
  322. package/dist/esm/api/routes/manager/reviewPolicies.js +7 -7
  323. package/dist/esm/api/routes/manager/reviews.d.ts +2 -2
  324. package/dist/esm/api/routes/manager/reviews.js +8 -8
  325. package/dist/esm/api/routes/manager/routeLayout.d.ts +2 -2
  326. package/dist/esm/api/routes/manager/routeLayout.js +6 -6
  327. package/dist/esm/api/routes/manager/setDefaultLanguage.d.ts +2 -2
  328. package/dist/esm/api/routes/manager/setDefaultLanguage.js +5 -5
  329. package/dist/esm/api/routes/manager/template.d.ts +4 -3
  330. package/dist/esm/api/routes/manager/template.d.ts.map +1 -1
  331. package/dist/esm/api/routes/manager/template.js +11 -11
  332. package/dist/esm/api/routes/manager/template.js.map +1 -1
  333. package/dist/esm/api/routes/manager/templateCollaboration.d.ts +17 -0
  334. package/dist/esm/api/routes/manager/templateCollaboration.d.ts.map +1 -0
  335. package/dist/esm/api/routes/manager/templateCollaboration.js +95 -0
  336. package/dist/esm/api/routes/manager/templateCollaboration.js.map +1 -0
  337. package/dist/esm/api/routes/manager/translateDocument.d.ts +2 -2
  338. package/dist/esm/api/routes/manager/translateDocument.d.ts.map +1 -1
  339. package/dist/esm/api/routes/manager/translateDocument.js +14 -32
  340. package/dist/esm/api/routes/manager/translateDocument.js.map +1 -1
  341. package/dist/esm/api/routes/manager/trash.d.ts +3 -3
  342. package/dist/esm/api/routes/manager/trash.d.ts.map +1 -1
  343. package/dist/esm/api/routes/manager/trash.js +13 -9
  344. package/dist/esm/api/routes/manager/trash.js.map +1 -1
  345. package/dist/esm/api/routes/manager/uiLocales.d.ts +3 -1
  346. package/dist/esm/api/routes/manager/uiLocales.d.ts.map +1 -1
  347. package/dist/esm/api/routes/manager/uiLocales.js +16 -5
  348. package/dist/esm/api/routes/manager/uiLocales.js.map +1 -1
  349. package/dist/esm/api/routes/manager/update.d.ts +5 -5
  350. package/dist/esm/api/routes/manager/update.d.ts.map +1 -1
  351. package/dist/esm/api/routes/manager/update.js +18 -14
  352. package/dist/esm/api/routes/manager/update.js.map +1 -1
  353. package/dist/esm/api/routes/manager/users.d.ts +2 -2
  354. package/dist/esm/api/routes/manager/users.js +2 -2
  355. package/dist/esm/api/routes/manager/versions/get.d.ts +2 -2
  356. package/dist/esm/api/routes/manager/versions/get.js +2 -2
  357. package/dist/esm/api/routes/manager/versions/list.d.ts +2 -2
  358. package/dist/esm/api/routes/manager/versions/list.js +2 -2
  359. package/dist/esm/api/routes/manager/versions/restore.d.ts +2 -2
  360. package/dist/esm/api/routes/manager/versions/restore.js +8 -8
  361. package/dist/esm/api/routes/web/llms.d.ts +1 -1
  362. package/dist/esm/api/routes/web/llms.d.ts.map +1 -1
  363. package/dist/esm/api/routes/web/llms.js +7 -7
  364. package/dist/esm/api/routes/web/llms.js.map +1 -1
  365. package/dist/esm/api/routes/web/page.d.ts +5 -5
  366. package/dist/esm/api/routes/web/page.js +21 -21
  367. package/dist/esm/api/routes/web/previewPage.d.ts +8 -8
  368. package/dist/esm/api/routes/web/previewPage.js +9 -9
  369. package/dist/esm/api/routes/web/robots.d.ts +1 -1
  370. package/dist/esm/api/routes/web/robots.js +2 -2
  371. package/dist/esm/api/routes/web/seo.d.ts +1 -1
  372. package/dist/esm/api/routes/web/seo.d.ts.map +1 -1
  373. package/dist/esm/api/routes/web/seo.js +1 -1
  374. package/dist/esm/api/routes/web/seo.js.map +1 -1
  375. package/dist/esm/api/routes/web/sitemap.d.ts +1 -1
  376. package/dist/esm/api/routes/web/sitemap.js +4 -4
  377. package/dist/esm/api/routes/web/staticPaths.d.ts +3 -3
  378. package/dist/esm/api/routes/web/staticPaths.js +4 -4
  379. package/dist/esm/api/sessionCookie.d.ts +1 -1
  380. package/dist/esm/api/utils/authEvents.d.ts +2 -2
  381. package/dist/esm/api/utils/authEvents.js +2 -2
  382. package/dist/esm/api/utils/authRateLimit.d.ts +1 -1
  383. package/dist/esm/api/utils/authRateLimit.js +1 -1
  384. package/dist/esm/api/utils/breadcrums.d.ts +5 -5
  385. package/dist/esm/api/utils/breadcrums.js +3 -3
  386. package/dist/esm/api/utils/checkFailureCase.d.ts +1 -1
  387. package/dist/esm/api/utils/checkFailureCase.js +1 -1
  388. package/dist/esm/api/utils/checkOwnership.d.ts +2 -2
  389. package/dist/esm/api/utils/checkOwnership.js +5 -5
  390. package/dist/esm/api/utils/checkPermissions.d.ts +2 -2
  391. package/dist/esm/api/utils/checkPermissions.js +3 -3
  392. package/dist/esm/api/utils/contentTemplate.d.ts +2 -2
  393. package/dist/esm/api/utils/contentTemplate.d.ts.map +1 -1
  394. package/dist/esm/api/utils/contentTemplate.js +8 -11
  395. package/dist/esm/api/utils/contentTemplate.js.map +1 -1
  396. package/dist/esm/api/utils/dynamicData.d.ts +5 -5
  397. package/dist/esm/api/utils/dynamicData.js +11 -11
  398. package/dist/esm/api/utils/getLanguages.d.ts +2 -2
  399. package/dist/esm/api/utils/getLanguages.js +2 -2
  400. package/dist/esm/api/utils/getLink.d.ts +2 -2
  401. package/dist/esm/api/utils/getLink.js +5 -5
  402. package/dist/esm/api/utils/getUser.d.ts +7 -7
  403. package/dist/esm/api/utils/getUser.js +4 -4
  404. package/dist/esm/api/utils/index.d.ts +25 -25
  405. package/dist/esm/api/utils/index.js +25 -25
  406. package/dist/esm/api/utils/iteratorVisibility.d.ts +1 -1
  407. package/dist/esm/api/utils/iteratorVisibility.d.ts.map +1 -1
  408. package/dist/esm/api/utils/iteratorVisibility.js +1 -1
  409. package/dist/esm/api/utils/iteratorVisibility.js.map +1 -1
  410. package/dist/esm/api/utils/localeVariants.d.ts +1 -1
  411. package/dist/esm/api/utils/localeVariants.js +8 -8
  412. package/dist/esm/api/utils/managerNotifications.d.ts +5 -5
  413. package/dist/esm/api/utils/managerNotifications.d.ts.map +1 -1
  414. package/dist/esm/api/utils/managerNotifications.js +7 -4
  415. package/dist/esm/api/utils/managerNotifications.js.map +1 -1
  416. package/dist/esm/api/utils/mediaUploadToken.d.ts +1 -1
  417. package/dist/esm/api/utils/mediaUploadToken.d.ts.map +1 -1
  418. package/dist/esm/api/utils/mediaUploadToken.js +3 -3
  419. package/dist/esm/api/utils/mediaUploadToken.js.map +1 -1
  420. package/dist/esm/api/utils/parseId.d.ts +1 -1
  421. package/dist/esm/api/utils/parseId.js +1 -1
  422. package/dist/esm/api/utils/passwords.d.ts.map +1 -1
  423. package/dist/esm/api/utils/passwords.js +14 -1
  424. package/dist/esm/api/utils/passwords.js.map +1 -1
  425. package/dist/esm/api/utils/populates/populateLinks.d.ts +2 -2
  426. package/dist/esm/api/utils/populates/populateLinks.js +5 -5
  427. package/dist/esm/api/utils/populates/populateRelations.d.ts +2 -2
  428. package/dist/esm/api/utils/populates/populateRelations.js +7 -7
  429. package/dist/esm/api/utils/previewToken.d.ts.map +1 -1
  430. package/dist/esm/api/utils/previewToken.js +2 -2
  431. package/dist/esm/api/utils/previewToken.js.map +1 -1
  432. package/dist/esm/api/utils/realtime.d.ts +3 -0
  433. package/dist/esm/api/utils/realtime.d.ts.map +1 -0
  434. package/dist/esm/api/utils/realtime.js +15 -0
  435. package/dist/esm/api/utils/realtime.js.map +1 -0
  436. package/dist/esm/api/utils/recoveryCodes.d.ts.map +1 -1
  437. package/dist/esm/api/utils/recoveryCodes.js +7 -4
  438. package/dist/esm/api/utils/recoveryCodes.js.map +1 -1
  439. package/dist/esm/api/utils/redirects/createSlugChangeRedirects.d.ts +2 -2
  440. package/dist/esm/api/utils/redirects/createSlugChangeRedirects.js +6 -6
  441. package/dist/esm/api/utils/redirects/resolveRedirect.js +2 -2
  442. package/dist/esm/api/utils/redirects/slugPathChanges.d.ts +1 -1
  443. package/dist/esm/api/utils/redirects/slugPathChanges.js +4 -4
  444. package/dist/esm/api/utils/requireContentType.d.ts +1 -1
  445. package/dist/esm/api/utils/requireContentType.js +2 -2
  446. package/dist/esm/api/utils/reviews.d.ts +12 -12
  447. package/dist/esm/api/utils/reviews.js +4 -4
  448. package/dist/esm/api/utils/routes/revalidatePath.js +5 -5
  449. package/dist/esm/api/utils/routes/routeDefinitions.d.ts +1 -1
  450. package/dist/esm/api/utils/routes/routeMapHelpers.d.ts +6 -6
  451. package/dist/esm/api/utils/routes/routeMapHelpers.js +8 -8
  452. package/dist/esm/api/utils/routes/syncConfiguredRoutes.d.ts +2 -2
  453. package/dist/esm/api/utils/routes/syncConfiguredRoutes.js +5 -5
  454. package/dist/esm/api/utils/routes/updateRoutesMap.d.ts +2 -2
  455. package/dist/esm/api/utils/routes/updateRoutesMap.js +5 -5
  456. package/dist/esm/api/utils/safeManagerQuery.d.ts +2 -2
  457. package/dist/esm/api/utils/safeManagerQuery.d.ts.map +1 -1
  458. package/dist/esm/api/utils/safeManagerQuery.js +2 -2
  459. package/dist/esm/api/utils/safeManagerQuery.js.map +1 -1
  460. package/dist/esm/api/utils/sanitizeManagerOutput.d.ts +1 -1
  461. package/dist/esm/api/utils/sanitizeManagerOutput.d.ts.map +1 -1
  462. package/dist/esm/api/utils/sanitizeManagerOutput.js +2 -2
  463. package/dist/esm/api/utils/sanitizeManagerOutput.js.map +1 -1
  464. package/dist/esm/api/utils/transformObjectIdsToStrings.js +1 -1
  465. package/dist/esm/api/utils/transformStringToObjectIds.js +1 -1
  466. package/dist/esm/api/utils/translate.d.ts +1 -1
  467. package/dist/esm/api/utils/translate.js +3 -3
  468. package/dist/esm/api/utils/validateModule.d.ts +1 -1
  469. package/dist/esm/api/utils/validateModule.js +2 -2
  470. package/dist/esm/auth/accountRecovery.d.ts +1 -1
  471. package/dist/esm/auth/completePrimaryAuthentication.d.ts +1 -1
  472. package/dist/esm/auth/completePrimaryAuthentication.d.ts.map +1 -1
  473. package/dist/esm/auth/completePrimaryAuthentication.js +6 -6
  474. package/dist/esm/auth/completePrimaryAuthentication.js.map +1 -1
  475. package/dist/esm/auth/loginAdapters.d.ts +1 -1
  476. package/dist/esm/auth/passwordFail2ban.d.ts +2 -2
  477. package/dist/esm/auth/passwordFail2ban.js +4 -4
  478. package/dist/esm/auth/passwordResetMailTemplate.d.ts +2 -2
  479. package/dist/esm/auth/passwordResetMailTemplate.js +1 -1
  480. package/dist/esm/bootstrapState.d.ts +25 -15
  481. package/dist/esm/bootstrapState.d.ts.map +1 -1
  482. package/dist/esm/bootstrapState.js.map +1 -1
  483. package/dist/esm/client.d.ts +68 -62
  484. package/dist/esm/client.d.ts.map +1 -1
  485. package/dist/esm/client.js +36 -33
  486. package/dist/esm/client.js.map +1 -1
  487. package/dist/esm/collaboration/document.d.ts +7 -0
  488. package/dist/esm/collaboration/document.d.ts.map +1 -0
  489. package/dist/esm/collaboration/document.js +130 -0
  490. package/dist/esm/collaboration/document.js.map +1 -0
  491. package/dist/esm/collaboration/index.d.ts +12 -0
  492. package/dist/esm/collaboration/index.d.ts.map +1 -0
  493. package/dist/esm/collaboration/index.js +19 -0
  494. package/dist/esm/collaboration/index.js.map +1 -0
  495. package/dist/esm/collaboration/memory.d.ts +3 -0
  496. package/dist/esm/collaboration/memory.d.ts.map +1 -0
  497. package/dist/esm/collaboration/memory.js +34 -0
  498. package/dist/esm/collaboration/memory.js.map +1 -0
  499. package/dist/esm/collaboration/rooms.d.ts +10 -0
  500. package/dist/esm/collaboration/rooms.d.ts.map +1 -0
  501. package/dist/esm/collaboration/rooms.js +33 -0
  502. package/dist/esm/collaboration/rooms.js.map +1 -0
  503. package/dist/esm/collaboration/service.d.ts +48 -0
  504. package/dist/esm/collaboration/service.d.ts.map +1 -0
  505. package/dist/esm/collaboration/service.js +200 -0
  506. package/dist/esm/collaboration/service.js.map +1 -0
  507. package/dist/esm/collaboration/types.d.ts +30 -0
  508. package/dist/esm/collaboration/types.d.ts.map +1 -0
  509. package/dist/esm/collaboration/types.js +2 -0
  510. package/dist/esm/collaboration/types.js.map +1 -0
  511. package/dist/esm/contracts.d.ts +62 -60
  512. package/dist/esm/contracts.d.ts.map +1 -1
  513. package/dist/esm/contracts.js +62 -60
  514. package/dist/esm/contracts.js.map +1 -1
  515. package/dist/esm/eventLog/index.d.ts +6 -6
  516. package/dist/esm/eventLog/index.js +4 -4
  517. package/dist/esm/eventLog/mongo.d.ts +1 -1
  518. package/dist/esm/eventLog/mongo.js +2 -2
  519. package/dist/esm/eventLog/service.d.ts +1 -1
  520. package/dist/esm/index.d.ts +55 -52
  521. package/dist/esm/index.d.ts.map +1 -1
  522. package/dist/esm/index.js +64 -52
  523. package/dist/esm/index.js.map +1 -1
  524. package/dist/esm/internal-content-types/ApiOperation.d.ts +162 -162
  525. package/dist/esm/internal-content-types/ApiOperation.js +2 -2
  526. package/dist/esm/internal-content-types/Backup.d.ts +199 -199
  527. package/dist/esm/internal-content-types/Backup.js +2 -2
  528. package/dist/esm/internal-content-types/BackupDocument.d.ts +162 -162
  529. package/dist/esm/internal-content-types/BackupDocument.js +2 -2
  530. package/dist/esm/internal-content-types/ContentComment.d.ts +1789 -1789
  531. package/dist/esm/internal-content-types/ContentComment.js +3 -3
  532. package/dist/esm/internal-content-types/ContentCommentReadState.d.ts +1253 -1253
  533. package/dist/esm/internal-content-types/ContentCommentReadState.js +3 -3
  534. package/dist/esm/internal-content-types/ContentReview.d.ts +5858 -5858
  535. package/dist/esm/internal-content-types/ContentReview.js +4 -4
  536. package/dist/esm/internal-content-types/ContentReviewDecision.d.ts +1245 -1245
  537. package/dist/esm/internal-content-types/ContentReviewDecision.js +3 -3
  538. package/dist/esm/internal-content-types/ContentReviewPolicy.d.ts +516 -516
  539. package/dist/esm/internal-content-types/ContentReviewPolicy.js +3 -3
  540. package/dist/esm/internal-content-types/ContentTemplate.d.ts +106 -106
  541. package/dist/esm/internal-content-types/ContentTemplate.js +2 -2
  542. package/dist/esm/internal-content-types/ContentVersion.d.ts +338 -338
  543. package/dist/esm/internal-content-types/ContentVersion.js +2 -2
  544. package/dist/esm/internal-content-types/HelloWorld.d.ts +42 -42
  545. package/dist/esm/internal-content-types/HelloWorld.js +2 -2
  546. package/dist/esm/internal-content-types/Language.d.ts +81 -81
  547. package/dist/esm/internal-content-types/Language.js +2 -2
  548. package/dist/esm/internal-content-types/LiteralTranslation.d.ts +83 -83
  549. package/dist/esm/internal-content-types/LiteralTranslation.js +2 -2
  550. package/dist/esm/internal-content-types/LlmsSettings.d.ts +762 -762
  551. package/dist/esm/internal-content-types/LlmsSettings.js +2 -2
  552. package/dist/esm/internal-content-types/LoginIpBlock.d.ts +98 -98
  553. package/dist/esm/internal-content-types/LoginIpBlock.js +2 -2
  554. package/dist/esm/internal-content-types/ManagerFavorite.d.ts +1213 -1213
  555. package/dist/esm/internal-content-types/ManagerFavorite.js +3 -3
  556. package/dist/esm/internal-content-types/ManagerNotification.d.ts +2500 -2500
  557. package/dist/esm/internal-content-types/ManagerNotification.js +3 -3
  558. package/dist/esm/internal-content-types/ManagerRole.d.ts +47 -47
  559. package/dist/esm/internal-content-types/ManagerRole.js +2 -2
  560. package/dist/esm/internal-content-types/ManagerRoleHooks.d.ts +2 -2
  561. package/dist/esm/internal-content-types/ManagerRoleHooks.js +3 -3
  562. package/dist/esm/internal-content-types/ManagerUser.d.ts +532 -532
  563. package/dist/esm/internal-content-types/ManagerUser.js +3 -3
  564. package/dist/esm/internal-content-types/ManagerUserHooks.d.ts +2 -2
  565. package/dist/esm/internal-content-types/ManagerUserHooks.js +3 -3
  566. package/dist/esm/internal-content-types/Media.d.ts +1187 -1187
  567. package/dist/esm/internal-content-types/Media.js +3 -3
  568. package/dist/esm/internal-content-types/MediaFolder.d.ts +192 -192
  569. package/dist/esm/internal-content-types/MediaFolder.js +2 -2
  570. package/dist/esm/internal-content-types/MfaChallenge.d.ts +1301 -1301
  571. package/dist/esm/internal-content-types/MfaChallenge.js +3 -3
  572. package/dist/esm/internal-content-types/Migration.d.ts +330 -330
  573. package/dist/esm/internal-content-types/Migration.js +2 -2
  574. package/dist/esm/internal-content-types/MigrationLock.d.ts +18 -18
  575. package/dist/esm/internal-content-types/MigrationLock.js +2 -2
  576. package/dist/esm/internal-content-types/Page.d.ts +121 -121
  577. package/dist/esm/internal-content-types/Page.js +3 -3
  578. package/dist/esm/internal-content-types/PasswordResetToken.d.ts +1205 -1205
  579. package/dist/esm/internal-content-types/PasswordResetToken.js +4 -4
  580. package/dist/esm/internal-content-types/PreviewSnapshot.d.ts +378 -378
  581. package/dist/esm/internal-content-types/PreviewSnapshot.js +2 -2
  582. package/dist/esm/internal-content-types/Redirect.d.ts +378 -378
  583. package/dist/esm/internal-content-types/Redirect.js +2 -2
  584. package/dist/esm/internal-content-types/RobotsRule.d.ts +250 -250
  585. package/dist/esm/internal-content-types/RobotsRule.js +2 -2
  586. package/dist/esm/internal-content-types/Route.d.ts +232 -232
  587. package/dist/esm/internal-content-types/Route.js +2 -2
  588. package/dist/esm/internal-content-types/RouteLayoutModule.d.ts +266 -266
  589. package/dist/esm/internal-content-types/RouteLayoutModule.js +2 -2
  590. package/dist/esm/internal-content-types/RouteLayoutModuleOverride.d.ts +242 -242
  591. package/dist/esm/internal-content-types/RouteLayoutModuleOverride.js +2 -2
  592. package/dist/esm/internal-content-types/RouteLocaleVariant.d.ts +242 -242
  593. package/dist/esm/internal-content-types/RouteLocaleVariant.js +2 -2
  594. package/dist/esm/internal-content-types/RouteMap.d.ts +258 -258
  595. package/dist/esm/internal-content-types/RouteMap.js +2 -2
  596. package/dist/esm/internal-content-types/RouteSettings.d.ts +340 -340
  597. package/dist/esm/internal-content-types/RouteSettings.js +3 -3
  598. package/dist/esm/internal-content-types/SchemaState.d.ts +82 -82
  599. package/dist/esm/internal-content-types/SchemaState.js +2 -2
  600. package/dist/esm/internal-content-types/Seo.d.ts +738 -738
  601. package/dist/esm/internal-content-types/Seo.js +2 -2
  602. package/dist/esm/internal-content-types/SeoAudit.d.ts +418 -418
  603. package/dist/esm/internal-content-types/SeoAudit.js +2 -2
  604. package/dist/esm/internal-content-types/SeoSettings.d.ts +1982 -1982
  605. package/dist/esm/internal-content-types/SeoSettings.js +3 -3
  606. package/dist/esm/internal-content-types/Session.d.ts +1189 -1189
  607. package/dist/esm/internal-content-types/Session.js +3 -3
  608. package/dist/esm/internal-content-types/StructuredData.d.ts +694 -694
  609. package/dist/esm/internal-content-types/StructuredData.js +2 -2
  610. package/dist/esm/internal-content-types/TemplateContent.d.ts +1 -1
  611. package/dist/esm/internal-content-types/TemplateContent.js +1 -1
  612. package/dist/esm/internal-content-types/UserMfa.d.ts +1290 -1290
  613. package/dist/esm/internal-content-types/UserMfa.js +4 -4
  614. package/dist/esm/internal-content-types/WebAuthnCredential.d.ts +1317 -1317
  615. package/dist/esm/internal-content-types/WebAuthnCredential.js +3 -3
  616. package/dist/esm/internal-content-types/WebAuthnRegChallenge.d.ts +1245 -1245
  617. package/dist/esm/internal-content-types/WebAuthnRegChallenge.js +3 -3
  618. package/dist/esm/internal-content-types/index.d.ts +45 -45
  619. package/dist/esm/internal-content-types/index.js +45 -45
  620. package/dist/esm/internal-content-types/syncAdminRole.d.ts +4 -4
  621. package/dist/esm/internal-content-types/syncAdminRole.js +4 -4
  622. package/dist/esm/lib/ContentType.d.ts +7 -7
  623. package/dist/esm/lib/ContentType.js +5 -5
  624. package/dist/esm/lib/Permissions.d.ts +2 -2
  625. package/dist/esm/lib/Permissions.js +4 -4
  626. package/dist/esm/lib/Registry.d.ts +6 -6
  627. package/dist/esm/lib/Registry.js +4 -4
  628. package/dist/esm/lib/errors/AppError.d.ts +1 -1
  629. package/dist/esm/lib/errors/AppError.js +1 -1
  630. package/dist/esm/lib/errors/index.d.ts +4 -4
  631. package/dist/esm/lib/errors/index.js +3 -3
  632. package/dist/esm/lib/errors/throwAppError.d.ts +1 -1
  633. package/dist/esm/lib/errors/throwAppError.js +3 -3
  634. package/dist/esm/lib/fields/Boolean.d.ts +1 -1
  635. package/dist/esm/lib/fields/Boolean.js +1 -1
  636. package/dist/esm/lib/fields/Breadcrums.d.ts +1 -1
  637. package/dist/esm/lib/fields/Breadcrums.js +1 -1
  638. package/dist/esm/lib/fields/ContentReference.d.ts +2 -2
  639. package/dist/esm/lib/fields/ContentReference.js +2 -2
  640. package/dist/esm/lib/fields/Date.d.ts +1 -1
  641. package/dist/esm/lib/fields/Date.js +1 -1
  642. package/dist/esm/lib/fields/Field.d.ts +15 -15
  643. package/dist/esm/lib/fields/Fields.d.ts +15 -15
  644. package/dist/esm/lib/fields/Fields.js +15 -15
  645. package/dist/esm/lib/fields/File.d.ts +1 -1
  646. package/dist/esm/lib/fields/File.js +2 -2
  647. package/dist/esm/lib/fields/Iterator.d.ts +3 -3
  648. package/dist/esm/lib/fields/Iterator.js +3 -3
  649. package/dist/esm/lib/fields/Link.d.ts +1 -1
  650. package/dist/esm/lib/fields/Link.js +2 -2
  651. package/dist/esm/lib/fields/List.d.ts +1 -1
  652. package/dist/esm/lib/fields/List.js +2 -2
  653. package/dist/esm/lib/fields/Menu.d.ts +2 -2
  654. package/dist/esm/lib/fields/Menu.js +2 -2
  655. package/dist/esm/lib/fields/Number.d.ts +1 -1
  656. package/dist/esm/lib/fields/Number.js +1 -1
  657. package/dist/esm/lib/fields/Relation.d.ts +3 -3
  658. package/dist/esm/lib/fields/Relation.js +3 -3
  659. package/dist/esm/lib/fields/Select.d.ts +1 -1
  660. package/dist/esm/lib/fields/Select.js +1 -1
  661. package/dist/esm/lib/fields/SelfRelation.d.ts +1 -1
  662. package/dist/esm/lib/fields/SelfRelation.js +2 -2
  663. package/dist/esm/lib/fields/SimpleList.d.ts +1 -1
  664. package/dist/esm/lib/fields/SimpleList.js +1 -1
  665. package/dist/esm/lib/fields/String.d.ts +1 -1
  666. package/dist/esm/lib/fields/String.js +3 -3
  667. package/dist/esm/lib/fields/index.d.ts +17 -17
  668. package/dist/esm/lib/fields/index.js +17 -17
  669. package/dist/esm/lib/hooks.d.ts +4 -4
  670. package/dist/esm/lib/routeableContent.js +1 -1
  671. package/dist/esm/lib/types/index.d.ts +2 -2
  672. package/dist/esm/lib/utils/getListField.d.ts +1 -1
  673. package/dist/esm/lib/utils/getSchemas.d.ts +1 -1
  674. package/dist/esm/lib/utils/getTranslation.d.ts +2 -2
  675. package/dist/esm/lib/utils/getTranslation.js +1 -1
  676. package/dist/esm/lib/utils/isRecord.d.ts +2 -0
  677. package/dist/esm/lib/utils/isRecord.d.ts.map +1 -0
  678. package/dist/esm/lib/utils/isRecord.js +5 -0
  679. package/dist/esm/lib/utils/isRecord.js.map +1 -0
  680. package/dist/esm/lib/utils/isTranslatableObject.d.ts +1 -1
  681. package/dist/esm/lib/utils/translateObject.d.ts +2 -2
  682. package/dist/esm/lib/utils/translateObject.js +3 -3
  683. package/dist/esm/literals/definitions.d.ts +1 -1
  684. package/dist/esm/literals/definitions.js +1 -1
  685. package/dist/esm/literals/index.d.ts +2 -2
  686. package/dist/esm/literals/index.js +2 -2
  687. package/dist/esm/mail/index.d.ts +6 -6
  688. package/dist/esm/mail/index.js +5 -5
  689. package/dist/esm/mail/mailService.d.ts +2 -2
  690. package/dist/esm/mail/mailService.d.ts.map +1 -1
  691. package/dist/esm/mail/mailService.js +3 -2
  692. package/dist/esm/mail/mailService.js.map +1 -1
  693. package/dist/esm/mail/templates.d.ts +1 -1
  694. package/dist/esm/mail/templates.js +2 -2
  695. package/dist/esm/manager.d.ts +33 -3
  696. package/dist/esm/manager.d.ts.map +1 -1
  697. package/dist/esm/manager.js +1 -1
  698. package/dist/esm/media/imageOptimization.d.ts +1 -1
  699. package/dist/esm/media/imageOptimization.d.ts.map +1 -1
  700. package/dist/esm/media/imageOptimization.js +23 -33
  701. package/dist/esm/media/imageOptimization.js.map +1 -1
  702. package/dist/esm/media/index.d.ts +4 -4
  703. package/dist/esm/media/index.js +3 -3
  704. package/dist/esm/media/mediaOptimization.d.ts +2 -2
  705. package/dist/esm/media/mediaOptimization.js +2 -2
  706. package/dist/esm/media/mediaService.d.ts +1 -1
  707. package/dist/esm/media/mediaService.d.ts.map +1 -1
  708. package/dist/esm/media/mediaService.js +2 -2
  709. package/dist/esm/media/mediaService.js.map +1 -1
  710. package/dist/esm/media/videoOptimization.d.ts +2 -2
  711. package/dist/esm/media/videoOptimization.d.ts.map +1 -1
  712. package/dist/esm/media/videoOptimization.js +13 -26
  713. package/dist/esm/media/videoOptimization.js.map +1 -1
  714. package/dist/esm/orm/backups.js +5 -5
  715. package/dist/esm/orm/createIndexes.js +4 -4
  716. package/dist/esm/orm/database.js +3 -3
  717. package/dist/esm/orm/dbService.d.ts +6 -6
  718. package/dist/esm/orm/index.d.ts +2 -2
  719. package/dist/esm/orm/index.js +16 -16
  720. package/dist/esm/orm/migrations.d.ts +1 -1
  721. package/dist/esm/orm/migrations.js +4 -4
  722. package/dist/esm/orm/mongodbPeer.js +1 -1
  723. package/dist/esm/orm/operations/clear.d.ts +2 -2
  724. package/dist/esm/orm/operations/clear.js +1 -1
  725. package/dist/esm/orm/operations/create.d.ts +4 -4
  726. package/dist/esm/orm/operations/create.js +7 -7
  727. package/dist/esm/orm/operations/delete.d.ts +3 -3
  728. package/dist/esm/orm/operations/delete.js +5 -5
  729. package/dist/esm/orm/operations/find.d.ts +2 -2
  730. package/dist/esm/orm/operations/find.js +3 -3
  731. package/dist/esm/orm/operations/findDependencies.d.ts +2 -2
  732. package/dist/esm/orm/operations/findDependencies.js +4 -4
  733. package/dist/esm/orm/operations/get.d.ts +3 -3
  734. package/dist/esm/orm/operations/get.js +3 -3
  735. package/dist/esm/orm/operations/getAll.d.ts +2 -2
  736. package/dist/esm/orm/operations/getAll.js +2 -2
  737. package/dist/esm/orm/operations/list.d.ts +2 -2
  738. package/dist/esm/orm/operations/list.js +3 -3
  739. package/dist/esm/orm/operations/update.d.ts +5 -5
  740. package/dist/esm/orm/operations/update.js +8 -8
  741. package/dist/esm/orm/operations/updateMany.d.ts +4 -4
  742. package/dist/esm/orm/operations/updateMany.js +4 -4
  743. package/dist/esm/orm/operations/upsert.d.ts +3 -3
  744. package/dist/esm/orm/operations/upsert.js +4 -4
  745. package/dist/esm/orm/utils/deepDeleteNulls.js +1 -1
  746. package/dist/esm/orm/utils/parseId.d.ts +1 -1
  747. package/dist/esm/orm/utils/parseId.js +1 -1
  748. package/dist/esm/orm/utils/transformObjectIdsToStrings.js +1 -1
  749. package/dist/esm/orm/utils/transformStringToObjectIds.js +1 -1
  750. package/dist/esm/orm/versions.d.ts +1 -1
  751. package/dist/esm/orm/versions.js +7 -7
  752. package/dist/esm/platform/image.d.ts +5 -0
  753. package/dist/esm/platform/image.d.ts.map +1 -0
  754. package/dist/esm/platform/image.js +134 -0
  755. package/dist/esm/platform/image.js.map +1 -0
  756. package/dist/esm/platform/index.d.ts +13 -0
  757. package/dist/esm/platform/index.d.ts.map +1 -0
  758. package/dist/esm/platform/index.js +34 -0
  759. package/dist/esm/platform/index.js.map +1 -0
  760. package/dist/esm/platform/node.d.ts +6 -0
  761. package/dist/esm/platform/node.d.ts.map +1 -0
  762. package/dist/esm/platform/node.js +43 -0
  763. package/dist/esm/platform/node.js.map +1 -0
  764. package/dist/esm/platform/realtime.d.ts +10 -0
  765. package/dist/esm/platform/realtime.d.ts.map +1 -0
  766. package/dist/esm/platform/realtime.js +49 -0
  767. package/dist/esm/platform/realtime.js.map +1 -0
  768. package/dist/esm/platform/realtimeServer.d.ts +26 -0
  769. package/dist/esm/platform/realtimeServer.d.ts.map +1 -0
  770. package/dist/esm/platform/realtimeServer.js +118 -0
  771. package/dist/esm/platform/realtimeServer.js.map +1 -0
  772. package/dist/esm/platform/realtimeTopics.d.ts +7 -0
  773. package/dist/esm/platform/realtimeTopics.d.ts.map +1 -0
  774. package/dist/esm/platform/realtimeTopics.js +7 -0
  775. package/dist/esm/platform/realtimeTopics.js.map +1 -0
  776. package/dist/esm/platform/runtime.d.ts +4 -0
  777. package/dist/esm/platform/runtime.d.ts.map +1 -0
  778. package/dist/esm/platform/runtime.js +3 -0
  779. package/dist/esm/platform/runtime.js.map +1 -0
  780. package/dist/esm/platform/types.d.ts +94 -0
  781. package/dist/esm/platform/types.d.ts.map +1 -0
  782. package/dist/esm/platform/types.js +2 -0
  783. package/dist/esm/platform/types.js.map +1 -0
  784. package/dist/esm/plugins.d.ts +11 -11
  785. package/dist/esm/schemas/manager/auth/externalLogin.js +1 -1
  786. package/dist/esm/schemas/manager/collaborationPresence.d.ts +20 -0
  787. package/dist/esm/schemas/manager/collaborationPresence.d.ts.map +1 -0
  788. package/dist/esm/schemas/manager/collaborationPresence.js +16 -0
  789. package/dist/esm/schemas/manager/collaborationPresence.js.map +1 -0
  790. package/dist/esm/schemas/manager/comments.js +1 -1
  791. package/dist/esm/schemas/manager/contentCollaboration.d.ts +51 -0
  792. package/dist/esm/schemas/manager/contentCollaboration.d.ts.map +1 -0
  793. package/dist/esm/schemas/manager/contentCollaboration.js +30 -0
  794. package/dist/esm/schemas/manager/contentCollaboration.js.map +1 -0
  795. package/dist/esm/schemas/manager/contentVersions.d.ts +64 -64
  796. package/dist/esm/schemas/manager/contentVersions.js +3 -3
  797. package/dist/esm/schemas/manager/literals/upsert.js +1 -1
  798. package/dist/esm/schemas/manager/localeVariants.d.ts +96 -96
  799. package/dist/esm/schemas/manager/localeVariants.js +1 -1
  800. package/dist/esm/schemas/manager/media/finalizeUpload.js +1 -1
  801. package/dist/esm/schemas/manager/media/getMediaUrl.js +1 -1
  802. package/dist/esm/schemas/manager/media/listFolders.js +1 -1
  803. package/dist/esm/schemas/manager/media/reimportMedia.d.ts +1 -1
  804. package/dist/esm/schemas/manager/media/reimportMedia.js +2 -2
  805. package/dist/esm/schemas/manager/media/replaceMedia.js +1 -1
  806. package/dist/esm/schemas/manager/notifications.js +1 -1
  807. package/dist/esm/schemas/manager/reviews.js +1 -1
  808. package/dist/esm/schemas/manager/templateCollaboration.d.ts +54 -0
  809. package/dist/esm/schemas/manager/templateCollaboration.d.ts.map +1 -0
  810. package/dist/esm/schemas/manager/templateCollaboration.js +30 -0
  811. package/dist/esm/schemas/manager/templateCollaboration.js.map +1 -0
  812. package/dist/esm/schemas/manager/uiLocales.d.ts +9 -0
  813. package/dist/esm/schemas/manager/uiLocales.d.ts.map +1 -1
  814. package/dist/esm/schemas/manager/uiLocales.js +13 -1
  815. package/dist/esm/schemas/manager/uiLocales.js.map +1 -1
  816. package/dist/esm/schemas/web/page.d.ts +40 -40
  817. package/dist/esm/schemas/web/page.js +1 -1
  818. package/dist/esm/schemas/web/previewPage.d.ts +28 -28
  819. package/dist/esm/schemas/web/previewPage.js +1 -1
  820. package/dist/esm/translation/document.d.ts +3 -3
  821. package/dist/esm/translation/document.d.ts.map +1 -1
  822. package/dist/esm/translation/document.js +1 -1
  823. package/dist/esm/translation/document.js.map +1 -1
  824. package/dist/esm/translation/index.d.ts +4 -4
  825. package/dist/esm/translation/index.js +3 -3
  826. package/dist/esm/translation/translationService.d.ts +1 -1
  827. package/dist/esm/web/client.d.ts +1 -1
  828. package/dist/esm/web/client.js +1 -1
  829. package/dist/esm/web/index.d.ts +2 -2
  830. package/dist/esm/web/index.js +2 -2
  831. package/dist/esm/web/page.d.ts +1 -1
  832. package/dist/tsconfig.build.tsbuildinfo +1 -1
  833. package/package.json +150 -140
package/README.md CHANGED
@@ -1,1280 +1,1387 @@
1
- # @rakun-kit/core
2
-
3
- `@rakun-kit/core` contains Rakun's shared domain model and runtime. It defines content types, fields, Zod schemas, manager/web operations, request context, MongoDB connection handling, media service, permissions, translations, and typed contracts used by adapters (`@rakun-kit/next`, `@rakun-kit/express`, `@rakun-kit/trpc`) and the manager client.
4
-
5
- ## Main Entry
6
-
7
- Typical imports:
8
-
9
- ```ts
10
- import { ContentType, f, rakunBootstrap, ensureRakunInitialized } from '@rakun-kit/core'
11
- ```
12
-
13
- The main entrypoint exports:
14
-
15
- - Bootstrap: `rakunBootstrap`, `ensureRakunBootstrap`, `ensureRakunInitialized`, `getRakunBootstrapOptions`.
16
- - Context: `createRequestContext`, `getSessionCookie`, `setSessionCookie`.
17
- - Operations: manager/web contracts and definitions.
18
- - Media: `createMediaService`, `getMediaService`, adapters, and types.
19
- - Mail: `createMailService`, `sendMail`, typed template registries, adapters, and types.
20
- - Permissions, translations, errors, contracts, and public types.
21
- - Basic internal content types: `Language`, `ManagerUser`, `Seo`.
22
-
23
- Relevant subpaths:
24
-
25
- - `@rakun-kit/core/manager`: manager operation metadata and types.
26
- - `@rakun-kit/core/contracts`: shared manager/web payload schemas.
27
- - `@rakun-kit/core/client`: client-facing types and utilities.
28
- - `@rakun-kit/core/logger`: logger.
29
- - `@rakun-kit/core/errors`: application errors.
30
- - `@rakun-kit/core/types`: types derived from content types.
31
-
32
- ## Bootstrap
33
-
34
- `rakunBootstrap(options)` registers global configuration and content types:
35
-
36
- ```ts
37
- rakunBootstrap({
38
- literals,
39
- contentTypes: [Page, Post],
40
- routes,
41
- apiOperations,
42
- mongo: {
43
- MONGO_URI: process.env.MONGO_URI!,
44
- ENVIRONMENT: process.env.NODE_ENV === 'test' ? 'test' : 'production',
45
- },
46
- media,
47
- logger: {
48
- level: 'info',
49
- prettify: true,
50
- },
51
- syncRoutes: true,
52
- })
53
- ```
54
-
55
- Options:
56
-
57
- - `literals`: text/translation catalog.
58
- - `contentTypes`: application content types.
59
- - `internalContentTypes`: optional overrides for Rakun internal content types, currently `Page`.
60
- - `routes`: configured routes used to resolve pages.
61
- - `apiOperations`: custom API operations added to the Rakun operation registry.
62
- - `mongo`: MongoDB connection. Required before serving Rakun requests.
63
- - `media`: media adapter/configuration. Optional.
64
- - `mail`: outbound mail adapter and default sender configuration. Optional.
65
- - `accountRecovery`: password-reset URL builder, expiry, and optional custom
66
- mail template. Requires `mail`; core provides the default template.
67
- - `login`: manager password-login toggle and external login adapters.
68
- - `logger`: logger configuration. If omitted, an `info` logger with `prettify` is created.
69
- - `syncRoutes`: syncs configured routes during initialization. Enabled by default.
70
-
71
- ## External manager login
72
-
73
- Configure external login methods in `rakunBootstrap`. The callback URL is the
74
- manager's public `/login/callback` route.
75
-
76
- ```ts
77
- import {
78
- createGitHubLoginAdapter,
79
- createGoogleLoginAdapter,
80
- createMicrosoftLoginAdapter,
81
- rakunBootstrap,
82
- } from '@rakun-kit/core'
83
-
84
- const callbackUrl = 'https://cms.example.com/backend/login/callback'
85
-
86
- rakunBootstrap({
87
- // ...
88
- login: {
89
- password: true,
90
- adapters: [
91
- createGitHubLoginAdapter({
92
- clientId: process.env.GITHUB_CLIENT_ID!,
93
- clientSecret: process.env.GITHUB_CLIENT_SECRET!,
94
- redirectUri: callbackUrl,
95
- }),
96
- createGoogleLoginAdapter({
97
- clientId: process.env.GOOGLE_CLIENT_ID!,
98
- clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
99
- redirectUri: callbackUrl,
100
- }),
101
- createMicrosoftLoginAdapter({
102
- clientId: process.env.MICROSOFT_CLIENT_ID!,
103
- clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
104
- redirectUri: callbackUrl,
105
- tenant: 'common',
106
- }),
107
- ],
108
- },
109
- })
110
- ```
111
-
112
- Register the exact callback URL in each provider console. External identities
113
- must return a verified email matching an
114
- existing manager user. Rakun does not create manager accounts automatically.
115
- The flow uses an HttpOnly state cookie and PKCE and preserves the existing MFA
116
- step. Set `password: false` only when at least one adapter is configured.
117
- Custom providers can be implemented with `defineLoginAdapter`.
118
-
119
- ### Persistent password-login IP blocking
120
-
121
- Password login enables persistent fail2ban-style IP blocking by default. Five
122
- failed attempts block the IP indefinitely; a successful login before reaching
123
- the limit clears its failed-attempt counter. Configure or disable it through
124
- `login.fail2ban`:
125
-
126
- ```ts
127
- rakunBootstrap({
128
- // ...
129
- login: {
130
- password: true,
131
- fail2ban: {
132
- maxAttempts: 5,
133
- // Optional when the deployment uses a custom trusted proxy:
134
- // resolveIp: (ctx) => ctx.req?.headers?.['your-trusted-ip-header'] as string,
135
- },
136
- },
137
- })
138
- ```
139
-
140
- Users with the `auth.ipBlocks.manage` permission can inspect and remove blocks
141
- under Manager Settings → Security. Admin roles receive this built-in permission
142
- automatically. A block can also be removed directly from MongoDB by deleting
143
- the matching document from the `LoginIpBlock` collection. Set `fail2ban: false`
144
- to disable this behavior. External login adapters are not affected.
145
-
146
- ## Account recovery and MFA recovery
147
-
148
- Password recovery is enabled when both `mail` and `accountRecovery` are
149
- configured. The reset URL must point to the manager's public
150
- `/reset-password` screen and preserve the token as the `token` query parameter:
151
-
152
- ```ts
153
- import { rakunBootstrap } from '@rakun-kit/core'
154
- import { createResendMailServiceConfig } from '@rakun-kit/resend'
155
-
156
- rakunBootstrap({
157
- // ...
158
- mail: createResendMailServiceConfig({
159
- apiKey: process.env.RESEND_API_KEY!,
160
- defaultFrom: process.env.RAKUN_MAIL_FROM!,
161
- }),
162
- accountRecovery: {
163
- passwordReset: {
164
- expiresInMs: 60 * 60 * 1000,
165
- createUrl: (token) =>
166
- `https://cms.example.com/backend/reset-password?token=${encodeURIComponent(token)}`,
167
- },
168
- },
169
- })
170
- ```
171
-
172
- Core uses its branded HTML and plain-text password-reset template by default.
173
- Pass `accountRecovery.passwordReset.template` only when the application needs
174
- to replace it with custom branding or copy.
175
-
176
- Reset tokens are random, stored only as SHA-256 hashes, expire, and are consumed
177
- once. A successful reset closes every existing session but never disables MFA.
178
- The request endpoint always returns the same result for known and unknown email
179
- addresses.
180
-
181
- Enabling TOTP or the first WebAuthn device returns one-time recovery codes to
182
- the manager UI. Only their hashes are persisted, each code is consumed after
183
- one login, and users can replace the entire set from their account after
184
- confirming their current password. MFA state cannot be disabled through generic
185
- manager content operations. If a user loses all MFA methods and recovery codes,
186
- recovery must be handled by an authorized person through a channel outside the
187
- CMS.
188
-
189
- Content types can define lifecycle `hooks` and opt into manager-selected
190
- `dynamicData` sources.
191
-
192
- Routes can define fixed layout module slots. Rakun syncs those slots and the
193
- manager lets users select an existing entry for each route:
194
-
195
- ```ts
196
- rakunBootstrap({
197
- // ...
198
- routes: [
199
- {
200
- key: 'pages',
201
- contentType: 'Page',
202
- field: 'slug',
203
- hasPage: true,
204
- dynamic: false,
205
- defaultBasePath: '',
206
- layout: [
207
- { key: 'header', contentType: 'Header' },
208
- { type: 'content' },
209
- { key: 'footer', contentType: 'Footer' },
210
- ],
211
- },
212
- ],
213
- })
214
- ```
215
-
216
- The web page response includes an ordered `layout` array containing module slots
217
- plus the content slot. This is the only module collection in the response:
218
- content modules live in the `modules` property of the `content` layout item and
219
- are not duplicated at the top level.
220
-
221
- The response `info` object contains the page's normal resolved fields plus
222
- route context such as `locale` and `variantGroupId`. Translated website literals
223
- live in the separate top-level `literals` property. Composition-only fields
224
- (`_iterator`, `_seo`, and the legacy `_iteratorUnlinked` marker) are removed
225
- recursively from the page and all populated relations before `info` is sent.
226
-
227
- ## Module picker
228
-
229
- Content types used as iterator modules can customize their card in the manager's
230
- add-module dialog. `modulePicker.preview` accepts an image URL available to the
231
- manager browser; relative URLs are also supported:
232
-
233
- ```ts
234
- const Hero = new ContentType({
235
- name: 'Hero',
236
- modulePicker: {
237
- title: 'Hero section',
238
- description: 'Large introduction with heading, copy, and CTA.',
239
- category: 'Marketing',
240
- icon: 'PanelTop',
241
- preview: '/images/modules/hero.webp',
242
- keywords: ['banner', 'cover'],
243
- },
244
- fields: {
245
- title: f.string().help('content.heroTitleHelp').required(),
246
- },
247
- })
248
- ```
249
-
250
- When `preview` is omitted or cannot be loaded, the picker displays a neutral
251
- placeholder so module cards keep a consistent height.
252
-
253
- Use field `.help(text)` for longer, optional guidance. The manager places a help
254
- icon beside that field and opens the text on demand, including inside iterator
255
- and blocks modules. `text` may be direct copy or a manager translation key
256
- supplied by the application locale packs. Use `.description(...)` instead for
257
- short guidance that should remain visible below the field label.
258
-
259
- `ensureRakunInitialized()` prepares logger, MongoDB, media, and route syncing. It uses a singleton promise to avoid concurrent initialization; if initialization fails, the promise is cleared so the next call can retry.
260
-
261
- `ensureRakunBootstrap(options)` only calls `rakunBootstrap` if the runtime has not been bootstrapped yet.
262
-
263
- English is built into `@rakun-kit/manager-react`. Install only the extra
264
- manager UI locales an application needs:
265
-
266
- ```sh
267
- bun add @rakun-kit/manager-locales
268
- # or: npm install @rakun-kit/manager-locales
269
- ```
270
-
271
- Import the required language subpath and register it with `managerLanguages`;
272
- the public `manager.uiLocales` operation returns configured packs to the
273
- manager client. Its safe runtime configuration also includes the public SEO
274
- `siteUrl` and the configured home-page variant group, when available, so the
275
- manager can expose its Visit site action and classify SEO fallbacks correctly:
276
-
277
- ```ts
278
- import { esManagerLocalePack } from '@rakun-kit/manager-locales/es'
279
-
280
- rakunBootstrap({
281
- // ...
282
- managerLanguages: [esManagerLocalePack],
283
- })
284
- ```
285
-
286
- `managerLanguages` may also extend locales with arbitrary project keys. This is
287
- useful for translatable content-type titles and categories without adding host
288
- keys to the manager's static `ManagerMessageKey` union:
289
-
290
- ```ts
291
- import { extendManagerLanguagePack } from '@rakun-kit/core/contracts'
292
- import { esManagerLocalePack } from '@rakun-kit/manager-locales/es'
293
-
294
- rakunBootstrap({
295
- // ...
296
- managerLanguages: [
297
- {
298
- code: 'en',
299
- name: 'English',
300
- messages: {
301
- 'field.title': 'Title',
302
- 'layoutModule.header': 'Header',
303
- 'project.contentTypes.article.menu': 'Articles',
304
- },
305
- },
306
- extendManagerLanguagePack(esManagerLocalePack, {
307
- 'field.title': 'Título',
308
- 'layoutModule.header': 'Cabecera',
309
- 'project.contentTypes.article.menu': 'Artículos',
310
- }),
311
- ],
312
- })
313
- ```
314
-
315
- Content-type field labels automatically use `field.<fieldName>` from these
316
- project messages. Missing translations fall back to a built-in manager label
317
- when available and otherwise to the humanized field name.
318
-
319
- Route layout module labels use `layoutModule.<layoutKey>`, so a slot configured
320
- with `key: 'header'` resolves `layoutModule.header`.
321
-
322
- ## Plugins
323
-
324
- Trusted server plugins contribute to the same bootstrap registry without coupling
325
- core to React:
326
-
327
- ```ts
328
- import { defineRakunPlugin, rakunBootstrap } from '@rakun-kit/core'
329
-
330
- export const analyticsPlugin = defineRakunPlugin({
331
- id: '@acme/rakun-analytics',
332
- contentTypes: [AnalyticsEvent],
333
- routes: analyticsRoutes,
334
- apiOperations: analyticsOperations,
335
- permissions: ['plugin.analytics.view'],
336
- literals: {},
337
- initialize: async ({ db }) => {
338
- // Services and migrations are ready here. Keep initialization idempotent.
339
- },
340
- })
341
-
342
- rakunBootstrap({
343
- plugins: [analyticsPlugin],
344
- contentTypes: [],
345
- literals: {},
346
- mongo,
347
- })
348
- ```
349
-
350
- Plugin ids and contributed content types, routes, operations, literals, and
351
- custom field editor ids must be unique. Rakun reports both owners on conflicts.
352
- Visual manager and web facets are registered separately in their browser
353
- runtimes.
354
-
355
- Custom field factories can use `createPluginField`. Their serializable
356
- `meta.editor` must match a field declaration in the server plugin and a React
357
- editor registered by its manager facet. Each field also declares capabilities
358
- that shared systems consume instead of branching on its built-in type:
359
-
360
- ```ts
361
- const externalLink = createPluginField({
362
- meta: {
363
- type: 'Link',
364
- ui: 'Link',
365
- editor: '@acme/external-link',
366
- capabilities: {
367
- valueKind: 'object',
368
- dynamic: {
369
- properties: { title: 'string', href: 'string' },
370
- mapProperties: true,
371
- },
372
- },
373
- },
374
- schemas: sameSchemas(() => ExternalLinkSchema),
375
- runtime: {
376
- populate: (value, { populateLink }) => populateLink(value),
377
- },
378
- })
379
- ```
380
-
381
- `valueKind` controls Dynamic Data compatibility. `dynamic.properties` exposes
382
- nested source paths, `mapProperties` makes those paths independent mapping
383
- targets, `relation` enables content-type traversal, and `collection` describes
384
- `homogeneous` or `heterogeneous` per-item mapping. Capabilities are encoded for
385
- the manager. `runtime.populate` remains server-only and receives the database,
386
- recursive `populate`, and built-in `populateLink` helpers. The web pipeline
387
- exports this generic phase as `populateFields`; `populateLinks` remains a
388
- deprecated compatibility alias.
389
-
390
- ## Content Types
391
-
392
- A `ContentType` defines a logical collection:
393
-
394
- ```ts
395
- const Post = new ContentType({
396
- name: 'Post',
397
- menu: {
398
- title: 'Posts',
399
- icon: 'newspaper',
400
- category: 'Content',
401
- },
402
- fields: {
403
- title: f.string(),
404
- slug: f.string().type('Slug'),
405
- body: f.string().type('RichText').optional(),
406
- published: f.boolean().optional(),
407
- },
408
- uniques: [['slug']],
409
- listFields: ['title', 'slug', 'published'],
410
- })
411
- ```
412
-
413
- Page-like content types can define ordered page modules with `iterator` outside
414
- `fields`. Rakun persists this generated field as `_iterator`:
415
-
416
- ```ts
417
- import { StructuredData } from '@rakun-kit/core/internal-content-types'
418
-
419
- const Page = new ContentType({
420
- name: 'Page',
421
- fields: {
422
- title: f.string(),
423
- slug: f.string().type('Slug'),
424
- },
425
- iterator: [
426
- { contentType: PageSection, type: 'new' },
427
- { contentType: Hero, type: 'new' },
428
- { contentType: LayoutWithInfo, type: 'new' },
429
- { contentType: Newsletter, type: 'new' },
430
- { contentType: StructuredData, type: 'new' },
431
- ],
432
- })
433
- ```
434
-
435
- `StructuredData` is a built-in iterator module for JSON-LD. It provides typed
436
- Product, Article, Organization, WebSite, and BreadcrumbList forms plus a Custom
437
- JSON form. Add it to a shared Template and map its fields from the current
438
- document to reuse one schema across every page of the content type. The official
439
- React and Next renderers handle this module natively, so applications do not
440
- create a matching component file.
441
-
442
- `iterator` always belongs to the individual document and is edited in the
443
- manager's Content tab. When the content type has a configured `hasPage: true`
444
- route, Rakun automatically enables a separate shared Template tab with the
445
- same module picker as Content. The manager also offers one special Content slot
446
- at the template root and inside every `f.blocks(...)` field. The template must
447
- contain that slot exactly once; web output replaces it with the current
448
- document's iterator modules. This supports compositions such as a shared hero,
449
- a `LayoutWithInfo` wrapper containing the unique content, and a shared
450
- newsletter. Route layout modules such as header and footer still wrap the
451
- assembled result.
452
-
453
- Iterator and template modules can also be made conditional from the manager. A
454
- condition is stored on the module entry and evaluated against the current
455
- document:
456
-
457
- ```ts
458
- {
459
- name: "Credits",
460
- value: { /* module relation */ },
461
- visibleWhen: {
462
- field: "credits",
463
- operator: "notEmpty",
464
- },
465
- }
466
- ```
467
-
468
- Supported operators are `notEmpty` and `empty`. Conditional modules remain in
469
- their stored Content or Template composition but are omitted from web and
470
- preview output when their condition does not match.
471
-
472
- Main properties:
473
-
474
- - `name`: stable type name. Also used as `_type`.
475
- - `fields`: field map.
476
- - `iterator`: page module entries. Generates the reserved `_iterator` field.
477
- - `menu`: manager metadata.
478
- - `uniques`: unique field groups.
479
- - `listFields`: preferred fields in lists and relations.
480
- - `hideFromManager()`: hides the content type from manager content type lists.
481
- - `apiOnly()`: applies `.apiOnly()` to every field in the content type.
482
- - `managerOnly()`: applies `.managerOnly()` to every field in the content type.
483
- - Saved manager documents support comments and user mentions by default.
484
- - `withHooks()`: attaches lifecycle hooks such as `beforeInsert`, `beforeUpdate`, and `onGet`.
485
- - Dynamic data bindings are available on manager-visible fields by default; use
486
- field-level `.noDynamic()` to opt out.
487
- - A string field can initialize a new document's SEO dynamic data with
488
- `.seo('title')`, `.seo('description')`, or another string field from Rakun's
489
- built-in SEO model. This does not change existing documents.
490
-
491
- When a content type has a configured route with `hasPage: true`, Rakun adds an
492
- optional reserved `_seo` relation automatically. If that content type has an
493
- `iterator`, Rakun also enables its shared Template editor automatically.
494
-
495
- Hooks run around DB mutations and public output resolution:
496
-
497
- ```ts
498
- const User = new ContentType({
499
- name: 'User',
500
- fields: {
501
- email: f.string().type('Email').required(),
502
- password: f.string().type('Password').required().managerOnly(),
503
- },
504
- }).withHooks({
505
- beforeInsert: ({ data }) => ({
506
- ...data,
507
- password: hashPassword(String(data.password)),
508
- }),
509
- onGet: ({ data }) => ({
510
- ...data,
511
- displayName: String(data.email).split('@')[0],
512
- }),
513
- })
514
- ```
515
-
516
- Dynamic data turns a content type into a reusable layout. The manager can bind
517
- manager-visible fields to another content item field, or to a generated `href`
518
- when a source content type has a page route. Fields are dynamic by default; call
519
- `.noDynamic()` on fields that should not accept bindings or be exported as
520
- source paths. Source content types are hidden by default; set
521
- `dynamicDataSource: true` on content types that should appear in the manager
522
- source selector. Field bindings can also select `Current document` to read values
523
- from the document being edited without marking its content type as a source.
524
-
525
- Source field selectors are type-aware. A string target only offers string-like
526
- source paths, number targets only offer numbers, and boolean targets only offer
527
- booleans. Object-like source fields are traversed so nested leaf fields can be
528
- selected, while reserved SEO metadata is omitted from dynamic data mappings.
529
- The generated `href` source is only shown for content types that have a
530
- configured route with `hasPage: true`.
531
-
532
- In list mappings, a target `f.link()` is exposed as two independent target
533
- paths, `<field>.title` and `<field>.href`. Mapping those paths reconstructs the
534
- target link object instead of assigning a URL string to the complete field.
535
-
536
- Homogeneous `f.array(f.link())` and `f.array(f.relation(...))` targets can keep
537
- using a direct array field binding or switch to per-item list mapping.
538
- `f.relation(...).multiple()` is the same relation-array model and has the same
539
- mapping support. Link arrays expose `title` and `href` for every item; relation
540
- arrays expose the related content type's fields. Their resolved items stay flat,
541
- whereas heterogeneous `f.blocks(...)` items keep their `{ name, value }`
542
- wrapper.
543
-
544
- For example, a routeable category can pre-link its SEO title when a new category
545
- is created:
546
-
547
- ```ts
548
- const Category = new ContentType({
549
- name: 'Category',
550
- fields: {
551
- title: f.string().required().seo('title'),
552
- },
553
- })
554
- ```
555
-
556
- The manager stores this as a normal editable dynamic data binding from
557
- `seo.title` to `Current document · title`.
558
-
559
- ```ts
560
- const Project = new ContentType({
561
- name: 'Project',
562
- dynamicDataSource: true,
563
- fields: {
564
- title: f.string().required(),
565
- slug: f.string().type('Slug').required(),
566
- },
567
- })
568
-
569
- const Carousel = new ContentType({
570
- name: 'Carousel',
571
- fields: {
572
- title: f.string().required(),
573
- internalNote: f.string().noDynamic(),
574
- items: f.blocks([
575
- {
576
- name: 'CarouselItem',
577
- field: f.relation(CarouselItem, 'new'),
578
- },
579
- ]),
580
- },
581
- })
582
- ```
583
-
584
- List bindings append dynamic items to manually stored items instead of replacing
585
- the list. If the same stable item id appears in both sources, the dynamically
586
- resolved item wins and the duplicate manual copy is skipped.
587
-
588
- A list inside a nested module can also use a compatible relation or blocks array
589
- from the current document. The manager exposes these sources as
590
- `Current document · <field>`, infers the content type of each array item, and
591
- lets each item be mapped with the same field mapping UI used for collections.
592
- These bindings store a `currentDocument` source alongside the inferred item
593
- content type:
594
-
595
- ```ts
596
- const headerBindings = {
597
- lists: {
598
- categories: {
599
- contentType: LinkItem.name,
600
- source: {
601
- kind: 'currentDocument',
602
- contentType: Project.name,
603
- path: 'categories',
604
- itemName: 'Category',
605
- },
606
- itemName: 'Category',
607
- map: {
608
- title: { contentType: LinkItem.name, path: 'title' },
609
- href: { contentType: LinkItem.name, path: 'href' },
610
- },
611
- },
612
- },
613
- }
614
- ```
615
-
616
- List query conditions can compare a source field with a value from the current
617
- context. At the top level this is the current document; inside a nested list it
618
- is the parent source item and the manager labels it `Current item`. Programmatic
619
- bindings use `{ $current: "path.to.field" }`. For example, a category can query
620
- only the projects whose related category has the same slug:
621
-
622
- ```ts
623
- query: {
624
- filter: {
625
- "category.slug": { $current: "slug" },
626
- },
627
- options: { limit: 10 },
628
- }
629
- ```
630
-
631
- Nested conditions can still read the root document by selecting `Current
632
- document`, which stores `{ $document: "path.to.field" }`. Current-item and
633
- current-document paths are checked against their respective content type's
634
- dynamic field rules before the query runs. `_id` is also available for relation
635
- queries.
636
-
637
- List mappings can be nested recursively when a mapped target field is itself a
638
- `blocks` list, link array, or relation array. Use `kind: "list"` for that map
639
- entry and configure its source, query, item type, and field map exactly like a
640
- top-level list binding. Inside the nested query, `$current` refers to the source
641
- item being mapped by the parent list:
642
-
643
- ```ts
644
- map: {
645
- title: { contentType: Category.name, path: "title" },
646
- images: {
647
- kind: "list",
648
- contentType: Project.name,
649
- itemName: "CategoriesGalleryItemImage",
650
- query: {
651
- filter: { "category._id": { $current: "_id" } },
652
- options: { limit: 10, sort: { title: "asc" } },
653
- },
654
- map: {
655
- title: { contentType: Project.name, path: "title" },
656
- href: { contentType: Project.name, virtual: "href" },
657
- image: { contentType: Project.name, path: "image" },
658
- },
659
- },
660
- }
661
- ```
662
-
663
- The manager exposes this as the `Nested list` mapping mode. Nested mappings
664
- apply the same source opt-in, field compatibility, query validation, and
665
- `.noDynamic()` rules at every level.
666
-
667
- A list mapping can also collect an array through a reverse relation without
668
- persisting that relation on the source document. For example, a category gallery
669
- can create one item per `Category` and collect the images of its related
670
- `Project` documents:
671
-
672
- ```ts
673
- const Category = new ContentType({
674
- name: 'Category',
675
- dynamicDataSource: true,
676
- fields: {
677
- title: f.string().required(),
678
- },
679
- })
680
-
681
- const Project = new ContentType({
682
- name: 'Project',
683
- dynamicDataSource: true,
684
- fields: {
685
- category: f.relation(Category, 'existing').required(),
686
- images: f.file().type('Image').multiple().required(),
687
- },
688
- })
689
-
690
- const galleryBindings = {
691
- lists: {
692
- items: {
693
- contentType: Category.name,
694
- itemName: 'CategoriesGalleryItem',
695
- map: {
696
- title: { contentType: Category.name, path: 'title' },
697
- images: {
698
- kind: 'relatedCollection',
699
- contentType: Project.name,
700
- relation: 'category',
701
- path: 'images',
702
- limit: 10,
703
- },
704
- },
705
- },
706
- },
707
- }
708
- ```
709
-
710
- The related collection query matches `Project.category._id` against the current
711
- category, preserves project and image order, and flattens the selected array by
712
- one level. Its numeric limit applies to related projects and is capped at 100.
713
-
714
- Schema and validation methods:
715
-
716
- - `getInputSchema()`: write schema. Includes `_type`, `createdBy`, and `updatedBy`.
717
- - `getSchema()`: persisted schema.
718
- - `getPopulatedSchema()`: output with populated relations and `_id`.
719
- - `getOutputSchema()`: public API output.
720
- - `getOutputSchemaWithoutIterators()`: output variant without iterator fields.
721
- - `validate`, `partialValidate`, `validateOutput`: Zod helpers.
722
-
723
- The registry lives in `lib/Registry`:
724
-
725
- - `registerContentType`, `registerInternalContentType`.
726
- - `getContentTypes`, `getExternalContentTypes`, `getInternalContentTypes`.
727
- - `getContentTypesForManager`: returns serializable content types without Zod schemas and without content types marked with `hideFromManager()`.
728
- - `getContentTypeByName`.
729
-
730
- ## Fields
731
-
732
- Use the concise `f` namespace for field factories. `Fields` remains exported as
733
- a backward-compatible alias and references the same object.
734
-
735
- Main factories:
736
-
737
- ```ts
738
- f.string()
739
- f.number()
740
- f.boolean()
741
- f.date()
742
- f.select(['draft', 'published'])
743
- f.relation(Post)
744
- f.contentReference('Post')
745
- f.selfRelation()
746
- f.blocks([{ name: 'title', field: f.string() }])
747
- f.array(f.string())
748
- f.link()
749
- f.menu()
750
- f.file()
751
- f.breadcrums()
752
- ```
753
-
754
- Common modifiers:
755
-
756
- - Fields are required by default. Use `.optional()` when a value may be omitted.
757
- - `.required()` explicitly marks a field as required. It remains available for
758
- clarity and backward compatibility.
759
- - `.translatable()`: stores values per language with shape `{ _tag: "Translatable", ... }`.
760
- - `.apiOnly()`: available for API/persistence, hidden from manager.
761
- - `.managerOnly()`: visible to manager, excluded from API output.
762
-
763
- Notable fields:
764
-
765
- - `StringField`: UI `Text`, `Textarea`, `RichText`, `Email`, `Slug`, `Password`, `Id`, `Url`; supports `.min()` and `.max()`.
766
- - `NumberField`: supports `.min()` and `.max()`.
767
- - `DateField`: supports `.type("Date")`, `.type("DateTime")` and
768
- `.type("Time")`. Date and datetime writes accept JavaScript `Date` values or
769
- their ISO datetime JSON representation and normalize them to `Date`; time
770
- values remain ISO time strings.
771
- - `RelationField`: relation to another `ContentType`; accepts existing references or inline creation. `f.relation(Post, "existing")` restricts to existing records; `"new"` restricts to new records. `.multiple()` returns a homogeneous array of relations.
772
- - `ContentReferenceField`: reference by content type name.
773
- - `LinkField`: the manager stores a direct `{ href, title }` value or an
774
- internal `{ routeId, contentTypeId, title }` reference. Titled links resolve
775
- to `{ href, title }` in web output, with internal `href` values localized by
776
- route. New input and web output always use an object shape. Persisted legacy
777
- direct URL strings remain readable and receive an empty `title` when loaded
778
- by the manager or normalized for web output.
779
- - `MenuField`: `f.menu()` stores an ordered navigation tree. Every node is an
780
- internal or direct link plus `children`, for example
781
- `{ title, href, children: [...] }`. Internal references use `routeId` and
782
- `contentTypeId` while persisted and resolve recursively to localized `href`
783
- values in web output. The manager supports drag-and-drop ordering and
784
- unlimited nested levels.
785
- - `BreadcrumsField`: `f.breadcrums()` is a computed, API-only field for page
786
- modules. In web and preview output it returns the localized route hierarchy as
787
- `{ label, href }[]`, ordered from the highest ancestor to the current page. It
788
- returns `null` when the content is resolved outside a routable page and is
789
- never accepted as input, persisted, or rendered in the manager.
790
- - `FileField`: integrates media and optimization options.
791
- - `f.blocks(...)`: heterogeneous ordered list. Each item stores a `name` and a `value`, and the value can match one of the named field shapes. Use it for block-like content where different item types can appear in the same list.
792
- - `f.array(...)`: homogeneous ordered list. Every item uses the same field shape. Chain `.min(count)` and `.max(count)` to validate its item count. The same limits are available after `.multiple()` on relation, file, select, and content-reference fields, for example `f.file().multiple().min(1).max(4)`.
793
- - `IteratorField`: repeatable structure based on content type entries.
794
-
795
- ## Derived Types
796
-
797
- `@rakun-kit/core/types` and `lib/types` derive types from `ContentType`:
798
-
799
- - `DataInput<T>`: accepted shape for create/update.
800
- - `DBOutput<T>`: persisted shape with metadata (`_id`, `_type`, timestamps, authorship).
801
- - `DataPopulated<T>`: shape with populated relations.
802
- - `DataFront<T>`: public output without `apiOnly` fields.
803
- - `Filter<T>`, `Query<T>`, `ListInput<T>`, `GetAllInput<T>`: typed filters and list inputs.
804
- - `MaybeTranslatableValue<T>`, `TranslatableValue<T>`.
805
-
806
- ## ORM and Mongo
807
-
808
- `core/src/orm` implements `DBService` on top of MongoDB:
809
-
810
- ```ts
811
- const db = await getMongoService()
812
-
813
- const post = await db.create(Post, {
814
- _type: 'Post',
815
- title: 'Hello',
816
- slug: 'hello',
817
- })
818
- ```
819
-
820
- Operations:
821
-
822
- - `get(contentType, id, fields?)`
823
- - `list(contentType, query)`
824
- - `create(contentType, data)`
825
- - `update(contentType, id, data)`
826
- - `updateMany(contentType, filter, data)`
827
- - `delete(contentType, filter)`
828
- - `find(contentType, filter, fields?)`
829
- - `clear(contentType)`
830
- - `findDependencies(contentType, id)`
831
- - `upsert(contentType, filter, data)`
832
- - `getAll(contentType, query?)`
833
-
834
- Connection:
835
-
836
- - `createMongoConnection(config)`: stores config.
837
- - `createMongoService(config)`: connects and creates handlers.
838
- - `getMongoService()`: returns the singleton or creates it from config.
839
- - `closeMongoService()`: closes the connection and clears the singleton.
840
-
841
- `MongoConfig`:
842
-
843
- ```ts
844
- type MongoConfig = {
845
- MONGO_URI: string
846
- ENVIRONMENT?: 'local' | 'development' | 'test' | 'production'
847
- }
848
- ```
849
-
850
- In environments other than `test`, the connection creates indexes defined by `createIndexes`.
851
-
852
- ## API Operations
853
-
854
- `api/operations` defines typed contracts and handlers for endpoints:
855
-
856
- - Manager: CRUD, auth, MFA, media, literals, settings.
857
- - Manager comments and mentions: `manager.comments.list`,
858
- `manager.comments.create`, `manager.comments.toggleReaction`,
859
- `manager.comments.markRead`, `manager.comments.unreadCount`,
860
- `manager.users.mentions`,
861
- `manager.notifications.list`, and `manager.notifications.markRead`.
862
- - Web: page resolution, static paths, sitemap, robots, `llms.txt`, and preview.
863
-
864
- `web.staticPaths` returns `{ path, ttl }` only for route-map entries backed by
865
- page routes configured with `dynamic: false`. Adapters can use this operation
866
- for static generation without querying Rakun's database directly. Monolithic
867
- server adapters can instead call `getRakunWebStaticPaths`, `getRakunWebPage`,
868
- and `getRakunWebPreviewPage` after bootstrapping and initializing core.
869
-
870
- `web.llms` returns the optional site-level `llms.txt` document configured in
871
- Manager Settings. Rakun renders a curated Markdown title, summary, guidance,
872
- sections, and links; it does not dump the sitemap automatically. Internal links
873
- are resolved for the requested language and become absolute when SEO `siteUrl`
874
- is configured. Links without a public route are omitted without hiding their
875
- section heading. Optional sections retain their titles beneath the conventional
876
- `## Optional` heading. The llms title and summary fall back to SEO `siteName` and the
877
- default SEO description. The result is `null` while publishing is disabled or
878
- there is no usable title. Server adapters can call `getRakunWebLlmsTxt`
879
- directly after initialization.
880
-
881
- `manager.localeVariants.list` includes an optional `path` on each language
882
- assignment when that exact assigned document is published and has a generated
883
- page route. Manager clients can use it for a contextual public-page link without
884
- granting editors direct access to internal `RouteMap` records.
885
-
886
- Routeable content with document visibility is public only when `_visibility`
887
- is explicitly `published` or `hidden`. Missing visibility is treated as draft,
888
- both while generating route maps and when resolving a public page, so legacy or
889
- incomplete records fail closed even if an obsolete route-map entry remains.
890
-
891
- Main helpers:
892
-
893
- - `defineOperationContract`: declares a contract with Zod input/output, method, path, and metadata.
894
- - `defineOperation`: combines a contract with its implementation.
895
- - `createRakunApiClient`: creates a browser/server HTTP client for custom operations.
896
- - `GetClient`: derives a typed client from an operation map.
897
- - `createManagerOperationContracts`, `createWebOperationContracts`.
898
- - `createManagerOperationDefinitions`, `createWebOperationDefinitions`.
899
- - `createRakunOperationDefinitions`: combines manager + web.
900
- - `createOperationManifest`: serializable metadata for clients.
901
- - `createOperationPath`: HTTP path from operation name.
902
- - `mergeOperationContracts`: combines maps.
903
-
904
- `@rakun-kit/core/manager` exposes the manifest and name-based types:
905
-
906
- ```ts
907
- import {
908
- managerOperationManifest,
909
- getManagerOperationMeta,
910
- type ManagerOperationInput,
911
- type ManagerOperationOutput,
912
- } from '@rakun-kit/core/manager'
913
- ```
914
-
915
- ### Custom Operations
916
-
917
- Applications can define operations in a separate object, pass that object to
918
- bootstrap, and reuse its type on the frontend:
919
-
920
- ```ts
921
- // server/api-operations.ts
922
- import { defineOperation } from '@rakun-kit/core'
923
- import { z } from 'zod'
924
-
925
- export const apiOperations = {
926
- 'demo.helloWorld': defineOperation<
927
- { text: string },
928
- { message: string },
929
- 'query',
930
- 'get',
931
- 'public'
932
- >({
933
- access: 'public',
934
- kind: 'query',
935
- method: 'get',
936
- description: 'Return a hello world message with the provided text',
937
- input: z.object({
938
- text: z.string().default('world'),
939
- }),
940
- output: z.object({
941
- message: z.string(),
942
- }),
943
- resolve: ({ input }) => ({
944
- message: `Hello ${input.text}`,
945
- }),
946
- }),
947
- }
948
- ```
949
-
950
- ```ts
951
- // bootstrap
952
- import { apiOperations } from './server/api-operations'
953
-
954
- rakunBootstrap({
955
- // ...
956
- apiOperations,
957
- })
958
- ```
959
-
960
- Operation names define their HTTP path: `demo.helloWorld` becomes
961
- `/demo/helloWorld`.
962
-
963
- Rules:
964
-
965
- - Names must not collide with built-in operations.
966
- - Operations prefixed with `manager.` are included in manager operation routers.
967
- - Operations prefixed with `web.` are included in web operation routers.
968
- - Operations with any other prefix are included when using the combined operation registry.
969
- - Use `access: "public"` for operations called from public web pages.
970
- - Use `access: "auth"` for operations that require a manager session.
971
-
972
- The manager route `manager.apiOperations` returns a JSON-serializable operation
973
- catalog for manager API docs/playgrounds. Input and output Zod schemas are
974
- converted to JSON Schema for display.
975
-
976
- ### Typed API Client
977
-
978
- `@rakun-kit/core/web` exposes a small typed HTTP client for operation maps:
979
-
980
- ```ts
981
- import { createRakunApiClient, type GetClient } from '@rakun-kit/core/web'
982
- import type { apiOperations } from './server/api-operations'
983
-
984
- type ApiClient = GetClient<typeof apiOperations>
985
-
986
- const client: ApiClient = createRakunApiClient<typeof apiOperations>({
987
- baseUrl: '/api',
988
- })
989
-
990
- const result = await client.query('demo.helloWorld', {
991
- text: 'Rakun',
992
- })
993
-
994
- result.message
995
- ```
996
-
997
- The client exposes:
998
-
999
- - `query(name, input?, options?)`: only accepts operations with `kind: "query"`.
1000
- - `mutation(name, input?, options?)`: only accepts operations with `kind: "mutation"`.
1001
-
1002
- The client derives input and output types from the Zod schemas in
1003
- `apiOperations`.
1004
-
1005
- ## Request Context and Auth
1006
-
1007
- `createRequestContext(input)` normalizes headers, cookies, and response:
1008
-
1009
- ```ts
1010
- const ctx = await createRequestContext({
1011
- headers: req.headers,
1012
- cookies,
1013
- res: {
1014
- setHeader: res.setHeader.bind(res),
1015
- cookie: res.cookie.bind(res),
1016
- },
1017
- })
1018
- ```
1019
-
1020
- The resulting context includes:
1021
-
1022
- - `req.headers`, `req.cookies`
1023
- - `res.setHeader`, `res.cookie`
1024
- - `user`: manager user or `null`
1025
- - `getUser()`: returns the user or throws `AUTH_REQUIRED`
1026
-
1027
- Session cookies are managed with `getSessionCookie` and `setSessionCookie`.
1028
-
1029
- ## Media
1030
-
1031
- Media uses a storage adapter:
1032
-
1033
- ```ts
1034
- createMediaService({
1035
- adapter,
1036
- defaultAccess: 'private',
1037
- defaultGetExpiresInSeconds: 300,
1038
- uploadUrl: '/api/rakun/manager/media/upload',
1039
- })
1040
- ```
1041
-
1042
- APIs:
1043
-
1044
- - `createMediaConnection(config)`: stores config without creating the service.
1045
- - `createMediaService(config)`: creates the singleton with an adapter.
1046
- - `getMediaService()`: returns the singleton or creates it from config.
1047
- - `handleMediaBinaryUpload`: processes manager binary uploads.
1048
-
1049
- The service supports prepare/finalize upload, URL generation, folders, and media optimization depending on adapter/configuration. Manager uploads encode original file names for transport, so Unicode names (including accents, non-Latin scripts, and emoji) are preserved without placing invalid characters in HTTP headers.
1050
-
1051
- Optimized video uploads use the `ffmpeg-static` peer dependency and produce an MP4 primary object plus MP4 and WebM entries in `sources`. Existing image-only optimize options remain compatible; use `video: { quality: 80 }` to configure video quality explicitly.
1052
-
1053
- Existing images and videos can be reimported from the media manager with the selected optimization settings. The replacement uses new storage keys and updates the existing `Media` record only after every requested variant has been written, preserving its ID and content references. Preview generation stores a compact `data:image/...` string on `previewUrl` for LQIP use in `@rakun-kit/react` `Image`, instead of writing a separate preview object to storage.
1054
-
1055
- The `manager.media.replace` mutation accepts a newly uploaded image and swaps it into an existing `Media` record. It preserves the media ID, name, title, alt text, folder, status, and all content references; after the database points to the new storage keys, Rakun removes the previous primary object and generated variants and revalidates directly dependent documents when route revalidation is configured.
1056
-
1057
- ## Persistent Event Log
1058
-
1059
- Rakun keeps business/audit events separate from its technical console logger.
1060
- The event log keeps events immutable and defaults to a MongoDB collection
1061
- with indexes for time, type, category, outcome, severity, correlation and tags:
1062
-
1063
- ```ts
1064
- import { recordEvent, queryEvents } from '@rakun-kit/core'
1065
-
1066
- await recordEvent({
1067
- type: 'content.article.published',
1068
- category: 'content',
1069
- outcome: 'success',
1070
- actor: { type: 'manager-user', id: userId },
1071
- resource: { type: 'Article', id: articleId },
1072
- correlationId: requestId,
1073
- tags: ['editorial'],
1074
- data: {
1075
- locale: 'es',
1076
- changedFields: 3,
1077
- },
1078
- })
1079
-
1080
- const page = await queryEvents({
1081
- categories: ['content'],
1082
- outcomes: ['success'],
1083
- from: new Date('2026-01-01T00:00:00.000Z'),
1084
- limit: 50,
1085
- })
1086
- ```
1087
-
1088
- `data` accepts nested JSON values. Event queries use cursor pagination and can
1089
- filter by types, categories, severities, outcomes, sources, correlation,
1090
- required tags and a date range. A custom persistence implementation can be
1091
- plugged in globally:
1092
-
1093
- ```ts
1094
- import type { EventLogAdapter } from '@rakun-kit/core'
1095
-
1096
- const adapter: EventLogAdapter = {
1097
- async append(event) {
1098
- return customStore.append(event)
1099
- },
1100
- async query(filters) {
1101
- return customStore.query(filters)
1102
- },
1103
- async deleteBefore(before) {
1104
- return customStore.deleteBefore(before)
1105
- },
1106
- }
1107
-
1108
- rakunBootstrap({
1109
- // ...
1110
- eventLog: { adapter },
1111
- })
1112
- ```
1113
-
1114
- Plugins receive the resolved `eventLog` service in their initialization
1115
- context. Reading a shared event stream should be protected with the built-in
1116
- `system.eventLog.read` permission. The built-in `manager.logs.list` operation and the
1117
- manager Settings → Logs screen both enforce it.
1118
-
1119
- Retention cleanup is available from the manager Logs screen to users with the
1120
- built-in `system.eventLog.manage` permission. It deletes events strictly older
1121
- than the selected cutoff. Custom adapters must implement `deleteBefore` to
1122
- support this action. The cleanup mutation itself is logged after deletion.
1123
-
1124
- Failed API operations are persisted automatically as `api.operation.failed`
1125
- events. This includes expected 4xx application errors and unexpected 5xx
1126
- failures across the core operation wrapper and the Express, Next.js, and tRPC
1127
- adapters. Events include the operation, status, method, kind, correlation ID,
1128
- and authenticated actor when available. Request payloads, application error
1129
- causes, and raw internal error messages are not persisted.
1130
-
1131
- Every successful API mutation is also persisted with an operation-specific
1132
- event type such as `manager.create.succeeded` or
1133
- `manager.backups.restore.succeeded`. Successful queries are not recorded by
1134
- default, which keeps the event stream focused on state changes. Mutation
1135
- events contain operation metadata and actor/correlation context, but never
1136
- copy the input payload or result.
1137
-
1138
- ## Mail
1139
-
1140
- Mail providers receive normalized, already-rendered messages through
1141
- `MailAdapter`, so neither `core` nor an adapter depends on React or a template
1142
- engine:
1143
-
1144
- ```ts
1145
- import type { MailAdapter } from '@rakun-kit/core'
1146
-
1147
- const adapter: MailAdapter = {
1148
- async send(message) {
1149
- const result = await provider.send(message)
1150
- return { id: result.id }
1151
- },
1152
- }
1153
-
1154
- rakunBootstrap({
1155
- // ...
1156
- mail: {
1157
- adapter,
1158
- defaultFrom: 'hello@example.com',
1159
- defaultReplyTo: 'support@example.com',
1160
- },
1161
- })
1162
- ```
1163
-
1164
- Send rendered content directly:
1165
-
1166
- ```ts
1167
- import { sendMail } from '@rakun-kit/core'
1168
-
1169
- await sendMail({
1170
- to: 'ada@example.com',
1171
- subject: 'Welcome',
1172
- html: '<p>Hello Ada</p>',
1173
- text: 'Hello Ada',
1174
- })
1175
- ```
1176
-
1177
- Or create a typed application template registry:
1178
-
1179
- ```ts
1180
- import { createMailSender, defineMailTemplate } from '@rakun-kit/core'
1181
-
1182
- const mail = createMailSender({
1183
- templates: {
1184
- welcome: defineMailTemplate<{ name: string }>({
1185
- subject: ({ name }) => `Welcome, ${name}`,
1186
- render: ({ name }) => ({
1187
- html: `<p>Hello ${name}</p>`,
1188
- text: `Hello ${name}`,
1189
- }),
1190
- }),
1191
- },
1192
- })
1193
-
1194
- await mail.send({
1195
- template: 'welcome',
1196
- props: { name: 'Ada' },
1197
- to: 'ada@example.com',
1198
- })
1199
- ```
1200
-
1201
- The common contract supports To/CC/BCC/Reply-To, custom headers and in-memory
1202
- `Uint8Array` attachments. Sending is immediate; queues, retries and delivery
1203
- events belong to application infrastructure.
1204
-
1205
- Every mail sent through a bootstrapped Rakun mail service creates append-only
1206
- `mail.send.attempted` and `mail.send.succeeded` or `mail.send.failed` events.
1207
- They share a correlation id and include only operational counts, provider,
1208
- template and duration. Recipient addresses, subject, HTML/text, headers,
1209
- attachment names/content, credentials and raw provider errors are never copied
1210
- to the persistent event log. If the initial attempt event cannot be persisted,
1211
- the provider is not called.
1212
-
1213
- ## Literals and Translation
1214
-
1215
- Bootstrap receives `literals`. Related utilities:
1216
-
1217
- - `getTranslation`: resolves translatable values.
1218
- - `translateObject`: translates objects with translatable fields.
1219
- - Manager schemas for listing/upserting website literals (not manager UI chrome).
1220
-
1221
- Translatable values use this shape:
1222
-
1223
- ```ts
1224
- {
1225
- _tag: "Translatable",
1226
- en: "Title",
1227
- es: "Titulo"
1228
- }
1229
- ```
1230
-
1231
- ## Permissions
1232
-
1233
- `lib/Permissions` exposes:
1234
-
1235
- - `getPermissionList`: generates a permission list.
1236
- - `hasPermissions`: validates user/role permissions.
1237
- - `mapPermissions`: transforms permissions.
1238
- - `Permission`: public type.
1239
-
1240
- Manager routes apply permissions, ownership, and auth through utilities in `api/utils`.
1241
-
1242
- ## Errors
1243
-
1244
- Application errors live in `lib/errors`:
1245
-
1246
- - `AppError`
1247
- - `throwAppError`
1248
- - `errors`
1249
- - `instanceofAppErrorShape`
1250
-
1251
- Database errors live in `orm/dbService`:
1252
-
1253
- - `DbError`
1254
- - `DbErrorUnknown`
1255
- - `DbErrorNotFound`
1256
- - `DbErrorInvalidData`
1257
- - `DbErrorConflict`
1258
- - `DbErrorSimulatedFailure`
1259
-
1260
- ## Runtime Flow
1261
-
1262
- 1. The app defines content types with `ContentType` and `f`.
1263
- 2. The app calls `rakunBootstrap(options)`.
1264
- 3. The HTTP adapter calls `ensureRakunInitialized()` before serving Rakun routes.
1265
- 4. `ensureRakunInitialized()` configures logger, MongoDB, the persistent event
1266
- log, media, mail, and route syncing.
1267
- 5. Each request creates a `RakunRequestContext`.
1268
- 6. Manager/web operations validate input, run logic, validate output, and return typed contracts.
1269
-
1270
- ## Tests and Development Notes
1271
-
1272
- The package includes targeted tests next to modules (`*.test.ts`), for example ORM, translation, routes, redirects, and populated relations.
1273
-
1274
- Package build:
1275
-
1276
- ```sh
1277
- bun run build --workspace @rakun-kit/core
1278
- ```
1279
-
1280
- The script generates ESM and CJS in `dist/`, adjusting imports and the CJS `package.json` through repo scripts.
1
+ # @rakun-kit/core
2
+
3
+ `@rakun-kit/core` contains Rakun's shared domain model and runtime. It defines content types, fields, Zod schemas, manager/web operations, request context, MongoDB connection handling, media service, permissions, translations, and typed contracts used by adapters (`@rakun-kit/next`, `@rakun-kit/express`, `@rakun-kit/trpc`) and the manager client.
4
+
5
+ ## Main Entry
6
+
7
+ Typical imports:
8
+
9
+ ```ts
10
+ import { ContentType, f, rakunBootstrap, ensureRakunInitialized } from '@rakun-kit/core'
11
+ ```
12
+
13
+ The main entrypoint exports:
14
+
15
+ - Bootstrap: `rakunBootstrap`, `ensureRakunBootstrap`, `ensureRakunInitialized`, `getRakunBootstrapOptions`.
16
+ - Context: `createRequestContext`, `getSessionCookie`, `setSessionCookie`.
17
+ - Operations: manager/web contracts and definitions.
18
+ - Media: `createMediaService`, `getMediaService`, adapters, and types.
19
+ - Mail: `createMailService`, `sendMail`, typed template registries, adapters, and types.
20
+ - Permissions, translations, errors, contracts, and public types.
21
+ - Basic internal content types: `Language`, `ManagerUser`, `Seo`.
22
+
23
+ Relevant subpaths:
24
+
25
+ - `@rakun-kit/core/manager`: manager operation metadata and types.
26
+ - `@rakun-kit/core/contracts`: shared manager/web payload schemas.
27
+ - `@rakun-kit/core/client`: client-facing types and utilities.
28
+ - `@rakun-kit/core/logger`: logger.
29
+ - `@rakun-kit/core/errors`: application errors.
30
+ - `@rakun-kit/core/types`: types derived from content types.
31
+
32
+ ## Bootstrap
33
+
34
+ `rakunBootstrap(options)` registers global configuration and content types:
35
+
36
+ ```ts
37
+ rakunBootstrap({
38
+ literals,
39
+ contentTypes: [Page, Post],
40
+ routes,
41
+ apiOperations,
42
+ mongo: {
43
+ MONGO_URI: process.env.MONGO_URI!,
44
+ ENVIRONMENT: process.env.NODE_ENV === 'test' ? 'test' : 'production',
45
+ },
46
+ media,
47
+ logger: {
48
+ level: 'info',
49
+ prettify: true,
50
+ },
51
+ syncRoutes: true,
52
+ })
53
+ ```
54
+
55
+ Options:
56
+
57
+ - `literals`: text/translation catalog.
58
+ - `contentTypes`: application content types.
59
+ - `internalContentTypes`: optional overrides for Rakun internal content types, currently `Page`.
60
+ - `routes`: configured routes used to resolve pages.
61
+ - `apiOperations`: custom API operations added to the Rakun operation registry.
62
+ - `mongo`: MongoDB connection. Required before serving Rakun requests.
63
+ - `media`: media adapter/configuration. Optional.
64
+ - `mail`: outbound mail adapter and default sender configuration. Optional.
65
+ - `collaboration`: storage adapter for unsaved Yjs content working documents.
66
+ Optional; defaults to process memory.
67
+ - `accountRecovery`: password-reset URL builder, expiry, and optional custom
68
+ mail template. Requires `mail`; core provides the default template.
69
+ - `login`: manager password-login toggle and external login adapters.
70
+ - `logger`: logger configuration. If omitted, an `info` logger with `prettify` is created.
71
+ - `syncRoutes`: syncs configured routes during initialization. Enabled by default.
72
+
73
+ ## Runtime Platform
74
+
75
+ `rakunBootstrap` accepts an optional resolved `platform`. Without one, Rakun
76
+ detects Bun through `process.versions.bun` and otherwise uses Node.js defaults.
77
+ Runtime, framework, and deployment are separate values, and every capability
78
+ can be overridden without configuring the rest:
79
+
80
+ ```ts
81
+ import { createPlatform, pollingRealtime, sharpImage } from '@rakun-kit/core'
82
+
83
+ rakunBootstrap({
84
+ ...options,
85
+ platform: createPlatform({
86
+ deployment: 'serverless',
87
+ image: sharpImage(),
88
+ realtime: pollingRealtime({ intervalMs: 5_000 }),
89
+ }),
90
+ })
91
+ ```
92
+
93
+ The default image processor is native `Bun.Image` when available and `sharp`
94
+ otherwise. Node.js image optimization therefore requires the optional `sharp`
95
+ peer. Bun also falls back to it when an OS-dependent codec cannot handle the
96
+ requested format. Custom `ImageProcessor` implementations provide
97
+ `metadata`, `transform`, and `placeholder`; the latter returns an inline
98
+ `{ dataUrl, mime }` LQIP. Bun uses native `Bun.Image.placeholder()`, while
99
+ Sharp produces the placeholder through its normal reduced transform and serves
100
+ as the fallback when the Bun method is unavailable. The
101
+ platform resolves crypto, filesystem, compression,
102
+ workers, and realtime capabilities. Polling is the safe realtime default when
103
+ the host cannot keep persistent connections open. The Next and Express
104
+ adapters automatically serve configured SSE streams.
105
+ Calling `sseRealtime()` uses the API-relative `/realtime` endpoint; pass an
106
+ explicit `endpoint` only when the public route differs.
107
+
108
+ The manager bootstrap exposes only the safe transport metadata from this
109
+ provider. Manager mutations publish invalidation topics for collaborative
110
+ documents, locale variants, versions, comments, and notifications. Event
111
+ transports therefore refresh matching queries on demand; polling keeps the
112
+ same behavior through its configured interval.
113
+
114
+ Server adapters share `parseRealtimeTopics`, `isRealtimeEndpointRequest`,
115
+ `createRealtimeSseStream`, and `authorizeRealtimeSubscription` from core. A
116
+ custom HTTP framework can use these primitives without importing either the
117
+ Next or Express adapter.
118
+
119
+ The built-in SSE provider uses an in-process topic broker. For multiple server
120
+ processes or replicas, provide a `RealtimeProvider` backed by a shared broker
121
+ while keeping the same transport metadata and subscription contract.
122
+
123
+ ## Collaborative content documents
124
+
125
+ Every saved content document and draft has an independent Yjs working document
126
+ keyed by its content type and `_id`. Editor updates synchronize through the
127
+ authenticated `manager.contentCollaboration.sync` operation without changing
128
+ the MongoDB content snapshot or triggering revalidation. Save calls
129
+ `manager.contentCollaboration.save`, which materializes and validates the
130
+ server-side CRDT state before using the normal update and revalidation flow.
131
+ Draft promotion remains separate and promotes only a saved draft snapshot.
132
+ `manager.contentCollaboration.discard` restores the room from that persisted
133
+ snapshot, publishes the reset to connected editors, and marks the restored Yjs
134
+ state as saved without creating a new content revision. Shared templates expose
135
+ the equivalent `manager.templateCollaboration.discard` operation.
136
+
137
+ The default adapter keeps working documents in process memory. Configure a
138
+ shared, durable implementation for replicated or restart-durable deployments:
139
+
140
+ ```ts
141
+ rakunBootstrap({
142
+ // ...
143
+ collaboration: {
144
+ adapter: {
145
+ load: async (roomId) => loadRoom(roomId),
146
+ save: async (roomId, state) => saveRoom(roomId, state),
147
+ loadPresence: async (roomId) => loadRoomPresence(roomId),
148
+ savePresence: async (roomId, presence) => saveRoomPresence(roomId, presence),
149
+ delete: async (roomId) => deleteRoom(roomId),
150
+ },
151
+ },
152
+ })
153
+ ```
154
+
155
+ `state.update` and `state.savedStateVector` are opaque `Uint8Array` values.
156
+ Store them outside the public content record. The built-in manager applies this
157
+ to saved content edit screens and to each content type's shared Template.
158
+ Templates use their own `template:<contentType>` room rather than an individual
159
+ document room. Settings, route-layout overrides, and other administrative forms
160
+ remain local. Translation changes join the working document and are persisted
161
+ only by Save.
162
+
163
+ The sync response also includes ephemeral viewers keyed by browser-tab client
164
+ id, including the focused field. Presence expires automatically and is not
165
+ part of `state.update`, `state.savedStateVector`, or the persistent event log.
166
+ With SSE, the authenticated subscription owns the tab presence: server
167
+ heartbeats renew it and closing the last stream for that tab removes it, so the
168
+ manager does not send a periodic collaboration sync request just to stay
169
+ visible. Polling refreshes presence through its normal sync cycle.
170
+ `loadPresence` and `savePresence` are optional; implement both in replicated
171
+ deployments that need presence shared across processes.
172
+
173
+ `@rakun-kit/manager-react` additionally stores these Yjs rooms per user in
174
+ IndexedDB, allowing cached content and Template fields to remain editable during
175
+ temporary connection loss. Reconnection exchanges the local updates with core;
176
+ only an online Save commits them to the public content snapshot.
177
+
178
+ ## External manager login
179
+
180
+ Configure external login methods in `rakunBootstrap`. The callback URL is the
181
+ manager's public `/login/callback` route.
182
+
183
+ ```ts
184
+ import {
185
+ createGitHubLoginAdapter,
186
+ createGoogleLoginAdapter,
187
+ createMicrosoftLoginAdapter,
188
+ rakunBootstrap,
189
+ } from '@rakun-kit/core'
190
+
191
+ const callbackUrl = 'https://cms.example.com/backend/login/callback'
192
+
193
+ rakunBootstrap({
194
+ // ...
195
+ login: {
196
+ password: true,
197
+ adapters: [
198
+ createGitHubLoginAdapter({
199
+ clientId: process.env.GITHUB_CLIENT_ID!,
200
+ clientSecret: process.env.GITHUB_CLIENT_SECRET!,
201
+ redirectUri: callbackUrl,
202
+ }),
203
+ createGoogleLoginAdapter({
204
+ clientId: process.env.GOOGLE_CLIENT_ID!,
205
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
206
+ redirectUri: callbackUrl,
207
+ }),
208
+ createMicrosoftLoginAdapter({
209
+ clientId: process.env.MICROSOFT_CLIENT_ID!,
210
+ clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
211
+ redirectUri: callbackUrl,
212
+ tenant: 'common',
213
+ }),
214
+ ],
215
+ },
216
+ })
217
+ ```
218
+
219
+ Register the exact callback URL in each provider console. External identities
220
+ must return a verified email matching an
221
+ existing manager user. Rakun does not create manager accounts automatically.
222
+ The flow uses an HttpOnly state cookie and PKCE and preserves the existing MFA
223
+ step. Set `password: false` only when at least one adapter is configured.
224
+ Custom providers can be implemented with `defineLoginAdapter`.
225
+
226
+ ### Persistent password-login IP blocking
227
+
228
+ Password login enables persistent fail2ban-style IP blocking by default. Five
229
+ failed attempts block the IP indefinitely; a successful login before reaching
230
+ the limit clears its failed-attempt counter. Configure or disable it through
231
+ `login.fail2ban`:
232
+
233
+ ```ts
234
+ rakunBootstrap({
235
+ // ...
236
+ login: {
237
+ password: true,
238
+ fail2ban: {
239
+ maxAttempts: 5,
240
+ // Optional when the deployment uses a custom trusted proxy:
241
+ // resolveIp: (ctx) => ctx.req?.headers?.['your-trusted-ip-header'] as string,
242
+ },
243
+ },
244
+ })
245
+ ```
246
+
247
+ Users with the `auth.ipBlocks.manage` permission can inspect and remove blocks
248
+ under Manager Settings → Security. Admin roles receive this built-in permission
249
+ automatically. A block can also be removed directly from MongoDB by deleting
250
+ the matching document from the `LoginIpBlock` collection. Set `fail2ban: false`
251
+ to disable this behavior. External login adapters are not affected.
252
+
253
+ ## Account recovery and MFA recovery
254
+
255
+ Password recovery is enabled when both `mail` and `accountRecovery` are
256
+ configured. The reset URL must point to the manager's public
257
+ `/reset-password` screen and preserve the token as the `token` query parameter:
258
+
259
+ ```ts
260
+ import { rakunBootstrap } from '@rakun-kit/core'
261
+ import { createResendMailServiceConfig } from '@rakun-kit/resend'
262
+
263
+ rakunBootstrap({
264
+ // ...
265
+ mail: createResendMailServiceConfig({
266
+ apiKey: process.env.RESEND_API_KEY!,
267
+ defaultFrom: process.env.RAKUN_MAIL_FROM!,
268
+ }),
269
+ accountRecovery: {
270
+ passwordReset: {
271
+ expiresInMs: 60 * 60 * 1000,
272
+ createUrl: (token) =>
273
+ `https://cms.example.com/backend/reset-password?token=${encodeURIComponent(token)}`,
274
+ },
275
+ },
276
+ })
277
+ ```
278
+
279
+ Core uses its branded HTML and plain-text password-reset template by default.
280
+ Pass `accountRecovery.passwordReset.template` only when the application needs
281
+ to replace it with custom branding or copy.
282
+
283
+ Reset tokens are random, stored only as SHA-256 hashes, expire, and are consumed
284
+ once. A successful reset closes every existing session but never disables MFA.
285
+ The request endpoint always returns the same result for known and unknown email
286
+ addresses.
287
+
288
+ Enabling TOTP or the first WebAuthn device returns one-time recovery codes to
289
+ the manager UI. Only their hashes are persisted, each code is consumed after
290
+ one login, and users can replace the entire set from their account after
291
+ confirming their current password. MFA state cannot be disabled through generic
292
+ manager content operations. If a user loses all MFA methods and recovery codes,
293
+ recovery must be handled by an authorized person through a channel outside the
294
+ CMS.
295
+
296
+ Content types can define lifecycle `hooks` and opt into manager-selected
297
+ `dynamicData` sources.
298
+
299
+ Routes can define fixed layout module slots. Rakun syncs those slots and the
300
+ manager lets users select an existing entry for each route:
301
+
302
+ ```ts
303
+ rakunBootstrap({
304
+ // ...
305
+ routes: [
306
+ {
307
+ key: 'pages',
308
+ contentType: 'Page',
309
+ field: 'slug',
310
+ hasPage: true,
311
+ dynamic: false,
312
+ defaultBasePath: '',
313
+ layout: [
314
+ { key: 'header', contentType: 'Header' },
315
+ { type: 'content' },
316
+ { key: 'footer', contentType: 'Footer' },
317
+ ],
318
+ },
319
+ ],
320
+ })
321
+ ```
322
+
323
+ The web page response includes an ordered `layout` array containing module slots
324
+ plus the content slot. This is the only module collection in the response:
325
+ content modules live in the `modules` property of the `content` layout item and
326
+ are not duplicated at the top level.
327
+
328
+ The response `info` object contains the page's normal resolved fields plus
329
+ route context such as `locale` and `variantGroupId`. Translated website literals
330
+ live in the separate top-level `literals` property. Composition-only fields
331
+ (`_iterator`, `_seo`, and the legacy `_iteratorUnlinked` marker) are removed
332
+ recursively from the page and all populated relations before `info` is sent.
333
+
334
+ ## Module picker
335
+
336
+ Content types used as iterator modules can customize their card in the manager's
337
+ add-module dialog. `modulePicker.preview` accepts an image URL available to the
338
+ manager browser; relative URLs are also supported:
339
+
340
+ ```ts
341
+ const Hero = new ContentType({
342
+ name: 'Hero',
343
+ modulePicker: {
344
+ title: 'Hero section',
345
+ description: 'Large introduction with heading, copy, and CTA.',
346
+ category: 'Marketing',
347
+ icon: 'PanelTop',
348
+ preview: '/images/modules/hero.webp',
349
+ keywords: ['banner', 'cover'],
350
+ },
351
+ fields: {
352
+ title: f.string().help('content.heroTitleHelp').required(),
353
+ },
354
+ })
355
+ ```
356
+
357
+ When `preview` is omitted or cannot be loaded, the picker displays a neutral
358
+ placeholder so module cards keep a consistent height.
359
+
360
+ Use field `.help(text)` for longer, optional guidance. The manager places a help
361
+ icon beside that field and opens the text on demand, including inside iterator
362
+ and blocks modules. `text` may be direct copy or a manager translation key
363
+ supplied by the application locale packs. Use `.description(...)` instead for
364
+ short guidance that should remain visible below the field label.
365
+
366
+ `ensureRakunInitialized()` prepares logger, MongoDB, media, and route syncing. It uses a singleton promise to avoid concurrent initialization; if initialization fails, the promise is cleared so the next call can retry.
367
+
368
+ `ensureRakunBootstrap(options)` only calls `rakunBootstrap` if the runtime has not been bootstrapped yet.
369
+
370
+ English is built into `@rakun-kit/manager-react`. Install only the extra
371
+ manager UI locales an application needs:
372
+
373
+ ```sh
374
+ bun add @rakun-kit/manager-locales
375
+ # or: npm install @rakun-kit/manager-locales
376
+ ```
377
+
378
+ Import the required language subpath and register it with `managerLanguages`;
379
+ the public `manager.uiLocales` operation returns configured packs to the
380
+ manager client. Its safe runtime configuration also includes the public SEO
381
+ `siteUrl` and the configured home-page variant group, when available, so the
382
+ manager can expose its Visit site action and classify SEO fallbacks correctly:
383
+
384
+ ```ts
385
+ import { esManagerLocalePack } from '@rakun-kit/manager-locales/es'
386
+
387
+ rakunBootstrap({
388
+ // ...
389
+ managerLanguages: [esManagerLocalePack],
390
+ })
391
+ ```
392
+
393
+ `managerLanguages` may also extend locales with arbitrary project keys. This is
394
+ useful for translatable content-type titles and categories without adding host
395
+ keys to the manager's static `ManagerMessageKey` union:
396
+
397
+ ```ts
398
+ import { extendManagerLanguagePack } from '@rakun-kit/core/contracts'
399
+ import { esManagerLocalePack } from '@rakun-kit/manager-locales/es'
400
+
401
+ rakunBootstrap({
402
+ // ...
403
+ managerLanguages: [
404
+ {
405
+ code: 'en',
406
+ name: 'English',
407
+ messages: {
408
+ 'field.title': 'Title',
409
+ 'layoutModule.header': 'Header',
410
+ 'project.contentTypes.article.menu': 'Articles',
411
+ },
412
+ },
413
+ extendManagerLanguagePack(esManagerLocalePack, {
414
+ 'field.title': 'Título',
415
+ 'layoutModule.header': 'Cabecera',
416
+ 'project.contentTypes.article.menu': 'Artículos',
417
+ }),
418
+ ],
419
+ })
420
+ ```
421
+
422
+ Content-type field labels automatically use `field.<fieldName>` from these
423
+ project messages. Missing translations fall back to a built-in manager label
424
+ when available and otherwise to the humanized field name.
425
+
426
+ Route layout module labels use `layoutModule.<layoutKey>`, so a slot configured
427
+ with `key: 'header'` resolves `layoutModule.header`.
428
+
429
+ ## Plugins
430
+
431
+ Trusted server plugins contribute to the same bootstrap registry without coupling
432
+ core to React:
433
+
434
+ ```ts
435
+ import { defineRakunPlugin, rakunBootstrap } from '@rakun-kit/core'
436
+
437
+ export const analyticsPlugin = defineRakunPlugin({
438
+ id: '@acme/rakun-analytics',
439
+ contentTypes: [AnalyticsEvent],
440
+ routes: analyticsRoutes,
441
+ apiOperations: analyticsOperations,
442
+ permissions: ['plugin.analytics.view'],
443
+ literals: {},
444
+ initialize: async ({ db }) => {
445
+ // Services and migrations are ready here. Keep initialization idempotent.
446
+ },
447
+ })
448
+
449
+ rakunBootstrap({
450
+ plugins: [analyticsPlugin],
451
+ contentTypes: [],
452
+ literals: {},
453
+ mongo,
454
+ })
455
+ ```
456
+
457
+ Plugin ids and contributed content types, routes, operations, literals, and
458
+ custom field editor ids must be unique. Rakun reports both owners on conflicts.
459
+ Visual manager and web facets are registered separately in their browser
460
+ runtimes.
461
+
462
+ Custom field factories can use `createPluginField`. Their serializable
463
+ `meta.editor` must match a field declaration in the server plugin and a React
464
+ editor registered by its manager facet. Each field also declares capabilities
465
+ that shared systems consume instead of branching on its built-in type:
466
+
467
+ ```ts
468
+ const externalLink = createPluginField({
469
+ meta: {
470
+ type: 'Link',
471
+ ui: 'Link',
472
+ editor: '@acme/external-link',
473
+ capabilities: {
474
+ valueKind: 'object',
475
+ dynamic: {
476
+ properties: { title: 'string', href: 'string' },
477
+ mapProperties: true,
478
+ },
479
+ },
480
+ },
481
+ schemas: sameSchemas(() => ExternalLinkSchema),
482
+ runtime: {
483
+ populate: (value, { populateLink }) => populateLink(value),
484
+ },
485
+ })
486
+ ```
487
+
488
+ `valueKind` controls Dynamic Data compatibility. `dynamic.properties` exposes
489
+ nested source paths, `mapProperties` makes those paths independent mapping
490
+ targets, `relation` enables content-type traversal, and `collection` describes
491
+ `homogeneous` or `heterogeneous` per-item mapping. Capabilities are encoded for
492
+ the manager. `runtime.populate` remains server-only and receives the database,
493
+ recursive `populate`, and built-in `populateLink` helpers. The web pipeline
494
+ exports this generic phase as `populateFields`; `populateLinks` remains a
495
+ deprecated compatibility alias.
496
+
497
+ ## Content Types
498
+
499
+ A `ContentType` defines a logical collection:
500
+
501
+ ```ts
502
+ const Post = new ContentType({
503
+ name: 'Post',
504
+ menu: {
505
+ title: 'Posts',
506
+ icon: 'newspaper',
507
+ category: 'Content',
508
+ },
509
+ fields: {
510
+ title: f.string(),
511
+ slug: f.string().type('Slug'),
512
+ body: f.string().type('RichText').optional(),
513
+ published: f.boolean().optional(),
514
+ },
515
+ uniques: [['slug']],
516
+ listFields: ['title', 'slug', 'published'],
517
+ })
518
+ ```
519
+
520
+ Page-like content types can define ordered page modules with `iterator` outside
521
+ `fields`. Rakun persists this generated field as `_iterator`:
522
+
523
+ ```ts
524
+ import { StructuredData } from '@rakun-kit/core/internal-content-types'
525
+
526
+ const Page = new ContentType({
527
+ name: 'Page',
528
+ fields: {
529
+ title: f.string(),
530
+ slug: f.string().type('Slug'),
531
+ },
532
+ iterator: [
533
+ { contentType: PageSection, type: 'new' },
534
+ { contentType: Hero, type: 'new' },
535
+ { contentType: LayoutWithInfo, type: 'new' },
536
+ { contentType: Newsletter, type: 'new' },
537
+ { contentType: StructuredData, type: 'new' },
538
+ ],
539
+ })
540
+ ```
541
+
542
+ `StructuredData` is a built-in iterator module for JSON-LD. It provides typed
543
+ Product, Article, Organization, WebSite, and BreadcrumbList forms plus a Custom
544
+ JSON form. Add it to a shared Template and map its fields from the current
545
+ document to reuse one schema across every page of the content type. The official
546
+ React and Next renderers handle this module natively, so applications do not
547
+ create a matching component file.
548
+
549
+ `iterator` always belongs to the individual document and is edited in the
550
+ manager's Content tab. When the content type has a configured `hasPage: true`
551
+ route, Rakun automatically enables a separate shared Template tab with the
552
+ same module picker as Content. The manager also offers one special Content slot
553
+ at the template root and inside every `f.blocks(...)` field. The template must
554
+ contain that slot exactly once; web output replaces it with the current
555
+ document's iterator modules. This supports compositions such as a shared hero,
556
+ a `LayoutWithInfo` wrapper containing the unique content, and a shared
557
+ newsletter. Route layout modules such as header and footer still wrap the
558
+ assembled result.
559
+
560
+ Iterator and template modules can also be made conditional from the manager. A
561
+ condition is stored on the module entry and evaluated against the current
562
+ document:
563
+
564
+ ```ts
565
+ {
566
+ name: "Credits",
567
+ value: { /* module relation */ },
568
+ visibleWhen: {
569
+ field: "credits",
570
+ operator: "notEmpty",
571
+ },
572
+ }
573
+ ```
574
+
575
+ Supported operators are `notEmpty` and `empty`. Conditional modules remain in
576
+ their stored Content or Template composition but are omitted from web and
577
+ preview output when their condition does not match.
578
+
579
+ Main properties:
580
+
581
+ - `name`: stable type name. Also used as `_type`.
582
+ - `fields`: field map.
583
+ - `iterator`: page module entries. Generates the reserved `_iterator` field.
584
+ - `menu`: manager metadata.
585
+ - `uniques`: unique field groups.
586
+ - `listFields`: preferred fields in lists and relations.
587
+ - `hideFromManager()`: hides the content type from manager content type lists.
588
+ - `apiOnly()`: applies `.apiOnly()` to every field in the content type.
589
+ - `managerOnly()`: applies `.managerOnly()` to every field in the content type.
590
+ - Saved manager documents support comments and user mentions by default.
591
+ - `withHooks()`: attaches lifecycle hooks such as `beforeInsert`, `beforeUpdate`, and `onGet`.
592
+ - Dynamic data bindings are available on manager-visible fields by default; use
593
+ field-level `.noDynamic()` to opt out.
594
+ - A string field can initialize a new document's SEO dynamic data with
595
+ `.seo('title')`, `.seo('description')`, or another string field from Rakun's
596
+ built-in SEO model. This does not change existing documents.
597
+
598
+ When a content type has a configured route with `hasPage: true`, Rakun adds an
599
+ optional reserved `_seo` relation automatically. If that content type has an
600
+ `iterator`, Rakun also enables its shared Template editor automatically.
601
+
602
+ Hooks run around DB mutations and public output resolution:
603
+
604
+ ```ts
605
+ const User = new ContentType({
606
+ name: 'User',
607
+ fields: {
608
+ email: f.string().type('Email').required(),
609
+ password: f.string().type('Password').required().managerOnly(),
610
+ },
611
+ }).withHooks({
612
+ beforeInsert: ({ data }) => ({
613
+ ...data,
614
+ password: hashPassword(String(data.password)),
615
+ }),
616
+ onGet: ({ data }) => ({
617
+ ...data,
618
+ displayName: String(data.email).split('@')[0],
619
+ }),
620
+ })
621
+ ```
622
+
623
+ Dynamic data turns a content type into a reusable layout. The manager can bind
624
+ manager-visible fields to another content item field, or to a generated `href`
625
+ when a source content type has a page route. Fields are dynamic by default; call
626
+ `.noDynamic()` on fields that should not accept bindings or be exported as
627
+ source paths. Source content types are hidden by default; set
628
+ `dynamicDataSource: true` on content types that should appear in the manager
629
+ source selector. Field bindings can also select `Current document` to read values
630
+ from the document being edited without marking its content type as a source.
631
+
632
+ Source field selectors are type-aware. A string target only offers string-like
633
+ source paths, number targets only offer numbers, and boolean targets only offer
634
+ booleans. Object-like source fields are traversed so nested leaf fields can be
635
+ selected, while reserved SEO metadata is omitted from dynamic data mappings.
636
+ The generated `href` source is only shown for content types that have a
637
+ configured route with `hasPage: true`.
638
+
639
+ In list mappings, a target `f.link()` is exposed as two independent target
640
+ paths, `<field>.title` and `<field>.href`. Mapping those paths reconstructs the
641
+ target link object instead of assigning a URL string to the complete field.
642
+
643
+ Homogeneous `f.array(f.link())` and `f.array(f.relation(...))` targets can keep
644
+ using a direct array field binding or switch to per-item list mapping.
645
+ `f.relation(...).multiple()` is the same relation-array model and has the same
646
+ mapping support. Link arrays expose `title` and `href` for every item; relation
647
+ arrays expose the related content type's fields. Their resolved items stay flat,
648
+ whereas heterogeneous `f.blocks(...)` items keep their `{ name, value }`
649
+ wrapper.
650
+
651
+ For example, a routeable category can pre-link its SEO title when a new category
652
+ is created:
653
+
654
+ ```ts
655
+ const Category = new ContentType({
656
+ name: 'Category',
657
+ fields: {
658
+ title: f.string().required().seo('title'),
659
+ },
660
+ })
661
+ ```
662
+
663
+ The manager stores this as a normal editable dynamic data binding from
664
+ `seo.title` to `Current document · title`.
665
+
666
+ ```ts
667
+ const Project = new ContentType({
668
+ name: 'Project',
669
+ dynamicDataSource: true,
670
+ fields: {
671
+ title: f.string().required(),
672
+ slug: f.string().type('Slug').required(),
673
+ },
674
+ })
675
+
676
+ const Carousel = new ContentType({
677
+ name: 'Carousel',
678
+ fields: {
679
+ title: f.string().required(),
680
+ internalNote: f.string().noDynamic(),
681
+ items: f.blocks([
682
+ {
683
+ name: 'CarouselItem',
684
+ field: f.relation(CarouselItem, 'new'),
685
+ },
686
+ ]),
687
+ },
688
+ })
689
+ ```
690
+
691
+ List bindings append dynamic items to manually stored items instead of replacing
692
+ the list. If the same stable item id appears in both sources, the dynamically
693
+ resolved item wins and the duplicate manual copy is skipped.
694
+
695
+ A list inside a nested module can also use a compatible relation or blocks array
696
+ from the current document. The manager exposes these sources as
697
+ `Current document · <field>`, infers the content type of each array item, and
698
+ lets each item be mapped with the same field mapping UI used for collections.
699
+ These bindings store a `currentDocument` source alongside the inferred item
700
+ content type:
701
+
702
+ ```ts
703
+ const headerBindings = {
704
+ lists: {
705
+ categories: {
706
+ contentType: LinkItem.name,
707
+ source: {
708
+ kind: 'currentDocument',
709
+ contentType: Project.name,
710
+ path: 'categories',
711
+ itemName: 'Category',
712
+ },
713
+ itemName: 'Category',
714
+ map: {
715
+ title: { contentType: LinkItem.name, path: 'title' },
716
+ href: { contentType: LinkItem.name, path: 'href' },
717
+ },
718
+ },
719
+ },
720
+ }
721
+ ```
722
+
723
+ List query conditions can compare a source field with a value from the current
724
+ context. At the top level this is the current document; inside a nested list it
725
+ is the parent source item and the manager labels it `Current item`. Programmatic
726
+ bindings use `{ $current: "path.to.field" }`. For example, a category can query
727
+ only the projects whose related category has the same slug:
728
+
729
+ ```ts
730
+ query: {
731
+ filter: {
732
+ "category.slug": { $current: "slug" },
733
+ },
734
+ options: { limit: 10 },
735
+ }
736
+ ```
737
+
738
+ Nested conditions can still read the root document by selecting `Current
739
+ document`, which stores `{ $document: "path.to.field" }`. Current-item and
740
+ current-document paths are checked against their respective content type's
741
+ dynamic field rules before the query runs. `_id` is also available for relation
742
+ queries.
743
+
744
+ List mappings can be nested recursively when a mapped target field is itself a
745
+ `blocks` list, link array, or relation array. Use `kind: "list"` for that map
746
+ entry and configure its source, query, item type, and field map exactly like a
747
+ top-level list binding. Inside the nested query, `$current` refers to the source
748
+ item being mapped by the parent list:
749
+
750
+ ```ts
751
+ map: {
752
+ title: { contentType: Category.name, path: "title" },
753
+ images: {
754
+ kind: "list",
755
+ contentType: Project.name,
756
+ itemName: "CategoriesGalleryItemImage",
757
+ query: {
758
+ filter: { "category._id": { $current: "_id" } },
759
+ options: { limit: 10, sort: { title: "asc" } },
760
+ },
761
+ map: {
762
+ title: { contentType: Project.name, path: "title" },
763
+ href: { contentType: Project.name, virtual: "href" },
764
+ image: { contentType: Project.name, path: "image" },
765
+ },
766
+ },
767
+ }
768
+ ```
769
+
770
+ The manager exposes this as the `Nested list` mapping mode. Nested mappings
771
+ apply the same source opt-in, field compatibility, query validation, and
772
+ `.noDynamic()` rules at every level.
773
+
774
+ A list mapping can also collect an array through a reverse relation without
775
+ persisting that relation on the source document. For example, a category gallery
776
+ can create one item per `Category` and collect the images of its related
777
+ `Project` documents:
778
+
779
+ ```ts
780
+ const Category = new ContentType({
781
+ name: 'Category',
782
+ dynamicDataSource: true,
783
+ fields: {
784
+ title: f.string().required(),
785
+ },
786
+ })
787
+
788
+ const Project = new ContentType({
789
+ name: 'Project',
790
+ dynamicDataSource: true,
791
+ fields: {
792
+ category: f.relation(Category, 'existing').required(),
793
+ images: f.file().type('Image').multiple().required(),
794
+ },
795
+ })
796
+
797
+ const galleryBindings = {
798
+ lists: {
799
+ items: {
800
+ contentType: Category.name,
801
+ itemName: 'CategoriesGalleryItem',
802
+ map: {
803
+ title: { contentType: Category.name, path: 'title' },
804
+ images: {
805
+ kind: 'relatedCollection',
806
+ contentType: Project.name,
807
+ relation: 'category',
808
+ path: 'images',
809
+ limit: 10,
810
+ },
811
+ },
812
+ },
813
+ },
814
+ }
815
+ ```
816
+
817
+ The related collection query matches `Project.category._id` against the current
818
+ category, preserves project and image order, and flattens the selected array by
819
+ one level. Its numeric limit applies to related projects and is capped at 100.
820
+
821
+ Schema and validation methods:
822
+
823
+ - `getInputSchema()`: write schema. Includes `_type`, `createdBy`, and `updatedBy`.
824
+ - `getSchema()`: persisted schema.
825
+ - `getPopulatedSchema()`: output with populated relations and `_id`.
826
+ - `getOutputSchema()`: public API output.
827
+ - `getOutputSchemaWithoutIterators()`: output variant without iterator fields.
828
+ - `validate`, `partialValidate`, `validateOutput`: Zod helpers.
829
+
830
+ The registry lives in `lib/Registry`:
831
+
832
+ - `registerContentType`, `registerInternalContentType`.
833
+ - `getContentTypes`, `getExternalContentTypes`, `getInternalContentTypes`.
834
+ - `getContentTypesForManager`: returns serializable content types without Zod schemas and without content types marked with `hideFromManager()`.
835
+ - `getContentTypeByName`.
836
+
837
+ ## Fields
838
+
839
+ Use the concise `f` namespace for field factories. `Fields` remains exported as
840
+ a backward-compatible alias and references the same object.
841
+
842
+ Main factories:
843
+
844
+ ```ts
845
+ f.string()
846
+ f.number()
847
+ f.boolean()
848
+ f.date()
849
+ f.select(['draft', 'published'])
850
+ f.relation(Post)
851
+ f.contentReference('Post')
852
+ f.selfRelation()
853
+ f.blocks([{ name: 'title', field: f.string() }])
854
+ f.array(f.string())
855
+ f.link()
856
+ f.menu()
857
+ f.file()
858
+ f.breadcrums()
859
+ ```
860
+
861
+ Common modifiers:
862
+
863
+ - Fields are required by default. Use `.optional()` when a value may be omitted.
864
+ - `.required()` explicitly marks a field as required. It remains available for
865
+ clarity and backward compatibility.
866
+ - `.translatable()`: stores values per language with shape `{ _tag: "Translatable", ... }`.
867
+ - `.apiOnly()`: available for API/persistence, hidden from manager.
868
+ - `.managerOnly()`: visible to manager, excluded from API output.
869
+
870
+ Notable fields:
871
+
872
+ - `StringField`: UI `Text`, `Textarea`, `RichText`, `Email`, `Slug`, `Password`, `Id`, `Url`; supports `.min()` and `.max()`.
873
+ - `NumberField`: supports `.min()` and `.max()`.
874
+ - `DateField`: supports `.type("Date")`, `.type("DateTime")` and
875
+ `.type("Time")`. Date and datetime writes accept JavaScript `Date` values or
876
+ their ISO datetime JSON representation and normalize them to `Date`; time
877
+ values remain ISO time strings.
878
+ - `RelationField`: relation to another `ContentType`; accepts existing references or inline creation. `f.relation(Post, "existing")` restricts to existing records; `"new"` restricts to new records. `.multiple()` returns a homogeneous array of relations.
879
+ - `ContentReferenceField`: reference by content type name.
880
+ - `LinkField`: the manager stores a direct `{ href, title }` value or an
881
+ internal `{ routeId, contentTypeId, title }` reference. Titled links resolve
882
+ to `{ href, title }` in web output, with internal `href` values localized by
883
+ route. New input and web output always use an object shape. Persisted legacy
884
+ direct URL strings remain readable and receive an empty `title` when loaded
885
+ by the manager or normalized for web output.
886
+ - `MenuField`: `f.menu()` stores an ordered navigation tree. Every node is an
887
+ internal or direct link plus `children`, for example
888
+ `{ title, href, children: [...] }`. Internal references use `routeId` and
889
+ `contentTypeId` while persisted and resolve recursively to localized `href`
890
+ values in web output. The manager supports drag-and-drop ordering and
891
+ unlimited nested levels.
892
+ - `BreadcrumsField`: `f.breadcrums()` is a computed, API-only field for page
893
+ modules. In web and preview output it returns the localized route hierarchy as
894
+ `{ label, href }[]`, ordered from the highest ancestor to the current page. It
895
+ returns `null` when the content is resolved outside a routable page and is
896
+ never accepted as input, persisted, or rendered in the manager.
897
+ - `FileField`: integrates media and optimization options.
898
+ - `f.blocks(...)`: heterogeneous ordered list. Each item stores a `name` and a `value`, and the value can match one of the named field shapes. Use it for block-like content where different item types can appear in the same list.
899
+ - `f.array(...)`: homogeneous ordered list. Every item uses the same field shape. Chain `.min(count)` and `.max(count)` to validate its item count. The same limits are available after `.multiple()` on relation, file, select, and content-reference fields, for example `f.file().multiple().min(1).max(4)`.
900
+ - `IteratorField`: repeatable structure based on content type entries.
901
+
902
+ ## Derived Types
903
+
904
+ `@rakun-kit/core/types` and `lib/types` derive types from `ContentType`:
905
+
906
+ - `DataInput<T>`: accepted shape for create/update.
907
+ - `DBOutput<T>`: persisted shape with metadata (`_id`, `_type`, timestamps, authorship).
908
+ - `DataPopulated<T>`: shape with populated relations.
909
+ - `DataFront<T>`: public output without `apiOnly` fields.
910
+ - `Filter<T>`, `Query<T>`, `ListInput<T>`, `GetAllInput<T>`: typed filters and list inputs.
911
+ - `MaybeTranslatableValue<T>`, `TranslatableValue<T>`.
912
+
913
+ ## ORM and Mongo
914
+
915
+ `core/src/orm` implements `DBService` on top of MongoDB:
916
+
917
+ ```ts
918
+ const db = await getMongoService()
919
+
920
+ const post = await db.create(Post, {
921
+ _type: 'Post',
922
+ title: 'Hello',
923
+ slug: 'hello',
924
+ })
925
+ ```
926
+
927
+ Operations:
928
+
929
+ - `get(contentType, id, fields?)`
930
+ - `list(contentType, query)`
931
+ - `create(contentType, data)`
932
+ - `update(contentType, id, data)`
933
+ - `updateMany(contentType, filter, data)`
934
+ - `delete(contentType, filter)`
935
+ - `find(contentType, filter, fields?)`
936
+ - `clear(contentType)`
937
+ - `findDependencies(contentType, id)`
938
+ - `upsert(contentType, filter, data)`
939
+ - `getAll(contentType, query?)`
940
+
941
+ Connection:
942
+
943
+ - `createMongoConnection(config)`: stores config.
944
+ - `createMongoService(config)`: connects and creates handlers.
945
+ - `getMongoService()`: returns the singleton or creates it from config.
946
+ - `closeMongoService()`: closes the connection and clears the singleton.
947
+
948
+ `MongoConfig`:
949
+
950
+ ```ts
951
+ type MongoConfig = {
952
+ MONGO_URI: string
953
+ ENVIRONMENT?: 'local' | 'development' | 'test' | 'production'
954
+ }
955
+ ```
956
+
957
+ In environments other than `test`, the connection creates indexes defined by `createIndexes`.
958
+
959
+ ## API Operations
960
+
961
+ `api/operations` defines typed contracts and handlers for endpoints:
962
+
963
+ - Manager: CRUD, auth, MFA, media, literals, settings.
964
+ - Manager comments and mentions: `manager.comments.list`,
965
+ `manager.comments.create`, `manager.comments.toggleReaction`,
966
+ `manager.comments.markRead`, `manager.comments.unreadCount`,
967
+ `manager.users.mentions`,
968
+ `manager.notifications.list`, and `manager.notifications.markRead`.
969
+ - Web: page resolution, static paths, sitemap, robots, `llms.txt`, and preview.
970
+
971
+ `web.staticPaths` returns `{ path, ttl }` only for route-map entries backed by
972
+ page routes configured with `dynamic: false`. Adapters can use this operation
973
+ for static generation without querying Rakun's database directly. Monolithic
974
+ server adapters can instead call `getRakunWebStaticPaths`, `getRakunWebPage`,
975
+ and `getRakunWebPreviewPage` after bootstrapping and initializing core.
976
+
977
+ `web.llms` returns the optional site-level `llms.txt` document configured in
978
+ Manager Settings. Rakun renders a curated Markdown title, summary, guidance,
979
+ sections, and links; it does not dump the sitemap automatically. Internal links
980
+ are resolved for the requested language and become absolute when SEO `siteUrl`
981
+ is configured. Links without a public route are omitted without hiding their
982
+ section heading. Optional sections retain their titles beneath the conventional
983
+ `## Optional` heading. The llms title and summary fall back to SEO `siteName` and the
984
+ default SEO description. The result is `null` while publishing is disabled or
985
+ there is no usable title. Server adapters can call `getRakunWebLlmsTxt`
986
+ directly after initialization.
987
+
988
+ `manager.localeVariants.list` includes an optional `path` on each language
989
+ assignment when that exact assigned document is published and has a generated
990
+ page route. Manager clients can use it for a contextual public-page link without
991
+ granting editors direct access to internal `RouteMap` records.
992
+
993
+ Routeable content with document visibility is public only when `_visibility`
994
+ is explicitly `published` or `hidden`. Missing visibility is treated as draft,
995
+ both while generating route maps and when resolving a public page, so legacy or
996
+ incomplete records fail closed even if an obsolete route-map entry remains.
997
+
998
+ Main helpers:
999
+
1000
+ - `defineOperationContract`: declares a contract with Zod input/output, method, path, and metadata.
1001
+ - `defineOperation`: combines a contract with its implementation.
1002
+ - `createRakunApiClient`: creates a browser/server HTTP client for custom operations.
1003
+ - `GetClient`: derives a typed client from an operation map.
1004
+ - `createManagerOperationContracts`, `createWebOperationContracts`.
1005
+ - `createManagerOperationDefinitions`, `createWebOperationDefinitions`.
1006
+ - `createRakunOperationDefinitions`: combines manager + web.
1007
+ - `createOperationManifest`: serializable metadata for clients.
1008
+ - `createOperationPath`: HTTP path from operation name.
1009
+ - `mergeOperationContracts`: combines maps.
1010
+
1011
+ `@rakun-kit/core/manager` exposes the manifest and name-based types:
1012
+
1013
+ ```ts
1014
+ import {
1015
+ managerOperationManifest,
1016
+ getManagerOperationMeta,
1017
+ type ManagerOperationInput,
1018
+ type ManagerOperationOutput,
1019
+ } from '@rakun-kit/core/manager'
1020
+ ```
1021
+
1022
+ ### Custom Operations
1023
+
1024
+ Applications can define operations in a separate object, pass that object to
1025
+ bootstrap, and reuse its type on the frontend:
1026
+
1027
+ ```ts
1028
+ // server/api-operations.ts
1029
+ import { defineOperation } from '@rakun-kit/core'
1030
+ import { z } from 'zod'
1031
+
1032
+ export const apiOperations = {
1033
+ 'demo.helloWorld': defineOperation<
1034
+ { text: string },
1035
+ { message: string },
1036
+ 'query',
1037
+ 'get',
1038
+ 'public'
1039
+ >({
1040
+ access: 'public',
1041
+ kind: 'query',
1042
+ method: 'get',
1043
+ description: 'Return a hello world message with the provided text',
1044
+ input: z.object({
1045
+ text: z.string().default('world'),
1046
+ }),
1047
+ output: z.object({
1048
+ message: z.string(),
1049
+ }),
1050
+ resolve: ({ input }) => ({
1051
+ message: `Hello ${input.text}`,
1052
+ }),
1053
+ }),
1054
+ }
1055
+ ```
1056
+
1057
+ ```ts
1058
+ // bootstrap
1059
+ import { apiOperations } from './server/api-operations'
1060
+
1061
+ rakunBootstrap({
1062
+ // ...
1063
+ apiOperations,
1064
+ })
1065
+ ```
1066
+
1067
+ Operation names define their HTTP path: `demo.helloWorld` becomes
1068
+ `/demo/helloWorld`.
1069
+
1070
+ Rules:
1071
+
1072
+ - Names must not collide with built-in operations.
1073
+ - Operations prefixed with `manager.` are included in manager operation routers.
1074
+ - Operations prefixed with `web.` are included in web operation routers.
1075
+ - Operations with any other prefix are included when using the combined operation registry.
1076
+ - Use `access: "public"` for operations called from public web pages.
1077
+ - Use `access: "auth"` for operations that require a manager session.
1078
+
1079
+ The manager route `manager.apiOperations` returns a JSON-serializable operation
1080
+ catalog for manager API docs/playgrounds. Input and output Zod schemas are
1081
+ converted to JSON Schema for display.
1082
+
1083
+ ### Typed API Client
1084
+
1085
+ `@rakun-kit/core/web` exposes a small typed HTTP client for operation maps:
1086
+
1087
+ ```ts
1088
+ import { createRakunApiClient, type GetClient } from '@rakun-kit/core/web'
1089
+ import type { apiOperations } from './server/api-operations'
1090
+
1091
+ type ApiClient = GetClient<typeof apiOperations>
1092
+
1093
+ const client: ApiClient = createRakunApiClient<typeof apiOperations>({
1094
+ baseUrl: '/api',
1095
+ })
1096
+
1097
+ const result = await client.query('demo.helloWorld', {
1098
+ text: 'Rakun',
1099
+ })
1100
+
1101
+ result.message
1102
+ ```
1103
+
1104
+ The client exposes:
1105
+
1106
+ - `query(name, input?, options?)`: only accepts operations with `kind: "query"`.
1107
+ - `mutation(name, input?, options?)`: only accepts operations with `kind: "mutation"`.
1108
+
1109
+ The client derives input and output types from the Zod schemas in
1110
+ `apiOperations`.
1111
+
1112
+ ## Request Context and Auth
1113
+
1114
+ `createRequestContext(input)` normalizes headers, cookies, and response:
1115
+
1116
+ ```ts
1117
+ const ctx = await createRequestContext({
1118
+ headers: req.headers,
1119
+ cookies,
1120
+ res: {
1121
+ setHeader: res.setHeader.bind(res),
1122
+ cookie: res.cookie.bind(res),
1123
+ },
1124
+ })
1125
+ ```
1126
+
1127
+ The resulting context includes:
1128
+
1129
+ - `req.headers`, `req.cookies`
1130
+ - `res.setHeader`, `res.cookie`
1131
+ - `user`: manager user or `null`
1132
+ - `getUser()`: returns the user or throws `AUTH_REQUIRED`
1133
+
1134
+ Session cookies are managed with `getSessionCookie` and `setSessionCookie`.
1135
+
1136
+ ## Media
1137
+
1138
+ Media uses a storage adapter:
1139
+
1140
+ ```ts
1141
+ createMediaService({
1142
+ adapter,
1143
+ defaultAccess: 'private',
1144
+ defaultGetExpiresInSeconds: 300,
1145
+ uploadUrl: '/api/rakun/manager/media/upload',
1146
+ })
1147
+ ```
1148
+
1149
+ APIs:
1150
+
1151
+ - `createMediaConnection(config)`: stores config without creating the service.
1152
+ - `createMediaService(config)`: creates the singleton with an adapter.
1153
+ - `getMediaService()`: returns the singleton or creates it from config.
1154
+ - `handleMediaBinaryUpload`: processes manager binary uploads.
1155
+
1156
+ The service supports prepare/finalize upload, URL generation, folders, and media optimization depending on adapter/configuration. Manager uploads encode original file names for transport, so Unicode names (including accents, non-Latin scripts, and emoji) are preserved without placing invalid characters in HTTP headers.
1157
+
1158
+ Optimized video uploads use the `ffmpeg-static` peer dependency and produce an MP4 primary object plus MP4 and WebM entries in `sources`. Existing image-only optimize options remain compatible; use `video: { quality: 80 }` to configure video quality explicitly.
1159
+
1160
+ Existing images and videos can be reimported from the media manager with the selected optimization settings. The replacement uses new storage keys and updates the existing `Media` record only after every requested variant has been written, preserving its ID and content references. Preview generation stores a compact `data:image/...` string on `previewUrl` for LQIP use in `@rakun-kit/react` `Image`, instead of writing a separate preview object to storage.
1161
+
1162
+ The `manager.media.replace` mutation accepts a newly uploaded image and swaps it into an existing `Media` record. It preserves the media ID, name, title, alt text, folder, status, and all content references; after the database points to the new storage keys, Rakun removes the previous primary object and generated variants and revalidates directly dependent documents when route revalidation is configured.
1163
+
1164
+ ## Persistent Event Log
1165
+
1166
+ Rakun keeps business/audit events separate from its technical console logger.
1167
+ The event log keeps events immutable and defaults to a MongoDB collection
1168
+ with indexes for time, type, category, outcome, severity, correlation and tags:
1169
+
1170
+ ```ts
1171
+ import { recordEvent, queryEvents } from '@rakun-kit/core'
1172
+
1173
+ await recordEvent({
1174
+ type: 'content.article.published',
1175
+ category: 'content',
1176
+ outcome: 'success',
1177
+ actor: { type: 'manager-user', id: userId },
1178
+ resource: { type: 'Article', id: articleId },
1179
+ correlationId: requestId,
1180
+ tags: ['editorial'],
1181
+ data: {
1182
+ locale: 'es',
1183
+ changedFields: 3,
1184
+ },
1185
+ })
1186
+
1187
+ const page = await queryEvents({
1188
+ categories: ['content'],
1189
+ outcomes: ['success'],
1190
+ from: new Date('2026-01-01T00:00:00.000Z'),
1191
+ limit: 50,
1192
+ })
1193
+ ```
1194
+
1195
+ `data` accepts nested JSON values. Event queries use cursor pagination and can
1196
+ filter by types, categories, severities, outcomes, sources, correlation,
1197
+ required tags and a date range. A custom persistence implementation can be
1198
+ plugged in globally:
1199
+
1200
+ ```ts
1201
+ import type { EventLogAdapter } from '@rakun-kit/core'
1202
+
1203
+ const adapter: EventLogAdapter = {
1204
+ async append(event) {
1205
+ return customStore.append(event)
1206
+ },
1207
+ async query(filters) {
1208
+ return customStore.query(filters)
1209
+ },
1210
+ async deleteBefore(before) {
1211
+ return customStore.deleteBefore(before)
1212
+ },
1213
+ }
1214
+
1215
+ rakunBootstrap({
1216
+ // ...
1217
+ eventLog: { adapter },
1218
+ })
1219
+ ```
1220
+
1221
+ Plugins receive the resolved `eventLog` service in their initialization
1222
+ context. Reading a shared event stream should be protected with the built-in
1223
+ `system.eventLog.read` permission. The built-in `manager.logs.list` operation and the
1224
+ manager Settings → Logs screen both enforce it.
1225
+
1226
+ Retention cleanup is available from the manager Logs screen to users with the
1227
+ built-in `system.eventLog.manage` permission. It deletes events strictly older
1228
+ than the selected cutoff. Custom adapters must implement `deleteBefore` to
1229
+ support this action. The cleanup mutation itself is logged after deletion.
1230
+
1231
+ Failed API operations are persisted automatically as `api.operation.failed`
1232
+ events. This includes expected 4xx application errors and unexpected 5xx
1233
+ failures across the core operation wrapper and the Express, Next.js, and tRPC
1234
+ adapters. Events include the operation, status, method, kind, correlation ID,
1235
+ and authenticated actor when available. Request payloads, application error
1236
+ causes, and raw internal error messages are not persisted.
1237
+
1238
+ Every successful API mutation is also persisted with an operation-specific
1239
+ event type such as `manager.create.succeeded` or
1240
+ `manager.backups.restore.succeeded`. Successful queries are not recorded by
1241
+ default, which keeps the event stream focused on state changes. Mutation
1242
+ events contain operation metadata and actor/correlation context, but never
1243
+ copy the input payload or result.
1244
+
1245
+ ## Mail
1246
+
1247
+ Mail providers receive normalized, already-rendered messages through
1248
+ `MailAdapter`, so neither `core` nor an adapter depends on React or a template
1249
+ engine:
1250
+
1251
+ ```ts
1252
+ import type { MailAdapter } from '@rakun-kit/core'
1253
+
1254
+ const adapter: MailAdapter = {
1255
+ async send(message) {
1256
+ const result = await provider.send(message)
1257
+ return { id: result.id }
1258
+ },
1259
+ }
1260
+
1261
+ rakunBootstrap({
1262
+ // ...
1263
+ mail: {
1264
+ adapter,
1265
+ defaultFrom: 'hello@example.com',
1266
+ defaultReplyTo: 'support@example.com',
1267
+ },
1268
+ })
1269
+ ```
1270
+
1271
+ Send rendered content directly:
1272
+
1273
+ ```ts
1274
+ import { sendMail } from '@rakun-kit/core'
1275
+
1276
+ await sendMail({
1277
+ to: 'ada@example.com',
1278
+ subject: 'Welcome',
1279
+ html: '<p>Hello Ada</p>',
1280
+ text: 'Hello Ada',
1281
+ })
1282
+ ```
1283
+
1284
+ Or create a typed application template registry:
1285
+
1286
+ ```ts
1287
+ import { createMailSender, defineMailTemplate } from '@rakun-kit/core'
1288
+
1289
+ const mail = createMailSender({
1290
+ templates: {
1291
+ welcome: defineMailTemplate<{ name: string }>({
1292
+ subject: ({ name }) => `Welcome, ${name}`,
1293
+ render: ({ name }) => ({
1294
+ html: `<p>Hello ${name}</p>`,
1295
+ text: `Hello ${name}`,
1296
+ }),
1297
+ }),
1298
+ },
1299
+ })
1300
+
1301
+ await mail.send({
1302
+ template: 'welcome',
1303
+ props: { name: 'Ada' },
1304
+ to: 'ada@example.com',
1305
+ })
1306
+ ```
1307
+
1308
+ The common contract supports To/CC/BCC/Reply-To, custom headers and in-memory
1309
+ `Uint8Array` attachments. Sending is immediate; queues, retries and delivery
1310
+ events belong to application infrastructure.
1311
+
1312
+ Every mail sent through a bootstrapped Rakun mail service creates append-only
1313
+ `mail.send.attempted` and `mail.send.succeeded` or `mail.send.failed` events.
1314
+ They share a correlation id and include only operational counts, provider,
1315
+ template and duration. Recipient addresses, subject, HTML/text, headers,
1316
+ attachment names/content, credentials and raw provider errors are never copied
1317
+ to the persistent event log. If the initial attempt event cannot be persisted,
1318
+ the provider is not called.
1319
+
1320
+ ## Literals and Translation
1321
+
1322
+ Bootstrap receives `literals`. Related utilities:
1323
+
1324
+ - `getTranslation`: resolves translatable values.
1325
+ - `translateObject`: translates objects with translatable fields.
1326
+ - Manager schemas for listing/upserting website literals (not manager UI chrome).
1327
+
1328
+ Translatable values use this shape:
1329
+
1330
+ ```ts
1331
+ {
1332
+ _tag: "Translatable",
1333
+ en: "Title",
1334
+ es: "Titulo"
1335
+ }
1336
+ ```
1337
+
1338
+ ## Permissions
1339
+
1340
+ `lib/Permissions` exposes:
1341
+
1342
+ - `getPermissionList`: generates a permission list.
1343
+ - `hasPermissions`: validates user/role permissions.
1344
+ - `mapPermissions`: transforms permissions.
1345
+ - `Permission`: public type.
1346
+
1347
+ Manager routes apply permissions, ownership, and auth through utilities in `api/utils`.
1348
+
1349
+ ## Errors
1350
+
1351
+ Application errors live in `lib/errors`:
1352
+
1353
+ - `AppError`
1354
+ - `throwAppError`
1355
+ - `errors`
1356
+ - `instanceofAppErrorShape`
1357
+
1358
+ Database errors live in `orm/dbService`:
1359
+
1360
+ - `DbError`
1361
+ - `DbErrorUnknown`
1362
+ - `DbErrorNotFound`
1363
+ - `DbErrorInvalidData`
1364
+ - `DbErrorConflict`
1365
+ - `DbErrorSimulatedFailure`
1366
+
1367
+ ## Runtime Flow
1368
+
1369
+ 1. The app defines content types with `ContentType` and `f`.
1370
+ 2. The app calls `rakunBootstrap(options)`.
1371
+ 3. The HTTP adapter calls `ensureRakunInitialized()` before serving Rakun routes.
1372
+ 4. `ensureRakunInitialized()` configures logger, MongoDB, the persistent event
1373
+ log, media, mail, and route syncing.
1374
+ 5. Each request creates a `RakunRequestContext`.
1375
+ 6. Manager/web operations validate input, run logic, validate output, and return typed contracts.
1376
+
1377
+ ## Tests and Development Notes
1378
+
1379
+ The package includes targeted tests next to modules (`*.test.ts`), for example ORM, translation, routes, redirects, and populated relations.
1380
+
1381
+ Package build:
1382
+
1383
+ ```sh
1384
+ bun run build --workspace @rakun-kit/core
1385
+ ```
1386
+
1387
+ The script generates ESM and CJS in `dist/`, adjusting imports and the CJS `package.json` through repo scripts.