@forgecart/cli 2.202608300703.0 → 2.202609190800.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 (76) hide show
  1. package/dist/src/commands/init.d.ts +21 -2
  2. package/dist/src/commands/init.js +18 -2
  3. package/dist/src/commands/init.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/storefront/README.md +42 -4
  6. package/templates/storefront/next.config.js +29 -7
  7. package/templates/storefront/src/app/%5F%5Ffc/identify/route.ts +205 -0
  8. package/templates/storefront/src/app/%5F%5Ffc/track/route.ts +28 -25
  9. package/templates/storefront/src/app/__forge_beacon/route.ts +1 -1
  10. package/templates/storefront/src/app/cart/page.tsx +14 -2
  11. package/templates/storefront/src/app/checkout/page.tsx +14 -2
  12. package/templates/storefront/src/app/layout.tsx +85 -22
  13. package/templates/storefront/src/app/page.tsx +63 -20
  14. package/templates/storefront/src/app/pages/[slug]/not-found.tsx +23 -0
  15. package/templates/storefront/src/app/pages/[slug]/page.tsx +114 -0
  16. package/templates/storefront/src/app/ping/route.ts +1 -1
  17. package/templates/storefront/src/app/products/[slug]/not-found.tsx +6 -4
  18. package/templates/storefront/src/app/products/[slug]/page.tsx +204 -21
  19. package/templates/storefront/src/app/products/page.tsx +41 -6
  20. package/templates/storefront/src/app/register/page.tsx +54 -0
  21. package/templates/storefront/src/app/reset-password/page.tsx +60 -0
  22. package/templates/storefront/src/app/robots.ts +69 -0
  23. package/templates/storefront/src/app/sitemap.ts +106 -0
  24. package/templates/storefront/src/app/verify/page.tsx +155 -0
  25. package/templates/storefront/src/components/CartView.tsx +26 -7
  26. package/templates/storefront/src/components/ForgeTracker.tsx +108 -1
  27. package/templates/storefront/src/components/Header.tsx +30 -10
  28. package/templates/storefront/src/components/LanguageSwitcher.tsx +88 -0
  29. package/templates/storefront/src/components/LocaleLink.tsx +49 -0
  30. package/templates/storefront/src/components/ProductCard.tsx +10 -4
  31. package/templates/storefront/src/components/account/AccountMessage.tsx +59 -0
  32. package/templates/storefront/src/components/account/RegisterForm.tsx +283 -0
  33. package/templates/storefront/src/components/account/RequestPasswordResetForm.tsx +96 -0
  34. package/templates/storefront/src/components/account/ResetPasswordForm.tsx +169 -0
  35. package/templates/storefront/src/components/checkout/CheckoutGate.tsx +12 -4
  36. package/templates/storefront/src/lib/account/account-link.ts +76 -0
  37. package/templates/storefront/src/lib/account/register-state.ts +133 -0
  38. package/templates/storefront/src/lib/account/reset-password-state.ts +111 -0
  39. package/templates/storefront/src/lib/account/verify-state.ts +56 -0
  40. package/templates/storefront/src/lib/account-actions.ts +76 -0
  41. package/templates/storefront/src/lib/account-session.ts +47 -0
  42. package/templates/storefront/src/lib/asset-alt.ts +34 -0
  43. package/templates/storefront/src/lib/content/render-fields.tsx +256 -0
  44. package/templates/storefront/src/lib/content/resolve-page.ts +143 -0
  45. package/templates/storefront/src/lib/experiments.ts +1 -1
  46. package/templates/storefront/src/lib/forgecart.ts +300 -27
  47. package/templates/storefront/src/lib/format.ts +12 -14
  48. package/templates/storefront/src/lib/identify-forward.ts +152 -0
  49. package/templates/storefront/src/lib/locale/channel-locales-loader.ts +169 -0
  50. package/templates/storefront/src/lib/locale/channel-locales-map.ts +46 -0
  51. package/templates/storefront/src/lib/locale/channel-locales.ts +191 -0
  52. package/templates/storefront/src/lib/locale/grammar.ts +194 -0
  53. package/templates/storefront/src/lib/locale/localized-path.ts +55 -0
  54. package/templates/storefront/src/lib/locale/middleware-plan.ts +107 -0
  55. package/templates/storefront/src/lib/locale/request-binding.ts +80 -0
  56. package/templates/storefront/src/lib/locale/request-locale.ts +66 -0
  57. package/templates/storefront/src/lib/marketing-params.ts +213 -0
  58. package/templates/storefront/src/lib/money.ts +50 -0
  59. package/templates/storefront/src/lib/seo/alternates.ts +120 -0
  60. package/templates/storefront/src/lib/seo/json-ld.ts +266 -0
  61. package/templates/storefront/src/lib/seo/metadata.ts +323 -0
  62. package/templates/storefront/src/lib/seo/noindex.ts +218 -0
  63. package/templates/storefront/src/lib/seo/public-origin.ts +166 -0
  64. package/templates/storefront/src/lib/seo/redirect-plan.ts +86 -0
  65. package/templates/storefront/src/lib/seo/resolve-path.ts +126 -0
  66. package/templates/storefront/src/lib/seo/scaffolded-routes.ts +83 -0
  67. package/templates/storefront/src/lib/seo/sitemap-cache.ts +114 -0
  68. package/templates/storefront/src/lib/seo/sitemap-entries.ts +321 -0
  69. package/templates/storefront/src/lib/session-actions.ts +15 -8
  70. package/templates/storefront/src/lib/session-cookies.ts +98 -0
  71. package/templates/storefront/src/lib/shop-config.ts +9 -2
  72. package/templates/storefront/src/lib/shop-session.ts +20 -1
  73. package/templates/storefront/src/lib/track-forward.ts +43 -14
  74. package/templates/storefront/src/middleware.ts +150 -8
  75. package/templates/storefront/src/seo/redirects.ts +44 -0
  76. package/templates/storefront/src/server/runner.ts +1 -2
