@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,321 @@
1
+ import { z } from 'zod';
2
+ import { type ProductAudience, type ProductListSort, type ProductSummary } from './catalog.js';
3
+ /**
4
+ * Maximum allowed phrase length, in characters. Mirrors the
5
+ * `phrase varchar(512)` upper bound in `data-model.md §1.1` so the wire
6
+ * contract refuses anything that the storage layer would refuse anyway.
7
+ */
8
+ export declare const SEARCH_PHRASE_MAX_LENGTH = 512;
9
+ /**
10
+ * Hard cap on `limit`; the per-channel `search.popup.suggestion_count`
11
+ * setting is what callers normally observe. The cap exists so a misbehaving
12
+ * client cannot ask for more than the popup UX could realistically render.
13
+ */
14
+ export declare const SEARCH_SUGGEST_LIMIT_MAX = 50;
15
+ export declare const SearchSuggestQuerySchema: z.ZodObject<{
16
+ q: z.ZodString;
17
+ limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
18
+ }, z.core.$strip>;
19
+ export type SearchSuggestQuery = z.infer<typeof SearchSuggestQuerySchema>;
20
+ export declare const SearchSuggestMetaSchema: z.ZodObject<{
21
+ limit: z.ZodNumber;
22
+ minimumQueryLength: z.ZodNumber;
23
+ queryEcho: z.ZodString;
24
+ }, z.core.$strip>;
25
+ export type SearchSuggestMeta = z.infer<typeof SearchSuggestMetaSchema>;
26
+ /**
27
+ * Money carried by an enriched suggestion. The amount is a decimal STRING
28
+ * (e.g. "199.00") to preserve trailing zeros across the wire, matching the
29
+ * `resolved-price` endpoint's shape (feature 011) rather than the foundation
30
+ * `moneySchema` (which uses a number for the legacy `ProductSummary.price`).
31
+ */
32
+ export declare const searchSuggestMoneySchema: z.ZodObject<{
33
+ amount: z.ZodString;
34
+ currency: z.ZodString;
35
+ }, z.core.$strip>;
36
+ export type SearchSuggestMoney = z.infer<typeof searchSuggestMoneySchema>;
37
+ /**
38
+ * A typeahead suggestion. Extends the foundation `ProductSummary` (id, sku,
39
+ * name, slug, `primaryAssetUrl`, legacy `price`) with the per-customer
40
+ * price-list resolution so the popup can render the SKU, image, and the
41
+ * price the searching user would actually pay — honouring their price list
42
+ * and price-visibility (`priceDisplayMode`, where `none` hides the price).
43
+ *
44
+ * The pricing fields are optional: when the search module runs without a
45
+ * pricing enricher wired (foundation tests, Meilisearch-only deployments)
46
+ * the response degrades to a plain `ProductSummary` and the storefront
47
+ * falls back to the legacy `price` projection.
48
+ */
49
+ export declare const searchSuggestItemSchema: z.ZodObject<{
50
+ id: z.ZodString;
51
+ sku: z.ZodString;
52
+ type: z.ZodEnum<{
53
+ simple: "simple";
54
+ configurable: "configurable";
55
+ grouped: "grouped";
56
+ bundle: "bundle";
57
+ virtual: "virtual";
58
+ }>;
59
+ name: z.ZodString;
60
+ slug: z.ZodString;
61
+ categorySlugs: z.ZodArray<z.ZodString>;
62
+ primaryAssetUrl: z.ZodNullable<z.ZodString>;
63
+ price: z.ZodNullable<z.ZodObject<{
64
+ amount: z.ZodNumber;
65
+ currency: z.ZodString;
66
+ }, z.core.$strip>>;
67
+ stockIndicator: z.ZodNullable<z.ZodEnum<{
68
+ available: "available";
69
+ out_of_stock: "out_of_stock";
70
+ to_order: "to_order";
71
+ }>>;
72
+ stockLevel: z.ZodNullable<z.ZodNumber>;
73
+ priceUnavailable: z.ZodOptional<z.ZodBoolean>;
74
+ availability: z.ZodOptional<z.ZodObject<{
75
+ band: z.ZodEnum<{
76
+ available: "available";
77
+ out_of_stock: "out_of_stock";
78
+ high: "high";
79
+ medium: "medium";
80
+ low: "low";
81
+ }>;
82
+ inStock: z.ZodBoolean;
83
+ }, z.core.$strip>>;
84
+ basePrice: z.ZodOptional<z.ZodNullable<z.ZodObject<{
85
+ amount: z.ZodString;
86
+ currency: z.ZodString;
87
+ }, z.core.$strip>>>;
88
+ salePrice: z.ZodOptional<z.ZodNullable<z.ZodObject<{
89
+ amount: z.ZodString;
90
+ currency: z.ZodString;
91
+ }, z.core.$strip>>>;
92
+ priceDisplayMode: z.ZodOptional<z.ZodEnum<{
93
+ both: "both";
94
+ none: "none";
95
+ gross_only: "gross_only";
96
+ net_only: "net_only";
97
+ }>>;
98
+ }, z.core.$strip>;
99
+ export type SearchSuggestItem = z.infer<typeof searchSuggestItemSchema>;
100
+ export declare const SearchSuggestResponseSchema: z.ZodObject<{
101
+ data: z.ZodArray<z.ZodObject<{
102
+ id: z.ZodString;
103
+ sku: z.ZodString;
104
+ type: z.ZodEnum<{
105
+ simple: "simple";
106
+ configurable: "configurable";
107
+ grouped: "grouped";
108
+ bundle: "bundle";
109
+ virtual: "virtual";
110
+ }>;
111
+ name: z.ZodString;
112
+ slug: z.ZodString;
113
+ categorySlugs: z.ZodArray<z.ZodString>;
114
+ primaryAssetUrl: z.ZodNullable<z.ZodString>;
115
+ price: z.ZodNullable<z.ZodObject<{
116
+ amount: z.ZodNumber;
117
+ currency: z.ZodString;
118
+ }, z.core.$strip>>;
119
+ stockIndicator: z.ZodNullable<z.ZodEnum<{
120
+ available: "available";
121
+ out_of_stock: "out_of_stock";
122
+ to_order: "to_order";
123
+ }>>;
124
+ stockLevel: z.ZodNullable<z.ZodNumber>;
125
+ priceUnavailable: z.ZodOptional<z.ZodBoolean>;
126
+ availability: z.ZodOptional<z.ZodObject<{
127
+ band: z.ZodEnum<{
128
+ available: "available";
129
+ out_of_stock: "out_of_stock";
130
+ high: "high";
131
+ medium: "medium";
132
+ low: "low";
133
+ }>;
134
+ inStock: z.ZodBoolean;
135
+ }, z.core.$strip>>;
136
+ basePrice: z.ZodOptional<z.ZodNullable<z.ZodObject<{
137
+ amount: z.ZodString;
138
+ currency: z.ZodString;
139
+ }, z.core.$strip>>>;
140
+ salePrice: z.ZodOptional<z.ZodNullable<z.ZodObject<{
141
+ amount: z.ZodString;
142
+ currency: z.ZodString;
143
+ }, z.core.$strip>>>;
144
+ priceDisplayMode: z.ZodOptional<z.ZodEnum<{
145
+ both: "both";
146
+ none: "none";
147
+ gross_only: "gross_only";
148
+ net_only: "net_only";
149
+ }>>;
150
+ }, z.core.$strip>>;
151
+ meta: z.ZodObject<{
152
+ limit: z.ZodNumber;
153
+ minimumQueryLength: z.ZodNumber;
154
+ queryEcho: z.ZodString;
155
+ }, z.core.$strip>;
156
+ }, z.core.$strip>;
157
+ export type SearchSuggestResponse = z.infer<typeof SearchSuggestResponseSchema>;
158
+ /**
159
+ * The four `search.llm.*` settings whose values are inspected by the
160
+ * toggle wrapper. `enabled` must be present; the other three must be
161
+ * non-empty when `enabled=true`. The wrapper itself only writes the
162
+ * `enabled` setting — the embedder fields are written through the
163
+ * generic Settings admin route and read by the wrapper at validation
164
+ * time.
165
+ */
166
+ export declare const LlmToggleRequestSchema: z.ZodObject<{
167
+ enabled: z.ZodBoolean;
168
+ salesChannelCodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
169
+ expectedVersion: z.ZodOptional<z.ZodNullable<z.ZodString>>;
170
+ }, z.core.$strip>;
171
+ export type LlmToggleRequest = z.infer<typeof LlmToggleRequestSchema>;
172
+ export declare const LlmToggleResponseSchema: z.ZodObject<{
173
+ code: z.ZodLiteral<"search.llm.enabled">;
174
+ enabled: z.ZodBoolean;
175
+ newVersion: z.ZodString;
176
+ appliedChannelIds: z.ZodArray<z.ZodUUID>;
177
+ }, z.core.$strip>;
178
+ export type LlmToggleResponse = z.infer<typeof LlmToggleResponseSchema>;
179
+ /**
180
+ * Fire-and-forget analytics ingest. The storefront's `/search` page
181
+ * fires this on render, AFTER `listProducts` resolves so `resultCount`
182
+ * is meaningful. The endpoint always returns `202 { ok: true }` once
183
+ * the input passes shape validation; persistence failures degrade to a
184
+ * warn log (FR-015 — recording must never delay or fail the search
185
+ * response).
186
+ */
187
+ export declare const RecordPhraseRequestSchema: z.ZodObject<{
188
+ phrase: z.ZodString;
189
+ resultCount: z.ZodOptional<z.ZodNumber>;
190
+ }, z.core.$strip>;
191
+ export type RecordPhraseRequest = z.infer<typeof RecordPhraseRequestSchema>;
192
+ export declare const RecordPhraseResponseSchema: z.ZodObject<{
193
+ ok: z.ZodLiteral<true>;
194
+ }, z.core.$strip>;
195
+ export type RecordPhraseResponse = z.infer<typeof RecordPhraseResponseSchema>;
196
+ /**
197
+ * Triggers an immediate, synchronous full reindex of every sales-channel
198
+ * Meilisearch index — the same work the periodic background sweep performs
199
+ * (`search.reindex_interval_minutes`). Body is empty; the response carries a
200
+ * per-run summary so the admin UI can confirm what was rebuilt.
201
+ */
202
+ export declare const SearchReindexResponseSchema: z.ZodObject<{
203
+ channelsReindexed: z.ZodNumber;
204
+ documentCount: z.ZodNumber;
205
+ }, z.core.$strip>;
206
+ export type SearchReindexResponse = z.infer<typeof SearchReindexResponseSchema>;
207
+ /** The channel a search runs in, resolved before the query is built. */
208
+ export interface ResolvedSearchChannel {
209
+ id: string;
210
+ code: string;
211
+ isPublic: boolean;
212
+ defaultCurrency: string;
213
+ defaultLanguage: string;
214
+ }
215
+ export interface SearchQueryContext {
216
+ resolvedChannel: ResolvedSearchChannel;
217
+ /**
218
+ * Who is asking (issue #227). The index is not the authority on this: it
219
+ * carries `visibility` as a filterable attribute and has never carried
220
+ * `allowed_organization_ids` at all, so the answer is recomputed against the
221
+ * Postgres rows the hits hydrate from — the same place this path already
222
+ * insists the price comes from.
223
+ */
224
+ audience: ProductAudience;
225
+ preferredLanguage?: string | undefined;
226
+ }
227
+ export interface SearchListProductsParams {
228
+ q?: string | undefined;
229
+ limit: number;
230
+ cursor?: string | undefined;
231
+ /**
232
+ * Feature 086 widened this to the catalogue's own sort union so the two
233
+ * backends keep sharing one input type. The two price members can never
234
+ * reach this backend — `catalog/routes.public.ts` routes a price ordering to
235
+ * Postgres before it chooses Meilisearch — and `buildSort` answers `[]` for
236
+ * them, because returning a sort expression for a field the document does not
237
+ * carry would be a trap for the next author.
238
+ */
239
+ sort?: ProductListSort | undefined;
240
+ categorySlug?: string | undefined;
241
+ attributeFilters?: Record<string, string[]> | undefined;
242
+ }
243
+ export interface SearchListResult {
244
+ data: ProductSummary[];
245
+ pagination: {
246
+ cursor: string | null;
247
+ hasMore: boolean;
248
+ limit: number;
249
+ };
250
+ }
251
+ /**
252
+ * What a listing query answered: the page, or the fact that the index could
253
+ * not be reached.
254
+ *
255
+ * The second arm exists so the caller does not have to write a `catch` to
256
+ * learn it. `search` raises `SearchBackendUnavailable` internally when
257
+ * Meilisearch is unreachable; the port converts it here, on the owner's side,
258
+ * because a consumer catching it would be a `catch` around a port call — and
259
+ * that `catch` would swallow `ModuleDisabledError` too, turning "the operator
260
+ * switched `search` off" into "the index is slow today". Two different facts
261
+ * with two different right answers, fused by one `catch` clause.
262
+ *
263
+ * This is the `allowedIdsFor(): Promise<string[] | null>` shape AGENTS.md names
264
+ * as the worked example: where a degrade genuinely belongs, it goes inside the
265
+ * owner's implementation and is expressed in the return type.
266
+ */
267
+ export type SearchListOutcome = {
268
+ status: 'ok';
269
+ result: SearchListResult;
270
+ }
271
+ /** Meilisearch refused or timed out. `reason` is for the caller's log line. */
272
+ | {
273
+ status: 'index-unavailable';
274
+ reason: string;
275
+ };
276
+ /**
277
+ * Container name: `searchQueryPort`. Owner: `search`.
278
+ *
279
+ * **The one port in the sweep whose consumer is right to degrade rather than
280
+ * fail**, and the degrade is already where it belongs: `catalog`'s listing
281
+ * route checks the backend setting and falls back to its own Postgres query.
282
+ * That is a `nonBindingDependencies` edge, declared, not a `catch` — a search
283
+ * index being unavailable must not take the catalogue down with it.
284
+ *
285
+ * Note what the fallback is *not* allowed to be: a `try`/`catch` around the
286
+ * call. Catching here would swallow `ModuleDisabledError` and make a
287
+ * switched-off `search` look like a slow one, which is the fail-open shape
288
+ * `check:port-catches` exists for. That is why `listProducts` answers a
289
+ * {@link SearchListOutcome} rather than throwing: the consumer distinguishes
290
+ * the two states by reading a field, and never by catching.
291
+ */
292
+ export interface SearchQueryPort {
293
+ listProducts(params: SearchListProductsParams, ctx: SearchQueryContext): Promise<SearchListOutcome>;
294
+ }
295
+ /**
296
+ * Container name: `searchReindexPort`. Owner: `search`.
297
+ *
298
+ * A full rebuild of every channel's index, over the one indexer `searchModule`
299
+ * holds — so a composition has exactly one Meilisearch client whichever entry
300
+ * point triggers the reindex. `catalog` runs it as a `search_reindex` bulk
301
+ * operation when an attribute's `searchable` flag flips, and the `search:reindex`
302
+ * CLI command is the other caller.
303
+ *
304
+ * Published by `specs/117-instance-bring-up/` Phase 6, which is when it first
305
+ * had a consumer that resolves it **by name**: a composition root used to
306
+ * resolve it and hand `catalog` a closure, and a root's read is outside
307
+ * `check:port-shape`'s population, so the name went unpublished for as long as
308
+ * nothing inside a module asked for it.
309
+ *
310
+ * **Owner off:** `catalog` decides presence in front of the gate rather than
311
+ * catching behind it, and answers `{ documentCount: 0 }` — its manifest declares
312
+ * the edge `degrades-without`, because with `search` absent there is no index to
313
+ * rebuild and 503-ing an unrelated attribute edit would make an operator's
314
+ * supported off-switch fail a catalogue write.
315
+ */
316
+ export interface SearchReindexPort {
317
+ reindexAll(): Promise<{
318
+ documentCount: number;
319
+ }>;
320
+ }
321
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAOtB;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,eAAO,MAAM,wBAAwB;;;iBAWnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,uBAAuB;;;;iBASlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;iBAGnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAMhF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB;;;;iBAcjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,eAAO,MAAM,uBAAuB;;;;;iBAWlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAMxE;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB;;;iBAOpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,0BAA0B;;iBAErC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAM9E;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAWhF,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,qBAAqB,CAAC;IACvC;;;;;;OAMG;IACH,QAAQ,EAAE,eAAe,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,WAAW,wBAAwB;IACvC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE;AAC5C,+EAA+E;GAC7E;IAAE,MAAM,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,eAAe;IAC9B,YAAY,CACV,MAAM,EAAE,wBAAwB,EAChC,GAAG,EAAE,kBAAkB,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,IAAI,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD"}
package/dist/search.js ADDED
@@ -0,0 +1,160 @@
1
+ // Search module — feature 006 contract surface.
2
+ //
3
+ // Public API:
4
+ // - GET /api/v1/search/suggest (typeahead popup feed — US1)
5
+ // - POST /api/v1/search/record (analytics ingest — US3)
6
+ // - POST /api/v1/admin/search/llm/toggle (LLM-mode toggle wrapper — US2)
7
+ //
8
+ // Schemas live in three sections, populated by the user-story phases that
9
+ // own them. Foundation (T002) seeded the file empty; US1 (T009) adds the
10
+ // suggest contracts below. US2 / US3 schemas land later.
11
+ import { z } from 'zod';
12
+ import { productSummarySchema, } from './catalog.js';
13
+ import { displayModeSchema } from './price-lists.js';
14
+ // ---------------------------------------------------------------------------
15
+ // (1) GET /api/v1/search/suggest — US1 / T009
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Maximum allowed phrase length, in characters. Mirrors the
19
+ * `phrase varchar(512)` upper bound in `data-model.md §1.1` so the wire
20
+ * contract refuses anything that the storage layer would refuse anyway.
21
+ */
22
+ export const SEARCH_PHRASE_MAX_LENGTH = 512;
23
+ /**
24
+ * Hard cap on `limit`; the per-channel `search.popup.suggestion_count`
25
+ * setting is what callers normally observe. The cap exists so a misbehaving
26
+ * client cannot ask for more than the popup UX could realistically render.
27
+ */
28
+ export const SEARCH_SUGGEST_LIMIT_MAX = 50;
29
+ export const SearchSuggestQuerySchema = z.object({
30
+ q: z
31
+ .string()
32
+ .min(1, 'phrase is required')
33
+ .max(SEARCH_PHRASE_MAX_LENGTH, 'phrase exceeds the maximum length'),
34
+ limit: z.coerce
35
+ .number()
36
+ .int()
37
+ .positive()
38
+ .max(SEARCH_SUGGEST_LIMIT_MAX)
39
+ .optional(),
40
+ });
41
+ export const SearchSuggestMetaSchema = z.object({
42
+ /** Effective limit applied to the response. */
43
+ limit: z.number().int().nonnegative(),
44
+ /** The channel's resolved minimum query length, echoed back so the
45
+ * client can self-correct without a separate round-trip. */
46
+ minimumQueryLength: z.number().int().positive(),
47
+ /** Echo of `q` after trimming, for client-side reconciliation when the
48
+ * popup is rendering stale state. */
49
+ queryEcho: z.string(),
50
+ });
51
+ /**
52
+ * Money carried by an enriched suggestion. The amount is a decimal STRING
53
+ * (e.g. "199.00") to preserve trailing zeros across the wire, matching the
54
+ * `resolved-price` endpoint's shape (feature 011) rather than the foundation
55
+ * `moneySchema` (which uses a number for the legacy `ProductSummary.price`).
56
+ */
57
+ export const searchSuggestMoneySchema = z.object({
58
+ amount: z.string(),
59
+ currency: z.string(),
60
+ });
61
+ /**
62
+ * A typeahead suggestion. Extends the foundation `ProductSummary` (id, sku,
63
+ * name, slug, `primaryAssetUrl`, legacy `price`) with the per-customer
64
+ * price-list resolution so the popup can render the SKU, image, and the
65
+ * price the searching user would actually pay — honouring their price list
66
+ * and price-visibility (`priceDisplayMode`, where `none` hides the price).
67
+ *
68
+ * The pricing fields are optional: when the search module runs without a
69
+ * pricing enricher wired (foundation tests, Meilisearch-only deployments)
70
+ * the response degrades to a plain `ProductSummary` and the storefront
71
+ * falls back to the legacy `price` projection.
72
+ */
73
+ export const searchSuggestItemSchema = productSummarySchema.extend({
74
+ /** Resolved price-list Base price for this customer; `null` when no
75
+ * bracket applies or the price is hidden. */
76
+ basePrice: searchSuggestMoneySchema.nullable().optional(),
77
+ /** Resolved Sale price, when a sale list applies to this customer. */
78
+ salePrice: searchSuggestMoneySchema.nullable().optional(),
79
+ /** Resolved price-visibility mode. `none` ⇒ the storefront must hide the
80
+ * price and offer a quote affordance instead. */
81
+ priceDisplayMode: displayModeSchema.optional(),
82
+ });
83
+ export const SearchSuggestResponseSchema = z.object({
84
+ data: z.array(searchSuggestItemSchema),
85
+ meta: SearchSuggestMetaSchema,
86
+ });
87
+ // ---------------------------------------------------------------------------
88
+ // (2) POST /api/v1/admin/search/llm/toggle — US2 / T021
89
+ // ---------------------------------------------------------------------------
90
+ /**
91
+ * The four `search.llm.*` settings whose values are inspected by the
92
+ * toggle wrapper. `enabled` must be present; the other three must be
93
+ * non-empty when `enabled=true`. The wrapper itself only writes the
94
+ * `enabled` setting — the embedder fields are written through the
95
+ * generic Settings admin route and read by the wrapper at validation
96
+ * time.
97
+ */
98
+ export const LlmToggleRequestSchema = z.object({
99
+ enabled: z.boolean(),
100
+ /**
101
+ * When omitted, applies to every channel in the setting's scope
102
+ * (parallels SettingsAdminService.setValueForAllChannels). When
103
+ * present, applies only to the listed channels.
104
+ */
105
+ salesChannelCodes: z.array(z.string().min(1)).optional(),
106
+ /**
107
+ * Optimistic-concurrency token from the latest GET of
108
+ * `search.llm.enabled`. When stale, the call rejects with
109
+ * `409 VERSION_CONFLICT` (mirrors Settings' existing model).
110
+ */
111
+ expectedVersion: z.string().nullable().optional(),
112
+ });
113
+ export const LlmToggleResponseSchema = z.object({
114
+ /** Always `search.llm.enabled` — present for parity with the generic Settings route. */
115
+ code: z.literal('search.llm.enabled'),
116
+ enabled: z.boolean(),
117
+ /** New optimistic-concurrency token; the admin UI stores it for the next save. */
118
+ newVersion: z.string(),
119
+ /**
120
+ * Channel UUIDs whose `setting_values` row was written. Mirrors the
121
+ * `affectedChannelIds` SetValueResult shape from feature 004.
122
+ */
123
+ appliedChannelIds: z.array(z.uuid()),
124
+ });
125
+ // ---------------------------------------------------------------------------
126
+ // (3) POST /api/v1/search/record — US3 / T030
127
+ // ---------------------------------------------------------------------------
128
+ /**
129
+ * Fire-and-forget analytics ingest. The storefront's `/search` page
130
+ * fires this on render, AFTER `listProducts` resolves so `resultCount`
131
+ * is meaningful. The endpoint always returns `202 { ok: true }` once
132
+ * the input passes shape validation; persistence failures degrade to a
133
+ * warn log (FR-015 — recording must never delay or fail the search
134
+ * response).
135
+ */
136
+ export const RecordPhraseRequestSchema = z.object({
137
+ phrase: z
138
+ .string()
139
+ .min(1)
140
+ .max(SEARCH_PHRASE_MAX_LENGTH),
141
+ /** Result-page count; `0` for dead-end phrases (FR-014). */
142
+ resultCount: z.number().int().nonnegative().optional(),
143
+ });
144
+ export const RecordPhraseResponseSchema = z.object({
145
+ ok: z.literal(true),
146
+ });
147
+ // ---------------------------------------------------------------------------
148
+ // (4) POST /api/v1/admin/search/reindex — manual full Meilisearch reindex
149
+ // ---------------------------------------------------------------------------
150
+ /**
151
+ * Triggers an immediate, synchronous full reindex of every sales-channel
152
+ * Meilisearch index — the same work the periodic background sweep performs
153
+ * (`search.reindex_interval_minutes`). Body is empty; the response carries a
154
+ * per-run summary so the admin UI can confirm what was rebuilt.
155
+ */
156
+ export const SearchReindexResponseSchema = z.object({
157
+ channelsReindexed: z.number().int().nonnegative(),
158
+ documentCount: z.number().int().nonnegative(),
159
+ });
160
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
package/dist/seo.d.ts ADDED
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * SEO meta + sitemap contracts (T235 / FR-101, FR-102).
4
+ *
5
+ * The `entityType` enum is closed — only entities the storefront actually
6
+ * renders public pages for can carry meta overrides.
7
+ */
8
+ export declare const seoEntityTypeSchema: z.ZodEnum<{
9
+ category: "category";
10
+ cms_page: "cms_page";
11
+ product: "product";
12
+ }>;
13
+ export type SeoEntityType = z.infer<typeof seoEntityTypeSchema>;
14
+ export declare const resolvedMetaSchema: z.ZodObject<{
15
+ title: z.ZodString;
16
+ description: z.ZodString;
17
+ openGraph: z.ZodObject<{
18
+ title: z.ZodString;
19
+ description: z.ZodString;
20
+ image: z.ZodNullable<z.ZodString>;
21
+ }, z.core.$strip>;
22
+ source: z.ZodEnum<{
23
+ override: "override";
24
+ rule: "rule";
25
+ }>;
26
+ locale: z.ZodString;
27
+ }, z.core.$strip>;
28
+ export type ResolvedMeta = z.infer<typeof resolvedMetaSchema>;
29
+ export declare const seoMetaOverrideSchema: z.ZodObject<{
30
+ id: z.ZodString;
31
+ entityType: z.ZodEnum<{
32
+ category: "category";
33
+ cms_page: "cms_page";
34
+ product: "product";
35
+ }>;
36
+ entityId: z.ZodString;
37
+ locale: z.ZodString;
38
+ title: z.ZodNullable<z.ZodString>;
39
+ description: z.ZodNullable<z.ZodString>;
40
+ ogTitle: z.ZodNullable<z.ZodString>;
41
+ ogDescription: z.ZodNullable<z.ZodString>;
42
+ ogImageUrl: z.ZodNullable<z.ZodString>;
43
+ createdAt: z.ZodString;
44
+ updatedAt: z.ZodString;
45
+ }, z.core.$strip>;
46
+ export declare const upsertSeoMetaOverrideRequestSchema: z.ZodObject<{
47
+ locale: z.ZodString;
48
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
49
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
50
+ ogTitle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
51
+ ogDescription: z.ZodOptional<z.ZodNullable<z.ZodString>>;
52
+ ogImageUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
53
+ }, z.core.$strip>;
54
+ export type UpsertSeoMetaOverrideRequest = z.infer<typeof upsertSeoMetaOverrideRequestSchema>;
55
+ export declare const sitemapStatusSchema: z.ZodObject<{
56
+ generatedAt: z.ZodNullable<z.ZodString>;
57
+ byteSize: z.ZodNullable<z.ZodNumber>;
58
+ urlCount: z.ZodNullable<z.ZodNumber>;
59
+ }, z.core.$strip>;
60
+ export type SitemapStatus = z.infer<typeof sitemapStatusSchema>;
61
+ /**
62
+ * Per-channel sitemap row returned by the admin list endpoint
63
+ * (`GET /api/v1/admin/seo/sitemap`). One row per active sales channel —
64
+ * each carries its own cache (regenerate, preview and download act on a
65
+ * single channel) and its own resolved storefront URL.
66
+ */
67
+ export declare const sitemapChannelStatusSchema: z.ZodObject<{
68
+ generatedAt: z.ZodNullable<z.ZodString>;
69
+ byteSize: z.ZodNullable<z.ZodNumber>;
70
+ urlCount: z.ZodNullable<z.ZodNumber>;
71
+ salesChannelCode: z.ZodString;
72
+ salesChannelName: z.ZodString;
73
+ storefrontUrl: z.ZodString;
74
+ storefrontUrlSource: z.ZodEnum<{
75
+ env: "env";
76
+ setting: "setting";
77
+ fallback: "fallback";
78
+ }>;
79
+ }, z.core.$strip>;
80
+ export type SitemapChannelStatus = z.infer<typeof sitemapChannelStatusSchema>;
81
+ export declare const sitemapChannelListResponseSchema: z.ZodObject<{
82
+ data: z.ZodArray<z.ZodObject<{
83
+ generatedAt: z.ZodNullable<z.ZodString>;
84
+ byteSize: z.ZodNullable<z.ZodNumber>;
85
+ urlCount: z.ZodNullable<z.ZodNumber>;
86
+ salesChannelCode: z.ZodString;
87
+ salesChannelName: z.ZodString;
88
+ storefrontUrl: z.ZodString;
89
+ storefrontUrlSource: z.ZodEnum<{
90
+ env: "env";
91
+ setting: "setting";
92
+ fallback: "fallback";
93
+ }>;
94
+ }, z.core.$strip>>;
95
+ }, z.core.$strip>;
96
+ export type SitemapChannelListResponse = z.infer<typeof sitemapChannelListResponseSchema>;
97
+ /**
98
+ * Container name: `seoMetaWritePort`. Owner: `seo`.
99
+ *
100
+ * Narrow write surface for PIM importers (feature 089 / FR-029): upsert product
101
+ * meta overrides by locale. Channel dimension does not exist on Endora SEO rows.
102
+ *
103
+ * **Owner off:** resolving this port throws `ModuleDisabledError` (503).
104
+ */
105
+ export interface SeoMetaWritePort {
106
+ upsertProductMeta(input: {
107
+ productId: string;
108
+ locale: string;
109
+ title?: string | null;
110
+ description?: string | null;
111
+ }): Promise<void>;
112
+ }
113
+ //# sourceMappingURL=seo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo.d.ts","sourceRoot":"","sources":["../src/seo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AAEH,eAAO,MAAM,mBAAmB;;;;EAA8C,CAAC;AAC/E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;iBAW7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;iBAYhC,CAAC;AAEH,eAAO,MAAM,kCAAkC;;;;;;;iBAO7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,eAAO,MAAM,mBAAmB;;;;iBAI9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAKrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;iBAE3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,KAAK,EAAE;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB"}
package/dist/seo.js ADDED
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ import { isoDateTimeSchema, uuidSchema } from './common.js';
3
+ /**
4
+ * SEO meta + sitemap contracts (T235 / FR-101, FR-102).
5
+ *
6
+ * The `entityType` enum is closed — only entities the storefront actually
7
+ * renders public pages for can carry meta overrides.
8
+ */
9
+ export const seoEntityTypeSchema = z.enum(['product', 'category', 'cms_page']);
10
+ export const resolvedMetaSchema = z.object({
11
+ title: z.string(),
12
+ description: z.string(),
13
+ openGraph: z.object({
14
+ title: z.string(),
15
+ description: z.string(),
16
+ image: z.string().nullable(),
17
+ }),
18
+ /** Whether the resolved values came from a stored override or the rule. */
19
+ source: z.enum(['override', 'rule']),
20
+ locale: z.string(),
21
+ });
22
+ export const seoMetaOverrideSchema = z.object({
23
+ id: uuidSchema,
24
+ entityType: seoEntityTypeSchema,
25
+ entityId: uuidSchema,
26
+ locale: z.string().min(2).max(10),
27
+ title: z.string().nullable(),
28
+ description: z.string().nullable(),
29
+ ogTitle: z.string().nullable(),
30
+ ogDescription: z.string().nullable(),
31
+ ogImageUrl: z.string().url().nullable(),
32
+ createdAt: isoDateTimeSchema,
33
+ updatedAt: isoDateTimeSchema,
34
+ });
35
+ export const upsertSeoMetaOverrideRequestSchema = z.object({
36
+ locale: z.string().min(2).max(10),
37
+ title: z.string().min(1).max(160).nullable().optional(),
38
+ description: z.string().min(1).max(400).nullable().optional(),
39
+ ogTitle: z.string().min(1).max(160).nullable().optional(),
40
+ ogDescription: z.string().min(1).max(400).nullable().optional(),
41
+ ogImageUrl: z.string().url().nullable().optional(),
42
+ });
43
+ export const sitemapStatusSchema = z.object({
44
+ generatedAt: isoDateTimeSchema.nullable(),
45
+ byteSize: z.number().int().nonnegative().nullable(),
46
+ urlCount: z.number().int().nonnegative().nullable(),
47
+ });
48
+ /**
49
+ * Per-channel sitemap row returned by the admin list endpoint
50
+ * (`GET /api/v1/admin/seo/sitemap`). One row per active sales channel —
51
+ * each carries its own cache (regenerate, preview and download act on a
52
+ * single channel) and its own resolved storefront URL.
53
+ */
54
+ export const sitemapChannelStatusSchema = sitemapStatusSchema.extend({
55
+ salesChannelCode: z.string(),
56
+ salesChannelName: z.string(),
57
+ storefrontUrl: z.string(),
58
+ storefrontUrlSource: z.enum(['setting', 'env', 'fallback']),
59
+ });
60
+ export const sitemapChannelListResponseSchema = z.object({
61
+ data: z.array(sitemapChannelStatusSchema),
62
+ });
63
+ //# sourceMappingURL=seo.js.map