includio-cms 0.25.0 → 0.27.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 (411) hide show
  1. package/API.md +97 -4
  2. package/CHANGELOG.md +118 -0
  3. package/DOCS.md +1 -1
  4. package/README.md +2 -0
  5. package/ROADMAP.md +14 -0
  6. package/dist/admin/auth-client.d.ts +42 -42
  7. package/dist/admin/client/account/lang.d.ts +1 -0
  8. package/dist/admin/client/account/lang.js +4 -2
  9. package/dist/admin/client/account/profile-section.svelte +2 -2
  10. package/dist/admin/client/account/security-section.svelte +27 -4
  11. package/dist/admin/client/account/sessions-section.svelte +1 -1
  12. package/dist/admin/client/admin/admin-after-login-layout-content.svelte +1 -1
  13. package/dist/admin/client/admin/admin-layout.svelte +12 -2
  14. package/dist/admin/client/admin/admin-layout.svelte.d.ts +2 -1
  15. package/dist/admin/client/admin/dashboard-page.svelte +34 -10
  16. package/dist/admin/client/collection/bulk-actions-bar.svelte +86 -44
  17. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +3 -1
  18. package/dist/admin/client/collection/collection-entries.svelte +52 -36
  19. package/dist/admin/client/collection/collection-entries.svelte.d.ts +3 -0
  20. package/dist/admin/client/collection/collection.svelte +28 -14
  21. package/dist/admin/client/collection/collection.svelte.d.ts +3 -0
  22. package/dist/admin/client/collection/data-table.svelte +240 -130
  23. package/dist/admin/client/collection/data-table.svelte.d.ts +9 -0
  24. package/dist/admin/client/collection/date-cell.svelte +4 -4
  25. package/dist/admin/client/collection/row-actions.svelte +2 -1
  26. package/dist/admin/client/collection/sortable-header.svelte +33 -9
  27. package/dist/admin/client/collection/state-display.svelte +102 -0
  28. package/dist/admin/client/collection/state-display.svelte.d.ts +12 -0
  29. package/dist/admin/client/collection/status-badge.svelte +99 -11
  30. package/dist/admin/client/collection/status-badge.svelte.d.ts +15 -1
  31. package/dist/admin/client/collection/table-pagination.svelte +21 -6
  32. package/dist/admin/client/collection/table-toolbar.svelte +105 -80
  33. package/dist/admin/client/collection/table-toolbar.svelte.d.ts +11 -8
  34. package/dist/admin/client/entry/entry-form.svelte +36 -11
  35. package/dist/admin/client/entry/entry-form.svelte.d.ts +1 -0
  36. package/dist/admin/client/entry/entry-header.svelte +22 -15
  37. package/dist/admin/client/entry/entry-header.svelte.d.ts +1 -0
  38. package/dist/admin/client/entry/entry.svelte +269 -165
  39. package/dist/admin/client/entry/header/a11y-header-badge.svelte +47 -0
  40. package/dist/admin/client/entry/header/a11y-header-badge.svelte.d.ts +8 -0
  41. package/dist/admin/client/entry/header/publish-panel.svelte +69 -13
  42. package/dist/admin/client/entry/header/save-indicator.svelte +57 -28
  43. package/dist/admin/client/entry/header/save-indicator.svelte.d.ts +1 -0
  44. package/dist/admin/client/entry/header/status-badge.svelte +60 -15
  45. package/dist/admin/client/entry/header/status-badge.svelte.d.ts +1 -2
  46. package/dist/admin/client/entry/header/version-history-sheet.svelte +1 -1
  47. package/dist/admin/client/entry/hybrid/hybrid-layout.svelte +74 -23
  48. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +1 -1
  49. package/dist/admin/client/entry/utils.d.ts +14 -0
  50. package/dist/admin/client/entry/utils.js +28 -0
  51. package/dist/admin/client/form/form-submission/form-submission.svelte +2 -2
  52. package/dist/admin/client/form/form-submissions.svelte +143 -194
  53. package/dist/admin/client/form/form-submissions.svelte.d.ts +2 -0
  54. package/dist/admin/client/login/lang.d.ts +3 -0
  55. package/dist/admin/client/login/lang.js +10 -4
  56. package/dist/admin/client/login/login-form.svelte +8 -1
  57. package/dist/admin/client/login/reset-password-page.svelte +24 -3
  58. package/dist/admin/client/login/schema.d.ts +14 -2
  59. package/dist/admin/client/login/schema.js +19 -8
  60. package/dist/admin/client/maintenance/maintenance-page.svelte +16 -17
  61. package/dist/admin/client/media/media-page.svelte +1 -1
  62. package/dist/admin/client/shop/coupon-edit-page.svelte +117 -13
  63. package/dist/admin/client/shop/coupon-form.svelte +282 -138
  64. package/dist/admin/client/shop/coupon-form.svelte.d.ts +1 -9
  65. package/dist/admin/client/shop/coupon-new-page.svelte +40 -10
  66. package/dist/admin/client/shop/coupon-new-page.svelte.d.ts +2 -17
  67. package/dist/admin/client/shop/coupon-schema.d.ts +28 -0
  68. package/dist/admin/client/shop/coupon-schema.js +53 -0
  69. package/dist/admin/client/shop/coupons-list-page.svelte +262 -118
  70. package/dist/admin/client/shop/coupons-list-page.svelte.d.ts +16 -1
  71. package/dist/admin/client/shop/refund-dialog.svelte +37 -1
  72. package/dist/admin/client/shop/refund-dialog.svelte.d.ts +3 -0
  73. package/dist/admin/client/shop/shipping-method-edit-page.svelte +108 -59
  74. package/dist/admin/client/shop/shipping-method-form.svelte +36 -9
  75. package/dist/admin/client/shop/shipping-method-new-page.svelte +44 -13
  76. package/dist/admin/client/shop/shipping-methods-list-page.svelte +101 -59
  77. package/dist/admin/client/shop/shop-order-detail-page.svelte +220 -84
  78. package/dist/admin/client/shop/shop-orders-list-page.svelte +302 -152
  79. package/dist/admin/client/shop/shop-orders-list-page.svelte.d.ts +18 -1
  80. package/dist/admin/client/shop/shop-products-list-page.svelte +355 -118
  81. package/dist/admin/client/shop/shop-products-list-page.svelte.d.ts +19 -1
  82. package/dist/admin/client/users/accept-invite-page.svelte +24 -3
  83. package/dist/admin/client/users/create-user-dialog.svelte +3 -8
  84. package/dist/admin/client/users/lang.d.ts +2 -0
  85. package/dist/admin/client/users/lang.js +4 -0
  86. package/dist/admin/client/users/pending-invitations.svelte +2 -9
  87. package/dist/admin/client/users/user-name-cell.svelte +20 -0
  88. package/dist/admin/client/users/user-name-cell.svelte.d.ts +9 -0
  89. package/dist/admin/client/users/user-role-badge.svelte +16 -0
  90. package/dist/admin/client/users/user-role-badge.svelte.d.ts +7 -0
  91. package/dist/admin/client/users/user-row-actions.svelte +72 -0
  92. package/dist/admin/client/users/user-row-actions.svelte.d.ts +20 -0
  93. package/dist/admin/client/users/user-sessions-sheet.svelte +2 -11
  94. package/dist/admin/client/users/users-page.svelte +283 -497
  95. package/dist/admin/client/users/users-page.svelte.d.ts +12 -1
  96. package/dist/admin/components/dashboard/form-submissions-widget.svelte +59 -74
  97. package/dist/admin/components/dashboard/recent-activity.svelte +17 -5
  98. package/dist/admin/components/dashboard/recent-entries.svelte +19 -7
  99. package/dist/admin/components/dialogs/confirmation-dialog.svelte +105 -0
  100. package/dist/admin/components/dialogs/confirmation-dialog.svelte.d.ts +13 -0
  101. package/dist/admin/components/fields/block-picker-modal.svelte +6 -0
  102. package/dist/admin/components/fields/blocks-field.svelte +46 -1
  103. package/dist/admin/components/fields/boolean-field.svelte +1 -1
  104. package/dist/admin/components/fields/field-renderer.svelte +29 -22
  105. package/dist/admin/components/fields/file-field.svelte +344 -30
  106. package/dist/admin/components/fields/icon-field.svelte +86 -0
  107. package/dist/admin/components/fields/icon-field.svelte.d.ts +8 -0
  108. package/dist/admin/components/fields/icon-picker-dialog.svelte +174 -0
  109. package/dist/admin/components/fields/icon-picker-dialog.svelte.d.ts +11 -0
  110. package/dist/admin/components/fields/media-field.svelte +16 -2
  111. package/dist/admin/components/fields/object-field.svelte +27 -7
  112. package/dist/admin/components/fields/radio-field.svelte +22 -0
  113. package/dist/admin/components/fields/relation-field.svelte +123 -97
  114. package/dist/admin/components/fields/relation-picker-dialog.svelte +2 -2
  115. package/dist/admin/components/fields/seo-field.svelte +60 -30
  116. package/dist/admin/components/fields/shop-field.svelte +219 -24
  117. package/dist/admin/components/fields/simple-array-field.svelte +321 -151
  118. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +3 -0
  119. package/dist/admin/components/fields/slug-field.svelte +146 -21
  120. package/dist/admin/components/fields/text-field-wrapper.svelte +37 -20
  121. package/dist/admin/components/fields/text-field.svelte +7 -2
  122. package/dist/admin/components/fields/url-field-wrapper.svelte +10 -0
  123. package/dist/admin/components/fields/url-field.svelte +36 -23
  124. package/dist/admin/components/forms/form-error-summary.svelte +143 -0
  125. package/dist/admin/components/forms/form-error-summary.svelte.d.ts +27 -0
  126. package/dist/admin/components/layout/app-sidebar.svelte +7 -2
  127. package/dist/admin/components/layout/detail-page-shell.svelte +71 -0
  128. package/dist/admin/components/layout/detail-page-shell.svelte.d.ts +24 -0
  129. package/dist/admin/components/layout/lang.d.ts +5 -0
  130. package/dist/admin/components/layout/lang.js +10 -0
  131. package/dist/admin/components/layout/layout-renderer.svelte +71 -2
  132. package/dist/admin/components/layout/layout-renderer.svelte.d.ts +1 -0
  133. package/dist/admin/components/layout/layout-tabs.svelte +173 -0
  134. package/dist/admin/components/layout/layout-tabs.svelte.d.ts +24 -0
  135. package/dist/admin/components/layout/nav-breadcrumbs.svelte +25 -7
  136. package/dist/admin/components/layout/nav-collections.svelte +23 -36
  137. package/dist/admin/components/layout/nav-forms.svelte +19 -35
  138. package/dist/admin/components/layout/nav-main.svelte +3 -28
  139. package/dist/admin/components/layout/nav-search.svelte +70 -2
  140. package/dist/admin/components/layout/nav-section.svelte +77 -0
  141. package/dist/admin/components/layout/nav-section.svelte.d.ts +22 -0
  142. package/dist/admin/components/layout/nav-shop.svelte +3 -27
  143. package/dist/admin/components/layout/nav-singletons.svelte +16 -28
  144. package/dist/admin/components/layout/page-header.stories.svelte +93 -0
  145. package/dist/admin/components/layout/page-header.stories.svelte.d.ts +27 -0
  146. package/dist/admin/components/layout/page-header.svelte +68 -0
  147. package/dist/admin/components/layout/page-header.svelte.d.ts +17 -0
  148. package/dist/admin/components/layout/site-header.svelte +9 -0
  149. package/dist/admin/components/layout/site-header.svelte.d.ts +2 -17
  150. package/dist/admin/components/media/file/file-name-input.svelte +6 -2
  151. package/dist/admin/components/media/file/file-preview.svelte +130 -17
  152. package/dist/admin/components/media/file-upload.svelte +16 -7
  153. package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
  154. package/dist/admin/components/media/files-list.svelte +153 -53
  155. package/dist/admin/components/media/files-list.svelte.d.ts +1 -0
  156. package/dist/admin/components/media/media-library.svelte +577 -198
  157. package/dist/admin/components/media/media-library.svelte.d.ts +4 -0
  158. package/dist/admin/components/media/media-selector.svelte +4 -2
  159. package/dist/admin/components/media/media-selector.svelte.d.ts +1 -0
  160. package/dist/admin/components/media/tag-sidebar.svelte +4 -4
  161. package/dist/admin/components/tiptap/FigureNodeView.svelte +10 -0
  162. package/dist/admin/components/tiptap/bubble-menu.svelte +104 -0
  163. package/dist/admin/components/tiptap/bubble-menu.svelte.d.ts +19 -0
  164. package/dist/admin/components/tiptap/content-editor.svelte +28 -24
  165. package/dist/admin/components/tiptap/editor-toolbar.svelte +7 -7
  166. package/dist/admin/components/tiptap/extensions.js +5 -1
  167. package/dist/admin/components/tiptap/image-dialog.svelte +5 -1
  168. package/dist/admin/components/tiptap/link-dialog.svelte +2 -0
  169. package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -20
  170. package/dist/admin/components/tiptap/video-dialog.svelte +1 -1
  171. package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte +109 -0
  172. package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte.d.ts +9 -0
  173. package/dist/admin/helpers/build-icon-set-map.d.ts +8 -0
  174. package/dist/admin/helpers/build-icon-set-map.js +16 -0
  175. package/dist/admin/helpers/index.d.ts +2 -0
  176. package/dist/admin/helpers/index.js +2 -0
  177. package/dist/admin/i18n/errors.d.ts +140 -0
  178. package/dist/admin/i18n/errors.js +151 -0
  179. package/dist/admin/remote/entry.remote.d.ts +59 -4
  180. package/dist/admin/remote/entry.remote.js +239 -62
  181. package/dist/admin/remote/shop.remote.d.ts +87 -48
  182. package/dist/admin/remote/shop.remote.js +70 -8
  183. package/dist/admin/shared/password-generate.d.ts +6 -0
  184. package/dist/admin/shared/password-generate.js +40 -0
  185. package/dist/admin/shared/password-schema.d.ts +6 -0
  186. package/dist/admin/shared/password-schema.js +10 -3
  187. package/dist/admin/state/icon-sets.svelte.d.ts +9 -0
  188. package/dist/admin/state/icon-sets.svelte.js +20 -0
  189. package/dist/admin/styles/admin.css +23 -6
  190. package/dist/admin/styles/tokens.md +244 -0
  191. package/dist/admin/utils/accordionActivation.d.ts +13 -0
  192. package/dist/admin/utils/accordionActivation.js +35 -0
  193. package/dist/admin/utils/entryLabel.d.ts +23 -0
  194. package/dist/admin/utils/entryLabel.js +51 -12
  195. package/dist/admin/utils/field-a11y.d.ts +29 -0
  196. package/dist/admin/utils/field-a11y.js +23 -0
  197. package/dist/admin/utils/fieldPathElement.d.ts +9 -0
  198. package/dist/admin/utils/fieldPathElement.js +18 -0
  199. package/dist/admin/utils/fileDisplay.d.ts +10 -0
  200. package/dist/admin/utils/fileDisplay.js +26 -0
  201. package/dist/admin/utils/flattenFormErrors.d.ts +19 -0
  202. package/dist/admin/utils/flattenFormErrors.js +102 -0
  203. package/dist/admin/utils/formatters.d.ts +12 -0
  204. package/dist/admin/utils/{formatDate.js → formatters.js} +23 -2
  205. package/dist/admin/utils/scrollWithin.d.ts +9 -0
  206. package/dist/admin/utils/scrollWithin.js +32 -0
  207. package/dist/admin/utils/tabActivation.d.ts +12 -0
  208. package/dist/admin/utils/tabActivation.js +24 -0
  209. package/dist/cli/scaffold/admin.js +2 -2
  210. package/dist/cms/runtime/schema.d.ts +1 -0
  211. package/dist/cms/runtime/schema.js +1 -0
  212. package/dist/cms/runtime/types.d.ts +80 -7
  213. package/dist/components/ui/accordion/accordion-content.svelte +17 -3
  214. package/dist/components/ui/accordion/accordion.stories.svelte +21 -1
  215. package/dist/components/ui/alert/alert.stories.svelte +14 -0
  216. package/dist/components/ui/alert-dialog/alert-dialog.stories.svelte +45 -0
  217. package/dist/components/ui/alert-dialog/alert-dialog.stories.svelte.d.ts +27 -0
  218. package/dist/components/ui/avatar/avatar.stories.svelte +27 -0
  219. package/dist/components/ui/badge/badge.stories.svelte +15 -0
  220. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +47 -0
  221. package/dist/components/ui/breadcrumb/breadcrumb.svelte +1 -1
  222. package/dist/components/ui/button/button.stories.svelte +53 -6
  223. package/dist/components/ui/button/button.svelte +39 -5
  224. package/dist/components/ui/button/button.svelte.d.ts +4 -0
  225. package/dist/components/ui/button-group/button-group.stories.svelte +44 -0
  226. package/dist/components/ui/button-group/button-group.stories.svelte.d.ts +27 -0
  227. package/dist/components/ui/calendar/calendar.stories.svelte +36 -0
  228. package/dist/components/ui/calendar/calendar.stories.svelte.d.ts +27 -0
  229. package/dist/components/ui/card/card.stories.svelte +7 -0
  230. package/dist/components/ui/carousel/carousel.stories.svelte +43 -0
  231. package/dist/components/ui/carousel/carousel.stories.svelte.d.ts +27 -0
  232. package/dist/components/ui/checkbox/checkbox.stories.svelte +67 -0
  233. package/dist/components/ui/checkbox/checkbox.stories.svelte.d.ts +27 -0
  234. package/dist/components/ui/checkbox/checkbox.svelte +1 -1
  235. package/dist/components/ui/command/command.stories.svelte +18 -0
  236. package/dist/components/ui/data-table/data-table.stories.svelte +61 -0
  237. package/dist/components/ui/data-table/data-table.stories.svelte.d.ts +18 -0
  238. package/dist/components/ui/dialog/dialog-content.svelte +5 -0
  239. package/dist/components/ui/dialog/dialog-content.svelte.d.ts +2 -0
  240. package/dist/components/ui/dialog/dialog.stories.svelte +35 -0
  241. package/dist/components/ui/dropdown-menu/dropdown-menu.stories.svelte +74 -0
  242. package/dist/components/ui/dropdown-menu/dropdown-menu.stories.svelte.d.ts +27 -0
  243. package/dist/components/ui/field/field-context.svelte.d.ts +22 -0
  244. package/dist/components/ui/field/field-context.svelte.js +9 -0
  245. package/dist/components/ui/field/field-control.svelte +18 -0
  246. package/dist/components/ui/field/field-control.svelte.d.ts +8 -0
  247. package/dist/components/ui/field/field-description.svelte +12 -0
  248. package/dist/components/ui/field/field-error.svelte +14 -6
  249. package/dist/components/ui/field/field-label.svelte +10 -0
  250. package/dist/components/ui/field/field.stories.svelte +95 -9
  251. package/dist/components/ui/field/field.svelte +57 -0
  252. package/dist/components/ui/field/field.svelte.d.ts +2 -0
  253. package/dist/components/ui/field/index.d.ts +3 -1
  254. package/dist/components/ui/field/index.js +4 -2
  255. package/dist/components/ui/form/form-field-errors.svelte +1 -1
  256. package/dist/components/ui/form/form.stories.svelte +25 -0
  257. package/dist/components/ui/form/form.stories.svelte.d.ts +26 -0
  258. package/dist/components/ui/input/input.stories.svelte +26 -0
  259. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  260. package/dist/components/ui/input-group/input-group.stories.svelte +43 -0
  261. package/dist/components/ui/input-group/input-group.stories.svelte.d.ts +27 -0
  262. package/dist/components/ui/item/item.stories.svelte +61 -0
  263. package/dist/components/ui/item/item.stories.svelte.d.ts +27 -0
  264. package/dist/components/ui/label/label.stories.svelte +7 -0
  265. package/dist/components/ui/live-region/index.d.ts +1 -0
  266. package/dist/components/ui/live-region/index.js +1 -0
  267. package/dist/components/ui/live-region/live-region-demo.svelte +32 -0
  268. package/dist/components/ui/live-region/live-region-demo.svelte.d.ts +7 -0
  269. package/dist/components/ui/live-region/live-region.stories.svelte +23 -0
  270. package/dist/components/ui/live-region/live-region.stories.svelte.d.ts +26 -0
  271. package/dist/components/ui/live-region/live-region.svelte +12 -0
  272. package/dist/components/ui/live-region/live-region.svelte.d.ts +8 -0
  273. package/dist/components/ui/popover/popover.stories.svelte +34 -0
  274. package/dist/components/ui/radio-group/radio-group.stories.svelte +58 -0
  275. package/dist/components/ui/radio-group/radio-group.stories.svelte.d.ts +27 -0
  276. package/dist/components/ui/resizable/resizable.stories.svelte +56 -0
  277. package/dist/components/ui/resizable/resizable.stories.svelte.d.ts +27 -0
  278. package/dist/components/ui/select/select.stories.svelte +49 -0
  279. package/dist/components/ui/separator/separator.stories.svelte +18 -0
  280. package/dist/components/ui/sheet/sheet.stories.svelte +34 -0
  281. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  282. package/dist/components/ui/sidebar/sidebar-menu-button.svelte +1 -0
  283. package/dist/components/ui/sidebar/sidebar-trigger.svelte +1 -1
  284. package/dist/components/ui/sidebar/sidebar.stories.svelte +72 -0
  285. package/dist/components/ui/sidebar/sidebar.stories.svelte.d.ts +27 -0
  286. package/dist/components/ui/skeleton/skeleton.stories.svelte +39 -0
  287. package/dist/components/ui/skeleton/skeleton.stories.svelte.d.ts +27 -0
  288. package/dist/components/ui/skeleton/skeleton.svelte +6 -0
  289. package/dist/components/ui/sonner/index.d.ts +1 -1
  290. package/dist/components/ui/sonner/index.js +1 -1
  291. package/dist/components/ui/sonner/sonner.stories.svelte +7 -0
  292. package/dist/components/ui/sonner/sonner.svelte +17 -1
  293. package/dist/components/ui/sonner/sonner.svelte.d.ts +6 -0
  294. package/dist/components/ui/spinner/spinner.stories.svelte +30 -0
  295. package/dist/components/ui/spinner/spinner.stories.svelte.d.ts +27 -0
  296. package/dist/components/ui/switch/switch.stories.svelte +56 -0
  297. package/dist/components/ui/switch/switch.stories.svelte.d.ts +27 -0
  298. package/dist/components/ui/table/table-cell.svelte +1 -1
  299. package/dist/components/ui/table/table-head.svelte +1 -1
  300. package/dist/components/ui/table/table.stories.svelte +68 -0
  301. package/dist/components/ui/table/table.stories.svelte.d.ts +27 -0
  302. package/dist/components/ui/table/table.svelte +1 -1
  303. package/dist/components/ui/tabs/tabs.stories.svelte +48 -0
  304. package/dist/components/ui/tabs/tabs.stories.svelte.d.ts +27 -0
  305. package/dist/components/ui/textarea/textarea.stories.svelte +21 -0
  306. package/dist/components/ui/toggle/toggle.stories.svelte +23 -0
  307. package/dist/components/ui/toggle-group/toggle-group.stories.svelte +43 -0
  308. package/dist/components/ui/tooltip/tooltip.stories.svelte +46 -6
  309. package/dist/core/cms.d.ts +11 -2
  310. package/dist/core/cms.js +29 -0
  311. package/dist/core/fields/fieldSchemaToTs.d.ts +7 -0
  312. package/dist/core/fields/fieldSchemaToTs.js +241 -90
  313. package/dist/core/fields/layoutUtils.d.ts +4 -1
  314. package/dist/core/fields/layoutUtils.js +41 -4
  315. package/dist/core/fields/resolveSeo.d.ts +70 -0
  316. package/dist/core/fields/resolveSeo.js +88 -0
  317. package/dist/core/fields/seoFieldDescriptor.d.ts +43 -0
  318. package/dist/core/fields/seoFieldDescriptor.js +74 -0
  319. package/dist/core/fields/slugPath.d.ts +13 -0
  320. package/dist/core/fields/slugPath.js +32 -0
  321. package/dist/core/fields/urlUtils.d.ts +8 -0
  322. package/dist/core/fields/urlUtils.js +27 -0
  323. package/dist/core/index.d.ts +1 -0
  324. package/dist/core/index.js +1 -0
  325. package/dist/core/server/entries/operations/create.js +13 -0
  326. package/dist/core/server/entries/operations/get.d.ts +7 -0
  327. package/dist/core/server/entries/operations/get.js +10 -6
  328. package/dist/core/server/entries/operations/slugUniqueness.d.ts +37 -0
  329. package/dist/core/server/entries/operations/slugUniqueness.js +116 -0
  330. package/dist/core/server/entries/operations/update.d.ts +6 -1
  331. package/dist/core/server/entries/operations/update.js +24 -1
  332. package/dist/core/server/fields/slugResolver.d.ts +3 -13
  333. package/dist/core/server/fields/slugResolver.js +8 -37
  334. package/dist/core/server/generator/fields.d.ts +2 -0
  335. package/dist/core/server/generator/fields.js +44 -18
  336. package/dist/core/server/generator/formFields.js +2 -1
  337. package/dist/core/server/generator/generator.js +6 -5
  338. package/dist/core/server/generator/utils.d.ts +1 -0
  339. package/dist/core/server/generator/utils.js +4 -0
  340. package/dist/db-postgres/schema/shop/order.d.ts +37 -1
  341. package/dist/db-postgres/schema/shop/order.js +3 -1
  342. package/dist/db-postgres/schema/shop/payment.d.ts +20 -0
  343. package/dist/db-postgres/schema/shop/payment.js +4 -1
  344. package/dist/db-postgres/schema/shop/product.d.ts +20 -0
  345. package/dist/db-postgres/schema/shop/product.js +3 -1
  346. package/dist/db-postgres/schema/shop/productVariant.d.ts +12 -2
  347. package/dist/db-postgres/schema/shop/productVariant.js +22 -0
  348. package/dist/shop/cart/types.d.ts +1 -0
  349. package/dist/shop/client/index.d.ts +54 -0
  350. package/dist/shop/client/index.js +5 -1
  351. package/dist/shop/expiry.d.ts +35 -0
  352. package/dist/shop/expiry.js +68 -0
  353. package/dist/shop/http/balance-handler.d.ts +20 -0
  354. package/dist/shop/http/balance-handler.js +91 -0
  355. package/dist/shop/http/cart-handler.js +19 -0
  356. package/dist/shop/http/checkout-handler.js +19 -1
  357. package/dist/shop/http/index.d.ts +2 -0
  358. package/dist/shop/http/index.js +2 -0
  359. package/dist/shop/http/upcoming-handler.d.ts +16 -0
  360. package/dist/shop/http/upcoming-handler.js +65 -0
  361. package/dist/shop/http/webhook-handler.js +46 -9
  362. package/dist/shop/index.d.ts +4 -1
  363. package/dist/shop/index.js +7 -1
  364. package/dist/shop/server/balance-payment.d.ts +40 -0
  365. package/dist/shop/server/balance-payment.js +140 -0
  366. package/dist/shop/server/cart-hydrate.js +2 -0
  367. package/dist/shop/server/init.d.ts +14 -0
  368. package/dist/shop/server/init.js +35 -0
  369. package/dist/shop/server/orders.d.ts +35 -0
  370. package/dist/shop/server/orders.js +155 -2
  371. package/dist/shop/server/payment-policy.d.ts +35 -0
  372. package/dist/shop/server/payment-policy.js +55 -0
  373. package/dist/shop/server/payments.d.ts +29 -0
  374. package/dist/shop/server/payments.js +64 -0
  375. package/dist/shop/server/populate.d.ts +1 -1
  376. package/dist/shop/server/refund.d.ts +17 -12
  377. package/dist/shop/server/refund.js +96 -13
  378. package/dist/shop/server/shop-data.d.ts +6 -1
  379. package/dist/shop/server/shop-data.js +44 -7
  380. package/dist/shop/template.d.ts +13 -0
  381. package/dist/shop/template.js +98 -0
  382. package/dist/shop/types.d.ts +142 -1
  383. package/dist/shop/variant-attributes.d.ts +28 -0
  384. package/dist/shop/variant-attributes.js +69 -0
  385. package/dist/sveltekit/server/handle.js +17 -0
  386. package/dist/sveltekit/server/index.d.ts +1 -0
  387. package/dist/sveltekit/server/index.js +2 -0
  388. package/dist/types/cms.d.ts +4 -3
  389. package/dist/types/cms.schema.d.ts +1 -1
  390. package/dist/types/cms.schema.js +13 -2
  391. package/dist/types/fields.d.ts +56 -2
  392. package/dist/types/index.d.ts +2 -2
  393. package/dist/types/index.js +1 -1
  394. package/dist/types/layout.d.ts +35 -2
  395. package/dist/types/plugins.d.ts +40 -0
  396. package/dist/types/plugins.js +4 -1
  397. package/dist/updates/0.26.0/index.d.ts +2 -0
  398. package/dist/updates/0.26.0/index.js +51 -0
  399. package/dist/updates/0.26.1/index.d.ts +2 -0
  400. package/dist/updates/0.26.1/index.js +19 -0
  401. package/dist/updates/0.27.0/index.d.ts +2 -0
  402. package/dist/updates/0.27.0/index.js +50 -0
  403. package/dist/updates/index.js +7 -1
  404. package/package.json +29 -7
  405. package/dist/admin/client/collection/empty-state.svelte +0 -28
  406. package/dist/admin/client/collection/empty-state.svelte.d.ts +0 -9
  407. package/dist/admin/client/form/submission-status-badge.svelte +0 -41
  408. package/dist/admin/client/form/submission-status-badge.svelte.d.ts +0 -7
  409. package/dist/admin/components/media/file-preview.svelte +0 -51
  410. package/dist/admin/components/media/file-preview.svelte.d.ts +0 -6
  411. package/dist/admin/utils/formatDate.d.ts +0 -5
