includio-cms 0.36.8 → 0.37.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 (259) hide show
  1. package/API.md +179 -3
  2. package/CHANGELOG.md +64 -0
  3. package/DOCS.md +28 -3
  4. package/ROADMAP.md +18 -0
  5. package/dist/admin/client/booking/booking-dashboard.svelte +40 -0
  6. package/dist/admin/client/booking/booking-dashboard.svelte.d.ts +3 -0
  7. package/dist/admin/client/booking/customer-panel.svelte +102 -0
  8. package/dist/admin/client/booking/customer-panel.svelte.d.ts +11 -0
  9. package/dist/admin/client/booking/customer-view.d.ts +52 -0
  10. package/dist/admin/client/booking/customer-view.js +134 -0
  11. package/dist/admin/client/booking/participant-data-panel.svelte +330 -0
  12. package/dist/admin/client/booking/participant-data-panel.svelte.d.ts +22 -0
  13. package/dist/admin/client/booking/reservation-detail-page.svelte +637 -0
  14. package/dist/admin/client/booking/reservation-detail-page.svelte.d.ts +3 -0
  15. package/dist/admin/client/booking/reservations-page.svelte +221 -0
  16. package/dist/admin/client/booking/reservations-page.svelte.d.ts +3 -0
  17. package/dist/admin/client/collection/collection-entries.svelte +21 -2
  18. package/dist/admin/client/collection/entry-link.svelte +6 -4
  19. package/dist/admin/client/collection/entry-link.svelte.d.ts +3 -1
  20. package/dist/admin/client/collection/row-actions.svelte +12 -4
  21. package/dist/admin/client/collection/row-actions.svelte.d.ts +1 -0
  22. package/dist/admin/client/collection/status-badge.svelte +39 -3
  23. package/dist/admin/client/collection/status-badge.svelte.d.ts +5 -3
  24. package/dist/admin/client/entry/entry.svelte +26 -9
  25. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +73 -10
  26. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte.d.ts +6 -5
  27. package/dist/admin/client/entry/preview-views-context.svelte.d.ts +22 -0
  28. package/dist/admin/client/entry/preview-views-context.svelte.js +58 -0
  29. package/dist/admin/client/entry/preview-views.d.ts +27 -0
  30. package/dist/admin/client/entry/preview-views.js +33 -0
  31. package/dist/admin/client/index.d.ts +3 -0
  32. package/dist/admin/client/index.js +3 -0
  33. package/dist/admin/client/modules/dashboard-registry.d.ts +7 -0
  34. package/dist/admin/client/modules/dashboard-registry.js +11 -0
  35. package/dist/admin/client/modules/module-dashboard-page.svelte +26 -0
  36. package/dist/admin/client/modules/module-dashboard-page.svelte.d.ts +6 -0
  37. package/dist/admin/client/modules/stat-card.svelte +22 -0
  38. package/dist/admin/client/modules/stat-card.svelte.d.ts +10 -0
  39. package/dist/admin/client/shop/shop-dashboard.svelte +29 -0
  40. package/dist/admin/client/shop/shop-dashboard.svelte.d.ts +3 -0
  41. package/dist/admin/components/fields/relation-field.svelte +11 -2
  42. package/dist/admin/components/icons/icon-map.js +8 -0
  43. package/dist/admin/components/layout/app-sidebar.svelte +45 -14
  44. package/dist/admin/components/layout/context-switcher.svelte +90 -0
  45. package/dist/admin/components/layout/context-switcher.svelte.d.ts +3 -0
  46. package/dist/admin/components/layout/lang.d.ts +4 -0
  47. package/dist/admin/components/layout/lang.js +8 -0
  48. package/dist/admin/components/layout/layout-renderer.svelte +2 -1
  49. package/dist/admin/components/layout/layout-tabs.svelte +14 -0
  50. package/dist/admin/components/layout/layout-tabs.svelte.d.ts +2 -0
  51. package/dist/admin/components/layout/nav-collections.svelte +16 -9
  52. package/dist/admin/components/layout/nav-collections.svelte.d.ts +5 -1
  53. package/dist/admin/components/layout/nav-global.svelte +45 -0
  54. package/dist/admin/components/layout/nav-global.svelte.d.ts +3 -0
  55. package/dist/admin/components/layout/nav-main.svelte +8 -32
  56. package/dist/admin/components/layout/nav-main.svelte.d.ts +6 -1
  57. package/dist/admin/components/layout/nav-module.svelte +57 -0
  58. package/dist/admin/components/layout/nav-module.svelte.d.ts +7 -0
  59. package/dist/admin/components/layout/nav-section.svelte +1 -1
  60. package/dist/admin/components/layout/nav-section.svelte.d.ts +1 -1
  61. package/dist/admin/components/layout/nav-singletons.svelte +14 -7
  62. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +5 -1
  63. package/dist/admin/remote/booking.remote.d.ts +244 -0
  64. package/dist/admin/remote/booking.remote.js +283 -0
  65. package/dist/admin/remote/entry.remote.d.ts +3 -0
  66. package/dist/admin/remote/entry.remote.js +14 -3
  67. package/dist/admin/remote/index.d.ts +2 -0
  68. package/dist/admin/remote/index.js +2 -0
  69. package/dist/admin/remote/modules.remote.d.ts +10 -0
  70. package/dist/admin/remote/modules.remote.js +14 -0
  71. package/dist/admin/state/active-context-resolve.d.ts +37 -0
  72. package/dist/admin/state/active-context-resolve.js +66 -0
  73. package/dist/admin/state/active-context.svelte.d.ts +8 -0
  74. package/dist/admin/state/active-context.svelte.js +27 -0
  75. package/dist/booking/adjustments.d.ts +33 -0
  76. package/dist/booking/adjustments.js +26 -0
  77. package/dist/booking/capacity.d.ts +12 -0
  78. package/dist/booking/capacity.js +10 -0
  79. package/dist/booking/client/index.d.ts +39 -0
  80. package/dist/booking/client/index.js +44 -0
  81. package/dist/booking/client/use-booking.svelte.d.ts +25 -0
  82. package/dist/booking/client/use-booking.svelte.js +56 -0
  83. package/dist/booking/config.d.ts +156 -0
  84. package/dist/booking/config.js +36 -0
  85. package/dist/booking/draft.d.ts +77 -0
  86. package/dist/booking/draft.js +67 -0
  87. package/dist/booking/expiry-logic.d.ts +30 -0
  88. package/dist/booking/expiry-logic.js +33 -0
  89. package/dist/booking/index.d.ts +18 -0
  90. package/dist/booking/index.js +52 -0
  91. package/dist/booking/ledger.d.ts +46 -0
  92. package/dist/booking/ledger.js +60 -0
  93. package/dist/booking/number.d.ts +2 -0
  94. package/dist/booking/number.js +11 -0
  95. package/dist/booking/participant-fields/access.d.ts +10 -0
  96. package/dist/booking/participant-fields/access.js +14 -0
  97. package/dist/booking/participant-fields/age.d.ts +12 -0
  98. package/dist/booking/participant-fields/age.js +28 -0
  99. package/dist/booking/participant-fields/completeness.d.ts +8 -0
  100. package/dist/booking/participant-fields/completeness.js +13 -0
  101. package/dist/booking/participant-fields/group.d.ts +6 -0
  102. package/dist/booking/participant-fields/group.js +15 -0
  103. package/dist/booking/participant-fields/index.d.ts +6 -0
  104. package/dist/booking/participant-fields/index.js +6 -0
  105. package/dist/booking/participant-fields/types.d.ts +50 -0
  106. package/dist/booking/participant-fields/types.js +5 -0
  107. package/dist/booking/participant-fields/validate.d.ts +7 -0
  108. package/dist/booking/participant-fields/validate.js +44 -0
  109. package/dist/booking/participant-pricing.d.ts +93 -0
  110. package/dist/booking/participant-pricing.js +91 -0
  111. package/dist/booking/participants-view.d.ts +32 -0
  112. package/dist/booking/participants-view.js +22 -0
  113. package/dist/booking/payment-context.d.ts +19 -0
  114. package/dist/booking/payment-context.js +29 -0
  115. package/dist/booking/pricing-inputs.d.ts +28 -0
  116. package/dist/booking/pricing-inputs.js +35 -0
  117. package/dist/booking/pricing.d.ts +86 -0
  118. package/dist/booking/pricing.js +125 -0
  119. package/dist/booking/recompute.d.ts +22 -0
  120. package/dist/booking/recompute.js +31 -0
  121. package/dist/booking/reminders-schedule.d.ts +11 -0
  122. package/dist/booking/reminders-schedule.js +28 -0
  123. package/dist/booking/server/adjustments.d.ts +27 -0
  124. package/dist/booking/server/adjustments.js +136 -0
  125. package/dist/booking/server/bookings.d.ts +45 -0
  126. package/dist/booking/server/bookings.js +288 -0
  127. package/dist/booking/server/catalog.d.ts +9 -0
  128. package/dist/booking/server/catalog.js +20 -0
  129. package/dist/booking/server/db.d.ts +7 -0
  130. package/dist/booking/server/db.js +18 -0
  131. package/dist/booking/server/email.d.ts +23 -0
  132. package/dist/booking/server/email.js +59 -0
  133. package/dist/booking/server/emailTemplateRegistry.d.ts +29 -0
  134. package/dist/booking/server/emailTemplateRegistry.js +125 -0
  135. package/dist/booking/server/http/create-handler.d.ts +5 -0
  136. package/dist/booking/server/http/create-handler.js +68 -0
  137. package/dist/booking/server/http/index.d.ts +4 -0
  138. package/dist/booking/server/http/index.js +4 -0
  139. package/dist/booking/server/http/maintenance-handler.d.ts +9 -0
  140. package/dist/booking/server/http/maintenance-handler.js +36 -0
  141. package/dist/booking/server/http/portal-handler.d.ts +13 -0
  142. package/dist/booking/server/http/portal-handler.js +99 -0
  143. package/dist/booking/server/http/webhook-handler.d.ts +9 -0
  144. package/dist/booking/server/http/webhook-handler.js +39 -0
  145. package/dist/booking/server/index.d.ts +12 -0
  146. package/dist/booking/server/index.js +14 -0
  147. package/dist/booking/server/maintenance.d.ts +28 -0
  148. package/dist/booking/server/maintenance.js +134 -0
  149. package/dist/booking/server/participants.d.ts +26 -0
  150. package/dist/booking/server/participants.js +92 -0
  151. package/dist/booking/server/payments.d.ts +34 -0
  152. package/dist/booking/server/payments.js +207 -0
  153. package/dist/booking/server/portal-session.d.ts +16 -0
  154. package/dist/booking/server/portal-session.js +42 -0
  155. package/dist/booking/server/portal.d.ts +207 -0
  156. package/dist/booking/server/portal.js +234 -0
  157. package/dist/booking/server/refunds.d.ts +7 -0
  158. package/dist/booking/server/refunds.js +58 -0
  159. package/dist/booking/server/reminders.d.ts +9 -0
  160. package/dist/booking/server/reminders.js +24 -0
  161. package/dist/booking/server/validation.d.ts +17 -0
  162. package/dist/booking/server/validation.js +55 -0
  163. package/dist/booking/state-machine.d.ts +41 -0
  164. package/dist/booking/state-machine.js +73 -0
  165. package/dist/booking/templates/_partials/footer.en.html +4 -0
  166. package/dist/booking/templates/_partials/footer.pl.html +4 -0
  167. package/dist/booking/templates/_partials/header.en.html +4 -0
  168. package/dist/booking/templates/_partials/header.pl.html +4 -0
  169. package/dist/booking/templates/balance-due.en.html +10 -0
  170. package/dist/booking/templates/balance-due.pl.html +10 -0
  171. package/dist/booking/templates/balance-overdue-cancelled.en.html +5 -0
  172. package/dist/booking/templates/balance-overdue-cancelled.pl.html +5 -0
  173. package/dist/booking/templates/booking-received.en.html +9 -0
  174. package/dist/booking/templates/booking-received.pl.html +9 -0
  175. package/dist/booking/templates/deposit-paid.en.html +6 -0
  176. package/dist/booking/templates/deposit-paid.pl.html +6 -0
  177. package/dist/booking/templates/payment-link.en.html +5 -0
  178. package/dist/booking/templates/payment-link.pl.html +5 -0
  179. package/dist/booking/templates/payment-received.en.html +10 -0
  180. package/dist/booking/templates/payment-received.pl.html +10 -0
  181. package/dist/booking/totals.d.ts +38 -0
  182. package/dist/booking/totals.js +61 -0
  183. package/dist/booking/webhook-logic.d.ts +28 -0
  184. package/dist/booking/webhook-logic.js +50 -0
  185. package/dist/cli/create-user.d.ts +7 -1
  186. package/dist/cli/create-user.js +23 -15
  187. package/dist/cli/index.js +55 -7
  188. package/dist/cli/scaffold/admin.d.ts +6 -0
  189. package/dist/cli/scaffold/admin.js +5 -3
  190. package/dist/cli/scaffold/booking.d.ts +12 -0
  191. package/dist/cli/scaffold/booking.js +103 -0
  192. package/dist/core/cms.d.ts +24 -9
  193. package/dist/core/cms.js +72 -34
  194. package/dist/core/config/validatePreviews.d.ts +12 -0
  195. package/dist/core/config/validatePreviews.js +90 -0
  196. package/dist/core/fields/dotPath.d.ts +4 -0
  197. package/dist/core/fields/dotPath.js +18 -0
  198. package/dist/core/fields/slugPath.d.ts +9 -1
  199. package/dist/core/fields/slugPath.js +16 -2
  200. package/dist/core/fields/slugify.d.ts +3 -0
  201. package/dist/core/fields/slugify.js +4 -0
  202. package/dist/core/server/entries/operations/duplicate.d.ts +10 -0
  203. package/dist/core/server/entries/operations/duplicate.js +73 -0
  204. package/dist/core/server/entries/operations/slugNormalize.d.ts +8 -0
  205. package/dist/core/server/entries/operations/slugNormalize.js +20 -0
  206. package/dist/core/server/entries/operations/slugUniqueness.d.ts +17 -0
  207. package/dist/core/server/entries/operations/slugUniqueness.js +39 -0
  208. package/dist/core/server/fields/resolveRelationFields.d.ts +11 -2
  209. package/dist/core/server/fields/resolveRelationFields.js +12 -3
  210. package/dist/core/server/generator/generator.js +7 -2
  211. package/dist/db-postgres/schema/booking/bookingAdjustments.d.ts +186 -0
  212. package/dist/db-postgres/schema/booking/bookingAdjustments.js +22 -0
  213. package/dist/db-postgres/schema/booking/bookingItemPersons.d.ts +117 -0
  214. package/dist/db-postgres/schema/booking/bookingItemPersons.js +22 -0
  215. package/dist/db-postgres/schema/booking/bookingItems.d.ts +181 -0
  216. package/dist/db-postgres/schema/booking/bookingItems.js +22 -0
  217. package/dist/db-postgres/schema/booking/bookingPayments.d.ts +204 -0
  218. package/dist/db-postgres/schema/booking/bookingPayments.js +25 -0
  219. package/dist/db-postgres/schema/booking/bookingPersons.d.ts +99 -0
  220. package/dist/db-postgres/schema/booking/bookingPersons.js +17 -0
  221. package/dist/db-postgres/schema/booking/bookingReminders.d.ts +131 -0
  222. package/dist/db-postgres/schema/booking/bookingReminders.js +18 -0
  223. package/dist/db-postgres/schema/booking/bookings.d.ts +307 -0
  224. package/dist/db-postgres/schema/booking/bookings.js +40 -0
  225. package/dist/db-postgres/schema/booking/index.d.ts +7 -0
  226. package/dist/db-postgres/schema/booking/index.js +7 -0
  227. package/dist/db-postgres/schema/index.d.ts +1 -0
  228. package/dist/db-postgres/schema/index.js +1 -0
  229. package/dist/db-postgres/schema-booking.d.ts +1 -0
  230. package/dist/db-postgres/schema-booking.js +1 -0
  231. package/dist/server/security/rate-limit-handler.d.ts +19 -0
  232. package/dist/server/security/rate-limit-handler.js +34 -0
  233. package/dist/shop/adapters/payu/client.js +3 -1
  234. package/dist/shop/adapters/payu/index.js +37 -17
  235. package/dist/shop/adapters/payu/payload.d.ts +6 -0
  236. package/dist/shop/adapters/payu/payload.js +1 -1
  237. package/dist/shop/adapters/stripe/index.js +36 -21
  238. package/dist/shop/index.d.ts +11 -2
  239. package/dist/shop/index.js +43 -1
  240. package/dist/shop/rate-limit.d.ts +1 -9
  241. package/dist/shop/rate-limit.js +3 -28
  242. package/dist/shop/types.d.ts +19 -0
  243. package/dist/sveltekit/config.d.ts +4 -0
  244. package/dist/sveltekit/config.js +6 -0
  245. package/dist/types/cms.d.ts +15 -1
  246. package/dist/types/cms.schema.d.ts +96 -2
  247. package/dist/types/cms.schema.js +84 -1
  248. package/dist/types/config.d.ts +33 -0
  249. package/dist/types/layout.d.ts +6 -0
  250. package/dist/types/modules.d.ts +133 -0
  251. package/dist/types/modules.js +27 -0
  252. package/dist/updates/0.36.9/index.d.ts +2 -0
  253. package/dist/updates/0.36.9/index.js +12 -0
  254. package/dist/updates/0.37.0/index.d.ts +2 -0
  255. package/dist/updates/0.37.0/index.js +52 -0
  256. package/dist/updates/index.js +5 -1
  257. package/package.json +19 -1
  258. package/dist/admin/components/layout/nav-shop.svelte +0 -33
  259. package/dist/admin/components/layout/nav-shop.svelte.d.ts +0 -3
