includio-cms 0.1.4 → 0.5.1

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 (310) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/ROADMAP.md +26 -13
  3. package/dist/admin/api/accept-invite.js +1 -5
  4. package/dist/admin/api/invite.js +7 -16
  5. package/dist/admin/client/account/account-page.svelte +20 -50
  6. package/dist/admin/client/account/lang.d.ts +15 -23
  7. package/dist/admin/client/account/lang.js +51 -67
  8. package/dist/admin/client/account/preferences-section.svelte +26 -84
  9. package/dist/admin/client/account/profile-section.svelte +60 -40
  10. package/dist/admin/client/account/schema.d.ts +11 -3
  11. package/dist/admin/client/account/schema.js +25 -16
  12. package/dist/admin/client/account/security-section.svelte +139 -105
  13. package/dist/admin/client/account/sessions-section.svelte +35 -34
  14. package/dist/admin/client/admin/admin-after-login-layout-content.svelte +3 -5
  15. package/dist/admin/client/admin/admin-layout.svelte +3 -2
  16. package/dist/admin/client/admin/admin-preloader.svelte +36 -0
  17. package/dist/admin/client/admin/admin-preloader.svelte.d.ts +18 -0
  18. package/dist/admin/client/admin/dashboard-page.svelte +35 -67
  19. package/dist/admin/client/collection/a11y-score-cell.svelte +45 -0
  20. package/dist/admin/client/collection/a11y-score-cell.svelte.d.ts +6 -0
  21. package/dist/admin/client/collection/bulk-actions-bar.svelte +100 -0
  22. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +10 -0
  23. package/dist/admin/client/collection/collection-entries.svelte +277 -258
  24. package/dist/admin/client/collection/collection-view.svelte.d.ts +4 -3
  25. package/dist/admin/client/collection/collection-view.svelte.js +9 -5
  26. package/dist/admin/client/collection/collection.svelte +22 -12
  27. package/dist/admin/client/collection/data-table.svelte +50 -39
  28. package/dist/admin/client/collection/data-table.svelte.d.ts +1 -0
  29. package/dist/admin/client/collection/date-cell.svelte +7 -5
  30. package/dist/admin/client/collection/date-cell.svelte.d.ts +1 -1
  31. package/dist/admin/client/collection/empty-state.svelte +28 -0
  32. package/dist/admin/client/collection/empty-state.svelte.d.ts +9 -0
  33. package/dist/admin/client/collection/entry-link.svelte +10 -4
  34. package/dist/admin/client/collection/entry-link.svelte.d.ts +1 -0
  35. package/dist/admin/client/collection/grid-view.svelte +21 -23
  36. package/dist/admin/client/collection/grid-view.svelte.d.ts +1 -2
  37. package/dist/admin/client/collection/row-actions.svelte +69 -0
  38. package/dist/admin/client/collection/row-actions.svelte.d.ts +10 -0
  39. package/dist/admin/client/collection/status-badge.svelte +7 -8
  40. package/dist/admin/client/collection/table-pagination.svelte +122 -79
  41. package/dist/admin/client/collection/table-pagination.svelte.d.ts +1 -0
  42. package/dist/admin/client/collection/table-toolbar.svelte +108 -88
  43. package/dist/admin/client/collection/table-toolbar.svelte.d.ts +8 -9
  44. package/dist/admin/client/entry/entry-form.svelte +109 -1
  45. package/dist/admin/client/entry/entry-header.svelte +143 -37
  46. package/dist/admin/client/entry/entry-header.svelte.d.ts +8 -0
  47. package/dist/admin/client/entry/entry.svelte +272 -61
  48. package/dist/admin/client/entry/header/a11y-validator.d.ts +47 -0
  49. package/dist/admin/client/entry/header/a11y-validator.js +352 -0
  50. package/dist/admin/client/entry/header/publish-panel.svelte +531 -129
  51. package/dist/admin/client/entry/header/publish-panel.svelte.d.ts +4 -0
  52. package/dist/admin/client/entry/header/save-indicator.svelte +33 -23
  53. package/dist/admin/client/entry/header/schedule-popover.svelte +1 -1
  54. package/dist/admin/client/entry/header/status-badge.svelte +25 -118
  55. package/dist/admin/client/entry/header/version-history-sheet.svelte +322 -98
  56. package/dist/admin/client/form/form-submission/form-submission.svelte +271 -83
  57. package/dist/admin/client/form/form-submission/submission-field.svelte +12 -12
  58. package/dist/admin/client/form/form-submissions.svelte +421 -139
  59. package/dist/admin/client/form/submission-link.svelte +8 -2
  60. package/dist/admin/client/form/submission-link.svelte.d.ts +1 -0
  61. package/dist/admin/client/form/submission-status-badge.svelte +18 -4
  62. package/dist/admin/client/form/submission-status-badge.svelte.d.ts +1 -0
  63. package/dist/admin/client/login/lang.d.ts +32 -0
  64. package/dist/admin/client/login/lang.js +66 -2
  65. package/dist/admin/client/login/login-form.svelte +237 -95
  66. package/dist/admin/client/login/login-form.svelte.d.ts +2 -17
  67. package/dist/admin/client/login/login-page.svelte +34 -98
  68. package/dist/admin/client/login/reset-password-page.svelte +235 -0
  69. package/dist/admin/client/login/reset-password-page.svelte.d.ts +4 -0
  70. package/dist/admin/client/login/schema.d.ts +15 -0
  71. package/dist/admin/client/login/schema.js +21 -0
  72. package/dist/admin/client/users/accept-invite-page.svelte +166 -37
  73. package/dist/admin/client/users/create-user-dialog.svelte +15 -7
  74. package/dist/admin/client/users/delete-user-dialog.svelte +81 -16
  75. package/dist/admin/client/users/delete-user-dialog.svelte.d.ts +4 -1
  76. package/dist/admin/client/users/edit-user-dialog.svelte +3 -0
  77. package/dist/admin/client/users/invite-user-dialog.svelte +16 -3
  78. package/dist/admin/client/users/lang.d.ts +27 -0
  79. package/dist/admin/client/users/lang.js +64 -10
  80. package/dist/admin/client/users/pending-invitations.svelte +59 -23
  81. package/dist/admin/client/users/users-page.svelte +471 -72
  82. package/dist/admin/components/accessibility/accessibility-overview.svelte +2 -7
  83. package/dist/admin/components/dashboard/a11y-gauge.svelte +90 -0
  84. package/dist/admin/components/dashboard/a11y-gauge.svelte.d.ts +18 -0
  85. package/dist/admin/components/dashboard/accessibility-hub.svelte +13 -12
  86. package/dist/admin/components/dashboard/changelog-dialog.svelte +157 -0
  87. package/dist/admin/components/dashboard/changelog-dialog.svelte.d.ts +6 -0
  88. package/dist/admin/components/dashboard/form-submissions-widget.svelte +71 -113
  89. package/dist/admin/components/dashboard/index.d.ts +4 -2
  90. package/dist/admin/components/dashboard/index.js +4 -2
  91. package/dist/admin/components/dashboard/orphaned-entries-notice.svelte +240 -0
  92. package/dist/admin/components/dashboard/orphaned-entries-notice.svelte.d.ts +13 -0
  93. package/dist/admin/components/dashboard/recent-activity.svelte +53 -75
  94. package/dist/admin/components/dashboard/recent-entries.svelte +94 -0
  95. package/dist/admin/components/dashboard/recent-entries.svelte.d.ts +18 -0
  96. package/dist/admin/components/dashboard/stat-card.svelte +2 -2
  97. package/dist/admin/components/dashboard/tip-of-the-day.svelte +109 -0
  98. package/dist/admin/components/dashboard/tip-of-the-day.svelte.d.ts +3 -0
  99. package/dist/admin/components/dashboard/welcome-header.svelte +45 -0
  100. package/dist/admin/components/dashboard/welcome-header.svelte.d.ts +3 -0
  101. package/dist/admin/components/fields/{array-field.svelte → blocks-field.svelte} +4 -4
  102. package/dist/admin/components/fields/{array-field.svelte.d.ts → blocks-field.svelte.d.ts} +5 -5
  103. package/dist/admin/components/fields/content-field.svelte +27 -0
  104. package/dist/admin/components/fields/content-field.svelte.d.ts +31 -0
  105. package/dist/admin/components/fields/field-renderer.svelte +9 -7
  106. package/dist/admin/components/fields/image-field.svelte +2 -2
  107. package/dist/admin/components/fields/media-field.svelte +2 -2
  108. package/dist/admin/components/fields/seo-field.svelte +205 -25
  109. package/dist/admin/components/fields/simple-array-field.svelte +289 -0
  110. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +30 -0
  111. package/dist/admin/components/fields/slug-field.svelte +3 -2
  112. package/dist/admin/components/fields/standalone-field-renderer.svelte +148 -0
  113. package/dist/admin/components/fields/standalone-field-renderer.svelte.d.ts +9 -0
  114. package/dist/admin/components/fields/text-field-wrapper.svelte +147 -3
  115. package/dist/admin/components/fields/text-field-wrapper.svelte.d.ts +2 -2
  116. package/dist/admin/components/fields/url-field.svelte +5 -4
  117. package/dist/admin/components/layout/app-sidebar.svelte +27 -24
  118. package/dist/admin/components/layout/lang.d.ts +6 -0
  119. package/dist/admin/components/layout/lang.js +13 -1
  120. package/dist/admin/components/layout/layout-renderer.svelte +352 -0
  121. package/dist/admin/components/layout/layout-renderer.svelte.d.ts +14 -0
  122. package/dist/admin/components/layout/nav-breadcrumbs.svelte +4 -4
  123. package/dist/admin/components/layout/nav-collections.svelte +65 -36
  124. package/dist/admin/components/layout/nav-footer.svelte +38 -0
  125. package/dist/admin/components/layout/nav-footer.svelte.d.ts +3 -0
  126. package/dist/admin/components/layout/nav-forms.svelte +55 -30
  127. package/dist/admin/components/layout/nav-main.svelte +14 -52
  128. package/dist/admin/components/layout/nav-search.svelte +4 -3
  129. package/dist/admin/components/layout/nav-singletons.svelte +59 -17
  130. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +17 -8
  131. package/dist/admin/components/layout/site-header.svelte +74 -13
  132. package/dist/admin/components/media/alt-input.svelte +32 -22
  133. package/dist/admin/components/media/bulk-action-bar.svelte +139 -150
  134. package/dist/admin/components/media/file/file-details.svelte +299 -217
  135. package/dist/admin/components/media/file/file-miniature.svelte +54 -41
  136. package/dist/admin/components/media/file/file-miniature.svelte.d.ts +1 -0
  137. package/dist/admin/components/media/file/file-preview.svelte +1 -1
  138. package/dist/admin/components/media/file-upload.svelte +24 -26
  139. package/dist/admin/components/media/files-list.svelte +112 -40
  140. package/dist/admin/components/media/files-list.svelte.d.ts +2 -0
  141. package/dist/admin/components/media/focal-point-input.svelte +122 -26
  142. package/dist/admin/components/media/media-library.svelte +127 -70
  143. package/dist/admin/components/media/media-search.svelte +6 -6
  144. package/dist/admin/components/media/media-sort.svelte +3 -1
  145. package/dist/admin/components/media/multi-file-summary.svelte +88 -68
  146. package/dist/admin/components/media/tag-combobox.svelte +141 -66
  147. package/dist/admin/components/media/tag-combobox.svelte.d.ts +1 -0
  148. package/dist/admin/components/media/tag-sidebar.svelte +139 -121
  149. package/dist/admin/components/tiptap/FigureNodeView.svelte +144 -15
  150. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte +254 -0
  151. package/dist/admin/components/tiptap/InlineBlockNodeView.svelte.d.ts +4 -0
  152. package/dist/admin/components/tiptap/SlashCommandPopup.svelte +212 -0
  153. package/dist/admin/components/tiptap/SlashCommandPopup.svelte.d.ts +8 -0
  154. package/dist/admin/components/tiptap/content-editor.svelte +280 -0
  155. package/dist/admin/components/tiptap/content-editor.svelte.d.ts +9 -0
  156. package/dist/admin/components/tiptap/editor-toolbar.svelte +230 -0
  157. package/dist/admin/components/tiptap/editor-toolbar.svelte.d.ts +16 -0
  158. package/dist/admin/components/tiptap/heading-a11y-plugin.d.ts +2 -0
  159. package/dist/admin/components/tiptap/heading-a11y-plugin.js +67 -0
  160. package/dist/admin/components/tiptap/image-dialog.svelte +172 -11
  161. package/dist/admin/components/tiptap/inline-block-node.d.ts +19 -0
  162. package/dist/admin/components/tiptap/inline-block-node.js +98 -0
  163. package/dist/admin/components/tiptap/link-dialog.svelte +9 -4
  164. package/dist/admin/components/tiptap/slash-command.d.ts +17 -0
  165. package/dist/admin/components/tiptap/slash-command.js +181 -0
  166. package/dist/admin/components/tiptap/structured-content-utils.d.ts +21 -0
  167. package/dist/admin/components/tiptap/structured-content-utils.js +150 -0
  168. package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -190
  169. package/dist/admin/email/invite-template.d.ts +8 -0
  170. package/dist/admin/email/invite-template.js +99 -0
  171. package/dist/admin/email/reset-password-template.d.ts +7 -0
  172. package/dist/admin/email/reset-password-template.js +96 -0
  173. package/dist/admin/remote/ai.remote.d.ts +1 -0
  174. package/dist/admin/remote/ai.remote.js +4 -1
  175. package/dist/admin/remote/entry.remote.d.ts +9 -0
  176. package/dist/admin/remote/entry.remote.js +58 -8
  177. package/dist/admin/remote/preview.remote.js +2 -1
  178. package/dist/admin/shared/password-schema.d.ts +5 -0
  179. package/dist/admin/shared/password-schema.js +10 -0
  180. package/dist/admin/state/content-language.svelte.d.ts +3 -0
  181. package/dist/admin/state/content-language.svelte.js +8 -0
  182. package/dist/admin/styles/admin.css +1530 -151
  183. package/dist/admin/utils/formatDate.d.ts +1 -0
  184. package/dist/admin/utils/formatDate.js +8 -0
  185. package/dist/admin/utils/roleLabel.d.ts +2 -0
  186. package/dist/admin/utils/roleLabel.js +13 -0
  187. package/dist/admin/utils/translationStatus.d.ts +17 -0
  188. package/dist/admin/utils/translationStatus.js +134 -0
  189. package/dist/ai-claude/index.d.ts +2 -0
  190. package/dist/ai-claude/index.js +56 -0
  191. package/dist/cms/runtime/api.d.ts +6 -1
  192. package/dist/cms/runtime/api.js +3 -0
  193. package/dist/cms/runtime/schemas.d.ts +9 -1
  194. package/dist/cms/runtime/schemas.js +8 -0
  195. package/dist/cms/runtime/types.d.ts +82 -10
  196. package/dist/cms/runtime/types.js +4 -0
  197. package/dist/components/ui/accordion/accordion.stories.svelte +39 -0
  198. package/dist/components/ui/accordion/accordion.stories.svelte.d.ts +27 -0
  199. package/dist/components/ui/alert/alert.stories.svelte +53 -0
  200. package/dist/components/ui/alert/alert.stories.svelte.d.ts +27 -0
  201. package/dist/components/ui/alert/alert.svelte +5 -0
  202. package/dist/components/ui/alert/alert.svelte.d.ts +9 -0
  203. package/dist/components/ui/avatar/avatar.stories.svelte +16 -0
  204. package/dist/components/ui/avatar/avatar.stories.svelte.d.ts +27 -0
  205. package/dist/components/ui/badge/badge.stories.svelte +33 -0
  206. package/dist/components/ui/badge/badge.stories.svelte.d.ts +27 -0
  207. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +33 -0
  208. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte.d.ts +27 -0
  209. package/dist/components/ui/button/button.stories.svelte +43 -0
  210. package/dist/components/ui/button/button.stories.svelte.d.ts +27 -0
  211. package/dist/components/ui/button/button.svelte +1 -2
  212. package/dist/components/ui/button/button.svelte.d.ts +0 -3
  213. package/dist/components/ui/button-group/button-group-separator.svelte.d.ts +1 -1
  214. package/dist/components/ui/card/card.stories.svelte +42 -0
  215. package/dist/components/ui/card/card.stories.svelte.d.ts +27 -0
  216. package/dist/components/ui/command/command.stories.svelte +51 -0
  217. package/dist/components/ui/command/command.stories.svelte.d.ts +27 -0
  218. package/dist/components/ui/dialog/dialog.stories.svelte +29 -0
  219. package/dist/components/ui/dialog/dialog.stories.svelte.d.ts +27 -0
  220. package/dist/components/ui/field/field-label.svelte.d.ts +1 -1
  221. package/dist/components/ui/field/field.stories.svelte +21 -0
  222. package/dist/components/ui/field/field.stories.svelte.d.ts +27 -0
  223. package/dist/components/ui/input/input.stories.svelte +40 -0
  224. package/dist/components/ui/input/input.stories.svelte.d.ts +27 -0
  225. package/dist/components/ui/input/input.svelte +2 -4
  226. package/dist/components/ui/item/item-separator.svelte.d.ts +1 -1
  227. package/dist/components/ui/label/label.stories.svelte +20 -0
  228. package/dist/components/ui/label/label.stories.svelte.d.ts +27 -0
  229. package/dist/components/ui/popover/popover.stories.svelte +29 -0
  230. package/dist/components/ui/popover/popover.stories.svelte.d.ts +27 -0
  231. package/dist/components/ui/select/select-group-heading.svelte.d.ts +1 -1
  232. package/dist/components/ui/select/select.stories.svelte +23 -0
  233. package/dist/components/ui/select/select.stories.svelte.d.ts +27 -0
  234. package/dist/components/ui/separator/separator.stories.svelte +24 -0
  235. package/dist/components/ui/separator/separator.stories.svelte.d.ts +27 -0
  236. package/dist/components/ui/sheet/sheet.stories.svelte +29 -0
  237. package/dist/components/ui/sheet/sheet.stories.svelte.d.ts +27 -0
  238. package/dist/components/ui/sidebar/sidebar-group.svelte +3 -3
  239. package/dist/components/ui/sidebar/sidebar-group.svelte.d.ts +2 -2
  240. package/dist/components/ui/sidebar/sidebar-menu-button.svelte +28 -30
  241. package/dist/components/ui/sidebar/sidebar-menu-button.svelte.d.ts +7 -7
  242. package/dist/components/ui/sidebar/sidebar-separator.svelte.d.ts +1 -1
  243. package/dist/components/ui/sidebar/sidebar-trigger.svelte +4 -4
  244. package/dist/components/ui/sonner/sonner.stories.svelte +22 -0
  245. package/dist/components/ui/sonner/sonner.stories.svelte.d.ts +26 -0
  246. package/dist/components/ui/sonner/sonner.svelte +8 -2
  247. package/dist/components/ui/sonner/toast-demo.svelte +29 -0
  248. package/dist/components/ui/sonner/toast-demo.svelte.d.ts +6 -0
  249. package/dist/components/ui/textarea/textarea.stories.svelte +22 -0
  250. package/dist/components/ui/textarea/textarea.stories.svelte.d.ts +27 -0
  251. package/dist/components/ui/textarea/textarea.svelte +0 -2
  252. package/dist/components/ui/toggle/toggle.stories.svelte +22 -0
  253. package/dist/components/ui/toggle/toggle.stories.svelte.d.ts +27 -0
  254. package/dist/components/ui/toggle-group/toggle-group.stories.svelte +17 -0
  255. package/dist/components/ui/toggle-group/toggle-group.stories.svelte.d.ts +27 -0
  256. package/dist/components/ui/tooltip/tooltip.stories.svelte +26 -0
  257. package/dist/components/ui/tooltip/tooltip.stories.svelte.d.ts +27 -0
  258. package/dist/core/fields/fieldSchemaToTs.d.ts +1 -0
  259. package/dist/core/fields/fieldSchemaToTs.js +133 -1
  260. package/dist/core/fields/layoutUtils.d.ts +17 -0
  261. package/dist/core/fields/layoutUtils.js +149 -0
  262. package/dist/core/fields/structuredToHtml.d.ts +9 -0
  263. package/dist/core/fields/structuredToHtml.js +161 -0
  264. package/dist/core/server/entries/operations/create.js +2 -1
  265. package/dist/core/server/entries/operations/get.js +10 -7
  266. package/dist/core/server/entries/operations/update.d.ts +3 -0
  267. package/dist/core/server/entries/operations/update.js +30 -2
  268. package/dist/core/server/fields/queryStructuredContent.d.ts +15 -0
  269. package/dist/core/server/fields/queryStructuredContent.js +65 -0
  270. package/dist/core/server/fields/resolveImageFields.js +51 -2
  271. package/dist/core/server/fields/resolveRelationFields.js +2 -2
  272. package/dist/core/server/fields/resolveRichtextLinks.js +80 -13
  273. package/dist/core/server/fields/resolveUrlFields.js +57 -6
  274. package/dist/core/server/fields/slugResolver.d.ts +10 -0
  275. package/dist/core/server/fields/slugResolver.js +34 -0
  276. package/dist/core/server/generator/fields.js +15 -4
  277. package/dist/core/server/generator/generator.js +3 -2
  278. package/dist/db-postgres/index.js +10 -6
  279. package/dist/files-local/index.js +126 -64
  280. package/dist/server/auth.d.ts +5 -0
  281. package/dist/server/auth.js +12 -1
  282. package/dist/sveltekit/components/structured-content.svelte +204 -0
  283. package/dist/sveltekit/components/structured-content.svelte.d.ts +21 -0
  284. package/dist/sveltekit/config.d.ts +13 -3
  285. package/dist/sveltekit/index.d.ts +3 -0
  286. package/dist/sveltekit/index.js +3 -0
  287. package/dist/sveltekit/server/handle.js +1 -0
  288. package/dist/types/config.d.ts +3 -0
  289. package/dist/types/entries.d.ts +3 -0
  290. package/dist/types/fields.d.ts +19 -2
  291. package/dist/types/index.d.ts +2 -0
  292. package/dist/types/index.js +2 -0
  293. package/dist/types/layout.d.ts +54 -0
  294. package/dist/types/layout.js +6 -0
  295. package/dist/types/structured-content.d.ts +63 -0
  296. package/dist/types/structured-content.js +1 -0
  297. package/dist/updates/0.1.5/index.d.ts +2 -0
  298. package/dist/updates/0.1.5/index.js +18 -0
  299. package/dist/updates/0.2.0/index.d.ts +2 -0
  300. package/dist/updates/0.2.0/index.js +11 -0
  301. package/dist/updates/0.2.2/index.d.ts +2 -0
  302. package/dist/updates/0.2.2/index.js +13 -0
  303. package/dist/updates/0.5.0/index.d.ts +2 -0
  304. package/dist/updates/0.5.0/index.js +14 -0
  305. package/dist/updates/0.5.1/index.d.ts +2 -0
  306. package/dist/updates/0.5.1/index.js +17 -0
  307. package/dist/updates/index.js +6 -1
  308. package/package.json +16 -9
  309. package/dist/admin/components/dashboard/updates-banner.svelte +0 -170
  310. package/dist/admin/components/dashboard/updates-banner.svelte.d.ts +0 -3
