@endora-commerce/contracts 0.100.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 (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -0
  3. package/dist/actor.d.ts +79 -0
  4. package/dist/actor.d.ts.map +1 -0
  5. package/dist/actor.js +41 -0
  6. package/dist/actor.js.map +1 -0
  7. package/dist/addresses.d.ts +134 -0
  8. package/dist/addresses.d.ts.map +1 -0
  9. package/dist/addresses.js +16 -0
  10. package/dist/addresses.js.map +1 -0
  11. package/dist/admin-actions.d.ts +367 -0
  12. package/dist/admin-actions.d.ts.map +1 -0
  13. package/dist/admin-actions.js +287 -0
  14. package/dist/admin-actions.js.map +1 -0
  15. package/dist/admin-contributions.d.ts +518 -0
  16. package/dist/admin-contributions.d.ts.map +1 -0
  17. package/dist/admin-contributions.js +495 -0
  18. package/dist/admin-contributions.js.map +1 -0
  19. package/dist/admin-i18n.d.ts +135 -0
  20. package/dist/admin-i18n.d.ts.map +1 -0
  21. package/dist/admin-i18n.js +72 -0
  22. package/dist/admin-i18n.js.map +1 -0
  23. package/dist/admin-notifications.d.ts +55 -0
  24. package/dist/admin-notifications.d.ts.map +1 -0
  25. package/dist/admin-notifications.js +16 -0
  26. package/dist/admin-notifications.js.map +1 -0
  27. package/dist/admin-roles.d.ts +125 -0
  28. package/dist/admin-roles.d.ts.map +1 -0
  29. package/dist/admin-roles.js +2 -0
  30. package/dist/admin-roles.js.map +1 -0
  31. package/dist/admin-users.d.ts +178 -0
  32. package/dist/admin-users.d.ts.map +1 -0
  33. package/dist/admin-users.js +14 -0
  34. package/dist/admin-users.js.map +1 -0
  35. package/dist/admin.d.ts +243 -0
  36. package/dist/admin.d.ts.map +1 -0
  37. package/dist/admin.js +246 -0
  38. package/dist/admin.js.map +1 -0
  39. package/dist/analytics.d.ts +123 -0
  40. package/dist/analytics.d.ts.map +1 -0
  41. package/dist/analytics.js +68 -0
  42. package/dist/analytics.js.map +1 -0
  43. package/dist/api-keys.d.ts +97 -0
  44. package/dist/api-keys.d.ts.map +1 -0
  45. package/dist/api-keys.js +64 -0
  46. package/dist/api-keys.js.map +1 -0
  47. package/dist/assets-library.d.ts +684 -0
  48. package/dist/assets-library.d.ts.map +1 -0
  49. package/dist/assets-library.js +181 -0
  50. package/dist/assets-library.js.map +1 -0
  51. package/dist/audit-logs.d.ts +141 -0
  52. package/dist/audit-logs.d.ts.map +1 -0
  53. package/dist/audit-logs.js +31 -0
  54. package/dist/audit-logs.js.map +1 -0
  55. package/dist/auth.d.ts +174 -0
  56. package/dist/auth.d.ts.map +1 -0
  57. package/dist/auth.js +27 -0
  58. package/dist/auth.js.map +1 -0
  59. package/dist/blog.d.ts +669 -0
  60. package/dist/blog.d.ts.map +1 -0
  61. package/dist/blog.js +360 -0
  62. package/dist/blog.js.map +1 -0
  63. package/dist/capabilities.d.ts +40 -0
  64. package/dist/capabilities.d.ts.map +1 -0
  65. package/dist/capabilities.js +38 -0
  66. package/dist/capabilities.js.map +1 -0
  67. package/dist/carts.d.ts +1367 -0
  68. package/dist/carts.d.ts.map +1 -0
  69. package/dist/carts.js +405 -0
  70. package/dist/carts.js.map +1 -0
  71. package/dist/catalog.d.ts +2855 -0
  72. package/dist/catalog.d.ts.map +1 -0
  73. package/dist/catalog.js +1543 -0
  74. package/dist/catalog.js.map +1 -0
  75. package/dist/cms.d.ts +872 -0
  76. package/dist/cms.d.ts.map +1 -0
  77. package/dist/cms.js +468 -0
  78. package/dist/cms.js.map +1 -0
  79. package/dist/common.d.ts +82 -0
  80. package/dist/common.d.ts.map +1 -0
  81. package/dist/common.js +72 -0
  82. package/dist/common.js.map +1 -0
  83. package/dist/comparisons.d.ts +487 -0
  84. package/dist/comparisons.d.ts.map +1 -0
  85. package/dist/comparisons.js +221 -0
  86. package/dist/comparisons.js.map +1 -0
  87. package/dist/credentials.d.ts +292 -0
  88. package/dist/credentials.d.ts.map +1 -0
  89. package/dist/credentials.js +142 -0
  90. package/dist/credentials.js.map +1 -0
  91. package/dist/credit-limits.d.ts +111 -0
  92. package/dist/credit-limits.d.ts.map +1 -0
  93. package/dist/credit-limits.js +35 -0
  94. package/dist/credit-limits.js.map +1 -0
  95. package/dist/currencies.d.ts +127 -0
  96. package/dist/currencies.d.ts.map +1 -0
  97. package/dist/currencies.js +20 -0
  98. package/dist/currencies.js.map +1 -0
  99. package/dist/custom-fields.d.ts +345 -0
  100. package/dist/custom-fields.d.ts.map +1 -0
  101. package/dist/custom-fields.js +185 -0
  102. package/dist/custom-fields.js.map +1 -0
  103. package/dist/customer-accounts.d.ts +690 -0
  104. package/dist/customer-accounts.d.ts.map +1 -0
  105. package/dist/customer-accounts.js +41 -0
  106. package/dist/customer-accounts.js.map +1 -0
  107. package/dist/customers.d.ts +305 -0
  108. package/dist/customers.d.ts.map +1 -0
  109. package/dist/customers.js +158 -0
  110. package/dist/customers.js.map +1 -0
  111. package/dist/dictionary.d.ts +580 -0
  112. package/dist/dictionary.d.ts.map +1 -0
  113. package/dist/dictionary.js +297 -0
  114. package/dist/dictionary.js.map +1 -0
  115. package/dist/email-address.d.ts +62 -0
  116. package/dist/email-address.d.ts.map +1 -0
  117. package/dist/email-address.js +64 -0
  118. package/dist/email-address.js.map +1 -0
  119. package/dist/email.d.ts +175 -0
  120. package/dist/email.d.ts.map +1 -0
  121. package/dist/email.js +45 -0
  122. package/dist/email.js.map +1 -0
  123. package/dist/envelopes.d.ts +15 -0
  124. package/dist/envelopes.d.ts.map +1 -0
  125. package/dist/envelopes.js +16 -0
  126. package/dist/envelopes.js.map +1 -0
  127. package/dist/environment-inputs.d.ts +306 -0
  128. package/dist/environment-inputs.d.ts.map +1 -0
  129. package/dist/environment-inputs.js +277 -0
  130. package/dist/environment-inputs.js.map +1 -0
  131. package/dist/erp-connector.d.ts +52 -0
  132. package/dist/erp-connector.d.ts.map +1 -0
  133. package/dist/erp-connector.js +34 -0
  134. package/dist/erp-connector.js.map +1 -0
  135. package/dist/errors.d.ts +455 -0
  136. package/dist/errors.d.ts.map +1 -0
  137. package/dist/errors.js +532 -0
  138. package/dist/errors.js.map +1 -0
  139. package/dist/google-analytics.d.ts +181 -0
  140. package/dist/google-analytics.d.ts.map +1 -0
  141. package/dist/google-analytics.js +176 -0
  142. package/dist/google-analytics.js.map +1 -0
  143. package/dist/google-tag-manager.d.ts +111 -0
  144. package/dist/google-tag-manager.d.ts.map +1 -0
  145. package/dist/google-tag-manager.js +129 -0
  146. package/dist/google-tag-manager.js.map +1 -0
  147. package/dist/i18n.d.ts +69 -0
  148. package/dist/i18n.d.ts.map +1 -0
  149. package/dist/i18n.js +59 -0
  150. package/dist/i18n.js.map +1 -0
  151. package/dist/import-export.d.ts +63 -0
  152. package/dist/import-export.d.ts.map +1 -0
  153. package/dist/import-export.js +37 -0
  154. package/dist/import-export.js.map +1 -0
  155. package/dist/index.d.ts +82 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +126 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/inventory.d.ts +673 -0
  160. package/dist/inventory.d.ts.map +1 -0
  161. package/dist/inventory.js +412 -0
  162. package/dist/inventory.js.map +1 -0
  163. package/dist/invoice-ledger.d.ts +366 -0
  164. package/dist/invoice-ledger.d.ts.map +1 -0
  165. package/dist/invoice-ledger.js +114 -0
  166. package/dist/invoice-ledger.js.map +1 -0
  167. package/dist/invoices.d.ts +845 -0
  168. package/dist/invoices.d.ts.map +1 -0
  169. package/dist/invoices.js +314 -0
  170. package/dist/invoices.js.map +1 -0
  171. package/dist/kernel.d.ts +49 -0
  172. package/dist/kernel.d.ts.map +1 -0
  173. package/dist/kernel.js +19 -0
  174. package/dist/kernel.js.map +1 -0
  175. package/dist/languages.d.ts +122 -0
  176. package/dist/languages.d.ts.map +1 -0
  177. package/dist/languages.js +24 -0
  178. package/dist/languages.js.map +1 -0
  179. package/dist/linkedin-ads.d.ts +167 -0
  180. package/dist/linkedin-ads.d.ts.map +1 -0
  181. package/dist/linkedin-ads.js +156 -0
  182. package/dist/linkedin-ads.js.map +1 -0
  183. package/dist/megamenu.d.ts +556 -0
  184. package/dist/megamenu.d.ts.map +1 -0
  185. package/dist/megamenu.js +186 -0
  186. package/dist/megamenu.js.map +1 -0
  187. package/dist/meta-ads.d.ts +126 -0
  188. package/dist/meta-ads.d.ts.map +1 -0
  189. package/dist/meta-ads.js +112 -0
  190. package/dist/meta-ads.js.map +1 -0
  191. package/dist/mfa.d.ts +274 -0
  192. package/dist/mfa.d.ts.map +1 -0
  193. package/dist/mfa.js +187 -0
  194. package/dist/mfa.js.map +1 -0
  195. package/dist/modules.d.ts +1706 -0
  196. package/dist/modules.d.ts.map +1 -0
  197. package/dist/modules.js +1390 -0
  198. package/dist/modules.js.map +1 -0
  199. package/dist/newsletter.d.ts +611 -0
  200. package/dist/newsletter.d.ts.map +1 -0
  201. package/dist/newsletter.js +345 -0
  202. package/dist/newsletter.js.map +1 -0
  203. package/dist/orders.d.ts +1175 -0
  204. package/dist/orders.d.ts.map +1 -0
  205. package/dist/orders.js +630 -0
  206. package/dist/orders.js.map +1 -0
  207. package/dist/organizations.d.ts +938 -0
  208. package/dist/organizations.d.ts.map +1 -0
  209. package/dist/organizations.js +418 -0
  210. package/dist/organizations.js.map +1 -0
  211. package/dist/pagination.d.ts +21 -0
  212. package/dist/pagination.d.ts.map +1 -0
  213. package/dist/pagination.js +22 -0
  214. package/dist/pagination.js.map +1 -0
  215. package/dist/payment-methods.d.ts +472 -0
  216. package/dist/payment-methods.d.ts.map +1 -0
  217. package/dist/payment-methods.js +175 -0
  218. package/dist/payment-methods.js.map +1 -0
  219. package/dist/payment-return-url.d.ts +53 -0
  220. package/dist/payment-return-url.d.ts.map +1 -0
  221. package/dist/payment-return-url.js +35 -0
  222. package/dist/payment-return-url.js.map +1 -0
  223. package/dist/payments.d.ts +386 -0
  224. package/dist/payments.d.ts.map +1 -0
  225. package/dist/payments.js +84 -0
  226. package/dist/payments.js.map +1 -0
  227. package/dist/pim-connector.d.ts +60 -0
  228. package/dist/pim-connector.d.ts.map +1 -0
  229. package/dist/pim-connector.js +43 -0
  230. package/dist/pim-connector.js.map +1 -0
  231. package/dist/pim-field-path.d.ts +6 -0
  232. package/dist/pim-field-path.d.ts.map +1 -0
  233. package/dist/pim-field-path.js +101 -0
  234. package/dist/pim-field-path.js.map +1 -0
  235. package/dist/platform-language.d.ts +20 -0
  236. package/dist/platform-language.d.ts.map +1 -0
  237. package/dist/platform-language.js +22 -0
  238. package/dist/platform-language.js.map +1 -0
  239. package/dist/price-lists.d.ts +685 -0
  240. package/dist/price-lists.d.ts.map +1 -0
  241. package/dist/price-lists.js +330 -0
  242. package/dist/price-lists.js.map +1 -0
  243. package/dist/product-feeds.d.ts +2837 -0
  244. package/dist/product-feeds.d.ts.map +1 -0
  245. package/dist/product-feeds.js +1504 -0
  246. package/dist/product-feeds.js.map +1 -0
  247. package/dist/product-scope-overrides.d.ts +134 -0
  248. package/dist/product-scope-overrides.d.ts.map +1 -0
  249. package/dist/product-scope-overrides.js +82 -0
  250. package/dist/product-scope-overrides.js.map +1 -0
  251. package/dist/product-value-resolver.d.ts +88 -0
  252. package/dist/product-value-resolver.d.ts.map +1 -0
  253. package/dist/product-value-resolver.js +128 -0
  254. package/dist/product-value-resolver.js.map +1 -0
  255. package/dist/promotions.d.ts +678 -0
  256. package/dist/promotions.d.ts.map +1 -0
  257. package/dist/promotions.js +479 -0
  258. package/dist/promotions.js.map +1 -0
  259. package/dist/prompt-actions.d.ts +582 -0
  260. package/dist/prompt-actions.d.ts.map +1 -0
  261. package/dist/prompt-actions.js +221 -0
  262. package/dist/prompt-actions.js.map +1 -0
  263. package/dist/pwa.d.ts +293 -0
  264. package/dist/pwa.d.ts.map +1 -0
  265. package/dist/pwa.js +204 -0
  266. package/dist/pwa.js.map +1 -0
  267. package/dist/quick-order.d.ts +340 -0
  268. package/dist/quick-order.d.ts.map +1 -0
  269. package/dist/quick-order.js +177 -0
  270. package/dist/quick-order.js.map +1 -0
  271. package/dist/quote-requests.d.ts +538 -0
  272. package/dist/quote-requests.d.ts.map +1 -0
  273. package/dist/quote-requests.js +308 -0
  274. package/dist/quote-requests.js.map +1 -0
  275. package/dist/returns.d.ts +774 -0
  276. package/dist/returns.d.ts.map +1 -0
  277. package/dist/returns.js +389 -0
  278. package/dist/returns.js.map +1 -0
  279. package/dist/sales-channels.d.ts +392 -0
  280. package/dist/sales-channels.d.ts.map +1 -0
  281. package/dist/sales-channels.js +285 -0
  282. package/dist/sales-channels.js.map +1 -0
  283. package/dist/scope-notice.d.ts +60 -0
  284. package/dist/scope-notice.d.ts.map +1 -0
  285. package/dist/scope-notice.js +56 -0
  286. package/dist/scope-notice.js.map +1 -0
  287. package/dist/search.d.ts +321 -0
  288. package/dist/search.d.ts.map +1 -0
  289. package/dist/search.js +160 -0
  290. package/dist/search.js.map +1 -0
  291. package/dist/seo.d.ts +113 -0
  292. package/dist/seo.d.ts.map +1 -0
  293. package/dist/seo.js +63 -0
  294. package/dist/seo.js.map +1 -0
  295. package/dist/settings.d.ts +453 -0
  296. package/dist/settings.d.ts.map +1 -0
  297. package/dist/settings.js +337 -0
  298. package/dist/settings.js.map +1 -0
  299. package/dist/shipments.d.ts +140 -0
  300. package/dist/shipments.d.ts.map +1 -0
  301. package/dist/shipments.js +14 -0
  302. package/dist/shipments.js.map +1 -0
  303. package/dist/shipping-methods.d.ts +350 -0
  304. package/dist/shipping-methods.d.ts.map +1 -0
  305. package/dist/shipping-methods.js +99 -0
  306. package/dist/shipping-methods.js.map +1 -0
  307. package/dist/shopping-lists.d.ts +122 -0
  308. package/dist/shopping-lists.d.ts.map +1 -0
  309. package/dist/shopping-lists.js +92 -0
  310. package/dist/shopping-lists.js.map +1 -0
  311. package/dist/taxes.d.ts +106 -0
  312. package/dist/taxes.d.ts.map +1 -0
  313. package/dist/taxes.js +80 -0
  314. package/dist/taxes.js.map +1 -0
  315. package/dist/text-normalization.d.ts +199 -0
  316. package/dist/text-normalization.d.ts.map +1 -0
  317. package/dist/text-normalization.js +205 -0
  318. package/dist/text-normalization.js.map +1 -0
  319. package/dist/transactional-emails.d.ts +459 -0
  320. package/dist/transactional-emails.d.ts.map +1 -0
  321. package/dist/transactional-emails.js +212 -0
  322. package/dist/transactional-emails.js.map +1 -0
  323. package/dist/webhooks.d.ts +69 -0
  324. package/dist/webhooks.d.ts.map +1 -0
  325. package/dist/webhooks.js +53 -0
  326. package/dist/webhooks.js.map +1 -0
  327. package/package.json +46 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * One diacritic fold — and, since issue #245, one slug generator — for every
3
+ * package that has to compare or key two spellings of the same word.
4
+ *
5
+ * `foldDiacritics` is the fold (issue #240). `slugify` is the generator built
6
+ * on it, and it replaced **eight** private copies; its own doc block carries
7
+ * the reasons that are specific to slugs. Everything below is about the fold.
8
+ *
9
+ * ## Why this file exists rather than a helper inside one consumer
10
+ *
11
+ * The fold was correct and unreachable. It lived inside
12
+ * `normalizeOrganizationName` in `organizations.ts` — published from
13
+ * `@endora-commerce/contracts`, so importable from `backend/`, `admin/` and `storefront/`
14
+ * alike, but named after **one caller**. Nobody looking for "how do I compare
15
+ * `Łatwy` with `latwy`" searches for an organisation helper, so within a year
16
+ * four private copies had grown in `admin/` (repaired by !745) and two more
17
+ * stand in `backend/` today. Every one of them was the same one-liner and every
18
+ * one of them shipped the same bug.
19
+ *
20
+ * ## The bug the one-liner has
21
+ *
22
+ * ```
23
+ * input.normalize('NFD').replace(/\p{Diacritic}/gu, '').toLowerCase()
24
+ * ```
25
+ *
26
+ * reads as complete and is not. `ł` is U+0142: a single indivisible code point
27
+ * with **no canonical decomposition**, so NFD leaves it exactly where it was,
28
+ * the strip finds no combining mark to remove, and the `[^a-z0-9]+` pass a
29
+ * slugifier runs next deletes the letter outright — `Łatwy szablon` becomes
30
+ * `atwy-szablon`. The same holds for `Ø ø Đ đ Ð ð Þ þ ß Æ æ Œ œ`. The fix is one
31
+ * extra step, and it is the only reason this is longer than a line: map those
32
+ * letters explicitly.
33
+ *
34
+ * ## A function, not a port
35
+ *
36
+ * It is pure over its argument, so it cannot answer differently depending on
37
+ * which modules are switched on, and a gated port answering 503 `MODULE_DISABLED`
38
+ * to "fold these diacritics" would be a bug rather than a safety property.
39
+ * Node natives only (Principle IV) — a full Unicode-folding library is not worth
40
+ * a dependency for fifteen letters.
41
+ */
42
+ /**
43
+ * Latin letters NFD does not decompose, and their ASCII approximations.
44
+ *
45
+ * Every entry is a **single code point** whose Unicode decomposition is empty:
46
+ * a stroked, hooked or ligated letter rather than a base plus a combining mark.
47
+ * That is precisely the set NFD cannot help with, which is why it is hand-written.
48
+ *
49
+ * Exported so a test can enumerate it and a reader can see the whole set at
50
+ * once — not as an invitation to write a second fold around it. Reach for
51
+ * `foldDiacritics` instead; a copy of the map without the decomposition step
52
+ * is half a fold, and half a fold is what this file exists to end.
53
+ */
54
+ export const NON_DECOMPOSING_LATIN = {
55
+ Ł: 'L',
56
+ ł: 'l',
57
+ Ø: 'O',
58
+ ø: 'o',
59
+ Đ: 'D',
60
+ đ: 'd',
61
+ Ð: 'D',
62
+ ð: 'd',
63
+ Þ: 'Th',
64
+ þ: 'th',
65
+ ß: 'ss',
66
+ Æ: 'AE',
67
+ æ: 'ae',
68
+ Œ: 'OE',
69
+ œ: 'oe',
70
+ };
71
+ /**
72
+ * Fold a string so two spellings of the same word compare equal: diacritics
73
+ * removed, non-decomposing Latin letters mapped to ASCII, lowercased.
74
+ *
75
+ * In order:
76
+ *
77
+ * 1. **Decompose (NFD)** — separate every base character from its combining
78
+ * marks, so `ó` becomes `o` + U+0301.
79
+ * 2. **Strip the combining marks** via `\p{Diacritic}`.
80
+ * 3. **Map what NFD could not decompose**, per `NON_DECOMPOSING_LATIN`. This
81
+ * step runs **after** the decomposition on purpose: `Ǿ` (U+01FE) decomposes
82
+ * to `Ø` + U+0301, so a map applied first would not see the `Ø` that step 1
83
+ * is about to expose. Mapping first — as `admin/`'s copy did until issue
84
+ * #240 — folds `Ǿ` to `ø` instead of `o`.
85
+ * 4. **Lowercase.** Callers compare case-insensitively without exception, and
86
+ * the ASCII expansions above (`Th`, `AE`, `OE`) have to be lowered anyway.
87
+ *
88
+ * Whitespace is deliberately **not** touched: what counts as one space is the
89
+ * caller's policy, not the fold's. `normalizeOrganizationName` collapses runs
90
+ * and trims because it writes a search column; `admin`'s `normalize` only
91
+ * trims. Both compose it here rather than each folding its own way.
92
+ *
93
+ * Not a case-folding routine in the Unicode sense: it lowercases, it does not
94
+ * apply full case folding, and anything outside Latin (Cyrillic, CJK, Greek)
95
+ * passes through with its case lowered and nothing else changed.
96
+ */
97
+ export function foldDiacritics(input) {
98
+ const stripped = input.normalize('NFD').replace(/\p{Diacritic}/gu, '');
99
+ let mapped = '';
100
+ for (const character of stripped) {
101
+ mapped += NON_DECOMPOSING_LATIN[character] ?? character;
102
+ }
103
+ return mapped.toLowerCase();
104
+ }
105
+ /** Escape a separator so it can sit inside a character class. */
106
+ function escapeForRegExp(literal) {
107
+ return literal.replace(/[.*+?^${}()|[\]\\-]/g, '\\$&');
108
+ }
109
+ /**
110
+ * The repository's one slug generator (issue #245).
111
+ *
112
+ * There were **eight**: three in `backend/`, five in `admin/`, each a private
113
+ * four-line chain that read as obviously right. Two of them
114
+ * (`cms-template-layout.ts`, `pim_ergonode`'s `slugFromSourceCode`) had no fold
115
+ * step at all — `Żółw` produced `w` and `Świeże Ćwikła` produced `wie-e-wik-a`
116
+ * — and `check:diacritic-folds` was structurally blind to both, because it
117
+ * counts folds written *outside* the shared helper and a site that folds
118
+ * nothing writes none. A shared generator is what makes "does this slug fold?"
119
+ * a question with one answer.
120
+ *
121
+ * The steps, in order:
122
+ *
123
+ * 1. **Fold** via `foldDiacritics` — decompose, strip the combining marks, map
124
+ * the letters NFD leaves standing, lowercase.
125
+ * 2. **Collapse** every run of `[^a-z0-9]` — minus whatever `preserve` adds to
126
+ * the usable set — to the separator.
127
+ * 3. **Strip a leading separator**, before the slice — a leading separator can
128
+ * never survive, so it must not spend a character of the caller's budget.
129
+ * 4. **Slice** to `maxLength`, when the caller has one.
130
+ * 5. **Strip trailing separators**, after the slice — the cut can *create* one,
131
+ * which is the whole point (see below).
132
+ * 6. **Fall back** when nothing survived.
133
+ *
134
+ * ## NFD, not NFKD, and that is a decision
135
+ *
136
+ * Three of the eight normalised with `NFKD`, which additionally maps the
137
+ * compatibility characters: `fi` to `fi`, `²` to `2`, the full-width forms to
138
+ * their ASCII. Those mappings reach a slug **only** through characters that map
139
+ * *into* `[a-z0-9]`, and none of them is typed into a product name, a feed
140
+ * template name or a CMS page title. Where one does appear it now collapses to
141
+ * the separator rather than to a letter, so the slug stays legal and merely
142
+ * loses a character nobody could see in it. Against that, `ł` was being deleted
143
+ * out of every Polish name those three touched. The trade was taken for the
144
+ * admin pair in !753 and holds identically here.
145
+ *
146
+ * It has one consequence worth naming: two inputs `NFKD` kept apart can now
147
+ * fold together (`Kabel²` and `Kabel³` both become `kabel`). Every caller whose
148
+ * slug is unique-constrained already allocates against the live table —
149
+ * `CatalogAdminService.allocateUniqueSlug`, `pim_ergonode`'s
150
+ * `createWithFreeSlug` — so the second one is suffixed rather than rejected,
151
+ * exactly as two products sharing a plain name already are.
152
+ *
153
+ * ## The trailing strip runs after the slice, which is a change for six sites
154
+ *
155
+ * Six of the eight stripped **both** edge separators before cutting to length,
156
+ * so a cut landing on a separator left the slug ending in one:
157
+ * `...w-wersji-rozszerzona-`. Only `BlockEditor` and `PageEditor` had it right.
158
+ * This is the one change here that is not about diacritics; it shows only on an
159
+ * input long enough to be truncated, and it makes the result satisfy the kebab
160
+ * grammar (`^[a-z0-9]+(?:-[a-z0-9]+)*$`) that `pim_ergonode`'s own doc block
161
+ * already claimed for it.
162
+ *
163
+ * The **leading** strip stays where all eight had it — before the slice — for a
164
+ * reason that is not symmetry: a leading separator is never part of the answer,
165
+ * so letting it consume a character of `maxLength` would shorten every slug
166
+ * whose input begins with punctuation by one, in six sites at once, for no
167
+ * gain. Only the trailing edge can be *created* by the cut, so only the
168
+ * trailing strip has to run after it.
169
+ *
170
+ * Historical values are **not** migrated (owner's ruling, 2026-08-19,
171
+ * originally for the admin pair in !753 and extended to the rest): only two
172
+ * developer environments exist, so re-slugging live rows buys nobody anything,
173
+ * and every one of these values is computed fresh at create time rather than
174
+ * re-derived to look an existing row up. New values are correct from here on.
175
+ *
176
+ * **`pim_ergonode` is the exception the ruling names, and it is the reason
177
+ * `preserve` exists.** Its two derivations are re-derived on every import run
178
+ * *to find the row a previous run created*, so a changed derivation orphans the
179
+ * row rather than improving it. Those values are migrated (issue #260), and the
180
+ * migration is only affordable because the derivation change is confined to the
181
+ * fold — which is what `preserve` buys.
182
+ *
183
+ * `normalizeOrganizationName` is **not** a caller and must not become one: it
184
+ * writes the persisted `organizations.name_search` column, which is a folded
185
+ * name rather than a slug.
186
+ */
187
+ export function slugify(input, options = {}) {
188
+ const separator = options.separator ?? '-';
189
+ const escaped = escapeForRegExp(separator);
190
+ const usable = escapeForRegExp(options.preserve ?? '');
191
+ const folded = foldDiacritics(input);
192
+ // Two spellings of one step, and the branch is not a micro-optimisation. The
193
+ // default has to stay a **literal** `.replace(/[^a-z0-9]+/g, …)`, because
194
+ // that expression is what `check:diacritic-folds` reads to know this file is
195
+ // still the slug generator it exempts: its vacuous-pass guard cannot read a
196
+ // pattern built from a template, and a guard that cannot see the owner
197
+ // reports every private copy of it as fine (issue #244's whole point).
198
+ const collapsed = (usable.length === 0
199
+ ? folded.replace(/[^a-z0-9]+/g, separator)
200
+ : folded.replace(new RegExp(`[^a-z0-9${usable}]+`, 'g'), separator)).replace(new RegExp(`^(?:${escaped})+`), '');
201
+ const sliced = options.maxLength === undefined ? collapsed : collapsed.slice(0, options.maxLength);
202
+ const trimmed = sliced.replace(new RegExp(`(?:${escaped})+$`), '');
203
+ return trimmed.length > 0 ? trimmed : (options.fallback ?? '');
204
+ }
205
+ //# sourceMappingURL=text-normalization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-normalization.js","sourceRoot":"","sources":["../src/text-normalization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAqC;IACrE,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,IAAI;CACR,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;IACvE,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,qBAAqB,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC;AAC9B,CAAC;AAyCD,iEAAiE;AACjE,SAAS,eAAe,CAAC,OAAe;IACtC,OAAO,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,UAA0B,EAAE;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC;IAC3C,MAAM,OAAO,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,6EAA6E;IAC7E,0EAA0E;IAC1E,6EAA6E;IAC7E,4EAA4E;IAC5E,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,SAAS,GAAG,CAChB,MAAM,CAAC,MAAM,KAAK,CAAC;QACjB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC;QAC1C,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,WAAW,MAAM,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,CACtE,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACnG,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,MAAM,OAAO,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACnE,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,459 @@
1
+ import { z } from 'zod';
2
+ /** Opaque Puck data tree for one language. */
3
+ export declare const puckDataTreeSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
4
+ export type PuckDataTreeDto = z.infer<typeof puckDataTreeSchema>;
5
+ export declare const transactionalEmailCodeRe: RegExp;
6
+ /** A variable an email declares for substitution + preview sample data. */
7
+ export declare const emailVariableDescriptorSchema: z.ZodObject<{
8
+ key: z.ZodString;
9
+ label: z.ZodString;
10
+ sampleValue: z.ZodOptional<z.ZodString>;
11
+ description: z.ZodOptional<z.ZodString>;
12
+ }, z.core.$strip>;
13
+ export type EmailVariableDescriptor = z.infer<typeof emailVariableDescriptorSchema>;
14
+ export declare const transactionalEmailManifestEntrySchema: z.ZodObject<{
15
+ code: z.ZodString;
16
+ name: z.ZodString;
17
+ description: z.ZodOptional<z.ZodString>;
18
+ group: z.ZodOptional<z.ZodString>;
19
+ variables: z.ZodDefault<z.ZodArray<z.ZodObject<{
20
+ key: z.ZodString;
21
+ label: z.ZodString;
22
+ sampleValue: z.ZodOptional<z.ZodString>;
23
+ description: z.ZodOptional<z.ZodString>;
24
+ }, z.core.$strip>>>;
25
+ }, z.core.$strip>;
26
+ export type TransactionalEmailManifestEntry = z.infer<typeof transactionalEmailManifestEntrySchema>;
27
+ /**
28
+ * Whether an operator may switch this one email off, and why not (issue #89).
29
+ *
30
+ * Deliberately the same pair of field names `ModulePresenceSchema` uses for the
31
+ * module axis: the two refusals are the same statement at two granularities,
32
+ * and an admin that renders one can render the other without learning a second
33
+ * vocabulary. The reason is the owning module's own sentence, carried from the
34
+ * `EmailDefaultsRegistry` — no frontend holds a list of protected codes.
35
+ */
36
+ export declare const transactionalEmailProtectionSchema: z.ZodObject<{
37
+ deactivatable: z.ZodBoolean;
38
+ nonDeactivatableReason: z.ZodNullable<z.ZodString>;
39
+ }, z.core.$strip>;
40
+ export declare const transactionalEmailSummarySchema: z.ZodObject<{
41
+ code: z.ZodString;
42
+ name: z.ZodString;
43
+ ownerModule: z.ZodString;
44
+ group: z.ZodNullable<z.ZodString>;
45
+ active: z.ZodBoolean;
46
+ languages: z.ZodArray<z.ZodString>;
47
+ hasGlobalOverride: z.ZodBoolean;
48
+ hasChannelOverride: z.ZodBoolean;
49
+ deactivatable: z.ZodBoolean;
50
+ nonDeactivatableReason: z.ZodNullable<z.ZodString>;
51
+ }, z.core.$strip>;
52
+ export type TransactionalEmailSummary = z.infer<typeof transactionalEmailSummarySchema>;
53
+ export declare const transactionalEmailListResponseSchema: z.ZodObject<{
54
+ items: z.ZodArray<z.ZodObject<{
55
+ code: z.ZodString;
56
+ name: z.ZodString;
57
+ ownerModule: z.ZodString;
58
+ group: z.ZodNullable<z.ZodString>;
59
+ active: z.ZodBoolean;
60
+ languages: z.ZodArray<z.ZodString>;
61
+ hasGlobalOverride: z.ZodBoolean;
62
+ hasChannelOverride: z.ZodBoolean;
63
+ deactivatable: z.ZodBoolean;
64
+ nonDeactivatableReason: z.ZodNullable<z.ZodString>;
65
+ }, z.core.$strip>>;
66
+ }, z.core.$strip>;
67
+ export type TransactionalEmailListResponse = z.infer<typeof transactionalEmailListResponseSchema>;
68
+ export declare const resolvedEmailContentSchema: z.ZodObject<{
69
+ subject: z.ZodString;
70
+ content: z.ZodRecord<z.ZodString, z.ZodUnknown>;
71
+ source: z.ZodEnum<{
72
+ default: "default";
73
+ channel: "channel";
74
+ global: "global";
75
+ }>;
76
+ version: z.ZodNullable<z.ZodNumber>;
77
+ }, z.core.$strip>;
78
+ export type ResolvedEmailContent = z.infer<typeof resolvedEmailContentSchema>;
79
+ export declare const transactionalEmailDetailSchema: z.ZodObject<{
80
+ code: z.ZodString;
81
+ name: z.ZodString;
82
+ ownerModule: z.ZodString;
83
+ group: z.ZodNullable<z.ZodString>;
84
+ description: z.ZodNullable<z.ZodString>;
85
+ active: z.ZodBoolean;
86
+ languages: z.ZodArray<z.ZodString>;
87
+ variables: z.ZodArray<z.ZodObject<{
88
+ key: z.ZodString;
89
+ label: z.ZodString;
90
+ sampleValue: z.ZodOptional<z.ZodString>;
91
+ description: z.ZodOptional<z.ZodString>;
92
+ }, z.core.$strip>>;
93
+ scope: z.ZodObject<{
94
+ salesChannelId: z.ZodNullable<z.ZodString>;
95
+ language: z.ZodString;
96
+ }, z.core.$strip>;
97
+ effective: z.ZodObject<{
98
+ subject: z.ZodString;
99
+ content: z.ZodRecord<z.ZodString, z.ZodUnknown>;
100
+ source: z.ZodEnum<{
101
+ default: "default";
102
+ channel: "channel";
103
+ global: "global";
104
+ }>;
105
+ version: z.ZodNullable<z.ZodNumber>;
106
+ }, z.core.$strip>;
107
+ default: z.ZodObject<{
108
+ subject: z.ZodString;
109
+ content: z.ZodRecord<z.ZodString, z.ZodUnknown>;
110
+ }, z.core.$strip>;
111
+ hasGlobalOverride: z.ZodBoolean;
112
+ hasChannelOverride: z.ZodBoolean;
113
+ deactivatable: z.ZodBoolean;
114
+ nonDeactivatableReason: z.ZodNullable<z.ZodString>;
115
+ }, z.core.$strip>;
116
+ export type TransactionalEmailDetail = z.infer<typeof transactionalEmailDetailSchema>;
117
+ export declare const transactionalEmailDetailQuerySchema: z.ZodObject<{
118
+ salesChannelId: z.ZodOptional<z.ZodString>;
119
+ language: z.ZodOptional<z.ZodString>;
120
+ }, z.core.$strip>;
121
+ export type TransactionalEmailDetailQuery = z.infer<typeof transactionalEmailDetailQuerySchema>;
122
+ export declare const putEmailContentQuerySchema: z.ZodObject<{
123
+ salesChannelId: z.ZodOptional<z.ZodString>;
124
+ language: z.ZodString;
125
+ }, z.core.$strip>;
126
+ export type PutEmailContentQuery = z.infer<typeof putEmailContentQuerySchema>;
127
+ export declare const putEmailContentRequestSchema: z.ZodObject<{
128
+ subject: z.ZodString;
129
+ content: z.ZodRecord<z.ZodString, z.ZodUnknown>;
130
+ expectedVersion: z.ZodOptional<z.ZodNumber>;
131
+ }, z.core.$strip>;
132
+ export type PutEmailContentRequest = z.infer<typeof putEmailContentRequestSchema>;
133
+ /**
134
+ * The operator's per-email on/off switch.
135
+ *
136
+ * The module that owns this surface is non-deactivatable (issue #88), because
137
+ * the granularity the business wants is this one: an operator silences the
138
+ * back-in-stock notice without silencing account verification. The read side
139
+ * has honoured `active` since feature 047; this is the write side.
140
+ *
141
+ * Shaped like `ModuleActivationRequestSchema` — one boolean, stated rather than
142
+ * toggled, so a retry lands on the state the operator asked for instead of the
143
+ * opposite one.
144
+ */
145
+ export declare const setTransactionalEmailActiveRequestSchema: z.ZodObject<{
146
+ active: z.ZodBoolean;
147
+ }, z.core.$strip>;
148
+ export type SetTransactionalEmailActiveRequest = z.infer<typeof setTransactionalEmailActiveRequestSchema>;
149
+ /** The recomputed summary, so the list re-reads what the server resolved. */
150
+ export declare const setTransactionalEmailActiveResponseSchema: z.ZodObject<{
151
+ code: z.ZodString;
152
+ name: z.ZodString;
153
+ ownerModule: z.ZodString;
154
+ group: z.ZodNullable<z.ZodString>;
155
+ active: z.ZodBoolean;
156
+ languages: z.ZodArray<z.ZodString>;
157
+ hasGlobalOverride: z.ZodBoolean;
158
+ hasChannelOverride: z.ZodBoolean;
159
+ deactivatable: z.ZodBoolean;
160
+ nonDeactivatableReason: z.ZodNullable<z.ZodString>;
161
+ }, z.core.$strip>;
162
+ export type SetTransactionalEmailActiveResponse = z.infer<typeof setTransactionalEmailActiveResponseSchema>;
163
+ export declare const previewEmailRequestSchema: z.ZodObject<{
164
+ salesChannelId: z.ZodOptional<z.ZodString>;
165
+ language: z.ZodOptional<z.ZodString>;
166
+ draftSubject: z.ZodOptional<z.ZodString>;
167
+ draftContent: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
168
+ }, z.core.$strip>;
169
+ export type PreviewEmailRequest = z.infer<typeof previewEmailRequestSchema>;
170
+ export declare const previewEmailResponseSchema: z.ZodObject<{
171
+ subject: z.ZodString;
172
+ html: z.ZodString;
173
+ text: z.ZodString;
174
+ }, z.core.$strip>;
175
+ export type PreviewEmailResponse = z.infer<typeof previewEmailResponseSchema>;
176
+ export declare const emailBlockSummarySchema: z.ZodObject<{
177
+ id: z.ZodString;
178
+ code: z.ZodString;
179
+ name: z.ZodString;
180
+ active: z.ZodBoolean;
181
+ isSystem: z.ZodBoolean;
182
+ scope: z.ZodEnum<{
183
+ channel: "channel";
184
+ global: "global";
185
+ }>;
186
+ languages: z.ZodArray<z.ZodString>;
187
+ version: z.ZodNumber;
188
+ }, z.core.$strip>;
189
+ export type EmailBlockSummary = z.infer<typeof emailBlockSummarySchema>;
190
+ export declare const emailBlockDetailSchema: z.ZodObject<{
191
+ id: z.ZodString;
192
+ code: z.ZodString;
193
+ name: z.ZodString;
194
+ active: z.ZodBoolean;
195
+ isSystem: z.ZodBoolean;
196
+ scope: z.ZodEnum<{
197
+ channel: "channel";
198
+ global: "global";
199
+ }>;
200
+ languages: z.ZodArray<z.ZodString>;
201
+ version: z.ZodNumber;
202
+ description: z.ZodNullable<z.ZodString>;
203
+ content: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>;
204
+ salesChannelIds: z.ZodArray<z.ZodString>;
205
+ }, z.core.$strip>;
206
+ export type EmailBlockDetail = z.infer<typeof emailBlockDetailSchema>;
207
+ export declare const createEmailBlockRequestSchema: z.ZodObject<{
208
+ code: z.ZodString;
209
+ name: z.ZodString;
210
+ description: z.ZodOptional<z.ZodString>;
211
+ active: z.ZodOptional<z.ZodBoolean>;
212
+ salesChannelIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
213
+ languages: z.ZodArray<z.ZodString>;
214
+ }, z.core.$strip>;
215
+ export type CreateEmailBlockRequest = z.infer<typeof createEmailBlockRequestSchema>;
216
+ export declare const patchEmailBlockRequestSchema: z.ZodObject<{
217
+ name: z.ZodOptional<z.ZodString>;
218
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
219
+ active: z.ZodOptional<z.ZodBoolean>;
220
+ salesChannelIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
221
+ expectedVersion: z.ZodOptional<z.ZodNumber>;
222
+ }, z.core.$strip>;
223
+ export type PatchEmailBlockRequest = z.infer<typeof patchEmailBlockRequestSchema>;
224
+ export declare const putEmailBlockContentRequestSchema: z.ZodObject<{
225
+ content: z.ZodRecord<z.ZodString, z.ZodUnknown>;
226
+ expectedVersion: z.ZodNumber;
227
+ }, z.core.$strip>;
228
+ export type PutEmailBlockContentRequest = z.infer<typeof putEmailBlockContentRequestSchema>;
229
+ export declare const emailTemplateSummarySchema: z.ZodObject<{
230
+ version: z.ZodNumber;
231
+ languages: z.ZodArray<z.ZodString>;
232
+ id: z.ZodString;
233
+ code: z.ZodString;
234
+ name: z.ZodString;
235
+ isSystem: z.ZodBoolean;
236
+ scope: z.ZodEnum<{
237
+ channel: "channel";
238
+ global: "global";
239
+ }>;
240
+ }, z.core.$strip>;
241
+ export type EmailTemplateSummary = z.infer<typeof emailTemplateSummarySchema>;
242
+ export declare const emailTemplateDetailSchema: z.ZodObject<{
243
+ version: z.ZodNumber;
244
+ languages: z.ZodArray<z.ZodString>;
245
+ id: z.ZodString;
246
+ description: z.ZodNullable<z.ZodString>;
247
+ code: z.ZodString;
248
+ name: z.ZodString;
249
+ isSystem: z.ZodBoolean;
250
+ scope: z.ZodEnum<{
251
+ channel: "channel";
252
+ global: "global";
253
+ }>;
254
+ content: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>;
255
+ salesChannelIds: z.ZodArray<z.ZodString>;
256
+ }, z.core.$strip>;
257
+ export type EmailTemplateDetail = z.infer<typeof emailTemplateDetailSchema>;
258
+ export declare const createEmailTemplateRequestSchema: z.ZodObject<{
259
+ languages: z.ZodArray<z.ZodString>;
260
+ description: z.ZodOptional<z.ZodString>;
261
+ code: z.ZodString;
262
+ name: z.ZodString;
263
+ salesChannelIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
264
+ }, z.core.$strip>;
265
+ export type CreateEmailTemplateRequest = z.infer<typeof createEmailTemplateRequestSchema>;
266
+ export declare const patchEmailTemplateRequestSchema: z.ZodObject<{
267
+ name: z.ZodOptional<z.ZodString>;
268
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
269
+ salesChannelIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
270
+ expectedVersion: z.ZodOptional<z.ZodNumber>;
271
+ }, z.core.$strip>;
272
+ export type PatchEmailTemplateRequest = z.infer<typeof patchEmailTemplateRequestSchema>;
273
+ export declare const emailBrandingSchema: z.ZodObject<{
274
+ salesChannelId: z.ZodNullable<z.ZodString>;
275
+ logoAssetId: z.ZodString;
276
+ logoUrl: z.ZodString;
277
+ accentColor: z.ZodString;
278
+ headerBlockCode: z.ZodString;
279
+ footerBlockCode: z.ZodString;
280
+ source: z.ZodEnum<{
281
+ default: "default";
282
+ channel: "channel";
283
+ global: "global";
284
+ }>;
285
+ }, z.core.$strip>;
286
+ export type EmailBranding = z.infer<typeof emailBrandingSchema>;
287
+ export declare const putEmailBrandingRequestSchema: z.ZodObject<{
288
+ logoAssetId: z.ZodOptional<z.ZodString>;
289
+ accentColor: z.ZodOptional<z.ZodString>;
290
+ headerBlockCode: z.ZodOptional<z.ZodString>;
291
+ footerBlockCode: z.ZodOptional<z.ZodString>;
292
+ }, z.core.$strip>;
293
+ export type PutEmailBrandingRequest = z.infer<typeof putEmailBrandingRequestSchema>;
294
+ export declare const emailScopeQuerySchema: z.ZodObject<{
295
+ salesChannelId: z.ZodOptional<z.ZodString>;
296
+ }, z.core.$strip>;
297
+ export type EmailScopeQuery = z.infer<typeof emailScopeQuerySchema>;
298
+ export declare const emailPageBuilderDescriptorSchema: z.ZodObject<{
299
+ schemaVersion: z.ZodNumber;
300
+ components: z.ZodArray<z.ZodObject<{
301
+ name: z.ZodString;
302
+ previewIcon: z.ZodOptional<z.ZodString>;
303
+ }, z.core.$strip>>;
304
+ }, z.core.$strip>;
305
+ export type EmailPageBuilderDescriptor = z.infer<typeof emailPageBuilderDescriptorSchema>;
306
+ export interface TransactionalEmailSendInput {
307
+ code: string;
308
+ /**
309
+ * The channel whose content, branding and embeds apply, or `null` for the
310
+ * platform-wide ones.
311
+ *
312
+ * Widened from `string` (issue #103). The sender's own resolution chain has
313
+ * always been channel → global → default and has always spelled "global" as
314
+ * `null`; only this input type could not say it, so `invoices` passed `''`,
315
+ * which the settings seam guard rejects — the branding read threw, the
316
+ * dispatcher's `catch` absorbed it, and the invoice e-mail vanished.
317
+ */
318
+ salesChannelId: string | null;
319
+ language: string;
320
+ to: string;
321
+ variables: Record<string, unknown>;
322
+ /** Idempotency key — preserve the per-email message id used today. */
323
+ messageId: string;
324
+ /**
325
+ * Optional binary attachments (feature 047 invoices — PDF delivery). Uses
326
+ * `Uint8Array` (not Node's `Buffer`) so this shared contract stays
327
+ * browser-safe for the api-client; a backend `Buffer` satisfies it.
328
+ */
329
+ attachments?: Array<{
330
+ filename: string;
331
+ content: Uint8Array;
332
+ contentType?: string;
333
+ }>;
334
+ /**
335
+ * The business document this message delivers, when it delivers one (D-59).
336
+ * Forwarded to the transport so the delivery record is findable by the
337
+ * document an operator is asked about — an invoice, in the case the ruling
338
+ * was written for — rather than only by a recipient and a message id.
339
+ */
340
+ document?: {
341
+ type: string;
342
+ id: string;
343
+ };
344
+ meta?: Record<string, unknown>;
345
+ }
346
+ /**
347
+ * Why a send did — or did not — reach the transport.
348
+ *
349
+ * `send` used to answer `Promise<void>` for four unrelated situations, so a
350
+ * caller could not tell "the operator switched this email off" from "no
351
+ * template exists for this code yet". Only the latter justifies falling back to
352
+ * a caller's legacy in-code builder; the others mean the platform deliberately
353
+ * sent nothing and a fallback would send mail the operator did not ask for.
354
+ */
355
+ export type TransactionalSendOutcome =
356
+ /** Handed to the transport. */
357
+ {
358
+ status: 'sent';
359
+ }
360
+ /** The definition exists and an operator set `active = false`. */
361
+ | {
362
+ status: 'deactivated';
363
+ }
364
+ /** No mailer is wired in this composition — nothing can be delivered. */
365
+ | {
366
+ status: 'no_transport';
367
+ }
368
+ /** No definition for this code: the caller may use its own builder. */
369
+ | {
370
+ status: 'no_definition';
371
+ };
372
+ export interface TransactionalEmailSender {
373
+ /**
374
+ * An implementor must say which of the four happened — reporting nothing
375
+ * while delivering nothing is the defect this type exists to close. A
376
+ * deployment decoration that still answers `void` stops compiling against
377
+ * this interface, and that compile break is the intended signal.
378
+ */
379
+ send(input: TransactionalEmailSendInput): Promise<TransactionalSendOutcome>;
380
+ }
381
+ /** The subject and body a module ships for one of its own e-mails. */
382
+ export interface EmailDefaults {
383
+ /** Per-language subject: `{ lang: string }`. */
384
+ defaultSubject: Record<string, string>;
385
+ /** Content envelope: `{ schema_version, languages: { lang: tree } }`. */
386
+ defaultContent: Record<string, unknown>;
387
+ /**
388
+ * Declared exactly when this individual e-mail may never be switched off
389
+ * (issue #89) — the per-e-mail twin of a manifest's
390
+ * `activation.nonDeactivatable`, and named after it on purpose.
391
+ *
392
+ * It lives **here**, on the contribution the owning module already pushes,
393
+ * rather than as a list in `transactional_emails` or in the admin app: only
394
+ * the module that sends the e-mail knows whether its flow survives silence,
395
+ * and a list anywhere else is a second source of truth that drifts the first
396
+ * time a module adds one.
397
+ *
398
+ * Presence carries the "true": a shape with a separate boolean and a reason
399
+ * has a state where the two contradict each other, and this one does not.
400
+ */
401
+ nonDeactivatable?: {
402
+ reason: string;
403
+ };
404
+ }
405
+ /**
406
+ * Container name: `emailDefaultsPort`. Owner: `transactional_emails`.
407
+ *
408
+ * A **contribution seam**, and the most-contributed-to one in the tree: seven
409
+ * modules push the defaults for their own e-mails from `ctx.onBoot`, and this
410
+ * module's boot reconciler reads the table once.
411
+ *
412
+ * The ordering is by construction rather than by luck — boot hooks run during
413
+ * composition and plugin bodies only when the Fastify app is built, so every
414
+ * contribution lands before the read. Publishing the shape must not change
415
+ * that, and must not gate it: a gate would throw during composition, exactly
416
+ * as `assetReferenceRegistry`'s did before it was un-gated.
417
+ *
418
+ * **Owner off:** nothing throws here. This is a **contribution seam**, a plain
419
+ * `di.register` rather than a `providePort`, so a push still lands and
420
+ * `transactional_emails` filters by contributor when it enumerates. Converting it to
421
+ * `providePort` would move every edge into it from `contributes` to
422
+ * `fails-closed` in the deactivation-consequence ledger, and change the
423
+ * sentence the operator's confirmation dialog renders.
424
+ */
425
+ export interface EmailDefaultsRegistryPort {
426
+ register(code: string, defaults: EmailDefaults, ownerModuleId: string): void;
427
+ get(code: string): EmailDefaults | undefined;
428
+ has(code: string): boolean;
429
+ /** Which module contributed the code, or `undefined` when nobody did. */
430
+ ownerOf(code: string): string | undefined;
431
+ /** Every code and its contributor. */
432
+ owners(): ReadonlyMap<string, string>;
433
+ /**
434
+ * The reason this e-mail may never be switched off, or `null` when it may.
435
+ * The write path and the admin projection both read it, so the two never
436
+ * disagree.
437
+ */
438
+ nonDeactivatableReasonOf(code: string): string | null;
439
+ }
440
+ /**
441
+ * Container name: `templateEmailPort`. Owner: `transactional_emails`.
442
+ *
443
+ * `organizations` is the only cross-module consumer. `true` means "handled —
444
+ * do not use your legacy in-code builder", and it covers three outcomes: a
445
+ * delivered e-mail, one an operator deactivated, and a composition with no
446
+ * transport. In all three the platform decided what to send, and a fallback
447
+ * would either send mail the operator switched off or fail the same way. Only
448
+ * a code with no definition at all answers `false`.
449
+ */
450
+ export interface TemplateEmailPort {
451
+ trySend(input: {
452
+ code: string;
453
+ to: string;
454
+ messageId: string;
455
+ variables: Record<string, unknown>;
456
+ meta?: Record<string, unknown> | undefined;
457
+ }): Promise<boolean>;
458
+ }
459
+ //# sourceMappingURL=transactional-emails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional-emails.d.ts","sourceRoot":"","sources":["../src/transactional-emails.ts"],"names":[],"mappings":"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"}