@elytracms/next 0.0.85 → 0.0.87

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/dist/index.d.ts CHANGED
@@ -1,14 +1,13 @@
1
1
  import { ComponentType, ReactNode } from 'react';
2
2
  import { ComponentRegistry, ComponentIssue, ComponentManifest } from '@elytracms/core/component-registry';
3
3
  export { ComponentManifest, PropField, SlotSpec, defineComponent } from '@elytracms/core/component-registry';
4
- import { ComponentImplementations, ResolveBinding, RelationTarget, DocumentBodyItem } from '@elytracms/core/runtime-renderer';
4
+ import { ComponentImplementations, RelationTarget, DocumentBodyItem } from '@elytracms/core/runtime-renderer';
5
5
  export { ComponentImplementations, RenderAsset } from '@elytracms/core/runtime-renderer';
6
+ import { HostComponentEntry } from '@elytracms/core/studio-core';
6
7
  import { ResolvePageResult, ResolvedAsset, Perspective, ContentClient, CacheTags, ContentDocumentSource, ResolvedDocument } from '@elytracms/core/content';
7
8
  export { Perspective } from '@elytracms/core/content';
8
9
  import { LocaleConfig, Locale, RouteRecord, CollectionDef, RedirectRecord } from '@elytracms/core/cms-core';
9
- export { CmsDocument, CollectionDef, FieldDef, Locale, LocaleConfig, LocaleUrlPath, RedirectRecord, RouteRecord, documentSchema, fallbackHomeRoutes, localeConfigSchema, localeUrlPath, mergeRouteRecords, redirectRecordSchema, routeRecordSchema, splitLocalePath } from '@elytracms/core/cms-core';
10
- import { ComponentNode } from '@elytracms/core/project-graph';
11
- export { ComponentNode } from '@elytracms/core/project-graph';
10
+ export { CmsDocument, CollectionDef, FieldDef, Locale, LocaleConfig, LocaleRequest, LocaleStrategy, LocaleUrlPath, RedirectRecord, RouteRecord, documentSchema, fallbackHomeRoutes, localeConfigSchema, localeUrlPath, mergeRouteRecords, redirectRecordSchema, resolveLocale, routeRecordSchema, splitLocalePath, textDirection } from '@elytracms/core/cms-core';
12
11
  import { AssetRecord, PersistenceAdapter } from '@elytracms/core/persistence';
13
12
  export { AssetRecord } from '@elytracms/core/persistence';
14
13
  import { Metadata, MetadataRoute } from 'next';
@@ -16,12 +15,19 @@ import { z } from 'zod';
16
15
  export { z } from 'zod';
17
16
  import { ImageLoader } from 'next/image';
18
17
  import { HostImageRenderer } from './host-image.js';
19
- import { HostComponentEntry } from '@elytracms/core/studio-core';
18
+ export { ComponentNode } from '@elytracms/core/project-graph';
20
19
 
21
20
  /**
22
21
  * One host-repo component: the manifest the builder edits against plus the
23
22
  * real React implementation that renders it (vision AD-2 — project components
24
23
  * are real code in the host repo, never generated).
24
+ *
25
+ * This is the STRICT shape — the one a hand-authored component module should
26
+ * export (`export const heroComponent: HostComponent = …`), so the compiler
27
+ * checks the implementation where it is written. It is assignable to the
28
+ * config's {@link HostComponentEntry}, which is what {@link defineHostComponents}
29
+ * accepts, so both hand-built lists and the config's own list register through
30
+ * the one signature (EC-541).
25
31
  */