@@ -1,6 +1,14 @@
1
1
  import { Command } from 'commander';
2
- /** Default shop API base. Overridable via `--api-url`. */
3
- export declare const DEFAULT_SHOP_API_URL = "https://api.forgecart.com";
2
+ /**
3
+ * Default shop API base, INCLUDING the mounted path. The API serves the shop
4
+ * GraphQL endpoint at `/shop-api` (`app/forgecart/src/main.ts`), and the SDK
5
+ * takes this value verbatim as its endpoint — it swaps the scheme for the
6
+ * websocket and appends nothing to the path — so a pathless origin scaffolds a
7
+ * storefront whose every read dials a socket nothing answers (#2102): the
8
+ * operation rides graphql-ws's whole retry ladder and the page hangs with
9
+ * nothing logged. Overridable via `--api-url`.
10
+ */
11
+ export declare const DEFAULT_SHOP_API_URL = "https://api.forgecart.com/shop-api";
4
12
  /**
5
13
  * Environment variable naming a dependency-warmed storefront template — a copy
6
14
  * of the bundled template with `node_modules` (and the generated
@@ -40,6 +48,17 @@ export interface InitOptions {
40
48
  * sides (deployed storefronts, loopback dev).
41
49
  */
42
50
  browserApiUrl?: string;
51
+ /**
52
+ * The storefront's PUBLIC origin (`https://shop.example.com`) — the address
53
+ * shoppers and crawlers reach it at.
54
+ *
55
+ * Supplied for a published storefront and deliberately OMITTED for preview
56
+ * pods, whose hostnames are ephemeral. The template treats absence as "this
57
+ * deployment has no stable address to be canonical about" and serves every
58
+ * route noindex with `Disallow: /` (`lib/seo/public-origin.ts`), so omitting
59
+ * it is the safe state rather than an incomplete one.
60
+ */
61
+ publicOrigin?: string;
43
62
  /** Whether to also scaffold a NestJS application (planned for a later phase). */
44
63
  application?: boolean;
