Skip to content

feat: add bundled XDN/XPA delivery client package [AIS-147]#27

Closed
Lisa White (whitelisab) wants to merge 11 commits into
mainfrom
feat/delivery-client
Closed

feat: add bundled XDN/XPA delivery client package [AIS-147]#27
Lisa White (whitelisab) wants to merge 11 commits into
mainfrom
feat/delivery-client

Conversation

@whitelisab

@whitelisab Lisa White (whitelisab) commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

AIS-147

Summary

  • Creates packages/client (@contentful/experiences-client) — a new internal workspace package that extends ContentfulViewDeliveryClient and adds spaceId, environmentId, and preview fields; preview: false routes to the XDN endpoint, preview: true routes to the XPA preview endpoint
  • Re-exports createExperienceClient from both @contentful/experiences-react and @contentful/experiences-svelte so customers install only the framework adapter — @contentful/experience-delivery is no longer a direct customer dependency
  • Updates both example apps (nextjs, sveltekit) to use createExperienceClient from their respective adapter package; updates AGENTS.md, root README, and adapter-react README to reflect the new architecture

Test plan

  • npx vitest run packages/client/src — 7 unit tests pass (XDN selection, XPA selection, missing credentials errors, view client exposed)
  • cd packages/client && npx tsup — builds cleanly with ESM output and .d.ts declarations
  • Confirm @contentful/experience-delivery no longer appears in examples/nextjs/package.json or examples/sveltekit/package.json
  • Confirm createExperienceClient is importable from @contentful/experiences-react and @contentful/experiences-svelte

Generated with Claude Code

Lisa White (whitelisab) and others added 11 commits July 9, 2026 09:55
Creates @contentful/experiences-client — an internal workspace package that
wraps @contentful/experience-delivery and encapsulates XDN vs. XPA endpoint
selection behind a createExperienceClient() factory. preview: false routes to
the XDN (https://xdn.contentful.com) client; preview: true routes to the XPA
preview endpoint (https://preview.xdn.contentful.com). Both adapter packages
now list @contentful/experiences-client as a workspace dependency, and
@contentful/experience-delivery is removed as a direct dep from both example
apps whose delivery-client helpers are updated to use the factory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Customers import from the framework adapter they already have installed.
The @contentful/experiences-client internal package is now a hidden
transitive dep, not something callers reference directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminates manual property forwarding — view, fragment, and any future
top-level clients on the Fern-generated base are inherited automatically.
Also drops accessToken from the public interface (no reason to expose a
credential on the client object).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevents accidental npm publish via nx release.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New packages/client/README.md explaining the factory, XDN/XPA selection,
  and why it's a separate package
- AGENTS.md: repo layout, package roles table, and delivery-client design
  decision updated to reflect bundled client approach
- Root README: install line drops @contentful/experience-delivery, getting
  started example uses createExperienceClient, workspace table adds client
- adapter-react README: install line, public API listing, and quick
  reference updated to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering, do you think we need an additional package defined in this monorepo or could we just simply have the https://github.com/contentful/contentful-experience-delivery.js be used directly from each adapter?

Looking at the current source code of the new client package, it seems to be a simple wrapper around the existing delivery client with a helper function to instantiate an instance of the class.

We could simplify a lot of code here by not having an additional package, unless do you think this new client package will grow significantly over time?

@whitelisab
Lisa White (whitelisab) marked this pull request as draft July 10, 2026 22:53
Lisa White (whitelisab) added a commit that referenced this pull request Jul 14, 2026
…IS-147] (#30)

* feat(client): scaffold packages/client with ContentfulViewDeliveryClient re-export

Creates the packages/client workspace package (@contentful/experiences-client).
Replaces the ExperienceClient class approach from PR #27 — re-exports
ContentfulViewDeliveryClient directly from @contentful/experience-delivery so
consumers have full client access without a separate install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(client): add fetchExperience with flat discriminated union signature

fetchExperience accepts either inline credentials ({ accessToken, preview?,
spaceId, environmentId, experienceId }) or a pre-created
ContentfulViewDeliveryClient ({ client, spaceId, environmentId, experienceId }).

Inline path constructs the client internally with XDN or XPA base URL based
on the preview flag. Pre-created client path is pass-through, enabling reuse
across calls. When personalization is passed, dispatches to
getExperienceWithOverrides and opts into sourceMap automatically.

Returns PortableRenderPlan | null (null when payload has no nodes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(adapters): re-export ContentfulViewDeliveryClient and fetchExperience

Both @contentful/experiences-react and @contentful/experiences-svelte now
re-export ContentfulViewDeliveryClient and fetchExperience from
@contentful/experiences-client, so consumers install only the framework
adapter and never touch @contentful/experience-delivery or
@contentful/experiences-client directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(examples): migrate to fetchExperience from adapter; drop @contentful/experience-delivery

Both example apps now import fetchExperience and ContentfulViewDeliveryClient
from their framework adapter rather than @contentful/experience-delivery
directly. The separate resolveExperience call is removed — fetch and resolve
happen in one step via fetchExperience. delivery-client.ts in each example
wraps the SDK call with a thin page-level helper that reads env vars and
passes the config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(examples): inline fetchExperience directly into page files; drop delivery-client wrappers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: update lock file for @contentful/experiences-client; apply prettier

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(client): add project.json for NX build graph; use ContentfulViewDelivery namespace for request type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(client): remove personalization placeholder; call getExperience unconditionally

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update READMEs and AGENTS for fetchExperience + packages/client

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: fix prettier formatting on AGENTS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(client): split fetchExperience into 3 positional args; replace preview with host

Signature is now `fetchExperience(experienceOptions, clientOptions, resolveOptions)`,
with `ClientOptions` a discriminated union of `{ accessToken, host? }` or `{ client }`.
`host` is a full base URL string that's passed through as `baseUrl`; XPA vs XDN
endpoint selection is now the caller's concern. Defaults to XDN when omitted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(client): add createClient helper mapping accessToken/host to token/baseUrl

createClient is a functional constructor over ContentfulViewDeliveryClient
that maps the SDK's option names (accessToken, host) to the delivery client's
underlying names (token, baseUrl). Other options pass through unchanged.

fetchExperience's inline-creds branch now routes through createClient, making
it the single source of truth for the name mapping.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(client): let empty-nodes payloads flow to the resolver; expose NotFoundError

fetchExperience no longer treats an empty-nodes payload as "not found" — the
resolver handles empty nodes gracefully and returns a valid PortableRenderPlan.
Return type narrows from `PortableRenderPlan | null` to `PortableRenderPlan`.

To keep the missing-experience case as a proper 404 (not a 500), NotFoundError
is now re-exported from the client + adapter packages. Example call sites
wrap fetchExperience in try/catch and route NotFoundError to their framework's
404 idiom (notFound() in Next, error(404) in SvelteKit).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: update READMEs and AGENTS for new fetchExperience signature

Covers the three review-driven changes:
- 3 positional args (experienceOptions, clientOptions, resolveOptions)
- createClient helper + host string in place of preview boolean
- Empty-nodes payloads pass through; NotFoundError re-exported for the
  missing-experience case

Adds design-decision entries in AGENTS.md capturing the *why* behind each
choice so future contributors don't re-litigate them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

2 participants