@@ -1,5 +1,6 @@
1
1
  import { urlFieldDataSchema, urlFieldDataWithRelationSchema } from '../../../schemas/field/url.js';
2
2
  import { getEntries } from '../entries/operations/get.js';
3
+ import { getEntrySlugPath, getSlugFromEntryData } from './slugResolver.js';
3
4
  export async function resolveUrlFields(data, fields, language) {
4
5
  const entriesIds = [];
5
6
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -26,7 +27,7 @@ export async function resolveUrlFields(data, fields, language) {
26
27
  case 'object':
27
28
  collectIds(val.data, field.fields);
28
29
  break;
29
- case 'array':
30
+ case 'blocks':
30
31
  if (Array.isArray(val)) {
31
32
  val.forEach((item) => {
32
33
  // Find the object definition for this item based on its slug
@@ -37,14 +38,29 @@ export async function resolveUrlFields(data, fields, language) {
37
38
  });
38
39
  }
39
40
  break;
41
+ case 'array':
42
+ if (field.of === 'url' && Array.isArray(val)) {
43
+ for (const item of val) {
44
+ const parsedVal = urlFieldDataWithRelationSchema.safeParse(item);
45
+ if (parsedVal.success) {
46
+ entriesIds.push(parsedVal.data.id);
47
+ }
48
+ }
49
+ }
50
+ break;
40
51
  }
41
52
  }
42
53
  };
