Skip to content

feat(commerce-elastic-path)!: delete the browser token surface - #663

Merged
field123 merged 3 commits into
masterfrom
feat/544-delete-browser-token-surface
Oct 6, 2026
Merged

field123 merged 3 commits into
masterfrom
feat/544-delete-browser-token-surface

Conversation

@field123

@field123 field123 commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #544.

The contract half of expand-then-contract. One token surface — the shopper
envelope, held by the server. The browser's Elastic Path client is deleted,
and with it the parallel cart routes, the hooks built on them, and a request
header that let the page assert which shopper it was with no verification.

CHANGELOG.md carries the migration table: every removal with its named
replacement, or a statement that it has none.

The seams land first

#544's acceptance criteria make the release conditional on #553's three seams
being green beforehand, so the first commit is the seams, proven against the
code as it stood.

  • Seam 1, in the package suite: render the storefront tree in jsdom, call
    every operation a component can reach, assert each request addresses the
    page's own origin.
  • Seam 3, new: one shopper journey in a real browser against the example
    storefront and the integration store — products in the server-rendered HTML,
    a refined listing, a basket, an account member in two accounts with neither
    auto-selected, a price that changes on the account switch, checkout to
    payment, a session outliving the old five-minute window. Spine: zero requests
    from the browser to an Elastic Path host. It needs a store, so it is not in
    CI.
  • Seam 2 lands with the deletion, since it guards absences: the fork
    integrity suite now fails if a browser client, a cart route or the header
    comes back.

Verified

Both suites green. The journey passes against npm pack installed over the
example app's node_modules, with no aliases — twice: once before the
deletion, once after.

The journey exercises this build's server half: every proxy, auth and
account call in it is served by the packed artifact. Its browser half is not
this build — see the first note below — so the components were walked
separately, on a throwaway page in the example app importing them straight
from the packed tarball and rendered by Next. Cart read through the proxy,
item count 1 → 2 on an add, useCheckoutCart returning the same cart id as
useEpCart (one cache entry, which is the point of the rebuild), the promo
input and a twelve-product grid rendering, and an EP Shopper Context husk
carrying cartId="an-id-that-must-be-ignored" changing nothing and appearing
nowhere in the page. Zero requests to any Elastic Path host.

Studio design time still works, driven through the integration UI against the
sandbox project. The canvas renders the EP components from this build, and
Studio's refresh dialog reports updated props only — no removed component
and no removed prop, which is what publish-hostless rejects. EP Shopper
Context reads "(deprecated)" and its props no longer appear.

Two things to know before the release

The hostless bundle must be republished in the same release. A project that
installs commerce-elastic-path gets its browser components from the published
hostless bundle, not from node_modules, and registering the package in the
app host does not displace them — measured: the storefront still called
GET /api/ep/cart with registerAll mounted. So until that bundle is
republished, a storefront taking this package keeps calling a route this change
deletes. Measured with and without the route on the example storefront, nothing
a shopper sees changes: the old components read it only to refresh after a
write, and add-to-cart already fails on it (#477) before falling back to the
proxy. The visible exception is EP Checkout Cart Summary, which takes its lines
from that read — on a storefront that uses it, the order summary empties until
the republish. The package publish and the hostless publish are one step, not
two. It also means the browser half of this change cannot be
walked through the example storefront at all, which is why it was walked on a
harness page instead.

Switching organisation leaves the shopper with no basket, by design.
Selecting the first account keeps the guest basket; switching to the second
returns no cart. session-cart.ts nulls the guest cart at an accountSwitch
deliberately: carrying a cart out of the account that owns it would append the
new account to it and take the old account's prices into checkout. The new
account had no cart of its own, so there was nothing to adopt. This reads as a
conflict with spec story 15 ("my basket follows me when I switch
organisation"), and closing it means copying or re-pricing a cart under another
account — the merge problem ADR-0003 refuses to invent a language for, since
Elastic Path's own copy adds quantities together. A decision for #553, not a
fix to slip into this release; the journey refills the basket after the switch
rather than assert the story.

Beyond the ticket's enumeration, and recorded in the CHANGELOG: the ep_cart
cookie builders go too. They build a cookie for the route contract this release
removes, and the envelope's own cart pointer replaced them in #534.

Two of the three acceptance seams on #553, in place and green against the
code as it stands, which is what the breaking release's safety argument
rests on.

Seam 1 renders the storefront tree in jsdom and calls every operation a
component can reach, asserting each request addresses the page's own
origin. Seam 3 walks one shopper journey in a real browser against the
example storefront and a real store: products in the server-rendered
HTML, a refined listing, a basket, an account member in two accounts with
neither auto-selected, a price that changes on the account switch,
checkout to payment, and a session that outlives the five-minute window
the envelope used to fall back to. Its spine is one assertion — no
request leaves the browser for an Elastic Path host.

Seam 3 needs a storefront and a store, so it has its own package and
stays out of CI; root jest ignores the directory.
One token surface: the shopper envelope, held by the server. The browser's
Elastic Path client is gone, and with it the parallel cart routes, the hooks
built on them, and a request header that let the page assert which shopper it
was without any verification.

What goes: `src/client.ts` with its memory storage adapter, global-singleton
workaround and multi-location inventory interceptor; `commerce.client`;
`createCartRoutes` and the `/api/ep/cart/*` contract, whose 422 on every add
is never fixed; `useShopperFetch`, `useShopperContext`, `use-cart`,
`use-add-item`, `use-update-item`, `use-remove-item`; `resolve-cart-id` and
the `ep_cart` cookie builders, replaced by the envelope's own cart pointer;
`X-Shopper-Context`, which ranked above the HttpOnly cookie with no origin
check and no ownership check, making it an override channel rather than a
mask. `useCheckoutCart` is rebuilt on `useEpCart`, so a checkout summary and
a cart drawer share one cache entry.

Two deletions were decided after the #495 census and land here with the rest:
`cartMergeStrategy`, which `sessionCartResolver` replaced, and the
client-supplied-token shape of `/ep/account/login` — the server mints the
account credential itself, so the verification step that existed only because
the browser supplied it goes too.

Hostless publishing rejects a removed component or a removed published prop,
so EP Shopper Context and EP Promo Code Input's `useServerRoutes` stay
registered and are emptied instead. A husk that still threads props into a
context nobody reads is how a cart id gets re-plumbed past a closed identity
surface, so the body is `return <>{children}</>` and the four props are
hidden. Their descriptions are rewritten, not merely labelled: `accountId`
advertised a logged-in customer id and the context advertised a working
cart-id override, which dies with no replacement — reviving it means handing
a cart id to an unauthenticated route, and the id is the capability.

Seam 2 of #553 lands with the deletion rather than before it: the fork
integrity suite now guards absences as well as presences, so a re-imported
browser client, a restored cart route or a reinstated header fails a test
instead of passing unnoticed.
Jest matched `e2e/shopper-journey.spec.ts` by name and failed to resolve
`@playwright/test`. The ignore entry added to `jest.config.js` does not reach
CI: the workflow passes `--testPathIgnorePatterns` on the command line, which
replaces the config array rather than extending it.

The journey is `*.e2e.ts`, so jest's `testRegex` cannot match it whatever the
ignore list says. The workflow's list gains the directory as well, for anything
added beside it later.
@field123
field123 merged commit bf673ce into master Oct 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Delete the browser token surface and mark the unremovable registrations inert

1 participant