@contentful/experiences-client 0.9.0 → 0.9.2

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 (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +1 -87
  3. package/dist/client-resolution.d.ts +34 -0
  4. package/dist/client-resolution.js +23 -0
  5. package/dist/client-resolution.js.map +1 -0
  6. package/dist/create-delivery-client.d.ts +1 -15
  7. package/dist/create-delivery-client.js +1 -15
  8. package/dist/create-delivery-client.js.map +1 -1
  9. package/dist/destination-guards.d.ts +16 -0
  10. package/dist/destination-guards.js +14 -0
  11. package/dist/destination-guards.js.map +1 -0
  12. package/dist/errors.d.ts +6 -5
  13. package/dist/errors.js.map +1 -1
  14. package/dist/fetch-destination-sitemap.d.ts +41 -0
  15. package/dist/fetch-destination-sitemap.js +50 -0
  16. package/dist/fetch-destination-sitemap.js.map +1 -0
  17. package/dist/fetch-experience.d.ts +3 -25
  18. package/dist/fetch-experience.js +9 -25
  19. package/dist/fetch-experience.js.map +1 -1
  20. package/dist/index.d.ts +4 -11
  21. package/dist/index.js +2 -19
  22. package/dist/index.js.map +1 -1
  23. package/package.json +2 -5
  24. package/dist/constants.d.ts +0 -11
  25. package/dist/constants.js +0 -7
  26. package/dist/constants.js.map +0 -1
  27. package/dist/contentful-experiences.d.ts +0 -75
  28. package/dist/contentful-experiences.js +0 -104
  29. package/dist/contentful-experiences.js.map +0 -1
  30. package/dist/create-optimization-client.d.ts +0 -20
  31. package/dist/create-optimization-client.js +0 -30
  32. package/dist/create-optimization-client.js.map +0 -1
  33. package/dist/event-builder.d.ts +0 -444
  34. package/dist/event-builder.js +0 -282
  35. package/dist/event-builder.js.map +0 -1
  36. package/dist/runtime-event-handoff.d.ts +0 -33
  37. package/dist/runtime-event-handoff.js +0 -68
  38. package/dist/runtime-event-handoff.js.map +0 -1
  39. package/dist/runtime-event-methods.d.ts +0 -52
  40. package/dist/runtime-event-methods.js +0 -109
  41. package/dist/runtime-event-methods.js.map +0 -1
  42. package/dist/sdk-info.d.ts +0 -12
  43. package/dist/sdk-info.js +0 -8
  44. package/dist/sdk-info.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 0.9.2 (2026-10-09)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **runtime:** extract shared runtime from client ([#228](https://github.com/contentful/experiences/pull/228))
6
+
7
+ ### 🧱 Updated Dependencies
8
+
9
+ - Updated core to 0.12.1
10
+
11
+ ## 0.9.1 (2026-10-08)
12
+
13
+ ### 🚀 Features
14
+
15
+ - add fetchDestinationSitemap for paginated route listing [AIS-569] ([#218](https://github.com/contentful/experiences/pull/218))
16
+
1
17
  ## 0.9.0 (2026-10-08)
2
18
 
3
19
  ### 🚀 Features
package/README.md CHANGED
@@ -2,98 +2,12 @@
2
2
 
3
3
  > **Internal package.** Published to npm so framework adapters can resolve it at install time, but you're not meant to import it directly. It comes in transitively via the framework adapter (`@contentful/experiences-react`, `@contentful/experiences-svelte`, etc.).
4
4
 
5
- Isolates `@contentful/experience-delivery` — the generated experience delivery client — so that `@contentful/experiences-sdk-core` stays dependency-free and framework adapters that don't need network access don't pull it in transitively. It also owns the lower-layer shared `ContentfulExperiences` runtime for the public Node and Web SDKs and event construction.
6
-
7
- `ContentfulExperiences` is intentionally not re-exported by framework adapters
8
- and is not a new application-facing adapter API. The existing free functions
9
- remain supported for adapter and application integration.
5
+ Isolates `@contentful/experience-delivery` — the generated Experience Delivery client — and owns the free fetch functions, payload conversion, delivery errors, and XDA-derived request types. The shared runtime and complete `EventBuilder` live in `@contentful/experiences-runtime`, which depends on this package. Client does not build events or depend on Runtime. The existing free functions remain supported for adapter and application integration.
10
6
 
11
7
  ---
12
8
 
13
9
  ## What's in here
14
10
 
15
- ### Shared lower-layer runtime
16
-
17
- `ContentfulExperiences` is an internal shared runtime contract for the public
18
- Node/Web SDKs. Construct it with a space, environment, resolver configuration,
19
- a delivery configuration, and an `eventBuilder` config with an explicit
20
- `channel`; optionally provide a preview configuration. It constructs and retains
21
- those clients, merges configured resolve defaults with per-call options, and
22
- exposes `resolveExperience` and by-id/destination `fetchExperience` methods.
23
- Calling `fetchExperience` with `preview: true` requires preview configuration.
24
-
25
- The runtime-owned Delivery and Preview clients support two security models. For
26
- direct API access, provide an `accessToken`; it may be a supplier and is sent to
27
- the selected endpoint. For a trusted application proxy that adds the upstream
28
- token itself, omit `accessToken` and provide an explicit `host`; the runtime
29
- disables generated bearer authentication. `environmentId` is the Contentful
30
- environment identifier passed to by-ID requests, not the generated delivery
31
- client's optional endpoint-valued `environment`. Endpoint precedence is
32
- explicit `host`, then generated-client `environment`, then the delivery or
33
- preview default.
34
-
35
- Direct browser tokens are credentials but are intentionally browser-visible
36
- when an application's scope and CORS policy permit that model. Proxy mode keeps
37
- the upstream token server-side. In either mode, treat custom endpoints as
38
- trusted configuration.
39
-
40
- The optional preview client enables direct CPA fetches by Experience ID through
41
- the same shared runtime. It is separate from `@contentful/experiences-live-preview`,
42
- which owns Preview Session HTTP loading, WebSocket subscription, and update state.
43
- Destination operations continue to use the delivery client.
44
-
45
- The runtime constructs and owns one `EventBuilder` and one Optimization API
46
- client. The caller supplies the event channel, while the runtime retains this
47
- SDK's default library identity and the runtime locale unless overridden. This
48
- package owns event construction and direct transport for the lower layer,
49
- rather than Core. It exposes a protected binding point used by the public Node
50
- and Web leaves; Client itself does not retain a mutable event profile.
51
-
52
- By default the Optimization client uses the runtime's `spaceId` and maps its
53
- Contentful `environmentId` to the Optimization client's `environment` option.
54
- This is the same Contentful environment identifier passed to by-ID delivery
55
- requests, not the generated delivery client's endpoint-valued `environment`
56
- setting. The optional `optimization` configuration can provide API-specific
57
- endpoint, feature, and fetch overrides; the runtime always constructs and owns
58
- the Optimization client. Custom Optimization base URLs receive complete event
59
- and profile payloads, so treat them as sensitive-data destinations and use only
60
- trusted HTTPS origins outside explicit local development.
61
-
62
- The bound event methods normally send immediately: `identify`, `page`, and
63
- `track` upsert a profile through the Personalization API; view, click, hover,
64
- and flag-view events send a one-event batch through Analytics. Queues, durable
65
- profile persistence, consent gating, lifecycle/beacon delivery, automatic
66
- interaction tracking, and Live Preview integration are intentionally absent.
67
- The Node leaf additionally has a request-scoped, one-shot handoff journal for
68
- paired browser replay; it is not an SDK queue or persistence mechanism. Because
69
- calls are not serialized, callers must await profile-producing calls before
70
- starting another profile-producing or Analytics call on the same bound runtime.
71
-
72
- ### Server-to-browser event handoff contract
73
-
74
- `RuntimeEventHandoff` is the internal replay payload between Node and Web. Node
75
- alone chooses direct `commit` or paired-browser `handoff` delivery per request.
76
- The handoff holds a version, Contentful scope, optional initial profile, ordered
77
- Personalization/Analytics event bodies, and an optional initial-page route key. Its
78
- internal serialized-payload cap is 64 KiB. Node can preflight the initial
79
- Personalization sequence as one batch; individual Personalization methods remain
80
- cumulative and Analytics methods stage without transport. Web validates the
81
- payload, admits page-bearing journals only for a matching browser route,
82
- batches compatible adjacent Personalization entries, preserves locale and Analytics
83
- ordering boundaries, and chains each resulting profile.
84
-
85
- The payload is browser-visible sensitive data. Application integration must use
86
- escaped serialization in a private, no-store response and exclude secrets and
87
- server-only traits. Do not cache, log, or persist it. Replay may partially
88
- commit, has no automatic retry or distributed exactly-once guarantee, and is
89
- independent from Live Preview. A failed replay rejects its receipt but releases
90
- later direct Web calls. See the root [paired replay guide](../../README.md#paired-server-to-browser-replay).
91
-
92
- This package's direct dependencies are the generated delivery client,
93
- `@contentful/optimization-api-client` (event schemas, logger, and transport), `es-toolkit`
94
- (event-property merging), and `zod` (event argument schemas), in addition to
95
- Core. None of those dependencies are introduced into Core.
96
-
97
11
  ### `fetchExperience(experienceOptions, clientOptions, resolveOptions)`
98
12
 
99
13
  The primary fetch + resolve entry point. Fetches an Experience payload from the Experience Delivery API and resolves it into a `PortableRenderPlan` in one call.
@@ -0,0 +1,34 @@
1
+ import { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';
2
+ import { createDebugLogger } from '@contentful/experiences-sdk-core';
3
+
4
+ type ClientOptions = {
5
+ accessToken: string;
6
+ /**
7
+ * Preview access token. Required when calling with `preview: true`.
8
+ */
9
+ previewToken?: string;
10
+ /**
11
+ * Flip between delivery (default) and preview at request time. When
12
+ * `true`, uses `previewToken` and the preview host; when `false` or
13
+ * unset, uses `accessToken` and the delivery host. Ignored when a
14
+ * pre-made `client` is passed instead of inline creds.
15
+ */
16
+ preview?: boolean;
17
+ /**
18
+ * Custom base URL for the delivery client (staging, proxy, per-region).
19
+ * Wins over the `preview`-derived default host — combine `host` with
20
+ * `preview: true` to point preview mode at a non-prod endpoint.
21
+ * Omit for the standard delivery / preview hosts.
22
+ */
23
+ host?: string;
24
+ } | {
25
+ client: ContentfulViewDeliveryClient;
26
+ };
27
+ /**
28
+ * Resolve a `ClientOptions` union into a real delivery client. Shared by
29
+ * `fetchExperience` and `fetchDestinationSitemap` — both accept the same
30
+ * inline-creds-or-pre-made-client shape.
31
+ */
32
+ declare function resolveDeliveryClient(clientOptions: ClientOptions, log: ReturnType<typeof createDebugLogger>, callerName: string): ContentfulViewDeliveryClient;
33
+
34
+ export { type ClientOptions, resolveDeliveryClient };
@@ -0,0 +1,23 @@
1
+ import { createClient } from "./create-delivery-client.js";
2
+ import { PREVIEW_HOST } from "./hosts.js";
3
+ function resolveDeliveryClient(clientOptions, log, callerName) {
4
+ if ("client" in clientOptions) {
5
+ log.log("using caller-supplied delivery client");
6
+ return clientOptions.client;
7
+ }
8
+ const { accessToken, previewToken, preview, host } = clientOptions;
9
+ if (preview && !previewToken) {
10
+ throw new Error(`${callerName}() called with preview: true but no previewToken was provided`);
11
+ }
12
+ const resolvedHost = host ?? (preview ? PREVIEW_HOST : void 0);
13
+ const client = createClient({
14
+ accessToken: preview ? previewToken : accessToken,
15
+ host: resolvedHost
16
+ });
17
+ log.log("created delivery client", { preview: Boolean(preview), host: resolvedHost });
18
+ return client;
19
+ }
20
+ export {
21
+ resolveDeliveryClient
22
+ };
23
+ //# sourceMappingURL=client-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client-resolution.ts"],"sourcesContent":["import type { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';\nimport { createClient } from './create-delivery-client.js';\nimport { createDebugLogger } from '@contentful/experiences-sdk-core';\nimport { PREVIEW_HOST } from './hosts.js';\n\nexport type ClientOptions =\n | {\n accessToken: string;\n /**\n * Preview access token. Required when calling with `preview: true`.\n */\n previewToken?: string;\n /**\n * Flip between delivery (default) and preview at request time. When\n * `true`, uses `previewToken` and the preview host; when `false` or\n * unset, uses `accessToken` and the delivery host. Ignored when a\n * pre-made `client` is passed instead of inline creds.\n */\n preview?: boolean;\n /**\n * Custom base URL for the delivery client (staging, proxy, per-region).\n * Wins over the `preview`-derived default host — combine `host` with\n * `preview: true` to point preview mode at a non-prod endpoint.\n * Omit for the standard delivery / preview hosts.\n */\n host?: string;\n }\n | { client: ContentfulViewDeliveryClient };\n\n/**\n * Resolve a `ClientOptions` union into a real delivery client. Shared by\n * `fetchExperience` and `fetchDestinationSitemap` — both accept the same\n * inline-creds-or-pre-made-client shape.\n */\nexport function resolveDeliveryClient(\n clientOptions: ClientOptions,\n log: ReturnType<typeof createDebugLogger>,\n callerName: string\n): ContentfulViewDeliveryClient {\n if ('client' in clientOptions) {\n log.log('using caller-supplied delivery client');\n return clientOptions.client;\n }\n\n const { accessToken, previewToken, preview, host } = clientOptions;\n if (preview && !previewToken) {\n throw new Error(`${callerName}() called with preview: true but no previewToken was provided`);\n }\n const resolvedHost = host ?? (preview ? PREVIEW_HOST : undefined);\n const client = createClient({\n accessToken: preview ? (previewToken as string) : accessToken,\n host: resolvedHost,\n });\n log.log('created delivery client', { preview: Boolean(preview), host: resolvedHost });\n return client;\n}\n"],"mappings":"AACA,SAAS,oBAAoB;AAE7B,SAAS,oBAAoB;AA+BtB,SAAS,sBACd,eACA,KACA,YAC8B;AAC9B,MAAI,YAAY,eAAe;AAC7B,QAAI,IAAI,uCAAuC;AAC/C,WAAO,cAAc;AAAA,EACvB;AAEA,QAAM,EAAE,aAAa,cAAc,SAAS,KAAK,IAAI;AACrD,MAAI,WAAW,CAAC,cAAc;AAC5B,UAAM,IAAI,MAAM,GAAG,UAAU,+DAA+D;AAAA,EAC9F;AACA,QAAM,eAAe,SAAS,UAAU,eAAe;AACvD,QAAM,SAAS,aAAa;AAAA,IAC1B,aAAa,UAAW,eAA0B;AAAA,IAClD,MAAM;AAAA,EACR,CAAC;AACD,MAAI,IAAI,2BAA2B,EAAE,SAAS,QAAQ,OAAO,GAAG,MAAM,aAAa,CAAC;AACpF,SAAO;AACT;","names":[]}
@@ -10,20 +10,6 @@ type CreateClientOptions = {
10
10
  */
11
11
  host?: string;
12
12
  } & Omit<ContentfulViewDeliveryClient.Options, 'token' | 'baseUrl'>;
13
- type RuntimeDeliveryClientBaseOptions = Omit<ContentfulViewDeliveryClient.Options, 'token' | 'baseUrl'>;
14
- /** Configuration for a Delivery or Preview client owned by a runtime. */
15
- type RuntimeDeliveryClientOptions = RuntimeDeliveryClientBaseOptions & ({
16
- /** Bearer token or token supplier used for direct Delivery/Preview API access. */
17
- accessToken: ContentfulViewDeliveryClient.Options['token'];
18
- /** Optional endpoint override or endpoint supplier. */
19
- host?: ContentfulViewDeliveryClient.Options['baseUrl'];
20
- } | {
21
- accessToken?: never;
22
- /** Explicit proxy endpoint that owns upstream authentication. */
23
- host: NonNullable<ContentfulViewDeliveryClient.Options['baseUrl']>;
24
- });
25
13
  declare function createClient(options: CreateClientOptions): ContentfulViewDeliveryClient;
26
- /** Constructs a runtime-owned client, disabling generated auth for a tokenless proxy. */
27
- declare function createRuntimeDeliveryClient(options: RuntimeDeliveryClientOptions, defaultHost?: NonNullable<ContentfulViewDeliveryClient.Options['baseUrl']>): ContentfulViewDeliveryClient;
28
14
 
29
- export { type CreateClientOptions, type RuntimeDeliveryClientOptions, createClient, createRuntimeDeliveryClient };
15
+ export { type CreateClientOptions, createClient };
@@ -7,21 +7,7 @@ function createClient(options) {
7
7
  baseUrl: host
8
8
  });
9
9
  }
10
- function createRuntimeDeliveryClient(options, defaultHost) {
11
- const { accessToken, host, ...rest } = options;
12
- if (accessToken === void 0 && host === void 0) {
13
- throw new Error(
14
- "Runtime delivery configuration requires accessToken unless an explicit proxy host is provided"
15
- );
16
- }
17
- const baseUrl = host ?? (rest.environment === void 0 ? defaultHost : void 0);
18
- const clientOptions = accessToken === void 0 ? { ...rest, auth: rest.auth ?? false, baseUrl } : { ...rest, token: accessToken, baseUrl };
19
- return new ContentfulViewDeliveryClient(
20
- clientOptions
21
- );
22
- }
23
10
  export {
24
- createClient,
25
- createRuntimeDeliveryClient
11
+ createClient
26
12
  };
27
13
  //# sourceMappingURL=create-delivery-client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-delivery-client.ts"],"sourcesContent":["import { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';\n\n/** Options for the legacy functional Delivery client constructor. */\nexport type CreateClientOptions = {\n accessToken: string;\n /**\n * Base URL for the delivery client. Defaults to `DELIVERY_HOST` when omitted.\n * Pass `PREVIEW_HOST` for preview mode, or any custom URL (staging, proxy,\n * per-region). Both constants are exported from this package.\n */\n host?: string;\n} & Omit<ContentfulViewDeliveryClient.Options, 'token' | 'baseUrl'>;\n\ntype RuntimeDeliveryClientBaseOptions = Omit<\n ContentfulViewDeliveryClient.Options,\n 'token' | 'baseUrl'\n>;\n\n/** Configuration for a Delivery or Preview client owned by a runtime. */\nexport type RuntimeDeliveryClientOptions = RuntimeDeliveryClientBaseOptions &\n (\n | {\n /** Bearer token or token supplier used for direct Delivery/Preview API access. */\n accessToken: ContentfulViewDeliveryClient.Options['token'];\n /** Optional endpoint override or endpoint supplier. */\n host?: ContentfulViewDeliveryClient.Options['baseUrl'];\n }\n | {\n accessToken?: never;\n /** Explicit proxy endpoint that owns upstream authentication. */\n host: NonNullable<ContentfulViewDeliveryClient.Options['baseUrl']>;\n }\n );\n\nexport function createClient(options: CreateClientOptions): ContentfulViewDeliveryClient {\n const { accessToken, host, ...rest } = options;\n return new ContentfulViewDeliveryClient({\n ...rest,\n token: accessToken,\n baseUrl: host,\n });\n}\n\n/** Constructs a runtime-owned client, disabling generated auth for a tokenless proxy. */\nexport function createRuntimeDeliveryClient(\n options: RuntimeDeliveryClientOptions,\n defaultHost?: NonNullable<ContentfulViewDeliveryClient.Options['baseUrl']>\n): ContentfulViewDeliveryClient {\n const { accessToken, host, ...rest } = options;\n if (accessToken === undefined && host === undefined) {\n throw new Error(\n 'Runtime delivery configuration requires accessToken unless an explicit proxy host is provided'\n );\n }\n\n const baseUrl = host ?? (rest.environment === undefined ? defaultHost : undefined);\n const clientOptions =\n accessToken === undefined\n ? { ...rest, auth: rest.auth ?? false, baseUrl }\n : { ...rest, token: accessToken, baseUrl };\n\n return new ContentfulViewDeliveryClient(\n clientOptions as unknown as ContentfulViewDeliveryClient.Options\n );\n}\n"],"mappings":"AAAA,SAAS,oCAAoC;AAkCtC,SAAS,aAAa,SAA4D;AACvF,QAAM,EAAE,aAAa,MAAM,GAAG,KAAK,IAAI;AACvC,SAAO,IAAI,6BAA6B;AAAA,IACtC,GAAG;AAAA,IACH,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AACH;AAGO,SAAS,4BACd,SACA,aAC8B;AAC9B,QAAM,EAAE,aAAa,MAAM,GAAG,KAAK,IAAI;AACvC,MAAI,gBAAgB,UAAa,SAAS,QAAW;AACnD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,SAAS,KAAK,gBAAgB,SAAY,cAAc;AACxE,QAAM,gBACJ,gBAAgB,SACZ,EAAE,GAAG,MAAM,MAAM,KAAK,QAAQ,OAAO,QAAQ,IAC7C,EAAE,GAAG,MAAM,OAAO,aAAa,QAAQ;AAE7C,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/create-delivery-client.ts"],"sourcesContent":["import { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';\n\n/** Options for the legacy functional Delivery client constructor. */\nexport type CreateClientOptions = {\n accessToken: string;\n /**\n * Base URL for the delivery client. Defaults to `DELIVERY_HOST` when omitted.\n * Pass `PREVIEW_HOST` for preview mode, or any custom URL (staging, proxy,\n * per-region). Both constants are exported from this package.\n */\n host?: string;\n} & Omit<ContentfulViewDeliveryClient.Options, 'token' | 'baseUrl'>;\n\nexport function createClient(options: CreateClientOptions): ContentfulViewDeliveryClient {\n const { accessToken, host, ...rest } = options;\n return new ContentfulViewDeliveryClient({\n ...rest,\n token: accessToken,\n baseUrl: host,\n });\n}\n"],"mappings":"AAAA,SAAS,oCAAoC;AAatC,SAAS,aAAa,SAA4D;AACvF,QAAM,EAAE,aAAa,MAAM,GAAG,KAAK,IAAI;AACvC,SAAO,IAAI,6BAA6B;AAAA,IACtC,GAAG;AAAA,IACH,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AACH;","names":[]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Throws `DestinationPreviewNotSupportedError` when `clientOptions.preview` is
3
+ * `true` and `experienceOptions` is destination-shaped (a caller-supplied
4
+ * `{ client }` already ignores `preview` entirely, so this only fires for the
5
+ * inline-credentials branch). Shared by `fetchExperience` and
6
+ * `fetchDestinationSitemap` — both call into the same preview-less
7
+ * Destinations Delivery API.
8
+ */
9
+ declare function assertDestinationPreviewSupported(isPreview: boolean | undefined, destination: {
10
+ spaceId: string;
11
+ destinationId: string;
12
+ nodeId?: string;
13
+ path?: string;
14
+ }): void;
15
+
16
+ export { assertDestinationPreviewSupported };
@@ -0,0 +1,14 @@
1
+ import { DestinationPreviewNotSupportedError } from "./errors.js";
2
+ function assertDestinationPreviewSupported(isPreview, destination) {
3
+ if (!isPreview) return;
4
+ const { spaceId, destinationId, nodeId, path } = destination;
5
+ const locator = nodeId !== void 0 ? `nodeId "${nodeId}"` : `path "${path}"`;
6
+ throw new DestinationPreviewNotSupportedError(
7
+ `preview: true was combined with a destination-shaped call (destinationId "${destinationId}", ${locator}). The Destinations Delivery API does not support preview mode yet \u2014 pass preview: false (or omit it) for destination-based fetches.`,
8
+ { spaceId, destinationId, nodeId, path }
9
+ );
10
+ }
11
+ export {
12
+ assertDestinationPreviewSupported
13
+ };
14
+ //# sourceMappingURL=destination-guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/destination-guards.ts"],"sourcesContent":["import { DestinationPreviewNotSupportedError } from './errors.js';\n\n/**\n * Throws `DestinationPreviewNotSupportedError` when `clientOptions.preview` is\n * `true` and `experienceOptions` is destination-shaped (a caller-supplied\n * `{ client }` already ignores `preview` entirely, so this only fires for the\n * inline-credentials branch). Shared by `fetchExperience` and\n * `fetchDestinationSitemap` — both call into the same preview-less\n * Destinations Delivery API.\n */\nexport function assertDestinationPreviewSupported(\n isPreview: boolean | undefined,\n destination: { spaceId: string; destinationId: string; nodeId?: string; path?: string }\n): void {\n if (!isPreview) return;\n\n const { spaceId, destinationId, nodeId, path } = destination;\n const locator = nodeId !== undefined ? `nodeId \"${nodeId}\"` : `path \"${path}\"`;\n throw new DestinationPreviewNotSupportedError(\n `preview: true was combined with a destination-shaped call (destinationId ` +\n `\"${destinationId}\", ${locator}). The Destinations Delivery API does not support ` +\n `preview mode yet — pass preview: false (or omit it) for destination-based fetches.`,\n { spaceId, destinationId, nodeId, path }\n );\n}\n"],"mappings":"AAAA,SAAS,2CAA2C;AAU7C,SAAS,kCACd,WACA,aACM;AACN,MAAI,CAAC,UAAW;AAEhB,QAAM,EAAE,SAAS,eAAe,QAAQ,KAAK,IAAI;AACjD,QAAM,UAAU,WAAW,SAAY,WAAW,MAAM,MAAM,SAAS,IAAI;AAC3E,QAAM,IAAI;AAAA,IACR,6EACM,aAAa,MAAM,OAAO;AAAA,IAEhC,EAAE,SAAS,eAAe,QAAQ,KAAK;AAAA,EACzC;AACF;","names":[]}
package/dist/errors.d.ts CHANGED
@@ -27,11 +27,12 @@ declare class ExperienceFetchError extends Error {
27
27
  });
28
28
  }
29
29
  /**
30
- * Thrown by `fetchExperience` when `clientOptions.preview` is `true` and
31
- * `experienceOptions` is destination-shaped (`destinationId` + `nodeId` or
32
- * `destinationId` + `path`). The Destinations Delivery API has no preview
33
- * host or preview-token support yet, so this guards against silently hitting the
34
- * production host with a preview token it doesn't accept.
30
+ * Thrown by `fetchExperience` and `fetchDestinationSitemap` when
31
+ * `clientOptions.preview` is `true` for a destination-shaped call
32
+ * (`destinationId` + `nodeId`/`path`, or a sitemap fetch). The Destinations
33
+ * Delivery API has no preview host or preview-token support yet, so this
34
+ * guards against silently hitting the production host with a preview token
35
+ * it doesn't accept.
35
36
  *
36
37
  * This is a bridge, not a permanent design: revisit once the platform ships
37
38
  * preview support for destinations.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Thrown by `fetchExperience` for a fetch failure that is not \"experience not\n * found\" — network failure, an invalid/expired token, a 5xx from the\n * delivery API, etc. This failure mode is \"surface clearly to the host app,\"\n * unlike the diagnostics `resolveExperience` collects for resolve/render-time\n * issues — there's no partial payload to render around a fetch that never\n * returned one, so a real thrown class is the right shape.\n *\n * `NotFoundError` (the delivery client's class, re-exported from this\n * package) is the one exception: it passes through undisturbed so callers\n * can route it to their framework's 404 idiom, per the existing\n * distinguishable-404 contract.\n *\n * `environmentId` is `undefined` for the destination-shaped `fetchExperience`\n * branches — the destinations delivery endpoints are space-scoped only, with\n * no environment segment, unlike the by-id path.\n */\nexport class ExperienceFetchError extends Error {\n readonly spaceId: string;\n readonly environmentId: string | undefined;\n readonly experienceId: string;\n\n constructor(\n message: string,\n options: {\n spaceId: string;\n environmentId?: string;\n experienceId: string;\n cause?: unknown;\n }\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = 'ExperienceFetchError';\n this.spaceId = options.spaceId;\n this.environmentId = options.environmentId;\n this.experienceId = options.experienceId;\n }\n}\n\n/**\n * Thrown by `fetchExperience` when `clientOptions.preview` is `true` and\n * `experienceOptions` is destination-shaped (`destinationId` + `nodeId` or\n * `destinationId` + `path`). The Destinations Delivery API has no preview\n * host or preview-token support yet, so this guards against silently hitting the\n * production host with a preview token it doesn't accept.\n *\n * This is a bridge, not a permanent design: revisit once the platform ships\n * preview support for destinations.\n *\n * Only fires for the inline-credentials branch of `ClientOptions`. A\n * caller-supplied `{ client }` already ignores `preview` entirely, per that\n * branch's existing contract.\n */\nexport class DestinationPreviewNotSupportedError extends Error {\n readonly spaceId: string;\n readonly destinationId: string;\n readonly nodeId: string | undefined;\n readonly path: string | undefined;\n\n constructor(\n message: string,\n options: { spaceId: string; destinationId: string; nodeId?: string; path?: string }\n ) {\n super(message);\n this.name = 'DestinationPreviewNotSupportedError';\n this.spaceId = options.spaceId;\n this.destinationId = options.destinationId;\n this.nodeId = options.nodeId;\n this.path = options.path;\n }\n}\n"],"mappings":"AAiBO,MAAM,6BAA6B,MAAM;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,SACA,SAMA;AACA,UAAM,SAAS,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,MAAS;AACjF,SAAK,OAAO;AACZ,SAAK,UAAU,QAAQ;AACvB,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,eAAe,QAAQ;AAAA,EAC9B;AACF;AAgBO,MAAM,4CAA4C,MAAM;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,SACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,UAAU,QAAQ;AACvB,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,SAAS,QAAQ;AACtB,SAAK,OAAO,QAAQ;AAAA,EACtB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Thrown by `fetchExperience` for a fetch failure that is not \"experience not\n * found\" — network failure, an invalid/expired token, a 5xx from the\n * delivery API, etc. This failure mode is \"surface clearly to the host app,\"\n * unlike the diagnostics `resolveExperience` collects for resolve/render-time\n * issues — there's no partial payload to render around a fetch that never\n * returned one, so a real thrown class is the right shape.\n *\n * `NotFoundError` (the delivery client's class, re-exported from this\n * package) is the one exception: it passes through undisturbed so callers\n * can route it to their framework's 404 idiom, per the existing\n * distinguishable-404 contract.\n *\n * `environmentId` is `undefined` for the destination-shaped `fetchExperience`\n * branches — the destinations delivery endpoints are space-scoped only, with\n * no environment segment, unlike the by-id path.\n */\nexport class ExperienceFetchError extends Error {\n readonly spaceId: string;\n readonly environmentId: string | undefined;\n readonly experienceId: string;\n\n constructor(\n message: string,\n options: {\n spaceId: string;\n environmentId?: string;\n experienceId: string;\n cause?: unknown;\n }\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = 'ExperienceFetchError';\n this.spaceId = options.spaceId;\n this.environmentId = options.environmentId;\n this.experienceId = options.experienceId;\n }\n}\n\n/**\n * Thrown by `fetchExperience` and `fetchDestinationSitemap` when\n * `clientOptions.preview` is `true` for a destination-shaped call\n * (`destinationId` + `nodeId`/`path`, or a sitemap fetch). The Destinations\n * Delivery API has no preview host or preview-token support yet, so this\n * guards against silently hitting the production host with a preview token\n * it doesn't accept.\n *\n * This is a bridge, not a permanent design: revisit once the platform ships\n * preview support for destinations.\n *\n * Only fires for the inline-credentials branch of `ClientOptions`. A\n * caller-supplied `{ client }` already ignores `preview` entirely, per that\n * branch's existing contract.\n */\nexport class DestinationPreviewNotSupportedError extends Error {\n readonly spaceId: string;\n readonly destinationId: string;\n readonly nodeId: string | undefined;\n readonly path: string | undefined;\n\n constructor(\n message: string,\n options: { spaceId: string; destinationId: string; nodeId?: string; path?: string }\n ) {\n super(message);\n this.name = 'DestinationPreviewNotSupportedError';\n this.spaceId = options.spaceId;\n this.destinationId = options.destinationId;\n this.nodeId = options.nodeId;\n this.path = options.path;\n }\n}\n"],"mappings":"AAiBO,MAAM,6BAA6B,MAAM;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,SACA,SAMA;AACA,UAAM,SAAS,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,MAAS;AACjF,SAAK,OAAO;AACZ,SAAK,UAAU,QAAQ;AACvB,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,eAAe,QAAQ;AAAA,EAC9B;AACF;AAiBO,MAAM,4CAA4C,MAAM;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,SACA,SACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,UAAU,QAAQ;AACvB,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,SAAS,QAAQ;AACtB,SAAK,OAAO,QAAQ;AAAA,EACtB;AACF;","names":[]}
@@ -0,0 +1,41 @@
1
+ import { ClientOptions } from './client-resolution.js';
2
+ import '@contentful/experience-delivery';
3
+ import '@contentful/experiences-sdk-core';
4
+
5
+ type FetchDestinationSitemapOptions = {
6
+ spaceId: string;
7
+ destinationId: string;
8
+ };
9
+ type FetchDestinationSitemapPaginationOptions = {
10
+ /** Maximum number of paths to return. Defaults to `100`, maximum `1000`. */
11
+ limit?: number;
12
+ /** Cursor from a previous call's `pages.next`. Treat as opaque; don't combine with `pagePrev`. */
13
+ pageNext?: string;
14
+ /** Cursor from a previous call's `pages.prev`. Treat as opaque; don't combine with `pageNext`. */
15
+ pagePrev?: string;
16
+ };
17
+ type SitemapPathItem = {
18
+ /** Path this entry routes. */
19
+ path: string;
20
+ /** Flat id extracted from the entry's `experienceUrn`. */
21
+ experienceId: string;
22
+ /** Type of the Destination Node that claims this path, e.g. `"DestinationExperienceNode"`. */
23
+ type: string;
24
+ };
25
+ type DestinationSitemapResult = {
26
+ items: SitemapPathItem[];
27
+ limit: number;
28
+ pages: {
29
+ next?: string;
30
+ prev?: string;
31
+ };
32
+ };
33
+ /**
34
+ * List every routable path in a published Destination. Bulk enumeration —
35
+ * pairs with `fetchExperience`'s `destinationId` + `nodeId`/`path` branches,
36
+ * which resolve one path at a time. Does not hydrate any Experience itself
37
+ * and does not call `fetchExperience` internally.
38
+ */
39
+ declare function fetchDestinationSitemap(destinationOptions: FetchDestinationSitemapOptions, clientOptions: ClientOptions, paginationOptions?: FetchDestinationSitemapPaginationOptions): Promise<DestinationSitemapResult>;
40
+
41
+ export { type DestinationSitemapResult, type FetchDestinationSitemapOptions, type FetchDestinationSitemapPaginationOptions, type SitemapPathItem, fetchDestinationSitemap };
@@ -0,0 +1,50 @@
1
+ import { ContentfulViewDelivery } from "@contentful/experience-delivery";
2
+ import { createDebugLogger } from "@contentful/experiences-sdk-core";
3
+ import { assertDestinationPreviewSupported } from "./destination-guards.js";
4
+ import { resolveDeliveryClient } from "./client-resolution.js";
5
+ import { ExperienceFetchError } from "./errors.js";
6
+ async function fetchDestinationSitemap(destinationOptions, clientOptions, paginationOptions = {}) {
7
+ const { spaceId, destinationId } = destinationOptions;
8
+ const log = createDebugLogger(false, "client");
9
+ if (!("client" in clientOptions)) {
10
+ assertDestinationPreviewSupported(clientOptions.preview, { spaceId, destinationId });
11
+ }
12
+ const client = resolveDeliveryClient(clientOptions, log, "fetchDestinationSitemap");
13
+ const { limit, pageNext, pagePrev } = paginationOptions;
14
+ let response;
15
+ try {
16
+ response = await client.destination.sitemap(spaceId, destinationId, {
17
+ limit,
18
+ pageNext,
19
+ pagePrev
20
+ });
21
+ } catch (err) {
22
+ if (err instanceof ContentfulViewDelivery.NotFoundError) {
23
+ throw err;
24
+ }
25
+ const reason = err instanceof Error ? err.message : String(err);
26
+ throw new ExperienceFetchError(
27
+ `Failed to fetch sitemap for Destination "${destinationId}" (space "${spaceId}"): ${reason}. Check network connectivity, the access token, and that the space/destination ids are correct.`,
28
+ { spaceId, experienceId: destinationId, cause: err }
29
+ );
30
+ }
31
+ const result = {
32
+ items: response.paths.items.map((item) => ({
33
+ path: item.path,
34
+ experienceId: extractIdFromUrn(item.experienceUrn),
35
+ type: item.sys.type
36
+ })),
37
+ limit: response.paths.limit,
38
+ pages: response.paths.pages
39
+ };
40
+ log.lazy("received sitemap page", () => result);
41
+ return result;
42
+ }
43
+ function extractIdFromUrn(urn) {
44
+ const segments = urn.split("/").filter((s) => s.length > 0);
45
+ return segments[segments.length - 1] ?? urn;
46
+ }
47
+ export {
48
+ fetchDestinationSitemap
49
+ };
50
+ //# sourceMappingURL=fetch-destination-sitemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fetch-destination-sitemap.ts"],"sourcesContent":["import { ContentfulViewDelivery } from '@contentful/experience-delivery';\nimport { createDebugLogger } from '@contentful/experiences-sdk-core';\nimport { assertDestinationPreviewSupported } from './destination-guards.js';\nimport { resolveDeliveryClient, type ClientOptions } from './client-resolution.js';\nimport { ExperienceFetchError } from './errors.js';\n\nexport type FetchDestinationSitemapOptions = {\n spaceId: string;\n destinationId: string;\n};\n\nexport type FetchDestinationSitemapPaginationOptions = {\n /** Maximum number of paths to return. Defaults to `100`, maximum `1000`. */\n limit?: number;\n /** Cursor from a previous call's `pages.next`. Treat as opaque; don't combine with `pagePrev`. */\n pageNext?: string;\n /** Cursor from a previous call's `pages.prev`. Treat as opaque; don't combine with `pageNext`. */\n pagePrev?: string;\n};\n\nexport type SitemapPathItem = {\n /** Path this entry routes. */\n path: string;\n /** Flat id extracted from the entry's `experienceUrn`. */\n experienceId: string;\n /** Type of the Destination Node that claims this path, e.g. `\"DestinationExperienceNode\"`. */\n type: string;\n};\n\nexport type DestinationSitemapResult = {\n items: SitemapPathItem[];\n limit: number;\n pages: { next?: string; prev?: string };\n};\n\n/**\n * List every routable path in a published Destination. Bulk enumeration —\n * pairs with `fetchExperience`'s `destinationId` + `nodeId`/`path` branches,\n * which resolve one path at a time. Does not hydrate any Experience itself\n * and does not call `fetchExperience` internally.\n */\nexport async function fetchDestinationSitemap(\n destinationOptions: FetchDestinationSitemapOptions,\n clientOptions: ClientOptions,\n paginationOptions: FetchDestinationSitemapPaginationOptions = {}\n): Promise<DestinationSitemapResult> {\n const { spaceId, destinationId } = destinationOptions;\n const log = createDebugLogger(false, 'client');\n\n if (!('client' in clientOptions)) {\n assertDestinationPreviewSupported(clientOptions.preview, { spaceId, destinationId });\n }\n\n const client = resolveDeliveryClient(clientOptions, log, 'fetchDestinationSitemap');\n\n const { limit, pageNext, pagePrev } = paginationOptions;\n\n let response: ContentfulViewDelivery.DestinationSitemap;\n try {\n response = await client.destination.sitemap(spaceId, destinationId, {\n limit,\n pageNext,\n pagePrev,\n });\n } catch (err) {\n if (err instanceof ContentfulViewDelivery.NotFoundError) {\n throw err;\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new ExperienceFetchError(\n `Failed to fetch sitemap for Destination \"${destinationId}\" (space \"${spaceId}\"): ` +\n `${reason}. Check network connectivity, the access token, and that the space/` +\n `destination ids are correct.`,\n { spaceId, experienceId: destinationId, cause: err }\n );\n }\n\n const result = {\n items: response.paths.items.map((item) => ({\n path: item.path,\n experienceId: extractIdFromUrn(item.experienceUrn),\n type: item.sys.type,\n })),\n limit: response.paths.limit,\n pages: response.paths.pages,\n };\n\n log.lazy('received sitemap page', () => result);\n\n return result;\n}\n\n/**\n * Extract the flat id from an `experienceUrn`. Real shape:\n * crn:contentful:::experience:spaces/$self/environments/$self/experiences/<id>\n *\n * Same one-line logic as the private `extractIdFromUrn` in\n * `packages/core/src/resolve-experience.ts` — not shared across the\n * `core`/`client` package boundary (core stays zero-dep) for one line.\n */\nfunction extractIdFromUrn(urn: string): string {\n const segments = urn.split('/').filter((s) => s.length > 0);\n return segments[segments.length - 1] ?? urn;\n}\n"],"mappings":"AAAA,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAClC,SAAS,yCAAyC;AAClD,SAAS,6BAAiD;AAC1D,SAAS,4BAA4B;AAqCrC,eAAsB,wBACpB,oBACA,eACA,oBAA8D,CAAC,GAC5B;AACnC,QAAM,EAAE,SAAS,cAAc,IAAI;AACnC,QAAM,MAAM,kBAAkB,OAAO,QAAQ;AAE7C,MAAI,EAAE,YAAY,gBAAgB;AAChC,sCAAkC,cAAc,SAAS,EAAE,SAAS,cAAc,CAAC;AAAA,EACrF;AAEA,QAAM,SAAS,sBAAsB,eAAe,KAAK,yBAAyB;AAElF,QAAM,EAAE,OAAO,UAAU,SAAS,IAAI;AAEtC,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,OAAO,YAAY,QAAQ,SAAS,eAAe;AAAA,MAClE;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,eAAe,uBAAuB,eAAe;AACvD,YAAM;AAAA,IACR;AACA,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI;AAAA,MACR,4CAA4C,aAAa,aAAa,OAAO,OACxE,MAAM;AAAA,MAEX,EAAE,SAAS,cAAc,eAAe,OAAO,IAAI;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,SAAS;AAAA,IACb,OAAO,SAAS,MAAM,MAAM,IAAI,CAAC,UAAU;AAAA,MACzC,MAAM,KAAK;AAAA,MACX,cAAc,iBAAiB,KAAK,aAAa;AAAA,MACjD,MAAM,KAAK,IAAI;AAAA,IACjB,EAAE;AAAA,IACF,OAAO,SAAS,MAAM;AAAA,IACtB,OAAO,SAAS,MAAM;AAAA,EACxB;AAEA,MAAI,KAAK,yBAAyB,MAAM,MAAM;AAE9C,SAAO;AACT;AAUA,SAAS,iBAAiB,KAAqB;AAC7C,QAAM,WAAW,IAAI,MAAM,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC1D,SAAO,SAAS,SAAS,SAAS,CAAC,KAAK;AAC1C;","names":[]}
@@ -1,5 +1,6 @@
1
- import { ContentfulViewDelivery, ContentfulViewDeliveryClient } from '@contentful/experience-delivery';
1
+ import { ContentfulViewDelivery } from '@contentful/experience-delivery';
2
2
  import { ResolverConfig, PortableRenderPlan } from '@contentful/experiences-sdk-core';
3
+ import { ClientOptions } from './client-resolution.js';
3
4
 
4
5
  type DeliveryExperienceRequestExtensions = ContentfulViewDelivery.GetWithOverridesExperienceRequestExtensions;
5
6
  type PersonalizationOptions = NonNullable<DeliveryExperienceRequestExtensions['personalization']>;
@@ -52,29 +53,6 @@ interface DestinationRedirectResult {
52
53
  path: string;
53
54
  };
54
55
  }