@@ -0,0 +1,307 @@
1
+ import type { BookingStatus } from '../../../booking/state-machine.js';
2
+ import type { AppliedRule, Perk } from '../../../booking/pricing.js';
3
+ /** Computed money snapshot kept on the booking for fast list/detail reads. */
4
+ export interface BookingTotals {
5
+ gross: number;
6
+ discount: number;
7
+ /** Σ active manual surcharges (minor units). */
8
+ surcharge: number;
9
+ /** Σ active manual discounts, magnitude (minor units). */
10
+ manualDiscount: number;
11
+ paid: number;
12
+ balance: number;
13
+ /** Granted non-monetary perks (staff notes; manual fulfilment). */
14
+ perks: Perk[];
15
+ /** Per-rule discount breakdown for display. */
16
+ appliedRules: AppliedRule[];
17
+ }
18
+ /**
19
+ * A long-lived, mutable reservation with a payment ledger. Unlike a shop order
20
+ * ("buy → ship → done"), a booking is edited after payment and its price is
21
+ * recomputed. Amounts are in minor units.
22
+ */
23
+ export declare const bookingsTable: import("drizzle-orm/pg-core/table", { with: { "resolution-mode": "require" } }).PgTableWithColumns<{
24
+ name: "bookings";
25
+ schema: undefined;
26
+ columns: {
27
+ id: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
28
+ name: "id";
29
+ tableName: "bookings";
30
+ dataType: "string";
31
+ columnType: "PgUUID";
32
+ data: string;
33
+ driverParam: string;
34
+ notNull: true;
35
+ hasDefault: true;
36
+ isPrimaryKey: true;
37
+ isAutoincrement: false;
38
+ hasRuntimeDefault: false;
39
+ enumValues: undefined;
40
+ baseColumn: never;
41
+ identity: undefined;
42
+ generated: undefined;
43
+ }, {}, {}>;
44
+ number: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
45
+ name: "number";
46
+ tableName: "bookings";
47
+ dataType: "string";
48
+ columnType: "PgText";
49
+ data: string;
50
+ driverParam: string;
51
+ notNull: true;
52
+ hasDefault: false;
53
+ isPrimaryKey: false;
54
+ isAutoincrement: false;
55
+ hasRuntimeDefault: false;
56
+ enumValues: [string, ...string[]];
57
+ baseColumn: never;
58
+ identity: undefined;
59
+ generated: undefined;
60
+ }, {}, {}>;
61
+ status: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
62
+ name: "status";
63
+ tableName: "bookings";
64
+ dataType: "string";
65
+ columnType: "PgText";
66
+ data: BookingStatus;
67
+ driverParam: string;
68
+ notNull: true;
69
+ hasDefault: true;
70
+ isPrimaryKey: false;
71
+ isAutoincrement: false;
72
+ hasRuntimeDefault: false;
73
+ enumValues: [string, ...string[]];
74
+ baseColumn: never;
75
+ identity: undefined;
76
+ generated: undefined;
77
+ }, {}, {
78
+ $type: BookingStatus;
79
+ }>;
80
+ currency: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
81
+ name: "currency";
82
+ tableName: "bookings";
83
+ dataType: "string";
84
+ columnType: "PgText";
85
+ data: string;
86
+ driverParam: string;
87
+ notNull: true;
88
+ hasDefault: false;
89
+ isPrimaryKey: false;
90
+ isAutoincrement: false;
91
+ hasRuntimeDefault: false;
92
+ enumValues: [string, ...string[]];
93
+ baseColumn: never;
94
+ identity: undefined;
95
+ generated: undefined;
96
+ }, {}, {}>;
97
+ customer: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
98
+ name: "customer";
99
+ tableName: "bookings";
100
+ dataType: "json";
101
+ columnType: "PgJsonb";
102
+ data: Record<string, unknown>;
103
+ driverParam: unknown;
104
+ notNull: true;
105
+ hasDefault: true;
106
+ isPrimaryKey: false;
107
+ isAutoincrement: false;
108
+ hasRuntimeDefault: false;
109
+ enumValues: undefined;
110
+ baseColumn: never;
111
+ identity: undefined;
112
+ generated: undefined;
113
+ }, {}, {
114
+ $type: Record<string, unknown>;
115
+ }>;
116
+ totals: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
117
+ name: "totals";
118
+ tableName: "bookings";
119
+ dataType: "json";
120
+ columnType: "PgJsonb";
121
+ data: BookingTotals;
122
+ driverParam: unknown;
123
+ notNull: true;
124
+ hasDefault: true;
125
+ isPrimaryKey: false;
126
+ isAutoincrement: false;
127
+ hasRuntimeDefault: false;
128
+ enumValues: undefined;
129
+ baseColumn: never;
130
+ identity: undefined;
131
+ generated: undefined;
132
+ }, {}, {
133
+ $type: BookingTotals;
134
+ }>;
135
+ accessToken: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
136
+ name: "access_token";
137
+ tableName: "bookings";
138
+ dataType: "string";
139
+ columnType: "PgUUID";
140
+ data: string;
141
+ driverParam: string;
142
+ notNull: true;
143
+ hasDefault: true;
144
+ isPrimaryKey: false;
145
+ isAutoincrement: false;
146
+ hasRuntimeDefault: false;
147
+ enumValues: undefined;
148
+ baseColumn: never;
149
+ identity: undefined;
150
+ generated: undefined;
151
+ }, {}, {}>;
152
+ staffNotes: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
153
+ name: "staff_notes";
154
+ tableName: "bookings";
155
+ dataType: "string";
156
+ columnType: "PgText";
157
+ data: string;
158
+ driverParam: string;
159
+ notNull: false;
160
+ hasDefault: false;
161
+ isPrimaryKey: false;
162
+ isAutoincrement: false;
163
+ hasRuntimeDefault: false;
164
+ enumValues: [string, ...string[]];
165
+ baseColumn: never;
166
+ identity: undefined;
167
+ generated: undefined;
168
+ }, {}, {}>;
169
+ language: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
170
+ name: "language";
171
+ tableName: "bookings";
172
+ dataType: "string";
173
+ columnType: "PgText";
174
+ data: string;
175
+ driverParam: string;
176
+ notNull: false;
177
+ hasDefault: false;
178
+ isPrimaryKey: false;
179
+ isAutoincrement: false;
180
+ hasRuntimeDefault: false;
181
+ enumValues: [string, ...string[]];
182
+ baseColumn: never;
183
+ identity: undefined;
184
+ generated: undefined;
185
+ }, {}, {}>;
186
+ balanceDueAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
187
+ name: "balance_due_at";
188
+ tableName: "bookings";
189
+ dataType: "date";
190
+ columnType: "PgTimestamp";
191
+ data: Date;
192
+ driverParam: string;
193
+ notNull: false;
194
+ hasDefault: false;
195
+ isPrimaryKey: false;
196
+ isAutoincrement: false;
197
+ hasRuntimeDefault: false;
198
+ enumValues: undefined;
199
+ baseColumn: never;
200
+ identity: undefined;
201
+ generated: undefined;
202
+ }, {}, {}>;
203
+ holdExpiresAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
204
+ name: "hold_expires_at";
205
+ tableName: "bookings";
206
+ dataType: "date";
207
+ columnType: "PgTimestamp";
208
+ data: Date;
209
+ driverParam: string;
210
+ notNull: false;
211
+ hasDefault: false;
212
+ isPrimaryKey: false;
213
+ isAutoincrement: false;
214
+ hasRuntimeDefault: false;
215
+ enumValues: undefined;
216
+ baseColumn: never;
217
+ identity: undefined;
218
+ generated: undefined;
219
+ }, {}, {}>;
220
+ cancelReason: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
221
+ name: "cancel_reason";
222
+ tableName: "bookings";
223
+ dataType: "string";
224
+ columnType: "PgText";
225
+ data: string;
226
+ driverParam: string;
227
+ notNull: false;
228
+ hasDefault: false;
229
+ isPrimaryKey: false;
230
+ isAutoincrement: false;
231
+ hasRuntimeDefault: false;
232
+ enumValues: [string, ...string[]];
233
+ baseColumn: never;
234
+ identity: undefined;
235
+ generated: undefined;
236
+ }, {}, {}>;
237
+ deletedAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
238
+ name: "deleted_at";
239
+ tableName: "bookings";
240
+ dataType: "date";
241
+ columnType: "PgTimestamp";
242
+ data: Date;
243
+ driverParam: string;
244
+ notNull: false;
245
+ hasDefault: false;
246
+ isPrimaryKey: false;
247
+ isAutoincrement: false;
248
+ hasRuntimeDefault: false;
249
+ enumValues: undefined;
250
+ baseColumn: never;
251
+ identity: undefined;
252
+ generated: undefined;
253
+ }, {}, {}>;
254
+ deletedBy: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
255
+ name: "deleted_by";
256
+ tableName: "bookings";
257
+ dataType: "string";
258
+ columnType: "PgText";
259
+ data: string;
260
+ driverParam: string;
261
+ notNull: false;
262
+ hasDefault: false;
263
+ isPrimaryKey: false;
264
+ isAutoincrement: false;
265
+ hasRuntimeDefault: false;
266
+ enumValues: [string, ...string[]];
267
+ baseColumn: never;
268
+ identity: undefined;
269
+ generated: undefined;
270
+ }, {}, {}>;
271
+ createdAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
272
+ name: "created_at";
273
+ tableName: "bookings";
274
+ dataType: "date";
275
+ columnType: "PgTimestamp";
276
+ data: Date;
277
+ driverParam: string;
278
+ notNull: true;
279
+ hasDefault: true;
280
+ isPrimaryKey: false;
281
+ isAutoincrement: false;
282
+ hasRuntimeDefault: false;
283
+ enumValues: undefined;
284
+ baseColumn: never;
285
+ identity: undefined;
286
+ generated: undefined;
287
+ }, {}, {}>;
288
+ updatedAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
289
+ name: "updated_at";
290
+ tableName: "bookings";
291
+ dataType: "date";
292
+ columnType: "PgTimestamp";
293
+ data: Date;
294
+ driverParam: string;
295
+ notNull: true;
296
+ hasDefault: true;
297
+ isPrimaryKey: false;
298
+ isAutoincrement: false;
299
+ hasRuntimeDefault: false;
300
+ enumValues: undefined;
301
+ baseColumn: never;
302
+ identity: undefined;
303
+ generated: undefined;
304
+ }, {}, {}>;
305
+ };
306
+ dialect: "pg";
307
+ }>;
@@ -0,0 +1,40 @@
1
+ import { jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
2
+ /**
3
+ * A long-lived, mutable reservation with a payment ledger. Unlike a shop order
4
+ * ("buy → ship → done"), a booking is edited after payment and its price is
5
+ * recomputed. Amounts are in minor units.
6
+ */
7
+ export const bookingsTable = pgTable('bookings', {
8
+ id: uuid('id').primaryKey().defaultRandom(),
9
+ number: text('number').notNull().unique(),
10
+ status: text('status').$type().notNull().default('draft'),
11
+ currency: text('currency').notNull(),
12
+ /** Booker details (name, email, phone, "how did you hear about us", …). */
13
+ customer: jsonb('customer').$type().notNull().default({}),
14
+ /** Computed totals (gross/discount/paid/balance), refreshed on every edit. */
15
+ totals: jsonb('totals').$type().notNull().default({
16
+ gross: 0,
17
+ discount: 0,
18
+ surcharge: 0,
19
+ manualDiscount: 0,
20
+ paid: 0,
21
+ balance: 0,
22
+ perks: [],
23
+ appliedRules: []
24
+ }),
25
+ /** Unguessable token for the self-service portal (`/rezerwacja/[token]`). */
26
+ accessToken: uuid('access_token').defaultRandom().notNull(),
27
+ staffNotes: text('staff_notes'),
28
+ language: text('language'),
29
+ /** Deadline to pay the balance; past it with balance>0 ⇒ auto-cancel + deposit forfeited. */
30
+ balanceDueAt: timestamp('balance_due_at', { withTimezone: true }),
31
+ /** TTL for an abandoned draft (deposit never paid) ⇒ auto-cancel. Cleared on deposit payment. */
32
+ holdExpiresAt: timestamp('hold_expires_at', { withTimezone: true }),
33
+ /** Why a booking was cancelled: 'abandoned' | 'balance_overdue' | 'manual'. */
34
+ cancelReason: text('cancel_reason'),
35
+ // Soft-delete (accounting/audit safety) — NULL = visible.
36
+ deletedAt: timestamp('deleted_at', { withTimezone: true }),
37
+ deletedBy: text('deleted_by'),
38
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
39
+ updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
40
+ });
@@ -0,0 +1,7 @@
1
+ export * from './bookings.js';
2
+ export * from './bookingItems.js';
3
+ export * from './bookingPersons.js';
4
+ export * from './bookingItemPersons.js';
5
+ export * from './bookingPayments.js';
6
+ export * from './bookingReminders.js';
7
+ export * from './bookingAdjustments.js';
@@ -0,0 +1,7 @@
1
+ export * from './bookings.js';
2
+ export * from './bookingItems.js';
3
+ export * from './bookingPersons.js';
4
+ export * from './bookingItemPersons.js';
5
+ export * from './bookingPayments.js';
6
+ export * from './bookingReminders.js';
7
+ export * from './bookingAdjustments.js';
@@ -9,3 +9,4 @@ export * from './imageStyle.js';
9
9
  export * from './videoStyle.js';
10
10
  export * from './mediaJob.js';
11
11
  export * from './shop/index.js';
12
+ export * from './booking/index.js';
@@ -9,3 +9,4 @@ export * from './imageStyle.js';
9
9
  export * from './videoStyle.js';
10
10
  export * from './mediaJob.js';
11
11
  export * from './shop/index.js';
12
+ export * from './booking/index.js';
@@ -0,0 +1 @@
1
+ export * from './schema/booking/index.js';
@@ -0,0 +1 @@
1
+ export * from './schema/booking/index.js';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Per-handler in-memory rate limiter (fixed window). Generic infra shared by the
3
+ * shop and booking HTTP handlers — not shop-specific. For a single instance this
4
+ * needs no external store; multi-instance deployments would swap the bucket map
5
+ * for a shared store.
6
+ */
7
+ /** A fixed-window rule: at most `limit` hits per `windowSec` seconds. */
8
+ export interface RateLimitRule {
9
+ limit: number;
10
+ windowSec: number;
11
+ }
12
+ export interface RateLimitResult {
13
+ allowed: boolean;
14
+ remaining: number;
15
+ resetAt: number;
16
+ }
17
+ export declare function checkRateLimit(key: string, rule: RateLimitRule): RateLimitResult;
18
+ export declare function resetRateLimit(key?: string): void;
19
+ export declare function clientKey(req: Request, prefix: string): string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Per-handler in-memory rate limiter (fixed window). Generic infra shared by the
3
+ * shop and booking HTTP handlers — not shop-specific. For a single instance this
4
+ * needs no external store; multi-instance deployments would swap the bucket map
5
+ * for a shared store.
6
+ */
7
+ const buckets = new Map();
8
+ export function checkRateLimit(key, rule) {
9
+ const now = Date.now();
10
+ const windowMs = rule.windowSec * 1000;
11
+ const bucket = buckets.get(key);
12
+ if (!bucket || bucket.resetAt <= now) {
13
+ const fresh = { count: 1, resetAt: now + windowMs };
14
+ buckets.set(key, fresh);
15
+ return { allowed: true, remaining: rule.limit - 1, resetAt: fresh.resetAt };
16
+ }
17
+ if (bucket.count >= rule.limit) {
18
+ return { allowed: false, remaining: 0, resetAt: bucket.resetAt };
19
+ }
20
+ bucket.count += 1;
21
+ return { allowed: true, remaining: rule.limit - bucket.count, resetAt: bucket.resetAt };
22
+ }
23
+ export function resetRateLimit(key) {
24
+ if (key)
25
+ buckets.delete(key);
26
+ else
27
+ buckets.clear();
28
+ }
29
+ export function clientKey(req, prefix) {
30
+ const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
31
+ req.headers.get('x-real-ip') ||
32
+ 'unknown';
33
+ return `${prefix}:${ip}`;
34
+ }
@@ -52,7 +52,9 @@ export class PayuClient {
52
52
  });
