Skip to content

feat(ep-commerce): publish $ctx.productExtensions from product provider (ADR-0007)#350

Merged
field123 merged 1 commit into
masterfrom
feat/product-extensions-map
Jun 4, 2026
Merged

feat(ep-commerce): publish $ctx.productExtensions from product provider (ADR-0007)#350
field123 merged 1 commit into
masterfrom
feat/product-extensions-map

Conversation

@field123

@field123 field123 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

What

Publishes the current product's extensions as a flat, null-safe, slug-keyed map on $ctx.productExtensions from EPProductProvider, so designers and custom components bind to curated named fields — e.g. $ctx.productExtensions['products(iso-standard)'].reference — instead of raw $ctx.currentProduct.rawData.data.attributes.extensions[...] chains.

Why

Companion to the field-display components (#333/#339): those insulate rendered fields; this insulates bindable values (component props, hrefs) that a rendering component can't feed. It's the visual-builder "curated named data source" pattern — Plasmic's native equivalent is a DataProvider whose fields are discoverable in the data picker.

Changes

  • utils/extensions-map.tsbuildExtensionsMap(templates) flattens to { slug: { key: value } }, keyed by the raw template slug (same value the field components' template prop uses) and exposing raw values. A Proxy returns a frozen {} for absent slugs so a binding can never throw — the only missing-slug-safe path inside set-data-cond, which rejects ?.. (Verified @plasmicapp/host's DataProvider keeps data by reference, so the Proxy survives at runtime; ownKeys passes through so picker discovery still works.)
  • EPProductProvider publishes $ctx.productExtensions alongside currentProduct, with a canvas mock-swap mirroring EPProductExtensionsProvider so design-time preview stays populated.
  • Relocation — the pure extension primitives move out of product-extensions/composable/ into shared lower layers so product/ depends downward, not up: format.tsutils/field-format.ts, Extension* types → types/extensions.ts, mocks → utils/extensions-mock.ts. All internal importers updated.

Tests

utils/__tests__/extensions-map.test.ts5/5 passing (root jest): raw-slug keying, raw values, absent-slug {}, absent-field undefined, empty-templates.

Notes

  • Additive & backward-compatible — nothing existing changes; cost is one memoized object walk per product, shipped to every storefront on the package.
  • Full design rationale + alternatives in ADR-0007 (iso-storefront repo).

…er; relocate extension primitives to shared utils/types (ADR-0007)

Adds a flat, null-safe, slug-keyed product-extensions map (buildExtensionsMap; Proxy returns {} for absent slugs) published as $ctx.productExtensions by EPProductProvider, so designers and custom components bind to curated named fields instead of raw rawData chains. Relocates the pure extension primitives (format, Extension* types, mocks) from product-extensions/composable into shared utils/+types so product/ depends downward; all internal importers updated to the new paths. Decision recorded in ADR-0007 (iso-storefront). extensions-map.test.ts passes under root jest.
@field123 field123 force-pushed the feat/product-extensions-map branch from 9d7548f to 78dd75d Compare June 4, 2026 14:46
@field123 field123 merged commit ca131af into master Jun 4, 2026
9 checks passed
field123 added a commit that referenced this pull request Jun 11, 2026
…ation (#372)

* fix(ep-commerce): payment-integrity hardening for the composable checkout

Server-authoritative integrity fixes in the checkout-session pipeline (#369),
all test-first and housed in a new security regression suite (#325).

- Free-vs-paid: settle a free order only on an authoritative
  meta.display_price total of 0. Removes the item-summing fallback that let a
  paid cart whose items didn't parse settle via the manual gateway with no
  charge; an absent total now fails closed to the paid path.
- Order-fields allow-list: gate client customAttributes against a server-side
  allowedCustomAttributeKeys list (string[] | "*"), enforced at the
  updateSession boundary and again before the EP cart/order writes. Fail closed
  when unset; "*" is an explicit permissive opt-out. Stops a client forging or
  overwriting defined order-flow slugs (e.g. consent/audit fields) via
  session.customAttributes.
- confirmOrder reconciliation: verify a real paymentID before calling; on a
  missing id or a failure, log at error level and mark the session
  needsReconciliation plus a reconciliationPending response flag instead of
  swallowing the failure. The order is already charged, so it still completes,
  but a charged-but-unreconciled order is now discoverable.
- Server-authoritative requiresShipping: infer the requirement from the cart's
  product commodity_type (batched product lookup, run only when the client
  suppressed shipping); the client flag may only add a requirement, never
  suppress one a physical cart imposes. A physical cart can no longer ship to
  the billing address via requiresShipping:false. Fails open on a total lookup
  error (availability), closed on a partial resolution.

The security suite also adds token-leak assertions across all checkout-session
routes (including the order-custom-fields write and free-order branch) and a
no-PII-logging assertion. Full elastic-path suite green (118 suites / 1882
tests).

* fix(ep-commerce): repair package build after the #350 extension relocation

- Drop duplicate FormatSpec imports from a non-existent ./format module
  in product-extensions (useResolvedField.tsx, resolve-field.ts) — stale
  leftovers from the ADR-0007 primitive relocation that broke the tsdx
  esm typecheck.
- Sync build-server.mjs's hand-written server.d.ts re-export template with
  src/server.ts (it had drifted: missing createClientCredentialsTokenResolver
  plus several cart functions/types), so the server entry's runtime exports
  have matching type declarations.
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.

1 participant