@sixtyninecommerce/client 0.30.0 → 0.31.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 (352) hide show
  1. package/README.md +548 -14
  2. package/dist/admin/http.d.ts.map +1 -1
  3. package/dist/admin/http.js.map +1 -1
  4. package/dist/admin/types.d.ts.map +1 -1
  5. package/dist/build-client.d.ts +9 -0
  6. package/dist/build-client.d.ts.map +1 -0
  7. package/dist/build-client.js +58 -0
  8. package/dist/build-client.js.map +1 -0
  9. package/dist/cache-tags.d.ts +35 -1
  10. package/dist/cache-tags.d.ts.map +1 -1
  11. package/dist/cache-tags.js +39 -1
  12. package/dist/cache-tags.js.map +1 -1
  13. package/dist/client.d.ts +25 -0
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/client.js +9 -62
  16. package/dist/client.js.map +1 -1
  17. package/dist/commerce/age-verification.d.ts +7 -4
  18. package/dist/commerce/age-verification.d.ts.map +1 -1
  19. package/dist/commerce/age-verification.js +6 -4
  20. package/dist/commerce/age-verification.js.map +1 -1
  21. package/dist/commerce/arguments.d.ts +13 -0
  22. package/dist/commerce/arguments.d.ts.map +1 -0
  23. package/dist/commerce/arguments.js +19 -0
  24. package/dist/commerce/arguments.js.map +1 -0
  25. package/dist/commerce/branding.d.ts.map +1 -1
  26. package/dist/commerce/branding.js.map +1 -1
  27. package/dist/commerce/capabilities.d.ts +40 -0
  28. package/dist/commerce/capabilities.d.ts.map +1 -0
  29. package/dist/commerce/capabilities.js +41 -0
  30. package/dist/commerce/capabilities.js.map +1 -0
  31. package/dist/commerce/cart.d.ts +9 -12
  32. package/dist/commerce/cart.d.ts.map +1 -1
  33. package/dist/commerce/cart.js +6 -1
  34. package/dist/commerce/cart.js.map +1 -1
  35. package/dist/commerce/checkout.d.ts +95 -15
  36. package/dist/commerce/checkout.d.ts.map +1 -1
  37. package/dist/commerce/checkout.js +230 -10
  38. package/dist/commerce/checkout.js.map +1 -1
  39. package/dist/commerce/customer.d.ts +82 -38
  40. package/dist/commerce/customer.d.ts.map +1 -1
  41. package/dist/commerce/customer.js +146 -10
  42. package/dist/commerce/customer.js.map +1 -1
  43. package/dist/commerce/favorites.d.ts +2 -5
  44. package/dist/commerce/favorites.d.ts.map +1 -1
  45. package/dist/commerce/favorites.js.map +1 -1
  46. package/dist/commerce/index.d.ts +189 -133
  47. package/dist/commerce/index.d.ts.map +1 -1
  48. package/dist/commerce/index.js +9 -0
  49. package/dist/commerce/index.js.map +1 -1
  50. package/dist/commerce/order-states.d.ts +18 -0
  51. package/dist/commerce/order-states.d.ts.map +1 -0
  52. package/dist/commerce/order-states.js +30 -0
  53. package/dist/commerce/order-states.js.map +1 -0
  54. package/dist/commerce/parcel-pricing.d.ts +48 -0
  55. package/dist/commerce/parcel-pricing.d.ts.map +1 -0
  56. package/dist/commerce/parcel-pricing.js +39 -0
  57. package/dist/commerce/parcel-pricing.js.map +1 -0
  58. package/dist/commerce/parked-orders.d.ts +22 -0
  59. package/dist/commerce/parked-orders.d.ts.map +1 -0
  60. package/dist/commerce/parked-orders.js +47 -0
  61. package/dist/commerce/parked-orders.js.map +1 -0
  62. package/dist/commerce/payments.d.ts +12 -5
  63. package/dist/commerce/payments.d.ts.map +1 -1
  64. package/dist/commerce/payments.js +10 -1
  65. package/dist/commerce/payments.js.map +1 -1
  66. package/dist/commerce/placement-error.d.ts +17 -0
  67. package/dist/commerce/placement-error.d.ts.map +1 -1
  68. package/dist/commerce/placement-error.js.map +1 -1
  69. package/dist/commerce/products.d.ts +92 -38
  70. package/dist/commerce/products.d.ts.map +1 -1
  71. package/dist/commerce/products.js +157 -123
  72. package/dist/commerce/products.js.map +1 -1
  73. package/dist/commerce/queries.d.ts +197 -48
  74. package/dist/commerce/queries.d.ts.map +1 -1
  75. package/dist/commerce/queries.js +1289 -746
  76. package/dist/commerce/queries.js.map +1 -1
  77. package/dist/commerce/quotes.d.ts +5 -4
  78. package/dist/commerce/quotes.d.ts.map +1 -1
  79. package/dist/commerce/quotes.js +1 -36
  80. package/dist/commerce/quotes.js.map +1 -1
  81. package/dist/commerce/recommendations.d.ts +3 -3
  82. package/dist/commerce/recommendations.d.ts.map +1 -1
  83. package/dist/commerce/recommendations.js +3 -1
  84. package/dist/commerce/recommendations.js.map +1 -1
  85. package/dist/commerce/returns.d.ts +7 -6
  86. package/dist/commerce/returns.d.ts.map +1 -1
  87. package/dist/commerce/returns.js.map +1 -1
  88. package/dist/commerce/reviews.d.ts +24 -15
  89. package/dist/commerce/reviews.d.ts.map +1 -1
  90. package/dist/commerce/reviews.js +67 -50
  91. package/dist/commerce/reviews.js.map +1 -1
  92. package/dist/commerce/saved-lists.d.ts +15 -15
  93. package/dist/commerce/saved-lists.d.ts.map +1 -1
  94. package/dist/commerce/saved-lists.js +67 -56
  95. package/dist/commerce/saved-lists.js.map +1 -1
  96. package/dist/commerce/search.d.ts +3 -2
  97. package/dist/commerce/search.d.ts.map +1 -1
  98. package/dist/commerce/search.js +2 -2
  99. package/dist/commerce/search.js.map +1 -1
  100. package/dist/commerce/shipping.d.ts +11 -4
  101. package/dist/commerce/shipping.d.ts.map +1 -1
  102. package/dist/commerce/shipping.js +17 -2
  103. package/dist/commerce/shipping.js.map +1 -1
  104. package/dist/commerce/stock-notifications.d.ts +2 -0
  105. package/dist/commerce/stock-notifications.d.ts.map +1 -1
  106. package/dist/commerce/stock-notifications.js +2 -2
  107. package/dist/commerce/stock-notifications.js.map +1 -1
  108. package/dist/commerce/subscriptions.d.ts +35 -37
  109. package/dist/commerce/subscriptions.d.ts.map +1 -1
  110. package/dist/commerce/subscriptions.js +159 -120
  111. package/dist/commerce/subscriptions.js.map +1 -1
  112. package/dist/commerce/vat-validation.d.ts +6 -3
  113. package/dist/commerce/vat-validation.d.ts.map +1 -1
  114. package/dist/commerce/vat-validation.js +24 -19
  115. package/dist/commerce/vat-validation.js.map +1 -1
  116. package/dist/constants.d.ts.map +1 -1
  117. package/dist/constants.js.map +1 -1
  118. package/dist/content/assets.d.ts +5 -4
  119. package/dist/content/assets.d.ts.map +1 -1
  120. package/dist/content/assets.js.map +1 -1
  121. package/dist/content/collection-pages.d.ts +3 -2
  122. package/dist/content/collection-pages.d.ts.map +1 -1
  123. package/dist/content/collection-pages.js.map +1 -1
  124. package/dist/content/collections.d.ts +5 -4
  125. package/dist/content/collections.d.ts.map +1 -1
  126. package/dist/content/collections.js.map +1 -1
  127. package/dist/content/filtered-products.d.ts.map +1 -1
  128. package/dist/content/filtered-products.js.map +1 -1
  129. package/dist/content/form-field-types.d.ts +3 -3
  130. package/dist/content/form-field-types.d.ts.map +1 -1
  131. package/dist/content/form-field-types.js.map +1 -1
  132. package/dist/content/forms.d.ts +4 -3
  133. package/dist/content/forms.d.ts.map +1 -1
  134. package/dist/content/forms.js +3 -0
  135. package/dist/content/forms.js.map +1 -1
  136. package/dist/content/index.d.ts +18 -17
  137. package/dist/content/index.d.ts.map +1 -1
  138. package/dist/content/items.d.ts +5 -4
  139. package/dist/content/items.d.ts.map +1 -1
  140. package/dist/content/items.js +2 -0
  141. package/dist/content/items.js.map +1 -1
  142. package/dist/content/menus.d.ts +3 -2
  143. package/dist/content/menus.d.ts.map +1 -1
  144. package/dist/content/menus.js +7 -1
  145. package/dist/content/menus.js.map +1 -1
  146. package/dist/content/resolve.d.ts +3 -2
  147. package/dist/content/resolve.d.ts.map +1 -1
  148. package/dist/content/resolve.js +1 -1
  149. package/dist/content/resolve.js.map +1 -1
  150. package/dist/content/sitemap.d.ts +3 -2
  151. package/dist/content/sitemap.d.ts.map +1 -1
  152. package/dist/content/sitemap.js.map +1 -1
  153. package/dist/content/strings.d.ts +16 -2
  154. package/dist/content/strings.d.ts.map +1 -1
  155. package/dist/content/strings.js +32 -4
  156. package/dist/content/strings.js.map +1 -1
  157. package/dist/core/context.d.ts +127 -1
  158. package/dist/core/context.d.ts.map +1 -1
  159. package/dist/core/context.js +436 -45
  160. package/dist/core/context.js.map +1 -1
  161. package/dist/core/currency.d.ts +14 -0
  162. package/dist/core/currency.d.ts.map +1 -0
  163. package/dist/core/currency.js +52 -0
  164. package/dist/core/currency.js.map +1 -0
  165. package/dist/core/deprecation.d.ts +23 -0
  166. package/dist/core/deprecation.d.ts.map +1 -0
  167. package/dist/core/deprecation.js +81 -0
  168. package/dist/core/deprecation.js.map +1 -0
  169. package/dist/core/http.d.ts +15 -1
  170. package/dist/core/http.d.ts.map +1 -1
  171. package/dist/core/http.js +55 -3
  172. package/dist/core/http.js.map +1 -1
  173. package/dist/core/public-artifact.d.ts +72 -0
  174. package/dist/core/public-artifact.d.ts.map +1 -0
  175. package/dist/core/public-artifact.js +122 -0
  176. package/dist/core/public-artifact.js.map +1 -0
  177. package/dist/core/unknown-field-rejection.d.ts +18 -0
  178. package/dist/core/unknown-field-rejection.d.ts.map +1 -0
  179. package/dist/core/unknown-field-rejection.js +37 -0
  180. package/dist/core/unknown-field-rejection.js.map +1 -0
  181. package/dist/credit/credit-status.d.ts +2 -0
  182. package/dist/credit/credit-status.d.ts.map +1 -1
  183. package/dist/credit/credit-status.js +71 -19
  184. package/dist/credit/credit-status.js.map +1 -1
  185. package/dist/credit/index.d.ts +1 -1
  186. package/dist/credit/payment-terms.d.ts +3 -3
  187. package/dist/credit/payment-terms.d.ts.map +1 -1
  188. package/dist/credit/payment-terms.js +28 -24
  189. package/dist/credit/payment-terms.js.map +1 -1
  190. package/dist/currency/exchange.d.ts +7 -7
  191. package/dist/currency/exchange.d.ts.map +1 -1
  192. package/dist/currency/exchange.js +5 -1
  193. package/dist/currency/exchange.js.map +1 -1
  194. package/dist/errors.d.ts +17 -0
  195. package/dist/errors.d.ts.map +1 -1
  196. package/dist/errors.js +13 -0
  197. package/dist/errors.js.map +1 -1
  198. package/dist/events/index.d.ts +20 -7
  199. package/dist/events/index.d.ts.map +1 -1
  200. package/dist/events/index.js +17 -5
  201. package/dist/events/index.js.map +1 -1
  202. package/dist/events/payloads.d.ts +7 -4
  203. package/dist/events/payloads.d.ts.map +1 -1
  204. package/dist/events/payloads.js +6 -3
  205. package/dist/events/payloads.js.map +1 -1
  206. package/dist/feeds/index.d.ts +5 -0
  207. package/dist/feeds/index.d.ts.map +1 -0
  208. package/dist/feeds/index.js +12 -0
  209. package/dist/feeds/index.js.map +1 -0
  210. package/dist/feeds/types.d.ts +18 -0
  211. package/dist/feeds/types.d.ts.map +1 -0
  212. package/dist/feeds/types.js +8 -0
  213. package/dist/feeds/types.js.map +1 -0
  214. package/dist/generated/cms.d.ts +2136 -0
  215. package/dist/generated/cms.d.ts.map +1 -0
  216. package/dist/generated/cms.js +4 -0
  217. package/dist/generated/cms.js.map +1 -0
  218. package/dist/generated/plugins.d.ts +550 -0
  219. package/dist/generated/plugins.d.ts.map +1 -0
  220. package/dist/generated/plugins.js +4 -0
  221. package/dist/generated/plugins.js.map +1 -0
  222. package/dist/generated/shop.d.ts +5539 -0
  223. package/dist/generated/shop.d.ts.map +1 -0
  224. package/dist/generated/shop.js +5 -0
  225. package/dist/generated/shop.js.map +1 -0
  226. package/dist/graphql-document.d.ts +29 -0
  227. package/dist/graphql-document.d.ts.map +1 -0
  228. package/dist/graphql-document.js +21 -0
  229. package/dist/graphql-document.js.map +1 -0
  230. package/dist/index.d.ts +9 -4
  231. package/dist/index.d.ts.map +1 -1
  232. package/dist/index.js +4 -2
  233. package/dist/index.js.map +1 -1
  234. package/dist/media/urls.d.ts.map +1 -1
  235. package/dist/media/urls.js +12 -1
  236. package/dist/media/urls.js.map +1 -1
  237. package/dist/media/video.d.ts.map +1 -1
  238. package/dist/media/video.js.map +1 -1
  239. package/dist/multi-store.d.ts +200 -0
  240. package/dist/multi-store.d.ts.map +1 -0
  241. package/dist/multi-store.js +558 -0
  242. package/dist/multi-store.js.map +1 -0
  243. package/dist/navigation/routes.d.ts +4 -3
  244. package/dist/navigation/routes.d.ts.map +1 -1
  245. package/dist/navigation/routes.js.map +1 -1
  246. package/dist/next/artifact-handler.d.ts +21 -13
  247. package/dist/next/artifact-handler.d.ts.map +1 -1
  248. package/dist/next/artifact-handler.js +28 -66
  249. package/dist/next/artifact-handler.js.map +1 -1
  250. package/dist/next/event-revalidate-handler.d.ts +26 -6
  251. package/dist/next/event-revalidate-handler.d.ts.map +1 -1
  252. package/dist/next/event-revalidate-handler.js +126 -19
  253. package/dist/next/event-revalidate-handler.js.map +1 -1
  254. package/dist/next/feed-handler.d.ts +50 -0
  255. package/dist/next/feed-handler.d.ts.map +1 -0
  256. package/dist/next/feed-handler.js +61 -0
  257. package/dist/next/feed-handler.js.map +1 -0
  258. package/dist/next/index.d.ts +3 -1
  259. package/dist/next/index.d.ts.map +1 -1
  260. package/dist/next/index.js +1 -0
  261. package/dist/next/index.js.map +1 -1
  262. package/dist/next/metadata.d.ts.map +1 -1
  263. package/dist/next/metadata.js.map +1 -1
  264. package/dist/next/redirect-middleware.d.ts +5 -31
  265. package/dist/next/redirect-middleware.d.ts.map +1 -1
  266. package/dist/next/redirect-middleware.js +4 -46
  267. package/dist/next/redirect-middleware.js.map +1 -1
  268. package/dist/next/revalidate-handler.d.ts.map +1 -1
  269. package/dist/next/revalidate-handler.js.map +1 -1
  270. package/dist/pricing/index.d.ts.map +1 -1
  271. package/dist/pricing/index.js.map +1 -1
  272. package/dist/pricing/tax-display.js +1 -1
  273. package/dist/pricing/tax-display.js.map +1 -1
  274. package/dist/pricing/tiers.d.ts +3 -2
  275. package/dist/pricing/tiers.d.ts.map +1 -1
  276. package/dist/pricing/tiers.js.map +1 -1
  277. package/dist/pricing/validation.d.ts +63 -12
  278. package/dist/pricing/validation.d.ts.map +1 -1
  279. package/dist/pricing/validation.js +60 -12
  280. package/dist/pricing/validation.js.map +1 -1
  281. package/dist/redirects/index.d.ts.map +1 -1
  282. package/dist/redirects/index.js +2 -1
  283. package/dist/redirects/index.js.map +1 -1
  284. package/dist/redirects/store.d.ts.map +1 -1
  285. package/dist/redirects/store.js +6 -2
  286. package/dist/redirects/store.js.map +1 -1
  287. package/dist/redirects/types.d.ts +5 -2
  288. package/dist/redirects/types.d.ts.map +1 -1
  289. package/dist/revalidation/event-plan.d.ts +55 -4
  290. package/dist/revalidation/event-plan.d.ts.map +1 -1
  291. package/dist/revalidation/event-plan.js +115 -8
  292. package/dist/revalidation/event-plan.js.map +1 -1
  293. package/dist/revalidation/event-verify.d.ts +8 -37
  294. package/dist/revalidation/event-verify.d.ts.map +1 -1
  295. package/dist/revalidation/event-verify.js +7 -97
  296. package/dist/revalidation/event-verify.js.map +1 -1
  297. package/dist/revalidation/types.d.ts.map +1 -1
  298. package/dist/revalidation/url-context.d.ts +17 -0
  299. package/dist/revalidation/url-context.d.ts.map +1 -1
  300. package/dist/revalidation/url-context.js +35 -2
  301. package/dist/revalidation/url-context.js.map +1 -1
  302. package/dist/seo/ai.d.ts.map +1 -1
  303. package/dist/seo/ai.js.map +1 -1
  304. package/dist/seo/artifacts.d.ts +86 -4
  305. package/dist/seo/artifacts.d.ts.map +1 -1
  306. package/dist/seo/artifacts.js +115 -16
  307. package/dist/seo/artifacts.js.map +1 -1
  308. package/dist/seo/availability.d.ts +2 -2
  309. package/dist/seo/availability.d.ts.map +1 -1
  310. package/dist/seo/availability.js.map +1 -1
  311. package/dist/seo/index.d.ts +2 -0
  312. package/dist/seo/index.d.ts.map +1 -1
  313. package/dist/seo/index.js +3 -1
  314. package/dist/seo/index.js.map +1 -1
  315. package/dist/seo/json-ld.d.ts.map +1 -1
  316. package/dist/seo/json-ld.js.map +1 -1
  317. package/dist/seo/meta-tags.d.ts.map +1 -1
  318. package/dist/seo/meta-tags.js.map +1 -1
  319. package/dist/seo/metadata-parser.d.ts.map +1 -1
  320. package/dist/seo/metadata-parser.js.map +1 -1
  321. package/dist/seo/metadata-types.d.ts.map +1 -1
  322. package/dist/seo/robots.d.ts.map +1 -1
  323. package/dist/seo/robots.js.map +1 -1
  324. package/dist/seo/types.d.ts +9 -2
  325. package/dist/seo/types.d.ts.map +1 -1
  326. package/dist/seo/url-builder.d.ts +5 -3
  327. package/dist/seo/url-builder.d.ts.map +1 -1
  328. package/dist/seo/url-builder.js +3 -1
  329. package/dist/seo/url-builder.js.map +1 -1
  330. package/dist/types.d.ts +1226 -2522
  331. package/dist/types.d.ts.map +1 -1
  332. package/dist/web/artifact-responder.d.ts +69 -0
  333. package/dist/web/artifact-responder.d.ts.map +1 -0
  334. package/dist/web/artifact-responder.js +90 -0
  335. package/dist/web/artifact-responder.js.map +1 -0
  336. package/dist/web/feed-responder.d.ts +72 -0
  337. package/dist/web/feed-responder.d.ts.map +1 -0
  338. package/dist/web/feed-responder.js +81 -0
  339. package/dist/web/feed-responder.js.map +1 -0
  340. package/dist/web/index.d.ts +23 -0
  341. package/dist/web/index.d.ts.map +1 -0
  342. package/dist/web/index.js +19 -0
  343. package/dist/web/index.js.map +1 -0
  344. package/dist/web/redirect-middleware.d.ts +76 -0
  345. package/dist/web/redirect-middleware.d.ts.map +1 -0
  346. package/dist/web/redirect-middleware.js +94 -0
  347. package/dist/web/redirect-middleware.js.map +1 -0
  348. package/dist/web/respond.d.ts +20 -0
  349. package/dist/web/respond.d.ts.map +1 -0
  350. package/dist/web/respond.js +52 -0
  351. package/dist/web/respond.js.map +1 -0
  352. package/package.json +18 -2
