@contentful/experiences-svelte 0.5.10 → 0.5.12
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 +20 -0
- package/dist/ClientExperienceRenderer.svelte +34 -3
- package/dist/ClientExperienceRenderer.svelte.d.ts.map +1 -1
- package/dist/ComponentError.svelte +35 -0
- package/dist/ComponentError.svelte.d.ts +5 -0
- package/dist/ComponentError.svelte.d.ts.map +1 -0
- package/dist/DebugExperience.svelte +22 -3
- package/dist/DebugExperience.svelte.d.ts.map +1 -1
- package/dist/DebugExperience.test.js +62 -2
- package/dist/NodeRenderer.svelte +62 -6
- package/dist/NodeRenderer.svelte.d.ts +3 -1
- package/dist/NodeRenderer.svelte.d.ts.map +1 -1
- package/dist/NodeRenderer.test.d.ts +2 -0
- package/dist/NodeRenderer.test.d.ts.map +1 -0
- package/dist/NodeRenderer.test.js +124 -0
- package/dist/NodesRenderer.svelte +48 -5
- package/dist/NodesRenderer.svelte.d.ts +3 -1
- package/dist/NodesRenderer.svelte.d.ts.map +1 -1
- package/dist/ServerExperienceRenderer.svelte +21 -3
- package/dist/ServerExperienceRenderer.svelte.d.ts.map +1 -1
- package/dist/component-props.d.ts +28 -0
- package/dist/component-props.d.ts.map +1 -1
- package/dist/debug-panel-coverage.ssr.test.d.ts +2 -0
- package/dist/debug-panel-coverage.ssr.test.d.ts.map +1 -0
- package/dist/debug-panel-coverage.ssr.test.js +61 -0
- package/dist/debug-panel-coverage.test.d.ts +2 -0
- package/dist/debug-panel-coverage.test.d.ts.map +1 -0
- package/dist/debug-panel-coverage.test.js +229 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/nodes-renderer.ssr.test.js +49 -0
- package/dist/server-renderer.test.js +5 -3
- package/dist/test-fixtures/BrokenFixture.svelte +4 -0
- package/dist/test-fixtures/BrokenFixture.svelte.d.ts +19 -0
- package/dist/test-fixtures/BrokenFixture.svelte.d.ts.map +1 -0
- package/package.json +4 -4
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { PortableRenderNode } from '@contentful/experiences-sdk-core';
|
|
2
|
-
import type { RenderUnknown } from './component-props.js';
|
|
2
|
+
import type { DiagnosticReporter, RenderError, RenderUnknown } from './component-props.js';
|
|
3
3
|
import type { Config, RenderContext } from './types.js';
|
|
4
4
|
interface NodesRendererProps {
|
|
5
5
|
nodes: PortableRenderNode[];
|
|
6
6
|
config: Config;
|
|
7
7
|
experience: RenderContext;
|
|
8
8
|
renderUnknown: RenderUnknown;
|
|
9
|
+
renderError: RenderError;
|
|
10
|
+
onDiagnostic: DiagnosticReporter;
|
|
9
11
|
}
|
|
10
12
|
declare const NodesRenderer: import("svelte").Component<NodesRendererProps, {}, "">;
|
|
11
13
|
type NodesRenderer = ReturnType<typeof NodesRenderer>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodesRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/NodesRenderer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAG3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"NodesRenderer.svelte.d.ts","sourceRoot":"","sources":["../src/NodesRenderer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAG3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGtD,UAAU,kBAAkB;IAC1B,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,aAAa,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,kBAAkB,CAAC;CAClC;AA6FH,QAAA,MAAM,aAAa,wDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,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 ComponentError from './ComponentError.svelte';
|
|
14
15
|
import DebugExperience from './DebugExperience.svelte';
|
|
15
16
|
import MissingComponent from './MissingComponent.svelte';
|
|
16
17
|
import NodesRenderer from './NodesRenderer.svelte';
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
metadata,
|
|
39
40
|
debug = false,
|
|
40
41
|
renderUnknown = MissingComponent,
|
|
42
|
+
renderError = ComponentError,
|
|
41
43
|
}: ServerExperienceRendererProps = $props();
|
|
42
44
|
|
|
43
45
|
function buildContext(): RenderContext {
|
|
@@ -56,16 +58,32 @@
|
|
|
56
58
|
|
|
57
59
|
const renderContext = buildContext();
|
|
58
60
|
setExperience(renderContext);
|
|
61
|
+
|
|
62
|
+
// Render-time diagnostics (unregistered id, a component that threw),
|
|
63
|
+
// collected into a plain array rather than `$state`: Svelte SSR is
|
|
64
|
+
// synchronous top-down, so by the time `<DebugExperience>` renders — after
|
|
65
|
+
// the tree, matching the React adapter's element-order fix for
|
|
66
|
+
// consistency, even though Svelte's own reactivity wouldn't strictly
|
|
67
|
+
// require it — this array is already fully populated.
|
|
68
|
+
const renderDiagnostics: Error[] = [];
|
|
69
|
+
function onDiagnostic(error: Error): void {
|
|
70
|
+
renderDiagnostics.push(error);
|
|
71
|
+
}
|
|
59
72
|
</script>
|
|
60
73
|
|
|
61
74
|
{#if experience}
|
|
62
|
-
{#if debug}
|
|
63
|
-
<DebugExperience {experience} />
|
|
64
|
-
{/if}
|
|
65
75
|
<NodesRenderer
|
|
66
76
|
nodes={experience.nodes}
|
|
67
77
|
{config}
|
|
68
78
|
experience={renderContext}
|
|
69
79
|
{renderUnknown}
|
|
80
|
+
{renderError}
|
|
81
|
+
{onDiagnostic}
|
|
70
82
|
/>
|
|
83
|
+
{#if debug}
|
|
84
|
+
<DebugExperience
|
|
85
|
+
{experience}
|
|
86
|
+
errors={[...(experience.diagnostics ?? []), ...renderDiagnostics]}
|
|
87
|
+
/>
|
|
88
|
+
{/if}
|
|
71
89
|
{/if}
|
|
@@ -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;AA8E1E,QAAA,MAAM,wBAAwB,mEAAwC,CAAC;AACvE,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC5E,eAAe,wBAAwB,CAAC"}
|
|
@@ -17,6 +17,8 @@ export interface ServerExperienceRendererProps {
|
|
|
17
17
|
*/
|
|
18
18
|
debug?: boolean;
|
|
19
19
|
renderUnknown?: RenderUnknown;
|
|
20
|
+
/** Override the fallback rendered when a registered component throws. */
|
|
21
|
+
renderError?: RenderError;
|
|
20
22
|
}
|
|
21
23
|
export type ClientExperienceRendererProps = ServerExperienceRendererProps;
|
|
22
24
|
export interface MissingComponentProps {
|
|
@@ -25,10 +27,36 @@ export interface MissingComponentProps {
|
|
|
25
27
|
nodeId?: string;
|
|
26
28
|
}
|
|
27
29
|
export type RenderUnknown = Component<MissingComponentProps>;
|
|
30
|
+
export interface ComponentErrorProps {
|
|
31
|
+
componentId: string;
|
|
32
|
+
/** Optional — only present when the payload supplied an id for this node. */
|
|
33
|
+
nodeId?: string;
|
|
34
|
+
/** The caught error's message, when available. */
|
|
35
|
+
message?: string;
|
|
36
|
+
}
|
|
37
|
+
export type RenderError = Component<ComponentErrorProps>;
|
|
38
|
+
/**
|
|
39
|
+
* Reports one render-time diagnostic (unregistered id, a component that
|
|
40
|
+
* threw). `ServerExperienceRenderer` passes a closure that pushes onto a
|
|
41
|
+
* plain array (Svelte SSR is synchronous top-down, so the array is fully
|
|
42
|
+
* populated by the time `<DebugExperience>` reads it). `ClientExperienceRenderer`
|
|
43
|
+
* passes a closure that mutates a `$state` array instead, so `<DebugExperience>`
|
|
44
|
+
* re-renders reactively when a later interaction throws.
|
|
45
|
+
*/
|
|
46
|
+
export type DiagnosticReporter = (error: Error) => void;
|
|
28
47
|
export interface DebugExperienceProps {
|
|
29
48
|
/** The resolved plan to inspect (what a renderer receives as `experience`). */
|
|
30
49
|
experience: PortableRenderPlan;
|
|
31
50
|
/** Start expanded. Defaults to collapsed to stay out of the way. */
|
|
32
51
|
defaultOpen?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve-time + render-time diagnostics for this render, merged by the
|
|
54
|
+
* caller (`ServerExperienceRenderer` / `ClientExperienceRenderer`). Plain
|
|
55
|
+
* `Error`s — the two cases that wrap a real caught exception
|
|
56
|
+
* (`component-render-error`, `resolve-data-failed`) set `.cause` to the
|
|
57
|
+
* original error, inspectable via `console.error` or `error.cause.stack`.
|
|
58
|
+
* Defaults to `[]` for a manually-mounted `<DebugExperience>`.
|
|
59
|
+
*/
|
|
60
|
+
errors?: Error[];
|
|
33
61
|
}
|
|
34
62
|
//# 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,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;
|
|
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;IAC9B,yEAAyE;IACzE,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,MAAM,6BAA6B,GAAG,6BAA6B,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,mBAAmB,CAAC,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,UAAU,EAAE,kBAAkB,CAAC;IAC/B,oEAAoE;IACpE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-panel-coverage.ssr.test.d.ts","sourceRoot":"","sources":["../src/debug-panel-coverage.ssr.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* True SSR coverage of a resolve-time diagnostic reaching the debug panel —
|
|
3
|
+
* renders through `svelte/server` (Svelte's *server*-compiled output), not
|
|
4
|
+
* `@testing-library/svelte` (the client build every other coverage test in
|
|
5
|
+
* this package uses). `token-unresolved` is the trigger: it's produced by
|
|
6
|
+
* `resolveExperience` itself, so it's already baked into `plan.diagnostics`
|
|
7
|
+
* before any renderer sees it — proving the panel surfaces a resolve-time
|
|
8
|
+
* diagnostic under a real, synchronous server render, not just under a
|
|
9
|
+
* client-mounted test harness.
|
|
10
|
+
*
|
|
11
|
+
* Runs under vitest.ssr.config.ts (environment: node) — the `.ssr.test.ts`
|
|
12
|
+
* suffix is load-bearing, see nodes-renderer.ssr.test.ts for why.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
15
|
+
import { render } from 'svelte/server';
|
|
16
|
+
import { resolveExperience } from '@contentful/experiences-sdk-core';
|
|
17
|
+
import ServerExperienceRenderer from './ServerExperienceRenderer.svelte';
|
|
18
|
+
import ButtonFixture from './test-fixtures/ButtonFixture.svelte';
|
|
19
|
+
const VIEWPORTS = [{ id: 'desktop', query: '*', displayName: 'Desktop', previewSize: '100%' }];
|
|
20
|
+
function componentNode(typeId, rest = {}) {
|
|
21
|
+
return {
|
|
22
|
+
component: {
|
|
23
|
+
sys: {
|
|
24
|
+
type: 'ResourceLink',
|
|
25
|
+
linkType: 'Contentful:Component',
|
|
26
|
+
urn: `crn:contentful:::experience:spaces/$self/environments/$self/components/${typeId}`,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
...rest,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
describe('debug panel — true SSR coverage of a resolve-time diagnostic', () => {
|
|
33
|
+
it('token-unresolved reaches the debug panel under svelte/server', async () => {
|
|
34
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
35
|
+
try {
|
|
36
|
+
const payload = {
|
|
37
|
+
viewports: VIEWPORTS,
|
|
38
|
+
nodes: [
|
|
39
|
+
componentNode('button', {
|
|
40
|
+
id: 'b',
|
|
41
|
+
designProperties: { cfColor: { type: 'DesignToken', value: 'color.brand' } },
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
const config = {
|
|
46
|
+
components: { button: ButtonFixture },
|
|
47
|
+
resolveToken: () => undefined,
|
|
48
|
+
};
|
|
49
|
+
const plan = await resolveExperience(payload, config);
|
|
50
|
+
const html = render(ServerExperienceRenderer, {
|
|
51
|
+
props: { experience: plan, config, debug: true },
|
|
52
|
+
}).body;
|
|
53
|
+
expect(html).toContain('data-experiences-debug-errors');
|
|
54
|
+
expect(html).toContain('color.brand');
|
|
55
|
+
expect(html).toContain('resolveToken');
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
warn.mockRestore();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-panel-coverage.test.d.ts","sourceRoot":"","sources":["../src/debug-panel-coverage.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* End-to-end proof that every one of the 8 non-happy-path failure modes
|
|
3
|
+
* reaches `<DebugExperience>`'s rendered output — through a real
|
|
4
|
+
* `resolveExperience` + renderer pipeline, not a hand-built diagnostic
|
|
5
|
+
* fixture (see DebugExperience.test.ts for the fixture-level tests of the
|
|
6
|
+
* panel's own rendering logic). Each test asserts a distinguishing substring
|
|
7
|
+
* of the real message, not the whole string, so wording tweaks don't make
|
|
8
|
+
* these overly brittle.
|
|
9
|
+
*
|
|
10
|
+
* True SSR coverage of a resolve-time diagnostic (`token-unresolved`) lives
|
|
11
|
+
* in debug-panel-coverage.ssr.test.ts, using `svelte/server`'s `render` — this
|
|
12
|
+
* file's `ServerExperienceRenderer` tests mount client-side via
|
|
13
|
+
* `@testing-library/svelte`, same as the rest of this package's suite.
|
|
14
|
+
*/
|
|
15
|
+
import { render } from '@testing-library/svelte';
|
|
16
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
17
|
+
import { resolveExperience } from '@contentful/experiences-sdk-core';
|
|
18
|
+
import ClientExperienceRenderer from './ClientExperienceRenderer.svelte';
|
|
19
|
+
import ServerExperienceRenderer from './ServerExperienceRenderer.svelte';
|
|
20
|
+
import BrokenFixture from './test-fixtures/BrokenFixture.svelte';
|
|
21
|
+
import ButtonFixture from './test-fixtures/ButtonFixture.svelte';
|
|
22
|
+
// `ClientExperienceRenderer`'s onDiagnostic defers via `queueMicrotask` (see
|
|
23
|
+
// its own comment for why); a test asserting against the debug panel needs to
|
|
24
|
+
// let that microtask settle first.
|
|
25
|
+
async function flushMicrotasks() {
|
|
26
|
+
await new Promise((resolve) => queueMicrotask(resolve));
|
|
27
|
+
}
|
|
28
|
+
const VIEWPORTS = [{ id: 'desktop', query: '*', displayName: 'Desktop', previewSize: '100%' }];
|
|
29
|
+
function componentNode(typeId, rest = {}) {
|
|
30
|
+
return {
|
|
31
|
+
component: {
|
|
32
|
+
sys: {
|
|
33
|
+
type: 'ResourceLink',
|
|
34
|
+
linkType: 'Contentful:Component',
|
|
35
|
+
urn: `crn:contentful:::experience:spaces/$self/environments/$self/components/${typeId}`,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
...rest,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
describe('debug panel — end-to-end coverage of every non-happy-path failure mode', () => {
|
|
42
|
+
it('malformed-payload: a non-array nodes field', async () => {
|
|
43
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
44
|
+
try {
|
|
45
|
+
const plan = await resolveExperience({ viewports: VIEWPORTS, nodes: 'not-an-array' }, { components: {} });
|
|
46
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
47
|
+
props: { experience: plan, config: { components: {} }, debug: true },
|
|
48
|
+
});
|
|
49
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
50
|
+
expect(container.innerHTML).toContain('"nodes" is not an array');
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
warn.mockRestore();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
it('malformed-slot: a slot value that is not an array', async () => {
|
|
57
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
58
|
+
try {
|
|
59
|
+
const payload = {
|
|
60
|
+
viewports: VIEWPORTS,
|
|
61
|
+
nodes: [
|
|
62
|
+
componentNode('contentful-container', {
|
|
63
|
+
id: 'page',
|
|
64
|
+
slots: { children: 'oops' },
|
|
65
|
+
}),
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
const config = { components: { 'contentful-container': ButtonFixture } };
|
|
69
|
+
const plan = await resolveExperience(payload, config);
|
|
70
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
71
|
+
props: { experience: plan, config, debug: true },
|
|
72
|
+
});
|
|
73
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
74
|
+
expect(container.innerHTML).toContain('Slot "children"');
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
warn.mockRestore();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it('unidentifiable-node: a node with no readable component/experienceTemplate ref', async () => {
|
|
81
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
82
|
+
try {
|
|
83
|
+
const payload = {
|
|
84
|
+
viewports: VIEWPORTS,
|
|
85
|
+
nodes: [{ pattern: {} }, componentNode('button', { id: 'b' })],
|
|
86
|
+
};
|
|
87
|
+
const config = { components: { button: ButtonFixture } };
|
|
88
|
+
const plan = await resolveExperience(payload, config);
|
|
89
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
90
|
+
props: { experience: plan, config, debug: true },
|
|
91
|
+
});
|
|
92
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
93
|
+
expect(container.innerHTML).toContain('Skipping unidentifiable node');
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
warn.mockRestore();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
it('resolve-data-failed: resolveData throws for a registered component', async () => {
|
|
100
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
101
|
+
try {
|
|
102
|
+
const payload = {
|
|
103
|
+
viewports: VIEWPORTS,
|
|
104
|
+
nodes: [componentNode('button', { id: 'b' })],
|
|
105
|
+
};
|
|
106
|
+
const config = {
|
|
107
|
+
components: {
|
|
108
|
+
button: {
|
|
109
|
+
resolveData: () => {
|
|
110
|
+
throw new Error('enrichment service down');
|
|
111
|
+
},
|
|
112
|
+
component: ButtonFixture,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
const plan = await resolveExperience(payload, config);
|
|
117
|
+
expect(plan.diagnostics[0]).toBeInstanceOf(Error);
|
|
118
|
+
expect(plan.diagnostics[0].cause?.message).toBe('enrichment service down');
|
|
119
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
120
|
+
props: { experience: plan, config, debug: true },
|
|
121
|
+
});
|
|
122
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
123
|
+
expect(container.innerHTML).toContain('component:button');
|
|
124
|
+
expect(container.innerHTML).toContain('enrichment service down');
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
warn.mockRestore();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
it('token-unresolved: a DesignToken with no resolveToken mapping', async () => {
|
|
131
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
132
|
+
try {
|
|
133
|
+
const payload = {
|
|
134
|
+
viewports: VIEWPORTS,
|
|
135
|
+
nodes: [
|
|
136
|
+
componentNode('button', {
|
|
137
|
+
id: 'b',
|
|
138
|
+
designProperties: { cfColor: { type: 'DesignToken', value: 'color.brand' } },
|
|
139
|
+
}),
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
const config = {
|
|
143
|
+
components: { button: ButtonFixture },
|
|
144
|
+
resolveToken: () => undefined,
|
|
145
|
+
};
|
|
146
|
+
const plan = await resolveExperience(payload, config);
|
|
147
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
148
|
+
props: { experience: plan, config, debug: true },
|
|
149
|
+
});
|
|
150
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
151
|
+
expect(container.innerHTML).toContain('color.brand');
|
|
152
|
+
expect(container.innerHTML).toContain('resolveToken');
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
warn.mockRestore();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
it('component-not-registered: an id with no matching entry in config.components', async () => {
|
|
159
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
160
|
+
try {
|
|
161
|
+
const payload = {
|
|
162
|
+
viewports: VIEWPORTS,
|
|
163
|
+
nodes: [componentNode('missing', { id: 'm' })],
|
|
164
|
+
};
|
|
165
|
+
const config = { components: {} };
|
|
166
|
+
const plan = await resolveExperience(payload, config);
|
|
167
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
168
|
+
props: { experience: plan, config, debug: true },
|
|
169
|
+
});
|
|
170
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
171
|
+
expect(container.innerHTML).toContain('No component registered for id "missing"');
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
warn.mockRestore();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
it('experience-template-not-registered: a template id with no matching entry in config.experienceTemplates', async () => {
|
|
178
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
179
|
+
try {
|
|
180
|
+
const payload = {
|
|
181
|
+
viewports: VIEWPORTS,
|
|
182
|
+
nodes: [
|
|
183
|
+
{
|
|
184
|
+
experienceTemplate: {
|
|
185
|
+
sys: {
|
|
186
|
+
type: 'ResourceLink',
|
|
187
|
+
linkType: 'Contentful:ExperienceTemplate',
|
|
188
|
+
urn: 'crn:contentful:::experience:spaces/$self/environments/$self/experienceTemplates/missing-template',
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
id: 'tpl',
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
const config = { components: {} };
|
|
196
|
+
const plan = await resolveExperience(payload, config);
|
|
197
|
+
const { container } = render(ServerExperienceRenderer, {
|
|
198
|
+
props: { experience: plan, config, debug: true },
|
|
199
|
+
});
|
|
200
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
201
|
+
expect(container.innerHTML).toContain('No experience template registered for id "missing-template"');
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
warn.mockRestore();
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
it('component-render-error: a registered component that throws while rendering', async () => {
|
|
208
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
209
|
+
try {
|
|
210
|
+
const payload = {
|
|
211
|
+
viewports: VIEWPORTS,
|
|
212
|
+
nodes: [componentNode('broken', { id: 'b' })],
|
|
213
|
+
};
|
|
214
|
+
const config = { components: { broken: BrokenFixture } };
|
|
215
|
+
const plan = await resolveExperience(payload, config);
|
|
216
|
+
const { container } = render(ClientExperienceRenderer, {
|
|
217
|
+
props: { experience: plan, config, debug: true },
|
|
218
|
+
});
|
|
219
|
+
await flushMicrotasks();
|
|
220
|
+
const errorList = container.querySelector('[data-experiences-debug-errors]');
|
|
221
|
+
expect(errorList).not.toBeNull();
|
|
222
|
+
expect(errorList.textContent).toContain('Component "broken"');
|
|
223
|
+
expect(errorList.textContent).toContain('boom');
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
warn.mockRestore();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
});
|
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 ComponentError } from './ComponentError.svelte';
|
|
5
6
|
export { default as DebugExperience } from './DebugExperience.svelte';
|
|
6
7
|
export { default as NodesRenderer } from './NodesRenderer.svelte';
|
|
7
8
|
export { useActiveViewport } from './use-active-viewport.svelte.js';
|
|
@@ -10,13 +11,13 @@ export { getExperience, getContentfulComponent, getContentfulExperienceTemplate,
|
|
|
10
11
|
export { getDesignValues } from './get-design-values.js';
|
|
11
12
|
export { toCss } from './design-utils.js';
|
|
12
13
|
export type { ToCssOptions } from './design-utils.js';
|
|
13
|
-
export type { ClientExperienceRendererProps, ClientExperienceRendererProps as ExperienceRendererProps, DebugExperienceProps, MissingComponentProps, RenderUnknown, ServerExperienceRendererProps, } from './component-props.js';
|
|
14
|
+
export type { ClientExperienceRendererProps, ClientExperienceRendererProps as ExperienceRendererProps, ComponentErrorProps, DebugExperienceProps, DiagnosticReporter, MissingComponentProps, RenderError, RenderUnknown, ServerExperienceRendererProps, } from './component-props.js';
|
|
14
15
|
export { defineComponent, defineExperienceTemplate, normalizeComponentRegistration, normalizeExperienceTemplateRegistration, } from './types.js';
|
|
15
16
|
export type { ComponentConfig, Components, Config, ContentfulComponent, ContentfulExperienceTemplate, ExperienceTemplateConfig, ExperienceTemplateRegistration, ExperienceTemplates, Registration, RenderContext, ResolveToken, } from './types.js';
|
|
16
17
|
export { resolveExperience } from '@contentful/experiences-sdk-core';
|
|
17
18
|
export type { ResolverConfig, ResolveExperienceOptions } from '@contentful/experiences-sdk-core';
|
|
18
19
|
export type { ComponentNode, ComponentRef, DesignPropValue, DesignToken, ExperienceContext, ExperienceNode, ExperiencePayload, ExperienceSys, ExperienceTemplateNode, ExperienceTemplateRef, ManualDesignValue, PortableRegistration, PortableRenderNode, PortableRenderPlan, ResolveContext, ValuesByViewport, ViewportDef, } from '@contentful/experiences-sdk-core';
|
|
19
20
|
export { CSS_PROPERTIES, getValueForViewport, getViewportIndex, isCssProperty, resolveDesignProperties, toCssKey, toCssMediaQuery, } from '@contentful/experiences-design';
|
|
20
|
-
export { ContentfulViewDelivery, ContentfulViewDeliveryClient, DELIVERY_HOST, NotFoundError, PREVIEW_HOST, createClient, fetchExperience, } from '@contentful/experiences-client';
|
|
21
|
+
export { ContentfulViewDelivery, ContentfulViewDeliveryClient, DELIVERY_HOST, ExperienceFetchError, NotFoundError, PREVIEW_HOST, createClient, fetchExperience, } from '@contentful/experiences-client';
|
|
21
22
|
export type { ClientOptions, CreateClientOptions, ExperienceOptions, ResolveOptions, } from '@contentful/experiences-client';
|
|
22
23
|
//# sourceMappingURL=index.d.ts.map
|
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;AACxE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAKtE,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,EACL,aAAa,EACb,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,cAAc,CAAC;AACtB,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,wBAAwB,EACxB,8BAA8B,EAC9B,uCAAuC,GACxC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,4BAA4B,EAC5B,wBAAwB,EACxB,8BAA8B,EAC9B,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAGjG,YAAY,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,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"}
|
|
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,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAKtE,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,EACL,aAAa,EACb,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,cAAc,CAAC;AACtB,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,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,8BAA8B,EAC9B,uCAAuC,GACxC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,UAAU,EACV,MAAM,EACN,mBAAmB,EACnB,4BAA4B,EAC5B,wBAAwB,EACxB,8BAA8B,EAC9B,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAGjG,YAAY,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,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,oBAAoB,EACpB,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 ComponentError } from './ComponentError.svelte';
|
|
19
20
|
export { default as DebugExperience } from './DebugExperience.svelte';
|
|
20
21
|
// Every slot already arrives as a same-named `Snippet[]` prop; this is exposed so
|
|
21
22
|
// advanced customers can render a slot's raw nodes themselves instead:
|
|
@@ -33,4 +34,4 @@ export { resolveExperience } from '@contentful/experiences-sdk-core';
|
|
|
33
34
|
// ─── Design utilities (re-exported from design) ───────────────────────────
|
|
34
35
|
export { CSS_PROPERTIES, getValueForViewport, getViewportIndex, isCssProperty, resolveDesignProperties, toCssKey, toCssMediaQuery, } from '@contentful/experiences-design';
|
|
35
36
|
// ─── Delivery client + fetchExperience ────────────────────────────────────
|
|
36
|
-
export { ContentfulViewDelivery, ContentfulViewDeliveryClient, DELIVERY_HOST, NotFoundError, PREVIEW_HOST, createClient, fetchExperience, } from '@contentful/experiences-client';
|
|
37
|
+
export { ContentfulViewDelivery, ContentfulViewDeliveryClient, DELIVERY_HOST, ExperienceFetchError, NotFoundError, PREVIEW_HOST, createClient, fetchExperience, } from '@contentful/experiences-client';
|
|
@@ -15,6 +15,7 @@ import { describe, expect, it } from 'vitest';
|
|
|
15
15
|
import { render } from 'svelte/server';
|
|
16
16
|
import { resolveExperience } from '@contentful/experiences-sdk-core';
|
|
17
17
|
import ServerExperienceRenderer from './ServerExperienceRenderer.svelte';
|
|
18
|
+
import BrokenFixture from './test-fixtures/BrokenFixture.svelte';
|
|
18
19
|
import ButtonFixture from './test-fixtures/ButtonFixture.svelte';
|
|
19
20
|
import ContainerFixture from './test-fixtures/ContainerFixture.svelte';
|
|
20
21
|
import ExperienceTemplateFixture from './test-fixtures/ExperienceTemplateFixture.svelte';
|
|
@@ -124,3 +125,51 @@ describe('NodesRenderer — SSR (server-compiled output)', () => {
|
|
|
124
125
|
expect(html).not.toContain('<main');
|
|
125
126
|
});
|
|
126
127
|
});
|
|
128
|
+
/*
|
|
129
|
+
* The load-bearing SSR/CSR asymmetry this ticket documents: unlike React's
|
|
130
|
+
* class-boundary + internal <Suspense> (which degrades gracefully under both
|
|
131
|
+
* legacy and streaming SSR — see the React adapter's nodes-renderer.ssr.test.tsx),
|
|
132
|
+
* Svelte's native `<svelte:boundary>` has NO server-rendering story at all.
|
|
133
|
+
* `svelte/server`'s render is a synchronous string walk with no boundary
|
|
134
|
+
* concept; a throw during that walk simply propagates. This is proven here,
|
|
135
|
+
* not just asserted in a comment, per the plan's "prove it, don't just
|
|
136
|
+
* document it" bar. See `NodeRenderer.test.ts` for the client-side catch.
|
|
137
|
+
*/
|
|
138
|
+
describe('NodesRenderer — SSR component-render-error (the documented gap)', () => {
|
|
139
|
+
const brokenConfig = {
|
|
140
|
+
components: { broken: BrokenFixture, 'contentful-button': ButtonFixture },
|
|
141
|
+
};
|
|
142
|
+
it('has no recovery path under svelte/server — no graceful fallback markup is produced', async () => {
|
|
143
|
+
const payload = {
|
|
144
|
+
viewports: VIEWPORTS,
|
|
145
|
+
nodes: [componentNode('broken', { id: 'b' }), button('sibling')],
|
|
146
|
+
};
|
|
147
|
+
const plan = await resolveExperience(payload, brokenConfig);
|
|
148
|
+
// Propagates as an exception in most runs, but the exact timing is
|
|
149
|
+
// inconsistent across invocation contexts (sync throw in some runs,
|
|
150
|
+
// seemingly swallowed in others) — unlike React's deterministic
|
|
151
|
+
// Suspense-based degradation, Svelte's `<svelte:boundary>` has no
|
|
152
|
+
// supported SSR recovery contract at all, so nothing guarantees *how*
|
|
153
|
+
// the failure surfaces. The one invariant that holds in every run: the
|
|
154
|
+
// `failed` snippet's markup never appears, so there's no graceful
|
|
155
|
+
// degradation to rely on either way. No adapter-level fix is possible —
|
|
156
|
+
// `svelte/server` has no per-node recovery primitive below its single
|
|
157
|
+
// top-level `render()` call — see the README's SvelteKit mitigation note.
|
|
158
|
+
let html;
|
|
159
|
+
let caught;
|
|
160
|
+
try {
|
|
161
|
+
html = render(ServerExperienceRenderer, {
|
|
162
|
+
props: { experience: plan, config: brokenConfig },
|
|
163
|
+
}).body;
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
caught = err;
|
|
167
|
+
}
|
|
168
|
+
if (caught) {
|
|
169
|
+
expect(caught.message).toBe('boom');
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
expect(html).not.toContain('data-experiences-render-error');
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -390,12 +390,14 @@ describe('ServerExperienceRenderer', () => {
|
|
|
390
390
|
};
|
|
391
391
|
const plan = await resolveExperience(tplPayload, cfg);
|
|
392
392
|
const { container } = render(ServerExperienceRenderer, {
|
|
393
|
-
props: { experience: plan, config: cfg },
|
|
393
|
+
props: { experience: plan, config: cfg, debug: true },
|
|
394
394
|
});
|
|
395
395
|
// The subtree survives — an unregistered template must not blank the page.
|
|
396
396
|
expect(container.innerHTML).toContain('data-value="unwrapped"');
|
|
397
397
|
expect(container.innerHTML).not.toContain('data-experience-template');
|
|
398
398
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining('missing-experienceTemplate'));
|
|
399
|
+
expect(container.innerHTML).toContain('data-experiences-debug-errors');
|
|
400
|
+
expect(container.innerHTML).toContain('No experience template registered for id "missing-experienceTemplate"');
|
|
399
401
|
warn.mockRestore();
|
|
400
402
|
});
|
|
401
403
|
});
|
|
@@ -557,7 +559,7 @@ describe('ServerExperienceRenderer — resolveToken', () => {
|
|
|
557
559
|
expect(container.innerHTML).toContain('data-bg="#4f39f6"');
|
|
558
560
|
expect(container.innerHTML).not.toContain('DesignToken');
|
|
559
561
|
});
|
|
560
|
-
it('warns and
|
|
562
|
+
it('warns and passes the raw token through the design values when the resolver returns undefined', async () => {
|
|
561
563
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
562
564
|
const cfg = {
|
|
563
565
|
components: { 'contentful-button': CapturingComponent },
|
|
@@ -576,7 +578,7 @@ describe('ServerExperienceRenderer — resolveToken', () => {
|
|
|
576
578
|
render(ServerExperienceRenderer, {
|
|
577
579
|
props: { experience: plan, config: cfg },
|
|
578
580
|
});
|
|
579
|
-
expect(captureSink[0].designValues).
|
|
581
|
+
expect(captureSink[0].designValues.cfBackgroundColor).toEqual(dt('color/unknown'));
|
|
580
582
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining('color/unknown'));
|
|
581
583
|
expect(warn).toHaveBeenCalledWith(expect.stringContaining('contentful-button'));
|
|
582
584
|
warn.mockRestore();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const BrokenFixture: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type BrokenFixture = InstanceType<typeof BrokenFixture>;
|
|
18
|
+
export default BrokenFixture;
|
|
19
|
+
//# sourceMappingURL=BrokenFixture.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrokenFixture.svelte.d.ts","sourceRoot":"","sources":["../../src/test-fixtures/BrokenFixture.svelte.ts"],"names":[],"mappings":"AAUA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,aAAa;;kBAA+E,CAAC;AACjF,KAAK,aAAa,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,eAAe,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experiences-svelte",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
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.3.
|
|
39
|
-
"@contentful/experiences-sdk-core": "0.7.
|
|
40
|
-
"@contentful/experiences-design": "0.7.
|
|
38
|
+
"@contentful/experiences-client": "0.3.11",
|
|
39
|
+
"@contentful/experiences-sdk-core": "0.7.11",
|
|
40
|
+
"@contentful/experiences-design": "0.7.11"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"svelte": "^5.55.7"
|