@contentful/experiences-client 0.4.7 → 0.4.9

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## 0.4.9 (2026-09-28)
2
+
3
+ ### 🚀 Features
4
+
5
+ - add shared Experiences client runtime ([#206](https://github.com/contentful/experiences/pull/206))
6
+
7
+ ### 🧱 Updated Dependencies
8
+
9
+ - Updated core to 0.8.7
10
+
11
+ ## 0.4.8 (2026-09-22)
12
+
13
+ ### 🚀 Features
14
+
15
+ - guard against preview mode with destination fetches [AIS-568] ([#201](https://github.com/contentful/experiences/pull/201))
16
+
1
17
  ## 0.4.7 (2026-09-22)
2
18
 
3
19
  ### 🚀 Features
package/README.md CHANGED
@@ -2,12 +2,36 @@
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 zero-dep and framework adapters that don't need network access don't pull it in transitively.
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 future Node/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.
6
10
 
7
11
  ---
8
12
 
9
13
  ## What's in here
10
14
 
15
+ ### Shared lower-layer runtime
16
+
17
+ `ContentfulExperiences` is an internal shared runtime contract for future
18
+ Node/Web SDKs. Construct it with a space, environment, resolver configuration,
19
+ and a delivery client source; optionally provide a preview client source. It
20
+ retains those clients, merges configured resolve defaults with per-call options,
21
+ and exposes `resolveExperience` and by-id/destination `fetchExperience` methods.
22
+ Calling `fetchExperience` with `preview: true` requires the optional preview
23
+ client.
24
+
25
+ The runtime constructs and owns one `EventBuilder`. Its defaults are server
26
+ channel, this SDK's library identity, and the runtime locale; callers may
27
+ override the supported event-builder configuration. This is the owner of event
28
+ construction for the lower layer, rather than Core.
29
+
30
+ This package's direct dependencies are the generated delivery client,
31
+ `@contentful/optimization-api-client` (event schemas and logger), `es-toolkit`
32
+ (event-property merging), and `zod` (event argument schemas), in addition to
33
+ Core. None of those dependencies are introduced into Core.
34
+
11
35
  ### `fetchExperience(experienceOptions, clientOptions, resolveOptions)`
12
36
 
13
37
  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,69 @@
1
+ import { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';
2
+ import { ExperiencePayload, PortableRenderPlan, ResolverConfig } from '@contentful/experiences-sdk-core';
3
+ import EventBuilder, { EventBuilderConfig } from './event-builder.js';
4
+ import { DestinationRedirectResult } from './fetch-experience.js';
5
+ import { CreateClientOptions } from './create-client.js';
6
+ import '@contentful/optimization-api-client/api-schemas';
7
+ import 'zod/mini';
8
+
9
+ type RuntimeClientSource = CreateClientOptions | {
10
+ client: ContentfulViewDeliveryClient;
11
+ };
12
+ type RuntimeResolveOptions = {
13
+ metadata?: Record<string, unknown>;
14
+ debug?: boolean;
15
+ initialViewportId?: string;
16
+ };
17
+ type RuntimeEventBuilderConfig = Omit<EventBuilderConfig, 'channel' | 'library'> & {
18
+ channel?: EventBuilderConfig['channel'];
19
+ library?: Partial<EventBuilderConfig['library']>;
20
+ };
21
+ type RuntimeFetchExperienceOptions = {
22
+ experienceId: string;
23
+ locale?: string;
24
+ preview?: boolean;
25
+ withSourceMap?: boolean;
26
+ };
27
+ type RuntimeFetchByDestinationNodeOptions = {
28
+ destinationId: string;
29
+ nodeId: string;
30
+ };
31
+ type RuntimeFetchByDestinationPathOptions = {
32
+ destinationId: string;
33
+ path: string;
34
+ };
35
+ /** Structural contract implemented by the Client runtime. */
36
+ interface ExperienceRuntime {
37
+ readonly spaceId: string;
38
+ readonly environmentId: string;
39
+ readonly locale: string | undefined;
40
+ readonly eventBuilder: EventBuilder;
41
+ resolveExperience(payload: ExperiencePayload, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan>;
42
+ fetchExperience(options: RuntimeFetchExperienceOptions, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan>;
43
+ fetchByDestinationNode(options: RuntimeFetchByDestinationNodeOptions, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan | DestinationRedirectResult>;
44
+ fetchByDestinationPath(options: RuntimeFetchByDestinationPathOptions, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan | DestinationRedirectResult>;
45
+ }
46
+ type ContentfulExperiencesConfig = {
47
+ spaceId: string;
48
+ environmentId: string;
49
+ locale?: string;
50
+ resolverConfig: ResolverConfig;
51
+ delivery: RuntimeClientSource;
52
+ preview?: RuntimeClientSource;
53
+ resolveDefaults?: Pick<RuntimeResolveOptions, 'metadata' | 'debug'>;
54
+ eventBuilder?: RuntimeEventBuilderConfig;
55
+ };
56
+ declare class ContentfulExperiences implements ExperienceRuntime {
57
+ #private;
58
+ readonly spaceId: string;
59
+ readonly environmentId: string;
60
+ readonly locale: string | undefined;
61
+ readonly eventBuilder: EventBuilder;
62
+ constructor(config: ContentfulExperiencesConfig);
63
+ resolveExperience(payload: ExperiencePayload, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan>;
64
+ fetchExperience(options: RuntimeFetchExperienceOptions, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan>;
65
+ fetchByDestinationNode(options: RuntimeFetchByDestinationNodeOptions, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan | DestinationRedirectResult>;
66
+ fetchByDestinationPath(options: RuntimeFetchByDestinationPathOptions, resolveOptions?: RuntimeResolveOptions): Promise<PortableRenderPlan | DestinationRedirectResult>;
67
+ }
68
+
69
+ export { ContentfulExperiences, type ContentfulExperiencesConfig, type ExperienceRuntime, type RuntimeClientSource, type RuntimeEventBuilderConfig, type RuntimeFetchByDestinationNodeOptions, type RuntimeFetchByDestinationPathOptions, type RuntimeFetchExperienceOptions, type RuntimeResolveOptions };
@@ -0,0 +1,87 @@
1
+ import { resolveExperience as resolveCoreExperience } from "@contentful/experiences-sdk-core";
2
+ import EventBuilder from "./event-builder.js";
3
+ import { createClient } from "./create-client.js";
4
+ import { fetchExperience } from "./fetch-experience.js";
5
+ import { PREVIEW_HOST } from "./hosts.js";
6
+ import { DEFAULT_EVENT_CONTEXT_LIBRARY } from "./sdk-info.js";
7
+ class ContentfulExperiences {
8
+ spaceId;
9
+ environmentId;
10
+ locale;
11
+ eventBuilder;
12
+ #resolverConfig;
13
+ #deliveryClient;
14
+ #previewClient;
15
+ #resolveDefaults;
16
+ constructor(config) {
17
+ this.spaceId = config.spaceId;
18
+ this.environmentId = config.environmentId;
19
+ this.locale = config.locale;
20
+ this.#resolverConfig = config.resolverConfig;
21
+ this.#deliveryClient = resolveClient(config.delivery);
22
+ this.#previewClient = config.preview === void 0 ? void 0 : resolveClient(config.preview, PREVIEW_HOST);
23
+ this.#resolveDefaults = config.resolveDefaults ?? {};
24
+ const eventBuilder = config.eventBuilder ?? {};
25
+ this.eventBuilder = new EventBuilder({
26
+ ...eventBuilder,
27
+ channel: eventBuilder.channel ?? "server",
28
+ library: { ...DEFAULT_EVENT_CONTEXT_LIBRARY, ...eventBuilder.library },
29
+ getLocale: eventBuilder.getLocale ?? (() => this.locale)
30
+ });
31
+ }
32
+ resolveExperience(payload, resolveOptions) {
33
+ return resolveCoreExperience(
34
+ payload,
35
+ this.#resolverConfig,
36
+ this.#mergeResolveOptions(resolveOptions)
37
+ );
38
+ }
39
+ fetchExperience(options, resolveOptions) {
40
+ if (options.preview === true && this.#previewClient === void 0) {
41
+ throw new Error(
42
+ "fetchExperience() called with preview: true but this runtime has no preview client"
43
+ );
44
+ }
45
+ return fetchExperience(
46
+ {
47
+ spaceId: this.spaceId,
48
+ environmentId: this.environmentId,
49
+ experienceId: options.experienceId,
50
+ locale: options.locale ?? this.locale,
51
+ withSourceMap: options.withSourceMap
52
+ },
53
+ {
54
+ client: options.preview ? this.#previewClient : this.#deliveryClient
55
+ },
56
+ { config: this.#resolverConfig, ...this.#mergeResolveOptions(resolveOptions) }
57
+ );
58
+ }
59
+ fetchByDestinationNode(options, resolveOptions) {
60
+ return fetchExperience(
61
+ { spaceId: this.spaceId, destinationId: options.destinationId, nodeId: options.nodeId },
62
+ { client: this.#deliveryClient },
63
+ { config: this.#resolverConfig, ...this.#mergeResolveOptions(resolveOptions) }
64
+ );
65
+ }
66
+ fetchByDestinationPath(options, resolveOptions) {
67
+ return fetchExperience(
68
+ { spaceId: this.spaceId, destinationId: options.destinationId, path: options.path },
69
+ { client: this.#deliveryClient },
70
+ { config: this.#resolverConfig, ...this.#mergeResolveOptions(resolveOptions) }
71
+ );
72
+ }
73
+ #mergeResolveOptions(options) {
74
+ return {
75
+ metadata: { ...this.#resolveDefaults.metadata, ...options?.metadata },
76
+ debug: options?.debug ?? this.#resolveDefaults.debug,
77
+ initialViewportId: options?.initialViewportId
78
+ };
79
+ }
80
+ }
81
+ function resolveClient(source, defaultHost) {
82
+ return "client" in source ? source.client : createClient({ ...source, host: source.host ?? defaultHost });
83
+ }
84
+ export {
85
+ ContentfulExperiences
86
+ };
87
+ //# sourceMappingURL=contentful-experiences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contentful-experiences.ts"],"sourcesContent":["import type { ContentfulViewDeliveryClient } from '@contentful/experience-delivery';\nimport { resolveExperience as resolveCoreExperience } from '@contentful/experiences-sdk-core';\nimport type {\n ExperiencePayload,\n PortableRenderPlan,\n ResolverConfig,\n} from '@contentful/experiences-sdk-core';\nimport EventBuilder from './event-builder.js';\nimport type { EventBuilderConfig } from './event-builder.js';\nimport { createClient } from './create-client.js';\nimport { fetchExperience, type DestinationRedirectResult } from './fetch-experience.js';\nimport type { CreateClientOptions } from './create-client.js';\nimport { PREVIEW_HOST } from './hosts.js';\nimport { DEFAULT_EVENT_CONTEXT_LIBRARY } from './sdk-info.js';\n\nexport type RuntimeClientSource = CreateClientOptions | { client: ContentfulViewDeliveryClient };\n\nexport type RuntimeResolveOptions = {\n metadata?: Record<string, unknown>;\n debug?: boolean;\n initialViewportId?: string;\n};\n\nexport type RuntimeEventBuilderConfig = Omit<EventBuilderConfig, 'channel' | 'library'> & {\n channel?: EventBuilderConfig['channel'];\n library?: Partial<EventBuilderConfig['library']>;\n};\n\nexport type RuntimeFetchExperienceOptions = {\n experienceId: string;\n locale?: string;\n preview?: boolean;\n withSourceMap?: boolean;\n};\n\nexport type RuntimeFetchByDestinationNodeOptions = {\n destinationId: string;\n nodeId: string;\n};\n\nexport type RuntimeFetchByDestinationPathOptions = {\n destinationId: string;\n path: string;\n};\n\n/** Structural contract implemented by the Client runtime. */\nexport interface ExperienceRuntime {\n readonly spaceId: string;\n readonly environmentId: string;\n readonly locale: string | undefined;\n readonly eventBuilder: EventBuilder;\n resolveExperience(\n payload: ExperiencePayload,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan>;\n fetchExperience(\n options: RuntimeFetchExperienceOptions,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan>;\n fetchByDestinationNode(\n options: RuntimeFetchByDestinationNodeOptions,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan | DestinationRedirectResult>;\n fetchByDestinationPath(\n options: RuntimeFetchByDestinationPathOptions,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan | DestinationRedirectResult>;\n}\n\nexport type ContentfulExperiencesConfig = {\n spaceId: string;\n environmentId: string;\n locale?: string;\n resolverConfig: ResolverConfig;\n delivery: RuntimeClientSource;\n preview?: RuntimeClientSource;\n resolveDefaults?: Pick<RuntimeResolveOptions, 'metadata' | 'debug'>;\n eventBuilder?: RuntimeEventBuilderConfig;\n};\n\nexport class ContentfulExperiences implements ExperienceRuntime {\n readonly spaceId: string;\n readonly environmentId: string;\n readonly locale: string | undefined;\n readonly eventBuilder: EventBuilder;\n\n readonly #resolverConfig: ResolverConfig;\n readonly #deliveryClient: ContentfulViewDeliveryClient;\n readonly #previewClient: ContentfulViewDeliveryClient | undefined;\n readonly #resolveDefaults: Pick<RuntimeResolveOptions, 'metadata' | 'debug'>;\n\n constructor(config: ContentfulExperiencesConfig) {\n this.spaceId = config.spaceId;\n this.environmentId = config.environmentId;\n this.locale = config.locale;\n this.#resolverConfig = config.resolverConfig;\n this.#deliveryClient = resolveClient(config.delivery);\n this.#previewClient =\n config.preview === undefined ? undefined : resolveClient(config.preview, PREVIEW_HOST);\n this.#resolveDefaults = config.resolveDefaults ?? {};\n\n const eventBuilder = config.eventBuilder ?? {};\n this.eventBuilder = new EventBuilder({\n ...eventBuilder,\n channel: eventBuilder.channel ?? 'server',\n library: { ...DEFAULT_EVENT_CONTEXT_LIBRARY, ...eventBuilder.library },\n getLocale: eventBuilder.getLocale ?? (() => this.locale),\n });\n }\n\n resolveExperience(\n payload: ExperiencePayload,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan> {\n return resolveCoreExperience(\n payload,\n this.#resolverConfig,\n this.#mergeResolveOptions(resolveOptions)\n );\n }\n\n fetchExperience(\n options: RuntimeFetchExperienceOptions,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan> {\n if (options.preview === true && this.#previewClient === undefined) {\n throw new Error(\n 'fetchExperience() called with preview: true but this runtime has no preview client'\n );\n }\n return fetchExperience(\n {\n spaceId: this.spaceId,\n environmentId: this.environmentId,\n experienceId: options.experienceId,\n locale: options.locale ?? this.locale,\n withSourceMap: options.withSourceMap,\n },\n {\n client: options.preview\n ? (this.#previewClient as ContentfulViewDeliveryClient)\n : this.#deliveryClient,\n },\n { config: this.#resolverConfig, ...this.#mergeResolveOptions(resolveOptions) }\n );\n }\n\n fetchByDestinationNode(\n options: RuntimeFetchByDestinationNodeOptions,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan | DestinationRedirectResult> {\n return fetchExperience(\n { spaceId: this.spaceId, destinationId: options.destinationId, nodeId: options.nodeId },\n { client: this.#deliveryClient },\n { config: this.#resolverConfig, ...this.#mergeResolveOptions(resolveOptions) }\n );\n }\n\n fetchByDestinationPath(\n options: RuntimeFetchByDestinationPathOptions,\n resolveOptions?: RuntimeResolveOptions\n ): Promise<PortableRenderPlan | DestinationRedirectResult> {\n return fetchExperience(\n { spaceId: this.spaceId, destinationId: options.destinationId, path: options.path },\n { client: this.#deliveryClient },\n { config: this.#resolverConfig, ...this.#mergeResolveOptions(resolveOptions) }\n );\n }\n\n #mergeResolveOptions(options: RuntimeResolveOptions | undefined): RuntimeResolveOptions {\n return {\n metadata: { ...this.#resolveDefaults.metadata, ...options?.metadata },\n debug: options?.debug ?? this.#resolveDefaults.debug,\n initialViewportId: options?.initialViewportId,\n };\n }\n}\n\nfunction resolveClient(\n source: RuntimeClientSource,\n defaultHost?: string\n): ContentfulViewDeliveryClient {\n return 'client' in source\n ? source.client\n : createClient({ ...source, host: source.host ?? defaultHost });\n}\n"],"mappings":"AACA,SAAS,qBAAqB,6BAA6B;AAM3D,OAAO,kBAAkB;AAEzB,SAAS,oBAAoB;AAC7B,SAAS,uBAAuD;AAEhE,SAAS,oBAAoB;AAC7B,SAAS,qCAAqC;AAmEvC,MAAM,sBAAmD;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,QAAqC;AAC/C,SAAK,UAAU,OAAO;AACtB,SAAK,gBAAgB,OAAO;AAC5B,SAAK,SAAS,OAAO;AACrB,SAAK,kBAAkB,OAAO;AAC9B,SAAK,kBAAkB,cAAc,OAAO,QAAQ;AACpD,SAAK,iBACH,OAAO,YAAY,SAAY,SAAY,cAAc,OAAO,SAAS,YAAY;AACvF,SAAK,mBAAmB,OAAO,mBAAmB,CAAC;AAEnD,UAAM,eAAe,OAAO,gBAAgB,CAAC;AAC7C,SAAK,eAAe,IAAI,aAAa;AAAA,MACnC,GAAG;AAAA,MACH,SAAS,aAAa,WAAW;AAAA,MACjC,SAAS,EAAE,GAAG,+BAA+B,GAAG,aAAa,QAAQ;AAAA,MACrE,WAAW,aAAa,cAAc,MAAM,KAAK;AAAA,IACnD,CAAC;AAAA,EACH;AAAA,EAEA,kBACE,SACA,gBAC6B;AAC7B,WAAO;AAAA,MACL;AAAA,MACA,KAAK;AAAA,MACL,KAAK,qBAAqB,cAAc;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,gBACE,SACA,gBAC6B;AAC7B,QAAI,QAAQ,YAAY,QAAQ,KAAK,mBAAmB,QAAW;AACjE,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,QACE,SAAS,KAAK;AAAA,QACd,eAAe,KAAK;AAAA,QACpB,cAAc,QAAQ;AAAA,QACtB,QAAQ,QAAQ,UAAU,KAAK;AAAA,QAC/B,eAAe,QAAQ;AAAA,MACzB;AAAA,MACA;AAAA,QACE,QAAQ,QAAQ,UACX,KAAK,iBACN,KAAK;AAAA,MACX;AAAA,MACA,EAAE,QAAQ,KAAK,iBAAiB,GAAG,KAAK,qBAAqB,cAAc,EAAE;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,uBACE,SACA,gBACyD;AACzD,WAAO;AAAA,MACL,EAAE,SAAS,KAAK,SAAS,eAAe,QAAQ,eAAe,QAAQ,QAAQ,OAAO;AAAA,MACtF,EAAE,QAAQ,KAAK,gBAAgB;AAAA,MAC/B,EAAE,QAAQ,KAAK,iBAAiB,GAAG,KAAK,qBAAqB,cAAc,EAAE;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,uBACE,SACA,gBACyD;AACzD,WAAO;AAAA,MACL,EAAE,SAAS,KAAK,SAAS,eAAe,QAAQ,eAAe,MAAM,QAAQ,KAAK;AAAA,MAClF,EAAE,QAAQ,KAAK,gBAAgB;AAAA,MAC/B,EAAE,QAAQ,KAAK,iBAAiB,GAAG,KAAK,qBAAqB,cAAc,EAAE;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,qBAAqB,SAAmE;AACtF,WAAO;AAAA,MACL,UAAU,EAAE,GAAG,KAAK,iBAAiB,UAAU,GAAG,SAAS,SAAS;AAAA,MACpE,OAAO,SAAS,SAAS,KAAK,iBAAiB;AAAA,MAC/C,mBAAmB,SAAS;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,SAAS,cACP,QACA,aAC8B;AAC9B,SAAO,YAAY,SACf,OAAO,SACP,aAAa,EAAE,GAAG,QAAQ,MAAM,OAAO,QAAQ,YAAY,CAAC;AAClE;","names":[]}
package/dist/errors.d.ts CHANGED
@@ -26,5 +26,31 @@ declare class ExperienceFetchError extends Error {
26
26
  cause?: unknown;
27
27
  });
28
28
  }
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.
35
+ *
36
+ * This is a bridge, not a permanent design: revisit once the platform ships
37
+ * preview support for destinations.
38
+ *
39
+ * Only fires for the inline-credentials branch of `ClientOptions`. A
40
+ * caller-supplied `{ client }` already ignores `preview` entirely, per that
41
+ * branch's existing contract.
42
+ */
43
+ declare class DestinationPreviewNotSupportedError extends Error {
44
+ readonly spaceId: string;
45
+ readonly destinationId: string;
46
+ readonly nodeId: string | undefined;
47
+ readonly path: string | undefined;
48
+ constructor(message: string, options: {
49
+ spaceId: string;
50
+ destinationId: string;
51
+ nodeId?: string;
52
+ path?: string;
53
+ });
54
+ }
29
55
 
30
- export { ExperienceFetchError };
56
+ export { DestinationPreviewNotSupportedError, ExperienceFetchError };
package/dist/errors.js CHANGED
@@ -10,7 +10,22 @@ class ExperienceFetchError extends Error {
10
10
  this.experienceId = options.experienceId;
11
11
  }
12
12
  }
13
+ class DestinationPreviewNotSupportedError extends Error {
14
+ spaceId;
15
+ destinationId;
16
+ nodeId;
17
+ path;
18
+ constructor(message, options) {
19
+ super(message);
20
+ this.name = "DestinationPreviewNotSupportedError";
21
+ this.spaceId = options.spaceId;
22
+ this.destinationId = options.destinationId;
23
+ this.nodeId = options.nodeId;
24
+ this.path = options.path;
25
+ }
26
+ }
13
27
  export {
28
+ DestinationPreviewNotSupportedError,
14
29
  ExperienceFetchError
15
30
  };
16
31
  //# sourceMappingURL=errors.js.map
@@ -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"],"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;","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` 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":[]}
@@ -0,0 +1,444 @@
1
+ import { App, Channel, Library, Page, UniversalEventProperties, ExoViewEvent, ExoClickEvent, ExoHoverEvent, ViewEvent, IdentifyEvent, PageViewEvent, TrackEvent } from '@contentful/optimization-api-client/api-schemas';
2
+ import * as z from 'zod/mini';
3
+
4
+ interface EventBuilderConfig {
5
+ app?: App;
6
+ channel: Channel;
7
+ library: Library;
8
+ getLocale?: () => string | undefined;
9
+ getPageProperties?: () => Page;
10
+ getUserAgent?: () => string | undefined;
11
+ getConsent?: () => boolean | undefined;
12
+ }
13
+ declare const UniversalEventBuilderArgs: z.ZodMiniObject<{
14
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
15
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
16
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
17
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
18
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
19
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
20
+ }, z.core.$strip>>;
21
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
22
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
23
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
24
+ latitude: z.ZodMiniNumber<number>;
25
+ longitude: z.ZodMiniNumber<number>;
26
+ }, z.core.$strip>>;
27
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
28
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
29
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
30
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
31
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
32
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
33
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
34
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
35
+ }, z.core.$strip>>;
36
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
37
+ path: z.ZodMiniString<string>;
38
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
39
+ referrer: z.ZodMiniString<string>;
40
+ search: z.ZodMiniString<string>;
41
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
42
+ url: z.ZodMiniString<string>;
43
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
44
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
45
+ name: z.ZodMiniString<string>;
46
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
47
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
48
+ }, z.core.$strip>;
49
+ type UniversalEventBuilderArgs = z.infer<typeof UniversalEventBuilderArgs>;
50
+ declare const InteractionBuilderArgsBase: z.ZodMiniObject<{
51
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
52
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
53
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
54
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
55
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
56
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
57
+ }, z.core.$strip>>;
58
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
59
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
60
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
61
+ latitude: z.ZodMiniNumber<number>;
62
+ longitude: z.ZodMiniNumber<number>;
63
+ }, z.core.$strip>>;
64
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
65
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
66
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
67
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
68
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
69
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
70
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
71
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
72
+ }, z.core.$strip>>;
73
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
74
+ path: z.ZodMiniString<string>;
75
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
76
+ referrer: z.ZodMiniString<string>;
77
+ search: z.ZodMiniString<string>;
78
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
79
+ url: z.ZodMiniString<string>;
80
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
81
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
82
+ name: z.ZodMiniString<string>;
83
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
84
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
85
+ entityId: z.ZodMiniString<string>;
86
+ entityKind: z.ZodMiniEnum<{
87
+ Experience: "Experience";
88
+ Fragment: "Fragment";
89
+ InlineFragment: "InlineFragment";
90
+ InlineComponent: "InlineComponent";
91
+ }>;
92
+ entityKindId: z.ZodMiniOptional<z.ZodMiniString<string>>;
93
+ entryIds: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
94
+ optimizationId: z.ZodMiniOptional<z.ZodMiniString<string>>;
95
+ parameters: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniJSONSchema>>;
96
+ parentExperienceId: z.ZodMiniOptional<z.ZodMiniString<string>>;
97
+ variantId: z.ZodMiniOptional<z.ZodMiniString<string>>;
98
+ variantIndex: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
99
+ }, z.core.$strip>;
100
+ type InteractionBuilderArgsBase = z.infer<typeof InteractionBuilderArgsBase>;
101
+ declare const ViewBuilderArgs: z.ZodMiniObject<{
102
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
103
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
104
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
105
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
106
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
107
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
108
+ }, z.core.$strip>>;
109
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
110
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
111
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
112
+ latitude: z.ZodMiniNumber<number>;
113
+ longitude: z.ZodMiniNumber<number>;
114
+ }, z.core.$strip>>;
115
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
116
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
117
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
118
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
119
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
120
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
121
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
122
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
123
+ }, z.core.$strip>>;
124
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
125
+ path: z.ZodMiniString<string>;
126
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
127
+ referrer: z.ZodMiniString<string>;
128
+ search: z.ZodMiniString<string>;
129
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
130
+ url: z.ZodMiniString<string>;
131
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
132
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
133
+ name: z.ZodMiniString<string>;
134
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
135
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
136
+ entityId: z.ZodMiniString<string>;
137
+ entityKind: z.ZodMiniEnum<{
138
+ Experience: "Experience";
139
+ Fragment: "Fragment";
140
+ InlineFragment: "InlineFragment";
141
+ InlineComponent: "InlineComponent";
142
+ }>;
143
+ entityKindId: z.ZodMiniOptional<z.ZodMiniString<string>>;
144
+ entryIds: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
145
+ optimizationId: z.ZodMiniOptional<z.ZodMiniString<string>>;
146
+ parameters: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniJSONSchema>>;
147
+ parentExperienceId: z.ZodMiniOptional<z.ZodMiniString<string>>;
148
+ variantId: z.ZodMiniOptional<z.ZodMiniString<string>>;
149
+ variantIndex: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
150
+ viewId: z.ZodMiniString<string>;
151
+ viewDurationMs: z.ZodMiniInt;
152
+ }, z.core.$strip>;
153
+ type ViewBuilderArgs = z.infer<typeof ViewBuilderArgs>;
154
+ declare const ClickBuilderArgs: z.ZodMiniObject<{
155
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
156
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
157
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
158
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
159
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
160
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
161
+ }, z.core.$strip>>;
162
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
163
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
164
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
165
+ latitude: z.ZodMiniNumber<number>;
166
+ longitude: z.ZodMiniNumber<number>;
167
+ }, z.core.$strip>>;
168
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
169
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
170
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
171
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
172
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
173
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
174
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
175
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
176
+ }, z.core.$strip>>;
177
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
178
+ path: z.ZodMiniString<string>;
179
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
180
+ referrer: z.ZodMiniString<string>;
181
+ search: z.ZodMiniString<string>;
182
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
183
+ url: z.ZodMiniString<string>;
184
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
185
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
186
+ name: z.ZodMiniString<string>;
187
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
188
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
189
+ entityId: z.ZodMiniString<string>;
190
+ entityKind: z.ZodMiniEnum<{
191
+ Experience: "Experience";
192
+ Fragment: "Fragment";
193
+ InlineFragment: "InlineFragment";
194
+ InlineComponent: "InlineComponent";
195
+ }>;
196
+ entityKindId: z.ZodMiniOptional<z.ZodMiniString<string>>;
197
+ entryIds: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
198
+ optimizationId: z.ZodMiniOptional<z.ZodMiniString<string>>;
199
+ parameters: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniJSONSchema>>;
200
+ parentExperienceId: z.ZodMiniOptional<z.ZodMiniString<string>>;
201
+ variantId: z.ZodMiniOptional<z.ZodMiniString<string>>;
202
+ variantIndex: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
203
+ }, z.core.$strip>;
204
+ type ClickBuilderArgs = z.infer<typeof ClickBuilderArgs>;
205
+ declare const HoverBuilderArgs: z.ZodMiniObject<{
206
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
207
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
208
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
209
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
210
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
211
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
212
+ }, z.core.$strip>>;
213
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
214
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
215
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
216
+ latitude: z.ZodMiniNumber<number>;
217
+ longitude: z.ZodMiniNumber<number>;
218
+ }, z.core.$strip>>;
219
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
220
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
221
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
222
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
223
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
224
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
225
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
226
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
227
+ }, z.core.$strip>>;
228
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
229
+ path: z.ZodMiniString<string>;
230
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
231
+ referrer: z.ZodMiniString<string>;
232
+ search: z.ZodMiniString<string>;
233
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
234
+ url: z.ZodMiniString<string>;
235
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
236
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
237
+ name: z.ZodMiniString<string>;
238
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
239
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
240
+ entityId: z.ZodMiniString<string>;
241
+ entityKind: z.ZodMiniEnum<{
242
+ Experience: "Experience";
243
+ Fragment: "Fragment";
244
+ InlineFragment: "InlineFragment";
245
+ InlineComponent: "InlineComponent";
246
+ }>;
247
+ entityKindId: z.ZodMiniOptional<z.ZodMiniString<string>>;
248
+ entryIds: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniString<string>>>;
249
+ optimizationId: z.ZodMiniOptional<z.ZodMiniString<string>>;
250
+ parameters: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniJSONSchema>>;
251
+ parentExperienceId: z.ZodMiniOptional<z.ZodMiniString<string>>;
252
+ variantId: z.ZodMiniOptional<z.ZodMiniString<string>>;
253
+ variantIndex: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
254
+ hoverId: z.ZodMiniString<string>;
255
+ hoverDurationMs: z.ZodMiniInt;
256
+ }, z.core.$strip>;
257
+ type HoverBuilderArgs = z.infer<typeof HoverBuilderArgs>;
258
+ declare const FlagViewBuilderArgs: z.ZodMiniObject<{
259
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
260
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
261
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
262
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
263
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
264
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
265
+ }, z.core.$strip>>;
266
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
267
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
268
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
269
+ latitude: z.ZodMiniNumber<number>;
270
+ longitude: z.ZodMiniNumber<number>;
271
+ }, z.core.$strip>>;
272
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
273
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
274
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
275
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
276
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
277
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
278
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
279
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
280
+ }, z.core.$strip>>;
281
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
282
+ path: z.ZodMiniString<string>;
283
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
284
+ referrer: z.ZodMiniString<string>;
285
+ search: z.ZodMiniString<string>;
286
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
287
+ url: z.ZodMiniString<string>;
288
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
289
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
290
+ name: z.ZodMiniString<string>;
291
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
292
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
293
+ componentId: z.ZodMiniString<string>;
294
+ experienceId: z.ZodMiniOptional<z.ZodMiniString<string>>;
295
+ variantIndex: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
296
+ viewId: z.ZodMiniOptional<z.ZodMiniString<string>>;
297
+ viewDurationMs: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
298
+ }, z.core.$strip>;
299
+ type FlagViewBuilderArgs = z.infer<typeof FlagViewBuilderArgs>;
300
+ declare const IdentifyBuilderArgs: z.ZodMiniObject<{
301
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
302
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
303
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
304
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
305
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
306
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
307
+ }, z.core.$strip>>;
308
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
309
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
310
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
311
+ latitude: z.ZodMiniNumber<number>;
312
+ longitude: z.ZodMiniNumber<number>;
313
+ }, z.core.$strip>>;
314
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
315
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
316
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
317
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
318
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
319
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
320
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
321
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
322
+ }, z.core.$strip>>;
323
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
324
+ path: z.ZodMiniString<string>;
325
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
326
+ referrer: z.ZodMiniString<string>;
327
+ search: z.ZodMiniString<string>;
328
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
329
+ url: z.ZodMiniString<string>;
330
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
331
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
332
+ name: z.ZodMiniString<string>;
333
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
334
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
335
+ traits: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniJSONSchema>>;
336
+ userId: z.ZodMiniString<string>;
337
+ }, z.core.$strip>;
338
+ type IdentifyBuilderArgs = z.infer<typeof IdentifyBuilderArgs>;
339
+ declare const PageViewBuilderArgs: z.ZodMiniObject<{
340
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
341
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
342
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
343
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
344
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
345
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
346
+ }, z.core.$strip>>;
347
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
348
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
349
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
350
+ latitude: z.ZodMiniNumber<number>;
351
+ longitude: z.ZodMiniNumber<number>;
352
+ }, z.core.$strip>>;
353
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
354
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
355
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
356
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
357
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
358
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
359
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
360
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
361
+ }, z.core.$strip>>;
362
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
363
+ path: z.ZodMiniString<string>;
364
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
365
+ referrer: z.ZodMiniString<string>;
366
+ search: z.ZodMiniString<string>;
367
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
368
+ url: z.ZodMiniString<string>;
369
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
370
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
371
+ name: z.ZodMiniString<string>;
372
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
373
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
374
+ properties: z.ZodMiniOptional<z.ZodMiniObject<{
375
+ path: z.ZodMiniOptional<z.ZodMiniString<string>>;
376
+ query: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
377
+ referrer: z.ZodMiniOptional<z.ZodMiniString<string>>;
378
+ search: z.ZodMiniOptional<z.ZodMiniString<string>>;
379
+ title: z.ZodMiniOptional<z.ZodMiniOptional<z.ZodMiniString<string>>>;
380
+ url: z.ZodMiniOptional<z.ZodMiniString<string>>;
381
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
382
+ }, z.core.$strip>;
383
+ type PageViewBuilderArgs = z.infer<typeof PageViewBuilderArgs>;
384
+ declare const TrackBuilderArgs: z.ZodMiniObject<{
385
+ campaign: z.ZodMiniOptional<z.ZodMiniObject<{
386
+ name: z.ZodMiniOptional<z.ZodMiniString<string>>;
387
+ source: z.ZodMiniOptional<z.ZodMiniString<string>>;
388
+ medium: z.ZodMiniOptional<z.ZodMiniString<string>>;
389
+ term: z.ZodMiniOptional<z.ZodMiniString<string>>;
390
+ content: z.ZodMiniOptional<z.ZodMiniString<string>>;
391
+ }, z.core.$strip>>;
392
+ locale: z.ZodMiniOptional<z.ZodMiniString<string>>;
393
+ location: z.ZodMiniOptional<z.ZodMiniObject<{
394
+ coordinates: z.ZodMiniOptional<z.ZodMiniObject<{
395
+ latitude: z.ZodMiniNumber<number>;
396
+ longitude: z.ZodMiniNumber<number>;
397
+ }, z.core.$strip>>;
398
+ city: z.ZodMiniOptional<z.ZodMiniString<string>>;
399
+ postalCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
400
+ region: z.ZodMiniOptional<z.ZodMiniString<string>>;
401
+ regionCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
402
+ country: z.ZodMiniOptional<z.ZodMiniString<string>>;
403
+ countryCode: z.ZodMiniOptional<z.ZodMiniString<string>>;
404
+ continent: z.ZodMiniOptional<z.ZodMiniString<string>>;
405
+ timezone: z.ZodMiniOptional<z.ZodMiniString<string>>;
406
+ }, z.core.$strip>>;
407
+ page: z.ZodMiniOptional<z.ZodMiniObject<{
408
+ path: z.ZodMiniString<string>;
409
+ query: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>;
410
+ referrer: z.ZodMiniString<string>;
411
+ search: z.ZodMiniString<string>;
412
+ title: z.ZodMiniOptional<z.ZodMiniString<string>>;
413
+ url: z.ZodMiniString<string>;
414
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
415
+ screen: z.ZodMiniOptional<z.ZodMiniObject<{
416
+ name: z.ZodMiniString<string>;
417
+ }, z.core.$catchall<z.ZodMiniJSONSchema>>>;
418
+ userAgent: z.ZodMiniOptional<z.ZodMiniString<string>>;
419
+ event: z.ZodMiniString<string>;
420
+ properties: z.ZodMiniOptional<z.ZodMiniPrefault<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniJSONSchema>>>;
421
+ }, z.core.$strip>;
422
+ type TrackBuilderArgs = z.infer<typeof TrackBuilderArgs>;
423
+ declare const DEFAULT_PAGE_PROPERTIES: Page;
424
+ declare class EventBuilder {
425
+ app?: App;
426
+ channel: Channel;
427
+ library: Library;
428
+ getLocale: () => string | undefined;
429
+ getPageProperties: () => Page;
430
+ getUserAgent: () => string | undefined;
431
+ getConsent: () => boolean | undefined;
432
+ constructor(config: EventBuilderConfig);
433
+ protected buildUniversalEventProperties({ campaign, locale, location, page, screen, userAgent, }: UniversalEventBuilderArgs): UniversalEventProperties;
434
+ private buildInteractionBase;
435
+ buildView(args: ViewBuilderArgs): ExoViewEvent;
436
+ buildClick(args: ClickBuilderArgs): ExoClickEvent;
437
+ buildHover(args: HoverBuilderArgs): ExoHoverEvent;
438
+ buildFlagView(args: FlagViewBuilderArgs): ViewEvent;
439
+ buildIdentify(args: IdentifyBuilderArgs): IdentifyEvent;
440
+ buildPageView(args?: PageViewBuilderArgs): PageViewEvent;
441
+ buildTrack(args: TrackBuilderArgs): TrackEvent;
442
+ }
443
+
444
+ export { ClickBuilderArgs, DEFAULT_PAGE_PROPERTIES, type EventBuilderConfig, FlagViewBuilderArgs, HoverBuilderArgs, IdentifyBuilderArgs, InteractionBuilderArgsBase, PageViewBuilderArgs, TrackBuilderArgs, UniversalEventBuilderArgs, ViewBuilderArgs, EventBuilder as default };
@@ -0,0 +1,274 @@
1
+ import {
2
+ Campaign,
3
+ ExoEntityKind,
4
+ GeoLocation,
5
+ Page,
6
+ PageEventContext,
7
+ parseWithFriendlyError,
8
+ Properties,
9
+ Screen,
10
+ Traits
11
+ } from "@contentful/optimization-api-client/api-schemas";
12
+ import { createScopedLogger } from "@contentful/optimization-api-client/logger";
13
+ import { merge } from "es-toolkit/object";
14
+ import * as z from "zod/mini";
15
+ const eventBuilderLogger = createScopedLogger("Experiences:EventBuilder");
16
+ const UTM_CAMPAIGN_PARAMETERS = [
17
+ ["utm_campaign", "name"],
18
+ ["utm_source", "source"],
19
+ ["utm_medium", "medium"],
20
+ ["utm_term", "term"],
21
+ ["utm_content", "content"]
22
+ ];
23
+ function extractCampaignFromUrl(url) {
24
+ if (url === void 0) return void 0;
25
+ try {
26
+ const { searchParams } = new URL(url);
27
+ const campaign = {};
28
+ let hasCampaignParameter = false;
29
+ for (const [parameter, property] of UTM_CAMPAIGN_PARAMETERS) {
30
+ if (!searchParams.has(parameter)) continue;
31
+ campaign[property] = searchParams.get(parameter) ?? "";
32
+ hasCampaignParameter = true;
33
+ }
34
+ return hasCampaignParameter ? campaign : void 0;
35
+ } catch {
36
+ return void 0;
37
+ }
38
+ }
39
+ const UniversalEventBuilderArgs = z.object({
40
+ campaign: z.optional(Campaign),
41
+ locale: z.optional(z.string()),
42
+ location: z.optional(GeoLocation),
43
+ page: z.optional(Page),
44
+ screen: z.optional(Screen),
45
+ userAgent: z.optional(z.string())
46
+ });
47
+ const InteractionBuilderArgsBase = z.extend(UniversalEventBuilderArgs, {
48
+ entityId: z.string(),
49
+ entityKind: ExoEntityKind,
50
+ entityKindId: z.optional(z.string()),
51
+ entryIds: z.optional(z.array(z.string())),
52
+ optimizationId: z.optional(z.string()),
53
+ parameters: z.optional(z.record(z.string(), z.json())),
54
+ parentExperienceId: z.optional(z.string()),
55
+ variantId: z.optional(z.string()),
56
+ variantIndex: z.optional(z.number())
57
+ });
58
+ const ViewBuilderArgs = z.extend(InteractionBuilderArgsBase, {
59
+ viewId: z.string(),
60
+ viewDurationMs: z.int().check(z.minimum(0))
61
+ });
62
+ const ClickBuilderArgs = InteractionBuilderArgsBase;
63
+ const HoverBuilderArgs = z.extend(InteractionBuilderArgsBase, {
64
+ hoverId: z.string(),
65
+ hoverDurationMs: z.int().check(z.minimum(0))
66
+ });
67
+ const FlagInteractionBuilderArgsBase = z.extend(UniversalEventBuilderArgs, {
68
+ componentId: z.string(),
69
+ experienceId: z.optional(z.string()),
70
+ variantIndex: z.optional(z.number())
71
+ });
72
+ const FlagViewBuilderArgs = z.extend(FlagInteractionBuilderArgsBase, {
73
+ viewId: z.optional(z.string()),
74
+ viewDurationMs: z.optional(z.number())
75
+ });
76
+ const IdentifyBuilderArgs = z.extend(UniversalEventBuilderArgs, {
77
+ traits: z.optional(Traits),
78
+ userId: z.string()
79
+ });
80
+ const PageViewBuilderArgs = z.extend(UniversalEventBuilderArgs, {
81
+ properties: z.optional(z.partial(Page))
82
+ });
83
+ const TrackBuilderArgs = z.extend(UniversalEventBuilderArgs, {
84
+ event: z.string(),
85
+ properties: z.optional(z.prefault(Properties, {}))
86
+ });
87
+ const PAGE_CONTEXT_CONFLICT_KEYS = ["path", "query", "search", "url"];
88
+ function warnIfPageContextConflicts(explicitPage, properties) {
89
+ if (!explicitPage) return;
90
+ const conflictingKeys = PAGE_CONTEXT_CONFLICT_KEYS.filter((key) => {
91
+ const { [key]: explicitValue } = explicitPage;
92
+ const { [key]: propertyValue } = properties;
93
+ return JSON.stringify(explicitValue) !== JSON.stringify(propertyValue);
94
+ });
95
+ if (conflictingKeys.length === 0) return;
96
+ eventBuilderLogger.warn(
97
+ `Explicit page context differs from page event properties for: ${conflictingKeys.join(", ")}`
98
+ );
99
+ }
100
+ const DEFAULT_PAGE_PROPERTIES = {
101
+ path: "",
102
+ query: {},
103
+ referrer: "",
104
+ search: "",
105
+ title: "",
106
+ url: ""
107
+ };
108
+ class EventBuilder {
109
+ app;
110
+ channel;
111
+ library;
112
+ getLocale;
113
+ getPageProperties;
114
+ getUserAgent;
115
+ getConsent;
116
+ constructor(config) {
117
+ const { app, channel, library, getLocale, getPageProperties, getUserAgent, getConsent } = config;
118
+ this.app = app;
119
+ this.channel = channel;
120
+ this.library = library;
121
+ this.getLocale = getLocale ?? (() => "en-US");
122
+ this.getPageProperties = getPageProperties ?? (() => DEFAULT_PAGE_PROPERTIES);
123
+ this.getUserAgent = getUserAgent ?? (() => void 0);
124
+ this.getConsent = getConsent ?? (() => void 0);
125
+ }
126
+ buildUniversalEventProperties({
127
+ campaign,
128
+ locale,
129
+ location,
130
+ page,
131
+ screen,
132
+ userAgent
133
+ }) {
134
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
135
+ const resolvedPage = page ?? this.getPageProperties();
136
+ return {
137
+ channel: this.channel,
138
+ context: {
139
+ app: this.app,
140
+ campaign: campaign ?? extractCampaignFromUrl(resolvedPage.url) ?? {},
141
+ gdpr: { isConsentGiven: this.getConsent() === true },
142
+ library: this.library,
143
+ locale: locale ?? this.getLocale() ?? "en-US",
144
+ location,
145
+ page: resolvedPage,
146
+ screen,
147
+ userAgent: userAgent ?? this.getUserAgent()
148
+ },
149
+ messageId: crypto.randomUUID(),
150
+ originalTimestamp: timestamp,
151
+ sentAt: timestamp,
152
+ timestamp
153
+ };
154
+ }
155
+ buildInteractionBase(args) {
156
+ const { campaign, locale, location, page, screen, userAgent, ...interactionProperties } = args;
157
+ return {
158
+ ...this.buildUniversalEventProperties({
159
+ campaign,
160
+ locale,
161
+ location,
162
+ page,
163
+ screen,
164
+ userAgent
165
+ }),
166
+ ...interactionProperties
167
+ };
168
+ }
169
+ buildView(args) {
170
+ const { viewId, viewDurationMs, ...interaction } = parseWithFriendlyError(
171
+ ViewBuilderArgs,
172
+ args
173
+ );
174
+ return {
175
+ ...this.buildInteractionBase(interaction),
176
+ type: "exo_node_view",
177
+ viewId,
178
+ viewDurationMs
179
+ };
180
+ }
181
+ buildClick(args) {
182
+ const interaction = parseWithFriendlyError(ClickBuilderArgs, args);
183
+ return {
184
+ ...this.buildInteractionBase(interaction),
185
+ type: "exo_node_click"
186
+ };
187
+ }
188
+ buildHover(args) {
189
+ const { hoverId, hoverDurationMs, ...interaction } = parseWithFriendlyError(
190
+ HoverBuilderArgs,
191
+ args
192
+ );
193
+ return {
194
+ ...this.buildInteractionBase(interaction),
195
+ type: "exo_node_hover",
196
+ hoverId,
197
+ hoverDurationMs
198
+ };
199
+ }
200
+ buildFlagView(args) {
201
+ const { componentId, experienceId, variantIndex, viewId, viewDurationMs, ...universal } = parseWithFriendlyError(FlagViewBuilderArgs, args);
202
+ return {
203
+ ...this.buildUniversalEventProperties(universal),
204
+ ...viewDurationMs === void 0 ? {} : { viewDurationMs },
205
+ ...viewId === void 0 ? {} : { viewId },
206
+ type: "component",
207
+ componentType: "Variable",
208
+ componentId,
209
+ experienceId,
210
+ variantIndex: variantIndex ?? 0
211
+ };
212
+ }
213
+ buildIdentify(args) {
214
+ const { traits = {}, userId, ...universal } = parseWithFriendlyError(IdentifyBuilderArgs, args);
215
+ return {
216
+ ...this.buildUniversalEventProperties(universal),
217
+ type: "identify",
218
+ traits,
219
+ userId
220
+ };
221
+ }
222
+ buildPageView(args = {}) {
223
+ const { properties = {}, ...universal } = parseWithFriendlyError(PageViewBuilderArgs, args);
224
+ const propertiesCampaign = extractCampaignFromUrl(properties.url);
225
+ const pageProperties = this.getPageProperties();
226
+ const merged = merge(
227
+ {
228
+ ...pageProperties,
229
+ title: pageProperties.title ?? DEFAULT_PAGE_PROPERTIES.title
230
+ },
231
+ properties
232
+ );
233
+ warnIfPageContextConflicts(universal.page, merged);
234
+ const {
235
+ context: { screen: _, ...universalContext },
236
+ ...universalProperties
237
+ } = this.buildUniversalEventProperties({
238
+ ...universal,
239
+ campaign: universal.campaign ?? propertiesCampaign,
240
+ page: universal.page ?? merged
241
+ });
242
+ const context = parseWithFriendlyError(PageEventContext, universalContext);
243
+ return {
244
+ ...universalProperties,
245
+ context,
246
+ type: "page",
247
+ properties: merged
248
+ };
249
+ }
250
+ buildTrack(args) {
251
+ const { event, properties = {}, ...universal } = parseWithFriendlyError(TrackBuilderArgs, args);
252
+ return {
253
+ ...this.buildUniversalEventProperties(universal),
254
+ type: "track",
255
+ event,
256
+ properties
257
+ };
258
+ }
259
+ }
260
+ var event_builder_default = EventBuilder;
261
+ export {
262
+ ClickBuilderArgs,
263
+ DEFAULT_PAGE_PROPERTIES,
264
+ FlagViewBuilderArgs,
265
+ HoverBuilderArgs,
266
+ IdentifyBuilderArgs,
267
+ InteractionBuilderArgsBase,
268
+ PageViewBuilderArgs,
269
+ TrackBuilderArgs,
270
+ UniversalEventBuilderArgs,
271
+ ViewBuilderArgs,
272
+ event_builder_default as default
273
+ };
274
+ //# sourceMappingURL=event-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/event-builder.ts"],"sourcesContent":["/* eslint-disable no-redeclare */\n\nimport {\n type App,\n Campaign,\n type Channel,\n type ExoClickEvent,\n ExoEntityKind,\n type ExoHoverEvent,\n type ExoViewEvent,\n GeoLocation,\n type IdentifyEvent,\n type Library,\n Page,\n PageEventContext,\n type PageViewEvent,\n parseWithFriendlyError,\n Properties,\n Screen,\n type TrackEvent,\n Traits,\n UniversalEventProperties,\n type ViewEvent,\n} from '@contentful/optimization-api-client/api-schemas';\nimport { createScopedLogger } from '@contentful/optimization-api-client/logger';\nimport { merge } from 'es-toolkit/object';\nimport * as z from 'zod/mini';\n\nconst eventBuilderLogger = createScopedLogger('Experiences:EventBuilder');\n\nconst UTM_CAMPAIGN_PARAMETERS = [\n ['utm_campaign', 'name'],\n ['utm_source', 'source'],\n ['utm_medium', 'medium'],\n ['utm_term', 'term'],\n ['utm_content', 'content'],\n] as const;\n\nfunction extractCampaignFromUrl(url: string | undefined): Campaign | undefined {\n if (url === undefined) return undefined;\n\n try {\n const { searchParams } = new URL(url);\n const campaign: Campaign = {};\n let hasCampaignParameter = false;\n\n for (const [parameter, property] of UTM_CAMPAIGN_PARAMETERS) {\n if (!searchParams.has(parameter)) continue;\n\n campaign[property] = searchParams.get(parameter) ?? '';\n hasCampaignParameter = true;\n }\n\n return hasCampaignParameter ? campaign : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface EventBuilderConfig {\n app?: App;\n channel: Channel;\n library: Library;\n getLocale?: () => string | undefined;\n getPageProperties?: () => Page;\n getUserAgent?: () => string | undefined;\n getConsent?: () => boolean | undefined;\n}\n\nexport const UniversalEventBuilderArgs = z.object({\n campaign: z.optional(Campaign),\n locale: z.optional(z.string()),\n location: z.optional(GeoLocation),\n page: z.optional(Page),\n screen: z.optional(Screen),\n userAgent: z.optional(z.string()),\n});\n\nexport type UniversalEventBuilderArgs = z.infer<typeof UniversalEventBuilderArgs>;\n\nexport const InteractionBuilderArgsBase = z.extend(UniversalEventBuilderArgs, {\n entityId: z.string(),\n entityKind: ExoEntityKind,\n entityKindId: z.optional(z.string()),\n entryIds: z.optional(z.array(z.string())),\n optimizationId: z.optional(z.string()),\n parameters: z.optional(z.record(z.string(), z.json())),\n parentExperienceId: z.optional(z.string()),\n variantId: z.optional(z.string()),\n variantIndex: z.optional(z.number()),\n});\n\nexport type InteractionBuilderArgsBase = z.infer<typeof InteractionBuilderArgsBase>;\n\nexport const ViewBuilderArgs = z.extend(InteractionBuilderArgsBase, {\n viewId: z.string(),\n viewDurationMs: z.int().check(z.minimum(0)),\n});\n\nexport type ViewBuilderArgs = z.infer<typeof ViewBuilderArgs>;\n\nexport const ClickBuilderArgs = InteractionBuilderArgsBase;\n\nexport type ClickBuilderArgs = z.infer<typeof ClickBuilderArgs>;\n\nexport const HoverBuilderArgs = z.extend(InteractionBuilderArgsBase, {\n hoverId: z.string(),\n hoverDurationMs: z.int().check(z.minimum(0)),\n});\n\nexport type HoverBuilderArgs = z.infer<typeof HoverBuilderArgs>;\n\nconst FlagInteractionBuilderArgsBase = z.extend(UniversalEventBuilderArgs, {\n componentId: z.string(),\n experienceId: z.optional(z.string()),\n variantIndex: z.optional(z.number()),\n});\n\nexport const FlagViewBuilderArgs = z.extend(FlagInteractionBuilderArgsBase, {\n viewId: z.optional(z.string()),\n viewDurationMs: z.optional(z.number()),\n});\n\nexport type FlagViewBuilderArgs = z.infer<typeof FlagViewBuilderArgs>;\n\nexport const IdentifyBuilderArgs = z.extend(UniversalEventBuilderArgs, {\n traits: z.optional(Traits),\n userId: z.string(),\n});\n\nexport type IdentifyBuilderArgs = z.infer<typeof IdentifyBuilderArgs>;\n\nexport const PageViewBuilderArgs = z.extend(UniversalEventBuilderArgs, {\n properties: z.optional(z.partial(Page)),\n});\n\nexport type PageViewBuilderArgs = z.infer<typeof PageViewBuilderArgs>;\n\nexport const TrackBuilderArgs = z.extend(UniversalEventBuilderArgs, {\n event: z.string(),\n properties: z.optional(z.prefault(Properties, {})),\n});\n\nexport type TrackBuilderArgs = z.infer<typeof TrackBuilderArgs>;\n\nconst PAGE_CONTEXT_CONFLICT_KEYS = ['path', 'query', 'search', 'url'] as const;\n\nfunction warnIfPageContextConflicts(explicitPage: Page | undefined, properties: Page): void {\n if (!explicitPage) return;\n\n const conflictingKeys = PAGE_CONTEXT_CONFLICT_KEYS.filter((key) => {\n const { [key]: explicitValue } = explicitPage;\n const { [key]: propertyValue } = properties;\n return JSON.stringify(explicitValue) !== JSON.stringify(propertyValue);\n });\n\n if (conflictingKeys.length === 0) return;\n\n eventBuilderLogger.warn(\n `Explicit page context differs from page event properties for: ${conflictingKeys.join(', ')}`\n );\n}\n\nexport const DEFAULT_PAGE_PROPERTIES: Page = {\n path: '',\n query: {},\n referrer: '',\n search: '',\n title: '',\n url: '',\n};\n\nclass EventBuilder {\n app?: App;\n channel: Channel;\n library: Library;\n getLocale: () => string | undefined;\n getPageProperties: () => Page;\n getUserAgent: () => string | undefined;\n getConsent: () => boolean | undefined;\n\n constructor(config: EventBuilderConfig) {\n const { app, channel, library, getLocale, getPageProperties, getUserAgent, getConsent } =\n config;\n this.app = app;\n this.channel = channel;\n this.library = library;\n this.getLocale = getLocale ?? (() => 'en-US');\n this.getPageProperties = getPageProperties ?? (() => DEFAULT_PAGE_PROPERTIES);\n this.getUserAgent = getUserAgent ?? (() => undefined);\n this.getConsent = getConsent ?? (() => undefined);\n }\n\n protected buildUniversalEventProperties({\n campaign,\n locale,\n location,\n page,\n screen,\n userAgent,\n }: UniversalEventBuilderArgs): UniversalEventProperties {\n const timestamp = new Date().toISOString();\n const resolvedPage = page ?? this.getPageProperties();\n\n return {\n channel: this.channel,\n context: {\n app: this.app,\n campaign: campaign ?? extractCampaignFromUrl(resolvedPage.url) ?? {},\n gdpr: { isConsentGiven: this.getConsent() === true },\n library: this.library,\n locale: locale ?? this.getLocale() ?? 'en-US',\n location,\n page: resolvedPage,\n screen,\n userAgent: userAgent ?? this.getUserAgent(),\n },\n messageId: crypto.randomUUID(),\n originalTimestamp: timestamp,\n sentAt: timestamp,\n timestamp,\n };\n }\n\n private buildInteractionBase(\n args: InteractionBuilderArgsBase\n ): UniversalEventProperties & InteractionBuilderArgsBase {\n const { campaign, locale, location, page, screen, userAgent, ...interactionProperties } = args;\n\n return {\n ...this.buildUniversalEventProperties({\n campaign,\n locale,\n location,\n page,\n screen,\n userAgent,\n }),\n ...interactionProperties,\n };\n }\n\n buildView(args: ViewBuilderArgs): ExoViewEvent {\n const { viewId, viewDurationMs, ...interaction } = parseWithFriendlyError(\n ViewBuilderArgs,\n args\n );\n\n return {\n ...this.buildInteractionBase(interaction),\n type: 'exo_node_view',\n viewId,\n viewDurationMs,\n };\n }\n\n buildClick(args: ClickBuilderArgs): ExoClickEvent {\n const interaction = parseWithFriendlyError(ClickBuilderArgs, args);\n\n return {\n ...this.buildInteractionBase(interaction),\n type: 'exo_node_click',\n };\n }\n\n buildHover(args: HoverBuilderArgs): ExoHoverEvent {\n const { hoverId, hoverDurationMs, ...interaction } = parseWithFriendlyError(\n HoverBuilderArgs,\n args\n );\n\n return {\n ...this.buildInteractionBase(interaction),\n type: 'exo_node_hover',\n hoverId,\n hoverDurationMs,\n };\n }\n\n buildFlagView(args: FlagViewBuilderArgs): ViewEvent {\n const { componentId, experienceId, variantIndex, viewId, viewDurationMs, ...universal } =\n parseWithFriendlyError(FlagViewBuilderArgs, args);\n\n return {\n ...this.buildUniversalEventProperties(universal),\n ...(viewDurationMs === undefined ? {} : { viewDurationMs }),\n ...(viewId === undefined ? {} : { viewId }),\n type: 'component',\n componentType: 'Variable',\n componentId,\n experienceId,\n variantIndex: variantIndex ?? 0,\n };\n }\n\n buildIdentify(args: IdentifyBuilderArgs): IdentifyEvent {\n const { traits = {}, userId, ...universal } = parseWithFriendlyError(IdentifyBuilderArgs, args);\n\n return {\n ...this.buildUniversalEventProperties(universal),\n type: 'identify',\n traits,\n userId,\n };\n }\n\n buildPageView(args: PageViewBuilderArgs = {}): PageViewEvent {\n const { properties = {}, ...universal } = parseWithFriendlyError(PageViewBuilderArgs, args);\n const propertiesCampaign = extractCampaignFromUrl(properties.url);\n const pageProperties = this.getPageProperties();\n const merged = merge(\n {\n ...pageProperties,\n title: pageProperties.title ?? DEFAULT_PAGE_PROPERTIES.title,\n },\n properties\n );\n\n warnIfPageContextConflicts(universal.page, merged);\n\n const {\n context: { screen: _, ...universalContext },\n ...universalProperties\n } = this.buildUniversalEventProperties({\n ...universal,\n campaign: universal.campaign ?? propertiesCampaign,\n page: universal.page ?? merged,\n });\n\n const context = parseWithFriendlyError(PageEventContext, universalContext);\n\n return {\n ...universalProperties,\n context,\n type: 'page',\n properties: merged,\n };\n }\n\n buildTrack(args: TrackBuilderArgs): TrackEvent {\n const { event, properties = {}, ...universal } = parseWithFriendlyError(TrackBuilderArgs, args);\n\n return {\n ...this.buildUniversalEventProperties(universal),\n type: 'track',\n event,\n properties,\n };\n }\n}\n\nexport default EventBuilder;\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import { ContentfulViewDelivery } from "@contentful/experience-delivery";
2
2
  import { createDebugLogger, resolveExperience } from "@contentful/experiences-sdk-core";
3
3
  import { createClient } from "./create-client.js";
4
- import { ExperienceFetchError } from "./errors.js";
4
+ import { DestinationPreviewNotSupportedError, ExperienceFetchError } from "./errors.js";
5
5
  import { PREVIEW_HOST } from "./hosts.js";
6
6
  import {
7
7
  readSourceMap,
@@ -11,6 +11,14 @@ import {
11
11
  async function fetchExperience(experienceOptions, clientOptions, resolveOptions) {
12
12
  const { config, metadata, debug, initialViewportId } = resolveOptions;
13
13
  const log = createDebugLogger(debug, "client");
14
+ if (!("client" in clientOptions) && clientOptions.preview && "destinationId" in experienceOptions) {
15
+ const { spaceId: spaceId2, destinationId } = experienceOptions;
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
+ }
14
22
  let client;
15
23
  if ("client" in clientOptions) {
16
24
  client = clientOptions.client;
@@ -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-client.js';\nimport { 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\nexport type ByIdExperienceOptions = {\n spaceId: string;\n environmentId: string;\n experienceId: string;\n locale?: string;\n /**\n * Fetch the content source map alongside the experience, onto\n * `PortableRenderPlan.sourceMap`. Defaults to `false` because the map is large.\n *\n * Switches the request from `GET` to `POST` (the opt-in is a request-body\n * field, only accepted by `getWithOverrides`), so it is not CDN-cacheable.\n * Query params, auth, and the response shape are unchanged.\n */\n withSourceMap?: boolean;\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 * Per-request fallback viewport for server-side design pre-resolution. Pass\n * the same id you seed the renderer's `initialViewportId` with (e.g. a\n * User-Agent-derived viewport) so SSR paints correct design on first render.\n */\n initialViewportId?: string;\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, initialViewportId } = resolveOptions;\n const log = createDebugLogger(debug, 'client');\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, initialViewportId }\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, initialViewportId }\n );\n }\n\n const { spaceId, environmentId, experienceId, locale, withSourceMap } = experienceOptions;\n\n log.log('fetching experience', {\n spaceId,\n environmentId,\n experienceId,\n locale,\n withSourceMap: Boolean(withSourceMap),\n });\n\n // Typed as the union of both operations' responses — they declare the same\n // union, but which one runs depends on `withSourceMap` below.\n let response: ExperienceResponse;\n try {\n // Both methods hit the same endpoint; only the POST accepts a body, and\n // `extensions` (the source-map opt-in) lives there. Otherwise identical.\n // The alpha-feature header is sent by the delivery client itself since\n // 1.0.0-dev.7, so a caller-supplied `{ client }` is covered too.\n response = withSourceMap\n ? await client.experience.getWithOverrides(spaceId, environmentId, experienceId, {\n locale,\n extensions: { sourceMap: {} },\n })\n : await client.experience.get(spaceId, environmentId, experienceId, { locale });\n } catch (err) {\n // `NotFoundError` is a distinguishable, expected outcome (draft/unpublished/\n // wrong id) — callers already route it to their framework's 404 idiom, so\n // it passes through as-is. Everything else (network failure, bad/expired\n // token, a 5xx) is unexpected and gets wrapped in an actionable error\n // instead of leaking whatever shape the delivery client happened to throw.\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 const payload = toExperiencePayload(response);\n const sourceMap = withSourceMap ? readSourceMap(response) : undefined;\n\n log.lazy('received raw payload', () => payload);\n if (withSourceMap && !sourceMap) log.log('source map requested but not returned');\n\n return resolveExperience(payload, config, {\n metadata,\n debug,\n initialViewportId,\n sourceMap,\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 initialViewportId: string | undefined;\n }\n): Promise<PortableRenderPlan | DestinationRedirectResult> {\n const { spaceId, destinationId, locator } = identity;\n const { config, metadata, debug, initialViewportId } = 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 initialViewportId,\n });\n}\n"],"mappings":"AACA,SAAS,8BAA8B;AACvC,SAAS,mBAAmB,yBAAyB;AAErD,SAAS,oBAAoB;AAC7B,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AA0HP,eAAsB,gBACpB,mBACA,eACA,gBACyD;AACzD,QAAM,EAAE,QAAQ,UAAU,OAAO,kBAAkB,IAAI;AACvD,QAAM,MAAM,kBAAkB,OAAO,QAAQ;AAE7C,MAAI;AACJ,MAAI,YAAY,eAAe;AAC7B,aAAS,cAAc;AACvB,QAAI,IAAI,uCAAuC;AAAA,EACjD,OAAO;AACL,UAAM,EAAE,aAAa,cAAc,SAAS,KAAK,IAAI;AACrD,QAAI,WAAW,CAAC,cAAc;AAC5B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,eAAe,SAAS,UAAU,eAAe;AACvD,aAAS,aAAa;AAAA,MACpB,aAAa,UAAW,eAA0B;AAAA,MAClD,MAAM;AAAA,IACR,CAAC;AACD,QAAI,IAAI,2BAA2B,EAAE,SAAS,QAAQ,OAAO,GAAG,MAAM,aAAa,CAAC;AAAA,EACtF;AAEA,MAAI,YAAY,mBAAmB;AACjC,UAAM,EAAE,SAAAA,UAAS,eAAe,OAAO,IAAI;AAC3C,WAAO;AAAA,MACL,EAAE,SAAAA,UAAS,eAAe,SAAS,OAAO;AAAA,MAC1C,MAAM,OAAO,YAAY,gBAAgBA,UAAS,eAAe,MAAM;AAAA,MACvE;AAAA,MACA,EAAE,QAAQ,UAAU,OAAO,kBAAkB;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,UAAU,mBAAmB;AAC/B,UAAM,EAAE,SAAAA,UAAS,eAAe,KAAK,IAAI;AACzC,WAAO;AAAA,MACL,EAAE,SAAAA,UAAS,eAAe,SAAS,KAAK;AAAA,MACxC,MAAM,OAAO,YAAY,cAAcA,UAAS,eAAe,EAAE,KAAK,CAAC;AAAA,MACvE;AAAA,MACA,EAAE,QAAQ,UAAU,OAAO,kBAAkB;AAAA,IAC/C;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,eAAe,cAAc,QAAQ,cAAc,IAAI;AAExE,MAAI,IAAI,uBAAuB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,QAAQ,aAAa;AAAA,EACtC,CAAC;AAID,MAAI;AACJ,MAAI;AAKF,eAAW,gBACP,MAAM,OAAO,WAAW,iBAAiB,SAAS,eAAe,cAAc;AAAA,MAC7E;AAAA,MACA,YAAY,EAAE,WAAW,CAAC,EAAE;AAAA,IAC9B,CAAC,IACD,MAAM,OAAO,WAAW,IAAI,SAAS,eAAe,cAAc,EAAE,OAAO,CAAC;AAAA,EAClF,SAAS,KAAK;AAMZ,QAAI,eAAe,uBAAuB,eAAe;AACvD,YAAM;AAAA,IACR;AACA,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI;AAAA,MACR,+BAA+B,YAAY,aAAa,OAAO,mBACzD,aAAa,OAAO,MAAM;AAAA,MAEhC,EAAE,SAAS,eAAe,cAAc,OAAO,IAAI;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,UAAU,oBAAoB,QAAQ;AAC5C,QAAM,YAAY,gBAAgB,cAAc,QAAQ,IAAI;AAE5D,MAAI,KAAK,wBAAwB,MAAM,OAAO;AAC9C,MAAI,iBAAiB,CAAC,UAAW,KAAI,IAAI,uCAAuC;AAEhF,SAAO,kBAAkB,SAAS,QAAQ;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAMA,eAAe,mBACb,UACA,SACA,KACA,gBAMyD;AACzD,QAAM,EAAE,SAAS,eAAe,QAAQ,IAAI;AAC5C,QAAM,EAAE,QAAQ,UAAU,OAAO,kBAAkB,IAAI;AAEvD,MAAI,IAAI,oCAAoC,EAAE,SAAS,eAAe,QAAQ,CAAC;AAE/E,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,QAAQ;AAAA,EAC3B,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,kCAAkC,aAAa,SAAS,OAAO,aAAa,OAAO,OAC9E,MAAM;AAAA,MAEX,EAAE,SAAS,cAAc,SAAS,OAAO,IAAI;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,cAAc,UAAU;AAC1B,QAAI,IAAI,sCAAsC,SAAS,QAAQ;AAC/D,WAAO,EAAE,UAAU,SAAS,SAAS;AAAA,EACvC;AAEA,QAAM,CAAC,eAAe,IAAI,SAAS;AACnC,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAI;AAAA,MACR,gBAAgB,aAAa,eAAe,OAAO,iCACtC,OAAO;AAAA,MACpB,EAAE,SAAS,cAAc,QAAQ;AAAA,IACnC;AAAA,EACF;AAEA,QAAM,UAAU,mCAAmC,gBAAgB,UAAU;AAC7E,MAAI,KAAK,wBAAwB,MAAM,OAAO;AAE9C,SAAO,kBAAkB,SAAS,QAAQ;AAAA,IACxC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;","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 { createClient } from './create-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\nexport type ByIdExperienceOptions = {\n spaceId: string;\n environmentId: string;\n experienceId: string;\n locale?: string;\n /**\n * Fetch the content source map alongside the experience, onto\n * `PortableRenderPlan.sourceMap`. Defaults to `false` because the map is large.\n *\n * Switches the request from `GET` to `POST` (the opt-in is a request-body\n * field, only accepted by `getWithOverrides`), so it is not CDN-cacheable.\n * Query params, auth, and the response shape are unchanged.\n */\n withSourceMap?: boolean;\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 * Per-request fallback viewport for server-side design pre-resolution. Pass\n * the same id you seed the renderer's `initialViewportId` with (e.g. a\n * User-Agent-derived viewport) so SSR paints correct design on first render.\n */\n initialViewportId?: string;\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, initialViewportId } = 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, initialViewportId }\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, initialViewportId }\n );\n }\n\n const { spaceId, environmentId, experienceId, locale, withSourceMap } = experienceOptions;\n\n log.log('fetching experience', {\n spaceId,\n environmentId,\n experienceId,\n locale,\n withSourceMap: Boolean(withSourceMap),\n });\n\n // Typed as the union of both operations' responses — they declare the same\n // union, but which one runs depends on `withSourceMap` below.\n let response: ExperienceResponse;\n try {\n // Both methods hit the same endpoint; only the POST accepts a body, and\n // `extensions` (the source-map opt-in) lives there. Otherwise identical.\n // The alpha-feature header is sent by the delivery client itself since\n // 1.0.0-dev.7, so a caller-supplied `{ client }` is covered too.\n response = withSourceMap\n ? await client.experience.getWithOverrides(spaceId, environmentId, experienceId, {\n locale,\n extensions: { sourceMap: {} },\n })\n : await client.experience.get(spaceId, environmentId, experienceId, { locale });\n } catch (err) {\n // `NotFoundError` is a distinguishable, expected outcome (draft/unpublished/\n // wrong id) — callers already route it to their framework's 404 idiom, so\n // it passes through as-is. Everything else (network failure, bad/expired\n // token, a 5xx) is unexpected and gets wrapped in an actionable error\n // instead of leaking whatever shape the delivery client happened to throw.\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 const payload = toExperiencePayload(response);\n const sourceMap = withSourceMap ? readSourceMap(response) : undefined;\n\n log.lazy('received raw payload', () => payload);\n if (withSourceMap && !sourceMap) log.log('source map requested but not returned');\n\n return resolveExperience(payload, config, {\n metadata,\n debug,\n initialViewportId,\n sourceMap,\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 initialViewportId: string | undefined;\n }\n): Promise<PortableRenderPlan | DestinationRedirectResult> {\n const { spaceId, destinationId, locator } = identity;\n const { config, metadata, debug, initialViewportId } = 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 initialViewportId,\n });\n}\n"],"mappings":"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","names":["spaceId"]}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import { ContentfulViewDelivery } from '@contentful/experience-delivery';
2
2
  export { ContentfulViewDelivery, ContentfulViewDeliveryClient } from '@contentful/experience-delivery';
3
- export { ExperienceFetchError } from './errors.js';
3
+ export { DestinationPreviewNotSupportedError, ExperienceFetchError } from './errors.js';
4
4
  export { CreateClientOptions, createClient } from './create-client.js';
5
+ export { ClickBuilderArgs, DEFAULT_PAGE_PROPERTIES, default as EventBuilder, EventBuilderConfig, FlagViewBuilderArgs, HoverBuilderArgs, IdentifyBuilderArgs, InteractionBuilderArgsBase, PageViewBuilderArgs, TrackBuilderArgs, UniversalEventBuilderArgs, ViewBuilderArgs } from './event-builder.js';
5
6
  export { ByDestinationNodeIdExperienceOptions, ByDestinationPathExperienceOptions, ByIdExperienceOptions, ClientOptions, DestinationRedirectResult, ExperienceOptions, ResolveOptions, fetchExperience } from './fetch-experience.js';
7
+ export { ContentfulExperiences, ContentfulExperiencesConfig, ExperienceRuntime, RuntimeClientSource, RuntimeEventBuilderConfig, RuntimeFetchByDestinationNodeOptions, RuntimeFetchByDestinationPathOptions, RuntimeFetchExperienceOptions, RuntimeResolveOptions } from './contentful-experiences.js';
6
8
  export { ExperienceResponse, readSourceMap, toExperiencePayload, toExperiencePayloadFromDestination } from './to-experience-payload.js';
7
9
  export { DELIVERY_HOST, PREVIEW_HOST, PREVIEW_WEBSOCKET_HOST } from './hosts.js';
10
+ import '@contentful/optimization-api-client/api-schemas';
11
+ import 'zod/mini';
8
12
  import '@contentful/experiences-sdk-core';
9
13
 
10
14
  declare const NotFoundError: typeof ContentfulViewDelivery.NotFoundError;
package/dist/index.js CHANGED
@@ -4,9 +4,12 @@ import {
4
4
  ContentfulViewDeliveryClient
5
5
  } from "@contentful/experience-delivery";
6
6
  const NotFoundError = ContentfulViewDelivery.NotFoundError;
7
- import { ExperienceFetchError } from "./errors.js";
7
+ import { ExperienceFetchError, DestinationPreviewNotSupportedError } from "./errors.js";
8
8
  import { createClient } from "./create-client.js";
9
+ import { default as default2 } from "./event-builder.js";
10
+ export * from "./event-builder.js";
9
11
  import { fetchExperience } from "./fetch-experience.js";
12
+ import { ContentfulExperiences } from "./contentful-experiences.js";
10
13
  import {
11
14
  readSourceMap,
12
15
  toExperiencePayload,
@@ -14,9 +17,12 @@ import {
14
17
  } from "./to-experience-payload.js";
15
18
  import { DELIVERY_HOST, PREVIEW_HOST, PREVIEW_WEBSOCKET_HOST } from "./hosts.js";
16
19
  export {
20
+ ContentfulExperiences,
17
21
  ContentfulViewDelivery2 as ContentfulViewDelivery,
18
22
  ContentfulViewDeliveryClient,
19
23
  DELIVERY_HOST,
24
+ DestinationPreviewNotSupportedError,
25
+ default2 as EventBuilder,
20
26
  ExperienceFetchError,
21
27
  NotFoundError,
22
28
  PREVIEW_HOST,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { ContentfulViewDelivery } from '@contentful/experience-delivery';\n\nexport {\n ContentfulViewDelivery,\n ContentfulViewDeliveryClient,\n} from '@contentful/experience-delivery';\nexport const NotFoundError = ContentfulViewDelivery.NotFoundError;\n// eslint-disable-next-line no-redeclare -- value + type share a name across separate TS namespaces, not a real redeclaration\nexport type NotFoundError = InstanceType<typeof ContentfulViewDelivery.NotFoundError>;\nexport { ExperienceFetchError } from './errors.js';\nexport { createClient } from './create-client.js';\nexport type { CreateClientOptions } from './create-client.js';\nexport { fetchExperience } from './fetch-experience.js';\nexport {\n readSourceMap,\n toExperiencePayload,\n toExperiencePayloadFromDestination,\n} from './to-experience-payload.js';\nexport type { ExperienceResponse } from './to-experience-payload.js';\nexport type {\n ExperienceOptions,\n ByIdExperienceOptions,\n ByDestinationNodeIdExperienceOptions,\n ByDestinationPathExperienceOptions,\n ClientOptions,\n ResolveOptions,\n DestinationRedirectResult,\n} from './fetch-experience.js';\nexport { DELIVERY_HOST, PREVIEW_HOST, PREVIEW_WEBSOCKET_HOST } from './hosts.js';\n"],"mappings":"AAAA,SAAS,8BAA8B;AAEvC;AAAA,EACE,0BAAAA;AAAA,EACA;AAAA,OACK;AACA,MAAM,gBAAgB,uBAAuB;AAGpD,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAE7B,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWP,SAAS,eAAe,cAAc,8BAA8B;","names":["ContentfulViewDelivery"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { ContentfulViewDelivery } from '@contentful/experience-delivery';\n\nexport {\n ContentfulViewDelivery,\n ContentfulViewDeliveryClient,\n} from '@contentful/experience-delivery';\nexport const NotFoundError = ContentfulViewDelivery.NotFoundError;\n// eslint-disable-next-line no-redeclare -- value + type share a name across separate TS namespaces, not a real redeclaration\nexport type NotFoundError = InstanceType<typeof ContentfulViewDelivery.NotFoundError>;\nexport { ExperienceFetchError, DestinationPreviewNotSupportedError } from './errors.js';\nexport { createClient } from './create-client.js';\nexport type { CreateClientOptions } from './create-client.js';\nexport { default as EventBuilder } from './event-builder.js';\nexport * from './event-builder.js';\nexport { fetchExperience } from './fetch-experience.js';\nexport { ContentfulExperiences } from './contentful-experiences.js';\nexport type {\n ContentfulExperiencesConfig,\n ExperienceRuntime,\n RuntimeFetchExperienceOptions,\n RuntimeFetchByDestinationNodeOptions,\n RuntimeFetchByDestinationPathOptions,\n RuntimeClientSource,\n RuntimeEventBuilderConfig,\n RuntimeResolveOptions,\n} from './contentful-experiences.js';\nexport {\n readSourceMap,\n toExperiencePayload,\n toExperiencePayloadFromDestination,\n} from './to-experience-payload.js';\nexport type { ExperienceResponse } from './to-experience-payload.js';\nexport type {\n ExperienceOptions,\n ByIdExperienceOptions,\n ByDestinationNodeIdExperienceOptions,\n ByDestinationPathExperienceOptions,\n ClientOptions,\n ResolveOptions,\n DestinationRedirectResult,\n} from './fetch-experience.js';\nexport { DELIVERY_HOST, PREVIEW_HOST, PREVIEW_WEBSOCKET_HOST } from './hosts.js';\n"],"mappings":"AAAA,SAAS,8BAA8B;AAEvC;AAAA,EACE,0BAAAA;AAAA,EACA;AAAA,OACK;AACA,MAAM,gBAAgB,uBAAuB;AAGpD,SAAS,sBAAsB,2CAA2C;AAC1E,SAAS,oBAAoB;AAE7B,SAAoB,WAAXC,gBAA+B;AACxC,cAAc;AACd,SAAS,uBAAuB;AAChC,SAAS,6BAA6B;AAWtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWP,SAAS,eAAe,cAAc,8BAA8B;","names":["ContentfulViewDelivery","default"]}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default library metadata attached to the context of built Events.
3
+ *
4
+ * Build artifacts receive their package metadata through tsup defines. Source
5
+ * execution (including tests) uses stable fallback values instead.
6
+ */
7
+ declare const DEFAULT_EVENT_CONTEXT_LIBRARY: {
8
+ name: string;
9
+ version: string;
10
+ };
11
+
12
+ export { DEFAULT_EVENT_CONTEXT_LIBRARY };
@@ -0,0 +1,8 @@
1
+ const DEFAULT_EVENT_CONTEXT_LIBRARY = {
2
+ name: true ? "@contentful/experiences-client" : "@contentful/experiences-client",
3
+ version: true ? "0.4.8" : "0.0.0"
4
+ };
5
+ export {
6
+ DEFAULT_EVENT_CONTEXT_LIBRARY
7
+ };
8
+ //# sourceMappingURL=sdk-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sdk-info.ts"],"sourcesContent":["declare const __DEFAULT_EVENT_CONTEXT_LIBRARY_NAME__: string | undefined;\ndeclare const __DEFAULT_EVENT_CONTEXT_LIBRARY_VERSION__: string | undefined;\n\n/**\n * Default library metadata attached to the context of built Events.\n *\n * Build artifacts receive their package metadata through tsup defines. Source\n * execution (including tests) uses stable fallback values instead.\n */\nexport const DEFAULT_EVENT_CONTEXT_LIBRARY = {\n name:\n typeof __DEFAULT_EVENT_CONTEXT_LIBRARY_NAME__ === 'string'\n ? __DEFAULT_EVENT_CONTEXT_LIBRARY_NAME__\n : '@contentful/experiences-client',\n version:\n typeof __DEFAULT_EVENT_CONTEXT_LIBRARY_VERSION__ === 'string'\n ? __DEFAULT_EVENT_CONTEXT_LIBRARY_VERSION__\n : '0.0.0',\n};\n"],"mappings":"AASO,MAAM,gCAAgC;AAAA,EAC3C,MACE,OACI,mCACA;AAAA,EACN,SACE,OACI,UACA;AACR;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experiences-client",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Delivery client + fetchExperience for Contentful Experiences",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,6 +31,9 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@contentful/experience-delivery": "1.0.0-dev.8",
34
- "@contentful/experiences-sdk-core": "0.8.6"
34
+ "@contentful/experiences-sdk-core": "0.8.7",
35
+ "@contentful/optimization-api-client": "^2.0.1",
36
+ "es-toolkit": "^1.46.0",
37
+ "zod": "^4.3.6"
35
38
  }
36
39
  }