55
- type ClientOptions = {
56
- accessToken: string;
57
- /**
58
- * Preview access token. Required when calling with `preview: true`.
59
- */
60
- previewToken?: string;
61
- /**
62
- * Flip between delivery (default) and preview at request time. When
63
- * `true`, `fetchExperience` uses `previewToken` and the preview host;
64
- * when `false` or unset, it uses `accessToken` and the delivery host.
65
- * Ignored when a pre-made `client` is passed instead of inline creds.
66
- */
67
- preview?: boolean;
68
- /**
69
- * Custom base URL for the delivery client (staging, proxy, per-region).
70
- * Wins over the `preview`-derived default host — combine `host` with
71
- * `preview: true` to point preview mode at a non-prod endpoint.
72
- * Omit for the standard delivery / preview hosts.
73
- */
74
- host?: string;
75
- } | {
76
- client: ContentfulViewDeliveryClient;
77
- };
78
56
  type ResolveOptions = {
79
57
  config: ResolverConfig;
80
58
  /**
@@ -95,4 +73,4 @@ declare function fetchExperience(experienceOptions: ByIdExperienceOptions, clien
95
73
  declare function fetchExperience(experienceOptions: ByDestinationNodeIdExperienceOptions, clientOptions: ClientOptions, resolveOptions: ResolveOptions): Promise<PortableRenderPlan | DestinationRedirectResult>;
96
74
  declare function fetchExperience(experienceOptions: ByDestinationPathExperienceOptions, clientOptions: ClientOptions, resolveOptions: ResolveOptions): Promise<PortableRenderPlan | DestinationRedirectResult>;
97
75
 
98
- export { type ByDestinationNodeIdExperienceOptions, type ByDestinationPathExperienceOptions, type ByIdExperienceOptions, type ClientOptions, type DestinationRedirectResult, type ExperienceOptions, type ExperienceRequestExtensions, type PersonalizationOptions, type ResolveOptions, fetchExperience };
76
+ export { type ByDestinationNodeIdExperienceOptions, type ByDestinationPathExperienceOptions, type ByIdExperienceOptions, ClientOptions, type DestinationRedirectResult, type ExperienceOptions, type ExperienceRequestExtensions, type PersonalizationOptions, type ResolveOptions, fetchExperience };
@@ -1,8 +1,8 @@
1
1
  import { ContentfulViewDelivery } from "@contentful/experience-delivery";
2
2
  import { createDebugLogger, resolveExperience } from "@contentful/experiences-sdk-core";
3
- import { createClient } from "./create-delivery-client.js";
4
- import { DestinationPreviewNotSupportedError, ExperienceFetchError } from "./errors.js";
5
- import { PREVIEW_HOST } from "./hosts.js";
3
+ import { assertDestinationPreviewSupported } from "./destination-guards.js";
4
+ import { resolveDeliveryClient } from "./client-resolution.js";
5
+ import { ExperienceFetchError } from "./errors.js";
6
6
  import {
7
7
  readSourceMap,
8
8
  toExperiencePayload,
@@ -11,32 +11,16 @@ import {
11
11
  async function fetchExperience(experienceOptions, clientOptions, resolveOptions) {
12
12
  const { config, metadata, debug } = resolveOptions;
13
13
  const log = createDebugLogger(debug, "client");
14
- if (!("client" in clientOptions) && clientOptions.preview && "destinationId" in experienceOptions) {
14
+ if (!("client" in clientOptions) && "destinationId" in experienceOptions) {
15
15
  const { spaceId: spaceId2, destinationId } = experienceOptions;
16
16
  const locator = "nodeId" in experienceOptions ? { nodeId: experienceOptions.nodeId } : { path: experienceOptions.path };
17
- throw new DestinationPreviewNotSupportedError(
18
- `fetchExperience() called with preview: true and a destination-shaped experienceOptions (destinationId "${destinationId}"). The Destinations Delivery API does not support preview mode yet \u2014 pass preview: false (or omit it) for destination-based fetches.`,
19
- { spaceId: spaceId2, destinationId, ...locator }
20
- );
21
- }
22
- let client;
23
- if ("client" in clientOptions) {
24
- client = clientOptions.client;
25
- log.log("using caller-supplied delivery client");
26
- } else {
27
- const { accessToken, previewToken, preview, host } = clientOptions;
28
- if (preview && !previewToken) {
29
- throw new Error(
30
- "fetchExperience() called with preview: true but no previewToken was provided"
31
- );
32
- }
33
- const resolvedHost = host ?? (preview ? PREVIEW_HOST : void 0);
34
- client = createClient({
35
- accessToken: preview ? previewToken : accessToken,
36
- host: resolvedHost
17
+ assertDestinationPreviewSupported(clientOptions.preview, {
18
+ spaceId: spaceId2,
19
+ destinationId,
20
+ ...locator
37
21
  });
38
- log.log("created delivery client", { preview: Boolean(preview), host: resolvedHost });
39
22
  }
23
+ const client = resolveDeliveryClient(clientOptions, log, "fetchExperience");
40
24
  if ("nodeId" in experienceOptions) {
41
25
  const { spaceId: spaceId2, destinationId, nodeId } = experienceOptions;
42
26
  return fetchByDestination(
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fetch-experience.ts"],"sourcesContent":["import type { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';\nimport { ContentfulViewDelivery } from '@contentful/experience-delivery';\nimport { createDebugLogger, resolveExperience } from '@contentful/experiences-sdk-core';\nimport type { PortableRenderPlan, ResolverConfig } from '@contentful/experiences-sdk-core';\nimport { createClient } from './create-delivery-client.js';\nimport { DestinationPreviewNotSupportedError, ExperienceFetchError } from './errors.js';\nimport { PREVIEW_HOST } from './hosts.js';\nimport {\n readSourceMap,\n toExperiencePayload,\n toExperiencePayloadFromDestination,\n type ExperienceResponse,\n} from './to-experience-payload.js';\n\ntype DeliveryExperienceRequestExtensions =\n ContentfulViewDelivery.GetWithOverridesExperienceRequestExtensions;\n\nexport type PersonalizationOptions = NonNullable<\n DeliveryExperienceRequestExtensions['personalization']\n>;\n\nexport type ExperienceRequestExtensions = Omit<\n DeliveryExperienceRequestExtensions,\n 'personalization'\n>;\n\nexport type ByIdExperienceOptions = {\n spaceId: string;\n environmentId: string;\n experienceId: string;\n locale?: string;\n /**\n * Profile and events used by XDA to personalize the resolved Experience.\n */\n personalization?: PersonalizationOptions;\n /**\n * Additional XDA request extensions such as source maps. Supplying a defined\n * extension switches the request to POST.\n */\n extensions?: ExperienceRequestExtensions;\n};\n\n/**\n * Resolve an Experience by the Destination Node instead of `experienceId`\n * The destinations delivery endpoints are space-scoped only and have no locale\n * param, unlike the by-id path.\n */\nexport type ByDestinationNodeIdExperienceOptions = {\n spaceId: string;\n destinationId: string;\n nodeId: string;\n};\n\n/**\n * Resolve an Experience by the absolute path wired up to a\n * Destination Node, instead of a node id directly. Same scoping as\n * `ByDestinationNodeIdExperienceOptions` — no `environmentId` or `locale`.\n */\nexport type ByDestinationPathExperienceOptions = {\n spaceId: string;\n destinationId: string;\n /** Absolute path to resolve. Must start with `/`. */\n path: string;\n};\n\nexport type ExperienceOptions =\n ByIdExperienceOptions | ByDestinationNodeIdExperienceOptions | ByDestinationPathExperienceOptions;\n\n/**\n * A Destination resolution that the app must act on as control flow — honor\n * it by navigating or re-resolving at the new path — rather than a hydrated\n * Experience to render. Modeled as a normal return value, not a thrown error,\n * per the Destinations delivery API's own framing of a redirect response.\n */\nexport interface DestinationRedirectResult {\n redirect: { path: string };\n}\n\nexport type ClientOptions =\n | {\n accessToken: string;\n /**\n * Preview access token. Required when calling with `preview: true`.\n */\n previewToken?: string;\n /**\n * Flip between delivery (default) and preview at request time. When\n * `true`, `fetchExperience` uses `previewToken` and the preview host;\n * when `false` or unset, it uses `accessToken` and the delivery host.\n * Ignored when a pre-made `client` is passed instead of inline creds.\n */\n preview?: boolean;\n /**\n * Custom base URL for the delivery client (staging, proxy, per-region).\n * Wins over the `preview`-derived default host — combine `host` with\n * `preview: true` to point preview mode at a non-prod endpoint.\n * Omit for the standard delivery / preview hosts.\n */\n host?: string;\n }\n | { client: ContentfulViewDeliveryClient };\n\nexport type ResolveOptions = {\n config: ResolverConfig;\n /**\n * Arbitrary per-render metadata exposed to every resolver as\n * `ctx.experience.metadata` and readable at render time via the framework\n * adapter's `useExperience()` / `getExperience()`.\n */\n metadata?: Record<string, unknown>;\n /**\n * Observability switch. When on: logs the fetch (host, ids) and the raw\n * payload here, and threads through to `resolveExperience` (resolution steps\n * + `resolveData` timings) and the renderer (visible missing-component box,\n * debug fallback). A single boolean across fetch + resolve + render.\n */\n debug?: boolean;\n};\n\n// TS overload signatures, not real redeclarations — base ESLint's no-redeclare doesn't know the\n// overload form (@typescript-eslint's own version does), so each repeated signature needs a\n// disable comment.\nexport async function fetchExperience(\n experienceOptions: ByIdExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan>;\n// eslint-disable-next-line no-redeclare -- see above\nexport async function fetchExperience(\n experienceOptions: ByDestinationNodeIdExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan | DestinationRedirectResult>;\n// eslint-disable-next-line no-redeclare -- see above\nexport async function fetchExperience(\n experienceOptions: ByDestinationPathExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan | DestinationRedirectResult>;\n// eslint-disable-next-line no-redeclare -- see above\nexport async function fetchExperience(\n experienceOptions: ExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan | DestinationRedirectResult> {\n const { config, metadata, debug } = resolveOptions;\n const log = createDebugLogger(debug, 'client');\n\n if (\n !('client' in clientOptions) &&\n clientOptions.preview &&\n 'destinationId' in experienceOptions\n ) {\n const { spaceId, destinationId } = experienceOptions;\n const locator =\n 'nodeId' in experienceOptions\n ? { nodeId: experienceOptions.nodeId }\n : { path: experienceOptions.path };\n throw new DestinationPreviewNotSupportedError(\n `fetchExperience() called with preview: true and a destination-shaped experienceOptions ` +\n `(destinationId \"${destinationId}\"). The Destinations Delivery API does not support ` +\n `preview mode yet — pass preview: false (or omit it) for destination-based fetches.`,\n { spaceId, destinationId, ...locator }\n );\n }\n\n let client: ContentfulViewDeliveryClient;\n if ('client' in clientOptions) {\n client = clientOptions.client;\n log.log('using caller-supplied delivery client');\n } else {\n const { accessToken, previewToken, preview, host } = clientOptions;\n if (preview && !previewToken) {\n throw new Error(\n 'fetchExperience() called with preview: true but no previewToken was provided'\n );\n }\n const resolvedHost = host ?? (preview ? PREVIEW_HOST : undefined);\n client = createClient({\n accessToken: preview ? (previewToken as string) : accessToken,\n host: resolvedHost,\n });\n log.log('created delivery client', { preview: Boolean(preview), host: resolvedHost });\n }\n\n if ('nodeId' in experienceOptions) {\n const { spaceId, destinationId, nodeId } = experienceOptions;\n return fetchByDestination(\n { spaceId, destinationId, locator: nodeId },\n () => client.destination.resolveByNodeId(spaceId, destinationId, nodeId),\n log,\n { config, metadata, debug }\n );\n }\n\n if ('path' in experienceOptions) {\n const { spaceId, destinationId, path } = experienceOptions;\n return fetchByDestination(\n { spaceId, destinationId, locator: path },\n () => client.destination.resolveByPath(spaceId, destinationId, { path }),\n log,\n { config, metadata, debug }\n );\n }\n\n const { spaceId, environmentId, experienceId, locale, personalization, extensions } =\n experienceOptions;\n\n log.log('fetching experience', {\n spaceId,\n environmentId,\n experienceId,\n locale,\n // avoiding log any personalization sensitive data\n personalization: personalization !== undefined,\n extensions,\n });\n\n const response = await fetchByExperienceId(client, experienceOptions);\n\n const payload = toExperiencePayload(response);\n const sourceMap = extensions?.sourceMap !== undefined ? readSourceMap(response) : undefined;\n\n log.lazy('received raw payload', () => payload);\n\n return resolveExperience(payload, config, {\n metadata,\n debug,\n sourceMap,\n });\n}\n\nasync function fetchByExperienceId(\n client: ContentfulViewDeliveryClient,\n options: ByIdExperienceOptions\n): Promise<ExperienceResponse> {\n const { spaceId, environmentId, experienceId, locale, personalization, extensions } = options;\n const requestExtensions: DeliveryExperienceRequestExtensions = {\n ...extensions,\n ...(personalization === undefined ? {} : { personalization }),\n };\n\n try {\n // Both methods hit the same endpoint, but only the POST operation accepts\n // extensions in its request body. Keep the cacheable GET when there are no\n // extensions to evaluate.\n // The generated delivery client owns the alpha-feature header on both\n // paths, including when the caller supplied the client instance.\n //\n // Await inside the try so rejected requests are normalized below.\n if (hasRequestExtensions(requestExtensions)) {\n return await client.experience.getWithOverrides(spaceId, environmentId, experienceId, {\n locale,\n extensions: requestExtensions,\n });\n }\n\n return await client.experience.get(spaceId, environmentId, experienceId, { locale });\n } catch (err) {\n // Preserve expected 404 control flow for framework routers. Other failures\n // are wrapped with stable SDK context instead of exposing transport-specific\n // error shapes.\n if (err instanceof ContentfulViewDelivery.NotFoundError) {\n throw err;\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new ExperienceFetchError(\n `Failed to fetch Experience \"${experienceId}\" (space \"${spaceId}\", environment ` +\n `\"${environmentId}\"): ${reason}. Check network connectivity, the access token, and ` +\n `that the space/environment/experience ids are correct.`,\n { spaceId, environmentId, experienceId, cause: err }\n );\n }\n}\n\n/**\n * Shared response-handling for both destination-shaped `fetchExperience`\n * branches (by node id, by path).\n */\nasync function fetchByDestination(\n identity: { spaceId: string; destinationId: string; locator: string },\n resolve: () => Promise<ContentfulViewDelivery.DestinationExperienceResolutionResponse>,\n log: ReturnType<typeof createDebugLogger>,\n resolveOptions: {\n config: ResolverConfig;\n metadata: Record<string, unknown> | undefined;\n debug: boolean | undefined;\n }\n): Promise<PortableRenderPlan | DestinationRedirectResult> {\n const { spaceId, destinationId, locator } = identity;\n const { config, metadata, debug } = resolveOptions;\n\n log.log('resolving destination experience', { spaceId, destinationId, locator });\n\n let response: ContentfulViewDelivery.DestinationExperienceResolutionResponse;\n try {\n response = await resolve();\n } catch (err) {\n if (err instanceof ContentfulViewDelivery.NotFoundError) {\n throw err;\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new ExperienceFetchError(\n `Failed to resolve Destination \"${destinationId}\" at \"${locator}\" (space \"${spaceId}\"): ` +\n `${reason}. Check network connectivity, the access token, and that the space/` +\n `destination/node-or-path values are correct.`,\n { spaceId, experienceId: locator, cause: err }\n );\n }\n\n if ('redirect' in response) {\n log.log('destination resolved to a redirect', response.redirect);\n return { redirect: response.redirect };\n }\n\n const [firstExperience] = response.experiences;\n if (!firstExperience) {\n throw new ExperienceFetchError(\n `Destination \"${destinationId}\" resolved \"${locator}\" to zero Experiences ` +\n `(space \"${spaceId}\"). Expected exactly one hydrated Experience or a redirect.`,\n { spaceId, experienceId: locator }\n );\n }\n\n const payload = toExperiencePayloadFromDestination(firstExperience.experience);\n log.lazy('received raw payload', () => payload);\n\n return resolveExperience(payload, config, {\n metadata,\n debug,\n });\n}\n\nfunction hasRequestExtensions(\n extensions: DeliveryExperienceRequestExtensions | undefined\n): extensions is DeliveryExperienceRequestExtensions {\n return (\n extensions !== undefined &&\n Object.values(extensions).some((extension) => extension !== undefined)\n );\n}\n"],"mappings":"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","names":["spaceId"]}
1
+ {"version":3,"sources":["../src/fetch-experience.ts"],"sourcesContent":["import type { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';\nimport { ContentfulViewDelivery } from '@contentful/experience-delivery';\nimport { createDebugLogger, resolveExperience } from '@contentful/experiences-sdk-core';\nimport type { PortableRenderPlan, ResolverConfig } from '@contentful/experiences-sdk-core';\nimport { assertDestinationPreviewSupported } from './destination-guards.js';\nimport { resolveDeliveryClient, type ClientOptions } from './client-resolution.js';\nimport { ExperienceFetchError } from './errors.js';\nimport {\n readSourceMap,\n toExperiencePayload,\n toExperiencePayloadFromDestination,\n type ExperienceResponse,\n} from './to-experience-payload.js';\n\nexport type { ClientOptions };\n\ntype DeliveryExperienceRequestExtensions =\n ContentfulViewDelivery.GetWithOverridesExperienceRequestExtensions;\n\nexport type PersonalizationOptions = NonNullable<\n DeliveryExperienceRequestExtensions['personalization']\n>;\n\nexport type ExperienceRequestExtensions = Omit<\n DeliveryExperienceRequestExtensions,\n 'personalization'\n>;\n\nexport type ByIdExperienceOptions = {\n spaceId: string;\n environmentId: string;\n experienceId: string;\n locale?: string;\n /**\n * Profile and events used by XDA to personalize the resolved Experience.\n */\n personalization?: PersonalizationOptions;\n /**\n * Additional XDA request extensions such as source maps. Supplying a defined\n * extension switches the request to POST.\n */\n extensions?: ExperienceRequestExtensions;\n};\n\n/**\n * Resolve an Experience by the Destination Node instead of `experienceId`\n * The destinations delivery endpoints are space-scoped only and have no locale\n * param, unlike the by-id path.\n */\nexport type ByDestinationNodeIdExperienceOptions = {\n spaceId: string;\n destinationId: string;\n nodeId: string;\n};\n\n/**\n * Resolve an Experience by the absolute path wired up to a\n * Destination Node, instead of a node id directly. Same scoping as\n * `ByDestinationNodeIdExperienceOptions` — no `environmentId` or `locale`.\n */\nexport type ByDestinationPathExperienceOptions = {\n spaceId: string;\n destinationId: string;\n /** Absolute path to resolve. Must start with `/`. */\n path: string;\n};\n\nexport type ExperienceOptions =\n ByIdExperienceOptions | ByDestinationNodeIdExperienceOptions | ByDestinationPathExperienceOptions;\n\n/**\n * A Destination resolution that the app must act on as control flow — honor\n * it by navigating or re-resolving at the new path — rather than a hydrated\n * Experience to render. Modeled as a normal return value, not a thrown error,\n * per the Destinations delivery API's own framing of a redirect response.\n */\nexport interface DestinationRedirectResult {\n redirect: { path: string };\n}\n\nexport type ResolveOptions = {\n config: ResolverConfig;\n /**\n * Arbitrary per-render metadata exposed to every resolver as\n * `ctx.experience.metadata` and readable at render time via the framework\n * adapter's `useExperience()` / `getExperience()`.\n */\n metadata?: Record<string, unknown>;\n /**\n * Observability switch. When on: logs the fetch (host, ids) and the raw\n * payload here, and threads through to `resolveExperience` (resolution steps\n * + `resolveData` timings) and the renderer (visible missing-component box,\n * debug fallback). A single boolean across fetch + resolve + render.\n */\n debug?: boolean;\n};\n\n// TS overload signatures, not real redeclarations — base ESLint's no-redeclare doesn't know the\n// overload form (@typescript-eslint's own version does), so each repeated signature needs a\n// disable comment.\nexport async function fetchExperience(\n experienceOptions: ByIdExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan>;\n// eslint-disable-next-line no-redeclare -- see above\nexport async function fetchExperience(\n experienceOptions: ByDestinationNodeIdExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan | DestinationRedirectResult>;\n// eslint-disable-next-line no-redeclare -- see above\nexport async function fetchExperience(\n experienceOptions: ByDestinationPathExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan | DestinationRedirectResult>;\n// eslint-disable-next-line no-redeclare -- see above\nexport async function fetchExperience(\n experienceOptions: ExperienceOptions,\n clientOptions: ClientOptions,\n resolveOptions: ResolveOptions\n): Promise<PortableRenderPlan | DestinationRedirectResult> {\n const { config, metadata, debug } = resolveOptions;\n const log = createDebugLogger(debug, 'client');\n\n if (!('client' in clientOptions) && 'destinationId' in experienceOptions) {\n const { spaceId, destinationId } = experienceOptions;\n const locator =\n 'nodeId' in experienceOptions\n ? { nodeId: experienceOptions.nodeId }\n : { path: experienceOptions.path };\n assertDestinationPreviewSupported(clientOptions.preview, {\n spaceId,\n destinationId,\n ...locator,\n });\n }\n\n const client = resolveDeliveryClient(clientOptions, log, 'fetchExperience');\n\n if ('nodeId' in experienceOptions) {\n const { spaceId, destinationId, nodeId } = experienceOptions;\n return fetchByDestination(\n { spaceId, destinationId, locator: nodeId },\n () => client.destination.resolveByNodeId(spaceId, destinationId, nodeId),\n log,\n { config, metadata, debug }\n );\n }\n\n if ('path' in experienceOptions) {\n const { spaceId, destinationId, path } = experienceOptions;\n return fetchByDestination(\n { spaceId, destinationId, locator: path },\n () => client.destination.resolveByPath(spaceId, destinationId, { path }),\n log,\n { config, metadata, debug }\n );\n }\n\n const { spaceId, environmentId, experienceId, locale, personalization, extensions } =\n experienceOptions;\n\n log.log('fetching experience', {\n spaceId,\n environmentId,\n experienceId,\n locale,\n // avoiding log any personalization sensitive data\n personalization: personalization !== undefined,\n extensions,\n });\n\n const response = await fetchByExperienceId(client, experienceOptions);\n\n const payload = toExperiencePayload(response);\n const sourceMap = extensions?.sourceMap !== undefined ? readSourceMap(response) : undefined;\n\n log.lazy('received raw payload', () => payload);\n\n return resolveExperience(payload, config, {\n metadata,\n debug,\n sourceMap,\n });\n}\n\nasync function fetchByExperienceId(\n client: ContentfulViewDeliveryClient,\n options: ByIdExperienceOptions\n): Promise<ExperienceResponse> {\n const { spaceId, environmentId, experienceId, locale, personalization, extensions } = options;\n const requestExtensions: DeliveryExperienceRequestExtensions = {\n ...extensions,\n ...(personalization === undefined ? {} : { personalization }),\n };\n\n try {\n // Both methods hit the same endpoint, but only the POST operation accepts\n // extensions in its request body. Keep the cacheable GET when there are no\n // extensions to evaluate.\n // The generated delivery client owns the alpha-feature header on both\n // paths, including when the caller supplied the client instance.\n //\n // Await inside the try so rejected requests are normalized below.\n if (hasRequestExtensions(requestExtensions)) {\n return await client.experience.getWithOverrides(spaceId, environmentId, experienceId, {\n locale,\n extensions: requestExtensions,\n });\n }\n\n return await client.experience.get(spaceId, environmentId, experienceId, { locale });\n } catch (err) {\n // Preserve expected 404 control flow for framework routers. Other failures\n // are wrapped with stable SDK context instead of exposing transport-specific\n // error shapes.\n if (err instanceof ContentfulViewDelivery.NotFoundError) {\n throw err;\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new ExperienceFetchError(\n `Failed to fetch Experience \"${experienceId}\" (space \"${spaceId}\", environment ` +\n `\"${environmentId}\"): ${reason}. Check network connectivity, the access token, and ` +\n `that the space/environment/experience ids are correct.`,\n { spaceId, environmentId, experienceId, cause: err }\n );\n }\n}\n\n/**\n * Shared response-handling for both destination-shaped `fetchExperience`\n * branches (by node id, by path).\n */\nasync function fetchByDestination(\n identity: { spaceId: string; destinationId: string; locator: string },\n resolve: () => Promise<ContentfulViewDelivery.DestinationExperienceResolutionResponse>,\n log: ReturnType<typeof createDebugLogger>,\n resolveOptions: {\n config: ResolverConfig;\n metadata: Record<string, unknown> | undefined;\n debug: boolean | undefined;\n }\n): Promise<PortableRenderPlan | DestinationRedirectResult> {\n const { spaceId, destinationId, locator } = identity;\n const { config, metadata, debug } = resolveOptions;\n\n log.log('resolving destination experience', { spaceId, destinationId, locator });\n\n let response: ContentfulViewDelivery.DestinationExperienceResolutionResponse;\n try {\n response = await resolve();\n } catch (err) {\n if (err instanceof ContentfulViewDelivery.NotFoundError) {\n throw err;\n }\n const reason = err instanceof Error ? err.message : String(err);\n throw new ExperienceFetchError(\n `Failed to resolve Destination \"${destinationId}\" at \"${locator}\" (space \"${spaceId}\"): ` +\n `${reason}. Check network connectivity, the access token, and that the space/` +\n `destination/node-or-path values are correct.`,\n { spaceId, experienceId: locator, cause: err }\n );\n }\n\n if ('redirect' in response) {\n log.log('destination resolved to a redirect', response.redirect);\n return { redirect: response.redirect };\n }\n\n const [firstExperience] = response.experiences;\n if (!firstExperience) {\n throw new ExperienceFetchError(\n `Destination \"${destinationId}\" resolved \"${locator}\" to zero Experiences ` +\n `(space \"${spaceId}\"). Expected exactly one hydrated Experience or a redirect.`,\n { spaceId, experienceId: locator }\n );\n }\n\n const payload = toExperiencePayloadFromDestination(firstExperience.experience);\n log.lazy('received raw payload', () => payload);\n\n return resolveExperience(payload, config, {\n metadata,\n debug,\n });\n}\n\nfunction hasRequestExtensions(\n extensions: DeliveryExperienceRequestExtensions | undefined\n): extensions is DeliveryExperienceRequestExtensions {\n return (\n extensions !== undefined &&\n Object.values(extensions).some((extension) => extension !== undefined)\n );\n}\n"],"mappings":"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","names":["spaceId"]}