26
32
  interface HostComponent {
27
33
  manifest: ComponentManifest;
@@ -36,7 +42,11 @@ interface HostComponent {
36
42
  interface HostComponents {
37
43
  registry: ComponentRegistry;
38
44
  implementations: ComponentImplementations;
39
- /** Structural registration issues (duplicate ids, invalid namespaces). */
45
+ /**
46
+ * Structural registration issues: the registry's own (duplicate ids, invalid
47
+ * namespaces, …) plus `invalid-implementation` for an entry whose
48
+ * `implementation` is not a React component (EC-541).
49
+ */
40
50
  issues: readonly ComponentIssue[];
41
51
  }
42
52
  interface DefineHostComponentsOptions {
@@ -53,73 +63,30 @@ interface DefineHostComponentsOptions {
53
63
  * default. A host implementation registered under a primitive id replaces the
54
64
  * default implementation (the manifest of the first registration wins, which
55
65
  * is the primitive's — by design, so prop schemas stay canonical).
56
- */
57
- declare function defineHostComponents(components?: readonly HostComponent[], options?: DefineHostComponentsOptions): HostComponents;
58
-
59
- /**
60
- * Binding payloads for the embedded runtime (EC-144): one payload value per
61
- * binding `sourceId`. The catch-all route helper provides `document` (the
62
- * route-bound resolved document) and `params` (dynamic route params) by
63
- * default; hosts add more through the route helper's `payloads` factory
64
- * (e.g. a `listDocuments` result for a posts repeater).
65
- *
66
- * This is a deliberately small, pure resolver — the embedded runtime never
67
- * imports `@elytracms/core/data-binding` (a builder-side package). Semantics mirror
68
- * EC-023 tokens: a binding token is a JSON Pointer (RFC 6901) into the
69
- * payload; a miss resolves to `undefined`, never a throw (the renderer turns
70
- * unresolvable bindings into visible fallback behavior, EC-015).
71
- */
72
- type BindingPayloads = Record<string, unknown>;
73
- /**
74
- * Marker key of an {@link SourcePayloadError} payload. A plain string property
75
- * (not a Symbol) on purpose: the route helper's cached path JSON-serializes
76
- * payloads through `unstable_cache`, and the marker must survive that
77
- * round-trip.
78
- */
79
- declare const SOURCE_PAYLOAD_ERROR_KEY = "__elytraSourcePayloadError";
80
- /**
81
- * Explicit issue payload for a source whose stored query was REJECTED at
82
- * delivery (EC-177 gap 3) — e.g. a sort/filter field that is no longer
83
- * filterable in the live schema. Materialization stores this instead of a
84
- * silently-absent payload; `createBindingResolver` throws on it, which the
85
- * renderer converts into its visible per-node render-error fallback (EC-015).
86
- * Validation principle: a broken stored query is a visible state, never an
87
- * empty section.
88
- */
89
- interface SourcePayloadError {
90
- [SOURCE_PAYLOAD_ERROR_KEY]: true;
91
- sourceId: string;
92
- /** Human-readable messages of the structured query errors. */
93
- messages: string[];
94
- }
95
- /** Build the explicit issue payload for one failed source query. */
96
- declare function sourcePayloadError(sourceId: string, messages: readonly string[]): SourcePayloadError;
97
- /** `true` when a payload value is an explicit {@link SourcePayloadError}. */
98
- declare function isSourcePayloadError(value: unknown): value is SourcePayloadError;
99
- /**
100
- * Resolve a JSON Pointer token within a payload value. Numeric segments index
101
- * arrays; string segments index plain objects. Any miss (unknown key,
102
- * out-of-range index, traversal through a primitive) yields `undefined`.
103
- */
104
- declare function resolvePayloadToken(payload: unknown, token: string): unknown;
105
- /**
106
- * Create the renderer's `ResolveBinding` over a payload map. Mode semantics
107
- * (brief §4.6, uniform binding model):
108
- *
109
- * - `value` / `object` / `condition` — the value at the token within the
110
- * payload named by `sourceId`.
111
- * - `spread` — same lookup; the renderer spreads the resulting object.
112
- * - `repeaterItem` — the token resolves against the active repeater item,
113
- * not the root payload.
114
- *
115
- * Total for data misses: an unknown `sourceId` or missing path resolves to
116
- * `undefined` — the renderer (EC-015) degrades visibly instead of crashing.
117
- * An explicit {@link SourcePayloadError} payload (a REJECTED stored query, not
118
- * merely missing data) throws instead: the renderer catches per node and
119
- * renders its visible render-error fallback with the query error message;
120
- * conditions treat the throw as `false` (the node hides) — never a page crash.
121
- */
122
- declare function createBindingResolver(payloads: BindingPayloads): ResolveBinding;
66
+ *
67
+ * Accepts the CONFIG's entry shape (`implementation: unknown`) rather than the
68
+ * strict {@link HostComponent}: the config declares implementations opaquely,
69
+ * and EC-539 made `createElytraHost({ ...site })` the contract, so a
70
+ * compile-time check at this seam was already gone for the recommended path.
71
+ * The check is RUNTIME instead (EC-541), through core's one registration step
72
+ * `registerImplementations` (EC-555 — the studio registry and the preview
73
+ * harness call the same one): a value that is not a component is
74
+ * recorded as an `invalid-implementation` issue naming the component id and
75
+ * the received kind, and is NOT put into `implementations` — so the renderer
76
+ * takes its missing-component path (a visible EC-015 fallback) instead of
77
+ * crashing the server render on `createElement` ("Element type is invalid")
78
+ * or, for a string, silently rendering an HTML tag. The manifest still
79
+ * registers, so the registry's own issues (duplicate id, invalid id) for that
80
+ * entry are reported alongside.
81
+ *
82
+ * One consequence of the `unknown` parameter: an INLINE
83
+ * `implementation: (props) => …` literal no longer gets `props` contextually
84
+ * typed (it is `noImplicitAny` under strict). Annotate the parameter, or
85
+ * author the entry as a {@link HostComponent} — which is the documented shape
86
+ * anyway. (An overload pair was tried and does not restore it: TypeScript
87
+ * resolves the context-sensitive literal against the wider overload.)
88
+ */
89
+ declare function defineHostComponents(components?: readonly HostComponentEntry[], options?: DefineHostComponentsOptions): HostComponents;
123
90
 
124
91
  /**
125
92
  * `<CanvasRenderer />` (EC-144, vision AD-1): an async React Server Component
@@ -134,13 +101,6 @@ interface CanvasRendererProps {
134
101
  result: ResolvePageResult;
135
102
  /** Host component surface from `defineHostComponents`. */
136
103
  components: HostComponents;
137
- /**
138
- * Binding payloads keyed by `sourceId`. Defaults to
139
- * `{ document, params }` derived from the result.
140
- */
141
- payloads?: BindingPayloads;
142
- /** Full custom binding resolver — overrides `payloads` when provided. */
143
- resolveBinding?: ResolveBinding;
144
104
  /**
145
105
  * Delivery-shaped assets the page may reference (EC-195, from the render
146
106
  * outcome). An `asset`-typed Image prop resolves through these to a url +
@@ -194,8 +154,6 @@ interface DeliveryPresentation {
194
154
  /** Field names whose values form the body, in render order. */
195
155
  body?: readonly string[];
196
156
  }
197
- /** Default payloads: the route-bound document and the dynamic route params. */
198
- declare function defaultBindingPayloads(result: ResolvePageResult): BindingPayloads;
199
157
  /**
200
158
  * Render a resolved page server-side. Async so hosts can compose it like any
201
159
  * other RSC; resolution itself is synchronous (the content was prefetched by
@@ -221,22 +179,10 @@ declare function bodyItemsOf(document: Record<string, unknown> | null | undefine
221
179
  *
222
180
  * Accessors are synchronous over a prefetched `ContentLookup` (vision AD-4);
223
181
  * any fetching happens when the source is loaded, before clients are minted.
182
+ *
183
+ * EC-574: the `CanvasDataSource` shape and the `sources` member (the data-binding
184
+ * subsystem's per-section query definitions, EC-166) are gone with that subsystem.
224
185
  */
225
- /**
226
- * One data-source definition the published graph's bindings reference
227
- * (EC-166 delivery): the structural shape of an `@elytracms/core/data-binding`
228
- * `DataSource`, declared here so the embedded runtime stays free of
229
- * builder-side packages. Only `cms`-kind sources with the EC-166 per-scope id
230
- * prefixes (`cms.section.<nodeId>` / `cms.template.<pageId>`) participate in
231
- * automatic payload materialization; everything else is ignored.
232
- */
233
- interface CanvasDataSource {
234
- kind: string;
235
- id: string;
236
- label?: string;
237
- /** Kind-specific config; cms query configs parse via `@elytracms/core/content`. */
238
- config?: unknown;
239
- }
240
186
  interface ContentSource {
241
187
  /** The project's locale configuration (default + supported set, EC-018). */
242
188
  readonly locales: LocaleConfig;
@@ -267,15 +213,6 @@ interface ContentSource {
267
213
  * picker decides, exactly as before.
268
214
  */
269
215
  readonly collections?: readonly CollectionDef[];
270
- /**
271
- * Data-source definitions the graph's bindings reference (EC-166 delivery,
272
- * additive). When present, the route helper materializes per-section list
273
- * sources automatically by replaying their stored where/sort/limit through
274
- * `listDocuments` — see `materializeSourcePayloads`. Optional so existing
275
- * custom sources stay valid; absent definitions degrade to unresolved
276
- * bindings (the renderer's visible fallbacks), never a crash.
277
- */
278
- readonly sources?: readonly CanvasDataSource[];
279
216
  /**
280
217
  * The project's delivery-shaped assets (EC-195, additive). Lets the route
281
218
  * helper bake the assets a page may reference into the (serializable) render
@@ -295,60 +232,6 @@ interface ContentSource {
295
232
  */
296
233
  declare function mergeCacheTags(first: CacheTags, ...rest: readonly CacheTags[]): CacheTags;
297
234
 
298
- /**
299
- * Automatic payload materialization for EC-166 repeating sections — the
300
- * delivery half of the builder's collection-driven LAYOUTS (EC-187: layouts
301
- * are the only graph trees that can bind section sources now). A published
302
- * layout may carry bindings against per-scope CMS sources; the renderer needs
303
- * the source PAYLOADS:
304
- *
305
- * - **Repeating sections** (`cms.section.<nodeId>`): the stored source config
306
- * carries the closed AD-3 query. For every section source a referenced
307
- * layout tree actually binds, the stored where/sort/limit replays through
308
- * the request client's `listDocuments` — published perspective and ambient
309
- * locale come from the client itself. The list result's `CacheTags` (the
310
- * fetch-time doc tags of what was actually returned plus the coarse
311
- * collection tag — the EC-146 contract) are surfaced so the route helper
312
- * merges them into the request tag set.
313
- *
314
- * The `cms.template.<pageId>` "current document" source is RETIRED (EC-187):
315
- * there is no template page, and a routed page document's `body` uses static
316
- * props (v1). A collection detail route's document is read by the dev route
317
- * component directly via the route's `document` ref.
318
- *
319
- * Everything degrades visibly, never a crash:
320
- *
321
- * - a missing source definition, a non-cms kind, or a malformed config leaves
322
- * the payload absent, so the binding resolves to `undefined` and the
323
- * renderer shows its EC-015 fallback (an empty list renders the section's
324
- * empty state);
325
- * - a structured query error (the stored query was REJECTED at delivery, e.g.
326
- * a sort field no longer filterable in the live schema) stores an explicit
327
- * {@link sourcePayloadError} payload instead — the binding resolver throws
328
- * on it and the renderer shows its visible per-node render-error fallback,
329
- * not a silently empty section (EC-177 gap 3).
330
- */
331
- /** Collect every binding `sourceId` referenced in a node tree (props, conditions, slots). */
332
- declare function collectBindingSourceIds(node: ComponentNode, into?: Set<string>): Set<string>;
333
- interface MaterializeSourcePayloadsInput {
334
- /** Accessor client bound to this request's perspective + locale. */
335
- client: ContentClient;
336
- result: ResolvePageResult;
337
- /** Source definitions from the `ContentSource` (absent ones simply skip). */
338
- sources: readonly CanvasDataSource[];
339
- }
340
- interface MaterializedSourcePayloads {
341
- payloads: BindingPayloads;
342
- /** `CacheTags` of every accessor call made here (one entry per section list). */
343
- tags: CacheTags[];
344
- }
345
- /**
346
- * Materialize the EC-166 source payloads for one resolved page. Pure over the
347
- * prefetched source — `listDocuments` is synchronous; tags are returned (not
348
- * applied) so the caller owns the merge.
349
- */
350
- declare function materializeSourcePayloads(input: MaterializeSourcePayloadsInput): MaterializedSourcePayloads;
351
-
352
235
  /**
353
236
  * Bridge from a `PersistenceAdapter` to the synchronous `ContentLookup` the
354
237
  * accessors (EC-143) read through: prefetch the project's collections,
@@ -379,12 +262,6 @@ interface ContentSnapshotOptions {
379
262
  * config declares.
380
263
  */
381
264
  locales: LocaleConfig;
382
- /**
383
- * Data-source definitions the graph's bindings reference (EC-166): section
384
- * sources (`cms.section.<nodeId>`) carry their stored where/sort/limit, and
385
- * the route helper materializes their payloads automatically.
386
- */
387
- sources?: readonly CanvasDataSource[];
388
265
  }
389
266
  interface ContentSnapshot extends ContentSource {
390
267
  readonly projectId: string;
@@ -419,11 +296,6 @@ interface StaticContentData {
419
296
  * explicit unresolved-placeholder scheme (never a silent empty string).
420
297
  */
421
298
  assetUrl?: (asset: AssetRecord) => string;
422
- /**
423
- * Data-source definitions section bindings reference (EC-166 delivery).
424
- * Absent definitions degrade to unresolved bindings, never a crash.
425
- */
426
- sources?: readonly CanvasDataSource[];
427
299
  }
428
300
 
429
301
  /**
@@ -444,8 +316,9 @@ declare function createContentSnapshot(adapter: PersistenceAdapter, projectId: s
444
316
 
445
317
  /**
446
318
  * The pure half of the catch-all route helper (EC-144): URL → outcome, with
447
- * no Next.js imports — so redirects, 404s, locale handling, payload assembly,
448
- * and tag merging are all unit-testable as plain functions. `route.tsx` maps
319
+ * no Next.js imports — so redirects, 404s, locale handling, asset/relation/
320
+ * listing/link baking, and tag merging are all unit-testable as plain
321
+ * functions. `route.tsx` maps
449
322
  * each outcome onto `redirect()` / `permanentRedirect()` / `notFound()` /
450
323
  * `<CanvasRenderer />`.
451
324
  */
@@ -486,27 +359,6 @@ interface CanvasWrapContext extends CanvasRequestInfo {
486
359
  */
487
360
  result: ResolvePageResult;
488
361
  }
489
- /** What the host's payload factory sees for one request. */
490
- interface CanvasPayloadContext {
491
- /** Accessor client bound to this request's perspective + locale. */
492
- client: ContentClient;
493
- result: ResolvePageResult;
494
- perspective: Perspective;
495
- /** The normalized request path (locale prefix stripped). */
496
- path: string;
497
- params: Record<string, string>;
498
- /**
499
- * Register the `CacheTags` of any extra accessor call this factory makes
500
- * (e.g. `listDocuments`) so they join the request's merged tag set — the
501
- * EC-146 invalidation hook sees everything the request actually read.
502
- */
503
- addTags(tags: CacheTags): void;
504
- }
505
- /**
506
- * Builds the binding payloads for a page render. The defaults
507
- * (`document`, `params`) are always present; factory entries win on conflict.
508
- */
509
- type CanvasPayloadsFactory = (ctx: CanvasPayloadContext) => BindingPayloads | Promise<BindingPayloads>;
510
362
  type CanvasPageOutcome = {
511
363
  kind: 'redirect';
512
364
  target: string;
@@ -518,7 +370,6 @@ type CanvasPageOutcome = {
518
370
  } | {
519
371
  kind: 'render';
520
372
  result: ResolvePageResult;
521
- payloads: BindingPayloads;
522
373
  /**
523
374
  * The delivery-shaped assets the page may reference (EC-195). Baked into
524
375
  * the outcome (serializable) so the renderer resolves `asset`-typed Image
@@ -572,7 +423,6 @@ type CanvasPageOutcome = {
572
423
  declare function pathFromSlug(slug: readonly string[] | undefined): string;
573
424
 
574
425
  interface ResolveCanvasPageOptions {
575
- payloads?: CanvasPayloadsFactory;
576
426
  /**
577
427
  * Component registry (EC-205): when provided, the baked {@link CanvasPageOutcome}
578
428
  * `assets` are PAGE-SCOPED to exactly the asset ids this page's composition +
@@ -598,6 +448,13 @@ interface CanvasCacheOptions {
598
448
  * Omit only when the emitter also emits unscoped tags (see `cache.ts`).
599
449
  */
600
450
  scope?: string;
451
+ /**
452
+ * Which content world `loadContent` reads — the delivery kit passes its
453
+ * `contentMode` (`'live' | 'fallback' | 'empty'`), so entries written in one
454
+ * world can never be served in another (EC-543, see `canvasCacheKey`). A host
455
+ * driving this helper directly with a single world may omit it.
456
+ */
457
+ mode?: string;
601
458
  }
602
459
  /**
603
460
  * What the route helper tells the content loader about the request it is
@@ -624,22 +481,18 @@ interface CanvasRouteOptions {
624
481
  loadContent: (request: CanvasContentRequest) => Promise<ContentSource>;
625
482
  /** Host component surface from `defineHostComponents`. */
626
483
  components: HostComponents;
627
- /** Extra binding payloads per request (e.g. `listDocuments` for a list page). */
628
- payloads?: CanvasPayloadsFactory;
629
484
  /**
630
485
  * EC-146 instant publish: cache the published-perspective resolution in
631
486
  * Next's data cache (`unstable_cache`), tagged with the response's own
632
487
  * fetch-time tags, invalidated by the `/api/revalidate` webhook. Draft mode
633
488
  * always bypasses this cache entirely. Tag-driven only — no time-based
634
- * revalidation (the AD-5 model). Cached values are JSON-serialized, so
635
- * payload factories must return plain data on the cached path.
489
+ * revalidation (the AD-5 model). Cached values are JSON-serialized plain data.
636
490
  */
637
491
  cache?: CanvasCacheOptions;
638
492
  /**
639
493
  * EC-146 hook: receives the merged `CacheTags` of everything this request
640
- * read (the `resolvePage` tags plus any tags the payload factory
641
- * registered via `addTags`). On a cache hit these are the stored tags of
642
- * the run that wrote the entry.
494
+ * read (the `resolvePage` tags plus the baked relations/listings/links). On a
495
+ * cache hit these are the stored tags of the run that wrote the entry.
643
496
  */
644
497
  onTags?: (tags: CacheTags, info: CanvasRequestInfo) => void;
645
498
  /** Override the generated `<head>` metadata for a resolved page. */
@@ -1411,7 +1264,9 @@ declare function createPreviewRoute(options: PreviewRouteOptions): PreviewRouteH
1411
1264
  /**
1412
1265
  * Create the preview-disable route handler (e.g.
1413
1266
  * `app/api/preview/disable/route.ts`). Disabling needs no token — it only
1414
- * ever reduces visibility back to the published perspective.
1267
+ * ever reduces visibility back to the published perspective. The `redirect`
1268
+ * target passes the same same-site check as the enable route; an off-site one
1269
+ * redirects to `/` instead.
1415
1270
  */
1416
1271
  declare function createPreviewDisableRoute(): PreviewRouteHandlers;
1417
1272
 
@@ -1503,13 +1358,16 @@ interface ElytraHostOptions {
1503
1358
  * / `WorkspaceProjectEntry` annotation did not. Both production consumers hit
1504
1359
  * exactly that and each carried one hand-restated `components` line (EC-539).
1505
1360
  *
1506
- * What this costs, stated plainly rather than glossed: a list passed HERE no
1507
- * longer gets a compile-time check that each `implementation` is really a
1508
- * component, and nothing yet checks it at runtime either — a garbage value
1509
- * would reach the renderer. `defineHostComponents` keeps the strict
1510
- * {@link HostComponent} signature, so a hand-built list passed there directly
1511
- * is still checked. Closing the gap properly needs a registration issue code
1512
- * in the core registry; booked as EC-541 rather than smuggled in here.
1361
+ * What the widening costs, and where it is paid (EC-541): a list passed HERE
1362
+ * gets no compile-time check that each `implementation` is really a
1363
+ * component. The check is RUNTIME instead — `defineHostComponents` takes the
1364
+ * same `HostComponentEntry` shape (no cast, one signature) and tests every
1365
+ * implementation on registration; a value that is not a component becomes an
1366
+ * `invalid-implementation` issue in `components.issues` and is withheld from
1367
+ * the implementation map, so its nodes render the visible missing-component
1368
+ * fallback rather than crashing the server render. Authoring a component
1369
+ * module as the strict {@link HostComponent} keeps the compile-time check at
1370
+ * the place the implementation is written.
1513
1371
  */
1514
1372
  components?: readonly HostComponentEntry[];
1515
1373
  /**
@@ -1533,8 +1391,10 @@ interface ElytraHostOptions {
1533
1391
  * The no-backend content fallback, used whenever `ELYTRA_CONVEX_URL` +
1534
1392
  * `ELYTRA_PROJECT_ID` are not both set. Default: a build-safe EMPTY source —
1535
1393
  * consumer CI builds green with no backend, and every URL is a clean 404
1536
- * until content exists. The starter template overrides this with its seeded
1537
- * in-memory world.
1394
+ * until content exists — in every cache state, not only a cold one, because
1395
+ * the content mode is part of the route's data-cache key (see
1396
+ * {@link ElytraContentMode}). The starter template overrides this with its
1397
+ * seeded in-memory world.
1538
1398
  */
1539
1399
  fallbackContent?: (request: CanvasContentRequest) => Promise<ContentSource>;
1540
1400
  /** Chrome-globals conventions (settings/navigation collection ids, items fields). */
@@ -1544,8 +1404,6 @@ interface ElytraHostOptions {
1544
1404
  }
1545
1405
  /** Pass-through canvas-route options the kit does not own. */
1546
1406
  interface HostCanvasRouteOptions {
1547
- /** Extra binding payloads per request (see `CanvasRouteOptions.payloads`). */
1548
- payloads?: CanvasPayloadsFactory;
1549
1407
  /** Tag observation hook (see `CanvasRouteOptions.onTags`). */
1550
1408
  onTags?: (tags: CacheTags, info: CanvasRequestInfo) => void;
1551
1409
  /** Metadata override (see `CanvasRouteOptions.metadata`). */
@@ -1560,6 +1418,8 @@ interface HostCanvasRouteOptions {
1560
1418
  * Tagged data cache (instant publish). Default: enabled, scoped to the
1561
1419
  * kit's content scope so the studio's webhook tags match. `false` disables
1562
1420
  * caching entirely; an explicit `CanvasCacheOptions` overrides the scope.
1421
+ * The kit's {@link ElytraHost.contentMode} is always part of the entry key
1422
+ * (EC-543) — an override can replace the scope, never drop the mode.
1563
1423
  */
1564
1424
  cache?: CanvasCacheOptions | false;
1565
1425
  }
@@ -1584,6 +1444,15 @@ interface LoadGlobalsOptions {
1584
1444
  * §5 gets no signal that the thing under test was never exercised. Declared
1585
1445
  * once, read here, so a host can *say* which world it is in instead of
1586
1446
  * re-deriving it from `env` and drifting.
1447
+ *
1448
+ * **The mode is also part of the route's data-cache key** (EC-543). A cache
1449
+ * entry outlives the process that wrote it and a hit never runs the loader, so
1450
+ * until 0.0.85 the "clean 404" above held only on a COLD cache: measured, a
1451
+ * `next start` with both env vars blanked and a warm `.next/cache` from a live
1452
+ * run answered `200` with the live world's bytes on every cached URL while
1453
+ * reporting `empty` — the wrong-world failure EC-527 removed, one layer down.
1454
+ * Keyed on the mode (`canvasCacheKey`, `cache.ts`), a world change is a cold
1455
+ * miss per URL, never a stale hit, in either direction.
1587
1456
  */
1588
1457
  type ElytraContentMode = 'live' | 'fallback' | 'empty';
1589
1458
  /** The delivery host handle {@link createElytraHost} yields. */
@@ -1687,13 +1556,13 @@ interface LiveContentOptions {
1687
1556
  /**
1688
1557
  * The `delivery.ts` payload shape (validated field-by-field below). A
1689
1558
  * deployment IS one environment (EC-179); the snapshot carries content only
1690
- * (EC-287) — documents, assets, editorial redirects (EC-303), and persisted
1691
- * data-source definitions (EC-166).
1559
+ * (EC-287) — documents, assets, editorial redirects (EC-303). (A `sources`
1560
+ * strand from an older deployment is ignored — it belonged to the deleted
1561
+ * data-binding subsystem, EC-574.)
1692
1562
  */
1693
1563
  interface DeliverySnapshotPayload {
1694
1564
  projectId: string;
1695
1565
  redirects?: unknown[];
1696
- sources?: unknown;
1697
1566
  documents: unknown[];
1698
1567
  assets: unknown[];
1699
1568
  }
@@ -1755,13 +1624,25 @@ type PreviewEvaluation = {
1755
1624
  message: string;
1756
1625
  };
1757
1626
  /**
1758
- * Only same-site path targets are accepted (`/...` but not `//host`), so the
1759
- * preview endpoint can never be used as an open redirect.
1627
+ * The ONE same-site check for a preview redirect target, shared by the enable and
1628
+ * disable routes, so the preview endpoints can never be used as an open redirect.
1629
+ * `null` means rejected; an empty value yields `fallback`.
1630
+ *
1631
+ * EC-594: the parser a browser uses decides, not a blocklist of shapes (the old
1632
+ * blocklist caught `//host` and backslashes — EC-304 B-3 — but not `/<TAB>/host`,
1633
+ * which the parser collapses to `//host`):
1634
+ *
1635
+ * - only a path-absolute reference (`/…`) is accepted — a relative one would be
1636
+ * resolved against the route's own path by a browser, so it has no fixed meaning;
1637
+ * - any C0 control or DEL is rejected outright;
1638
+ * - the value is resolved against a fixed origin and must stay on it;
1639
+ * - what is returned is the parser's normalised `pathname + search + hash`, never
1640
+ * the raw input — and that returned target must itself stay on the origin, since
1641
+ * dot-segment removal can leave a pathname starting with `//` (`/.//host`,
1642
+ * `/..//host`, `/%2e//host`), which as a `Location` is scheme-relative.
1760
1643
  *
1761
- * EC-304 B-3: a backslash is rejected too — browsers normalize `\`→`/` when resolving
1762
- * a URL, so `/\host`, `/\/host` and `\\host` all resolve off-origin the same way
1763
- * `//host` does. A leading single `/` followed by a non-slash, with no backslash
1764
- * anywhere, is the only accepted same-site shape.
1644
+ * A scheme-relative or absolute value naming the resolution origin itself resolves
1645
+ * on it and yields its path — harmless, because only that path is returned.
1765
1646
  */
1766
1647
  declare function safeRedirectTarget(value: string | null | undefined, fallback?: string): string | null;
1767
1648
  /**
@@ -1787,4 +1668,4 @@ declare function evaluatePreviewRequest(requestUrl: string | URL, expectedToken:
1787
1668
  */
1788
1669
  declare const PACKAGE = "@elytracms/next";
1789
1670
 
1790
- export { type AssetImageLoaderOptions, type BindingPayloads, type CacheWrapper, type CacheWrapperOptions, type CachedEntry, type CanvasCacheOptions, type CanvasContentRequest, type CanvasDataSource, type CanvasPageOutcome, type CanvasPayloadContext, type CanvasPayloadsFactory, CanvasRenderer, type CanvasRendererProps, type CanvasRequestInfo, type CanvasRoute, type CanvasRouteOptions, type CanvasRouteProps, type CanvasSitemapOptions, type CanvasSitemapResult, type CanvasWrapContext, type ContentSnapshot, type ContentSnapshotOptions, type ContentSource, type CreateCanvasSitemapOptions, type DefineHostComponentsOptions, type DeliveryPresentation, ELYTRA_HOST_ENV_KEYS, type ElytraContentMode, type ElytraHost, type ElytraHostEnv, type ElytraHostImageOptions, type ElytraHostOptions, ElytraImage, type ElytraImageAsset, type ElytraImageProps, type GlobalsConventions, type HostCanvasRouteOptions, type HostComponent, type HostComponents, type LiveContentOptions, type LoadGlobalsOptions, type MaterializeSourcePayloadsInput, type MaterializedSourcePayloads, type NextImagePrimitiveOptions, PACKAGE, type PreviewEvaluation, type PreviewRouteHandlers, type PreviewRouteOptions, REVALIDATE_SIGNATURE_HEADER, type ResolveCanvasPageOptions, type ResolveGlobalsOptions, type ResolvedLink, type ResolvedNavEntry, type ResolvedNavLink, type RevalidateEvaluation, type RevalidatePayload, type RevalidateRouteHandlers, type RevalidateRouteOptions, type SiteGlobals, type SitemapParamsProvider, type SitemapRouteContext, type SitemapSkippedRoute, type SourcePayloadError, type StaticContentData, assembleLiveSource, bodyItemsOf, canvasPageMetadata, canvasSitemapEntries, collectBindingSourceIds, collectionCacheTag, createAssetImageLoader, createBindingResolver, createCanvasRoute, createCanvasSitemap, createContentSnapshot, createElytraHost, createPreviewDisableRoute, createPreviewRoute, createRevalidateRoute, createStaticContentSource, defaultBindingPayloads, defineHostComponents, docCacheTag, elytraHostImage, evaluatePreviewRequest, evaluateRevalidateRequest, firstBlockFullBleed, isSourcePayloadError, loadCachedEntry, loadLiveContent, materializeSourcePayloads, mergeCacheTags, nextCacheTags, nextImagePrimitive, pathFromSlug, projectSweepTag, resolveCanvasPage, resolveGlobals, resolveHostEnv, resolvePayloadToken, revalidatePayloadSchema, routeCacheTag, safeRedirectTarget, scopeCacheTag, signRevalidateBody, sourcePayloadError };
1671
+ export { type AssetImageLoaderOptions, type CacheWrapper, type CacheWrapperOptions, type CachedEntry, type CanvasCacheOptions, type CanvasContentRequest, type CanvasPageOutcome, CanvasRenderer, type CanvasRendererProps, type CanvasRequestInfo, type CanvasRoute, type CanvasRouteOptions, type CanvasRouteProps, type CanvasSitemapOptions, type CanvasSitemapResult, type CanvasWrapContext, type ContentSnapshot, type ContentSnapshotOptions, type ContentSource, type CreateCanvasSitemapOptions, type DefineHostComponentsOptions, type DeliveryPresentation, ELYTRA_HOST_ENV_KEYS, type ElytraContentMode, type ElytraHost, type ElytraHostEnv, type ElytraHostImageOptions, type ElytraHostOptions, ElytraImage, type ElytraImageAsset, type ElytraImageProps, type GlobalsConventions, type HostCanvasRouteOptions, type HostComponent, type HostComponents, type LiveContentOptions, type LoadGlobalsOptions, type NextImagePrimitiveOptions, PACKAGE, type PreviewEvaluation, type PreviewRouteHandlers, type PreviewRouteOptions, REVALIDATE_SIGNATURE_HEADER, type ResolveCanvasPageOptions, type ResolveGlobalsOptions, type ResolvedLink, type ResolvedNavEntry, type ResolvedNavLink, type RevalidateEvaluation, type RevalidatePayload, type RevalidateRouteHandlers, type RevalidateRouteOptions, type SiteGlobals, type SitemapParamsProvider, type SitemapRouteContext, type SitemapSkippedRoute, type StaticContentData, assembleLiveSource, bodyItemsOf, canvasPageMetadata, canvasSitemapEntries, collectionCacheTag, createAssetImageLoader, createCanvasRoute, createCanvasSitemap, createContentSnapshot, createElytraHost, createPreviewDisableRoute, createPreviewRoute, createRevalidateRoute, createStaticContentSource, defineHostComponents, docCacheTag, elytraHostImage, evaluatePreviewRequest, evaluateRevalidateRequest, firstBlockFullBleed, loadCachedEntry, loadLiveContent, mergeCacheTags, nextCacheTags, nextImagePrimitive, pathFromSlug, projectSweepTag, resolveCanvasPage, resolveGlobals, resolveHostEnv, revalidatePayloadSchema, routeCacheTag, safeRedirectTarget, scopeCacheTag, signRevalidateBody };