@@ -3,20 +3,23 @@
3
3
  * storefront's own origin (fetch-and-serve — hosting-adapter-independent,
4
4
  * unlike next.config rewrites).
5
5
  *
6
- * Two deliberate header behaviors:
7
- * - The inbound client User-Agent is NOT forwarded — the platform host blocks
8
- * known crawler UAs, and it must see the storefront server, not Googlebot.
9
- * - X-Robots-Tag from the platform host is dropped; Cache-Control is replaced
10
- * with the storefront's own policy.
6
+ * A thin wrapper over `createSeoArtifactResponder` from
7
+ * `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
8
+ * crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
9
+ * the storefront's, `ETag`/`If-None-Match` pass through). Two things are Next's
10
+ * own:
11
+ * - the upstream fetch carries `next: { tags: ['seo-artifact'] }`, the tag both
12
+ * revalidation handlers purge;
13
+ * - it fetches with the GLOBAL `fetch`, read per request, not the client's
14
+ * configured one — Next's data cache reads `next.tags` only through the
15
+ * `fetch` it patched onto `globalThis`, and this handler has always used it.
11
16
  */
17
+ import { type MultiStoreClient } from '../multi-store.js';
18
+ import { type SeoArtifact } from '../seo/artifacts.js';
12
19
  import type { StorefrontClient } from '../types.js';
13
- export type SeoArtifact = 'sitemap' | 'sitemap-child' | 'robots' | 'llms-txt' | 'indexnow-key';
14
- export interface SeoArtifactHandlerOptions {
15
- /** Cache-Control served to crawlers/CDN. */
16
- cacheControl?: string;
17
- /** Upstream fetch timeout. Default 15s. */
18
- timeoutMs?: number;
19
- }
20
+ import { type SeoArtifactResponderOptions } from '../web/artifact-responder.js';
21
+ export type { SeoArtifact } from '../seo/artifacts.js';
22
+ export type SeoArtifactHandlerOptions = SeoArtifactResponderOptions;
20
23
  /**
21
24
  * Usage (App Router):
22
25
  * ```ts
