@contentful/experiences-react 0.13.3 → 0.13.4

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,42 @@
1
+ ## 0.13.4 (2026-10-14)
2
+
3
+ ### 🚀 Features
4
+
5
+ - ⚠️ **web:** [NT-4312] track by scope occurrence instead of node id ([#225](https://github.com/contentful/experiences/pull/225))
6
+ - [NT-4312] expose source-map attribution through the adapter context accessors ([#224](https://github.com/contentful/experiences/pull/224))
7
+
8
+ ### ⚠️ Breaking Changes
9
+
10
+ - **web:** [NT-4312] track by scope occurrence instead of node id ([#225](https://github.com/contentful/experiences/pull/225))
11
+ `data-ctfl-node-id` is replaced by `data-ctfl-scopes`, a
12
+ space-separated list of scope occurrence keys, and `resolveAttribution` now
13
+ takes a key rather than a node id. `getTrackingAttributes` takes a node's
14
+ `attribution` instead of its `nodeId`.
15
+ A node id cannot say that one element sits in several scopes, or that several
16
+ root elements make up one Fragment occurrence. Keys resolve through
17
+ `plan.attribution.scopes`.
18
+ `getTrackingAttributes` and the attribute names are pure string functions, so
19
+ they move into core, and React, Svelte and Angular re-export them: components
20
+ stamp the attribute from `attribution` without installing `web`. The
21
+ `tracking-attributes` subpath re-exports them from core.
22
+ The detectors group elements by key and hand each group to one observer, so an
23
+ occurrence is one view or hover series: it is in view while any element has a
24
+ visible pixel, hovered until the last element is left, and its final event keeps
25
+ the attribution that qualified it. Clicks go to the innermost Fragment
26
+ occurrence of the nearest tracked ancestor, read from the attribute so the order
27
+ survives `refresh()`. Experiences remain view-only.
28
+ This diverges from the Optimization SDK, which tracks per element; the ticket's
29
+ grouping requirement forces it. The interaction-tracking ADR is removed.
30
+ Refs: NT-4312
31
+
32
+ ### 🧱 Updated Dependencies
33
+
34
+ - Updated live-preview to 0.6.4
35
+ - Updated runtime to 0.0.3
36
+ - Updated client to 0.9.4
37
+ - Updated design to 0.12.2
38
+ - Updated core to 0.12.3
39
+
1
40
  ## 0.13.3 (2026-10-12)
2
41
 
3
42
  ### 🧱 Updated Dependencies
package/README.md CHANGED
@@ -120,8 +120,9 @@ MissingComponent; // Default fallback for unregistered component types
120
120
  useDesignValues<T>(); // Escape hatch: the same resolved design record that auto-fills props
121
121
  toCss(design, options?); // Turns a design record into CSSProperties, keeping only real CSS keys
122
122
  useExperience(); // RenderContext: debug, metadata
123
- useContentfulComponent(); // Raw payload for the enclosing node (or null)
123
+ useContentfulComponent(); // Raw payload for the enclosing node (or null), including `attribution`
124
124
  useContentfulExperienceTemplate(); // Same, for an enclosing coded Experience Template node
125
+ getTrackingAttributes(attribution); // Props to spread on a node's outermost element for interaction tracking
125
126
  type ToCssOptions;
126
127
  ```
127
128
 
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export { useContentfulComponent, useContentfulExperienceTemplate, useExperience
12
12
  export { useDesignValues } from './use-design-values.js';
13
13
  export { ToCssOptions, toCss } from './design-utils.js';
14
14
  export { ComponentConfig, Components, Config, ContentfulComponent, ContentfulExperienceTemplate, ExperienceTemplateConfig, ExperienceTemplateRegistration, ExperienceTemplates, Registration, defineComponent, defineExperienceTemplate, normalizeComponentRegistration, normalizeExperienceTemplateRegistration } from './types.js';
15
- export { ComponentNode, ComponentRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSourceMap, ExperienceSys, ExperienceTemplateNode, ExperienceTemplateRef, ManualDesignValue, PortableRegistration, PortableRenderNode, PortableRenderPlan, RenderContext, ResolveContext, ResolveExperienceOptions, ResolveToken, ResolverConfig, resolveExperience } from '@contentful/experiences-sdk-core';
15
+ export { ComponentNode, ComponentRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSourceMap, ExperienceSys, ExperienceTemplateNode, ExperienceTemplateRef, ManualDesignValue, PortableRegistration, PortableRenderNode, PortableRenderPlan, RenderContext, ResolveContext, ResolveExperienceOptions, ResolveToken, ResolverConfig, TRACKING_CLICKABLE_ATTRIBUTE, TRACKING_SCOPES_ATTRIBUTE, TrackingAttributes, getTrackingAttributes, resolveExperience } from '@contentful/experiences-sdk-core';
16
16
  export { ANONYMOUS_ID_COOKIE, ClickBuilderArgs, DEFAULT_PAGE_PROPERTIES, EventBuilder, EventBuilderConfig, FlagViewBuilderArgs, HoverBuilderArgs, IdentifyBuilderArgs, InteractionBuilderArgsBase, PageViewBuilderArgs, TrackBuilderArgs, UniversalEventBuilderArgs, ViewBuilderArgs } from '@contentful/experiences-runtime';
17
17
  export { CSS_PROPERTIES, isCssProperty, toCssKey } from '@contentful/experiences-design';
18
18
  export { ByDestinationNodeIdExperienceOptions, ByDestinationPathExperienceOptions, ByIdExperienceOptions, ClientOptions, ContentfulViewDelivery, ContentfulViewDeliveryClient, CreateClientOptions, DELIVERY_HOST, DestinationRedirectResult, DestinationSitemapResult, ExperienceFetchError, ExperienceOptions, ExperienceResponse, FetchDestinationSitemapOptions, FetchDestinationSitemapPaginationOptions, NotFoundError, PREVIEW_HOST, ResolveOptions, SitemapPathItem, createClient, fetchDestinationSitemap, fetchExperience, readSourceMap, toExperiencePayload, toExperiencePayloadFromDestination } from '@contentful/experiences-client';
package/dist/index.js CHANGED
@@ -25,6 +25,11 @@ import {
25
25
  normalizeExperienceTemplateRegistration
26
26
  } from "./types";
27
27
  import { resolveExperience } from "@contentful/experiences-sdk-core";
28
+ import {
29
+ getTrackingAttributes,
30
+ TRACKING_CLICKABLE_ATTRIBUTE,
31
+ TRACKING_SCOPES_ATTRIBUTE
32
+ } from "@contentful/experiences-sdk-core";
28
33
  import { DEFAULT_PAGE_PROPERTIES, EventBuilder } from "@contentful/experiences-runtime";
29
34
  import { CSS_PROPERTIES, isCssProperty, toCssKey } from "@contentful/experiences-design";
30
35
  import {
@@ -62,12 +67,15 @@ export {
62
67
  PREVIEW_WEBSOCKET_HOST,
63
68
  PreviewSessionFetchError,
64
69
  ServerExperienceRenderer,
70
+ TRACKING_CLICKABLE_ATTRIBUTE,
71
+ TRACKING_SCOPES_ATTRIBUTE,
65
72
  createClient,
66
73
  defineComponent,
67
74
  defineExperienceTemplate,
68
75
  fetchDestinationSitemap,
69
76
  fetchExperience,
70
77
  fetchPreviewSession,
78
+ getTrackingAttributes,
71
79
  isCssProperty,
72
80
  normalizeComponentRegistration,
73
81
  normalizeExperienceTemplateRegistration,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/*\n * Public API surface for `@contentful/experiences-react`.\n *\n * Customers add ONLY this package to their app's dependencies. Everything\n * needed to render an Experience — types, the resolver, design utilities,\n * the renderer components, the authoring helpers — is re-exported from here.\n * The internal `@contentful/experiences-sdk-core` and\n * `@contentful/experiences-design` packages are workspace-only implementation\n * details; they are not part of the public API.\n *\n * `ExperienceRenderer` is an alias for `ClientExperienceRenderer`; SSR\n * consumers explicitly import `ServerExperienceRenderer`.\n */\n\n// ─── Renderers ─────────────────────────────────────────────────────────────\nexport {\n ClientExperienceRenderer as ExperienceRenderer,\n ClientExperienceRenderer,\n} from './client-renderer';\nexport type { ClientExperienceRendererProps as ExperienceRendererProps } from './client-renderer';\nexport type { ClientExperienceRendererProps } from './client-renderer';\n\nexport { ServerExperienceRenderer } from './server-renderer';\nexport type { ServerExperienceRendererProps } from './server-renderer';\n\nexport { MissingComponent } from './missing-component';\nexport type { MissingComponentProps } from './missing-component';\n\nexport { ComponentError } from './component-error';\nexport type { ComponentErrorProps } from './component-error';\n\nexport { DebugExperience } from './debug-experience';\nexport type { DebugExperienceProps } from './debug-experience';\n\nexport type { DiagnosticReporter, RenderError, RenderUnknown } from './nodes-renderer';\n\nexport { useLivePreviewExperience } from './use-live-preview-experience';\nexport type {\n UseLivePreviewExperienceOptions,\n UseLivePreviewExperienceResult,\n} from './use-live-preview-experience';\nexport { useLivePreview } from './use-live-preview';\nexport type { UseLivePreviewOptions, UseLivePreviewResult } from './use-live-preview';\n\nexport { useExperiencePlan } from './use-experience-plan';\nexport type {\n ExperiencePlanResolveOptions,\n UseExperiencePlanOptions,\n UseExperiencePlanResult,\n} from './use-experience-plan';\nexport type {\n LivePreviewClient,\n LivePreviewResult,\n PreviewSessionClientOptions,\n PreviewSessionExperienceOptions,\n PreviewSessionOptions,\n PreviewSessionResolveOptions,\n} from '@contentful/experiences-live-preview';\nexport {\n fetchPreviewSession,\n LivePreviewConnectionError,\n PREVIEW_WEBSOCKET_HOST,\n PreviewSessionFetchError,\n} from '@contentful/experiences-live-preview';\n\n// ─── Runtime context hooks ───────────────────────────────────────────────\nexport { useExperience, useContentfulComponent, useContentfulExperienceTemplate } from './context';\nexport { useDesignValues } from './use-design-values';\nexport { toCss } from './design-utils';\nexport type { ToCssOptions } from './design-utils';\n\n// ─── Authoring helpers + Config types ─────────────────────────────────────\nexport {\n defineComponent,\n defineExperienceTemplate,\n normalizeComponentRegistration,\n normalizeExperienceTemplateRegistration,\n} from './types';\nexport type {\n ComponentConfig,\n Components,\n Config,\n ContentfulComponent,\n ContentfulExperienceTemplate,\n ExperienceTemplateConfig,\n ExperienceTemplateRegistration,\n ExperienceTemplates,\n Registration,\n RenderContext,\n ResolveToken,\n} from './types';\n\n// ─── Resolver (re-exported from render-core) ──────────────────────────────\nexport { resolveExperience } from '@contentful/experiences-sdk-core';\nexport type { ResolverConfig, ResolveExperienceOptions } from '@contentful/experiences-sdk-core';\n\n// ─── Event builder (re-exported from runtime) ─────────────────────────────\nexport { DEFAULT_PAGE_PROPERTIES, EventBuilder } from '@contentful/experiences-runtime';\nexport type {\n ClickBuilderArgs,\n EventBuilderConfig,\n FlagViewBuilderArgs,\n HoverBuilderArgs,\n IdentifyBuilderArgs,\n InteractionBuilderArgsBase,\n PageViewBuilderArgs,\n TrackBuilderArgs,\n UniversalEventBuilderArgs,\n ViewBuilderArgs,\n} from '@contentful/experiences-runtime';\n\n// ─── Core IR + payload types (re-exported from render-core) ───────────────\nexport type {\n ComponentNode,\n ComponentRef,\n DesignPropValue,\n DesignToken,\n ExperienceContext,\n ExperienceNode,\n ExperiencePayload,\n ExperienceSourceMap,\n ExperienceSys,\n ExperienceTemplateNode,\n ExperienceTemplateRef,\n ManualDesignValue,\n PortableRegistration,\n PortableRenderNode,\n PortableRenderPlan,\n ResolveContext,\n} from '@contentful/experiences-sdk-core';\n\n// ─── Design utilities (re-exported from design) ───────────────────────────\nexport { CSS_PROPERTIES, isCssProperty, toCssKey } from '@contentful/experiences-design';\n\n// ─── Delivery client + fetchExperience ────────────────────────────────────\nexport {\n ContentfulViewDelivery,\n ContentfulViewDeliveryClient,\n DELIVERY_HOST,\n ExperienceFetchError,\n NotFoundError,\n PREVIEW_HOST,\n createClient,\n fetchExperience,\n fetchDestinationSitemap,\n readSourceMap,\n toExperiencePayload,\n toExperiencePayloadFromDestination,\n} from '@contentful/experiences-client';\nexport { ANONYMOUS_ID_COOKIE } from '@contentful/experiences-runtime';\nexport type {\n ByDestinationNodeIdExperienceOptions,\n ByDestinationPathExperienceOptions,\n ByIdExperienceOptions,\n ClientOptions,\n CreateClientOptions,\n DestinationRedirectResult,\n DestinationSitemapResult,\n ExperienceOptions,\n ExperienceResponse,\n FetchDestinationSitemapOptions,\n FetchDestinationSitemapPaginationOptions,\n ResolveOptions,\n SitemapPathItem,\n} from '@contentful/experiences-client';\n"],"mappings":"AAeA;AAAA,EAC8B;AAAA,EAC5B,4BAAAA;AAAA,OACK;AAIP,SAAS,gCAAgC;AAGzC,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAG/B,SAAS,uBAAuB;AAKhC,SAAS,gCAAgC;AAKzC,SAAS,sBAAsB;AAG/B,SAAS,yBAAyB;AAclC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,eAAe,wBAAwB,uCAAuC;AACvF,SAAS,uBAAuB;AAChC,SAAS,aAAa;AAItB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgBP,SAAS,yBAAyB;AAIlC,SAAS,yBAAyB,oBAAoB;AAmCtD,SAAS,gBAAgB,eAAe,gBAAgB;AAGxD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;","names":["ClientExperienceRenderer"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/*\n * Public API surface for `@contentful/experiences-react`.\n *\n * Customers add ONLY this package to their app's dependencies. Everything\n * needed to render an Experience — types, the resolver, design utilities,\n * the renderer components, the authoring helpers — is re-exported from here.\n * The internal `@contentful/experiences-sdk-core` and\n * `@contentful/experiences-design` packages are workspace-only implementation\n * details; they are not part of the public API.\n *\n * `ExperienceRenderer` is an alias for `ClientExperienceRenderer`; SSR\n * consumers explicitly import `ServerExperienceRenderer`.\n */\n\n// ─── Renderers ─────────────────────────────────────────────────────────────\nexport {\n ClientExperienceRenderer as ExperienceRenderer,\n ClientExperienceRenderer,\n} from './client-renderer';\nexport type { ClientExperienceRendererProps as ExperienceRendererProps } from './client-renderer';\nexport type { ClientExperienceRendererProps } from './client-renderer';\n\nexport { ServerExperienceRenderer } from './server-renderer';\nexport type { ServerExperienceRendererProps } from './server-renderer';\n\nexport { MissingComponent } from './missing-component';\nexport type { MissingComponentProps } from './missing-component';\n\nexport { ComponentError } from './component-error';\nexport type { ComponentErrorProps } from './component-error';\n\nexport { DebugExperience } from './debug-experience';\nexport type { DebugExperienceProps } from './debug-experience';\n\nexport type { DiagnosticReporter, RenderError, RenderUnknown } from './nodes-renderer';\n\nexport { useLivePreviewExperience } from './use-live-preview-experience';\nexport type {\n UseLivePreviewExperienceOptions,\n UseLivePreviewExperienceResult,\n} from './use-live-preview-experience';\nexport { useLivePreview } from './use-live-preview';\nexport type { UseLivePreviewOptions, UseLivePreviewResult } from './use-live-preview';\n\nexport { useExperiencePlan } from './use-experience-plan';\nexport type {\n ExperiencePlanResolveOptions,\n UseExperiencePlanOptions,\n UseExperiencePlanResult,\n} from './use-experience-plan';\nexport type {\n LivePreviewClient,\n LivePreviewResult,\n PreviewSessionClientOptions,\n PreviewSessionExperienceOptions,\n PreviewSessionOptions,\n PreviewSessionResolveOptions,\n} from '@contentful/experiences-live-preview';\nexport {\n fetchPreviewSession,\n LivePreviewConnectionError,\n PREVIEW_WEBSOCKET_HOST,\n PreviewSessionFetchError,\n} from '@contentful/experiences-live-preview';\n\n// ─── Runtime context hooks ───────────────────────────────────────────────\nexport { useExperience, useContentfulComponent, useContentfulExperienceTemplate } from './context';\nexport { useDesignValues } from './use-design-values';\nexport { toCss } from './design-utils';\nexport type { ToCssOptions } from './design-utils';\n\n// ─── Authoring helpers + Config types ─────────────────────────────────────\nexport {\n defineComponent,\n defineExperienceTemplate,\n normalizeComponentRegistration,\n normalizeExperienceTemplateRegistration,\n} from './types';\nexport type {\n ComponentConfig,\n Components,\n Config,\n ContentfulComponent,\n ContentfulExperienceTemplate,\n ExperienceTemplateConfig,\n ExperienceTemplateRegistration,\n ExperienceTemplates,\n Registration,\n RenderContext,\n ResolveToken,\n} from './types';\n\n// ─── Resolver (re-exported from render-core) ──────────────────────────────\nexport { resolveExperience } from '@contentful/experiences-sdk-core';\nexport {\n getTrackingAttributes,\n TRACKING_CLICKABLE_ATTRIBUTE,\n TRACKING_SCOPES_ATTRIBUTE,\n} from '@contentful/experiences-sdk-core';\nexport type { TrackingAttributes } from '@contentful/experiences-sdk-core';\nexport type { ResolverConfig, ResolveExperienceOptions } from '@contentful/experiences-sdk-core';\n\n// ─── Event builder (re-exported from runtime) ─────────────────────────────\nexport { DEFAULT_PAGE_PROPERTIES, EventBuilder } from '@contentful/experiences-runtime';\nexport type {\n ClickBuilderArgs,\n EventBuilderConfig,\n FlagViewBuilderArgs,\n HoverBuilderArgs,\n IdentifyBuilderArgs,\n InteractionBuilderArgsBase,\n PageViewBuilderArgs,\n TrackBuilderArgs,\n UniversalEventBuilderArgs,\n ViewBuilderArgs,\n} from '@contentful/experiences-runtime';\n\n// ─── Core IR + payload types (re-exported from render-core) ───────────────\nexport type {\n ComponentNode,\n ComponentRef,\n DesignPropValue,\n DesignToken,\n ExperienceContext,\n ExperienceNode,\n ExperiencePayload,\n ExperienceSourceMap,\n ExperienceSys,\n ExperienceTemplateNode,\n ExperienceTemplateRef,\n ManualDesignValue,\n PortableRegistration,\n PortableRenderNode,\n PortableRenderPlan,\n ResolveContext,\n} from '@contentful/experiences-sdk-core';\n\n// ─── Design utilities (re-exported from design) ───────────────────────────\nexport { CSS_PROPERTIES, isCssProperty, toCssKey } from '@contentful/experiences-design';\n\n// ─── Delivery client + fetchExperience ────────────────────────────────────\nexport {\n ContentfulViewDelivery,\n ContentfulViewDeliveryClient,\n DELIVERY_HOST,\n ExperienceFetchError,\n NotFoundError,\n PREVIEW_HOST,\n createClient,\n fetchExperience,\n fetchDestinationSitemap,\n readSourceMap,\n toExperiencePayload,\n toExperiencePayloadFromDestination,\n} from '@contentful/experiences-client';\nexport { ANONYMOUS_ID_COOKIE } from '@contentful/experiences-runtime';\nexport type {\n ByDestinationNodeIdExperienceOptions,\n ByDestinationPathExperienceOptions,\n ByIdExperienceOptions,\n ClientOptions,\n CreateClientOptions,\n DestinationRedirectResult,\n DestinationSitemapResult,\n ExperienceOptions,\n ExperienceResponse,\n FetchDestinationSitemapOptions,\n FetchDestinationSitemapPaginationOptions,\n ResolveOptions,\n SitemapPathItem,\n} from '@contentful/experiences-client';\n"],"mappings":"AAeA;AAAA,EAC8B;AAAA,EAC5B,4BAAAA;AAAA,OACK;AAIP,SAAS,gCAAgC;AAGzC,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAG/B,SAAS,uBAAuB;AAKhC,SAAS,gCAAgC;AAKzC,SAAS,sBAAsB;AAG/B,SAAS,yBAAyB;AAclC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,eAAe,wBAAwB,uCAAuC;AACvF,SAAS,uBAAuB;AAChC,SAAS,aAAa;AAItB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgBP,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAKP,SAAS,yBAAyB,oBAAoB;AAmCtD,SAAS,gBAAgB,eAAe,gBAAgB;AAGxD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;","names":["ClientExperienceRenderer"]}
@@ -104,7 +104,8 @@ function NodeRenderer({
104
104
  nodeId: node.nodeId,
105
105
  content: node.props.content,
106
106
  design: node.props.designRaw,
107
- resolved: node.props.resolved
107
+ resolved: node.props.resolved,
108
+ attribution: node.attribution
108
109
  };
109
110
  return /* @__PURE__ */ jsx(ContentfulExperienceTemplateProvider, { value: contentful2, children: element });
110
111
  }
@@ -113,7 +114,8 @@ function NodeRenderer({
113
114
  nodeId: node.nodeId,
114
115
  content: node.props.content,
115
116
  design: node.props.designRaw,
116
- resolved: node.props.resolved
117
+ resolved: node.props.resolved,
118
+ attribution: node.attribution
117
119
  };
118
120
  return /* @__PURE__ */ jsx(ContentfulComponentProvider, { value: contentful, children: element });
119
121
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/nodes-renderer.tsx"],"sourcesContent":["/*\n * Recursive renderer over PortableRenderNodes. Resolved design values auto-fill\n * matching props (below content/resolveData) and are also available via\n * `useDesignValues()`.\n */\n\nimport { Fragment, createElement, type ReactNode } from 'react';\n\nimport type { PortableRenderNode } from '@contentful/experiences-sdk-core';\n\nimport { ComponentErrorBoundary } from './component-error-boundary';\nimport type { ComponentErrorProps } from './component-error';\nimport {\n ContentfulComponentProvider,\n ContentfulExperienceTemplateProvider,\n ResolvedDesignProvider,\n} from './context';\nimport type { MissingComponentProps } from './missing-component';\nimport {\n normalizeComponentRegistration,\n normalizeExperienceTemplateRegistration,\n type ContentfulComponent,\n type ContentfulExperienceTemplate,\n type Config,\n} from './types';\n\nexport type RenderUnknown = (props: MissingComponentProps) => ReactNode;\nexport type RenderError = (props: ComponentErrorProps) => ReactNode;\n\n/**\n * Reports one render-time diagnostic (unregistered id, a component that\n * threw). `ServerExperienceRenderer` passes a closure that pushes onto a\n * plain array (SSR is synchronous top-down, so the array is fully populated\n * by the time `<DebugExperience>` reads it — see the element-order note\n * there). `ClientExperienceRenderer` passes a `setState` updater instead, so\n * `<DebugExperience>` re-renders reactively when a later interaction throws.\n */\nexport type DiagnosticReporter = (error: Error) => void;\n\nexport interface NodesRendererProps {\n nodes: PortableRenderNode[];\n config: Config;\n renderUnknown: RenderUnknown;\n renderError: RenderError;\n onDiagnostic: DiagnosticReporter;\n}\n\nexport function NodesRenderer({\n nodes,\n config,\n renderUnknown,\n renderError,\n onDiagnostic,\n}: NodesRendererProps): ReactNode {\n if (!nodes.length) return null;\n return (\n <Fragment>\n {nodes.map((node, index) => (\n <NodeRenderer\n key={node.nodeId ?? index}\n node={node}\n config={config}\n renderUnknown={renderUnknown}\n renderError={renderError}\n onDiagnostic={onDiagnostic}\n />\n ))}\n </Fragment>\n );\n}\n\ninterface NodeRendererProps {\n node: PortableRenderNode;\n config: Config;\n renderUnknown: RenderUnknown;\n renderError: RenderError;\n onDiagnostic: DiagnosticReporter;\n}\n\nfunction NodeRenderer({\n node,\n config,\n renderUnknown,\n renderError,\n onDiagnostic,\n}: NodeRendererProps): ReactNode {\n const { kind, id } = node.registration;\n const isExperienceTemplate = kind === 'experienceTemplate';\n\n // Diagnostics are reported straight to `onDiagnostic`, synchronously during\n // render. Deliberate: this component is shared between SSR and CSR, and\n // `useEffect` never runs during `renderToStaticMarkup`/streaming SSR — an\n // effect-deferred report would silently vanish on the server.\n //\n // Dedup lives in `onDiagnostic` (see both renderers), not here. An ancestor\n // re-render re-executes this whole function, so an unregistered id or\n // malformed slot would otherwise be re-reported every time — the collectors\n // drop a message they already hold. Keeping the guard there rather than in a\n // `useRef` is what lets this module stay hook-free, and therefore usable from\n // a React Server Component: `ServerExperienceRenderer` renders it without a\n // `'use client'` boundary, which is what keeps `config` (component\n // references, unserializable) from having to cross one.\n\n // Pre-render each slot's children as an *array* of ReactNodes (one keyed\n // element per child), not a single wrapping node. A component can drop the\n // array straight into JSX for the common \"just render them\" case (React\n // renders keyed arrays), or map/filter/wrap the children individually.\n //\n // Slot name → prop name, for both node kinds: a coded Experience Template\n // declaring a `content` slot receives a `content` prop. `children` is not a\n // special case, just the conventional default slot name.\n const slotProps: Record<string, ReactNode[]> = {};\n for (const [slotName, children] of Object.entries(node.slots)) {\n // Defensive: `node.slots[x]` is typed as an array, but a hand-built\n // PortableRenderPlan (a supported path — customers can construct one\n // directly instead of going through `resolveExperience`) is not\n // type-checked at runtime. Warn + drop rather than letting `.map` throw.\n if (!Array.isArray(children)) {\n const message =\n `Slot \"${slotName}\" on ${kind} \"${id}\"${node.nodeId ? ` (node \"${node.nodeId}\")` : ''} ` +\n `is not an array of nodes; rendering it as empty instead of throwing.`;\n if (typeof console !== 'undefined') {\n console.warn(`[@contentful/experiences-react] ${message}`);\n }\n onDiagnostic(new Error(message));\n slotProps[slotName] = [];\n continue;\n }\n slotProps[slotName] = children.map((child, index) => (\n <NodeRenderer\n key={child.nodeId ?? index}\n node={child}\n config={config}\n renderUnknown={renderUnknown}\n renderError={renderError}\n onDiagnostic={onDiagnostic}\n />\n ));\n }\n\n const entry = isExperienceTemplate ? config.experienceTemplates?.[id] : config.components[id];\n if (!entry) {\n // An unregistered Experience Template would blank the page if we swapped\n // it for the missing-component box, so warn and render its slot children\n // unwrapped — the content survives, the diagnostic names what's missing.\n if (isExperienceTemplate) {\n const message = `No experience template registered for id \"${id}\". Rendering its slot children without the experience template wrapper.`;\n if (typeof console !== 'undefined') {\n console.warn(`[@contentful/experiences-react] ${message}`);\n }\n onDiagnostic(new Error(message));\n return <Fragment>{Object.values(slotProps).flat()}</Fragment>;\n }\n // `MissingComponent` (the default `renderUnknown`) does its own\n // console.warn; a custom override may not, so the diagnostic is recorded\n // here regardless of which fallback ends up rendering.\n onDiagnostic(\n new Error(\n `No component registered for id \"${id}\"${node.nodeId ? ` (nodeId: ${node.nodeId})` : ''}.`\n )\n );\n return createElement(renderUnknown, { componentId: id, nodeId: node.nodeId });\n }\n const registrationConfig = isExperienceTemplate\n ? normalizeExperienceTemplateRegistration(entry)\n : normalizeComponentRegistration(entry);\n\n const tokenResolvedDesign = node.props.design;\n\n // Merge precedence (last wins): defaults < design < content < resolveData < slots.\n const composed = {\n ...registrationConfig.defaults,\n ...tokenResolvedDesign,\n ...node.props.content,\n ...node.props.resolved,\n ...slotProps,\n };\n\n const element = (\n <ResolvedDesignProvider value={tokenResolvedDesign}>\n <ComponentErrorBoundary\n fallback={createElement(renderError, { componentId: id, nodeId: node.nodeId })}\n componentId={id}\n kind={kind}\n nodeId={node.nodeId}\n >\n {createElement(registrationConfig.component, composed)}\n </ComponentErrorBoundary>\n </ResolvedDesignProvider>\n );\n\n if (isExperienceTemplate) {\n const contentful: ContentfulExperienceTemplate = {\n experienceTemplateId: id,\n nodeId: node.nodeId,\n content: node.props.content,\n design: node.props.designRaw,\n resolved: node.props.resolved,\n };\n return (\n <ContentfulExperienceTemplateProvider value={contentful}>\n {element}\n </ContentfulExperienceTemplateProvider>\n );\n }\n\n const contentful: ContentfulComponent = {\n componentId: id,\n nodeId: node.nodeId,\n content: node.props.content,\n design: node.props.designRaw,\n resolved: node.props.resolved,\n };\n return <ContentfulComponentProvider value={contentful}>{element}</ContentfulComponentProvider>;\n}\n\n// `ComponentErrorBoundary` lives in its own `'use client'` file — see\n// component-error-boundary.tsx for why (it's a class component, which\n// Next.js's RSC bundler rejects from a directive-free file).\n"],"mappings":"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","names":["contentful"]}
1
+ {"version":3,"sources":["../src/nodes-renderer.tsx"],"sourcesContent":["/*\n * Recursive renderer over PortableRenderNodes. Resolved design values auto-fill\n * matching props (below content/resolveData) and are also available via\n * `useDesignValues()`.\n */\n\nimport { Fragment, createElement, type ReactNode } from 'react';\n\nimport type { PortableRenderNode } from '@contentful/experiences-sdk-core';\n\nimport { ComponentErrorBoundary } from './component-error-boundary';\nimport type { ComponentErrorProps } from './component-error';\nimport {\n ContentfulComponentProvider,\n ContentfulExperienceTemplateProvider,\n ResolvedDesignProvider,\n} from './context';\nimport type { MissingComponentProps } from './missing-component';\nimport {\n normalizeComponentRegistration,\n normalizeExperienceTemplateRegistration,\n type ContentfulComponent,\n type ContentfulExperienceTemplate,\n type Config,\n} from './types';\n\nexport type RenderUnknown = (props: MissingComponentProps) => ReactNode;\nexport type RenderError = (props: ComponentErrorProps) => ReactNode;\n\n/**\n * Reports one render-time diagnostic (unregistered id, a component that\n * threw). `ServerExperienceRenderer` passes a closure that pushes onto a\n * plain array (SSR is synchronous top-down, so the array is fully populated\n * by the time `<DebugExperience>` reads it — see the element-order note\n * there). `ClientExperienceRenderer` passes a `setState` updater instead, so\n * `<DebugExperience>` re-renders reactively when a later interaction throws.\n */\nexport type DiagnosticReporter = (error: Error) => void;\n\nexport interface NodesRendererProps {\n nodes: PortableRenderNode[];\n config: Config;\n renderUnknown: RenderUnknown;\n renderError: RenderError;\n onDiagnostic: DiagnosticReporter;\n}\n\nexport function NodesRenderer({\n nodes,\n config,\n renderUnknown,\n renderError,\n onDiagnostic,\n}: NodesRendererProps): ReactNode {\n if (!nodes.length) return null;\n return (\n <Fragment>\n {nodes.map((node, index) => (\n <NodeRenderer\n key={node.nodeId ?? index}\n node={node}\n config={config}\n renderUnknown={renderUnknown}\n renderError={renderError}\n onDiagnostic={onDiagnostic}\n />\n ))}\n </Fragment>\n );\n}\n\ninterface NodeRendererProps {\n node: PortableRenderNode;\n config: Config;\n renderUnknown: RenderUnknown;\n renderError: RenderError;\n onDiagnostic: DiagnosticReporter;\n}\n\nfunction NodeRenderer({\n node,\n config,\n renderUnknown,\n renderError,\n onDiagnostic,\n}: NodeRendererProps): ReactNode {\n const { kind, id } = node.registration;\n const isExperienceTemplate = kind === 'experienceTemplate';\n\n // Diagnostics are reported straight to `onDiagnostic`, synchronously during\n // render. Deliberate: this component is shared between SSR and CSR, and\n // `useEffect` never runs during `renderToStaticMarkup`/streaming SSR — an\n // effect-deferred report would silently vanish on the server.\n //\n // Dedup lives in `onDiagnostic` (see both renderers), not here. An ancestor\n // re-render re-executes this whole function, so an unregistered id or\n // malformed slot would otherwise be re-reported every time — the collectors\n // drop a message they already hold. Keeping the guard there rather than in a\n // `useRef` is what lets this module stay hook-free, and therefore usable from\n // a React Server Component: `ServerExperienceRenderer` renders it without a\n // `'use client'` boundary, which is what keeps `config` (component\n // references, unserializable) from having to cross one.\n\n // Pre-render each slot's children as an *array* of ReactNodes (one keyed\n // element per child), not a single wrapping node. A component can drop the\n // array straight into JSX for the common \"just render them\" case (React\n // renders keyed arrays), or map/filter/wrap the children individually.\n //\n // Slot name → prop name, for both node kinds: a coded Experience Template\n // declaring a `content` slot receives a `content` prop. `children` is not a\n // special case, just the conventional default slot name.\n const slotProps: Record<string, ReactNode[]> = {};\n for (const [slotName, children] of Object.entries(node.slots)) {\n // Defensive: `node.slots[x]` is typed as an array, but a hand-built\n // PortableRenderPlan (a supported path — customers can construct one\n // directly instead of going through `resolveExperience`) is not\n // type-checked at runtime. Warn + drop rather than letting `.map` throw.\n if (!Array.isArray(children)) {\n const message =\n `Slot \"${slotName}\" on ${kind} \"${id}\"${node.nodeId ? ` (node \"${node.nodeId}\")` : ''} ` +\n `is not an array of nodes; rendering it as empty instead of throwing.`;\n if (typeof console !== 'undefined') {\n console.warn(`[@contentful/experiences-react] ${message}`);\n }\n onDiagnostic(new Error(message));\n slotProps[slotName] = [];\n continue;\n }\n slotProps[slotName] = children.map((child, index) => (\n <NodeRenderer\n key={child.nodeId ?? index}\n node={child}\n config={config}\n renderUnknown={renderUnknown}\n renderError={renderError}\n onDiagnostic={onDiagnostic}\n />\n ));\n }\n\n const entry = isExperienceTemplate ? config.experienceTemplates?.[id] : config.components[id];\n if (!entry) {\n // An unregistered Experience Template would blank the page if we swapped\n // it for the missing-component box, so warn and render its slot children\n // unwrapped — the content survives, the diagnostic names what's missing.\n if (isExperienceTemplate) {\n const message = `No experience template registered for id \"${id}\". Rendering its slot children without the experience template wrapper.`;\n if (typeof console !== 'undefined') {\n console.warn(`[@contentful/experiences-react] ${message}`);\n }\n onDiagnostic(new Error(message));\n return <Fragment>{Object.values(slotProps).flat()}</Fragment>;\n }\n // `MissingComponent` (the default `renderUnknown`) does its own\n // console.warn; a custom override may not, so the diagnostic is recorded\n // here regardless of which fallback ends up rendering.\n onDiagnostic(\n new Error(\n `No component registered for id \"${id}\"${node.nodeId ? ` (nodeId: ${node.nodeId})` : ''}.`\n )\n );\n return createElement(renderUnknown, { componentId: id, nodeId: node.nodeId });\n }\n const registrationConfig = isExperienceTemplate\n ? normalizeExperienceTemplateRegistration(entry)\n : normalizeComponentRegistration(entry);\n\n const tokenResolvedDesign = node.props.design;\n\n // Merge precedence (last wins): defaults < design < content < resolveData < slots.\n const composed = {\n ...registrationConfig.defaults,\n ...tokenResolvedDesign,\n ...node.props.content,\n ...node.props.resolved,\n ...slotProps,\n };\n\n const element = (\n <ResolvedDesignProvider value={tokenResolvedDesign}>\n <ComponentErrorBoundary\n fallback={createElement(renderError, { componentId: id, nodeId: node.nodeId })}\n componentId={id}\n kind={kind}\n nodeId={node.nodeId}\n >\n {createElement(registrationConfig.component, composed)}\n </ComponentErrorBoundary>\n </ResolvedDesignProvider>\n );\n\n if (isExperienceTemplate) {\n const contentful: ContentfulExperienceTemplate = {\n experienceTemplateId: id,\n nodeId: node.nodeId,\n content: node.props.content,\n design: node.props.designRaw,\n resolved: node.props.resolved,\n attribution: node.attribution,\n };\n return (\n <ContentfulExperienceTemplateProvider value={contentful}>\n {element}\n </ContentfulExperienceTemplateProvider>\n );\n }\n\n const contentful: ContentfulComponent = {\n componentId: id,\n nodeId: node.nodeId,\n content: node.props.content,\n design: node.props.designRaw,\n resolved: node.props.resolved,\n attribution: node.attribution,\n };\n return <ContentfulComponentProvider value={contentful}>{element}</ContentfulComponentProvider>;\n}\n\n// `ComponentErrorBoundary` lives in its own `'use client'` file — see\n// component-error-boundary.tsx for why (it's a class component, which\n// Next.js's RSC bundler rejects from a directive-free file).\n"],"mappings":"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","names":["contentful"]}
@@ -0,0 +1,35 @@
1
+ type Leaf = {
2
+ kind: string;
3
+ id: string;
4
+ };
5
+ declare function minimalSourceMap({ experienceId, node, leaf, }?: {
6
+ experienceId?: string;
7
+ node?: string;
8
+ leaf?: Leaf;
9
+ }): {
10
+ version: number;
11
+ variants: {
12
+ type: string;
13
+ id: string;
14
+ }[];
15
+ spaces: never[];
16
+ environments: never[];
17
+ locales: never[];
18
+ entries: never[];
19
+ assets: never[];
20
+ layers: (Leaf | {
21
+ kind: string;
22
+ id: string;
23
+ variants: number[];
24
+ })[];
25
+ dataAssemblies: never[];
26
+ nodes: {
27
+ [node]: {
28
+ layers: number[];
29
+ scope: number;
30
+ contentProperties: never[];
31
+ };
32
+ };
33
+ };
34
+
35
+ export { minimalSourceMap };
@@ -0,0 +1,22 @@
1
+ function minimalSourceMap({
2
+ experienceId = "exp-a",
3
+ node = "n",
4
+ leaf = { kind: "Component", id: "probe" }
5
+ } = {}) {
6
+ return {
7
+ version: 1,
8
+ variants: [{ type: "personalization", id: "default" }],
9
+ spaces: [],
10
+ environments: [],
11
+ locales: [],
12
+ entries: [],
13
+ assets: [],
14
+ layers: [{ kind: "Experience", id: experienceId, variants: [0] }, leaf],
15
+ dataAssemblies: [],
16
+ nodes: { [node]: { layers: [1, 0], scope: 0, contentProperties: [] } }
17
+ };
18
+ }
19
+ export {
20
+ minimalSourceMap
21
+ };
22
+ //# sourceMappingURL=source-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/test-fixtures/source-map.ts"],"sourcesContent":["type Leaf = { kind: string; id: string };\n\nexport function minimalSourceMap({\n experienceId = 'exp-a',\n node = 'n',\n leaf = { kind: 'Component', id: 'probe' },\n}: { experienceId?: string; node?: string; leaf?: Leaf } = {}) {\n return {\n version: 1,\n variants: [{ type: 'personalization', id: 'default' }],\n spaces: [],\n environments: [],\n locales: [],\n entries: [],\n assets: [],\n layers: [{ kind: 'Experience', id: experienceId, variants: [0] }, leaf],\n dataAssemblies: [],\n nodes: { [node]: { layers: [1, 0], scope: 0, contentProperties: [] } },\n };\n}\n"],"mappings":"AAEO,SAAS,iBAAiB;AAAA,EAC/B,eAAe;AAAA,EACf,OAAO;AAAA,EACP,OAAO,EAAE,MAAM,aAAa,IAAI,QAAQ;AAC1C,IAA2D,CAAC,GAAG;AAC7D,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU,CAAC,EAAE,MAAM,mBAAmB,IAAI,UAAU,CAAC;AAAA,IACrD,QAAQ,CAAC;AAAA,IACT,cAAc,CAAC;AAAA,IACf,SAAS,CAAC;AAAA,IACV,SAAS,CAAC;AAAA,IACV,QAAQ,CAAC;AAAA,IACT,QAAQ,CAAC,EAAE,MAAM,cAAc,IAAI,cAAc,UAAU,CAAC,CAAC,EAAE,GAAG,IAAI;AAAA,IACtE,gBAAgB,CAAC;AAAA,IACjB,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,OAAO,GAAG,mBAAmB,CAAC,EAAE,EAAE;AAAA,EACvE;AACF;","names":[]}
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ComponentType } from 'react';
2
- import { ResolveContext, ResolveToken, DesignPropValue } from '@contentful/experiences-sdk-core';
2
+ import { ResolveContext, ResolveToken, DesignPropValue, NodeAttribution } from '@contentful/experiences-sdk-core';
3
3
  export { RenderContext, ResolveContext, ResolveToken } from '@contentful/experiences-sdk-core';
4
4
 
5
5
  /**
@@ -19,6 +19,12 @@ interface ContentfulComponent {
19
19
  content: Record<string, unknown>;
20
20
  design: Record<string, DesignPropValue>;
21
21
  resolved?: Record<string, unknown>;
22
+ /**
23
+ * Reportable scopes this node belongs to (Experience, persisted Fragment),
24
+ * resolved from the source map. Absent unless the fetch opted into
25
+ * `extensions.sourceMap` and the node has an id. Plain, serializable data.
26
+ */
27
+ attribution?: NodeAttribution;
22
28
  }
23
29
  /**
24
30
  * Same shape as `ContentfulComponent`, but for an Experience Template node.
@@ -31,6 +37,12 @@ interface ContentfulExperienceTemplate {
31
37
  content: Record<string, unknown>;
32
38
  design: Record<string, DesignPropValue>;
33
39
  resolved?: Record<string, unknown>;
40
+ /**
41
+ * Reportable scopes this node belongs to (Experience, persisted Fragment),
42
+ * resolved from the source map. Absent unless the fetch opted into
43
+ * `extensions.sourceMap` and the node has an id. Plain, serializable data.
44
+ */
45
+ attribution?: NodeAttribution;
34
46
  }
35
47
 
36
48
  /**
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport type {\n DesignPropValue,\n RenderContext,\n ResolveContext,\n ResolveToken,\n} from '@contentful/experiences-sdk-core';\n\n/**\n * Re-exported for ergonomics: customer code authoring `resolveData` doesn't\n * have to dig into the core package for the context type.\n */\nexport type { ResolveContext, ResolveToken };\n\n/**\n * The full Contentful-side payload for a single component instance, exposed\n * via `useContentfulComponent()` to any descendant of a rendered Experience\n * node. Useful for custom design-property resolution outside the SDK's\n * cascade, branching by `componentId` in a generic wrapper, keying\n * analytics on `nodeId`, or rendering a raw-payload panel in preview.\n *\n * Design properties stay in their **raw discriminated form** here (the same\n * shape `ctx.design` carries inside `resolveData`). Token-resolved values are\n * what `useDesignValues()` returns.\n */\nexport interface ContentfulComponent {\n componentId: string;\n nodeId?: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n}\n\n/**\n * Same shape as `ContentfulComponent`, but for an Experience Template node.\n * Exposed via `useContentfulExperienceTemplate()` inside an Experience\n * Template's component tree.\n */\nexport interface ContentfulExperienceTemplate {\n experienceTemplateId: string;\n nodeId?: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n}\n\n/** Render-time experience context, read with `useExperience()`. Declared in core so all adapters share one shape. */\nexport type { RenderContext };\n\n/**\n * Customer-supplied configuration for a single component. The `component`\n * receives the merged props (design + content + resolveData + slots); resolved\n * design values auto-fill matching props and are also readable via\n * `useDesignValues()`. Runtime context and raw payload come from\n * `useExperience()` / `useContentfulComponent()`.\n */\nexport interface ComponentConfig<Props extends object = Record<string, unknown>> {\n /**\n * Lowest-precedence defaults object. Merged in before content / resolveData /\n * slots. Useful for variant fallbacks the editorial layer doesn't always supply.\n */\n defaults?: Partial<Props>;\n /**\n * Optional sync-or-async hook that derives final props from the raw\n * Experience inputs. Runs once during `resolveExperience`, before render.\n */\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n /**\n * The React component to render. Receives the merged props.\n */\n component: ComponentType<Props>;\n}\n\n/**\n * Registry value. Register a bare React component for the common case, or\n * the full `ComponentConfig` shape when you need defaults or a `resolveData`\n * hook.\n *\n * button: Button, // bare\n * header: { component: Header, defaults: {...} }, // with defaults\n * card: defineComponent<CardProps>({ component: Card, resolveData: ... }),\n */\nexport type Registration<Props extends object = Record<string, unknown>> =\n ComponentType<Props> | ComponentConfig<Props>;\n\n/**\n * Customer-supplied configuration for a coded Experience Template. Identical in\n * shape and behavior to `ComponentConfig` — an Experience Template is just a\n * node whose implementation lives in the `experienceTemplates` registry. Its\n * slots arrive as named props (a `content` slot becomes a `content` prop), so\n * there is no `children` special case.\n */\nexport interface ExperienceTemplateConfig<Props extends object = Record<string, unknown>> {\n /**\n * Lowest-precedence defaults object. Merged in before content / resolveData /\n * slots. Useful for variant fallbacks the editorial layer doesn't always supply.\n */\n defaults?: Partial<Props>;\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n component: ComponentType<Props>;\n}\n\n/**\n * Registry value for Experience Templates. Same dual-shape as component\n * registrations.\n */\nexport type ExperienceTemplateRegistration<Props extends object = Record<string, unknown>> =\n ComponentType<Props> | ExperienceTemplateConfig<Props>;\n\n/**\n * Identity helper — returns the config as-is, but narrows the `resolveData`\n * and `component` parameter types to your declared `Props`.\n */\nexport function defineComponent<Props extends object = Record<string, unknown>>(\n config: ComponentConfig<Props>\n): ComponentConfig<Props> {\n return config;\n}\n\n/**\n * Identity helper — returns the Experience Template config as-is, with `Props`\n * narrowing.\n */\nexport function defineExperienceTemplate<Props extends object = Record<string, unknown>>(\n config: ExperienceTemplateConfig<Props>\n): ExperienceTemplateConfig<Props> {\n return config;\n}\n\n/**\n * Component registry — keyed by `componentId` (last slash-segment of\n * `component.sys.urn`). Per-entry prop narrowing happens at\n * `defineComponent<Props>` or at the bare-component's own type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type Components = Record<string, Registration<any>>;\n\n/**\n * Experience Template registry — keyed by `experienceTemplateId` (last\n * slash-segment of an Experience Template node's own\n * `experienceTemplate.sys.urn`). Not read from `payload.sys`; templates are\n * ordinary nodes.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type ExperienceTemplates = Record<string, ExperienceTemplateRegistration<any>>;\n\nexport interface Config {\n components: Components;\n experienceTemplates?: ExperienceTemplates;\n /**\n * Resolves `DesignToken` values to runtime values before they reach a\n * component. Unresolved tokens remain raw and produce a diagnostic. See\n * `ResolveToken` in `@contentful/experiences-sdk-core` for the full contract.\n */\n resolveToken?: ResolveToken;\n}\n\n/**\n * Normalize a registry entry — bare component OR config object — into the\n * common `ComponentConfig` shape used by the renderer.\n *\n * React function components are plain functions; `React.memo` /\n * `React.forwardRef` yield objects carrying a `$$typeof` symbol. Both count\n * as bare components; a config object is discriminated by having a\n * `component` field and no `$$typeof`.\n */\nexport function normalizeComponentRegistration<P extends object>(\n reg: Registration<P>\n): ComponentConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as ComponentConfig<P>;\n }\n return { component: reg as ComponentType<P> };\n}\n\nexport function normalizeExperienceTemplateRegistration<P extends object>(\n reg: ExperienceTemplateRegistration<P>\n): ExperienceTemplateConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as ExperienceTemplateConfig<P>;\n }\n return { component: reg as ComponentType<P> };\n}\n"],"mappings":"AAkHO,SAAS,gBACd,QACwB;AACxB,SAAO;AACT;AAMO,SAAS,yBACd,QACiC;AACjC,SAAO;AACT;AAuCO,SAAS,+BACd,KACoB;AACpB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAwB;AAC9C;AAEO,SAAS,wCACd,KAC6B;AAC7B,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAwB;AAC9C;","names":[]}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ComponentType } from 'react';\n\nimport type {\n DesignPropValue,\n NodeAttribution,\n RenderContext,\n ResolveContext,\n ResolveToken,\n} from '@contentful/experiences-sdk-core';\n\n/**\n * Re-exported for ergonomics: customer code authoring `resolveData` doesn't\n * have to dig into the core package for the context type.\n */\nexport type { ResolveContext, ResolveToken };\n\n/**\n * The full Contentful-side payload for a single component instance, exposed\n * via `useContentfulComponent()` to any descendant of a rendered Experience\n * node. Useful for custom design-property resolution outside the SDK's\n * cascade, branching by `componentId` in a generic wrapper, keying\n * analytics on `nodeId`, or rendering a raw-payload panel in preview.\n *\n * Design properties stay in their **raw discriminated form** here (the same\n * shape `ctx.design` carries inside `resolveData`). Token-resolved values are\n * what `useDesignValues()` returns.\n */\nexport interface ContentfulComponent {\n componentId: string;\n nodeId?: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n /**\n * Reportable scopes this node belongs to (Experience, persisted Fragment),\n * resolved from the source map. Absent unless the fetch opted into\n * `extensions.sourceMap` and the node has an id. Plain, serializable data.\n */\n attribution?: NodeAttribution;\n}\n\n/**\n * Same shape as `ContentfulComponent`, but for an Experience Template node.\n * Exposed via `useContentfulExperienceTemplate()` inside an Experience\n * Template's component tree.\n */\nexport interface ContentfulExperienceTemplate {\n experienceTemplateId: string;\n nodeId?: string;\n content: Record<string, unknown>;\n design: Record<string, DesignPropValue>;\n resolved?: Record<string, unknown>;\n /**\n * Reportable scopes this node belongs to (Experience, persisted Fragment),\n * resolved from the source map. Absent unless the fetch opted into\n * `extensions.sourceMap` and the node has an id. Plain, serializable data.\n */\n attribution?: NodeAttribution;\n}\n\n/** Render-time experience context, read with `useExperience()`. Declared in core so all adapters share one shape. */\nexport type { RenderContext };\n\n/**\n * Customer-supplied configuration for a single component. The `component`\n * receives the merged props (design + content + resolveData + slots); resolved\n * design values auto-fill matching props and are also readable via\n * `useDesignValues()`. Runtime context and raw payload come from\n * `useExperience()` / `useContentfulComponent()`.\n */\nexport interface ComponentConfig<Props extends object = Record<string, unknown>> {\n /**\n * Lowest-precedence defaults object. Merged in before content / resolveData /\n * slots. Useful for variant fallbacks the editorial layer doesn't always supply.\n */\n defaults?: Partial<Props>;\n /**\n * Optional sync-or-async hook that derives final props from the raw\n * Experience inputs. Runs once during `resolveExperience`, before render.\n */\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n /**\n * The React component to render. Receives the merged props.\n */\n component: ComponentType<Props>;\n}\n\n/**\n * Registry value. Register a bare React component for the common case, or\n * the full `ComponentConfig` shape when you need defaults or a `resolveData`\n * hook.\n *\n * button: Button, // bare\n * header: { component: Header, defaults: {...} }, // with defaults\n * card: defineComponent<CardProps>({ component: Card, resolveData: ... }),\n */\nexport type Registration<Props extends object = Record<string, unknown>> =\n ComponentType<Props> | ComponentConfig<Props>;\n\n/**\n * Customer-supplied configuration for a coded Experience Template. Identical in\n * shape and behavior to `ComponentConfig` — an Experience Template is just a\n * node whose implementation lives in the `experienceTemplates` registry. Its\n * slots arrive as named props (a `content` slot becomes a `content` prop), so\n * there is no `children` special case.\n */\nexport interface ExperienceTemplateConfig<Props extends object = Record<string, unknown>> {\n /**\n * Lowest-precedence defaults object. Merged in before content / resolveData /\n * slots. Useful for variant fallbacks the editorial layer doesn't always supply.\n */\n defaults?: Partial<Props>;\n resolveData?: (ctx: ResolveContext) => Partial<Props> | Promise<Partial<Props>>;\n component: ComponentType<Props>;\n}\n\n/**\n * Registry value for Experience Templates. Same dual-shape as component\n * registrations.\n */\nexport type ExperienceTemplateRegistration<Props extends object = Record<string, unknown>> =\n ComponentType<Props> | ExperienceTemplateConfig<Props>;\n\n/**\n * Identity helper — returns the config as-is, but narrows the `resolveData`\n * and `component` parameter types to your declared `Props`.\n */\nexport function defineComponent<Props extends object = Record<string, unknown>>(\n config: ComponentConfig<Props>\n): ComponentConfig<Props> {\n return config;\n}\n\n/**\n * Identity helper — returns the Experience Template config as-is, with `Props`\n * narrowing.\n */\nexport function defineExperienceTemplate<Props extends object = Record<string, unknown>>(\n config: ExperienceTemplateConfig<Props>\n): ExperienceTemplateConfig<Props> {\n return config;\n}\n\n/**\n * Component registry — keyed by `componentId` (last slash-segment of\n * `component.sys.urn`). Per-entry prop narrowing happens at\n * `defineComponent<Props>` or at the bare-component's own type.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type Components = Record<string, Registration<any>>;\n\n/**\n * Experience Template registry — keyed by `experienceTemplateId` (last\n * slash-segment of an Experience Template node's own\n * `experienceTemplate.sys.urn`). Not read from `payload.sys`; templates are\n * ordinary nodes.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- per-entry narrowing happens at the registration author's call site.\nexport type ExperienceTemplates = Record<string, ExperienceTemplateRegistration<any>>;\n\nexport interface Config {\n components: Components;\n experienceTemplates?: ExperienceTemplates;\n /**\n * Resolves `DesignToken` values to runtime values before they reach a\n * component. Unresolved tokens remain raw and produce a diagnostic. See\n * `ResolveToken` in `@contentful/experiences-sdk-core` for the full contract.\n */\n resolveToken?: ResolveToken;\n}\n\n/**\n * Normalize a registry entry — bare component OR config object — into the\n * common `ComponentConfig` shape used by the renderer.\n *\n * React function components are plain functions; `React.memo` /\n * `React.forwardRef` yield objects carrying a `$$typeof` symbol. Both count\n * as bare components; a config object is discriminated by having a\n * `component` field and no `$$typeof`.\n */\nexport function normalizeComponentRegistration<P extends object>(\n reg: Registration<P>\n): ComponentConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as ComponentConfig<P>;\n }\n return { component: reg as ComponentType<P> };\n}\n\nexport function normalizeExperienceTemplateRegistration<P extends object>(\n reg: ExperienceTemplateRegistration<P>\n): ExperienceTemplateConfig<P> {\n if (typeof reg === 'object' && reg !== null && !('$$typeof' in reg) && 'component' in reg) {\n return reg as ExperienceTemplateConfig<P>;\n }\n return { component: reg as ComponentType<P> };\n}\n"],"mappings":"AA+HO,SAAS,gBACd,QACwB;AACxB,SAAO;AACT;AAMO,SAAS,yBACd,QACiC;AACjC,SAAO;AACT;AAuCO,SAAS,+BACd,KACoB;AACpB,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAwB;AAC9C;AAEO,SAAS,wCACd,KAC6B;AAC7B,MAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,EAAE,cAAc,QAAQ,eAAe,KAAK;AACzF,WAAO;AAAA,EACT;AACA,SAAO,EAAE,WAAW,IAAwB;AAC9C;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experiences-react",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
4
4
  "description": "React adapter for Contentful Experiences — Server and Client renderers over a PortableRenderPlan",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,11 +30,11 @@
30
30
  "directory": "packages/adapter-react"
31
31
  },
32
32
  "dependencies": {
33
- "@contentful/experiences-client": "0.9.3",
34
- "@contentful/experiences-runtime": "0.0.2",
35
- "@contentful/experiences-sdk-core": "0.12.2",
36
- "@contentful/experiences-design": "0.12.1",
37
- "@contentful/experiences-live-preview": "0.6.3"
33
+ "@contentful/experiences-client": "0.9.4",
34
+ "@contentful/experiences-runtime": "0.0.3",
35
+ "@contentful/experiences-sdk-core": "0.12.3",
36
+ "@contentful/experiences-design": "0.12.2",
37
+ "@contentful/experiences-live-preview": "0.6.4"
38
38
  },
39
39
  "devDependencies": {
40
40
  "jsdom": "^30.0.1"