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
@@ -1,50 +1,21 @@
1
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
- */
2
+ import { getSlugFromEntryData, resolveSlugPath, applyPathTemplate } from '../../fields/slugPath.js';
3
+ export { getSlugFromEntryData } from '../../fields/slugPath.js';
4
+ /** Get dot-path to slug field for a collection. Falls back to 'seo.slug'. */
7
5
  export function getEntrySlugPath(configSlug) {
8
6
  try {
9
- const config = getCMS().getBySlug(configSlug);
10
- return config.slugField || DEFAULT_SLUG_PATH;
7
+ return resolveSlugPath(getCMS().getBySlug(configSlug).slugField);
11
8
  }
12
9
  catch {
13
- return DEFAULT_SLUG_PATH;
10
+ return resolveSlugPath(undefined);
14
11
  }
15
12
  }
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
- }
35
- /**
36
- * Build full entry path using collection's pathTemplate.
37
- * If no template configured, returns the raw slug.
38
- */
13
+ /** Build full entry path using collection's pathTemplate. */
39
14
  export function getEntryPath(configSlug, slug) {
40
15
  try {
41
- const config = getCMS().getBySlug(configSlug);
42
- if (config.pathTemplate) {
43
- return config.pathTemplate.replace('{slug}', slug);
44
- }
16
+ return applyPathTemplate(getCMS().getBySlug(configSlug).pathTemplate, slug);
45
17
  }
46
18
  catch {
47
- // Config not found — fall through
19
+ return applyPathTemplate(undefined, slug);
48
20
  }
49
- return slug;
50
21
  }
@@ -1,6 +1,8 @@
1
1
  import type { Field } from '../../../types/fields.js';
2
2
  import type { CustomFieldDefinition } from '../../../types/plugins.js';
3
+ import type { VariantAttribute } from '../../../shop/types.js';
3
4
  export declare function setGeneratorCustomFields(customFields: Map<string, CustomFieldDefinition>): void;
5
+ export declare function setGeneratorShopVariantAttributes(attrs: Record<string, VariantAttribute>): void;
4
6
  export declare function generateTsTypeFromFields(fields: Field[]): string;
5
7
  export declare function generateFlatTsTypeFromFields(fields: Field[]): string;