@@ -7,6 +7,7 @@ import { building } from '$app/environment';
7
7
  import { csrfGuard } from '../../server/security/csrf.js';
8
8
  import { rateLimitGuard } from '../../server/security/rate-limit.js';
9
9
  import { buildCspHeader } from '../../server/security/csp.js';
10
+ import { baseLocale, extractLocaleFromRequest, isLocale } from '../../paraglide/runtime.js';
10
11
  const adminGuard = async ({ event, resolve }) => {
11
12
  const { user, session } = event.locals;
12
13
  // Secure the admin routes
@@ -60,6 +61,21 @@ const handleAuth = async ({ event, resolve }) => {
60
61
  }
61
62
  return svelteKitHandler({ event, resolve, auth, building });
62
63
  };
64
+ const paraglideLang = async ({ event, resolve }) => {
65
+ let locale = baseLocale;
66
+ try {
67
+ const detected = extractLocaleFromRequest(event.request);
68
+ if (detected && isLocale(detected)) {
69
+ locale = detected;
70
+ }
71
+ }
72
+ catch {
73
+ // fall back to baseLocale
74
+ }
75
+ return resolve(event, {
76
+ transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale)
77
+ });
78
+ };
63
79
  const detectBrowserContext = async ({ event, resolve }) => {
64
80
  const ua = event.request.headers.get('user-agent') ?? '';
65
81
  const isSafari = /Safari/i.test(ua) && !/(?:Chrome|Chromium|Edg)/i.test(ua);
@@ -90,6 +106,7 @@ export function includioCMS(cmsConfig) {
90
106
  generateRuntime(cmsConfig); // Generate runtime code based on the CMS config
91
107
  initCMS(cmsConfig);
92
108
  const handles = [];
109
+ handles.push(paraglideLang);
93
110
  handles.push(detectBrowserContext);
94
111
  handles.push(csrfGuard);
95
112
  if (cmsConfig.auth) {
@@ -7,3 +7,4 @@ export { createConsentLog } from '../../core/server/consentLogs/operations/creat
7
7
  export { getPreviewEntry } from './preview.js';
8
8
  export { createRestApiHandler } from '../../admin/api/rest/handler.js';
9
9
  export { generateApiKey } from '../../admin/api/rest/middleware/generateApiKey.js';
10
+ export { createAdminApiHandler } from '../../admin/api/handler.js';
@@ -8,3 +8,5 @@ export { getPreviewEntry } from './preview.js';
8
8
  // Folded from `./admin/api/rest/handler` (dropped as separate export in 0.20.0)
9
9
  export { createRestApiHandler } from '../../admin/api/rest/handler.js';
10
10
  export { generateApiKey } from '../../admin/api/rest/middleware/generateApiKey.js';
11
+ // Folded from `./admin/api/handler` (dropped as separate export in 0.26.1)
12
+ export { createAdminApiHandler } from '../../admin/api/handler.js';
@@ -3,7 +3,7 @@ import type { FilesAdapter } from './adapters/files.js';
3
3
  import type { CollectionConfig, CollectionConfigWithType } from './collections.js';
4
4
  import type { Language } from './languages.js';
5
5
  import type { SingleConfig, SingleConfigWithType } from './singles.js';
6
- import type { CustomFieldDefinition, PluginConfig } from './plugins.js';
6
+ import type { CustomFieldDefinition, IconSetPlugin, Plugin } from './plugins.js';
7
7
  import type { FormConfig } from './forms.js';
8
8
  import type { AIAdapter } from './adapters/ai.js';
9
9
  import type { EmailAdapter } from './adapters/email.js';
@@ -77,7 +77,7 @@ export interface CMSConfig {
77
77
  files: FilesAdapter;
78
78
  email?: EmailAdapter;
79
79
  auth?: AuthConfig;
80
- plugins?: PluginConfig[];
80
+ plugins?: Plugin[];
81
81
  ai?: AIAdapter;
82
82
  media?: MediaConfig;
83
83
  apiKeys?: ApiKeyConfig[];
@@ -95,8 +95,9 @@ export interface ICMS {
95
95
  filesAdapter: FilesAdapter;
96
96
  emailAdapter: EmailAdapter | null;
97
97
  authConfig: AuthConfig | null;
98
- plugins: PluginConfig[];
98
+ plugins: Plugin[];
99
99
  customFields: Map<string, CustomFieldDefinition>;
100
+ iconSets: Map<string, IconSetPlugin>;
100
101
  aiAdapter: AIAdapter | null;
101
102
  mediaConfig: MediaConfig;
102
103
  apiKeys: ApiKeyConfig[];
@@ -445,7 +445,7 @@ export declare const _internal: {
445
445
  shop: z.ZodOptional<z.ZodUnknown>;
446
446
  cmp: z.ZodOptional<z.ZodUnknown>;
447
447
  }, z.core.$loose>;
448
- FIELD_TYPES: readonly ["text", "content", "number", "boolean", "date", "datetime", "file", "media", "select", "radio", "checkboxes", "relation", "object", "array", "blocks", "slug", "seo", "shop", "url", "custom"];
448
+ FIELD_TYPES: readonly ["text", "content", "number", "boolean", "date", "datetime", "file", "media", "select", "radio", "checkboxes", "relation", "object", "array", "blocks", "slug", "seo", "shop", "url", "icon", "custom"];
449
449
  FORM_FIELD_TYPES: readonly ["text", "email", "textarea", "checkbox", "select", "file"];
450
450
  getLangCode: typeof getLangCode;
451
451
  };
@@ -6,9 +6,9 @@ const localizedSchema = z.union([z.string(), z.record(z.string(), z.string())]);
6
6
  /* -------------------------------------------------------------------------- */
7
7
  /* Slug */
8
8
  /* -------------------------------------------------------------------------- */
9
- const SLUG_RE = /^[a-z][a-z0-9-]*$/;
9
+ const SLUG_RE = /^[a-z][a-zA-Z0-9-]*$/;
10
10
  const slugSchema = z.string().regex(SLUG_RE, {
11
- message: 'must be lowercase letters/digits/hyphens, starting with a letter'
11
+ message: 'must start with a lowercase letter; letters, digits, hyphens allowed'
12
12
  });
13
13
  /* -------------------------------------------------------------------------- */
14
14
  /* Languages */
@@ -51,6 +51,7 @@ const FIELD_TYPES = [
51
51
  'seo',
52
52
  'shop',
53
53
  'url',
54
+ 'icon',
54
55
  'custom'
55
56
  ];
56
57
  const baseFieldShape = {
@@ -82,6 +83,8 @@ const fieldSchema = z.lazy(() => z.discriminatedUnion('type', [
82
83
  minLength: z.number().int().nonnegative().optional(),
83
84
  maxLength: z.number().int().positive().optional(),
84
85
  pattern: z.string().optional(),
86
+ patternError: localizedSchema.optional(),
87
+ patternHint: localizedSchema.optional(),
85
88
  multiline: z.boolean().optional()
86
89
  })
87
90
  .passthrough(),
@@ -267,6 +270,14 @@ const fieldSchema = z.lazy(() => z.discriminatedUnion('type', [
267
270
  type: z.literal('custom'),
268
271
  fieldType: z.string().min(1, { message: 'custom.fieldType is required' }),
269
272
  config: z.record(z.string(), z.unknown()).optional()
273
+ })
274
+ .passthrough(),
275
+ // icon — set provided by IconSetPlugin (registered in plugins[])
276
+ z
277
+ .object({
278
+ ...baseFieldShape,
279
+ type: z.literal('icon'),
280
+ set: z.string().optional()
270
281
  })
271
282
  .passthrough()
272
283
  ]));
@@ -2,7 +2,7 @@ import type { FormatEnum } from 'sharp';
2
2
  import type { ImageStyle, MediaFile, VideoStyle } from './media.js';
3
3
  import type { Localized } from './languages.js';
4
4
  import type { StructuredContentDoc } from './structured-content.js';
5
- export type FieldType = 'text' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'shop' | 'url' | 'custom';
5
+ export type FieldType = 'text' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'shop' | 'url' | 'icon' | 'custom';
6
6
  export interface FieldCondition {
7
7
  field: string;
8
8
  equals?: string | string[];
@@ -23,6 +23,19 @@ export interface TextField extends BaseField {
23
23
  minLength?: number;
24
24
  maxLength?: number;
25
25
  pattern?: string;
26
+ /**
27
+ * Friendly validation message shown when {@link pattern} fails. A raw regex
28
+ * means nothing to an editor, so provide a localized human message here
29
+ * (e.g. "Use lowercase letters and dashes only"). Falls back to a generic
30
+ * message when omitted.
31
+ */
32
+ patternError?: Localized;
33
+ /**
34
+ * Localized hint describing the expected format, shown in the field
35
+ * description instead of the raw regex. Without it the raw {@link pattern}
36
+ * is never surfaced to the editor.
37
+ */
38
+ patternHint?: Localized;
26
39
  multiline?: boolean;
27
40
  defaultValue?: string;
28
41
  }
@@ -60,6 +73,12 @@ export interface FileField extends BaseField {
60
73
  maxSizeMB?: number;
61
74
  multiple?: boolean;
62
75
  defaultValue?: string | string[];
76
+ /**
77
+ * Render mode for selected file(s).
78
+ * - 'preview' (default): aspect-square miniature + carousel for multiple (parity with media field).
79
+ * - 'list': compact list of rows (icon + name + size + X) — better for non-visual files (PDF, audio, other).
80
+ */
81
+ display?: 'preview' | 'list';
63
82
  }
64
83
  export interface ImageFieldStyle {
65
84
  name: string;
@@ -172,17 +191,33 @@ export interface BlocksField extends BaseField {
172
191
  defaultValue?: ObjectFieldData[];
173
192
  displayMode?: 'simple' | 'blocks';
174
193
  }
194
+ /**
195
+ * @public
196
+ * Slug field — a normalized URL-safe string. Its presence does NOT imply a
197
+ * public URL; routability is decided by the collection's `slugField`/`pathTemplate`
198
+ * config, not by this field existing.
199
+ */
175
200
  export interface SlugField extends BaseField {
176
201
  type: 'slug';
177
202
  pattern?: string;
178
203
  sourceField?: string;
179
204
  defaultValue?: string;
180
205
  }
206
+ /**
207
+ * @public
208
+ * v1.0-frozen. New properties may only be added as OPTIONAL (additive,
209
+ * non-breaking).
210
+ */
181
211
  export interface SeoField extends BaseField {
182
212
  type: 'seo';
183
213
  slugSource?: string;
184
214
  titleSource?: string;
185
215
  }
216
+ /**
217
+ * @public
218
+ * v1.0-frozen data contract: `entry.seo.*`. New fields may only be added as
219
+ * OPTIONAL (additive, non-breaking).
220
+ */
186
221
  export interface SeoFieldData {
187
222
  slug: string;
188
223
  canonicalUrl?: string;
@@ -199,6 +234,11 @@ export interface ShopFieldData {
199
234
  basePrice: number;
200
235
  vatRate: number;
201
236
  isActive: boolean;
237
+ /**
238
+ * Per-product payment policy. Omitted = `{ type: 'full' }` (legacy behavior).
239
+ * @public
240
+ */
241
+ paymentPolicy?: import('../shop/types.js').PaymentPolicy;
202
242
  variants?: Array<{
203
243
  id?: string;
204
244
  sku?: string | null;
@@ -229,4 +269,18 @@ export interface CustomField extends BaseField {
229
269
  /** Plugin-specific configuration, opaque to core */
230
270
  config?: Record<string, unknown>;
231
271
  }
232
- export type Field = TextField | ContentField | NumberField | BooleanField | DateField | DateTimeField | FileField | MediaField | SelectField | RadioField | CheckboxesField | RelationField | ObjectField | ArrayField | BlocksField | SlugField | SeoField | ShopField | UrlField | CustomField;
272
+ /**
273
+ * @public
274
+ * Icon picker field. Stores a string key referencing an icon registered by an
275
+ * {@link import('./plugins.js').IconSetPlugin}. Admin UI renders a kafelek
276
+ * (collapsed) and opens a searchable grid dialog. The set of icons (Svelte
277
+ * components + labels) is provided by a plugin at admin bootstrap — NOT inline
278
+ * in `cms.config.ts` (which is server-only / Zod-validated).
279
+ */
280
+ export interface IconField extends BaseField {
281
+ type: 'icon';
282
+ /** Plugin slug to source icons from when multiple icon-set plugins registered. Defaults to the first one. */
283
+ set?: string;
284
+ defaultValue?: string;
285
+ }
286
+ export type Field = TextField | ContentField | NumberField | BooleanField | DateField | DateTimeField | FileField | MediaField | SelectField | RadioField | CheckboxesField | RelationField | ObjectField | ArrayField | BlocksField | SlugField | SeoField | ShopField | UrlField | IconField | CustomField;
@@ -10,8 +10,8 @@ export { type SingleConfig } from './singles.js';
10
10
  export { type FormConfig, type FormSubmission } from './forms.js';
11
11
  export { type FormField, type FormFieldType, type FormBaseField, type FormTextField, type FormEmailField, type FormTextareaField, type FormCheckboxField, type FormSelectField } from './formFields.js';
12
12
  export { type CMSConfig, type ApiKeyConfig, type AuthConfig, type TypographyConfig } from './cms.js';
13
- export { type PluginConfig, type CustomFieldDefinition } from './plugins.js';
13
+ export { type PluginConfig, type CustomFieldDefinition, type IconSetPlugin, type IconDefinition, type Plugin, isIconSetPlugin } from './plugins.js';
14
14
  export { type Language, type Localized } from './languages.js';
15
- export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode } from './layout.js';
15
+ export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode, type TabsNode, type TabNode } from './layout.js';
16
16
  export { type CmsContext } from './cms-context.js';
17
17
  export { type UserData, type Breadcrumb } from '../admin/types.js';
@@ -10,7 +10,7 @@ export {} from './singles.js';
10
10
  export {} from './forms.js';
11
11
  export {} from './formFields.js';
12
12
  export {} from './cms.js';
13
- export {} from './plugins.js';
13
+ export { isIconSetPlugin } from './plugins.js';
14
14
  export {} from './languages.js';
15
15
  export {} from './layout.js';
16
16
  export {} from './cms-context.js';
@@ -7,7 +7,7 @@ export type LayoutPreset = 'sidebar-right' | 'two-column' | {
7
7
  preset: 'two-column';
8
8
  left?: string[];
9
9
  };
10
- export type LayoutNodeType = 'section' | 'columns' | 'card' | 'accordion' | 'stack';
10
+ export type LayoutNodeType = 'section' | 'columns' | 'card' | 'accordion' | 'stack' | 'tabs' | 'tab';
11
11
  export type ColumnRatio = '1fr 1fr' | '2fr 1fr' | '1fr 2fr' | '3fr 1fr' | '1fr 3fr' | '1fr 1fr 1fr';
12
12
  interface LayoutNodeBase {
13
13
  type: LayoutNodeType;
@@ -51,7 +51,40 @@ export interface StackNode extends LayoutNodeBase {
51
51
  fields?: string[];
52
52
  children?: LayoutNode[];
53
53
  }
54
- export type LayoutNode = SectionNode | ColumnsNode | CardNode | AccordionNode | StackNode;
54
+ /**
55
+ * Tab strip. Renders an accessible tablist; each child `tab` becomes a panel.
56
+ * All panels stay mounted (inactive ones hidden) so validation, dirty-tracking
57
+ * and conditional visibility keep working across tabs.
58
+ *
59
+ * Constraints (enforced by {@link validateLayout}):
60
+ * - `children` must contain only `tab` nodes
61
+ * - `tabs` may not be nested inside another `tab`/`tabs`
62
+ * - allowed top-level or inside section/card/columns/stack
63
+ *
64
+ * @public
65
+ * @example
66
+ * { type: 'tabs', children: [
67
+ * { type: 'tab', label: { en: 'Content', pl: 'Treść' }, fields: ['title', 'body'] },
68
+ * { type: 'tab', label: { en: 'SEO' }, fields: ['seo'] }
69
+ * ] }
70
+ */
71
+ export interface TabsNode extends LayoutNodeBase {
72
+ type: 'tabs';
73
+ children: TabNode[];
74
+ }
75
+ /**
76
+ * A single tab. Only valid as a direct child of {@link TabsNode}.
77
+ * `label` is required — it is the trigger text. Use `fields` for a flat tab
78
+ * or `children` to nest sections/cards/columns inside the tab.
79
+ * @public
80
+ */
81
+ export interface TabNode extends LayoutNodeBase {
82
+ type: 'tab';
83
+ label: Localized;
84
+ fields?: string[];
85
+ children?: LayoutNode[];
86
+ }
87
+ export type LayoutNode = SectionNode | ColumnsNode | CardNode | AccordionNode | StackNode | TabsNode | TabNode;
55
88
  export declare function isLayoutLeaf(node: LayoutNode): node is LayoutNode & {
56
89
  fields: string[];
57
90
  };
@@ -2,6 +2,7 @@ import type { Component } from 'svelte';
2
2
  import type { z } from 'zod';
3
3
  import type { RawEntry } from './entries.js';
4
4
  import type { CustomField } from './fields.js';
5
+ import type { Localized } from './languages.js';
5
6
  import type { PopulateCtx } from '../core/server/entries/operations/resolveEntry.js';
6
7
  /**
7
8
  * Defines a custom field type contributed by a plugin.
@@ -40,3 +41,42 @@ export interface PluginConfig {
40
41
  afterDelete?: (id: string) => Promise<void>;
41
42
  };
42
43
  }
44
+ /**
45
+ * @public
46
+ * Single icon entry contributed by an {@link IconSetPlugin}. Carries a Svelte
47
+ * component, a localized label and optional search keywords. Components are
48
+ * eagerly imported (tree-shakable) — for small sets (10–50 icons) lazy import
49
+ * adds overhead without benefit.
50
+ */
51
+ export interface IconDefinition {
52
+ /** Svelte icon component (e.g. `Brain` from `@lucide/svelte`). */
53
+ component: Component;
54
+ /** Localized display label shown in collapsed state and in the picker grid. */
55
+ label: Localized;
56
+ /** Extra search keywords (non-localized). Search matches key + label + keywords. */
57
+ keywords?: string[];
58
+ }
59
+ /**
60
+ * @public
61
+ * Icon set plugin — registers a named set of icons consumable by the `icon`
62
+ * field type. Registered alongside other plugins in `cms.config.ts`'s
63
+ * `plugins:` array. Because Svelte components do NOT serialize to the client,
64
+ * the application's admin layout must wire the same plugin instance to
65
+ * `AdminLayout` via `buildIconSetMap(...)` — see admin/helpers.
66
+ */
67
+ export interface IconSetPlugin {
68
+ /** Unique slug — referenced by `IconField.set` when multiple sets exist. */
69
+ slug: string;
70
+ type: 'icon-set';
71
+ icons: Record<string, IconDefinition>;
72
+ }
73
+ /**
74
+ * @public
75
+ * Union of all plugin kinds accepted in `cms.config.ts → plugins:`. New
76
+ * plugin kinds must be added here. Type discriminated by:
77
+ * - {@link IconSetPlugin} → `type === 'icon-set'`
78
+ * - {@link PluginConfig} → everything else (no `type` field).
79
+ */
80
+ export type Plugin = PluginConfig | IconSetPlugin;
81
+ /** Type guard for {@link IconSetPlugin}. */
82
+ export declare function isIconSetPlugin(p: Plugin): p is IconSetPlugin;
@@ -1 +1,4 @@
1
- export {};
1
+ /** Type guard for {@link IconSetPlugin}. */
2
+ export function isIconSetPlugin(p) {
3
+ return p.type === 'icon-set';
4
+ }
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,51 @@
1
+ export const update = {
2
+ version: '0.26.0',
3
+ date: '2026-05-26',
4
+ description: 'UI Normalization → release readiness: slug regex camelCase + S8 detail/edit + S9 fields a11y polish + S10 a11y final audit + S10b/S10c mobile responsiveness full pass (sticky first column DataTable, Dialog fullscreenMobile prop, media library Sheet drawers, entry hybrid preview drawer toggle, dashboard mobile layout flip). 43/43 mobile Playwright pass (3 viewports × 12 widoków, 0 critical axe).',
5
+ features: [
6
+ '`getFieldA11yProps({ id, hasDescription, hasError, required })` (`src/lib/admin/utils/field-a11y.ts`) — buduje `aria-describedby` chain, `aria-invalid`, `aria-required` dla pól w `ui/form` (formsnap) — bridge bez breaking migracji ui/form ↔ ui/field koegzystencji z S3.',
7
+ '`<LiveRegion>` (`src/lib/components/ui/live-region/`) — prosty primitive (`sr-only` + `role="status"` + `aria-live`) zastąpił inline duplicates w relation-field i blocks-field; gotowy do reuse.',
8
+ '`field-renderer.svelte` propaguje `Form.Control` `props` (formsnap snippet `children({ props })`) do wszystkich field components (boolean, number, date, datetime, select, media, file, blocks, array, object, slug, seo, shop, url, relation). Dotąd tylko text/radio/checkboxes dostawały aria — reszta była niewired.',
9
+ 'Blocks field — keyboard reorder/add/remove/duplicate ogłasza przez `<LiveRegion>` ("Przeniesiono X na pozycję Y", "Dodano blok X" itp.). Block picker modal — `aria-labelledby` z Dialog.Title + `aria-live` na search results count.',
10
+ 'Slug field — live preview slugify\'d wartości pod inputem (gdy user typuje wielką literą), copy button z `UseClipboard` + toast "Skopiowano slug". Description default w demo blog-post.',
11
+ 'Relation field — combobox a11y wzmocnione: `aria-busy` na Command.List podczas async search, picker dialog `Dialog.Title`/`Dialog.Description` (sr-only), reorder LiveRegion przeniesione na primitive.',
12
+ 'Media + File fields — `Dialog.Title` + `Dialog.Description` (sr-only) na picker dialogs (eliminuje bits-ui ostrzeżenie o brakujących aria-labelledby).',
13
+ 'Shop field — `aria-pressed` na price mode toggle (Netto/Brutto), `role="group"` + `aria-label` na toggle group, `aria-label="Usuń wariant N"` na trash buttons.',
14
+ 'SEO field — `aria-live="polite"` na char counter (50/60, 120/160) — SR słyszy zmianę gdy user wpisuje.',
15
+ 'Storybook fields — nowe `file.stories.svelte` (4 warianty) i `shop.stories.svelte` (4 warianty). Slug stories rozszerzone o WithPreview/ClipboardCopy. Text stories — Required wariant. Slug ma już `tags: [\'autodocs\']` (zdjęty `no-test`).',
16
+ 'Slugi pól, kolekcji, singles i form akceptują camelCase (`/^[a-z][a-zA-Z0-9-]*$/`). Można pisać `slug: \'siteName\'` zamiast `\'site-name\'` i odczytywać `entry.data.siteName` bez bracket notation. Walidacja blokuje nadal PascalCase, prefix `_` i digit-leading.',
17
+ 'Helper `quoteKey()` w `src/lib/core/server/generator/utils.ts` — opakowuje slugi nie-spełniające JS identifier shape w cudzysłowy w wygenerowanym TS.',
18
+ 'Nowy węzeł layoutu `tabs`/`tab` (`src/lib/types/layout.ts`) — taby konfigurowane per kolekcja w `layout[]`, renderowane przez `LayoutRenderer` (bits-ui Tabs: `role=tab/tablist/tabpanel`, strzałki/Home/End, `aria-selected`). Wszystkie panele pozostają zamontowane (walidacja/dirty-tracking/`showWhen` działają na nieaktywnych tabach), error-summary i klik-w-preview (hybrid) automatycznie przełączają tab i fokusują pole (`activateContainingTabs`), trigger taba z błędem ma wskaźnik (kropka + tekst dla SR, nie sam kolor). Aktywny tab najwyższego poziomu utrwalany w `?tab` (deep-link, przeżywa reload). `tab` przyjmuje `fields` (płaski) lub `children` (sekcje/karty w środku); zakaz zagnieżdżonych tabów + walidacja struktury w `validateLayout`. Stary sztywny tab Content/SEO w `entry.svelte` usunięty — SEO to teraz zwykły `tab` w configu (demo blog-post).',
19
+ '`<FormErrorSummary>` (`src/lib/admin/components/forms/`) — banner z listą błędów, anchor links scroll do `[data-field-path]`, focus pierwszego błędnego pola, `role="alert"` + `aria-live="assertive"`. Adoptowany w entry, coupon-form, shipping-method-form, shop-order-detail.',
20
+ '`<ConfirmationDialog>` (`src/lib/admin/components/dialogs/`) — typed wrapper na bits-ui AlertDialog z destructive/default variant, default focus na Cancel (bezpieczniejszy), loading state. Zastąpił 5× `confirm()` (form-submissions, entry, shop-order-detail, coupons-list, dashboard) + dodany dla Archive entry.',
21
+ '`<DetailPageShell>` (`src/lib/admin/components/layout/`) — wspólny layout (PageHeader + body + opcjonalny sidebar) dla coupon-edit / shipping-method-edit. Skip-link target `#detail-main`.',
22
+ 'Coupon edit form zmigrowany na sveltekit-superforms + formsnap (jak account/profile-section) — schema zod (`coupon-schema.ts`), SPA mode, error summary + per-field errors.',
23
+ 'Entry SaveIndicator: nowe stany + COGA copy (`Niezapisane zmiany`, `Zapisuję…`, `Zapisano X temu` z `formatRelativeDate` recompute co 30s, `Błąd zapisu`), `aria-atomic` + role flip alert/status. `<svelte:window>` `beforeunload` guard gdy unsaved.',
24
+ '`<A11yHeaderBadge>` (`src/lib/admin/client/entry/header/a11y-header-badge.svelte`) — persistent warning chip w entry-header pokazujący liczbę a11y issues (images bez alt, heading hierarchy, generic link text). Reuse `validateA11y` z `a11y-validator.ts`. Strong-warning UX (B.1) — nie blokuje save, prowadzi autora. Kolumna a11y w collection-entries.svelte (S5) + persistent header badge (S10) + publish-panel sheet detail = pełna ścieżka widoczności.',
25
+ '`scripts/lighthouse-audit.ts` (`pnpm audit:lighthouse`) — ad-hoc CLI runner po 11 URL admin (login, dashboard, collections, users, media, forms, account, maintenance, shop ×3). Lighthouse 13 + chrome-launcher + auth cookie z `e2e/.auth/admin.json`. Output `.tmp/lighthouse/<view>.json` + `summary.md`, target a11y ≥95.',
26
+ '`e2e/admin/a11y-baseline.spec.ts` rozszerzone 4 → 14 widoków: 10 page-level (login, dashboard, collections-list, entry-edit, users-list, media-library, forms-detail, account, maintenance, user-create-dialog) + 4 shop best-effort (skip jeśli moduł nie configured). Per spec 0 critical violations enforced.',
27
+ 'TipTap link-dialog `aria-expanded` na 2 collapsible sections (Dostępność + Indeksowanie) — uzupełnia ATAG B.3 author tool a11y. FigureNodeView/image-dialog/publish-panel: `:focus-visible` fallback z `var(--ring)` outline (focus indicator widoczny zawsze, nawet gdy `:focus` zmienia tylko border-color).',
28
+ 'Publiczny `resolveSeo(entry, language?)` (`includio-cms/core`) — stabilny znormalizowany read-path nad zamrożonym kontraktem `entry.seo.*` (wartości localized spłaszczane po języku; `ogImage` przepuszczany jako string lub MediaFile) do budowy własnego `<head>` lub zasilenia komponentu `<Seo>`. `SlugField`/`SeoField`/`SeoFieldData` oznaczone `@public` + v1.0-frozen.',
29
+ 'Wewnętrzny `seoFieldDescriptor` — pojedyncze źródło prawdy kształtu pola SEO dla generatorów Zod + TS (wyjście bit-identyczne, pilnowane snapshotami i testami drift-guard). Przygotowuje przyszły plugin-rehoming SEO bez breaking change. Istniejąca niespójność Zod(required)/TS(optional) dla `description`/`ogImage` udokumentowana i zachowana 1:1 (do rozstrzygnięcia post-1.0).',
30
+ 'Mobile responsiveness full pass (S10c) — pełny mobile UX panelu admina: `DataTable` sticky first column + edge-bleed scroll wrapper (1 fix → wszystkie 8+ list pages, w tym shop), `Dialog.Content` `fullscreenMobile` prop (media/file picker + tiptap image/video dialogi), media library tags sidebar + FileDetails jako Sheet drawer na <md (`hidden md:flex` aside), entry hybrid preview Sheet drawer toggle z `IsMobile(1024)` (<lg pokazuje tylko form + floating "Podgląd" button), shop-order detail items table z `min-w-[640px]` + horizontal scroll, char counter w `text-field-wrapper` na mobile stackowany pod polem zamiast `absolute right`, EntryHeader padding + SaveIndicator text `max-sm:sr-only`, PageHeader text-scale `text-2xl sm:text-3xl lg:text-4xl`, breadcrumb truncacja middle segments `/…/` na <sm, table-toolbar search `w-full min-w-0` na mobile, tiptap toolbar separatory `hidden sm:block`, relation popover `w-[var(--bits-popover-anchor-width)] max-w-[calc(100vw-1rem)]`. Playwright `e2e/admin/mobile-responsive.spec.ts` rozszerzony o iPad portrait (768×1024) + shop-shipping view + `scrollWidth <= window.innerWidth` page-overflow assertion: 43/43 testów pass, 0 critical, 0-2 serious axe per (3 viewports × 12 widoków).',
31
+ 'Dashboard mobile layout flip (S10c follow-up) — `dashboard-page.svelte`: main wrapper `flex gap-5` → `flex flex-col gap-5 lg:flex-row` + sub-grid `grid-cols-2` → `grid-cols-1 sm:grid-cols-2` + sidebar `max-w-80 flex-1 shrink-0` → `w-full shrink-0 lg:max-w-80 lg:flex-1`. Side-by-side 2-col layout aktywowany dopiero od `lg` (1024px); poniżej stack vertically, sub-grid kolumna pojedyncza do `sm` (640px). Eliminuje cramped 2-col scroll na 375-768px.'
32
+ ],
33
+ fixes: [
34
+ '`runtime/types.ts` generuje poprawny TypeScript dla kebab-case slugów (np. `\'published-at\'?: string` zamiast gołego `published-at?: string`, który był syntaks błędem). Naprawione w `generateTsTypeFromFields`, `generateFlatTsTypeFromFields`, `generateInlineBlockTypeString`, object/blocks variants oraz form fields generator.',
35
+ 'Shop order detail: `bg-red-50` / `bg-green-50` zamienione na tokenized `bg-destructive-bg` / `bg-success-bg` z `role="alert"`/`role="status"`. Status form ma teraz SaveIndicator + FormErrorSummary.',
36
+ 'Shipping-method form: `<fieldset>/<legend>` na payment methods (poprawne grouping dla SR), `aria-live="polite"` na carrier config panel, FormErrorSummary zamiast `bg-red-50` div, `text-amber-700` zamienione na `text-warning` token.',
37
+ 'Coupons list: delete dialog A11y (focus trap, ESC, return focus) zamiast browser `confirm()`.',
38
+ 'Form submissions bulk delete: AlertDialog zamiast `confirm()`, plural PL forms ("3 zgłoszenia" vs "5 zgłoszeń").',
39
+ 'Dashboard delete-orphaned: AlertDialog z liczbą wpisów w description zamiast `confirm()`.',
40
+ '`pnpm demo` seed (`scripts/seed-demo.ts`) — twarda bramka walidacji: każdy `entry_version` przed surowym INSERT-em przechodzi przez realny schemat CMS (`generateZodSchemaFromFields(..., { localized: false })` — ten sam oracle co admin/publish). Niezgodne dane przerywają seed z czytelnym `collection/lang/entry: path: message`. Dane demo doprowadzone do zgodności: blog-post `title` bez nierealnego `minLength: 50` (+ usunięte śmieciowe debugowe pole `test`/`dupa` z kolekcji), media jako UUID-string zamiast `{id,url,alt}` (cover/thumbnail/image/logo/favicon/photo), daty pełne ISO, dołożone wymagane obiekty `seo`, `url` jako obiekt, bloki z `_slug` zamiast `_type`, `localizedTags`/`links` w kształcie schematu.',
41
+ 'Lista entries w kolekcji pokazuje "(bez tytułu)"/"(untitled)" zamiast surowego UUID gdy wpis nie ma tytułu — spójne z breadcrumbem na ekranie edycji. Wspólny helper `getCollectionEntryDisplayLabel` (`src/lib/admin/utils/entryLabel.ts`) używany przez list/grid i breadcrumb (fallback wykrywany po `language === null`, odporny na tytuł = id).',
42
+ 'Slug first-class: podgląd URL wpisu w adminie (`collection-entries.svelte`) odhardkodowany z wymogu pola typu `seo` — kolekcja ze standalone polem slug + skonfigurowanym `slugField` pokazuje URL; bez `slugField`/`pathTemplate` świadomie brak `_url` (np. kolekcje atrybutów), bez błędu. Wydzielony czysty resolver `slugPath.ts` (bez `getCMS`) współdzielony przez admin client i server.'
43
+ ],
44
+ breakingChanges: [
45
+ '**Entry autosave usunięty** — wcześniej 30s timeout + Ctrl/Cmd+S manual. Teraz tylko manual save (button "Zapisz szkic" zawsze widoczny + Ctrl/Cmd+S shortcut). Powód: stara impl mogła w niejasnych sytuacjach modyfikować published data; bezpieczniejsza re-introdukcja z draft-only guard zaplanowana w ROADMAP backlog. Migracja: użytkownicy muszą explicit zapisać przed nawigacją (jest `beforeunload` guard).',
46
+ 'EntryHeader props: nowy `lastSavedAt?: Date | null` (default `null`). Niezbędny dla SaveIndicator "Zapisano X temu". Konsumenci niestandardowi (entry-header-story-demo, custom integracje) muszą propagować z entry.svelte.',
47
+ 'EntryForm props: usunięty hardcoded podział Content/SEO (`tabFilter` nigdy nie trafił do release\'u — zastąpiony węzłem layoutu `tabs`). Grupowanie w taby = teraz config `layout[]`. Kolekcje bez `tabs` bez zmian; SEO w demo blog-post wyrażone jako `tab`. Dodany prop `errorPaths?: Set<string>` (EntryForm/LayoutRenderer) do wskaźnika błędu na tabie.',
48
+ '`coupon-form.svelte` przepisane od zera na sveltekit-superforms — props identyczne (`initial`, `submitLabel`, `onSubmit`, `onCancel`), ale wewnętrzny mechanizm walidacji zmieniony (zod schema zamiast hand-rolled). Call-sites bez zmian.'
49
+ ],
50
+ notes: 'V1-DECISIONS.md sekcja "Sesja 8" zawiera pełny audit decyzji + grep gates. Pełna superForm migracja shipping-method-form odłożona do backlogu (skomplikowany state: net/gross toggle + dynamic carrier + multi-lang record). Storybook entry-page story też w backlogu (wymaga mock setRemotes + RawEntry fixtures — robione w S10 a11y sweep). VoiceOver smoke test (manual user-delegated) — checklist w `V1-UI-AUDIT.md` sekcja "VoiceOver smoke (manual)". Kompatybilność z istniejącymi danymi: pełna (zero schema changes; coupon zachowuje stary kontrakt API).'
51
+ };
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,19 @@
1
+ export const update = {
2
+ version: '0.26.1',
3
+ date: '2026-05-28',
4
+ description: 'Nowy typ pola `icon` + IconSetPlugin (zestaw ikon dostarczany przez aplikację, dialog z gridem + search). Scaffolder fix + DataTable polish: scaffold admin używa publicznych entry points; pierwsza kolumna tabeli bez sticky-first; checkbox visual wraca do 16 px z transparent hit-area 24×24 (WCAG 2.5.5).',
5
+ features: [
6
+ 'Nowy typ pola `icon` (`IconField` @public, `src/lib/types/fields.ts`) — kompaktowy kafelek 96×96 w formularzu (ikona + nazwa, X do wyczyszczenia) otwiera dialog z gridem responsive 3–6 kolumn, wyszukiwarką (filtruje po `key`, lokalizowanym `label`, opcjonalnych `keywords`) i przyciskami Anuluj / OK. Klik na kafelku w dialogu zaznacza, OK zatwierdza; dwuklik = szybki zapis. Stan "missing" (wartość w DB nieobecna w bibliotece) pokazuje placeholder + przyjazne ostrzeżenie zamiast resetować wartość — zgodne z ToV "informuj, nie strasz". i18n PL/EN, `role="listbox"`/`role="option"`/`aria-selected`, LiveRegion z liczbą wyników.',
7
+ '`IconSetPlugin` / `IconDefinition` (`@public`, `src/lib/types/plugins.ts`) — nowy rodzaj pluginu rejestrujący nazwany zestaw ikon: `{ slug, type: \'icon-set\', icons: Record<key, { component, label: Localized, keywords?: string[] }> }`. Komponenty Svelte (eager import, tree-shakable — np. `Brain` z `@lucide/svelte`) ŻYJĄ w pluginie, bo `cms.config.ts` jest server-only i Zod-validated. `Plugin = PluginConfig | IconSetPlugin` + `isIconSetPlugin` type guard.',
8
+ '`buildIconSetMap(...plugins)` (`includio-cms/admin`) + nowy prop `iconSets?: Map<string, IconSetPlugin>` na `AdminLayout` — konsumencki `+layout.svelte` admina przekazuje plugin instancje na klienta (funkcji komponentów nie da się serializować przez SvelteKit data). `getIconSets()` / `resolveIconSet(slug?)` w `includio-cms/admin` — runtime lookup używany przez `icon-field.svelte` przez Svelte context.',
9
+ '`CMS.iconSets: Map<slug, IconSetPlugin>` agregowane przy starcie z `cms.config.plugins` przez `isIconSetPlugin` guard; duplikat slug → throw. Generator TS mapuje `icon` → `string` w wygenerowanym `types.ts`; Zod runtime → `z.string()` (analogicznie do `slug` field).'
10
+ ],
11
+ fixes: [
12
+ '`scaffold admin` generuje `src/routes/admin/api/[...path]/+server.ts` z `import { createAdminApiHandler } from \'includio-cms/sveltekit/server\'`. Wcześniej generowany import `includio-cms/admin/api/handler` rzucał Vite `Missing "./admin/api/handler" specifier in "includio-cms" package` przy starcie dev/build — `admin/api/handler` nie ma w `exports` (v1.0 frozen 18 ścieżek).',
13
+ '`scaffold admin` generuje `src/routes/admin/api/rest/[...restPath]/+server.ts` z `import { createRestApiHandler } from \'includio-cms/sveltekit/server\'` (analogicznie — był broken w 0.20.0+ ale scaffolder nie zaktualizowany).',
14
+ '`includio-cms/sveltekit/server` re-eksportuje teraz `createAdminApiHandler` (obok istniejącego `createRestApiHandler`).',
15
+ '`DataTable`: revert sticky-first column (z 0.26.0 / S10c) — pierwsza kolumna scrolluje razem z resztą, bez wyróżnienia kolorystycznego. Usunięty prop `stickyFirstColumn` i powiązany CSS.',
16
+ '`Checkbox` primitive: visual wraca do 16×16 px (z 24×24 w 0.26.0 / S10b), klikalny obszar utrzymany na ≥24×24 przez transparent `::after` pseudo-element (`after:-inset-1`) — WCAG 2.5.5 target-size AA dalej spełniony, lepszy wygląd zgodny z proporcjami pól tekstowych.'
17
+ ],
18
+ breakingChanges: []
19
+ };
@@ -0,0 +1,2 @@
1
+ import type { CmsUpdate } from '../index.js';
2
+ export declare const update: CmsUpdate;
@@ -0,0 +1,50 @@
1
+ export const update = {
2
+ version: '0.27.0',
3
+ date: '2026-05-29',
4
+ description: 'Shop platformowy — Fazy 1 + 2 + 3 + 4 + 5: schema-driven `variantAttributes` (typed `defineShop` schema, Zod walidacja, GIN index helper, ts-gen typed `variant.attributes`) + admin renderer per attribute type + `variantLabel.template` interpolacja z admin auto-prefill nazwy wariantu + `variantExpiry` opt-in (storefront filter, cart/checkout guard, admin "Zakończony" badge) + `paymentPolicy` A-lite (deposit + balance + signed token + refund per kind + admin balance link + per-kind refund dialog). Faza 6 envet pilot (consumer-side end-to-end QA: szkolenia variants + deposit flow + balance link — nie wymaga zmian w core). Post-Faza-5 hot-fixy uwzględnione w tym wydaniu (single release, nie patch): datetime walidator z offsetem, auto-detect deposit kind przy admin manual mark-paid, admin paymentPolicy UI + preserve-on-undefined w upsert. Additive only — żadnych breaking zmian. Plan: `/Users/patryk/.claude/plans/implementacja-ariacms-0-27-partitioned-kahn.md`.',
5
+ features: [
6
+ '`defineShop({ variantAttributes })` (`includio-cms/shop`) — typed schema dla atrybutów wariantu: `text | number | datetime | select | boolean | image | entry | slug` z polami `{ type, label, required?, indexable?, options?, entryCollection? }`. Zostaje jedno źródło prawdy: napędza Zod walidację, ts-gen typings, GIN index utility oraz renderer admin form.',
7
+ '`VariantAttribute` + per-type interfejsy (`VariantAttributeText/Number/Datetime/Select/Boolean/Image/Entry/Slug`) wyeksportowane jako `@public` z `includio-cms/shop`.',
8
+ '`InvalidVariantAttributesError` (code `INVALID_VARIANT_ATTRIBUTES`) — rzucany przez `upsertShopData` gdy `variant.attributes` nie pasują do schemy. Eksport publiczny z `includio-cms/shop`.',
9
+ '`runtime/types.ts` — pole `shop` w entry typings generuje teraz typed `variant.attributes` (`{ city: string; startsAt: string; online?: boolean; level: \'basic\' | \'advanced\' }`) zamiast `Record<string, string>` gdy `defineShop` ma `variantAttributes`. Brak `variantAttributes` = poprzednie `Record<string, string> | null` (backwards-compatible).',
10
+ '`createVariantAttributeIndexes(attrs)` w `$lib/db-postgres/schema/shop/productVariant.ts` — zwraca `CREATE INDEX IF NOT EXISTS shop_variant_attr_<key>_gin_idx ON shop_product_variants USING gin ((attributes->\'<key>\'))` DDL per atrybut z `indexable: true`. Wymusza safe SQL identifier dla klucza (`[A-Za-z][A-Za-z0-9_]*`).',
11
+ '`applyVariantAttributeIndexes(shop, db)` w `$lib/shop/server/init.ts` — idempotentny serialized applier indeksów GIN. `initCMS()` odpala go automatycznie fire-and-forget gdy shop jest skonfigurowany (zero ręcznych hooków u konsumenta). `cms.shopInitPromise` wystawiony jako `@internal` punkt synchronizacji dla testów.',
12
+ '`VariantAttributeRenderer.svelte` (NEW, `$lib/admin/components/variant-form/`) — switch po `attr.type`, deleguje do field components z `$lib/admin/components/fields/` (text/number/datetime/select/boolean) lub renderuje prosty Input dla slug/image/entry (pełny picker UI dla image/entry defer post-1.0).',
13
+ '`shop-field.svelte` (admin variant editor) — variant row pokazuje siatkę atrybutów (responsywna 1/2-col) ponad legacy polami (nazwa/SKU/delta/stock) gdy `shopConfig.variantAttributes` ma wpisy. Brak attrs = legacy behavior bez zmian. Hydratacja + save propaguje `variant.attributes` jako `Record<string, unknown>`.',
14
+ '`getShopConfig` remote (admin) — response zawiera teraz `variantAttributes` z `ResolvedShopConfig`, dzięki czemu `shop-field` może lokalnie renderować typed atrybuty bez dodatkowego round-tripa.',
15
+ '`upsertShopDataForEntry` (admin remote command) — input schema dla `variant.attributes` zluzowany do `z.record(z.string(), z.unknown())`; typed shape egzekwowany serwerowo przez `validateVariantAttributes` (jedno źródło prawdy).',
16
+ '`defineShop({ variantLabel: { template } })` (`includio-cms/shop`) — opcjonalny szablon nazwy wariantu w składni `{key|filter:arg}`. Filtry: `date` (long/medium/short), `currency` (kod waluty, domyślnie PLN), `uppercase`. Nieznany klucz → pusty string + dev warn; nieznany filtr → passthrough; malformed template → surowy template + warn.',
17
+ '`interpolateTemplate(template, vars, locale)` (`includio-cms/shop`, `@public`) — silnik interpolacji wykorzystywany wewnętrznie przez admin do pre-fill `variant.name`, dostępny też publicznie dla konsumentów (np. storefront-side derived labels).',
18
+ '`VariantLabelConfig` eksportowany jako `@public` typ z `includio-cms/shop`.',
19
+ '`getShopConfig` remote response zawiera teraz `variantLabel: shop.variantLabel ?? null`.',
20
+ '`shop-field.svelte` (admin variant editor) — gdy `variantLabel.template` zdefiniowany, pole `Nazwa` auto-wypełnia się z interpolacji `variantAttributes` (locale `pl` w 0.27; multi-locale defer post-1.0). Per-variant dirty flag: po hydratacji z niepustym `name` lock = aktywny (nie nadpisujemy editor decision); ręczna edycja w polu `Nazwa` (`oninput`) trwale wyłącza pre-fill dla wariantu w bieżącej sesji.',
21
+ '`defineShop({ variantExpiry: { source, offsetDays } })` (`includio-cms/shop`, `@public`) — opt-in filtr wariantów time-bound (events, courses, szkolenia). `source` = klucz atrybutu typu `datetime`; `offsetDays` przesuwa cut-off (`0` = expire dokładnie gdy datetime hits now, `1` = jeden dzień grace, `-1` = jeden dzień przed). Globalny config — brak per-product override.',
22
+ '`isVariantExpired(variant, config, now?)` + `filterUpcoming(variants, config, now?)` (`includio-cms/shop`, `@public`) — fail-open helpers: `null` config / brak source attribute / non-string / malformed datetime → variant traktowany jako upcoming (z `console.warn` dla malformed w dev). `filterUpcoming` zachowuje order; sort source-asc realizowany na poziomie HTTP / admin osobno.',
23
+ '`VariantExpiredError` (code `VARIANT_EXPIRED`, `variantId`) — eksport `@public`. Rzucany przez `createOrderFromCart` w `server/orders.ts` przed stock reservation (idempotent guard).',
24
+ '`POST /api/shop/cart` (add) — preflight `VARIANT_EXPIRED` guard: kiedy `variantExpiry` jest skonfigurowany i variant ma expired source attribute, response 400 JSON `{ error: "VARIANT_EXPIRED", variantId }` bez aktualizacji cart cookie.',
25
+ '`createUpcomingVariantsHandler` (`includio-cms/shop/http`, `@experimental`) — nowy GET endpoint `/api/shop/products/[id]/variants/upcoming`. Zwraca `{ items: VariantPublic[] }` przefiltrowanych przez `filterUpcoming` + posortowanych ascendingly po `attributes.<source>` (Date.parse) gdy `variantExpiry` ustawiony, w insertion order gdy brak.',
26
+ '`client.products.listUpcoming(productId)` + `VariantPublic` (`includio-cms/shop/client`, `@public`) — nowy namespace SDK. Zwraca upcoming variants per produkt; zero-config passthrough kiedy `variantExpiry` nieustawiony.',
27
+ '`getShopConfig` remote (admin) — response zawiera teraz `variantExpiry: shop.variantExpiry ?? null`.',
28
+ '`shop-field.svelte` (admin variant editor) — gdy `variantExpiry` skonfigurowany: per-variant badge "Zakończony" (greyed-out, `bg-muted text-muted-foreground` z border), checkbox filter "Pokaż zakończone (N)" w toolbarze wariantów (default off, ukryty gdy zero expired), opacity-60 na expired row, sort wariantów po source ASC (closer terms first). Brak config = zero zmian w UI.',
29
+ '`PaymentPolicy` + `DepositAmount` + `PartialPayment` (`includio-cms/shop`, `@public`) — per-product policy `{ type: "full" } | { type: "deposit", depositAmount: { type: "percent" | "amount", value } }` ustawiana w `ShopFieldData.paymentPolicy`. Cart deposit-aware: order zachowuje pełen `totalGross`, `partialPayment.balanceAmount` = co zostaje do dopłaty, `balanceOwed = true` po zapłaconym depozycie. Walidacja: percent w (0, 100], amount = positive integer (minor units). `validatePaymentPolicy` wymusza to przy upsert produktu.',
30
+ '`resolvePaymentAmount(policy, lineGross)` + `computeDepositAmount(spec, base)` (`$lib/shop/server/payment-policy.ts`, server-internal — nie eksportowane z publicznego entry pointu) — silnik resolver per linia. `percent` flooruje `base * value / 100`; `amount` klampuje do `base`. Null / `full` policy → pełen `lineGross`, `kind = "full"`, balance 0. 15 spec testów + 10 integration.',
31
+ '`MixedPaymentPolicyError` (code `MIXED_PAYMENT_POLICY`) — `createOrderFromCart` odrzuca cart z więcej niż jednym produktem gdy którykolwiek z nich ma deposit policy. Decyzja: deposit orders span ONE product (refund-per-kind + balance link unambiguous). Cart all-`full` mixed-product nadal działa.',
32
+ '`shop_orders.partial_payment jsonb + balance_owed boolean` — nowe kolumny. `shop_payments.kind enum ("full" | "deposit" | "balance")` z default `"full"` — back-compat dla istniejących płatności. `shop_products.payment_policy jsonb` przechowuje per-product policy.',
33
+ '`shop_payments` zaczyna być aktywnie wypełniane: `checkout-handler` po `adapter.createPayment` insert payment row z odpowiednim `kind` (full lub deposit) + `providerRef`. Webhook handler matchuje (provider, providerRef) → branchuje per kind: `full`/`deposit` przepuszcza `updateOrderStatus(paid, paymentKind)`, `balance` bypasses status flow i woła `markBalancePaid` (stock przy depozycie już permanentnie potwierdzony).',
34
+ '`createOrderFromCart` CreateOrderResult: nowe pola `amountToPay` (deposit-aware kwota do pobrania na checkout) + `paymentKind` (`"full" | "deposit"`). `checkout-handler` przekazuje `amountToPay` do adaptera przez OrderRef.totalGross override — adaptery PayU/Stripe/manual działają bez zmian.',
35
+ '`generateBalanceToken` + `verifyBalanceToken` (`includio-cms/shop/server`, `@internal`) — HMAC-SHA256 nad JSON payload `{orderId, type: "balance"}`, encoding `base64url(payload).base64url(sig)`. Deterministic per (orderId, secret) — brak rotation, brak exp, server-side invalidation przez `order.balanceOwed === true`. Secret = `INCLUDIO_BALANCE_TOKEN_SECRET` env (≥16 chars, hard-fail przy brak gdy deposit policy aktywne). 8 spec testów (roundtrip, tampered, wrong orderId/secret, malformed, deterministic).',
36
+ '`createBalanceSession(orderNumber, token, ctx?)` (`includio-cms/shop/server`, `@experimental`) — customer flow: verify token + `balanceOwed`, reuse `order.paymentMethod` adapter, call `adapter.createPayment` z `amount = partialPayment.balanceAmount`, insert payment row `kind: "balance"`, mirror providerRef na order. `markBalancePaid` (idempotent) clears balanceOwed + bumps `paidAmount` do `totalGross`.',
37
+ '`createBalanceHandler` (`includio-cms/shop/http`, `@experimental`) — GET `/api/shop/orders/[number]/balance?token=...` zwraca minimalny widok ({orderNumber, currency, totalGross, amountToPay, paidAmount, paymentMethod, language}); POST inicjuje payment session ({status, redirectUrl, requiresPaymentRedirect}). 403 przy złym tokenie lub wyczyszczonym `balanceOwed`.',
38
+ '`client.orders.payBalance(orderNumber, token)` + `PayBalanceResult` (`includio-cms/shop/client`, `@public` / `@experimental`) — REST wrapper dla customer flow dopłaty.',
39
+ '`refundOrder({ kind?, releaseStock?, ... })` (`includio-cms/shop/server`) — per-kind routing. `kind: "deposit" | "balance" | "full"` matchuje konkretny payment row; kwota cap = `row.amount - prior refunds`. Domyślnie `kind` = `"full"` z heurystyką: pojedynczy paid row → użyj go, multi-row → wybierz `full`, brak rows → legacy fallback do `order.paymentProviderRef`. Refund row link do `paymentId` dla per-row audit. `getCollectedAmount` decyduje o transition do `refunded` (sum paid rows lub `totalGross` dla legacy). `releaseStock: true` (opt-in) reinkrementuje variant.stock per order item. 6 integration spec.',
40
+ '`refundOrderCmd` (admin remote) dostaje `kind` + `releaseStock` w schemacie input. `refund-dialog.svelte` — radio "Która płatność?" (deposit/balance) widoczne gdy `hasPartial`, balance radio disabled gdy `balanceOwed=true` (nic do refund), checkbox "Zwolnij stan magazynowy".',
41
+ '`generateBalanceLinkForOrder` (admin remote) — auth-protected, requires `INCLUDIO_BALANCE_TOKEN_SECRET`, builds URL z configured `orderViewUrl` + `?balance=1&balanceToken=...`. Zwraca `{ success, url, balanceAmount }`.',
42
+ '`shop-order-detail-page.svelte` — nowa sekcja "Płatność dzielona" widoczna gdy `order.partialPayment != null`: Plum badge "Czeka na dopłatę" przy `balanceOwed=true` / success badge "Opłacone w całości" po dopłacie, paid/owed/total breakdown z `formatCentsPrice`, button "Wyślij link do dopłaty" generuje token + kopiuje URL do clipboard, wyświetla URL w `<code>` blocku jako fallback dla "skopiuj ręcznie".'
43
+ ],
44
+ fixes: [
45
+ '`variant-attributes.ts` — `datetime` walidator używa teraz `z.string().datetime({ offset: true })`: akceptuje pełen ISO 8601 (`Z` UTC z admin `DatetimeField.toISOString()` ORAZ offset `+02:00` z importów/seedów/zewnętrznych systemów). Wcześniej tryb default odrzucał offset, blokując offsetowe wartości atrybutów.',
46
+ '`updateOrderStatus` (`server/orders.ts`) — admin manual mark-paid auto-detektuje `deposit` kind: gdy `order.partialPayment.kind === "deposit"` + `!balanceOwed` + `paidAt == null`, `effectiveKind = "deposit"`. Wcześniej `updateOrderStatusCmd` (w przeciwieństwie do webhooka) nie przekazywał `paymentKind`, więc admin oznaczając deposit order jako paid pomijał deposit logic (`paidAmount` nie był bumpowany, `balanceOwed` nie był ustawiany). Pozwala testować pełny deposit flow przez manual adapter bez sandbox PayU; webhook bez zmian (zawsze pass explicit kind).',
47
+ 'admin `paymentPolicy` — trzy bugi powodujące kasowanie deposit policy przy każdym save naprawione razem: (1) `upsertShopData` (`server/shop-data.ts`) — `undefined` zachowuje istniejącą policy, tylko `null` kasuje (było `?? null` nadpisujące przy każdym upsert innego pola); (2) `shopDataInputSchema` (admin remote) — dodany discriminated union `paymentPolicy` (full/deposit + percent/amount), wcześniej command odrzucał pole; (3) `shop-field.svelte` — sekcja "Zasady płatności" (radio full/deposit + warunkowe inputy percent/amount + live preview kwoty depozytu), wcześniej brak UI do ustawienia deposit.'
48
+ ],
49
+ breakingChanges: []
50
+ };
@@ -59,6 +59,9 @@ import { update as update0220 } from './0.22.0/index.js';
59
59
  import { update as update0230 } from './0.23.0/index.js';
60
60
  import { update as update0240 } from './0.24.0/index.js';
61
61
  import { update as update0250 } from './0.25.0/index.js';
62
+ import { update as update0260 } from './0.26.0/index.js';
63
+ import { update as update0261 } from './0.26.1/index.js';
64
+ import { update as update0270 } from './0.27.0/index.js';
62
65
  export const updates = [
63
66
  update0065,
64
67
  update0066,
@@ -120,7 +123,10 @@ export const updates = [
120
123
  update0220,
121
124
  update0230,
122
125
  update0240,
123
- update0250
126
+ update0250,
127
+ update0260,
128
+ update0261,
129
+ update0270
124
130
  ];
125
131
  export const getUpdatesFrom = (fromVersion) => {
126
132
  const fromParts = fromVersion.split('.').map(Number);