43
54
  collectIds(data, fields);
44
- let entriesMap = {};
55
+ const slugMap = {};
45
56
  if (entriesIds.length > 0) {
46
57
  const entries = await getEntries({ ids: entriesIds, language });
47
- entriesMap = Object.fromEntries(entries.map((m) => [m.id, m]));
58
+ for (const entry of entries) {
59
+ const slugPath = getEntrySlugPath(entry.slug);
60
+ const slug = getSlugFromEntryData(entry.data, slugPath);
61
+ if (slug)
62
+ slugMap[entry.id] = slug;
63
+ }
48
64
  }
49
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
66
  const resolveValues = (value, fields) => {
@@ -83,8 +99,7 @@ export async function resolveUrlFields(data, fields, language) {
83
99
  extras.newTab = parsed.newTab;
84
100
  }
85
101
  if (parsedValWithRelation.success) {
86
- const entryData = entriesMap[parsedValWithRelation.data.id]?.data;
87
- const slug = entryData?.seo?.slug;
102
+ const slug = slugMap[parsedValWithRelation.data.id];
88
103
  if (slug) {
89
104
  result[field.slug] = { url: slug, ...extras };
90
105
  break;
@@ -102,7 +117,7 @@ export async function resolveUrlFields(data, fields, language) {
102
117
  data: resolveValues(val.data, field.fields)
103
118
  };
104
119
  break;
105
- case 'array':
120
+ case 'blocks':
106
121
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
122
  result[field.slug] = val.map((item) => {
108
123
  // Find the object definition for this item based on its slug
@@ -117,6 +132,42 @@ export async function resolveUrlFields(data, fields, language) {
117
132
  return item;
118
133
  });
119
134
  break;
135
+ case 'array':
136
+ if (field.of === 'url' && Array.isArray(val)) {
137
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
138
+ result[field.slug] = val.map((item) => {
139
+ const parsedValWithRelation = urlFieldDataWithRelationSchema.safeParse(item);
140
+ const parsedVal = urlFieldDataSchema.safeParse(item);
141
+ const parsed = parsedValWithRelation.success
142
+ ? parsedValWithRelation.data
143
+ : parsedVal.success
144
+ ? parsedVal.data
145
+ : null;
146
+ if (!parsed)
147
+ return item;
148
+ const extras = {};
149
+ if (parsed.text) {
150
+ extras.text = language ? parsed.text[language] : parsed.text;
151
+ }
152
+ if (parsed.newTab !== undefined) {
153
+ extras.newTab = parsed.newTab;
154
+ }
155
+ if (parsedValWithRelation.success) {
156
+ const slug = slugMap[parsedValWithRelation.data.id];
157
+ if (slug) {
158
+ return { url: slug, ...extras };
159
+ }
160
+ }
161
+ const resolvedUrl = language
162
+ ? parsed.url[language]
163
+ : parsed.url;
164
+ return { url: resolvedUrl, ...extras };
165
+ });
166
+ }
167
+ else {
168
+ result[field.slug] = val;
169
+ }
170
+ break;
120
171
  default:
121
172
  result[field.slug] = val;
122
173
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Get the dot-path to the slug field for a given collection/single slug.
3
+ * Falls back to 'seo.slug' if no slugField is configured.
4
+ */
5
+ export declare function getEntrySlugPath(configSlug: string): string;
6
+ /**
7
+ * Traverse a dot-path in entry data and return the slug value.
8
+ * Handles both plain strings and localized objects { lang: string }.
9
+ */
10
+ export declare function getSlugFromEntryData(data: Record<string, unknown>, slugPath: string, language?: string): string | undefined;
@@ -0,0 +1,34 @@
1
+ import { getCMS } from '../../cms.js';
2
+ const DEFAULT_SLUG_PATH = 'seo.slug';
3
+ /**
4
+ * Get the dot-path to the slug field for a given collection/single slug.
5
+ * Falls back to 'seo.slug' if no slugField is configured.
6
+ */
7
+ export function getEntrySlugPath(configSlug) {
8
+ try {
9
+ const config = getCMS().getBySlug(configSlug);
10
+ return config.slugField || DEFAULT_SLUG_PATH;
11
+ }
12
+ catch {
13
+ return DEFAULT_SLUG_PATH;
14
+ }
15
+ }
16
+ /**
17
+ * Traverse a dot-path in entry data and return the slug value.
18
+ * Handles both plain strings and localized objects { lang: string }.
19
+ */
20
+ export function getSlugFromEntryData(data, slugPath, language) {
21
+ const parts = slugPath.split('.');
22
+ let current = data;
23
+ for (const part of parts) {
24
+ if (current == null || typeof current !== 'object')
25
+ return undefined;
26
+ current = current[part];
27
+ }
28
+ if (typeof current === 'string')
29
+ return current;
30
+ if (language && current != null && typeof current === 'object') {
31
+ return current[language];
32
+ }
33
+ return undefined;
34
+ }
@@ -6,6 +6,8 @@ function getFieldTypeAsString(field) {
6
6
  case 'date':
7
7
  case 'datetime':
8
8
  return 'string';
9
+ case 'content':
10
+ return 'StructuredContentDoc';
9
11
  case 'radio':
10
12
  return field.options.map((opt) => `'${opt.value.trim()}'`).join(' | ');
11
13
  case 'image': {
@@ -39,11 +41,11 @@ function getFieldTypeAsString(field) {
39
41
  slug: '${field.slug}';
40
42
  data: {${field.fields.map((f) => ` ${f.slug}${f.required ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
41
43
  }`;
42
- case 'array':
44
+ case 'blocks':
43
45
  return `
44
46
  (
45
47
  ${field.of
46
- .map((f) => `
48
+ .map((f) => `
47
49
  {
48
50
  slug: '${f.slug}';
49
51
  data: {${f.fields.map((f) => ` ${f.slug}${f.required ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
@@ -51,13 +53,22 @@ function getFieldTypeAsString(field) {
51
53
  `)
52
54
  .join(' | ')}
53
55
  )[]`;
56
+ case 'array':
57
+ switch (field.of) {
58
+ case 'text':
59
+ return field.localized ? 'Record<string, string>[]' : 'string[]';
60
+ case 'number':
61
+ return 'number[]';
62
+ case 'url':
63
+ return 'UrlFieldData[]';
64
+ }
54
65
  case 'slug':
55
66
  return 'string';
56
67
  case 'seo':
57
68
  return `{
58
- slug?: string;
69
+ slug: string;
59
70
  canonicalUrl?: string;
60
- title?: string;
71
+ title: string;
61
72
  description?: string;
62
73
  ogImage?: string;
63
74
  keywords?: string;
@@ -4,6 +4,7 @@ import { generateTsTypeFromFields } from './fields.js';
4
4
  import { generateTsTypeFromFormFields } from './formFields.js';
5
5
  import { generateZodSchemaStringFromFormFieldsAsString } from './formFieldSchemaToString.js';
6
6
  import { toPascalCase } from './utils.js';
7
+ import { getFieldsFromConfig } from '../../fields/layoutUtils.js';
7
8
  function createCmsRuntimeDir() {
8
9
  const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
9
10
  mkdirSync(cmsDir, { recursive: true });
@@ -19,7 +20,7 @@ function generateTypesStringForRecords(type, records) {
19
20
  export interface ${toPascalCase(single.slug)} {
20
21
  id: string;
21
22
  slug: string;
22
- data: ${generateTsTypeFromFields(single.fields)}
23
+ data: ${generateTsTypeFromFields(getFieldsFromConfig(single))}
23
24
  publishedAt: Date | null;
24
25
  };
25
26
  `;
@@ -68,7 +69,7 @@ function generateTypes(config) {
68
69
  const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
69
70
  const filePath = join(cmsDir, 'types.ts');
70
71
  let code = `// This file is auto-generated. Do not edit directly.\n\n`;
71
- code += `import type { MediaFile, ImageFieldData, VideoFieldData } from 'includio-cms/types';\n\n`;
72
+ code += `import type { MediaFile, ImageFieldData, VideoFieldData, StructuredContentDoc } from 'includio-cms/types';\n\n`;
72
73
  if (config.singles && config.singles.length > 0) {
73
74
  code += generateTypesStringForRecords('single', Object.values(config.singles));
74
75
  }
@@ -101,9 +101,11 @@ export function pg(config) {
101
101
  options.type ? eq(schema.entriesTable.type, options.type) : undefined,
102
102
  options.slug ? eq(schema.entriesTable.slug, options.slug) : undefined,
103
103
  options.ids ? inArray(schema.entriesTable.id, options.ids) : undefined,
104
- options.onlyArchived
105
- ? sql `${schema.entriesTable.archivedAt} IS NOT NULL`
106
- : isNull(schema.entriesTable.archivedAt)
104
+ options.includeArchived
105
+ ? undefined
106
+ : options.onlyArchived
107
+ ? sql `${schema.entriesTable.archivedAt} IS NOT NULL`
108
+ : isNull(schema.entriesTable.archivedAt)
107
109
  ].filter(Boolean);
108
110
  const allConditions = [...baseConditions];
109
111
  if (allConditions.length > 0) {
@@ -127,9 +129,11 @@ export function pg(config) {
127
129
  options.type ? eq(schema.entriesTable.type, options.type) : undefined,
128
130
  options.slug ? eq(schema.entriesTable.slug, options.slug) : undefined,
129
131
  options.ids ? inArray(schema.entriesTable.id, options.ids) : undefined,
130
- options.onlyArchived
131
- ? sql `${schema.entriesTable.archivedAt} IS NOT NULL`
132
- : isNull(schema.entriesTable.archivedAt)
132
+ options.includeArchived
133
+ ? undefined
134
+ : options.onlyArchived
135
+ ? sql `${schema.entriesTable.archivedAt} IS NOT NULL`
136
+ : isNull(schema.entriesTable.archivedAt)
133
137
  ].filter(Boolean);
134
138
  const [result] = await db
135
139
  .select({ count: count() })
@@ -15,76 +15,138 @@ const ffmpegPath = process.env.FFMPEG_PATH || '/usr/bin/ffmpeg';
15
15
  const ffprobePath = process.env.FFPROBE_PATH || '/usr/bin/ffprobe';
16
16
  ffmpeg.setFfmpegPath(ffmpegPath);
17
17
  ffmpeg.setFfprobePath(ffprobePath);
18
- // Funkcja pomocnicza do przetwarzania wideo
19
- async function processVideo(filepath, filename) {
20
- return new Promise((resolve) => {
21
- const thumbnailFilename = `${filename}_thumb.jpg`;
22
- const thumbnailUrl = `/uploads/${thumbnailFilename}`;
23
- const posterFilename = `${filename}_poster.jpg`;
24
- const posterUrl = `/uploads/${posterFilename}`;
25
- ffmpeg(filepath)
26
- .on('error', () => {
18
+ function withTimeout(promise, ms, label) {
19
+ return new Promise((resolve, reject) => {
20
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
21
+ promise.then((val) => {
22
+ clearTimeout(timer);
23
+ resolve(val);
24
+ }, (err) => {
25
+ clearTimeout(timer);
26
+ reject(err);
27
+ });
28
+ });
29
+ }
30
+ function probeVideo(filepath) {
31
+ return new Promise((resolve, reject) => {
32
+ ffmpeg.ffprobe(filepath, (err, metadata) => {
33
+ if (err) {
34
+ reject(new Error(`ffprobe failed: ${err.message || err}`));
35
+ return;
36
+ }
37
+ const videoStream = metadata.streams.find((s) => s.codec_type === 'video');
38
+ const duration = metadata.format.duration ?? null;
27
39
  resolve({
28
- width: null,
29
- height: null,
30
- duration: null,
31
- thumbnailUrl: null,
32
- posterUrl: null
33
- });
34
- })
35
- .on('end', () => {
36
- // Po wygenerowaniu thumbnaiła, generujemy poster (wyższa jakość)
37
- ffmpeg(filepath)
38
- .on('error', () => {
39
- // Poster failed, continue with thumbnail only
40
- ffmpeg.ffprobe(filepath, (err, metadata) => {
41
- if (err) {
42
- resolve({ width: null, height: null, duration: null, thumbnailUrl, posterUrl: null });
43
- return;
44
- }
45
- const videoStream = metadata.streams.find((s) => s.codec_type === 'video');
46
- const duration = metadata.format.duration || null;
47
- resolve({
48
- width: videoStream?.width || null,
49
- height: videoStream?.height || null,
50
- duration: duration ? Math.round(duration) : null,
51
- thumbnailUrl,
52
- posterUrl: null
53
- });
54
- });
55
- })
56
- .on('end', () => {
57
- ffmpeg.ffprobe(filepath, (err, metadata) => {
58
- if (err) {
59
- resolve({ width: null, height: null, duration: null, thumbnailUrl, posterUrl });
60
- return;
61
- }
62
- const videoStream = metadata.streams.find((s) => s.codec_type === 'video');
63
- const duration = metadata.format.duration || null;
64
- resolve({
65
- width: videoStream?.width || null,
66
- height: videoStream?.height || null,
67
- duration: duration ? Math.round(duration) : null,
68
- thumbnailUrl,
69
- posterUrl
70
- });
71
- });
72
- })
73
- .screenshots({
74
- timestamps: ['00:00:01'],
75
- filename: posterFilename,
76
- folder: fullDir,
77
- size: '1280x?' // Poster: szerszy, proporcjonalny
40
+ width: videoStream?.width ?? null,
41
+ height: videoStream?.height ?? null,
42
+ duration: duration ? Math.round(duration * 100) / 100 : null
78
43
  });
79
- })
44
+ });
45
+ });
46
+ }
47
+ function takeScreenshot(filepath, config) {
48
+ return new Promise((resolve, reject) => {
49
+ ffmpeg(filepath)
50
+ .on('error', (err) => reject(new Error(`Screenshot failed: ${err.message || err}`)))
51
+ .on('end', () => resolve())
80
52
  .screenshots({
81
- timestamps: ['00:00:01'],
82
- filename: thumbnailFilename,
83
- folder: fullDir,
84
- size: '320x240'
53
+ timestamps: [config.timestamp],
54
+ filename: config.filename,
55
+ folder: config.folder,
56
+ size: config.size
57
+ });
58
+ });
59
+ }
60
+ function getSafeTimestamp(duration) {
61
+ if (duration === null || duration <= 0)
62
+ return '00:00:00.000';
63
+ if (duration < 1)
64
+ return '00:00:00.000';
65
+ if (duration < 3) {
66
+ const t = duration * 0.1;
67
+ return formatTimestamp(t);
68
+ }
69
+ return formatTimestamp(Math.min(1, duration * 0.1));
70
+ }
71
+ function formatTimestamp(seconds) {
72
+ const h = Math.floor(seconds / 3600);
73
+ const m = Math.floor((seconds % 3600) / 60);
74
+ const s = seconds % 60;
75
+ return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${s.toFixed(3).padStart(6, '0')}`;
76
+ }
77
+ let ffmpegAvailable = null;
78
+ async function checkFfmpegAvailable() {
79
+ if (ffmpegAvailable !== null)
80
+ return ffmpegAvailable;
81
+ return new Promise((resolve) => {
82
+ ffmpeg.ffprobe('/dev/null', (err) => {
83
+ if (err && (String(err).includes('ENOENT') || String(err).includes('spawn'))) {
84
+ console.warn('[video] ffmpeg/ffprobe not found — video thumbnails disabled');
85
+ ffmpegAvailable = false;
86
+ }
87
+ else {
88
+ ffmpegAvailable = true;
89
+ }
90
+ resolve(ffmpegAvailable);
85
91
  });
86
92
  });
87
93
  }
94
+ async function processVideo(filepath, filename) {
95
+ if (!(await checkFfmpegAvailable())) {
96
+ console.warn(`[video] Skipping processing for ${filename}: ffmpeg not available`);
97
+ return { width: null, height: null, duration: null, thumbnailUrl: null, posterUrl: null };
98
+ }
99
+ const thumbnailFilename = `${filename}_thumb.jpg`;
100
+ const thumbnailUrl = `/uploads/${thumbnailFilename}`;
101
+ const posterFilename = `${filename}_poster.jpg`;
102
+ const posterUrl = `/uploads/${posterFilename}`;
103
+ // 1. Probe — get metadata first
104
+ let probe;
105
+ try {
106
+ probe = await withTimeout(probeVideo(filepath), 10_000, 'ffprobe');
107
+ }
108
+ catch (err) {
109
+ console.warn(`[video] ffprobe failed for ${filename}:`, err);
110
+ return { width: null, height: null, duration: null, thumbnailUrl: null, posterUrl: null };
111
+ }
112
+ const timestamp = getSafeTimestamp(probe.duration);
113
+ // 2. Thumbnail (320px wide, maintain aspect ratio)
114
+ let finalThumbnailUrl = null;
115
+ try {
116
+ await withTimeout(takeScreenshot(filepath, {
117
+ timestamp,
118
+ filename: thumbnailFilename,
119
+ folder: fullDir,
120
+ size: '320x?'
121
+ }), 15_000, 'thumbnail');
122
+ finalThumbnailUrl = thumbnailUrl;
123
+ }
124
+ catch (err) {
125
+ console.warn(`[video] Thumbnail failed for ${filename}:`, err);
126
+ }
127
+ // 3. Poster (up to 1280px wide, no upscale)
128
+ let finalPosterUrl = null;
129
+ const posterWidth = Math.min(1280, probe.width ?? 1280);
130
+ try {
131
+ await withTimeout(takeScreenshot(filepath, {
132
+ timestamp,
133
+ filename: posterFilename,
134
+ folder: fullDir,
135
+ size: `${posterWidth}x?`
136
+ }), 15_000, 'poster');
137
+ finalPosterUrl = posterUrl;
138
+ }
139
+ catch (err) {
140
+ console.warn(`[video] Poster failed for ${filename}:`, err);
141
+ }
142
+ return {
143
+ width: probe.width,
144
+ height: probe.height,
145
+ duration: probe.duration ? Math.round(probe.duration) : null,
146
+ thumbnailUrl: finalThumbnailUrl,
147
+ posterUrl: finalPosterUrl
148
+ };
149
+ }
88
150
  async function ensureDir(dir) {
89
151
  try {
90
152
  await mkdir(dir, { recursive: true });
@@ -2,6 +2,11 @@ export declare const auth: import("better-auth", { with: { "resolution-mode": "r
2
2
  database: (options: import("better-auth", { with: { "resolution-mode": "require" } }).BetterAuthOptions) => import("better-auth/adapters/drizzle", { with: { "resolution-mode": "require" } }).DBAdapter<import("better-auth", { with: { "resolution-mode": "require" } }).BetterAuthOptions>;
3
3
  emailAndPassword: {
4
4
  enabled: true;
5
+ sendResetPassword({ user, url }: {
6
+ user: import("better-auth", { with: { "resolution-mode": "require" } }).User;
7
+ url: string;
8
+ token: string;
9
+ }, request: Request | undefined): Promise<void>;
5
10
  };
6
11
  plugins: [{
7
12
  id: "admin";
@@ -2,12 +2,23 @@ import { betterAuth } from 'better-auth';
2
2
  import { drizzleAdapter } from 'better-auth/adapters/drizzle';
3
3
  import { db } from './db/index.js';
4
4
  import { admin } from 'better-auth/plugins';
5
+ import { getCMS } from '../core/cms.js';
6
+ import { resetPasswordEmailTemplate } from '../admin/email/reset-password-template.js';
5
7
  export const auth = betterAuth({
6
8
  database: drizzleAdapter(db, {
7
9
  provider: 'pg'
8
10
  }),
9
11
  emailAndPassword: {
10
- enabled: true
12
+ enabled: true,
13
+ async sendResetPassword({ user, url }, request) {
14
+ const emailAdapter = getCMS().emailAdapter;
15
+ if (!emailAdapter) {
16
+ throw new Error('Email adapter not configured');
17
+ }
18
+ const lang = request?.headers.get('Accept-Language')?.startsWith('pl') ? 'pl' : 'en';
19
+ const { subject, html } = resetPasswordEmailTemplate({ resetUrl: url, lang });
20
+ await emailAdapter.sendMail({ to: [user.email], subject, html });
21
+ }
11
22
  },
12
23
  plugins: [admin()]
13
24
  });