6
8
  export interface InlineBlockTypeDef {
@@ -1,11 +1,44 @@
1
- import { toPascalCase } from './utils.js';
1
+ import { toPascalCase, quoteKey } from './utils.js';
2
+ import { buildSeoTsType } from '../../fields/seoFieldDescriptor.js';
2
3
  let _customFields = new Map();
4
+ let _shopVariantAttributes = {};
3
5
  function isGuaranteed(f) {
4
6
  return !!(f.required || f.type === 'seo' || f.defaultValue !== undefined);
5
7
  }
6
8
  export function setGeneratorCustomFields(customFields) {
7
9
  _customFields = customFields;
8
10
  }
11
+ export function setGeneratorShopVariantAttributes(attrs) {
12
+ _shopVariantAttributes = attrs;
13
+ }
14
+ function variantAttributeTsType(attr) {
15
+ switch (attr.type) {
16
+ case 'text':
17
+ case 'datetime':
18
+ case 'image':
19
+ case 'entry':
20
+ case 'slug':
21
+ return 'string';
22
+ case 'number':
23
+ return 'number';
24
+ case 'boolean':
25
+ return 'boolean';
26
+ case 'select':
27
+ return attr.options.map((o) => `'${o.value}'`).join(' | ');
28
+ }
29
+ }
30
+ function buildShopAttributesType() {
31
+ const entries = Object.entries(_shopVariantAttributes);
32
+ if (entries.length === 0)
33
+ return 'Record<string, string> | null';
34
+ const fields = entries
35
+ .map(([key, attr]) => {
36
+ const opt = attr.required ? '' : '?';
37
+ return `${quoteKey(key)}${opt}: ${variantAttributeTsType(attr)}`;
38
+ })
39
+ .join('; ');
40
+ return `{ ${fields} }`;
41
+ }
9
42
  function getFieldTypeAsString(field) {
10
43
  switch (field.type) {
11
44
  case 'text':
@@ -43,7 +76,7 @@ function getFieldTypeAsString(field) {
43
76
  case 'object':
44
77
  return `{
45
78
  slug: '${field.slug}';
46
- data: {${field.fields.map((f) => ` ${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
79
+ data: {${field.fields.map((f) => ` ${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
47
80
  }`;
48
81
  case 'blocks':
49
82
  return `
@@ -52,7 +85,7 @@ function getFieldTypeAsString(field) {
52
85
  .map((f) => `
53
86
  {
54
87
  slug: '${f.slug}';
55
- data: {${f.fields.map((f) => ` ${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
88
+ data: {${f.fields.map((f) => ` ${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')}};
56
89
  }
57
90
  `)
58
91
  .join(' | ')}
@@ -67,17 +100,10 @@ function getFieldTypeAsString(field) {
67
100
  return 'UrlFieldData[]';
68
101
  }
69
102
  case 'slug':
103
+ case 'icon':
70
104
  return 'string';
71
105
  case 'seo':
72
- return `{
73
- slug: string;
74
- canonicalUrl?: string;
75
- title: string;
76
- description?: string;
77
- ogImage?: string;
78
- keywords?: string;
79
- customCode?: string;
80
- }`;
106
+ return buildSeoTsType();
81
107
  case 'shop':
82
108
  return `{
83
109
  basePrice: number;
@@ -89,7 +115,7 @@ function getFieldTypeAsString(field) {
89
115
  name: Record<string, string> | null;
90
116
  priceDelta: number;
91
117
  stock: number | null;
92
- attributes: Record<string, string> | null;
118
+ attributes: ${buildShopAttributesType()};
93
119
  }>;
94
120
  } | null`;
95
121
  case 'url': {
@@ -110,12 +136,12 @@ function getFieldTypeAsString(field) {
110
136
  }
111
137
  export function generateTsTypeFromFields(fields) {
112
138
  return `{
113
- ${fields.map((f) => ` ${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')};
139
+ ${fields.map((f) => ` ${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFieldTypeAsString(f)}`).join(';\n')};
114
140
  }`;
115
141
  }
116
142
  export function generateFlatTsTypeFromFields(fields) {
117
143
  return `{
118
- ${fields.map((f) => ` ${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
144
+ ${fields.map((f) => ` ${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
119
145
  }`;
120
146
  }
121
147
  function getFlatFieldTypeAsString(field) {
@@ -133,7 +159,7 @@ function getFlatFieldTypeAsString(field) {
133
159
  case 'object':
134
160
  return `{
135
161
  _slug: '${field.slug}';
136
- ${field.fields.map((f) => `${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
162
+ ${field.fields.map((f) => `${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
137
163
  }`;
138
164
  case 'blocks':
139
165
  return `
@@ -142,7 +168,7 @@ function getFlatFieldTypeAsString(field) {
142
168
  .map((f) => `
143
169
  {
144
170
  _slug: '${f.slug}';
145
- ${f.fields.map((f) => `${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
171
+ ${f.fields.map((f) => `${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
146
172
  }
147
173
  `)
148
174
  .join(' | ')}
@@ -153,6 +179,6 @@ function getFlatFieldTypeAsString(field) {
153
179
  }
154
180
  export function generateInlineBlockTypeString(block) {
155
181
  return `{
156
- ${block.fields.map((f) => ` ${f.slug}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
182
+ ${block.fields.map((f) => ` ${quoteKey(f.slug)}${isGuaranteed(f) ? '' : '?'}: ${getFlatFieldTypeAsString(f)}`).join(';\n')};
157
183
  }`;
158
184
  }
@@ -1,3 +1,4 @@
1
+ import { quoteKey } from './utils.js';
1
2
  function getFormFieldTypeAsString(field) {
2
3
  switch (field.type) {
3
4
  case 'text':
@@ -15,6 +16,6 @@ function getFormFieldTypeAsString(field) {
15
16
  }
16
17
  export function generateTsTypeFromFormFields(fields) {
17
18
  return `{
18
- ${fields.map((f) => ` ${f.slug}: ${getFormFieldTypeAsString(f)}`).join(';\n')};
19
+ ${fields.map((f) => ` ${quoteKey(f.slug)}: ${getFormFieldTypeAsString(f)}`).join(';\n')};
19
20
  }`;
20
21
  }
@@ -1,9 +1,9 @@
1
1
  import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { generateTsTypeFromFields, generateFlatTsTypeFromFields, generateInlineBlockTypeString, setGeneratorCustomFields } from './fields.js';
3
+ import { generateTsTypeFromFields, generateFlatTsTypeFromFields, generateInlineBlockTypeString, setGeneratorCustomFields, setGeneratorShopVariantAttributes } from './fields.js';
4
4
  import { generateTsTypeFromFormFields } from './formFields.js';
5
5
  import { generateZodSchemaStringFromFormFieldsAsString } from './formFieldSchemaToString.js';
6
- import { toPascalCase } from './utils.js';
6
+ import { toPascalCase, quoteKey } from './utils.js';
7
7
  import { getFieldsFromConfig } from '../../fields/layoutUtils.js';
8
8
  function createCmsRuntimeDir() {
9
9
  const cmsDir = join(process.cwd(), 'src/lib/cms/runtime');
@@ -54,7 +54,7 @@ function generateTypesStringForRecords(type, records) {
54
54
  export type ${recordTypeString}EntryMap = {
55
55
  ${records
56
56
  .map((single) => {
57
- const key = single.slug.includes('-') ? `'${single.slug}'` : single.slug;
57
+ const key = quoteKey(single.slug);
58
58
  return `${key}: ${toPascalCase(single.slug)}`;
59
59
  })
60
60
  .join(';\n')}
@@ -82,7 +82,7 @@ function generateTypesStringForForms(records) {
82
82
  export type ${recordTypeString}EntryMap = {
83
83
  ${records
84
84
  .map((single) => {
85
- const key = single.slug.includes('-') ? `'${single.slug}'` : single.slug;
85
+ const key = quoteKey(single.slug);
86
86
  return `${key}: ${toPascalCase(single.slug)}`;
87
87
  })
88
88
  .join(';\n')}
@@ -163,7 +163,7 @@ function generateTypes(config) {
163
163
  const mapName = toPascalCase(record.slug) + toPascalCase(field.slug) + 'BlockDataMap';
164
164
  code += `\nexport type ${mapName} = {\n`;
165
165
  for (const block of cf.inlineBlocks) {
166
- const key = block.slug.includes('-') ? `'${block.slug}'` : block.slug;
166
+ const key = quoteKey(block.slug);
167
167
  code += ` ${key}: ${toPascalCase(block.slug)}BlockData;\n`;
168
168
  }
169
169
  code += `};\n`;
@@ -312,6 +312,7 @@ export function generateRuntime(config) {
312
312
  }
313
313
  }
314
314
  setGeneratorCustomFields(customFields);
315
+ setGeneratorShopVariantAttributes(config.shop?.variantAttributes ?? {});
315
316
  createCmsRuntimeDir();
316
317
  generateTypes(config);
317
318
  generateAPI(config);
@@ -1 +1,2 @@
1
1
  export declare function toPascalCase(slug: string): string;
2
+ export declare function quoteKey(slug: string): string;
@@ -4,3 +4,7 @@ export function toPascalCase(slug) {
4
4
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
5
5
  .join('');
6
6
  }
7
+ const JS_IDENT_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
8
+ export function quoteKey(slug) {
9
+ return JS_IDENT_RE.test(slug) ? slug : `'${slug}'`;
10
+ }
@@ -1,4 +1,4 @@
1
- import type { OrderStatus } from '../../../shop/types.js';
1
+ import type { OrderStatus, PartialPayment } from '../../../shop/types.js';
2
2
  export declare const shopOrdersTable: import("drizzle-orm/pg-core/table", { with: { "resolution-mode": "require" } }).PgTableWithColumns<{
3
3
  name: "shop_orders";
4
4
  schema: undefined;
@@ -459,6 +459,42 @@ export declare const shopOrdersTable: import("drizzle-orm/pg-core/table", { with
459
459
  identity: undefined;
460
460
  generated: undefined;
461
461
  }, {}, {}>;
462
+ partialPayment: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
463
+ name: "partial_payment";
464
+ tableName: "shop_orders";
465
+ dataType: "json";
466
+ columnType: "PgJsonb";
467
+ data: PartialPayment | null;
468
+ driverParam: unknown;
469
+ notNull: false;
470
+ hasDefault: false;
471
+ isPrimaryKey: false;
472
+ isAutoincrement: false;
473
+ hasRuntimeDefault: false;
474
+ enumValues: undefined;
475
+ baseColumn: never;
476
+ identity: undefined;
477
+ generated: undefined;
478
+ }, {}, {
479
+ $type: PartialPayment | null;
480
+ }>;
481
+ balanceOwed: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
482
+ name: "balance_owed";
483
+ tableName: "shop_orders";
484
+ dataType: "boolean";
485
+ columnType: "PgBoolean";
486
+ data: boolean;
487
+ driverParam: boolean;
488
+ notNull: true;
489
+ hasDefault: true;
490
+ isPrimaryKey: false;
491
+ isAutoincrement: false;
492
+ hasRuntimeDefault: false;
493
+ enumValues: undefined;
494
+ baseColumn: never;
495
+ identity: undefined;
496
+ generated: undefined;
497
+ }, {}, {}>;
462
498
  createdAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
463
499
  name: "created_at";
464
500
  tableName: "shop_orders";
@@ -1,4 +1,4 @@
1
- import { integer, jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
1
+ import { boolean, integer, jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
2
2
  import { shopShippingMethodsTable } from './shippingMethod.js';
3
3
  export const shopOrdersTable = pgTable('shop_orders', {
4
4
  id: uuid('id').primaryKey().defaultRandom(),
@@ -29,6 +29,8 @@ export const shopOrdersTable = pgTable('shop_orders', {
29
29
  notes: text('notes'),
30
30
  language: text('language'),
31
31
  accessToken: uuid('access_token').defaultRandom().notNull(),
32
+ partialPayment: jsonb('partial_payment').$type(),
33
+ balanceOwed: boolean('balance_owed').default(false).notNull(),
32
34
  createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
33
35
  updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
34
36
  });
@@ -1,4 +1,7 @@
1
1
  export type ShopPaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded' | 'cancelled';
2
+ /** `kind` distinguishes full / deposit / balance payment rows under paymentPolicy A-lite. */
3
+ export declare const shopPaymentKindEnum: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgEnum<["full", "deposit", "balance"]>;
4
+ export type ShopPaymentKind = 'full' | 'deposit' | 'balance';
2
5
  export declare const shopPaymentsTable: import("drizzle-orm/pg-core/table", { with: { "resolution-mode": "require" } }).PgTableWithColumns<{
3
6
  name: "shop_payments";
4
7
  schema: undefined;
@@ -90,6 +93,23 @@ export declare const shopPaymentsTable: import("drizzle-orm/pg-core/table", { wi
90
93
  }, {}, {
91
94
  $type: ShopPaymentStatus;
92
95
  }>;
96
+ kind: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
97
+ name: "kind";
98
+ tableName: "shop_payments";
99
+ dataType: "string";
100
+ columnType: "PgEnumColumn";
101
+ data: "full" | "deposit" | "balance";
102
+ driverParam: string;
103
+ notNull: true;
104
+ hasDefault: true;
105
+ isPrimaryKey: false;
106
+ isAutoincrement: false;
107
+ hasRuntimeDefault: false;
108
+ enumValues: ["full", "deposit", "balance"];
109
+ baseColumn: never;
110
+ identity: undefined;
111
+ generated: undefined;
112
+ }, {}, {}>;
93
113
  amount: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
94
114
  name: "amount";
95
115
  tableName: "shop_payments";
@@ -1,5 +1,7 @@
1
- import { integer, jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
1
+ import { integer, jsonb, pgEnum, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
2
2
  import { shopOrdersTable } from './order.js';
3
+ /** `kind` distinguishes full / deposit / balance payment rows under paymentPolicy A-lite. */
4
+ export const shopPaymentKindEnum = pgEnum('shop_payment_kind', ['full', 'deposit', 'balance']);
3
5
  export const shopPaymentsTable = pgTable('shop_payments', {
4
6
  id: uuid('id').primaryKey().defaultRandom(),
5
7
  orderId: uuid('order_id')
@@ -8,6 +10,7 @@ export const shopPaymentsTable = pgTable('shop_payments', {
8
10
  provider: text('provider').notNull(),
9
11
  providerRef: text('provider_ref'),
10
12
  status: text('status').$type().notNull().default('pending'),
13
+ kind: shopPaymentKindEnum('kind').notNull().default('full'),
11
14
  amount: integer('amount').notNull(),
12
15
  currency: text('currency').notNull(),
13
16
  raw: jsonb('raw'),
@@ -1,3 +1,4 @@
1
+ import type { PaymentPolicy } from '../../../shop/types.js';
1
2
  export declare const shopProductsTable: import("drizzle-orm/pg-core/table", { with: { "resolution-mode": "require" } }).PgTableWithColumns<{
2
3
  name: "shop_products";
3
4
  schema: undefined;
@@ -121,6 +122,25 @@ export declare const shopProductsTable: import("drizzle-orm/pg-core/table", { wi
121
122
  identity: undefined;
122
123
  generated: undefined;
123
124
  }, {}, {}>;
125
+ paymentPolicy: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
126
+ name: "payment_policy";
127
+ tableName: "shop_products";
128
+ dataType: "json";
129
+ columnType: "PgJsonb";
130
+ data: PaymentPolicy | null;
131
+ driverParam: unknown;
132
+ notNull: false;
133
+ hasDefault: false;
134
+ isPrimaryKey: false;
135
+ isAutoincrement: false;
136
+ hasRuntimeDefault: false;
137
+ enumValues: undefined;
138
+ baseColumn: never;
139
+ identity: undefined;
140
+ generated: undefined;
141
+ }, {}, {
142
+ $type: PaymentPolicy | null;
143
+ }>;
124
144
  createdAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
125
145
  name: "created_at";
126
146
  tableName: "shop_products";
@@ -1,4 +1,4 @@
1
- import { boolean, integer, numeric, pgTable, timestamp, uuid } from 'drizzle-orm/pg-core';
1
+ import { boolean, integer, jsonb, numeric, pgTable, timestamp, uuid } from 'drizzle-orm/pg-core';
2
2
  import { entriesTable } from '../entry.js';
3
3
  export const shopProductsTable = pgTable('shop_products', {
4
4
  id: uuid('id').primaryKey().defaultRandom(),
@@ -13,6 +13,8 @@ export const shopProductsTable = pgTable('shop_products', {
13
13
  sortOrder: integer('sort_order'),
14
14
  // Null = alert disabled. When stock decrement crosses threshold, fire-and-forget admin email.
15
15
  lowStockThreshold: integer('low_stock_threshold'),
16
+ // Per-product payment policy. Null = full payment (legacy).
17
+ paymentPolicy: jsonb('payment_policy').$type(),
16
18
  createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
17
19
  updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
18
20
  });
@@ -1,3 +1,4 @@
1
+ import type { VariantAttribute } from '../../../shop/types.js';
1
2
  export declare const shopProductVariantsTable: import("drizzle-orm/pg-core/table", { with: { "resolution-mode": "require" } }).PgTableWithColumns<{
2
3
  name: "shop_product_variants";
3
4
  schema: undefined;
@@ -111,7 +112,7 @@ export declare const shopProductVariantsTable: import("drizzle-orm/pg-core/table
111
112
  tableName: "shop_product_variants";
112
113
  dataType: "json";
113
114
  columnType: "PgJsonb";
114
- data: Record<string, string>;
115
+ data: Record<string, unknown>;
115
116
  driverParam: unknown;
116
117
  notNull: false;
117
118
  hasDefault: false;
@@ -123,7 +124,7 @@ export declare const shopProductVariantsTable: import("drizzle-orm/pg-core/table
123
124
  identity: undefined;
124
125
  generated: undefined;
125
126
  }, {}, {
126
- $type: Record<string, string>;
127
+ $type: Record<string, unknown>;
127
128
  }>;
128
129
  sortOrder: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
129
130
  name: "sort_order";
@@ -162,3 +163,12 @@ export declare const shopProductVariantsTable: import("drizzle-orm/pg-core/table
162
163
  };
163
164
  dialect: "pg";
164
165
  }>;
166
+ /**
167
+ * Build `CREATE INDEX IF NOT EXISTS … USING gin ((attributes->'<key>'))` DDL
168
+ * statements — one per attribute marked `indexable: true`. Drizzle's static
169
+ * schema can't capture user-defined variantAttributes, so apply these at shop
170
+ * bootstrap (or via the migration in `src/lib/updates/0.27.0`).
171
+ * Index name pattern: `shop_variant_attr_<key>_gin_idx`.
172
+ * @internal
173
+ */
174
+ export declare function createVariantAttributeIndexes(attrs: Record<string, VariantAttribute>): string[];
@@ -14,3 +14,25 @@ export const shopProductVariantsTable = pgTable('shop_product_variants', {
14
14
  sortOrder: integer('sort_order'),
15
15
  createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull()
16
16
  });
17
+ const SAFE_KEY = /^[a-zA-Z][a-zA-Z0-9_]*$/;
18
+ /**
19
+ * Build `CREATE INDEX IF NOT EXISTS … USING gin ((attributes->'<key>'))` DDL
20
+ * statements — one per attribute marked `indexable: true`. Drizzle's static
21
+ * schema can't capture user-defined variantAttributes, so apply these at shop
22
+ * bootstrap (or via the migration in `src/lib/updates/0.27.0`).
23
+ * Index name pattern: `shop_variant_attr_<key>_gin_idx`.
24
+ * @internal
25
+ */
26
+ export function createVariantAttributeIndexes(attrs) {
27
+ const stmts = [];
28
+ for (const [key, attr] of Object.entries(attrs)) {
29
+ if (!attr.indexable)
30
+ continue;
31
+ if (!SAFE_KEY.test(key)) {
32
+ throw new Error(`variantAttribute key "${key}" is not a safe SQL identifier (allowed: [A-Za-z][A-Za-z0-9_]*).`);
33
+ }
34
+ stmts.push(`CREATE INDEX IF NOT EXISTS shop_variant_attr_${key}_gin_idx ` +
35
+ `ON shop_product_variants USING gin ((attributes->'${key}'))`);
36
+ }
37
+ return stmts;
38
+ }
@@ -5,6 +5,7 @@ export interface CartItemRef {
5
5
  }
6
6
  export interface CartLine extends CartItemRef {
7
7
  entryId: string;
8
+ productId: string;
8
9
  variantName: Record<string, string> | null;
9
10
  variantSku: string | null;
10
11
  productTitle: string | null;
@@ -114,6 +114,34 @@ export interface RetryPaymentResult {
114
114
  requiresPaymentRedirect: boolean;
115
115
  redirectUrl: string | null;
116
116
  }
117
+ /**
118
+ * Result of `orders.payBalance` — initiates a payment session for the
119
+ * outstanding balance on a deposit order.
120
+ * @public
121
+ */
122
+ export interface PayBalanceResult {
123
+ status: 'redirect' | 'manual' | 'error';
124
+ requiresPaymentRedirect: boolean;
125
+ redirectUrl: string | null;
126
+ }
127
+ /**
128
+ * Public-facing variant row returned by `client.products.listUpcoming`.
129
+ * Mirrors the `shop_product_variants` schema with `priceDelta` decoded as
130
+ * the raw numeric(20,6) string and `attributes` as the typed map declared
131
+ * in `defineShop({ variantAttributes })`.
132
+ * @public
133
+ */
134
+ export interface VariantPublic {
135
+ id: string;
136
+ productId: string;
137
+ sku: string | null;
138
+ name: Record<string, string> | null;
139
+ priceDelta: string;
140
+ stock: number | null;
141
+ attributes: Record<string, unknown> | null;
142
+ sortOrder: number | null;
143
+ createdAt: string;
144
+ }
117
145
  /**
118
146
  * Headless shop SDK surface returned by `createShopClient()`.
119
147
  *
@@ -124,6 +152,21 @@ export interface RetryPaymentResult {
124
152
  * @public
125
153
  */
126
154
  export interface ShopClient {
155
+ /** Product / variant discovery. */
156
+ products: {
157
+ /**
158
+ * List upcoming (non-expired) variants for a product. When
159
+ * `defineShop({ variantExpiry })` is not configured this returns
160
+ * every variant; otherwise variants whose datetime attribute has
161
+ * already passed (per `offsetDays`) are filtered out and the
162
+ * remainder are sorted ascending by that attribute.
163
+ *
164
+ * @param productId - The shop product id (UUID).
165
+ */
166
+ listUpcoming(productId: string): Promise<{
167
+ items: VariantPublic[];
168
+ }>;
169
+ };
127
170
  /** Cart operations — read state and mutate items / coupon. */
128
171
  cart: {
129
172
  /**
@@ -201,6 +244,17 @@ export interface ShopClient {
201
244
  * re-fill the checkout form.
202
245
  */
203
246
  retryPayment(orderNumber: string, token?: string): Promise<RetryPaymentResult>;
247
+ /**
248
+ * Pay the outstanding balance on a deposit-policy order. `token` is
249
+ * the signed balance token from the admin-generated balance link;
250
+ * the server returns a redirect URL when the payment provider needs
251
+ * to take over, or `paymentStatus: 'manual'` for manual methods.
252
+ *
253
+ * @param orderNumber - The order number from the balance link.
254
+ * @param token - Signed balance token (query parameter on the link).
255
+ * @experimental
256
+ */
257
+ payBalance(orderNumber: string, token: string): Promise<PayBalanceResult>;
204
258
  };
205
259
  }
206
260
  /**
@@ -59,6 +59,9 @@ export function createShopClient(options = {}) {
59
59
  return (await res.json());
60
60
  }
61
61
  return {
62
+ products: {
63
+ listUpcoming: (productId) => call('GET', `/api/shop/products/${encodeURIComponent(productId)}/variants/upcoming`)
64
+ },
62
65
  cart: {
63
66
  get: () => call('GET', '/api/shop/cart'),
64
67
  add: (variantId, qty = 1) => call('POST', '/api/shop/cart', { variantId, qty }),
@@ -77,7 +80,8 @@ export function createShopClient(options = {}) {
77
80
  orders: {
78
81
  get: (orderNumber, token) => call('GET', `/api/shop/orders/${encodeURIComponent(orderNumber)}${tokenQuery(token)}`),
79
82
  refreshPayment: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/refresh-payment${tokenQuery(token)}`),
80
- retryPayment: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/retry-payment${tokenQuery(token)}`)
83
+ retryPayment: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/retry-payment${tokenQuery(token)}`),
84
+ payBalance: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/balance?token=${encodeURIComponent(token)}`, {})
81
85
  }
82
86
  };
83
87
  }
@@ -0,0 +1,35 @@
1
+ import type { VariantExpiryConfig } from './types.js';
2
+ /**
3
+ * Thrown by `createOrderFromCart` / cart-add HTTP guard when a referenced
4
+ * variant has already expired under the configured `variantExpiry`. The
5
+ * `code` is `VARIANT_EXPIRED` and `variantId` identifies the offending row.
6
+ * @public
7
+ */
8
+ export declare class VariantExpiredError extends Error {
9
+ readonly code = "VARIANT_EXPIRED";
10
+ readonly variantId: string;
11
+ constructor(variantId: string);
12
+ }
13
+ interface VariantLike {
14
+ attributes: Record<string, unknown> | null;
15
+ }
16
+ /**
17
+ * Check whether a variant has expired under the given config. Fail-open by
18
+ * design: `null` config, missing source attribute, non-string value, or
19
+ * malformed datetime all return `false` — only a parseable past datetime
20
+ * counts as expired. Malformed datetimes emit `console.warn` so authoring
21
+ * issues surface in dev without blocking checkout.
22
+ *
23
+ * @param variant - Object with an `attributes` map (e.g. `shop_product_variants.attributes`).
24
+ * @param config - Result of `defineShop({ variantExpiry })`, or `null` when not configured.
25
+ * @param now - Override the current time (test seam). Defaults to `Date.now()`.
26
+ * @public
27
+ */
28
+ export declare function isVariantExpired(variant: VariantLike, config: VariantExpiryConfig | null, now?: Date): boolean;
29
+ /**
30
+ * Return the subset of `variants` that have not yet expired. Order is
31
+ * preserved. `null` config = passthrough.
32
+ * @public
33
+ */
34
+ export declare function filterUpcoming<T extends VariantLike>(variants: T[], config: VariantExpiryConfig | null, now?: Date): T[];
35
+ export {};