45
64
  /**
@@ -5,8 +5,16 @@ import { Command } from 'commander';
5
5
  import { print } from '../output.js';
6
6
  import { readPackageVersion } from '../version.js';
7
7
  import { writeTemplateManifest } from './template-manifest.js';
8
- /** Default shop API base. Overridable via `--api-url`. */
9
- export const DEFAULT_SHOP_API_URL = 'https://api.forgecart.com';
8
+ /**
9
+ * Default shop API base, INCLUDING the mounted path. The API serves the shop
10
+ * GraphQL endpoint at `/shop-api` (`app/forgecart/src/main.ts`), and the SDK
11
+ * takes this value verbatim as its endpoint — it swaps the scheme for the
12
+ * websocket and appends nothing to the path — so a pathless origin scaffolds a
13
+ * storefront whose every read dials a socket nothing answers (#2102): the
14
+ * operation rides graphql-ws's whole retry ladder and the page hangs with
15
+ * nothing logged. Overridable via `--api-url`.
16
+ */
17
+ export const DEFAULT_SHOP_API_URL = 'https://api.forgecart.com/shop-api';
10
18
  /**
11
19
  * Environment variable naming a dependency-warmed storefront template — a copy
12
20
  * of the bundled template with `node_modules` (and the generated
@@ -145,6 +153,13 @@ function buildPlatformEnv(channelToken, options) {
145
153
  if (secret) {
146
154
  entries.set('FORGECART_ADMIN_SECRET', secret);
147
155
  }
156
+ // Conditional for the same #1123 reason as the two above, and with a sharper
157
+ // consequence: a preview drive omits it, and stripping an origin an earlier
158
+ // drive wrote would silently de-index a live storefront.
159
+ const publicOrigin = options.publicOrigin?.trim();
160
+ if (publicOrigin) {
161
+ entries.set('FORGECART_PUBLIC_ORIGIN', publicOrigin);
162
+ }
148
163
  return entries;
149
164
  }
150
165
  /**
@@ -304,6 +319,7 @@ export function createInitCommand() {
304
319
  .option('--admin-secret <secret>', 'Channel admin secret for server-side admin operations (written to .env only)')
305
320
  .option('--api-url <url>', 'Shop API base URL written to the storefront environment', DEFAULT_SHOP_API_URL)
306
321
  .option('--browser-api-url <url>', 'Shop API URL for the shopper browser when it differs from --api-url (split-horizon pods; must be HTTPS so the session socket can use WSS)')
322
+ .option('--public-origin <url>', 'Public origin the storefront is served at (https://shop.example.com). Omit for preview deployments: without it every route is noindex and robots.txt disallows the whole tree')
307
323
  .option('-a, --application', 'Also scaffold a NestJS application (TODO: next phase)')
308
324
  .option('--config-only', 'Write only per-channel config into an existing pre-warmed storefront (skip the template copy)')
309
325
  .action(async (name, options) => {
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/init.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgecart/cli",
3
- "version": "2.202608300703.0",
3
+ "version": "2.202609190800.0",
4
4
  "type": "module",
5
5
  "description": "CLI for scaffolding and operating ForgeCart channel storefronts",
6
6
  "bin": {
@@ -125,10 +125,40 @@ backstops.
125
125
  ## Sessions
126
126
 
127
127
  The anonymous session is minted by the API on the first cart mutation and
128
- persisted into the httpOnly `forgecart-session` cookie by the action runner.
129
- The `/__fc/track` analytics relay may **establish** that identity for pure
130
- browsers but never replaces an existing cookie, and the tracker defers its
131
- first flush so a fast add-to-cart wins the first-touch race.
128
+ persisted into the session cookie PAIR — the httpOnly `forgecart-session` plus
129
+ its JS-readable `forgecart-session-client` mirror, whose one contract lives in
130
+ [`src/lib/session-cookies.ts`](src/lib/session-cookies.ts) by the action
131
+ runner. The `/__fc/track` and `/__fc/identify` relays may **establish** that
132
+ pair for pure browsers but never replace an existing cookie, and the tracker
133
+ defers its first flush so a fast add-to-cart wins the first-touch race. The
134
+ mirror is what the browser's shop socket boots its auth from: written without
135
+ it, a relay-established session would never reach the client at all.
136
+
137
+ ## Account links
138
+
139
+ Two routes exist because the PLATFORM links to them from the e-mails it sends
140
+ a shopper, not because anything in this storefront navigates there:
141
+
142
+ | Route | Mail | What it does |
143
+ | ---------------------------- | ------------------- | ------------------------------------------------------------ |
144
+ | `/verify?token=…` | e-mail verification | consumes the token on RENDER and reports the outcome |
145
+ | `/reset-password?token=…` | password reset | renders the new-password form; the SUBMIT consumes the token |
146
+ | `/reset-password` (no token) | — | asks for an address and has a new link sent |
147
+
148
+ **Do not rename or prefix them.** The path and the `?token=` parameter are the
149
+ contract with the mail templates, and a link already sitting in a shopper's
150
+ inbox cannot be re-issued. They are in the once-per-store set
151
+ ([`src/lib/locale/grammar.ts`](src/lib/locale/grammar.ts)), so they never carry
152
+ a locale prefix, and in `NOINDEX_PATHS`
153
+ ([`src/lib/seo/noindex.ts`](src/lib/seo/noindex.ts)), so they are `noindex`,
154
+ absent from the XML sitemap and `Disallow`ed in `robots.txt` — a crawler that
155
+ fetched one would spend a real shopper's one-shot token.
156
+
157
+ The COPY is yours to rewrite. The states behind it are not invented per page:
158
+ [`src/lib/account/`](src/lib/account/) maps the shop API's `ErrorCode`s onto a
159
+ small state set (verified / link-invalid / wrong-store / …) that both pages
160
+ render, and each surface stamps `data-fc-account-mode` + `data-fc-account-state`
161
+ onto the element it renders, so the state is readable from the served HTML.
132
162
 
133
163
  ## Project layout
134
164
 
@@ -142,6 +172,8 @@ src/
142
172
  cart-actions.ts # cart Server Actions (incl. coupons)
143
173
  checkout-actions.ts # checkout Server Actions
144
174
  cart-context.tsx # client cart state + typed error channel
175
+ account-actions.ts # password-reset Server Actions
176
+ account/ # pure account-link rules (token, states, ErrorCode map)
145
177
  components/
146
178
  Header.tsx # top nav with live cart count
147
179
  ProductCard.tsx # product tile
@@ -152,6 +184,10 @@ src/
152
184
  AddressStep.tsx # autocomplete + countries + phone
153
185
  RatesStep.tsx # grouped radio-card rate selection
154
186
  PaymentFormEmbed.tsx # origin-checked hosted-form host
187
+ account/
188
+ AccountMessage.tsx # the one outcome block both account pages use
189
+ ResetPasswordForm.tsx # set a new password from a reset token
190
+ RequestPasswordResetForm.tsx # ask for a reset link
155
191
  app/
156
192
  layout.tsx # html shell + header/footer + CartProvider
157
193
  error.tsx # route render backstop
@@ -160,6 +196,8 @@ src/
160
196
  products/ # grid + [slug] detail
161
197
  cart/page.tsx # cart
162
198
  checkout/page.tsx # the stepped checkout
199
+ verify/page.tsx # e-mail verification link target
200
+ reset-password/page.tsx # password-reset link target + request form
163
201
  ```
164
202
 
165
203
  ## Development
@@ -37,13 +37,35 @@ const nextConfig = {
37
37
  'dashboard.test.forgecart.com', // test dashboard origin that embeds the editor
38
38
  'dashboard.forgecart.com', // prod dashboard origin that embeds the editor
39
39
  ],
40
- // PROGRESSIVE PRE-RENDERING (Next 16: Cache Components). Every route builds
41
- // a STATIC shell and streams its dynamic holes (product data, experiment
42
- // branches, the shopper's cart) per request uncached IO must sit inside a
43
- // Suspense boundary, which is exactly how the pages are structured. `next
44
- // dev` semantics are unchanged; the built/published storefront gets the
45
- // prerendered shells.
46
- cacheComponents: true,
40
+ // CACHE COMPONENTS: OFF, explicitly and the explicitness is the point.
41
+ //
42
+ // The root layout reads the request locale (`x-forgecart-locale`) to set
43
+ // `<html lang>`, which is an attribute on the document root and therefore
44
+ // structurally outside every Suspense boundary. Under Cache Components that
45
+ // combination is not buildable: `headers()` returns a hanging promise, and a
46
+ // component aborted with no Suspense frame in its stack raises `blocking-route`
47
+ // → `StaticGenBailoutError`. The only escape Next offers is a `<Suspense>`
48
+ // ABOVE `<body>` — in its own words, "an explicit signal from the user that
49
+ // they acknowledge the empty shell".
50
+ //
51
+ // Taking that escape would cost the locale contract entirely. A real 308/404
52
+ // is assigned only when the render promise REJECTS (`renderToStream`'s catch);
53
+ // with a boundary above, React resolves as CLIENT_RENDERED instead, so
54
+ // `notFound()` / `permanentRedirect()` from the layout would emit a 200
55
+ // carrying a 404 page — a soft-404, indexed rather than dropped, which is the
56
+ // exact failure this storefront's locale routing exists to prevent.
57
+ //
58
+ // Nothing is given up by turning it off: the escape hatch yields an empty
59
+ // prelude on every route anyway (identical TTFB), and the template uses no
60
+ // `use cache`. The per-route Suspense boundaries below still stream their
61
+ // holes — that is core SSR, not a Cache Components feature.
62
+ //
63
+ // Written as an explicit `false` rather than omitted: `enforceExperimentalFeatures`
64
+ // re-enables an UNDEFINED value ("we do respect an explicit value in the user
65
+ // config"), so on a future 16.x under this package's caret range a deleted key
66
+ // would silently restore the failure above — in the merchant's scaffold, far
67
+ // from this repo's CI. An explicit false is immune.
68
+ cacheComponents: false,
47
69
  experimental: {
48
70
  // Persist Turbopack's dev compile artifacts to `.next` so a pod can serve
49
71
  // a PRE-WARMED `.next` baked at build time instead of paying a cold first
@@ -0,0 +1,205 @@
1
+ import { NextResponse } from 'next/server';
2
+ import type { NextRequest } from 'next/server';
3
+
4
+ import {
5
+ forwardMarketingIdentifiers,
6
+ type MarketingIdentifierInput,
7
+ } from '../../../lib/identify-forward';
8
+ import { CLICK_ID_VALUE_MAX_LENGTH } from '../../../lib/marketing-params';
9
+ import { SESSION_COOKIE, establishSessionCookies } from '../../../lib/session-cookies';
10
+ import { getUpstreamConfig, isObviousBot } from '../../../lib/track-forward';
11
+
12
+ /**
13
+ * Same-origin marketing-identity relay (#1596).
14
+ *
15
+ * A shopper arriving from an ad carries the platform's CLICK ID in the landing
16
+ * URL and nowhere else — no cookie, no header, and gone the moment they reach a
17
+ * second page. `lib/marketing-params.ts` reads it on the client, the landing
18
+ * page POSTs what it found here, and this handler forwards the batch to the
19
+ * ForgeCart shop API as one `setMarketingIdentifiers` mutation.
20
+ *
21
+ * The upstream hop itself (raw HTTP GraphQL with the channel token, the shopper
22
+ * session as Bearer, and every failure resolving to an empty outcome instead of
23
+ * a throw) lives in `src/lib/identify-forward.ts`. This route owns everything
24
+ * request-shaped: parsing untrusted JSON, per-identifier validation, the bot
25
+ * gate, session threading and cookie persistence.
26
+ *
27
+ * Sibling by construction: `../track/route.ts` relays EVENTS over the same
28
+ * transport, through the same gates, onto the same session cookie PAIR (both
29
+ * routes write it through `src/lib/session-cookies.ts`) — read the two
30
+ * together, and change them together. They stay separate routes because a
31
+ * click ID is not an event: it names the IDENTITY the events belong to, it is
32
+ * sent once per landing rather than once per batch, and the ad platforms read
33
+ * it back out of that identity months later when a conversion is reported.
34
+ *
35
+ * Inert guard: before `forgecart init` writes `.env` (the image-build prewarm)
36
+ * there is no shop to talk to, so every submitted key answers `rejected` with
37
+ * zero upstream calls and the server never crashes.
38
+ *
39
+ * Session capture: the shop API surfaces a freshly minted session in the
40
+ * response `extensions` exactly once. The relay may ESTABLISH the shopper
41
+ * identity — persisting that mint into BOTH homes of the session pair, the
42
+ * httpOnly `forgecart-session` cookie and its JS-readable mirror
43
+ * `forgecart-session-client` (`src/lib/session-cookies.ts`) — when the request
44
+ * arrived cookie-less, but it never REPLACES an existing cookie. The asymmetry
45
+ * is the sibling's, for the sibling's reason: an incoming cookie is either a
46
+ * live session owned by the cart path (clobbering it would vanish a
47
+ * just-created cart) or a stale one, whose replacement is the cart path's job.
48
+ *
49
+ * Writing both homes matters most HERE (#1733). On an ad landing this route
50
+ * runs before anything else — `ForgeTracker` awaits it before enqueuing the
51
+ * first page_view — so it is what establishes the session the click ID has just
52
+ * been attached to. Write the httpOnly half alone and the shopper's own socket
53
+ * boots unauthenticated, its first cart operation mints a RIVAL session, and
54
+ * the click ID stays on an identity the settled order never resolves to: the
55
+ * attribution this route exists to make possible, lost behind a 200. Presence
56
+ * is still read from the httpOnly copy alone — it is the authoritative one, and
57
+ * the mirror is client-writable.
58
+ *
59
+ * Folder name: `%5F%5Ffc` is URL-encoded `__fc` — the App Router treats
60
+ * `_`-prefixed folders as PRIVATE (excluded from routing), and the `%5F` escape
61
+ * is Next's documented way to serve a literal-underscore URL segment. A folder
62
+ * literally named `__fc` would silently 404.
63
+ */
64
+
65
+ /**
66
+ * The largest identifier list this route will look at.
67
+ *
68
+ * A landing page sends at most one entry per click ID it recognises: three
69
+ * today (`lib/marketing-params.ts`), six registered backend-side. Sixteen is
70
+ * headroom for every registration that could plausibly be added without
71
+ * admitting a list no client of ours would ever send — past it the caller is
72
+ * not our page, and the request is refused whole rather than trimmed, because
73
+ * silently keeping a prefix of somebody else's batch is the worse answer.
74
+ */
75
+ const MAX_IDENTIFIERS = 16;
76
+
77
+ interface IdentifyResponseBody {
78
+ /** Identifier keys the backend recognised and stored. */
79
+ accepted: readonly string[];
80
+ /**
81
+ * Keys the backend does not know — a NORMAL outcome, never an error. The
82
+ * upstream partitions the submitted keys against its identifier registry,
83
+ * stores the ones it knows and reports the rest; nothing was rolled back and
84
+ * nothing may be retried. A key landing here means only that this storefront
85
+ * and the backend registry disagree about a spelling, which is a deployment
86
+ * fact worth being able to read.
87
+ */
88
+ rejected: readonly string[];
89
+ }
90
+
91
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
92
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
93
+ }
94
+
95
+ /** Every answer this route gives is the same pair of key lists. */
96
+ function respond(accepted: readonly string[], rejected: readonly string[]): NextResponse {
97
+ const body: IdentifyResponseBody = { accepted, rejected };
98
+ return NextResponse.json(body);
99
+ }
100
+
101
+ /** Parse the request body into a bounded identifier array, or `null` when malformed. */
102
+ async function parseIdentifiers(request: NextRequest): Promise<unknown[] | null> {
103
+ let body: unknown;
104
+ try {
105
+ body = await request.json();
106
+ } catch {
107
+ return null;
108
+ }
109
+ if (!isPlainObject(body) || !Array.isArray(body.identifiers)) return null;
110
+ if (body.identifiers.length === 0 || body.identifiers.length > MAX_IDENTIFIERS) return null;
111
+ return body.identifiers;
112
+ }
113
+
114
+ /**
115
+ * Validate one raw entry into a `MarketingIdentifierInput`, or `null` when it
116
+ * is not a usable pair. The route rebuilds the pair from untrusted JSON rather
117
+ * than trusting anything the client typed, so only these two fields cross.
118
+ *
119
+ * Both strings share the click-ID bound. The key is looked up in a registry
120
+ * whose entries are short words, so its cap is a garbage fence rather than a
121
+ * limit anyone can reach honestly — and a second number for the same purpose
122
+ * would be one more thing to keep in step, for no gain. The value's bound is
123
+ * the one that matters and is deliberately generous: Meta's `fbclid` routinely
124
+ * runs past 128 characters, and a click ID short by one character matches
125
+ * nothing at the platform that issued it.
126
+ */
127
+ function validateIdentifier(raw: unknown): MarketingIdentifierInput | null {
128
+ if (!isPlainObject(raw)) return null;
129
+ const { key, value } = raw;
130
+ if (typeof key !== 'string' || key.length === 0 || key.length > CLICK_ID_VALUE_MAX_LENGTH) {
131
+ return null;
132
+ }
133
+ if (typeof value !== 'string' || value.length === 0 || value.length > CLICK_ID_VALUE_MAX_LENGTH) {
134
+ return null;
135
+ }
136
+ return { key, value };
137
+ }
138
+
139
+ export async function POST(request: NextRequest): Promise<NextResponse> {
140
+ const submitted = await parseIdentifiers(request);
141
+ if (!submitted) {
142
+ return NextResponse.json({ error: 'malformed identifiers' }, { status: 400 });
143
+ }
144
+
145
+ // Validation runs BEFORE the gates here, unlike the event sibling: its reject
146
+ // shape is positional (one result per submitted item) and can be built from
147
+ // the raw batch, while this route answers with KEYS — which exist only once
148
+ // an entry has been proven to carry one.
149
+ const identifiers: MarketingIdentifierInput[] = [];
150
+ for (const entry of submitted) {
151
+ const identifier = validateIdentifier(entry);
152
+ // A malformed entry is DROPPED, never a 400: one bad pair must not cost a
153
+ // shopper the attribution the others carry. It is then absent from both
154
+ // answer lists rather than reported as rejected, so `rejected` keeps its
155
+ // single meaning — "the upstream does not know this key" — and can never be
156
+ // read as "you sent junk".
157
+ if (identifier) identifiers.push(identifier);
158
+ }
159
+ // Nothing survived validation: answer the empty verdict without an upstream
160
+ // call. Forwarding an empty list would mint a session for a request that has
161
+ // nothing to store against it.
162
+ if (identifiers.length === 0) return respond([], []);
163
+
164
+ const rejectAll = (): NextResponse => respond([], identifiers.map((identifier) => identifier.key));
165
+
166
+ // Inert before `forgecart init` writes `.env` (image-prewarm contract).
167
+ if (!getUpstreamConfig()) return rejectAll();
168
+
169
+ const userAgent = request.headers.get('user-agent') ?? '';
170
+ if (isObviousBot(userAgent)) return rejectAll();
171
+
172
+ const incomingSession = request.cookies.get(SESSION_COOKIE)?.value ?? null;
173
+ const forwardedFor = request.headers.get('x-forwarded-for');
174
+ // #1014: mirror the browser's low-entropy Client Hints trio upstream —
175
+ // Chromium sends them on every request; the shop API's device identification
176
+ // prefers them over the frozen UA.
177
+ const secChUa = request.headers.get('sec-ch-ua') ?? undefined;
178
+ const secChUaMobile = request.headers.get('sec-ch-ua-mobile') ?? undefined;
179
+ const secChUaPlatform = request.headers.get('sec-ch-ua-platform') ?? undefined;
180
+
181
+ // One call for the whole list: the mutation takes an array and the backend
182
+ // writes it against a single identity, so there is no per-item sequencing to
183
+ // get wrong here (the event relay loops only because `trackEvent` takes one
184
+ // event at a time).
185
+ const outcome = await forwardMarketingIdentifiers(identifiers, {
186
+ sessionToken: incomingSession,
187
+ userAgent,
188
+ forwardedFor,
189
+ secChUa,
190
+ secChUaMobile,
191
+ secChUaPlatform,
192
+ });
193
+
194
+ const response = respond(outcome.accepted, outcome.rejected);
195
+ // Establish-only persist: write the session ONLY when the request arrived
196
+ // without one, and then into BOTH of its homes — the httpOnly original the
197
+ // server reads and the mirror the shopper's own socket boots from. A mint
198
+ // against an EXISTING cookie is never persisted here — replacing a live
199
+ // session would vanish the cart it holds, and replacing a stale one belongs
200
+ // to the cart path (see the module docstring).
201
+ if (!incomingSession && outcome.sessionToken) {
202
+ establishSessionCookies(response, outcome.sessionToken);
203
+ }
204
+ return response;
205
+ }
@@ -1,6 +1,7 @@
1
1
  import { NextResponse } from 'next/server';
