@contentful/experiences-svelte 0.3.7 → 0.4.0
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 +42 -0
- package/README.md +1 -1
- package/dist/ClientExperienceRenderer.svelte +11 -6
- package/dist/ClientExperienceRenderer.svelte.d.ts.map +1 -1
- package/dist/DebugExperience.svelte +65 -0
- package/dist/DebugExperience.svelte.d.ts +5 -0
- package/dist/DebugExperience.svelte.d.ts.map +1 -0
- package/dist/DebugExperience.test.d.ts +2 -0
- package/dist/DebugExperience.test.d.ts.map +1 -0
- package/dist/DebugExperience.test.js +59 -0
- package/dist/MissingComponent.svelte +11 -4
- package/dist/MissingComponent.svelte.d.ts.map +1 -1
- package/dist/ServerExperienceRenderer.svelte +9 -4
- package/dist/ServerExperienceRenderer.svelte.d.ts.map +1 -1
- package/dist/component-props.d.ts +18 -2
- package/dist/component-props.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/server-renderer.test.js +25 -4
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
## 0.4.0 (2026-08-05)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- ⚠️ replace isPreview with top-level debug mode [AIS-243] ([#98](https://github.com/contentful/experiences/pull/98))
|
|
6
|
+
|
|
7
|
+
### ⚠️ Breaking Changes
|
|
8
|
+
|
|
9
|
+
- replace isPreview with top-level debug mode [AIS-243] ([#98](https://github.com/contentful/experiences/pull/98))
|
|
10
|
+
`isPreview` is removed from the render context and the nested
|
|
11
|
+
`context` option is gone. Pass `metadata` and `debug` as top-level options on
|
|
12
|
+
`fetchExperience`/`resolveExperience` and as props on the renderers.
|
|
13
|
+
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
14
|
+
* refactor: derive metadata once in sveltekit apps
|
|
15
|
+
Extract a single `metadata` object in the loaders and thread it through
|
|
16
|
+
both fetchExperience and the renderer, instead of duplicating the
|
|
17
|
+
`{ slug }` literal in +page.svelte. Addresses PR review feedback.
|
|
18
|
+
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
19
|
+
* refactor: mount DebugExperience above the tree and aggregate resolveData timing
|
|
20
|
+
Move the auto-mounted <DebugExperience> panel above the rendered tree in
|
|
21
|
+
both React and Svelte renderers (client + server) so the debug dump is
|
|
22
|
+
visible without scrolling past the experience. Update the doc comments
|
|
23
|
+
and README to match.
|
|
24
|
+
Replace per-node resolveData timing lines with a single aggregate span
|
|
25
|
+
over the whole fan-out — keeps the timing signal without emitting a log
|
|
26
|
+
line per node, which gets noisy on large trees.
|
|
27
|
+
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
28
|
+
|
|
29
|
+
### 🧱 Updated Dependencies
|
|
30
|
+
|
|
31
|
+
- Updated client to 0.2.0
|
|
32
|
+
- Updated design to 0.6.0
|
|
33
|
+
- Updated core to 0.6.0
|
|
34
|
+
|
|
35
|
+
## 0.3.8 (2026-08-04)
|
|
36
|
+
|
|
37
|
+
### 🧱 Updated Dependencies
|
|
38
|
+
|
|
39
|
+
- Updated client to 0.1.8
|
|
40
|
+
- Updated design to 0.5.6
|
|
41
|
+
- Updated core to 0.5.6
|
|
42
|
+
|
|
1
43
|
## 0.3.7 (2026-08-03)
|
|
2
44
|
|
|
3
45
|
### 🧱 Updated Dependencies
|
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ useActiveViewport; // Rune-backed reactive object; you'll rarely need it directl
|
|
|
44
44
|
```ts
|
|
45
45
|
getDesignValues<T>(); // Resolved design values for the current node; read in a $derived to stay reactive
|
|
46
46
|
toCss(design, options?); // Turns a design record into a plain style object, keeping only real CSS keys
|
|
47
|
-
getExperience(); // RenderContext:
|
|
47
|
+
getExperience(); // RenderContext: debug, metadata, viewports, activeViewport
|
|
48
48
|
getContentfulComponent(); // Raw payload for the enclosing node (or undefined)
|
|
49
49
|
getContentfulTemplate(); // Same, for the page-level template
|
|
50
50
|
type ToCssOptions;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
<script lang="ts">
|
|
12
12
|
import type { ExperienceContext, ViewportDef } from '@contentful/experiences-sdk-core';
|
|
13
13
|
|
|
14
|
+
import DebugExperience from './DebugExperience.svelte';
|
|
14
15
|
import MissingComponent from './MissingComponent.svelte';
|
|
15
16
|
import NodesRenderer from './NodesRenderer.svelte';
|
|
16
17
|
import WrapWithTemplate from './WrapWithTemplate.svelte';
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
import { useActiveViewport } from './use-active-viewport.svelte.js';
|
|
21
22
|
|
|
22
23
|
const DEFAULT_CONTEXT: ExperienceContext = {
|
|
23
|
-
|
|
24
|
+
debug: false,
|
|
24
25
|
metadata: {},
|
|
25
26
|
viewports: [],
|
|
26
27
|
};
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
experience,
|
|
37
38
|
config,
|
|
38
39
|
initialViewportId,
|
|
39
|
-
|
|
40
|
+
metadata,
|
|
41
|
+
debug = false,
|
|
40
42
|
renderUnknown = MissingComponent,
|
|
41
43
|
}: ClientExperienceRendererProps = $props();
|
|
42
44
|
|
|
@@ -47,8 +49,8 @@
|
|
|
47
49
|
// reactive across viewport changes. The fields update in an $effect below.
|
|
48
50
|
const liveContext = $state<RenderContext>({
|
|
49
51
|
...DEFAULT_CONTEXT,
|
|
50
|
-
|
|
51
|
-
metadata: { ...DEFAULT_CONTEXT.metadata, ...(
|
|
52
|
+
debug,
|
|
53
|
+
metadata: { ...DEFAULT_CONTEXT.metadata, ...(metadata ?? {}) },
|
|
52
54
|
viewports: experience?.viewports ?? [],
|
|
53
55
|
activeViewport: experience?.viewports[0] ?? FALLBACK_VIEWPORT,
|
|
54
56
|
activeViewportIndex: 0,
|
|
@@ -62,12 +64,15 @@
|
|
|
62
64
|
liveContext.viewports = experience.viewports;
|
|
63
65
|
liveContext.activeViewport = experience.viewports[idx] ?? FALLBACK_VIEWPORT;
|
|
64
66
|
liveContext.activeViewportIndex = idx;
|
|
65
|
-
liveContext.
|
|
66
|
-
liveContext.metadata = { ...DEFAULT_CONTEXT.metadata, ...(
|
|
67
|
+
liveContext.debug = debug;
|
|
68
|
+
liveContext.metadata = { ...DEFAULT_CONTEXT.metadata, ...(metadata ?? {}) };
|
|
67
69
|
});
|
|
68
70
|
</script>
|
|
69
71
|
|
|
70
72
|
{#if experience}
|
|
73
|
+
{#if debug}
|
|
74
|
+
<DebugExperience {experience} />
|
|
75
|
+
{/if}
|
|
71
76
|
<WrapWithTemplate template={experience.template} {config} experience={liveContext}>
|
|
72
77
|
{#snippet children()}
|
|
73
78
|
<NodesRenderer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ClientExperienceRenderer.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ClientExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ClientExperienceRenderer.svelte.ts"],"names":[],"mappings":"AAyFA,QAAA,MAAM,wBAAwB,kGAAwC,CAAC;AACvE,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC5E,eAAe,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* First-party debug panel. Renders the resolved Experience plan as pretty
|
|
3
|
+
* JSON so a customer can see exactly what the SDK interpreted from the payload
|
|
4
|
+
* — node tree, registrations, resolved props, viewports.
|
|
5
|
+
*
|
|
6
|
+
* Two ways to use it:
|
|
7
|
+
* - Auto-mounted by the renderers when `debug` is on (above the tree).
|
|
8
|
+
* - Mounted manually anywhere: `<DebugExperience experience={plan} />`.
|
|
9
|
+
*
|
|
10
|
+
* v1 is just the JSON dump wrapped in a native <details> so it collapses
|
|
11
|
+
* without any extra JS. Room to grow into a node-tree explorer, viewport
|
|
12
|
+
* indicator, and resolveData timing panel — kept deliberately small for now.
|
|
13
|
+
-->
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import type { DebugExperienceProps } from './component-props.js';
|
|
16
|
+
|
|
17
|
+
let { experience, defaultOpen = false }: DebugExperienceProps = $props();
|
|
18
|
+
|
|
19
|
+
const nodeCount = $derived(experience.nodes.length);
|
|
20
|
+
const summary = $derived(
|
|
21
|
+
`Experience debug — ${nodeCount} top-level node${nodeCount === 1 ? '' : 's'}${
|
|
22
|
+
experience.template ? `, template: ${experience.template.templateId}` : ''
|
|
23
|
+
}`
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// JSON.stringify with a circular-reference guard: a customer's resolveData
|
|
27
|
+
// could stash a non-serializable value on props.resolved — degrade to a
|
|
28
|
+
// placeholder rather than throwing inside a debug panel.
|
|
29
|
+
function safeStringify(value: unknown): string {
|
|
30
|
+
const seen = new WeakSet<object>();
|
|
31
|
+
try {
|
|
32
|
+
return JSON.stringify(
|
|
33
|
+
value,
|
|
34
|
+
(_key, val) => {
|
|
35
|
+
if (typeof val === 'object' && val !== null) {
|
|
36
|
+
if (seen.has(val)) return '[Circular]';
|
|
37
|
+
seen.add(val);
|
|
38
|
+
}
|
|
39
|
+
if (typeof val === 'function') return `[Function ${val.name || 'anonymous'}]`;
|
|
40
|
+
if (typeof val === 'undefined') return '[undefined]';
|
|
41
|
+
return val;
|
|
42
|
+
},
|
|
43
|
+
2
|
|
44
|
+
);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return `[DebugExperience: could not serialize plan — ${(err as Error).message}]`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const json = $derived(safeStringify(experience));
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<details
|
|
54
|
+
open={defaultOpen}
|
|
55
|
+
data-experiences-debug
|
|
56
|
+
style="margin: 1rem 0; border: 1px solid #6b7280; border-radius: 6px; background: #0b1021; color: #e2e8f0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.75rem; overflow: hidden;"
|
|
57
|
+
>
|
|
58
|
+
<summary
|
|
59
|
+
style="cursor: pointer; padding: 0.5rem 0.75rem; background: #111827; user-select: none;"
|
|
60
|
+
>
|
|
61
|
+
{summary}
|
|
62
|
+
</summary>
|
|
63
|
+
<pre
|
|
64
|
+
style="margin: 0; padding: 0.75rem; overflow: auto; max-height: 32rem; white-space: pre-wrap; word-break: break-word;">{json}</pre>
|
|
65
|
+
</details>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DebugExperienceProps } from './component-props.js';
|
|
2
|
+
declare const DebugExperience: import("svelte").Component<DebugExperienceProps, {}, "">;
|
|
3
|
+
type DebugExperience = ReturnType<typeof DebugExperience>;
|
|
4
|
+
export default DebugExperience;
|
|
5
|
+
//# sourceMappingURL=DebugExperience.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DebugExperience.svelte.d.ts","sourceRoot":"","sources":["../src/DebugExperience.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAmDjE,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DebugExperience.test.d.ts","sourceRoot":"","sources":["../src/DebugExperience.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { render } from '@testing-library/svelte';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import DebugExperience from './DebugExperience.svelte';
|
|
4
|
+
const emptyPlan = { nodes: [], viewports: [] };
|
|
5
|
+
function node(componentTypeId, content = {}) {
|
|
6
|
+
return {
|
|
7
|
+
registration: { componentTypeId },
|
|
8
|
+
props: { content, design: {} },
|
|
9
|
+
slots: {},
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
describe('DebugExperience.svelte', () => {
|
|
13
|
+
it('renders a collapsible details panel with the debug marker, collapsed by default', () => {
|
|
14
|
+
const { container } = render(DebugExperience, { props: { experience: emptyPlan } });
|
|
15
|
+
const details = container.querySelector('details[data-experiences-debug]');
|
|
16
|
+
expect(details).not.toBeNull();
|
|
17
|
+
expect(details.open).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it('expands on first paint when defaultOpen is set', () => {
|
|
20
|
+
const { container } = render(DebugExperience, {
|
|
21
|
+
props: { experience: emptyPlan, defaultOpen: true },
|
|
22
|
+
});
|
|
23
|
+
expect(container.querySelector('details').open).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it('summarizes the node count, pluralizing correctly', () => {
|
|
26
|
+
const { container: zero } = render(DebugExperience, { props: { experience: emptyPlan } });
|
|
27
|
+
expect(zero.innerHTML).toContain('Experience debug — 0 top-level nodes');
|
|
28
|
+
const one = { viewports: [], nodes: [node('button')] };
|
|
29
|
+
const { container } = render(DebugExperience, { props: { experience: one } });
|
|
30
|
+
expect(container.innerHTML).toContain('Experience debug — 1 top-level node');
|
|
31
|
+
});
|
|
32
|
+
it('names the template in the summary when present', () => {
|
|
33
|
+
const plan = {
|
|
34
|
+
viewports: [],
|
|
35
|
+
nodes: [],
|
|
36
|
+
template: { templateId: 'page', props: { content: {}, design: {} } },
|
|
37
|
+
};
|
|
38
|
+
const { container } = render(DebugExperience, { props: { experience: plan } });
|
|
39
|
+
expect(container.innerHTML).toContain('template: page');
|
|
40
|
+
});
|
|
41
|
+
it('dumps the plan as pretty JSON', () => {
|
|
42
|
+
const plan = {
|
|
43
|
+
viewports: [],
|
|
44
|
+
nodes: [node('button', { label: 'Go' })],
|
|
45
|
+
};
|
|
46
|
+
const { container } = render(DebugExperience, { props: { experience: plan } });
|
|
47
|
+
expect(container.innerHTML).toContain('componentTypeId');
|
|
48
|
+
expect(container.innerHTML).toContain('button');
|
|
49
|
+
expect(container.innerHTML).toContain('Go');
|
|
50
|
+
});
|
|
51
|
+
it('degrades a circular reference to a placeholder instead of throwing', () => {
|
|
52
|
+
const n = node('button');
|
|
53
|
+
n.props.resolved = { self: n.props };
|
|
54
|
+
const plan = { viewports: [], nodes: [n] };
|
|
55
|
+
expect(() => render(DebugExperience, { props: { experience: plan } })).not.toThrow();
|
|
56
|
+
const { container } = render(DebugExperience, { props: { experience: plan } });
|
|
57
|
+
expect(container.innerHTML).toContain('[Circular]');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Default fallback rendered when an instance references a component type
|
|
3
3
|
* not present in the Config.
|
|
4
4
|
*
|
|
5
|
-
* Behavior: a visible
|
|
5
|
+
* Behavior: a visible debug box when `debug` is on, silent null otherwise.
|
|
6
|
+
* The console.warn fires either way so the miss is never fully silent.
|
|
6
7
|
* Customers can override per-render via <ServerExperienceRenderer renderUnknown=... />.
|
|
7
8
|
-->
|
|
8
9
|
<script lang="ts">
|
|
@@ -22,14 +23,20 @@
|
|
|
22
23
|
});
|
|
23
24
|
</script>
|
|
24
25
|
|
|
25
|
-
{#if experience.
|
|
26
|
+
{#if experience.debug}
|
|
26
27
|
<div
|
|
27
|
-
style="border: 2px solid red; padding: 1rem; color: red; background: #fff;"
|
|
28
|
+
style="border: 2px solid red; padding: 1rem; color: red; background: #fff; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.8125rem;"
|
|
28
29
|
data-experiences-missing={componentTypeId}
|
|
29
30
|
>
|
|
30
31
|
<strong>Missing component ‘{componentTypeId}’</strong>
|
|
31
|
-
<p>
|
|
32
|
+
<p style="margin: 0.5rem 0;">
|
|
32
33
|
This component is referenced by the Experience payload but is not registered in the Config.
|
|
34
|
+
Register it under this key in your <code>components</code> map:
|
|
33
35
|
</p>
|
|
36
|
+
<pre style="margin: 0; white-space: pre-wrap;">{JSON.stringify(
|
|
37
|
+
{ componentTypeId, nodeId: nodeId ?? null },
|
|
38
|
+
null,
|
|
39
|
+
2
|
|
40
|
+
)}</pre>
|
|
34
41
|
</div>
|
|
35
42
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MissingComponent.svelte.d.ts","sourceRoot":"","sources":["../src/MissingComponent.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"MissingComponent.svelte.d.ts","sourceRoot":"","sources":["../src/MissingComponent.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAsClE,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { ExperienceContext, ViewportDef } from '@contentful/experiences-sdk-core';
|
|
12
12
|
import { getViewportIndex } from '@contentful/experiences-design';
|
|
13
13
|
|
|
14
|
+
import DebugExperience from './DebugExperience.svelte';
|
|
14
15
|
import MissingComponent from './MissingComponent.svelte';
|
|
15
16
|
import NodesRenderer from './NodesRenderer.svelte';
|
|
16
17
|
import WrapWithTemplate from './WrapWithTemplate.svelte';
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
import type { RenderContext } from './types.js';
|
|
20
21
|
|
|
21
22
|
const DEFAULT_CONTEXT: ExperienceContext = {
|
|
22
|
-
|
|
23
|
+
debug: false,
|
|
23
24
|
metadata: {},
|
|
24
25
|
viewports: [],
|
|
25
26
|
};
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
experience,
|
|
36
37
|
config,
|
|
37
38
|
initialViewportId,
|
|
38
|
-
|
|
39
|
+
metadata,
|
|
40
|
+
debug = false,
|
|
39
41
|
renderUnknown = MissingComponent,
|
|
40
42
|
}: ServerExperienceRendererProps = $props();
|
|
41
43
|
|
|
@@ -44,8 +46,8 @@
|
|
|
44
46
|
const idx = experience ? getViewportIndex(experience.viewports, initialViewportId) : 0;
|
|
45
47
|
return {
|
|
46
48
|
...DEFAULT_CONTEXT,
|
|
47
|
-
|
|
48
|
-
metadata: { ...DEFAULT_CONTEXT.metadata, ...(
|
|
49
|
+
debug,
|
|
50
|
+
metadata: { ...DEFAULT_CONTEXT.metadata, ...(metadata ?? {}) },
|
|
49
51
|
viewports,
|
|
50
52
|
activeViewport: experience?.viewports[idx] ?? FALLBACK_VIEWPORT,
|
|
51
53
|
activeViewportIndex: idx,
|
|
@@ -57,6 +59,9 @@
|
|
|
57
59
|
</script>
|
|
58
60
|
|
|
59
61
|
{#if experience}
|
|
62
|
+
{#if debug}
|
|
63
|
+
<DebugExperience {experience} />
|
|
64
|
+
{/if}
|
|
60
65
|
<WrapWithTemplate template={experience.template} {config} experience={renderContext}>
|
|
61
66
|
{#snippet children()}
|
|
62
67
|
<NodesRenderer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ServerExperienceRenderer.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ServerExperienceRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/ServerExperienceRenderer.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAqE1E,QAAA,MAAM,wBAAwB,mEAAwC,CAAC;AACvE,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC5E,eAAe,wBAAwB,CAAC"}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import type { Component } from 'svelte';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PortableRenderPlan } from '@contentful/experiences-sdk-core';
|
|
3
3
|
import type { Config } from './types.js';
|
|
4
4
|
export interface ServerExperienceRendererProps {
|
|
5
5
|
experience: PortableRenderPlan | null | undefined;
|
|
6
6
|
config: Config;
|
|
7
7
|
initialViewportId?: string;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Arbitrary per-render metadata, readable by descendants via
|
|
10
|
+
* `getExperience()` and by resolvers via `ctx.experience.metadata`.
|
|
11
|
+
*/
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Observability switch. When on: renders the visible missing-component box,
|
|
15
|
+
* turns the default `renderUnknown` fallback into the debug component, and
|
|
16
|
+
* auto-mounts `<DebugExperience>` after the tree.
|
|
17
|
+
*/
|
|
18
|
+
debug?: boolean;
|
|
9
19
|
renderUnknown?: RenderUnknown;
|
|
10
20
|
}
|
|
11
21
|
export type ClientExperienceRendererProps = ServerExperienceRendererProps;
|
|
@@ -15,4 +25,10 @@ export interface MissingComponentProps {
|
|
|
15
25
|
nodeId?: string;
|
|
16
26
|
}
|
|
17
27
|
export type RenderUnknown = Component<MissingComponentProps>;
|
|
28
|
+
export interface DebugExperienceProps {
|
|
29
|
+
/** The resolved plan to inspect (what a renderer receives as `experience`). */
|
|
30
|
+
experience: PortableRenderPlan;
|
|
31
|
+
/** Start expanded. Defaults to collapsed to stay out of the way. */
|
|
32
|
+
defaultOpen?: boolean;
|
|
33
|
+
}
|
|
18
34
|
//# sourceMappingURL=component-props.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-props.d.ts","sourceRoot":"","sources":["../src/component-props.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"component-props.d.ts","sourceRoot":"","sources":["../src/component-props.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAE3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,6BAA6B;IAC5C,UAAU,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,MAAM,6BAA6B,GAAG,6BAA6B,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAAC;AAE7D,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,UAAU,EAAE,kBAAkB,CAAC;IAC/B,oEAAoE;IACpE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { default as ClientExperienceRenderer } from './ClientExperienceRenderer.
|
|
|
2
2
|
export { default as ExperienceRenderer } from './ClientExperienceRenderer.svelte';
|
|
3
3
|
export { default as ServerExperienceRenderer } from './ServerExperienceRenderer.svelte';
|
|
4
4
|
export { default as MissingComponent } from './MissingComponent.svelte';
|
|
5
|
+
export { default as DebugExperience } from './DebugExperience.svelte';
|
|
5
6
|
export { default as NodesRenderer } from './NodesRenderer.svelte';
|
|
6
7
|
export { useActiveViewport } from './use-active-viewport.svelte.js';
|
|
7
8
|
export type { UseActiveViewportResult } from './use-active-viewport.svelte.js';
|
|
@@ -9,7 +10,7 @@ export { getExperience, getContentfulComponent, getContentfulTemplate } from './
|
|
|
9
10
|
export { getDesignValues } from './get-design-values.js';
|
|
10
11
|
export { toCss } from './design-utils.js';
|
|
11
12
|
export type { ToCssOptions } from './design-utils.js';
|
|
12
|
-
export type { ClientExperienceRendererProps, ClientExperienceRendererProps as ExperienceRendererProps, MissingComponentProps, RenderUnknown, ServerExperienceRendererProps, } from './component-props.js';
|
|
13
|
+
export type { ClientExperienceRendererProps, ClientExperienceRendererProps as ExperienceRendererProps, DebugExperienceProps, MissingComponentProps, RenderUnknown, ServerExperienceRendererProps, } from './component-props.js';
|
|
13
14
|
export { defineComponent, defineTemplate, normalizeComponentRegistration, normalizeTemplateRegistration, } from './types.js';
|
|
14
15
|
export type { ComponentConfig, Components, Config, ContentfulComponent, ContentfulTemplate, Registration, RenderContext, ResolveToken, TemplateConfig, TemplateRegistration, Templates, } from './types.js';
|
|
15
16
|
export { resolveExperience } from '@contentful/experiences-sdk-core';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAItE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG/E,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,YAAY,EACV,6BAA6B,EAC7B,6BAA6B,IAAI,uBAAuB,EACxD,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,cAAc,EACd,8BAA8B,EAC9B,6BAA6B,GAC9B,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAGjG,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,WAAW,GACZ,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,QAAQ,EACR,eAAe,GAChB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,sBAAsB,EACtB,4BAA4B,EAC5B,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GACf,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export { default as ClientExperienceRenderer } from './ClientExperienceRenderer.
|
|
|
16
16
|
export { default as ExperienceRenderer } from './ClientExperienceRenderer.svelte';
|
|
17
17
|
export { default as ServerExperienceRenderer } from './ServerExperienceRenderer.svelte';
|
|
18
18
|
export { default as MissingComponent } from './MissingComponent.svelte';
|
|
19
|
+
export { default as DebugExperience } from './DebugExperience.svelte';
|
|
19
20
|
// Exposed so advanced customers can render non-`children` slots manually:
|
|
20
21
|
// e.g. `<NodesRenderer nodes={contentful.slots.header as PortableRenderNode[]} ... />`.
|
|
21
22
|
export { default as NodesRenderer } from './NodesRenderer.svelte';
|
|
@@ -146,7 +146,7 @@ describe('ServerExperienceRenderer', () => {
|
|
|
146
146
|
expect(ctx.activeViewportIndex).toBe(2);
|
|
147
147
|
expect(ctx.activeViewport).toBe(VIEWPORTS[2]);
|
|
148
148
|
});
|
|
149
|
-
it('renders missing-component fallback in
|
|
149
|
+
it('renders missing-component fallback in debug mode', () => {
|
|
150
150
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
151
151
|
const justContainer = {
|
|
152
152
|
components: { 'contentful-container': ContainerFixture },
|
|
@@ -171,20 +171,41 @@ describe('ServerExperienceRenderer', () => {
|
|
|
171
171
|
},
|
|
172
172
|
],
|
|
173
173
|
};
|
|
174
|
-
const { container:
|
|
174
|
+
const { container: debugContainer } = render(ServerExperienceRenderer, {
|
|
175
175
|
props: {
|
|
176
176
|
experience: planWithMissing,
|
|
177
177
|
config: justContainer,
|
|
178
|
-
|
|
178
|
+
debug: true,
|
|
179
179
|
},
|
|
180
180
|
});
|
|
181
|
-
expect(
|
|
181
|
+
expect(debugContainer.innerHTML).toContain('data-experiences-missing="NotRegistered"');
|
|
182
182
|
const { container: prodContainer } = render(ServerExperienceRenderer, {
|
|
183
183
|
props: { experience: planWithMissing, config: justContainer },
|
|
184
184
|
});
|
|
185
185
|
expect(prodContainer.innerHTML).not.toContain('data-experiences-missing');
|
|
186
186
|
warn.mockRestore();
|
|
187
187
|
});
|
|
188
|
+
it('auto-mounts DebugExperience only when debug is on', async () => {
|
|
189
|
+
const captureConfig = { components: { capture: CapturingComponent } };
|
|
190
|
+
const plan = await resolveExperience({ viewports: VIEWPORTS, nodes: [componentNode('capture', { id: 'c' })] }, captureConfig);
|
|
191
|
+
const { container: off } = render(ServerExperienceRenderer, {
|
|
192
|
+
props: { experience: plan, config: captureConfig },
|
|
193
|
+
});
|
|
194
|
+
expect(off.innerHTML).not.toContain('data-experiences-debug');
|
|
195
|
+
const { container: on } = render(ServerExperienceRenderer, {
|
|
196
|
+
props: { experience: plan, config: captureConfig, debug: true },
|
|
197
|
+
});
|
|
198
|
+
expect(on.innerHTML).toContain('data-experiences-debug');
|
|
199
|
+
expect(on.innerHTML).toContain('Experience debug');
|
|
200
|
+
});
|
|
201
|
+
it('threads top-level metadata into getExperience()', async () => {
|
|
202
|
+
const captureConfig = { components: { capture: CapturingComponent } };
|
|
203
|
+
const plan = await resolveExperience({ viewports: VIEWPORTS, nodes: [componentNode('capture', { id: 'c' })] }, captureConfig);
|
|
204
|
+
render(ServerExperienceRenderer, {
|
|
205
|
+
props: { experience: plan, config: captureConfig, metadata: { slug: 'home' } },
|
|
206
|
+
});
|
|
207
|
+
expect(captureSink[0].experience.metadata).toEqual({ slug: 'home' });
|
|
208
|
+
});
|
|
188
209
|
it('merges defaults beneath content (content wins)', async () => {
|
|
189
210
|
const itemConfig = {
|
|
190
211
|
components: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experiences-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Svelte adapter for Contentful Experiences — Server and Client renderers over a PortableRenderPlan",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"check": "svelte-check --tsconfig ./tsconfig.json"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@contentful/experiences-client": "0.
|
|
39
|
-
"@contentful/experiences-sdk-core": "0.
|
|
40
|
-
"@contentful/experiences-design": "0.
|
|
38
|
+
"@contentful/experiences-client": "0.2.0",
|
|
39
|
+
"@contentful/experiences-sdk-core": "0.6.0",
|
|
40
|
+
"@contentful/experiences-design": "0.6.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"svelte": "^5.55.7"
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@testing-library/svelte": "^5.4.2",
|
|
50
50
|
"jsdom": "^29.1.1",
|
|
51
51
|
"publint": "^0.3.22",
|
|
52
|
-
"svelte": "^5.
|
|
52
|
+
"svelte": "^5.56.8",
|
|
53
53
|
"svelte-check": "^4.7.2"
|
|
54
54
|
}
|
|
55
55
|
}
|