53
53
  // PayU returns 302 by default with a JSON body (manual redirect is used to read it).
54
54
  if (res.status !== 302 && !res.ok) {
55
- throw new Error(`PayU createOrder failed: ${res.status}`);
55
+ // Surface PayU's error body (e.g. ERROR_ORDER_NOT_UNIQUE) — don't swallow it.
56
+ const detail = await res.text().catch(() => '');
57
+ throw new Error(`PayU createOrder failed: ${res.status}${detail ? ` — ${detail}` : ''}`);
56
58
  }
57
59
  const data = (await res.json());
58
60
  if (data.status && data.status.statusCode && data.status.statusCode !== 'SUCCESS') {
@@ -19,31 +19,51 @@ export function payuAdapter(opts) {
19
19
  id,
20
20
  label: opts.label ?? DEFAULT_LABEL,
21
21
  async createPayment(order, ctx) {
22
- const shop = requireShopConfig();
23
- const continueTemplate = opts.continueUrl ?? shop.orderViewUrl;
24
- if (!/^https?:\/\//i.test(continueTemplate)) {
25
- throw new Error('payuAdapter: continueUrl (or shop.orderViewUrl) must be an absolute URL.');
22
+ let continueUrl;
23
+ const buyer = {};
24
+ if (ctx?.continueUrl) {
25
+ // Non-shop caller (e.g. booking) supplied everything skip shop hydration.
26
+ if (!/^https?:\/\//i.test(ctx.continueUrl)) {
27
+ throw new Error('payuAdapter: ctx.continueUrl must be an absolute URL.');
28
+ }
29
+ continueUrl = ctx.continueUrl;
30
+ buyer.firstName = ctx.buyer?.firstName;
31
+ buyer.lastName = ctx.buyer?.lastName;
32
+ buyer.phone = ctx.buyer?.phone;
33
+ buyer.language = ctx.language ?? undefined;
34
+ }
35
+ else {
36
+ // Shop path: hydrate buyer + continueUrl from the stored shop order.
37
+ const shop = requireShopConfig();
38
+ const continueTemplate = opts.continueUrl ?? shop.orderViewUrl;
39
+ if (!/^https?:\/\//i.test(continueTemplate)) {
40
+ throw new Error('payuAdapter: continueUrl (or shop.orderViewUrl) must be an absolute URL.');
41
+ }
42
+ const full = await getOrderById(order.id);
43
+ const names = splitFullName(full?.customerName);
44
+ continueUrl = buildOrderViewUrl(continueTemplate, {
45
+ orderNumber: order.number,
46
+ orderId: order.id,
47
+ accessToken: full?.accessToken ?? '',
48
+ language: ctx?.language ?? full?.language ?? null
49
+ });
50
+ buyer.firstName = names.firstName;
51
+ buyer.lastName = names.lastName;
52
+ buyer.phone = full?.customerPhone ?? undefined;
53
+ buyer.language = ctx?.language ?? full?.language ?? undefined;
26
54
  }
27
- // Hydrate buyer details from stored order (customerName/phone not on OrderRef)
28
- const full = await getOrderById(order.id);
29
- const names = splitFullName(full?.customerName);
30
- const continueUrl = buildOrderViewUrl(continueTemplate, {
31
- orderNumber: order.number,
32
- orderId: order.id,
33
- accessToken: full?.accessToken ?? '',
34
- language: ctx?.language ?? full?.language ?? null
35
- });
36
55
  const payload = buildPayuOrderPayload({
37
56
  order,
38
57
  posId: opts.posId,
39
58
  notifyUrl: opts.notifyUrl,
40
59
  continueUrl,
41
60
  customerIp: ctx?.customerIp ?? '127.0.0.1',
61
+ externalId: ctx?.externalId,
42
62
  buyer: {
43
- firstName: names.firstName,
44
- lastName: names.lastName,
45
- phone: full?.customerPhone ?? undefined,
46
- language: ctx?.language ?? full?.language ?? undefined
63
+ firstName: buyer.firstName,
64
+ lastName: buyer.lastName,
65
+ phone: buyer.phone,
66
+ language: buyer.language
47
67
  }
48
68
  });
49
69
  const { redirectUri, orderId } = await client.createOrder(payload);
@@ -32,6 +32,12 @@ export interface BuildPayuPayloadInput {
32
32
  buyer?: Omit<PayuBuyer, 'email'> & {
33
33
  email?: string;
34
34
  };
35
+ /**
36
+ * Unique `extOrderId` for this attempt. PayU requires it unique per POS, so a
37
+ * single order/booking paying twice (deposit + balance) must vary it. Falls
38
+ * back to `order.number` when omitted.
39
+ */
40
+ externalId?: string;
35
41
  }
36
42
  /**
37
43
  * Split a single "Imię Nazwisko" string into first/last name heuristically.
@@ -35,7 +35,7 @@ export function buildPayuOrderPayload(input) {
35
35
  description: input.description ?? `Order ${order.number}`,
36
36
  currencyCode: order.currency,
37
37
  totalAmount: String(order.totalGross),
38
- extOrderId: order.number,
38
+ extOrderId: input.externalId ?? order.number,
39
39
  buyer,
40
40
  products: [
41
41
  {
@@ -63,29 +63,44 @@ export function stripeAdapter(opts) {
63
63
  id,
64
64
  label: opts.label ?? DEFAULT_LABEL,
65
65
  async createPayment(order, ctx) {
66
- const shop = requireShopConfig();
67
- const successTemplate = opts.successUrl ?? shop.orderViewUrl;
68
- const cancelTemplate = opts.cancelUrl ?? successTemplate;
69
- if (!/^https?:\/\//i.test(successTemplate)) {
70
- throw new Error('stripeAdapter: successUrl (or shop.orderViewUrl) must be an absolute URL.');
66
+ let successUrl;
67
+ let cancelUrl;
68
+ let language;
69
+ if (ctx?.continueUrl) {
70
+ // Non-shop caller (e.g. booking) supplied a fully-resolved return URL
71
+ // use it verbatim and skip any shop-order hydration (mirrors payuAdapter).
72
+ if (!/^https?:\/\//i.test(ctx.continueUrl)) {
73
+ throw new Error('stripeAdapter: ctx.continueUrl must be an absolute URL.');
74
+ }
75
+ successUrl = ctx.continueUrl;
76
+ cancelUrl = ctx.continueUrl;
77
+ language = ctx.language ?? null;
71
78
  }
72
- if (!/^https?:\/\//i.test(cancelTemplate)) {
73
- throw new Error('stripeAdapter: cancelUrl must be an absolute URL.');
79
+ else {
80
+ const shop = requireShopConfig();
81
+ const successTemplate = opts.successUrl ?? shop.orderViewUrl;
82
+ const cancelTemplate = opts.cancelUrl ?? successTemplate;
83
+ if (!/^https?:\/\//i.test(successTemplate)) {
84
+ throw new Error('stripeAdapter: successUrl (or shop.orderViewUrl) must be an absolute URL.');
85
+ }
86
+ if (!/^https?:\/\//i.test(cancelTemplate)) {
87
+ throw new Error('stripeAdapter: cancelUrl must be an absolute URL.');
88
+ }
89
+ const full = await getOrderById(order.id);
90
+ language = ctx?.language ?? full?.language ?? null;
91
+ successUrl = buildOrderViewUrl(successTemplate, {
92
+ orderNumber: order.number,
93
+ orderId: order.id,
94
+ accessToken: full?.accessToken ?? '',
95
+ language
96
+ });
97
+ cancelUrl = buildOrderViewUrl(cancelTemplate, {
98
+ orderNumber: order.number,
99
+ orderId: order.id,
100
+ accessToken: full?.accessToken ?? '',
101
+ language
102
+ });
74
103
  }
75
- const full = await getOrderById(order.id);
76
- const language = ctx?.language ?? full?.language ?? null;
77
- const successUrl = buildOrderViewUrl(successTemplate, {
78
- orderNumber: order.number,
79
- orderId: order.id,
80
- accessToken: full?.accessToken ?? '',
81
- language
82
- });
83
- const cancelUrl = buildOrderViewUrl(cancelTemplate, {
84
- orderNumber: order.number,
85
- orderId: order.id,
86
- accessToken: full?.accessToken ?? '',
87
- language
88
- });
89
104
  const productNameTpl = pickI18n(opts.productName ?? DEFAULT_PRODUCT_NAME, language);
90
105
  const productName = productNameTpl.replace('{number}', order.number);
91
106
  const payload = buildStripeCheckoutPayload({
@@ -1,5 +1,14 @@
1
- import type { ShopConfig, ResolvedShopConfig } from './types.js';
2
- export declare function defineShop(config: ShopConfig): ResolvedShopConfig;
1
+ import type { ShopConfig } from './types.js';
2
+ import type { CMSModule } from '../types/modules.js';
3
+ /**
4
+ * Defines the shop module. Returns a {@link CMSModule} for
5
+ * `defineConfig({ modules: [defineShop({...})] })`. The resolved shop config
6
+ * is carried on `module.config` and reachable via `getCMS().getModuleConfig('shop')`
7
+ * (or the back-compat `getCMS().shopConfig`).
8
+ *
9
+ * @public
10
+ */
11
+ export declare function defineShop(config: ShopConfig): CMSModule;
3
12
  export { InvalidVariantAttributesError } from './variant-attributes.js';
4
13
  export { isVariantExpired, filterUpcoming, VariantExpiredError } from './expiry.js';
5
14
  export { manualAdapter } from './adapters/manual/index.js';