2
2
  import type { NextRequest } from 'next/server';
3
3
 
4
+ import { SESSION_COOKIE, establishSessionCookies } from '../../../lib/session-cookies';
4
5
  import {
5
6
  forwardTrackEvent,
6
7
  getUpstreamConfig,
@@ -30,14 +31,25 @@ import {
30
31
  * response `extensions` (`forgecart-auth-token`) exactly once; the first
31
32
  * touched item captures it and the remaining items of the batch ride it as
32
33
  * Bearer (one identity per batch). The relay may ESTABLISH the shopper
33
- * identity — persisting the mint into the httpOnly `forgecart-session` cookie
34
- * (same attributes as the cart actions') when the request arrived cookie-less
35
- * but it never REPLACES an existing cookie. The asymmetry is deliberate:
36
- * an incoming cookie that the upstream re-mints against is either a live
37
- * session owned by the cart path (clobbering it would vanish a just-created
38
- * cart the first-touch double-mint race) or a stale one, whose replacement
39
- * is the cart path's job (the SDK adopts re-mints and the cart actions
40
- * persist them; the relay replacing it here would race those writers).
34
+ * identity — persisting that mint into BOTH homes of the session pair, the
35
+ * httpOnly `forgecart-session` cookie and its JS-readable mirror
36
+ * `forgecart-session-client`, through the contract `src/lib/session-cookies.ts`
37
+ * owns and `src/lib/session-actions.ts#syncShopSession` writes too but it
38
+ * never REPLACES an existing cookie. The asymmetry is deliberate: an incoming
39
+ * cookie that the upstream re-mints against is either a live session owned by
40
+ * the cart path (clobbering it would vanish a just-created cart — the
41
+ * first-touch double-mint race) or a stale one, whose replacement is the cart
42
+ * path's job (the SDK adopts re-mints and `syncShopSession` persists them; the
43
+ * relay replacing it here would race those writers).
44
+ *
45
+ * Both homes or neither (#1733): the browser's shop socket boots its auth from
46
+ * the MIRROR (`lib/shop-session.ts`), so a session established into the httpOnly
47
+ * half alone never reaches the client at all — the first cart operation mints a
48
+ * RIVAL session, `syncShopSession` overwrites the pair with it, and the events
49
+ * this batch just recorded stay on a session no attribution read resolves to.
50
+ * Presence, in contrast, is read from the httpOnly copy ALONE: it is the
51
+ * authoritative one, the mirror is derived from it, and a client-writable cookie
52
+ * must not be able to decide whether this relay establishes at all.
41
53
  *
42
54
  * Residual window: two writers that BOTH start cookie-less inside the same
43
55
  * in-flight window remain last-writer-wins. With the tracker's deferred first
@@ -55,10 +67,6 @@ import {
55
67
  * A folder literally named `__fc` would silently 404.
56
68
  */
57
69
 
58
- const SESSION_COOKIE = 'forgecart-session';
59
- /** Mirrors `cart-actions.ts#persistSession` — the cookie is one shared identity. */
60
- const SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
61
-
62
70
  /** The tracker flushes at ≤10 events; anything past this is not our client. */
63
71
  const MAX_BATCH_SIZE = 50;
64
72
 
@@ -169,18 +177,13 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
169
177
  }
170
178
 
171
179
  const response = NextResponse.json({ results });
172
- // Establish-only persist: write the cookie ONLY when the request arrived
173
- // without one. A mint against an EXISTING cookie is never persisted here
174
- // replacing a live session would vanish the cart it holds (first-touch
175
- // double-mint race), and replacing a stale one belongs to the cart path,
176
- // whose SDK capture + persist owns re-mints (see the module docstring).
177
- if (!incomingSession && sessionToken) {
178
- response.cookies.set(SESSION_COOKIE, sessionToken, {
179
- httpOnly: true,
180
- sameSite: 'lax',
181
- path: '/',
182
- maxAge: SESSION_COOKIE_MAX_AGE_SECONDS,
183
- });
184
- }
180
+ // Establish-only persist: write the session ONLY when the request arrived
181
+ // without one and then into BOTH of its homes, which is all
182
+ // `establishSessionCookies` does. A mint against an EXISTING cookie is never
183
+ // persisted here replacing a live session would vanish the cart it holds
184
+ // (first-touch double-mint race), and replacing a stale one belongs to the
185
+ // cart path, whose SDK capture + persist owns re-mints (see the module
186
+ // docstring).
187
+ if (!incomingSession && sessionToken) establishSessionCookies(response, sessionToken);
185
188
  return response;
186
189
  }
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * Gated entirely on `NODE_ENV === 'development'`: a deployed `next start` storefront
12
12
  * answers 404 here and forwards nothing — the whole beacon path ships only in the
13
- * in-pod dev-server. Request access keeps it out of the static prerender (cacheComponents) so it
13
+ * in-pod dev-server. Request access keeps it out of any static prerender so it
14
14
  * behaves identically under `next dev` and a dev build.
15
15
  */
16
16
 
@@ -1,14 +1,25 @@
1
+ import type { Metadata } from 'next';
1
2
  import { connection } from 'next/server';
2
3
  import { Suspense } from 'react';
3
4
 
4
5
  import { CartView } from '../../components/CartView';
5
6
  import { getChannelSellingPlanGroups } from '../../lib/forgecart';
7
+ import { getRequestLocale } from '../../lib/locale/request-binding';
8
+ import { routeRobots } from '../../lib/seo/noindex';
9
+
10
+ /**
11
+ * Never indexable: a cart is per-shopper state, identical in copy for everyone
12
+ * and meaningless to a visitor arriving from a search result.
13
+ */
14
+ export function generateMetadata(): Metadata {
15
+ return { robots: routeRobots('/cart') };
16
+ }
6
17
 
7
18
  /**
8
19
  * Cart route. The shopper's cart itself lives on their OWN websocket
9
20
  * (`CartView` hydrates it client-side); the server's only read here is the
10
21
  * PUBLIC channel subscription plans off the anonymous channel singleton —
11
- * streamed as a hole so the shell stays static (cacheComponents).
22
+ * streamed as a hole while the shell renders.
12
23
  */
13
24
  export default function CartPage() {
14
25
  return (
@@ -20,11 +31,12 @@ export default function CartPage() {
20
31
 
21
32
  async function CartWithPlans() {
22
33
  await connection();
34
+ const { binding } = await getRequestLocale();
23
35
  // Channel-wide subscription plans are public channel data — the one
24
36
  // server-fetched input; the "Subscribe to your whole order" box renders
25
37
  // only when there are any.
26
38
  const channelGroups = await getChannelSellingPlanGroups();
27
- return <CartView channelGroups={channelGroups} />;
39
+ return <CartView channelGroups={channelGroups} locale={binding} />;
28
40
  }
29
41
 
30
42
  function CartRouteSkeleton() {
@@ -1,13 +1,24 @@
1
+ import type { Metadata } from 'next';
1
2
  import { connection } from 'next/server';
2
3
  import { Suspense } from 'react';
3
4
 
4
5
  import { CheckoutGate } from '../../components/checkout/CheckoutGate';
5
6
  import { getAvailableCountries } from '../../lib/forgecart';
7
+ import { getRequestLocale } from '../../lib/locale/request-binding';
8
+ import { routeRobots } from '../../lib/seo/noindex';
9
+
10
+ /**
11
+ * Never indexable: checkout is a funnel step, not a destination, and every
12
+ * nested step inherits the decision through `NOINDEX_PATHS`.
13
+ */
14
+ export function generateMetadata(): Metadata {
15
+ return { robots: routeRobots('/checkout') };
16
+ }
6
17
 
7
18
  /**
8
19
  * Checkout entry. The server's only job here is the PUBLIC prefetch — the
9
20
  * channel's shipping countries off the anonymous channel singleton, streamed
10
- * as a hole (cacheComponents). The session work (the order itself, every
21
+ * as a hole. The session work (the order itself, every
11
22
  * checkout mutation) lives on the shopper's websocket: `CheckoutGate`
12
23
  * hydrates the live cart client-side and mounts the flow, or shows the
13
24
  * honest empty state.
@@ -25,8 +36,9 @@ export default function CheckoutPage() {
25
36
 
26
37
  async function CheckoutWithCountries() {
27
38
  await connection();
39
+ const { binding } = await getRequestLocale();
28
40
  const countries = await getAvailableCountries();
29
- return <CheckoutGate countries={countries} />;
41
+ return <CheckoutGate countries={countries} locale={binding} />;
30
42
  }
31
43
 
32
44
  function CheckoutRouteSkeleton() {