@@ -25,8 +28,13 @@ export interface SeoArtifactHandlerOptions {
25
28
  * // src/app/sitemaps/[name]/route.ts
26
29
  * export const GET = createSeoArtifactHandler(getClient(), 'sitemap-child');
27
30
  * ```
31
+ *
32
+ * Several stores in one app: pass the store router
33
+ * (`createStorefrontClient({ resolveStore })`). Each request is served the
34
+ * artifact of the store it resolves to, tagged for that store too, and a
35
+ * request no store serves is answered 404.
28
36
  */
29
- export declare function createSeoArtifactHandler(client: StorefrontClient | Promise<StorefrontClient>, artifact: SeoArtifact, options?: SeoArtifactHandlerOptions): (request: Request, context?: {
37
+ export declare function createSeoArtifactHandler(client: StorefrontClient | Promise<StorefrontClient> | MultiStoreClient, artifact: SeoArtifact, options?: SeoArtifactHandlerOptions): (request: Request, context?: {
30
38
  params?: Promise<{
31
39
  name?: string;
32
40
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-handler.d.ts","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,eAAe,GAAG,QAAQ,GAAG,UAAU,GAAG,cAAc,CAAC;AAE/F,MAAM,WAAW,yBAAyB;IACxC,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsBD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,EACpD,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,yBAAyB,GAClC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoD5F"}
1
+ {"version":3,"file":"artifact-handler.d.ts","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,8BAA8B,CAAC;AAGtC,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,EACvE,QAAQ,EAAE,WAAW,EACrB,OAAO,CAAC,EAAE,yBAAyB,GAClC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAuB5F"}
@@ -3,32 +3,22 @@
3
3
  * storefront's own origin (fetch-and-serve — hosting-adapter-independent,
4
4
  * unlike next.config rewrites).
5
5
  *
6
- * Two deliberate header behaviors:
7
- * - The inbound client User-Agent is NOT forwarded — the platform host blocks
8
- * known crawler UAs, and it must see the storefront server, not Googlebot.
9
- * - X-Robots-Tag from the platform host is dropped; Cache-Control is replaced
10
- * with the storefront's own policy.
6
+ * A thin wrapper over `createSeoArtifactResponder` from
7
+ * `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
8
+ * crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
9
+ * the storefront's, `ETag`/`If-None-Match` pass through). Two things are Next's
10
+ * own:
11
+ * - the upstream fetch carries `next: { tags: ['seo-artifact'] }`, the tag both
12
+ * revalidation handlers purge;
13
+ * - it fetches with the GLOBAL `fetch`, read per request, not the client's
14
+ * configured one — Next's data cache reads `next.tags` only through the
15
+ * `fetch` it patched onto `globalThis`, and this handler has always used it.
11
16
  */
12
17
  import { SEO_ARTIFACT_TAG } from '../cache-tags.js';
13
- const DEFAULT_CACHE_CONTROL = 'public, max-age=300, s-maxage=3600, stale-while-revalidate=86400';
14
- const DEFAULT_TIMEOUT_MS = 15_000;
15
- function upstreamUrl(client, artifact, childName) {
16
- switch (artifact) {
17
- case 'sitemap':
18
- return client.seo.sitemapUrl();
19
- case 'sitemap-child': {
20
- if (!childName)
21
- throw new Error('sitemap-child requires a file name');
22
- return client.seo.sitemapUrl(childName);
23
- }
24
- case 'robots':
25
- return client.seo.robotsUrl();
26
- case 'llms-txt':
27
- return client.seo.llmsTxtUrl();
28
- case 'indexnow-key':
29
- return client.seo.indexnowKeyUrl();
30
- }
31
- }
18
+ import { adapterCacheTags } from '../multi-store.js';
19
+ import { fetchSeoArtifact } from '../seo/artifacts.js';
20
+ import { buildSeoArtifactResponder, } from '../web/artifact-responder.js';
21
+ import { failureResponse } from '../web/respond.js';
32
22
  /**
33
23
  * Usage (App Router):
34
24
  * ```ts
@@ -37,55 +27,27 @@ function upstreamUrl(client, artifact, childName) {
37
27
  * // src/app/sitemaps/[name]/route.ts
38
28
  * export const GET = createSeoArtifactHandler(getClient(), 'sitemap-child');
39
29
  * ```
30
+ *
31
+ * Several stores in one app: pass the store router
32
+ * (`createStorefrontClient({ resolveStore })`). Each request is served the
33
+ * artifact of the store it resolves to, tagged for that store too, and a
34
+ * request no store serves is answered 404.
40
35
  */
41
36
  export function createSeoArtifactHandler(client, artifact, options) {
42
- const cacheControl = options?.cacheControl ?? DEFAULT_CACHE_CONTROL;
43
- const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
37
+ const respond = buildSeoArtifactResponder(client, artifact, options, (resolved, which, read) => fetchSeoArtifact(resolved.seo, (input, init) => globalThis.fetch(input, init), which, read,
38
+ // Tagged so the `seo-artifact` purge both revalidation handlers issue
39
+ // reaches a cached upstream fetch; before this tag, nothing carried it.
40
+ // A store router's request also carries the store-scoped copy.
41
+ { next: { tags: adapterCacheTags(resolved, SEO_ARTIFACT_TAG) } }));
44
42
  return async (request, context) => {
43
+ let params;
45
44
  try {
46
- const resolvedClient = await client;
47
- const params = context?.params ? await context.params : undefined;
48
- const url = upstreamUrl(resolvedClient, artifact, params?.name);
49
- const headers = {};
50
- const ifNoneMatch = request.headers.get('if-none-match');
51
- if (ifNoneMatch)
52
- headers['If-None-Match'] = ifNoneMatch;
53
- // Tagged so the `seo-artifact` purge both revalidation handlers issue
54
- // reaches a cached upstream fetch; before this tag, nothing carried it.
55
- const init = {
56
- headers,
57
- signal: AbortSignal.timeout(timeoutMs),
58
- next: { tags: [SEO_ARTIFACT_TAG] },
59
- };
60
- const upstream = await fetch(url, init);
61
- if (upstream.status === 304) {
62
- return new Response(null, { status: 304, headers: { 'Cache-Control': cacheControl } });
63
- }
64
- if (!upstream.ok) {
65
- return new Response(upstream.status === 404 ? 'Not found' : 'Upstream unavailable', {
66
- status: upstream.status === 404 ? 404 : 503,
67
- headers: {
68
- 'Content-Type': 'text/plain',
69
- ...(upstream.status !== 404 ? { 'Retry-After': '60' } : {}),
70
- },
71
- });
72
- }
73
- const body = await upstream.text();
74
- const responseHeaders = {
75
- 'Content-Type': upstream.headers.get('content-type') ?? 'text/plain; charset=utf-8',
76
- 'Cache-Control': cacheControl,
77
- };
78
- const etag = upstream.headers.get('etag');
79
- if (etag)
80
- responseHeaders.ETag = etag;
81
- return new Response(body, { status: 200, headers: responseHeaders });
45
+ params = context?.params ? await context.params : undefined;
82
46
  }
83
47
  catch (error) {
84
- return new Response(error instanceof Error ? error.message : 'Proxy failed', {
85
- status: 503,
86
- headers: { 'Content-Type': 'text/plain', 'Retry-After': '60' },
87
- });
48
+ return failureResponse(error);
88
49
  }
50
+ return respond(request, params);
89
51
  };
90
52
  }
91
53
  //# sourceMappingURL=artifact-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-handler.js","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"artifact-handler.js","sourceRoot":"","sources":["../../src/next/artifact-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAoB,MAAM,qBAAqB,CAAC;AAEzE,OAAO,EACL,yBAAyB,GAE1B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAOpD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAuE,EACvE,QAAqB,EACrB,OAAmC;IAEnC,MAAM,OAAO,GAAG,yBAAyB,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAC7F,gBAAgB,CACd,QAAQ,CAAC,GAAG,EACZ,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAC9C,KAAK,EACL,IAAI;IACJ,sEAAsE;IACtE,wEAAwE;IACxE,+DAA+D;IAC/D,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE,CACjE,CACF,CAAC;IAEF,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAqB,EAAE;QACnD,IAAI,MAAqC,CAAC;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
@@ -20,16 +20,28 @@
20
20
  * by the `delivered_at` window, so there is no dedupe store to run.
21
21
  */
22
22
  import type { PlatformEventEnvelope } from '../events/envelope.js';
23
+ import { type MultiStoreClient } from '../multi-store.js';
23
24
  import { type RevalidationPlan, type RevalidationPlanOptions } from '../revalidation/event-plan.js';
24
25
  import { type UrlContext, type UrlContextSource } from '../revalidation/url-context.js';
25
- export interface EventRevalidateHandlerOptions extends RevalidationPlanOptions {
26
- /** The subscription's signing secret, shown once when it was created. Server-only env. */
27
- secret: string;
26
+ export interface EventRevalidateHandlerOptions extends Omit<RevalidationPlanOptions, 'storeScoped'> {
27
+ /**
28
+ * The subscription's signing secret, shown once when it was created. Server-only env.
29
+ *
30
+ * Several stores on one route: each store's subscription has its OWN secret,
31
+ * so pass a function from the store code to that store's secret. A store it
32
+ * answers `undefined` (or `''`) for is treated like an unset secret — 500,
33
+ * retried, never 401: a subscription is created before its secret can be
34
+ * deployed, and a 401 streak would auto-disable it meanwhile. The code is
35
+ * read from the body before it is verified, and the signature over that same
36
+ * body is what binds it: a delivery claiming store B must be signed with B's
37
+ * secret.
38
+ */
39
+ secret: string | ((storeCode: string) => string | undefined);
28
40
  /** Pass `revalidatePath` from 'next/cache'. */
29
41
  revalidatePath(path: string, type?: 'page' | 'layout'): void;
30
42
  /**
31
- * Pass `revalidateTag` from 'next/cache'. Required: product and collection
32
- * events revalidate by tag alone. It is called with the tag only — if your
43
+ * Pass `revalidateTag` from 'next/cache'. Required: product, variant,
44
+ * collection, facet, price-list and menu events revalidate by tag alone. It is called with the tag only — if your
33
45
  * Next version's `revalidateTag` takes a cache-profile argument, pass a
34
46
  * wrapper that supplies one.
35
47
  */
@@ -37,8 +49,16 @@ export interface EventRevalidateHandlerOptions extends RevalidationPlanOptions {
37
49
  /**
38
50
  * The channel's URL rule set and languages, used to compose each event path
39
51
  * per locale — typically `urlContextFromClient(client)`.
52
+ *
53
+ * Several stores in one app: pass the store router
54
+ * (`createStorefrontClient({ resolveStore })`). Each event is then planned
55
+ * for its own store (the envelope's `channel_code`): its paths are composed
56
+ * on that store's address, `toRoutePath` receives the store code, and every
57
+ * tag is purged store-scoped (`store:<code>:<tag>`), which is what the
58
+ * router's reads carry beside the bare tags. A whole-site plan still
59
+ * revalidates the whole app (`revalidatePath('/', 'layout')`).
40
60
  */
41
- urls: UrlContext | UrlContextSource;
61
+ urls: UrlContext | UrlContextSource | MultiStoreClient;
42
62
  /** Reject deliveries whose `delivered_at` is further than this from now. Default 300s. */
43
63
  toleranceSeconds?: number;
44
64
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"event-revalidate-handler.d.ts","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AAKvC,OAAO,EACL,KAAK,UAAU,EACf,KAAK,gBAAgB,EAEtB,MAAM,gCAAgC,CAAC;AAExC,MAAM,WAAW,6BAA8B,SAAQ,uBAAuB;IAC5E,0FAA0F;IAC1F,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC7D;;;;;OAKG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;OAGG;IACH,IAAI,EAAE,UAAU,GAAG,gBAAgB,CAAC;IACpC,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,CAAC,QAAQ,EAAE,qBAAqB,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACjE;AAgBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAwGzC"}
1
+ {"version":3,"file":"event-revalidate-handler.d.ts","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC/F,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AAKvC,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,gBAAgB,EAGtB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,WAAW,6BACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC;IACpD;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7D,+CAA+C;IAC/C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC7D;;;;;OAKG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,UAAU,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;IACvD,0FAA0F;IAC1F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,CAAC,QAAQ,EAAE,qBAAqB,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACjE;AAgBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAyLzC"}
@@ -19,9 +19,10 @@
19
19
  * Stateless by design: revalidating twice is harmless, and a replay is bounded
20
20
  * by the `delivered_at` window, so there is no dedupe store to run.
21
21
  */
22
+ import { forgetStoreCode, isMultiStoreClient } from '../multi-store.js';
22
23
  import { classifyEvent, EventPayloadError, planFromIntent, resolveMaxPaths, } from '../revalidation/event-plan.js';
23
24
  import { DEFAULT_EVENT_TOLERANCE_SECONDS, verifyEventDelivery, } from '../revalidation/event-verify.js';
24
- import { urlContextFromChannelSettings, } from '../revalidation/url-context.js';
25
+ import { forgetStoreCodeOf, urlContextFromChannelSettings, urlContextFromClientFactory, } from '../revalidation/url-context.js';
25
26
  function json(status, body) {
26
27
  return new Response(JSON.stringify(body), {
27
28
  status,
@@ -52,35 +53,63 @@ export function createEventRevalidateHandler(options) {
52
53
  throw new TypeError(`toleranceSeconds must be a positive number, got ${toleranceSeconds}`);
53
54
  }
54
55
  const maxPaths = resolveMaxPaths(options.maxPaths);
55
- const source = options.urls;
56
- if (typeof source !== 'function' && (typeof source !== 'object' || source === null)) {
56
+ const urlsOption = options.urls;
57
+ if (typeof urlsOption !== 'function' && (typeof urlsOption !== 'object' || urlsOption === null)) {
57
58
  throw new TypeError('urls is required — pass urlContextFromClient(client) or a UrlContext');
58
59
  }
59
60
  const report = options.onError ?? defaultOnError;
61
+ // Several stores: every piece of per-store state below is keyed by the
62
+ // event's store, so one store's rule set or URL context is never used to
63
+ // compose another store's paths. One store: a single key, as before.
64
+ const stores = isMultiStoreClient(urlsOption) ? urlsOption : null;
65
+ const sources = new Map();
66
+ const sourceFor = (store) => {
67
+ if (!stores)
68
+ return urlsOption;
69
+ let source = sources.get(store);
70
+ if (!source) {
71
+ source = urlContextFromClientFactory(() => stores.forStore(store));
72
+ sources.set(store, source);
73
+ }
74
+ return source;
75
+ };
76
+ const keyOf = (envelope) => (stores ? envelope.channel_code : '');
60
77
  // The rule set the last `channel.url_settings_changed` delivery carried. It
61
78
  // is newer than anything `urls` can return: the client's channel snapshot is
62
79
  // taken at startup, and a static UrlContext never changes.
63
- let adopted = null;
64
- const resolveUrls = async () => adopted ?? (typeof source === 'function' ? await source() : source);
80
+ const adopted = new Map();
81
+ const resolveUrls = async (key) => {
82
+ const held = adopted.get(key);
83
+ if (held)
84
+ return held;
85
+ const source = sourceFor(key);
86
+ return typeof source === 'function' ? await source() : source;
87
+ };
65
88
  const planOptions = { maxPaths, toRoutePath: options.toRoutePath };
66
- // Path events whose URLs could not be composed since the last one that could.
67
- // Only the first of such a streak is reported, so a broken URL source does not
68
- // flood the log; every one of them is still visible through `onEvent`.
69
- let mappingFailures = 0;
70
- const planPaths = async (intent, envelope) => {
89
+ // Path events whose URLs could not be composed since the last one that could,
90
+ // per store: one store's breakage neither hides another's nor is re-reported
91
+ // because a different store's event mapped in between. Only the first of a
92
+ // streak is reported, so a broken URL source does not flood the log; every
93
+ // one of them is still visible through `onEvent`.
94
+ const mappingFailures = new Map();
95
+ const planPaths = async (intent, envelope, store) => {
71
96
  try {
72
- const plan = planFromIntent(intent, await resolveUrls(), planOptions);
73
- mappingFailures = 0;
97
+ const plan = planFromIntent(intent, await resolveUrls(keyOf(envelope)), planOptions, store);
98
+ mappingFailures.delete(keyOf(envelope));
74
99
  return plan;
75
100
  }
76
101
  catch (error) {
77
102
  // Never a 5xx: delivery is ordered, so a failure that cannot clear would
78
103
  // stall the subscription on this event for good without ever tripping the
79
104
  // auto-disable. The whole site needs no URL at all.
80
- if (mappingFailures === 0)
105
+ const streak = mappingFailures.get(keyOf(envelope)) ?? 0;
106
+ if (streak === 0)
81
107
  report(error, envelope);
82
- mappingFailures += 1;
83
- return { ...planFromIntent({ kind: 'all' }, null), degraded: 'url_mapping_failed' };
108
+ mappingFailures.set(keyOf(envelope), streak + 1);
109
+ return {
110
+ ...planFromIntent({ kind: 'all' }, null, undefined, store),
111
+ degraded: 'url_mapping_failed',
112
+ };
84
113
  }
85
114
  };
86
115
  return async (request) => {
@@ -96,24 +125,80 @@ export function createEventRevalidateHandler(options) {
96
125
  catch {
97
126
  return json(400, { error: 'unreadable request body' });
98
127
  }
99
- const verified = await verifyEventDelivery(rawBody, request.headers, options.secret, {
128
+ let secret;
129
+ if (typeof options.secret === 'function') {
130
+ const claimed = claimedStore(rawBody);
131
+ // No store named: nothing to verify the signature with, so the delivery
132
+ // is as unauthenticated as a bad signature. The platform always names one.
133
+ if (claimed === null)
134
+ return json(401, { error: 'the delivery names no store' });
135
+ const found = options.secret(claimed);
136
+ // A store with no secret yet is the misconfiguration above, per store:
137
+ // 500, retried once it is deployed (ADR 2026-09-10, decision 3).
138
+ if (typeof found !== 'string' || found === '') {
139
+ return json(500, { error: 'revalidation secret is not configured for this store' });
140
+ }
141
+ secret = found;
142
+ }
143
+ else {
144
+ secret = options.secret;
145
+ }
146
+ const verified = await verifyEventDelivery(rawBody, request.headers, secret, {
100
147
  toleranceSeconds,
101
148
  });
102
149
  if (!verified.ok)
103
150
  return json(verified.status, { error: verified.reason });
104
151
  const { envelope } = verified;
152
+ if (stores && envelope.channel_code === '') {
153
+ return json(400, { error: 'the envelope names no store (channel_code)' });
154
+ }
155
+ const key = keyOf(envelope);
156
+ const store = stores ? envelope.channel_code : undefined;
105
157
  let plan;
106
158
  try {
107
159
  const intent = classifyEvent(envelope);
160
+ const source = sourceFor(key);
108
161
  if (intent.kind === 'all' && intent.channelSettings) {
109
- adopted = urlContextFromChannelSettings(intent.channelSettings);
162
+ adopted.set(key, urlContextFromChannelSettings(intent.channelSettings));
110
163
  if (typeof source === 'function')
111
164
  source.invalidate?.();
112
165
  }
166
+ // head.changed / channel.renamed: nothing in the payload is a rule set to
167
+ // adopt, so a source that can be told to re-read is invalidated and the
168
+ // adopted rule set dropped with it — the next path event reads the context
169
+ // fresh. A static UrlContext, or a function with no `invalidate`, cannot be
170
+ // made to re-read: dropping an adopted rule set there would fall back to
171
+ // whatever it returns, which may be the OLDER startup snapshot.
172
+ if ((intent.kind === 'tags' || intent.kind === 'none') &&
173
+ intent.invalidateUrlContext &&
174
+ typeof source === 'function' &&
175
+ typeof source.invalidate === 'function') {
176
+ adopted.delete(key);
177
+ source.invalidate();
178
+ }
179
+ // channel.renamed: the client's channel resolver may still hold the OLD code's answer
180
+ // (its token rotated by the rename) for minutes. Dropping it now means the next request
181
+ // asks the CMS, which answers the old code as the store under its new code, and no
182
+ // request has to fail first (W8-16b). Best-effort: a client that cannot be reached here
183
+ // still heals on its first refused request.
184
+ if (envelope.type === 'channel.renamed') {
185
+ const previous = previousCodeOf(envelope);
186
+ if (previous !== null) {
187
+ try {
188
+ if (stores)
189
+ forgetStoreCode(stores, previous);
190
+ else
191
+ await forgetStoreCodeOf(source, previous);
192
+ }
193
+ catch (error) {
194
+ report(error, envelope);
195
+ }
196
+ }
197
+ }
113
198
  plan =
114
199
  intent.kind === 'paths'
115
- ? await planPaths(intent, envelope)
116
- : planFromIntent(intent, null, planOptions);
200
+ ? await planPaths(intent, envelope, store)
201
+ : planFromIntent(intent, null, planOptions, store);
117
202
  if (plan.all)
118
203
  options.revalidatePath('/', 'layout');
119
204
  for (const path of plan.paths)
@@ -147,4 +232,26 @@ export function createEventRevalidateHandler(options) {
147
232
  });
148
233
  };
149
234
  }
235
+ /** A `channel.renamed` payload's old code, or null when it names none. */
236
+ function previousCodeOf(envelope) {
237
+ const data = envelope.data;
238
+ const code = data?.previous_code;
239
+ return typeof code === 'string' && code !== '' ? code : null;
240
+ }
241
+ /**
242
+ * The store a delivery CLAIMS to be for — read from the body before it is
243
+ * verified, which is safe because the signature over that same body is then
244
+ * checked with that store's secret: a delivery claiming store B verifies only
245
+ * under B's secret. `null` when the body names no store.
246
+ */
247
+ function claimedStore(rawBody) {
248
+ let claimed;
249
+ try {
250
+ claimed = JSON.parse(rawBody)?.channel_code;
251
+ }
252
+ catch {
253
+ return null;
254
+ }
255
+ return typeof claimed === 'string' && claimed !== '' ? claimed : null;
256
+ }
150
257
  //# sourceMappingURL=event-revalidate-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-revalidate-handler.js","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"event-revalidate-handler.js","sourceRoot":"","sources":["../../src/next/event-revalidate-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Route-handler factory that proxies a platform-rendered product feed onto the
3
+ * storefront's own origin — same fetch-and-serve shape as
4
+ * `createSeoArtifactHandler`, so the merchant centre only ever sees the
5
+ * storefront domain (hosting-adapter-independent, unlike next.config rewrites).
6
+ *
7
+ * A thin wrapper over `createProductFeedResponder` from
8
+ * `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
9
+ * crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
10
+ * the storefront's, `ETag`/`If-None-Match` pass through, and the body is
11
+ * buffered so a timeout is a clean 503, never a truncated 200 — the responder
12
+ * says why). Two things are Next's own:
13
+ * - the upstream fetch carries `next: { tags: ['product-feed'] }`;
14
+ * - it fetches with the GLOBAL `fetch`, read per request — the one Next patches
15
+ * and reads `next.tags` through.
16
+ */
17
+ import { type MultiStoreClient } from '../multi-store.js';
18
+ import type { StorefrontClient } from '../types.js';
19
+ import { type ProductFeedResponderOptions } from '../web/feed-responder.js';
20
+ export interface ProductFeedHandlerOptions extends ProductFeedResponderOptions {
21
+ /**
22
+ * The feed's token (Dashboard → Marketing → Product feeds). Use on a static
23
+ * route. Omit it on a dynamic `[token]` route — the handler then reads the
24
+ * `token` route param instead.
25
+ */
26
+ token?: string;
27
+ }
28
+ /**
29
+ * Usage (App Router):
30
+ * ```ts
31
+ * // Static route — src/app/product-feed.xml/route.ts
32
+ * export const GET = createProductFeedHandler(getClient(), {
33
+ * token: process.env.GOOGLE_FEED_TOKEN,
34
+ * });
35
+ *
36
+ * // Dynamic route — src/app/feeds/[token]/route.ts
37
+ * export const GET = createProductFeedHandler(getClient());
38
+ * ```
39
+ *
40
+ * Several stores in one app: pass the store router and mount it on the
41
+ * dynamic `[token]` route — each store's feed has its own token — and each
42
+ * request is proxied from the store it resolves to (a request no store serves
43
+ * is answered 404).
44
+ */
45
+ export declare function createProductFeedHandler(client: StorefrontClient | Promise<StorefrontClient> | MultiStoreClient, options?: ProductFeedHandlerOptions): (request: Request, context?: {
46
+ params?: Promise<{
47
+ token?: string;
48
+ }>;
49
+ }) => Promise<Response>;
50
+ //# sourceMappingURL=feed-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed-handler.d.ts","sourceRoot":"","sources":["../../src/next/feed-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAEL,KAAK,2BAA2B,EAEjC,MAAM,0BAA0B,CAAC;AAIlC,MAAM,WAAW,yBAA0B,SAAQ,2BAA2B;IAC5E;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,EACvE,OAAO,CAAC,EAAE,yBAAyB,GAClC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CA2B7F"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Route-handler factory that proxies a platform-rendered product feed onto the
3
+ * storefront's own origin — same fetch-and-serve shape as
4
+ * `createSeoArtifactHandler`, so the merchant centre only ever sees the
5
+ * storefront domain (hosting-adapter-independent, unlike next.config rewrites).
6
+ *
7
+ * A thin wrapper over `createProductFeedResponder` from
8
+ * `@sixtyninecommerce/client/web`: the same statuses, headers and bodies (the
9
+ * crawler UA is not forwarded, `X-Robots-Tag` is dropped, `Cache-Control` is
10
+ * the storefront's, `ETag`/`If-None-Match` pass through, and the body is
11
+ * buffered so a timeout is a clean 503, never a truncated 200 — the responder
12
+ * says why). Two things are Next's own:
13
+ * - the upstream fetch carries `next: { tags: ['product-feed'] }`;
14
+ * - it fetches with the GLOBAL `fetch`, read per request — the one Next patches
15
+ * and reads `next.tags` through.
16
+ */
17
+ import { PRODUCT_FEED_TAG } from '../cache-tags.js';
18
+ import { readPublicArtifact } from '../core/public-artifact.js';
19
+ import { adapterCacheTags } from '../multi-store.js';
20
+ import { buildProductFeedResponder, readFeedBytes, } from '../web/feed-responder.js';
21
+ import { failureResponse } from '../web/respond.js';
22
+ /**
23
+ * Usage (App Router):
24
+ * ```ts
25
+ * // Static route — src/app/product-feed.xml/route.ts
26
+ * export const GET = createProductFeedHandler(getClient(), {
27
+ * token: process.env.GOOGLE_FEED_TOKEN,
28
+ * });
29
+ *
30
+ * // Dynamic route — src/app/feeds/[token]/route.ts
31
+ * export const GET = createProductFeedHandler(getClient());
32
+ * ```
33
+ *
34
+ * Several stores in one app: pass the store router and mount it on the
35
+ * dynamic `[token]` route — each store's feed has its own token — and each
36
+ * request is proxied from the store it resolves to (a request no store serves
37
+ * is answered 404).
38
+ */
39
+ export function createProductFeedHandler(client, options) {
40
+ const respond = buildProductFeedResponder(client, options, 'createProductFeedHandler requires a feed token — pass options.token or mount it on a [token] route', (resolved) => (url, read) => readPublicArtifact((input, init) => globalThis.fetch(input, init), url, readFeedBytes, {
41
+ ...read,
42
+ // Tagged for the same reason `createSeoArtifactHandler` tags its fetch:
43
+ // an upstream fetch that carries no tag cannot be purged at all, so a
44
+ // storefront that regenerates a feed on demand would have no way to drop
45
+ // the cached copy. The revalidation handlers deliberately do NOT purge
46
+ // it — see `cache-tags.ts`. A store router's request also carries the
47
+ // store-scoped copy.
48
+ init: { next: { tags: adapterCacheTags(resolved, PRODUCT_FEED_TAG) } },
49
+ }));
50
+ return async (request, context) => {
51
+ let params;
52
+ try {
53
+ params = context?.params ? await context.params : undefined;
54
+ }
55
+ catch (error) {
56
+ return failureResponse(error);
57
+ }
58
+ return respond(request, params);
59
+ };
60
+ }
61
+ //# sourceMappingURL=feed-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed-handler.js","sourceRoot":"","sources":["../../src/next/feed-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EACL,yBAAyB,EAEzB,aAAa,GACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAYpD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAuE,EACvE,OAAmC;IAEnC,MAAM,OAAO,GAAG,yBAAyB,CACvC,MAAM,EACN,OAAO,EACP,oGAAoG,EACpG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAC1B,kBAAkB,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,EAAE;QACrF,GAAG,IAAI;QACP,wEAAwE;QACxE,sEAAsE;QACtE,yEAAyE;QACzE,uEAAuE;QACvE,sEAAsE;QACtE,qBAAqB;QACrB,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE;KACvE,CAAC,CACL,CAAC;IAEF,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAqB,EAAE;QACnD,IAAI,MAAsC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC;AACJ,CAAC"}
@@ -13,10 +13,12 @@ export type { SeoArtifact, SeoArtifactHandlerOptions } from './artifact-handler.
13
13
  export { createSeoArtifactHandler } from './artifact-handler.js';
14
14
  export type { EventRevalidateHandlerOptions } from './event-revalidate-handler.js';
15
15
  export { createEventRevalidateHandler } from './event-revalidate-handler.js';
16
+ export type { ProductFeedHandlerOptions } from './feed-handler.js';
17
+ export { createProductFeedHandler } from './feed-handler.js';
16
18
  export { jsonLdScriptProps, serializeJsonLd } from './json-ld.js';
17
19
  export type { NextCompatibleMetadata } from './metadata.js';
18
20
  export { extraOpenGraphTags, toNextMetadata } from './metadata.js';
19
- export type { RedirectMiddleware, RedirectMiddlewareOptions } from './redirect-middleware.js';
21
+ export type { MultiStoreRedirectMiddleware, MultiStoreRedirectMiddlewareOptions, RedirectMiddleware, RedirectMiddlewareOptions, } from './redirect-middleware.js';
20
22
  export { createRedirectMiddleware } from './redirect-middleware.js';
21
23
  export type { RevalidateHandlerOptions } from './revalidate-handler.js';
22
24
  export { createRevalidateHandler } from './revalidate-handler.js';