@elytracms/studio 0.0.79 → 0.0.81

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.
Files changed (107) hide show
  1. package/convex/contract-parity.test.ts +1 -1
  2. package/convex/functions.ts +13 -3
  3. package/convex/http-routes.test.ts +143 -0
  4. package/convex/http.ts +19 -9
  5. package/convex/schema.ts +0 -19
  6. package/convex/users.ts +1 -1
  7. package/dist/components/app-views/default-modules.d.ts +141 -44
  8. package/dist/components/app-views/default-modules.js +229 -65
  9. package/dist/components/app-views/default-modules.js.map +1 -1
  10. package/dist/components/app-views/module.d.ts +38 -0
  11. package/dist/components/app-views/module.js.map +1 -1
  12. package/dist/components/app-views/sidebar.d.ts +5 -2
  13. package/dist/components/app-views/sidebar.js +16 -5
  14. package/dist/components/app-views/sidebar.js.map +1 -1
  15. package/dist/components/app-views/studio-config.d.ts +27 -0
  16. package/dist/components/app-views/studio-config.js.map +1 -1
  17. package/dist/components/builder/canvas-render-context.js +7 -1
  18. package/dist/components/builder/canvas-render-context.js.map +1 -1
  19. package/dist/components/builder/reuse-context.js +7 -1
  20. package/dist/components/builder/reuse-context.js.map +1 -1
  21. package/dist/components/builder/reuse.d.ts +94 -16
  22. package/dist/components/builder/reuse.js +201 -19
  23. package/dist/components/builder/reuse.js.map +1 -1
  24. package/dist/components/cms/bulk-actions.js +3 -1
  25. package/dist/components/cms/bulk-actions.js.map +1 -1
  26. package/dist/components/cms/cell-edit.js +6 -2
  27. package/dist/components/cms/cell-edit.js.map +1 -1
  28. package/dist/components/cms/cms-store.d.ts +40 -13
  29. package/dist/components/cms/cms-store.js +50 -22
  30. package/dist/components/cms/cms-store.js.map +1 -1
  31. package/dist/components/cms/collection-table.js +2 -2
  32. package/dist/components/cms/collection-table.js.map +1 -1
  33. package/dist/components/cms/date-input-core.d.ts +21 -0
  34. package/dist/components/cms/date-input-core.js +20 -0
  35. package/dist/components/cms/date-input-core.js.map +1 -1
  36. package/dist/components/cms/date-input.d.ts +9 -1
  37. package/dist/components/cms/date-input.js +2 -2
  38. package/dist/components/cms/date-input.js.map +1 -1
  39. package/dist/components/cms/detail-editor.js +11 -5
  40. package/dist/components/cms/detail-editor.js.map +1 -1
  41. package/dist/components/cms/editable-cell.js +2 -1
  42. package/dist/components/cms/editable-cell.js.map +1 -1
  43. package/dist/components/cms/field-control.js +4 -2
  44. package/dist/components/cms/field-control.js.map +1 -1
  45. package/dist/components/cms/field-renderer.js +8 -1
  46. package/dist/components/cms/field-renderer.js.map +1 -1
  47. package/dist/components/cms/import-export.js +2 -1
  48. package/dist/components/cms/import-export.js.map +1 -1
  49. package/dist/components/cms/issues-pane.d.ts +4 -2
  50. package/dist/components/cms/issues-pane.js +6 -4
  51. package/dist/components/cms/issues-pane.js.map +1 -1
  52. package/dist/components/cms/scalar-value-editor.js +2 -1
  53. package/dist/components/cms/scalar-value-editor.js.map +1 -1
  54. package/dist/components/components/transfer-dialog.js +10 -12
  55. package/dist/components/components/transfer-dialog.js.map +1 -1
  56. package/dist/components/project-list.d.ts +9 -3
  57. package/dist/components/project-list.js +11 -5
  58. package/dist/components/project-list.js.map +1 -1
  59. package/dist/components/project-switcher.d.ts +3 -1
  60. package/dist/components/project-switcher.js +4 -2
  61. package/dist/components/project-switcher.js.map +1 -1
  62. package/dist/components/redirects/redirects-store.d.ts +21 -0
  63. package/dist/components/redirects/redirects-store.js +17 -4
  64. package/dist/components/redirects/redirects-store.js.map +1 -1
  65. package/dist/components/section-nav.js +102 -4
  66. package/dist/components/section-nav.js.map +1 -1
  67. package/dist/customization.d.ts +7 -0
  68. package/dist/customization.js.map +1 -1
  69. package/dist/lib/data/collection-content.d.ts +6 -1
  70. package/dist/lib/data/collection-content.js +50 -12
  71. package/dist/lib/data/collection-content.js.map +1 -1
  72. package/dist/lib/i18n/messages.de.js +132 -0
  73. package/dist/lib/i18n/messages.de.js.map +1 -1
  74. package/dist/lib/i18n/messages.en.d.ts +103 -0
  75. package/dist/lib/i18n/messages.en.js +146 -0
  76. package/dist/lib/i18n/messages.en.js.map +1 -1
  77. package/dist/lib/locale/binding-fallback.d.ts +8 -1
  78. package/dist/lib/locale/binding-fallback.js +8 -1
  79. package/dist/lib/locale/binding-fallback.js.map +1 -1
  80. package/dist/lib/locale/locale-store.d.ts +41 -9
  81. package/dist/lib/locale/locale-store.js +34 -15
  82. package/dist/lib/locale/locale-store.js.map +1 -1
  83. package/dist/lib/locale/locale-switcher.d.ts +3 -1
  84. package/dist/lib/locale/locale-switcher.js +4 -2
  85. package/dist/lib/locale/locale-switcher.js.map +1 -1
  86. package/dist/lib/locale/use-studio-locale.d.ts +9 -4
  87. package/dist/lib/locale/use-studio-locale.js.map +1 -1
  88. package/dist/lib/persistence/convex-adapter.d.ts +1 -3
  89. package/dist/lib/persistence/convex-adapter.js +0 -55
  90. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  91. package/dist/lib/persona/labels.d.ts +42 -1
  92. package/dist/lib/persona/labels.js +57 -0
  93. package/dist/lib/persona/labels.js.map +1 -1
  94. package/dist/lib/routes/route-issues.d.ts +9 -0
  95. package/dist/lib/routes/route-issues.js +117 -0
  96. package/dist/lib/routes/route-issues.js.map +1 -0
  97. package/dist/lib/workspace/config-issues.d.ts +54 -0
  98. package/dist/lib/workspace/config-issues.js +70 -0
  99. package/dist/lib/workspace/config-issues.js.map +1 -0
  100. package/dist/lib/workspace/index.d.ts +9 -0
  101. package/dist/lib/workspace/index.js +11 -0
  102. package/dist/lib/workspace/index.js.map +1 -1
  103. package/dist/lib/workspace/switcher-source.d.ts +20 -3
  104. package/dist/lib/workspace/switcher-source.js.map +1 -1
  105. package/package.json +2 -2
  106. package/convex/cliTokens.ts +0 -63
  107. package/convex/sync.ts +0 -297
@@ -1 +1 @@
1
- {"version":3,"file":"collection-content.js","sourceRoot":"","sources":["../../../src/lib/data/collection-content.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createContentClient,\n createContentContext,\n queryError,\n validateLimit,\n validateSort,\n validateWhere,\n} from '@elytracms/core/content'\nimport type {\n ContentClient,\n ContentDocumentSource,\n ContentLookup,\n ContentQueryError,\n GetDocumentResult,\n ListDocumentsQuery,\n ListDocumentsResult,\n ResolvedDocument,\n} from '@elytracms/core/content'\nimport { documentKey, resolveDocumentTitle, sampleLocaleConfig } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n Locale,\n LocaleConfig,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { ListingQuery } from '@elytracms/core/runtime-renderer'\nimport { getStudioLocale } from '../locale/locale-store'\n\n/**\n * The studio's content delivery engine for canvas previews (EC-166).\n *\n * Repeating sections and template pages preview *delivery-shaped* documents —\n * the exact shape `@elytracms/core/content` serves in production — so the canvas\n * shows what publishing will show. This module wires the canonical content\n * client (closed AD-3 query vocabulary, published perspective, EC-018 locale\n * fallback, depth-1 population, resolved assets) over a **content session**:\n *\n * - Convex mode: the project-session gate (`components/studio/project-session`)\n * configures the session with the project's HYDRATED schema + documents, so\n * \"Preview with\" lists and section previews show the live content — never\n * fixture titles (EC-177 gap 1).\n * - Playground mode (EC-361): the SAME hydration loader runs over the seeded\n * in-memory adapter (through operations — AD-17), so canvas listing/relation\n * population resolves from the in-memory session documents with identical\n * semantics. With no session configured (tests, boot instants, no persisted\n * content) the engine serves nothing.\n *\n * Nothing here invents query semantics; both modes go through the same\n * canonical client.\n */\n\n/** The schema + documents the preview engine serves (one project session). */\nexport interface ContentSessionState {\n collections: readonly CollectionDef[]\n documents: readonly CmsDocument[]\n /**\n * Published document keys via the version POINTER (EC-224). When present, the\n * preview's published perspective resolves by the pin — never the flat\n * `state` enum — so it matches what delivery serves. Absent (playground\n * fixtures / tests) → the rows' own `state` gates the perspective, unchanged.\n */\n publishedKeys?: readonly string[]\n /** Locale config of the session content; defaults to the sample config. */\n localeConfig?: LocaleConfig\n /**\n * The project's config-sourced routes (EC-207). Wired into the content client so\n * the canvas preview's `resolveLink` (EC-291) derives a rich-text link's URL from\n * the SAME routes delivery serves — the studio-live half of the host link bake.\n * Absent (playground/tests) → no routes, so `resolveLink` returns null and a\n * reference link degrades to the visible `unresolved-ref` issue.\n */\n routes?: readonly RouteRecord[]\n /**\n * The project's asset records (assets table). Wired into the lookup so the\n * content client resolves `asset`-typed FIELDS on listing/relation results\n * (e.g. a ProductGrid product's `gallery`) to delivery shape — the same assets\n * the renderer's `resolveAsset` reads for static props (EC-262). Absent\n * (playground/tests) → assets come only from `asset`-collection fixture docs.\n */\n assets?: readonly AssetRecord[]\n}\n\n/**\n * Default (no-session) collection schema: EMPTY. The studio ships no demo content\n * (Oli, 2026-06-19) — with no backend/content session configured, there is nothing\n * to preview, and the studio says so rather than serving fixtures. A real project's\n * collections + documents arrive via {@link configureContentSession} in BOTH modes\n * (EC-361): Convex hydration, or the playground's seeded in-memory adapter through\n * the same loader.\n */\nexport const playgroundCollections: readonly CollectionDef[] = []\n\n/**\n * The perspective a canvas preview resolves references against (EC-300/AD-16). The\n * canvas is draft-coherent: the editable Draft view resolves references to their\n * `'draft'` targets (so a just-made reusable shows immediately), the read-only\n * Published view resolves `'published'` (an honest preview of what delivery serves;\n * an unpublished hard ref shows the in-canvas placeholder). Delivery stays\n * published-only — this does not weaken `live-is-published-only`.\n */\nexport type CanvasPerspective = 'draft' | 'published'\n\nlet contentSession: ContentSessionState | null = null\nlet lookup: ContentLookup = createLookup(playgroundCollections, [], [])\n// Keyed by `${locale}:${perspective}` (EC-300) — one deterministic client per pair.\nconst clients = new Map<string, ContentClient>()\n\n// EC-460: a monotonically increasing session epoch + listener set, so a REACT\n// surface (the canvas render context) can re-render when the session is\n// re-pointed at fresher content. The session itself stays a module global (the\n// EC-361 design); this only makes \"it changed\" observable — `configureContentSession`\n// remains the single setter.\nlet sessionEpoch = 0\nconst sessionListeners = new Set<() => void>()\n\n/** The current content-session epoch — bumps on every `configureContentSession`. */\nexport function contentSessionEpoch(): number {\n return sessionEpoch\n}\n\n/**\n * Subscribe to content-session swaps (EC-460). The listener fires after every\n * `configureContentSession`; returns the unsubscribe. Shaped for\n * `useSyncExternalStore(subscribeContentSession, contentSessionEpoch)`.\n */\nexport function subscribeContentSession(listener: () => void): () => void {\n sessionListeners.add(listener)\n return () => sessionListeners.delete(listener)\n}\n\n/**\n * Point the preview engine at a project's hydrated content — Convex hydration\n * or the playground's in-memory persisted state (EC-361), both through the same\n * `loadHydratedCms` loader — or back at the empty no-session default (`null`).\n */\nexport function configureContentSession(next: ContentSessionState | null): void {\n contentSession = next\n lookup = next\n ? createLookup(next.collections, next.documents, next.publishedKeys, next.assets)\n : createLookup(playgroundCollections, [], [])\n clients.clear()\n sessionEpoch += 1\n for (const listener of [...sessionListeners]) listener()\n}\n\n/** Whether a hydrated content session is configured (Convex mode). */\nexport function hasContentSession(): boolean {\n return contentSession !== null\n}\n\n/** The collections the preview engine currently serves (session or fixtures). */\nfunction activeCollections(): readonly CollectionDef[] {\n return contentSession?.collections ?? playgroundCollections\n}\n\nfunction activeLocaleConfig(): LocaleConfig {\n return contentSession?.localeConfig ?? sampleLocaleConfig\n}\n\n/** Resolve a collection in the active content session (fixtures in playground). */\nexport function playgroundCollection(id: string): CollectionDef | undefined {\n return activeCollections().find((collection) => collection.id === id)\n}\n\n/**\n * Overlay the active session's canonical `filterable` declarations onto a\n * schema that doesn't carry any of its own. The inspector hands the Content\n * tab fixture collection defs; the filter/sort pickers must offer exactly the\n * EC-143 declaration set the delivery engine enforces, so both ends agree. A\n * collection that declares its own filterable fields is left untouched.\n */\nexport function withPlaygroundFilterable(\n collections: readonly CollectionDef[],\n): readonly CollectionDef[] {\n return collections.map((collection) => {\n if (collection.fields.some((field) => field.filterable === true)) return collection\n const counterpart = playgroundCollection(collection.id)\n if (counterpart === undefined) return collection\n const flagged = new Set(\n counterpart.fields.filter((field) => field.filterable === true).map((field) => field.name),\n )\n if (flagged.size === 0) return collection\n return {\n ...collection,\n fields: collection.fields.map((field) =>\n flagged.has(field.name) ? { ...field, filterable: true } : field,\n ),\n }\n })\n}\n\n/** Synthesize the persistence-shaped asset record the asset resolver reads. */\nfunction assetRecordOf(doc: CmsDocument): AssetRecord {\n const values = doc.values\n const alt = doc.localized?.en?.alt\n return {\n id: doc.id,\n projectId: 'playground',\n filename: typeof values.filename === 'string' ? values.filename : doc.id,\n contentType: 'image/png',\n byteSize: 0,\n alt: typeof alt === 'string' ? alt : undefined,\n storageKey: typeof values.url === 'string' ? values.url : doc.id,\n createdAt: '2026-01-01T00:00:00.000Z',\n updatedAt: '2026-01-01T00:00:00.000Z',\n }\n}\n\nfunction createLookup(\n collectionDefs: readonly CollectionDef[],\n documents: readonly CmsDocument[],\n publishedKeys?: readonly string[],\n assetRecords?: readonly AssetRecord[],\n): ContentLookup {\n const collections = new Map(collectionDefs.map((c) => [c.id, c]))\n // EC-224: published-ness is the version POINTER, not the flat `state` enum.\n // A hydrated session hands `publishedKeys` (the pinned set) → wrap each row as\n // a `DocumentHistory` so the published perspective resolves by the pin (the\n // live row is always a draft now) with ZERO dependence on `state`. Without a\n // pointer set (playground fixtures / tests) we keep bare rows, whose own\n // `state` gates the published perspective — byte-identical to before.\n const published = publishedKeys ? new Set(publishedKeys) : null\n const toSource = (doc: CmsDocument): ContentDocumentSource => {\n if (!published) return doc\n return published.has(documentKey({ collection: doc.collection, id: doc.id }))\n ? { draft: doc, published: doc, versions: [] }\n : { draft: doc, published: undefined, versions: [] }\n }\n const byKey = new Map(\n documents.map((doc) => [`${doc.collection}:${doc.id}`, toSource(doc)] as const),\n )\n // Asset resolution source for `asset`-typed FIELDS on resolved documents\n // (listing/relation results, e.g. a product's `gallery`). Two sources, real\n // records last so they win: (1) `asset`-collection fixture documents — how\n // playground/tests model assets; (2) the project's real asset-table records\n // (EC-262) — a Convex session has its assets here, NOT as `asset` documents,\n // so without these the content client resolves no nested image.\n const assets = new Map<string, AssetRecord>()\n for (const doc of documents) {\n if (doc.collection === 'asset') assets.set(doc.id, assetRecordOf(doc))\n }\n for (const record of assetRecords ?? []) assets.set(record.id, record)\n return {\n collection: (id) => collections.get(id),\n document: (ref) => byKey.get(`${ref.collection}:${ref.id}`),\n documents: (collectionId) =>\n documents.filter((doc) => doc.collection === collectionId).map(toSource),\n asset: (id) => assets.get(id),\n // Prefer the record's resolved servable `url` — the one the renderer's\n // resolveAsset reads (EC-262). Real asset-table records carry a Convex blob\n // locator in `storageKey` and the servable URL in `url`; fixture/`asset`-document\n // records (assetRecordOf) leave `url` unset and store the servable url in\n // `storageKey`. Falling back keeps both correct.\n assetUrl: (asset) => asset.url ?? asset.storageKey,\n }\n}\n\n/**\n * One deterministic client per (locale, perspective): EC-018 locale fallback,\n * draft-or-published perspective (EC-300/AD-16, default `'published'`). The studio's\n * ambient active locale (EC-176, `lib/locale`) picks the locale at query time, so the\n * canvas previews follow the locale switcher exactly like delivery would; the canvas's\n * Draft/Published view picks the perspective. The cache resets whenever the content\n * session changes.\n */\nfunction clientFor(locale: Locale, perspective: CanvasPerspective = 'published'): ContentClient {\n const key = `${locale}:${perspective}`\n const existing = clients.get(key)\n if (existing) return existing\n const client = createContentClient({\n lookup,\n context: createContentContext({\n locale,\n perspective,\n locales: activeLocaleConfig(),\n }),\n // EC-291: the routes a rich-text link resolves against (config-sourced) — the\n // studio-live half of the host link bake.\n routes: contentSession?.routes ?? [],\n })\n clients.set(key, client)\n return client\n}\n\n/**\n * Run a closed-vocabulary list query (AD-3: eq/in/lt/lte/gt/gte, AND-only,\n * filterable fields only) and return the delivery-shaped result. Invalid\n * queries come back as structured errors — never thrown, never silently\n * widened. Resolves in the studio's active locale unless one is passed.\n */\nexport function runCollectionListQuery(\n collectionId: string,\n query: ListDocumentsQuery = {},\n locale: Locale = getStudioLocale(),\n): ListDocumentsResult {\n return clientFor(locale).listDocuments(collectionId, query)\n}\n\n/** Resolve one document (by id or slug) into delivery shape. */\nexport function getCollectionDocument(\n collectionId: string,\n idOrSlug: string,\n locale: Locale = getStudioLocale(),\n): GetDocumentResult {\n return clientFor(locale).getDocument(collectionId, idOrSlug)\n}\n\n/**\n * Populate a composition-node `relation` prop to delivery shape for the canvas\n * (EC-254) — the studio analog of the host's baked relations map. Resolves the\n * ref through the active session's client (published perspective, active locale),\n * so the canvas previews a block's picked documents (a ProductGrid's products, a\n * Reusable's element) exactly as delivery renders them. `null` when the target is\n * missing or not visible → the block degrades to its own empty state.\n */\nexport function resolveCanvasRelation(\n ref: DocumentRef,\n locale: Locale = getStudioLocale(),\n perspective: CanvasPerspective = 'published',\n): ResolvedDocument | null {\n return clientFor(locale, perspective).resolveRelation(ref)\n}\n\n/**\n * Resolve a listing block's query to its documents for the canvas (EC-255) — the\n * studio analog of the host's baked listings map. Runs the query (collection +\n * AND-only filter, optional sort/limit) through the active session's client\n * (published perspective, active locale), so the canvas previews a dynamic grid (a\n * ProductGrid keyed to a category) exactly as delivery renders it. An invalid query\n * (structured, never thrown) or a non-list lookup yields `[]` → the block's empty\n * state. Reuses the same `listDocuments` path as `runCollectionListQuery`.\n */\nexport function resolveCanvasListing(\n query: ListingQuery,\n locale: Locale = getStudioLocale(),\n perspective: CanvasPerspective = 'published',\n): ResolvedDocument[] {\n const result = clientFor(locale, perspective).listDocuments(query.collection, {\n where: query.where,\n ...(query.sort ? { sort: query.sort } : {}),\n ...(query.limit !== undefined ? { limit: query.limit } : {}),\n })\n return result.ok ? result.documents : []\n}\n\n/**\n * Statically validate a listing block's query against the ACTIVE session schema\n * (EC-460, the never-silent rule): the exact `validateWhere`/`validateSort`/\n * `validateLimit` checks `listDocuments` runs, WITHOUT executing the query — so\n * the canvas can surface a config-invalid listing (a filter/sort field the\n * collection doesn't declare `filterable`, an unknown collection) as a visible\n * notice instead of the block's indistinguishable empty state. With no content\n * session configured (boot instants, fixtures) there is no schema to judge\n * against, so it reports nothing rather than false-flagging every listing.\n */\nexport function validateCanvasListingQuery(query: ListingQuery): ContentQueryError[] {\n if (contentSession === null) return []\n const collection = playgroundCollection(query.collection)\n if (!collection) {\n return [\n queryError('unknown-collection', `Collection \"${query.collection}\" is not defined.`, [\n 'collection',\n ]),\n ]\n }\n return [\n ...validateWhere(collection, query.where),\n ...(query.sort ? validateSort(collection, query.sort) : []),\n ...(query.limit !== undefined ? validateLimit(query.limit) : []),\n ]\n}\n\n/**\n * Resolve a rich-text internal link's target document to its URL path for the canvas\n * (EC-291) — the studio analog of the host's baked links map. Derives the URL through\n * the active session's client (published perspective, active locale, config-sourced\n * routes), so the canvas previews a `{type:'reference'}` link's `href` exactly as\n * delivery serves it. `null` when the target is unpublished, missing, or addressed by\n * no route → the rich-text renderer shows the EC-290 `unresolved-ref` issue. Reuses\n * the same `resolveLink` path as the host bake, so both agree on the derived URL.\n */\nexport function resolveCanvasLink(\n ref: DocumentRef,\n locale: Locale = getStudioLocale(),\n perspective: CanvasPerspective = 'published',\n): string | null {\n return clientFor(locale, perspective).resolveLink(ref)\n}\n\n/** A pickable entry of a collection (the template-page preview switcher). */\nexport interface CollectionEntrySummary {\n id: string\n label: string\n}\n\n/** Published entries of a collection, labeled by the schema's title field. */\nexport function listCollectionEntries(collectionId: string): CollectionEntrySummary[] {\n const collection = playgroundCollection(collectionId)\n const result = runCollectionListQuery(collectionId)\n if (!result.ok) return []\n // EC-242: label via the shared identity resolver (string `titleField` or a\n // function pattern), over the delivery-shape values; falls back to the id.\n return result.documents.map((doc) => ({\n id: doc.id,\n label: collection ? resolveDocumentTitle(collection, doc, doc.id) : doc.id,\n }))\n}\n"]}
1
+ {"version":3,"file":"collection-content.js","sourceRoot":"","sources":["../../../src/lib/data/collection-content.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,yBAAyB,CAAA;AAWhC,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAW5E,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AA6D/E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA6B,EAAE,CAAA;AAYjE,IAAI,cAAc,GAA+B,IAAI,CAAA;AACrD,IAAI,MAAM,GAAkB,YAAY,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;AACvE,iFAAiF;AACjF,6EAA6E;AAC7E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAA;AAEhD,8EAA8E;AAC9E,wEAAwE;AACxE,+EAA+E;AAC/E,sFAAsF;AACtF,6BAA6B;AAC7B,IAAI,YAAY,GAAG,CAAC,CAAA;AACpB,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAc,CAAA;AAE9C,oFAAoF;AACpF,MAAM,UAAU,mBAAmB;IACjC,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAoB;IAC1D,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC9B,OAAO,GAAG,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAgC;IACtE,cAAc,GAAG,IAAI,CAAA;IACrB,MAAM,GAAG,IAAI;QACX,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC;QACjF,CAAC,CAAC,YAAY,CAAC,qBAAqB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IAC/C,OAAO,CAAC,KAAK,EAAE,CAAA;IACf,YAAY,IAAI,CAAC,CAAA;IACjB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,gBAAgB,CAAC;QAAE,QAAQ,EAAE,CAAA;AAC1D,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,iBAAiB;IAC/B,OAAO,cAAc,KAAK,IAAI,CAAA;AAChC,CAAC;AAED,iFAAiF;AACjF,SAAS,iBAAiB;IACxB,OAAO,cAAc,EAAE,WAAW,IAAI,qBAAqB,CAAA;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,SAAS,GAAG,EAAE,CAAA;AAEpB;;;;;;GAMG;AACH,SAAS,aAAa;IACpB,OAAO,eAAe,EAAE,IAAI,cAAc,EAAE,YAAY,EAAE,OAAO,IAAI,SAAS,CAAA;AAChF,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,kBAAkB,CAAC,MAAc;IACxC,OAAO,CACL,cAAc,EAAE,YAAY;QAC5B,qBAAqB,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAClE,CAAA;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,OAAO,iBAAiB,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AACvE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,WAAqC;IAErC,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QACpC,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;YAAE,OAAO,UAAU,CAAA;QACnF,MAAM,WAAW,GAAG,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;QACvD,IAAI,WAAW,KAAK,SAAS;YAAE,OAAO,UAAU,CAAA;QAChD,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC3F,CAAA;QACD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,UAAU,CAAA;QACzC,OAAO;YACL,GAAG,UAAU;YACb,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACtC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CACjE;SACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,aAAa,CAAC,GAAgB;IACrC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAA;IACzB,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,CAAA;IAClC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,YAAY;QACvB,QAAQ,EAAE,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;QACxE,WAAW,EAAE,WAAW;QACxB,QAAQ,EAAE,CAAC;QACX,GAAG,EAAE,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAC9C,UAAU,EAAE,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;QAChE,SAAS,EAAE,0BAA0B;QACrC,SAAS,EAAE,0BAA0B;KACtC,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CACnB,cAAwC,EACxC,SAAiC,EACjC,aAAiC,EACjC,YAAqC;IAErC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,4EAA4E;IAC5E,+EAA+E;IAC/E,4EAA4E;IAC5E,6EAA6E;IAC7E,yEAAyE;IACzE,sEAAsE;IACtE,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC/D,MAAM,QAAQ,GAAG,CAAC,GAAgB,EAAyB,EAAE;QAC3D,IAAI,CAAC,SAAS;YAAE,OAAO,GAAG,CAAA;QAC1B,OAAO,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3E,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE;YAC9C,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IACxD,CAAC,CAAA;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,CACnB,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAU,CAAC,CAChF,CAAA;IACD,yEAAyE;IACzE,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,gEAAgE;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAA;IAC7C,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,UAAU,KAAK,OAAO;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAA;IACxE,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,YAAY,IAAI,EAAE;QAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACtE,OAAO;QACL,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;QAC3D,SAAS,EAAE,CAAC,YAAY,EAAE,EAAE,CAC1B,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,KAAK,YAAY,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1E,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,uEAAuE;QACvE,4EAA4E;QAC5E,kFAAkF;QAClF,0EAA0E;QAC1E,iDAAiD;QACjD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,UAAU;KACnD,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,SAAS,CAAC,MAAc,EAAE,cAAiC,WAAW;IAC7E,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAC1C,+EAA+E;IAC/E,gFAAgF;IAChF,4EAA4E;IAC5E,mCAAmC;IACnC,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,WAAW,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;IACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,MAAM,MAAM,GAAG,mBAAmB,CAAC;QACjC,MAAM;QACN,OAAO,EAAE,oBAAoB,CAAC;YAC5B,MAAM;YACN,WAAW;YACX,OAAO;SACR,CAAC;QACF,8EAA8E;QAC9E,0CAA0C;QAC1C,MAAM,EAAE,cAAc,EAAE,MAAM,IAAI,EAAE;KACrC,CAAC,CAAA;IACF,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACxB,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,YAAoB,EACpB,QAA4B,EAAE,EAC9B,SAAiB,aAAa,EAAE;IAEhC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,YAAY,EAAE,KAAK,CAAC,CAAA;AAC7D,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,qBAAqB,CACnC,YAAoB,EACpB,QAAgB,EAChB,SAAiB,aAAa,EAAE;IAEhC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAgB,EAChB,SAAiB,aAAa,EAAE,EAChC,cAAiC,WAAW;IAE5C,OAAO,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;AAC5D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAmB,EACnB,SAAiB,aAAa,EAAE,EAChC,cAAiC,WAAW;IAE5C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,EAAE;QAC5E,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7D,CAAC,CAAA;IACF,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;AAC1C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAmB;IAC5D,IAAI,cAAc,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IACtC,MAAM,UAAU,GAAG,oBAAoB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACzD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,UAAU,CAAC,oBAAoB,EAAE,eAAe,KAAK,CAAC,UAAU,mBAAmB,EAAE;gBACnF,YAAY;aACb,CAAC;SACH,CAAA;IACH,CAAC;IACD,OAAO;QACL,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;QACzC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAAgB,EAChB,SAAiB,aAAa,EAAE,EAChC,cAAiC,WAAW;IAE5C,OAAO,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;AACxD,CAAC;AAQD,8EAA8E;AAC9E,MAAM,UAAU,qBAAqB,CAAC,YAAoB;IACxD,MAAM,UAAU,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,sBAAsB,CAAC,YAAY,CAAC,CAAA;IACnD,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,CAAA;IACzB,2EAA2E;IAC3E,2EAA2E;IAC3E,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACpC,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;KAC3E,CAAC,CAAC,CAAA;AACL,CAAC","sourcesContent":["import {\n createContentClient,\n createContentContext,\n queryError,\n validateLimit,\n validateSort,\n validateWhere,\n} from '@elytracms/core/content'\nimport type {\n ContentClient,\n ContentDocumentSource,\n ContentLookup,\n ContentQueryError,\n GetDocumentResult,\n ListDocumentsQuery,\n ListDocumentsResult,\n ResolvedDocument,\n} from '@elytracms/core/content'\nimport { documentKey, resolveDocumentTitle } from '@elytracms/core/cms-core'\nimport type {\n CmsDocument,\n CollectionDef,\n DocumentRef,\n Locale,\n LocaleConfig,\n RouteRecord,\n} from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { ListingQuery } from '@elytracms/core/runtime-renderer'\nimport { getStudioLocale, getStudioLocaleConfig } from '../locale/locale-store'\n\n/**\n * The studio's content delivery engine for canvas previews (EC-166).\n *\n * Repeating sections and template pages preview *delivery-shaped* documents —\n * the exact shape `@elytracms/core/content` serves in production — so the canvas\n * shows what publishing will show. This module wires the canonical content\n * client (closed AD-3 query vocabulary, published perspective, EC-018 locale\n * fallback, depth-1 population, resolved assets) over a **content session**:\n *\n * - Convex mode: the project-session gate (`components/studio/project-session`)\n * configures the session with the project's HYDRATED schema + documents, so\n * \"Preview with\" lists and section previews show the live content — never\n * fixture titles (EC-177 gap 1).\n * - Playground mode (EC-361): the SAME hydration loader runs over the seeded\n * in-memory adapter (through operations — AD-17), so canvas listing/relation\n * population resolves from the in-memory session documents with identical\n * semantics. With no session configured (tests, boot instants, no persisted\n * content) the engine serves nothing.\n *\n * Nothing here invents query semantics; both modes go through the same\n * canonical client.\n */\n\n/** The schema + documents the preview engine serves (one project session). */\nexport interface ContentSessionState {\n collections: readonly CollectionDef[]\n documents: readonly CmsDocument[]\n /**\n * Published document keys via the version POINTER (EC-224). When present, the\n * preview's published perspective resolves by the pin — never the flat\n * `state` enum — so it matches what delivery serves. Absent (playground\n * fixtures / tests) → the rows' own `state` gates the perspective, unchanged.\n */\n publishedKeys?: readonly string[]\n /**\n * Locale config of the session content — the project's own, composed by\n * `contentSessionOf` from `elytra.config.ts`. Absent (a kit session, tests) →\n * the ambient studio config, then the single locale being asked for; EC-479\n * retired the engine's `sampleLocaleConfig` fallback here.\n */\n localeConfig?: LocaleConfig\n /**\n * The project's config-sourced routes (EC-207). Wired into the content client so\n * the canvas preview's `resolveLink` (EC-291) derives a rich-text link's URL from\n * the SAME routes delivery serves — the studio-live half of the host link bake.\n * Absent (playground/tests) → no routes, so `resolveLink` returns null and a\n * reference link degrades to the visible `unresolved-ref` issue.\n */\n routes?: readonly RouteRecord[]\n /**\n * The project's asset records (assets table). Wired into the lookup so the\n * content client resolves `asset`-typed FIELDS on listing/relation results\n * (e.g. a ProductGrid product's `gallery`) to delivery shape — the same assets\n * the renderer's `resolveAsset` reads for static props (EC-262). Absent\n * (playground/tests) → assets come only from `asset`-collection fixture docs.\n */\n assets?: readonly AssetRecord[]\n}\n\n/**\n * Default (no-session) collection schema: EMPTY. The studio ships no demo content\n * (Oli, 2026-06-19) — with no backend/content session configured, there is nothing\n * to preview, and the studio says so rather than serving fixtures. A real project's\n * collections + documents arrive via {@link configureContentSession} in BOTH modes\n * (EC-361): Convex hydration, or the playground's seeded in-memory adapter through\n * the same loader.\n */\nexport const playgroundCollections: readonly CollectionDef[] = []\n\n/**\n * The perspective a canvas preview resolves references against (EC-300/AD-16). The\n * canvas is draft-coherent: the editable Draft view resolves references to their\n * `'draft'` targets (so a just-made reusable shows immediately), the read-only\n * Published view resolves `'published'` (an honest preview of what delivery serves;\n * an unpublished hard ref shows the in-canvas placeholder). Delivery stays\n * published-only — this does not weaken `live-is-published-only`.\n */\nexport type CanvasPerspective = 'draft' | 'published'\n\nlet contentSession: ContentSessionState | null = null\nlet lookup: ContentLookup = createLookup(playgroundCollections, [], [])\n// Keyed by locale + perspective (EC-300) + the resolved locale CONFIG (EC-479) —\n// one deterministic client per distinct resolution context. See `clientFor`.\nconst clients = new Map<string, ContentClient>()\n\n// EC-460: a monotonically increasing session epoch + listener set, so a REACT\n// surface (the canvas render context) can re-render when the session is\n// re-pointed at fresher content. The session itself stays a module global (the\n// EC-361 design); this only makes \"it changed\" observable — `configureContentSession`\n// remains the single setter.\nlet sessionEpoch = 0\nconst sessionListeners = new Set<() => void>()\n\n/** The current content-session epoch — bumps on every `configureContentSession`. */\nexport function contentSessionEpoch(): number {\n return sessionEpoch\n}\n\n/**\n * Subscribe to content-session swaps (EC-460). The listener fires after every\n * `configureContentSession`; returns the unsubscribe. Shaped for\n * `useSyncExternalStore(subscribeContentSession, contentSessionEpoch)`.\n */\nexport function subscribeContentSession(listener: () => void): () => void {\n sessionListeners.add(listener)\n return () => sessionListeners.delete(listener)\n}\n\n/**\n * Point the preview engine at a project's hydrated content — Convex hydration\n * or the playground's in-memory persisted state (EC-361), both through the same\n * `loadHydratedCms` loader — or back at the empty no-session default (`null`).\n */\nexport function configureContentSession(next: ContentSessionState | null): void {\n contentSession = next\n lookup = next\n ? createLookup(next.collections, next.documents, next.publishedKeys, next.assets)\n : createLookup(playgroundCollections, [], [])\n clients.clear()\n sessionEpoch += 1\n for (const listener of [...sessionListeners]) listener()\n}\n\n/** Whether a hydrated content session is configured (Convex mode). */\nexport function hasContentSession(): boolean {\n return contentSession !== null\n}\n\n/** The collections the preview engine currently serves (session or fixtures). */\nfunction activeCollections(): readonly CollectionDef[] {\n return contentSession?.collections ?? playgroundCollections\n}\n\n/**\n * The sentinel {@link previewLocale} yields when NOTHING has declared a locale —\n * no ambient studio config, no content session. It is not a locale code and\n * matches no stored key, which is the point: with no session the lookup is empty\n * (`playgroundCollections` is `[]`), so every query already resolves to nothing\n * and this only fills `CacheTags.locale`, which no studio surface reads. EC-479:\n * the engine used to answer this question with its `sampleLocaleConfig` fixture's\n * `en` — a locale nobody chose, standing in for the project's own.\n */\nconst NO_LOCALE = ''\n\n/**\n * The locale a canvas preview resolves in, when the caller names none: the\n * ambient studio locale (installed from the PROJECT's config by `CmsProvider`),\n * else the active content session's own declared default (surfaces that render\n * outside a CMS session — the Components view's previews — still resolve in the\n * project's locale), else {@link NO_LOCALE}. Never an engine fixture.\n */\nfunction previewLocale(): Locale {\n return getStudioLocale() ?? contentSession?.localeConfig?.default ?? NO_LOCALE\n}\n\n/**\n * The locale set a preview resolves fallbacks against (EC-018): the session's own\n * declaration, else the ambient studio config (the project's, via `elytra.config.ts`),\n * else the single locale actually being asked for.\n *\n * That last step is a DERIVATION, not an invention — it says \"the only locale we\n * know of is the one you named\", so `documentDefaultLocale` falls back to the\n * document's own `defaultLocale` and then to `doc.values`. Before EC-479 this line\n * read `?? sampleLocaleConfig`, which silently declared every locale-less studio\n * bilingual `en/de` and resolved fallbacks through an `en` no project had chosen.\n */\nfunction activeLocaleConfig(locale: Locale): LocaleConfig {\n return (\n contentSession?.localeConfig ??\n getStudioLocaleConfig() ?? { default: locale, locales: [locale] }\n )\n}\n\n/** Resolve a collection in the active content session (fixtures in playground). */\nexport function playgroundCollection(id: string): CollectionDef | undefined {\n return activeCollections().find((collection) => collection.id === id)\n}\n\n/**\n * Overlay the active session's canonical `filterable` declarations onto a\n * schema that doesn't carry any of its own. The inspector hands the Content\n * tab fixture collection defs; the filter/sort pickers must offer exactly the\n * EC-143 declaration set the delivery engine enforces, so both ends agree. A\n * collection that declares its own filterable fields is left untouched.\n */\nexport function withPlaygroundFilterable(\n collections: readonly CollectionDef[],\n): readonly CollectionDef[] {\n return collections.map((collection) => {\n if (collection.fields.some((field) => field.filterable === true)) return collection\n const counterpart = playgroundCollection(collection.id)\n if (counterpart === undefined) return collection\n const flagged = new Set(\n counterpart.fields.filter((field) => field.filterable === true).map((field) => field.name),\n )\n if (flagged.size === 0) return collection\n return {\n ...collection,\n fields: collection.fields.map((field) =>\n flagged.has(field.name) ? { ...field, filterable: true } : field,\n ),\n }\n })\n}\n\n/** Synthesize the persistence-shaped asset record the asset resolver reads. */\nfunction assetRecordOf(doc: CmsDocument): AssetRecord {\n const values = doc.values\n const alt = doc.localized?.en?.alt\n return {\n id: doc.id,\n projectId: 'playground',\n filename: typeof values.filename === 'string' ? values.filename : doc.id,\n contentType: 'image/png',\n byteSize: 0,\n alt: typeof alt === 'string' ? alt : undefined,\n storageKey: typeof values.url === 'string' ? values.url : doc.id,\n createdAt: '2026-01-01T00:00:00.000Z',\n updatedAt: '2026-01-01T00:00:00.000Z',\n }\n}\n\nfunction createLookup(\n collectionDefs: readonly CollectionDef[],\n documents: readonly CmsDocument[],\n publishedKeys?: readonly string[],\n assetRecords?: readonly AssetRecord[],\n): ContentLookup {\n const collections = new Map(collectionDefs.map((c) => [c.id, c]))\n // EC-224: published-ness is the version POINTER, not the flat `state` enum.\n // A hydrated session hands `publishedKeys` (the pinned set) → wrap each row as\n // a `DocumentHistory` so the published perspective resolves by the pin (the\n // live row is always a draft now) with ZERO dependence on `state`. Without a\n // pointer set (playground fixtures / tests) we keep bare rows, whose own\n // `state` gates the published perspective — byte-identical to before.\n const published = publishedKeys ? new Set(publishedKeys) : null\n const toSource = (doc: CmsDocument): ContentDocumentSource => {\n if (!published) return doc\n return published.has(documentKey({ collection: doc.collection, id: doc.id }))\n ? { draft: doc, published: doc, versions: [] }\n : { draft: doc, published: undefined, versions: [] }\n }\n const byKey = new Map(\n documents.map((doc) => [`${doc.collection}:${doc.id}`, toSource(doc)] as const),\n )\n // Asset resolution source for `asset`-typed FIELDS on resolved documents\n // (listing/relation results, e.g. a product's `gallery`). Two sources, real\n // records last so they win: (1) `asset`-collection fixture documents — how\n // playground/tests model assets; (2) the project's real asset-table records\n // (EC-262) — a Convex session has its assets here, NOT as `asset` documents,\n // so without these the content client resolves no nested image.\n const assets = new Map<string, AssetRecord>()\n for (const doc of documents) {\n if (doc.collection === 'asset') assets.set(doc.id, assetRecordOf(doc))\n }\n for (const record of assetRecords ?? []) assets.set(record.id, record)\n return {\n collection: (id) => collections.get(id),\n document: (ref) => byKey.get(`${ref.collection}:${ref.id}`),\n documents: (collectionId) =>\n documents.filter((doc) => doc.collection === collectionId).map(toSource),\n asset: (id) => assets.get(id),\n // Prefer the record's resolved servable `url` — the one the renderer's\n // resolveAsset reads (EC-262). Real asset-table records carry a Convex blob\n // locator in `storageKey` and the servable URL in `url`; fixture/`asset`-document\n // records (assetRecordOf) leave `url` unset and store the servable url in\n // `storageKey`. Falling back keeps both correct.\n assetUrl: (asset) => asset.url ?? asset.storageKey,\n }\n}\n\n/**\n * One deterministic client per (locale, perspective): EC-018 locale fallback,\n * draft-or-published perspective (EC-300/AD-16, default `'published'`). The studio's\n * ambient active locale (EC-176, `lib/locale`) picks the locale at query time, so the\n * canvas previews follow the locale switcher exactly like delivery would; the canvas's\n * Draft/Published view picks the perspective. The cache resets whenever the content\n * session changes.\n */\nfunction clientFor(locale: Locale, perspective: CanvasPerspective = 'published'): ContentClient {\n const locales = activeLocaleConfig(locale)\n // EC-479: the locale CONFIG is part of the client's identity now — it may come\n // from the ambient studio store, which changes independently of the session the\n // cache is cleared on, so keying by locale alone could serve a client built\n // against a superseded locale set.\n const key = `${locale}:${perspective}:${locales.default}|${locales.locales.join(',')}`\n const existing = clients.get(key)\n if (existing) return existing\n const client = createContentClient({\n lookup,\n context: createContentContext({\n locale,\n perspective,\n locales,\n }),\n // EC-291: the routes a rich-text link resolves against (config-sourced) — the\n // studio-live half of the host link bake.\n routes: contentSession?.routes ?? [],\n })\n clients.set(key, client)\n return client\n}\n\n/**\n * Run a closed-vocabulary list query (AD-3: eq/in/lt/lte/gt/gte, AND-only,\n * filterable fields only) and return the delivery-shaped result. Invalid\n * queries come back as structured errors — never thrown, never silently\n * widened. Resolves in the studio's active locale unless one is passed.\n */\nexport function runCollectionListQuery(\n collectionId: string,\n query: ListDocumentsQuery = {},\n locale: Locale = previewLocale(),\n): ListDocumentsResult {\n return clientFor(locale).listDocuments(collectionId, query)\n}\n\n/** Resolve one document (by id or slug) into delivery shape. */\nexport function getCollectionDocument(\n collectionId: string,\n idOrSlug: string,\n locale: Locale = previewLocale(),\n): GetDocumentResult {\n return clientFor(locale).getDocument(collectionId, idOrSlug)\n}\n\n/**\n * Populate a composition-node `relation` prop to delivery shape for the canvas\n * (EC-254) — the studio analog of the host's baked relations map. Resolves the\n * ref through the active session's client (published perspective, active locale),\n * so the canvas previews a block's picked documents (a ProductGrid's products, a\n * Reusable's element) exactly as delivery renders them. `null` when the target is\n * missing or not visible → the block degrades to its own empty state.\n */\nexport function resolveCanvasRelation(\n ref: DocumentRef,\n locale: Locale = previewLocale(),\n perspective: CanvasPerspective = 'published',\n): ResolvedDocument | null {\n return clientFor(locale, perspective).resolveRelation(ref)\n}\n\n/**\n * Resolve a listing block's query to its documents for the canvas (EC-255) — the\n * studio analog of the host's baked listings map. Runs the query (collection +\n * AND-only filter, optional sort/limit) through the active session's client\n * (published perspective, active locale), so the canvas previews a dynamic grid (a\n * ProductGrid keyed to a category) exactly as delivery renders it. An invalid query\n * (structured, never thrown) or a non-list lookup yields `[]` → the block's empty\n * state. Reuses the same `listDocuments` path as `runCollectionListQuery`.\n */\nexport function resolveCanvasListing(\n query: ListingQuery,\n locale: Locale = previewLocale(),\n perspective: CanvasPerspective = 'published',\n): ResolvedDocument[] {\n const result = clientFor(locale, perspective).listDocuments(query.collection, {\n where: query.where,\n ...(query.sort ? { sort: query.sort } : {}),\n ...(query.limit !== undefined ? { limit: query.limit } : {}),\n })\n return result.ok ? result.documents : []\n}\n\n/**\n * Statically validate a listing block's query against the ACTIVE session schema\n * (EC-460, the never-silent rule): the exact `validateWhere`/`validateSort`/\n * `validateLimit` checks `listDocuments` runs, WITHOUT executing the query — so\n * the canvas can surface a config-invalid listing (a filter/sort field the\n * collection doesn't declare `filterable`, an unknown collection) as a visible\n * notice instead of the block's indistinguishable empty state. With no content\n * session configured (boot instants, fixtures) there is no schema to judge\n * against, so it reports nothing rather than false-flagging every listing.\n */\nexport function validateCanvasListingQuery(query: ListingQuery): ContentQueryError[] {\n if (contentSession === null) return []\n const collection = playgroundCollection(query.collection)\n if (!collection) {\n return [\n queryError('unknown-collection', `Collection \"${query.collection}\" is not defined.`, [\n 'collection',\n ]),\n ]\n }\n return [\n ...validateWhere(collection, query.where),\n ...(query.sort ? validateSort(collection, query.sort) : []),\n ...(query.limit !== undefined ? validateLimit(query.limit) : []),\n ]\n}\n\n/**\n * Resolve a rich-text internal link's target document to its URL path for the canvas\n * (EC-291) — the studio analog of the host's baked links map. Derives the URL through\n * the active session's client (published perspective, active locale, config-sourced\n * routes), so the canvas previews a `{type:'reference'}` link's `href` exactly as\n * delivery serves it. `null` when the target is unpublished, missing, or addressed by\n * no route → the rich-text renderer shows the EC-290 `unresolved-ref` issue. Reuses\n * the same `resolveLink` path as the host bake, so both agree on the derived URL.\n */\nexport function resolveCanvasLink(\n ref: DocumentRef,\n locale: Locale = previewLocale(),\n perspective: CanvasPerspective = 'published',\n): string | null {\n return clientFor(locale, perspective).resolveLink(ref)\n}\n\n/** A pickable entry of a collection (the template-page preview switcher). */\nexport interface CollectionEntrySummary {\n id: string\n label: string\n}\n\n/** Published entries of a collection, labeled by the schema's title field. */\nexport function listCollectionEntries(collectionId: string): CollectionEntrySummary[] {\n const collection = playgroundCollection(collectionId)\n const result = runCollectionListQuery(collectionId)\n if (!result.ok) return []\n // EC-242: label via the shared identity resolver (string `titleField` or a\n // function pattern), over the delivery-shape values; falls back to the id.\n return result.documents.map((doc) => ({\n id: doc.id,\n label: collection ? resolveDocumentTitle(collection, doc, doc.id) : doc.id,\n }))\n}\n"]}
@@ -146,6 +146,32 @@ export const deMessages = {
146
146
  'shell.playground.hint': 'Playground-Modus: Daten liegen im Arbeitsspeicher dieses Geräts und werden beim Neuladen zurückgesetzt. Es wird nichts in einem Backend gespeichert.',
147
147
  'shell.deliveryHealth.dismiss': 'Schließen',
148
148
  // ─────────────────────────────────────────────────────────────────────────
149
+ // Customization issues — the section-rail foot (`NavIssues`)
150
+ // ─────────────────────────────────────────────────────────────────────────
151
+ // EC-478. Jede `{var}` ist ein BEZEICHNER (Eintrags-id, Erweiterungspunkt,
152
+ // Komponenten-/Sammlungs-id) und bleibt bewusst unübersetzt. Fachbegriffe, die
153
+ // im Code stehen (`reuse.component`, `reuse.relationProp`, `studio.config`,
154
+ // Prop, Slot, Block), bleiben ebenfalls unübersetzt — der Entwickler muss sie
155
+ // in seiner Konfiguration wiederfinden.
156
+ // EC-479 — das Projekt deklariert nichts, womit das Studio arbeiten könnte.
157
+ 'customization.issue.noCollections': 'Dieses Projekt deklariert keine Inhaltssammlungen. Fügen Sie sie in „{file}“ hinzu — das Studio zeigt Ihr Schema oder keines, niemals Beispielinhalte.',
158
+ 'customization.issue.duplicateOverrideId': 'Doppelte Überschreibungs-id „{id}“ für „{point}“ — die spätere wird ignoriert.',
159
+ 'customization.issue.unknownOverrideTarget': 'Die Überschreibung „{id}“ für „{point}“ passt zu keinem eingebauten Baustein, den sie ersetzen könnte.',
160
+ 'customization.issue.invalidOverride': 'Die Überschreibung „{id}“ für „{point}“ ist ungültig: {reason}',
161
+ 'customization.issue.reason.rendererNotAComponent': 'der Renderer ist keine Komponente',
162
+ 'customization.issue.reason.viewNoRender': 'die Ansicht hat keine render-Funktion',
163
+ 'customization.issue.reason.viewNoLabel': 'die Ansicht hat keine Beschriftung',
164
+ 'customization.issue.derivedIdBuiltinCollision': 'Die Ansicht „{label}“ für „{point}“ leitet die id „{id}“ aus ihrer Beschriftung ab, was der eingebauten Ansicht „{id}“ entspricht — die eingebaute Ansicht bleibt erhalten und diese wird ignoriert. Geben Sie eine explizite id an, um eine eingebaute Ansicht absichtlich zu ersetzen.',
165
+ 'customization.issue.derivedIdCollectionCollision': 'Die Ansichts-id „{id}“ für „{point}“ ist mehrdeutig: Sie verbindet die Sammlungen {collections} mit „-“, und eine Sammlung heißt buchstäblich „{joined}“ — deren eigene Ansicht leitet dieselbe id ab. Geben Sie dieser Gruppe eine explizite id.',
166
+ // EC-467 — die Deklaration wiederverwendbarer Elemente (`defineStudio({ reuse })`).
167
+ 'builder.reuse.config.unknownComponent': 'Wiederverwendbare Elemente: Die Studio-Konfiguration deklariert reuse.component „{component}“, was keine registrierte Komponente ist. Korrigieren Sie die id oder entfernen Sie den reuse-Block, damit das Studio die Einbettung selbst ermittelt.',
168
+ 'builder.reuse.config.noRelationProp': 'Wiederverwendbare Elemente: Die deklarierte Einbettung „{component}“ hat kein Relations-Prop für ein einzelnes Dokument und kann daher auf kein wiederverwendbares Dokument zeigen. Eine Einbettung benötigt genau ein Relations-Prop mit Kardinalität eins.',
169
+ 'builder.reuse.config.unknownRelationProp': 'Wiederverwendbare Elemente: Die deklarierte Einbettung „{component}“ hat kein Relations-Prop für ein einzelnes Dokument mit dem Namen „{relationProp}“. Ihre Relations-Props sind: {props}.',
170
+ 'builder.reuse.config.ambiguousRelationProp': 'Wiederverwendbare Elemente: Die deklarierte Einbettung „{component}“ hat {count} Relations-Props für ein einzelnes Dokument ({props}) — ergänzen Sie reuse.relationProp, um anzugeben, welches auf das wiederverwendbare Dokument zeigt.',
171
+ 'builder.reuse.config.unknownCollection': 'Wiederverwendbare Elemente: „{component}.{relationProp}“ verweist auf die Sammlung „{collection}“, die dieses Projekt nicht definiert.',
172
+ 'builder.reuse.config.noBlocksField': 'Wiederverwendbare Elemente: Die Sammlung „{collection}“ hinter „{component}.{relationProp}“ hat kein Block-Feld (Komposition), sodass die Einbettung nichts einbinden kann.',
173
+ 'builder.reuse.config.ambiguousInference': 'Wiederverwendbare Elemente: {count} Komponenten haben die Form einer wiederverwendbaren Einbettung ({components}). Ein Projekt hat genau eine wiederverwendbare Einbettung, deshalb rät das Studio nicht — deklarieren Sie sie in studio.config als reuse: { component: \'<id>\' }. Bis dahin sind die Wiederverwendungs-Gesten deaktiviert.',
174
+ // ─────────────────────────────────────────────────────────────────────────
149
175
  // Auth / errors
150
176
  // ─────────────────────────────────────────────────────────────────────────
151
177
  'auth.signIn': 'Anmelden',
@@ -232,6 +258,9 @@ export const deMessages = {
232
258
  // EC-448 — dritter Dok-Status (veröffentlicht mit unveröffentlichten Änderungen).
233
259
  'cms.docState.changed': 'geändert',
234
260
  'cms.docState.draft': 'Entwurf',
261
+ // EC-479 — ohne Projektsprachen kann keine CMS-Sitzung geöffnet werden.
262
+ 'cms.session.noLocales.title': 'Dieses Projekt deklariert keine Sprachen',
263
+ 'cms.session.noLocales.body': 'Fügen Sie Ihrer Projektkonfiguration eine locales-Liste hinzu. Die Standardsprache ist der Schlüssel, unter dem Ihre Inhalte gespeichert werden — das Studio wählt sie deshalb nicht für Sie aus.',
235
264
  'cms.list.view.all': 'Alle Einträge',
236
265
  'cms.list.unknownCollection': 'Unbekannte Sammlung {id}.',
237
266
  'cms.list.import.noId': '(keine ID)',
@@ -422,6 +451,109 @@ export const deMessages = {
422
451
  'cms.issueBody.document-nesting-depth.warning': 'Dieser Inhalt ist {depth} Ebenen tief verschachtelt und nähert sich dem Speicherlimit von {limit} Ebenen.',
423
452
  'cms.issueBody.hierarchy-cycle': 'Dieser Eintrag ist sein eigener Vorfahr – die Kette der übergeordneten Einträge bildet eine Schleife.',
424
453
  // ─────────────────────────────────────────────────────────────────────────
454
+ // CMS validation issue BODIES (EC-487)
455
+ //
456
+ // EC-443 localized the issue TITLES (`cms.issue.<code>` above); the bodies were
457
+ // still built as English template literals inside `packages/core`, which cannot
458
+ // import this catalog. EC-478 solved that shape for `CustomizationIssue` by having
459
+ // the producer carry a message KEY; these are the same carriage for
460
+ // `CmsValidationIssue`, so the rail foot, the Issues pane and the for-review lane
461
+ // never mix a German title with an English body.
462
+ //
463
+ // The English source is a SECOND copy of what the producer builds — the tie between
464
+ // them is `lib/persona/cms-issue-i18n.test.ts`, which renders every real producer's
465
+ // issue through this catalog and asserts byte-identity. Interpolated values are
466
+ // IDENTIFIERS (field/collection/route/locale ids, patterns, counts) and stay
467
+ // unlocalized in every language.
468
+ //
469
+ // `cms.issueDetail.*` are FRAGMENTS spliced into the sentences above.
470
+ // ─────────────────────────────────────────────────────────────────────────
471
+ 'cms.issueMessage.duplicateDocument': 'Doppeltes Dokument „{id}“ in der Sammlung „{collection}“.',
472
+ 'cms.issueMessage.fieldDetail': 'Das Feld „{field}“ {detail}.',
473
+ 'cms.issueMessage.fieldReason': 'Feld „{field}“: {reason}',
474
+ 'cms.issueMessage.propDetail': 'Die Eigenschaft „{field}“ {detail}.',
475
+ 'cms.issueMessage.propReason': 'Eigenschaft „{field}“: {reason}',
476
+ 'cms.issueMessage.relationUnknownTargetCollection': 'Das Beziehungsfeld „{field}“ zielt auf die unbekannte Sammlung „{target}“.',
477
+ 'cms.issueMessage.relationSingleValued': 'Das Beziehungsfeld „{field}“ ist einwertig, hat aber {count} Ziele.',
478
+ 'cms.issueMessage.relationNotAReference': 'Das Beziehungsfeld „{field}“ enthält einen Wert, der keine Dokumentreferenz ist.',
479
+ 'cms.issueMessage.relationWrongCollection': 'Das Beziehungsfeld „{field}“ zeigt auf die Sammlung „{got}“, zielt aber auf „{target}“.',
480
+ 'cms.issueMessage.relationMissingDocument': 'Das Beziehungsfeld „{field}“ verweist auf das fehlende Dokument „{id}“ in „{target}“.',
481
+ 'cms.issueMessage.relationUnpublishedDocument': 'Das Beziehungsfeld „{field}“ verweist auf das unveröffentlichte Dokument „{id}“ in „{target}“ — auf der veröffentlichten Website erscheint dort ein Platzhalter.',
482
+ 'cms.issueMessage.assetNoAssetCollection': 'Das Medienfeld „{field}“ benötigt eine Mediensammlung, es ist aber keine registriert.',
483
+ 'cms.issueMessage.assetSingleValued': 'Das Medienfeld „{field}“ ist einwertig, hat aber {count} Ziele.',
484
+ 'cms.issueMessage.assetExpectsId': 'Das Medienfeld „{field}“ erwartet eine Medien-id.',
485
+ 'cms.issueMessage.assetUnknown': 'Das Medienfeld „{field}“ verweist auf das unbekannte Medium „{asset}“.',
486
+ 'cms.issueMessage.requiredFieldMissing': 'Das Pflichtfeld „{field}“ fehlt.',
487
+ 'cms.issueMessage.requiredPropMissing': 'Die Pflichteigenschaft „{field}“ fehlt.',
488
+ 'cms.issueMessage.perLocaleMapValue': 'Das Feld „{field}“ erwartet einen Wert vom Typ „{type}“, enthält aber eine Sprach-Zuordnung (Schlüssel: {keys}) — sprachabhängige Werte liegen im „localized“-Bereich des Dokuments, niemals im Feldwert selbst.',
489
+ 'cms.issueMessage.richTextHoldsArray': 'Das Rich-Text-Feld „{field}“ erwartet eine Zeichenkette oder einen Rich-Text-Wert („{ version, doc }“), enthält aber ein Array.',
490
+ 'cms.issueMessage.richTextHoldsBareDoc': 'Das Rich-Text-Feld „{field}“ erwartet eine Zeichenkette oder einen Rich-Text-Wert („{ version, doc }“), enthält aber ein blankes Rich-Text-Dokument ohne die Hülle „{ version, doc }“.',
491
+ 'cms.issueMessage.richTextHoldsObject': 'Das Rich-Text-Feld „{field}“ erwartet eine Zeichenkette oder einen Rich-Text-Wert („{ version, doc }“), enthält aber ein Objekt mit den Schlüsseln „{keys}“.',
492
+ 'cms.issueMessage.unknownCollectionForDocument': 'Das Dokument „{id}“ verweist auf die unbekannte Sammlung „{collection}“.',
493
+ 'cms.issueMessage.localizedPlainValue': 'Das sprachabhängige Feld „{field}“ enthält einen einfachen Wert im gemeinsamen „values“-Bereich — erwartet werden sprachabhängige Werte unter „localized.<locale>.{field}“. Der einfache Wert dient nur noch als letzte Rückfallebene.',
494
+ 'cms.issueMessage.requiredLocalizedMissingDefault': 'Das sprachabhängige Pflichtfeld „{field}“ fehlt für die Standardsprache „{locale}“.',
495
+ 'cms.issueMessage.localizedNoValueNoFallback': 'Das sprachabhängige Feld „{field}“ hat keinen Wert für die Sprache „{locale}“ und keine Rückfallebene.',
496
+ 'cms.issueMessage.hierarchyCycle': 'Das Dokument „{id}“ ist sein eigener Vorfahre — die Elternkette bildet einen Zyklus.',
497
+ 'cms.issueMessage.nestingDepthError': 'Das Dokument „{id}“ ist {depth} Ebenen tief verschachtelt und überschreitet damit das Schreib-Limit des Backends von {limit} Ebenen — selbst das Falten der Tiefe beim Speichern rettet ein so tiefes Dokument nicht, der Schreibvorgang kann nicht ausgeführt werden. Teilen Sie es in kleinere verknüpfte Dokumente auf.',
498
+ 'cms.issueMessage.nestingDepthWarning': 'Das Dokument „{id}“ ist {depth} Ebenen tief verschachtelt und nähert sich damit dem Limit des Backends von {limit} Ebenen. Flachen Sie den tiefsten Pfad ab, bevor er weiter wächst.',
499
+ 'cms.issueMessage.propFilterable': 'Die Komponenten-Eigenschaft „{field}“ deklariert „filterable“, was nur für Dokumentfelder gilt.',
500
+ 'cms.issueMessage.propUnique': 'Die Komponenten-Eigenschaft „{field}“ deklariert „unique“, was nur über die Dokumente einer Sammlung hinweg gilt.',
501
+ 'cms.issueMessage.singletonDrift': 'Das Einzelstück „{collection}“ hat {count} Dokumente — es sollte genau eines haben. Behalten Sie das richtige Dokument und entfernen Sie die übrigen, damit es sich direkt öffnen lässt.',
502
+ 'cms.issueMessage.unknownAlias.one': 'Ein Dokument ist dem Alias „{alias}“ zugewiesen, der kein deklarierter Slot von „{collection}“ ist ({declared}). Weisen Sie einen gültigen Slot zu.',
503
+ 'cms.issueMessage.unknownAlias.other': '{count} Dokumente sind dem Alias „{alias}“ zugewiesen, der kein deklarierter Slot von „{collection}“ ist ({declared}). Weisen Sie einen gültigen Slot zu.',
504
+ 'cms.issueMessage.duplicateAlias': '{count} Dokumente teilen sich den Alias „{alias}“ in „{collection}“. Jeder Slot darf nur einem Dokument zugewiesen sein — weisen Sie neu zu oder entfernen Sie die Duplikate.',
505
+ 'cms.issueMessage.unfilledSlot': 'Der Slot „{slot}“ von „{collection}“ hat noch kein Dokument. Weisen Sie eines zu, damit Code, der „{slot}“ auflöst, es findet.',
506
+ 'cms.issueMessage.reservedFieldName': 'Der Feldname „{field}“ ist reserviert (der Diskriminator selbstbeschreibender Knoten).',
507
+ 'cms.issueMessage.duplicateSelectOption': 'Doppelte Auswahloption „{option}“ im Feld „{field}“.',
508
+ 'cms.issueMessage.duplicateObjectField': 'Doppeltes Feld „{field}“ im Objektfeld „{parent}“.',
509
+ 'cms.issueMessage.assetCollectionSingleton': 'Die Mediensammlung „{collection}“ kann kein Einzelstück sein.',
510
+ 'cms.issueMessage.duplicateCollectionField': 'Doppeltes Feld „{field}“ in der Sammlung „{collection}“.',
511
+ 'cms.issueMessage.duplicateCollectionId': 'Doppelte Sammlungs-id „{collection}“.',
512
+ 'cms.issueMessage.unknownLocale': 'Die Sprache „{locale}“ gehört nicht zum konfigurierten Sprachsatz.',
513
+ 'cms.issueMessage.unknownVersion': 'Für das Dokument „{id}“ ist keine Version {version} aufgezeichnet.',
514
+ 'cms.issueMessage.routeConflict': 'Die Routen „{first}“ und „{second}“ treffen auf dieselben URLs zu (Muster „{pattern}“).',
515
+ 'cms.issueMessage.redirectLoop': 'Die Weiterleitungskette ab „{from}“ bildet eine Schleife (…{chain}).',
516
+ 'cms.issueMessage.allDynamicShadowsMount': 'Die Route „{route}“ („{pattern}“) besteht nur aus dynamischen Segmenten und trifft damit auf JEDE URL mit {depth} Segmenten zu — die Hierarchie-Einhängung „{mountRoute}“ („{mountPattern}“) wird auf dieser Ebene nur dort herangezogen, wo die Route kein Dokument auflöst. Nichts ist dadurch grundsätzlich unerreichbar, aber der Vorrang entscheidet sich dort am INHALT statt an den Routen: {shadowed} werden stillschweigend verdeckt, sobald ein zusammengesetzter Pfad mit {claimed} kollidiert — und keine Seite erfährt davon. Geben Sie der Route ein statisches Präfix („/blog/:slug“), damit sie einen Namensraum beansprucht statt einer ganzen Ebene.',
517
+ 'cms.issueMessage.mountCannotServe': 'Die Hierarchie-Einhängung „{route}“ („{pattern}“) kann niemals ein Dokument ausliefern: {why}. Der Resolver überspringt eine solche Einhängung stillschweigend, deshalb liefert jede URL unter „{space}“ einen 404, ohne dass ein Hinweis erscheint.',
518
+ 'cms.issueMessage.deadCatchAll': 'Die Route „{route}“ („{pattern}“) liefert in beide Richtungen nichts: Sie endet in einem Catch-all, deshalb verwirft sie jeder Router — ihr Präfix enthält aber ein dynamisches Segment ({dynamic}), deshalb wird auch keine Hierarchie-Einhängung daraus gebaut. Das Präfix einer Einhängung muss statisch sein — deklarieren Sie eine Einhängung pro Sprache (`{ pattern: \'/de/:path*\', locale: \'de\' }`) oder entfernen Sie die Route.',
519
+ 'cms.issueMessage.mountConflict': '{opening} — Einhängungen werden in Deklarationsreihenfolge aufgelöst, nicht nach Spezifität; deshalb beansprucht „{earlier}“ (Sammlung „{earlierCollection}“) dort jede URL, die der eigene Baum auflöst, und Dokumente aus „{laterCollection}“ sind an diesen URLs unerreichbar.',
520
+ 'cms.issueMessage.routeBoundDocumentNotFound': 'Das an die Route gebundene Dokument „{collection}:{id}“ wurde nicht gefunden.',
521
+ 'cms.issueMessage.routeParamNotCaptured': 'Die Route „{route}“ bindet ihr Dokument an den URL-Parameter „:{param}“, den das Muster „{pattern}“ nicht erfasst.',
522
+ 'cms.issueMessage.collectionNotDefined': 'Die Sammlung „{collection}“ ist nicht definiert.',
523
+ 'cms.issueDetail.expectsString': 'erwartet eine Zeichenkette',
524
+ 'cms.issueDetail.expectsRichText': 'erwartet Rich-Text-Inhalt (eine Zeichenkette oder einen Rich-Text-Wert)',
525
+ 'cms.issueDetail.expectsNumber': 'erwartet eine Zahl',
526
+ 'cms.issueDetail.expectsBoolean': 'erwartet einen Wahrheitswert',
527
+ 'cms.issueDetail.expectsIsoDate': 'erwartet ein ISO-8601-Datum (z. B. „2026-09-18“)',
528
+ 'cms.issueDetail.expectsDate': 'erwartet ein Datum (ISO-8601-Zeichenkette oder Epochenzahl)',
529
+ 'cms.issueDetail.valueNotAmongOptions': 'hat den Wert „{value}“, der nicht zu seinen Optionen gehört',
530
+ 'cms.issueDetail.expectsObjectList': 'erwartet eine Liste von Objekten',
531
+ 'cms.issueDetail.expectsObject': 'erwartet ein Objekt',
532
+ 'cms.issueDetail.implausibleYear': 'hat das Jahr {year}, was unbeabsichtigt wirkt (erwartet wird eine vierstellige Jahreszahl)',
533
+ 'cms.issueDetail.dateBoundNotADate': 'deklariert {bound} `{declared}` auf einem Datumsfeld, was kein Datum ist — verwenden Sie eine ISO-8601-Zeichenkette wie „1900-01-01“',
534
+ 'cms.issueDetail.earlierThanMin': 'liegt vor {min}',
535
+ 'cms.issueDetail.laterThanMax': 'liegt nach {max}',
536
+ 'cms.issueDetail.stringBoundOffDate': 'deklariert {bound} „{declared}“ als Zeichenkette, was nur auf einem Datumsfeld sinnvoll ist — verwenden Sie eine Zahl',
537
+ 'cms.issueDetail.shorterThanMinLength': 'ist kürzer als die Mindestlänge {min}',
538
+ 'cms.issueDetail.longerThanMaxLength': 'ist länger als die Höchstlänge {max}',
539
+ 'cms.issueDetail.doesNotMatchPattern': 'entspricht nicht dem Muster {pattern}',
540
+ 'cms.issueDetail.lessThanMin': 'ist kleiner als das Minimum {min}',
541
+ 'cms.issueDetail.greaterThanMax': 'ist größer als das Maximum {max}',
542
+ 'cms.issueDetail.fewerThanMinItems': 'hat weniger als {min} Einträge',
543
+ 'cms.issueDetail.moreThanMaxItems': 'hat mehr als {max} Einträge',
544
+ 'cms.issueDetail.singleValuedManyTargets': 'ist einwertig, hat aber {count} Ziele',
545
+ 'cms.issueDetail.notADocumentReference': 'enthält einen Wert, der keine Dokumentreferenz ist',
546
+ 'cms.issueDetail.pointsAtOtherCollection': 'zeigt auf die Sammlung „{got}“, zielt aber auf „{target}“',
547
+ 'cms.issueDetail.expectsAssetIdString': 'erwartet eine Medien-id als Zeichenkette',
548
+ 'cms.issueDetail.shadowedRootDocuments': 'die WURZEL-Dokumente von „{collection}“ unter „{space}“',
549
+ 'cms.issueDetail.shadowedNestedDocuments': '„{collection}“-Dokumente, die {levels} Ebenen unter „{space}“ verschachtelt sind',
550
+ 'cms.issueDetail.claimedBySlug': 'einem Slug aus „{collection}“',
551
+ 'cms.issueDetail.claimedByOwnDocument': 'dem eigenen Dokument der Route',
552
+ 'cms.issueDetail.mountCollectionNotDeclared': 'die Sammlung „{collection}“ ist nicht deklariert',
553
+ 'cms.issueDetail.mountCollectionNotHierarchical': 'die Sammlung „{collection}“ hat keine `parent`-Selbstbeziehung und damit keine verschachtelten Pfade zum Zusammensetzen',
554
+ 'cms.issueDetail.mountsSamePath': 'Die Hierarchie-Einhängungen „{earlier}“ („{earlierPattern}“) und „{later}“ („{laterPattern}“) sind beide unter „{space}“ eingehängt',
555
+ 'cms.issueDetail.mountsNested': 'Die Hierarchie-Einhängung „{later}“ („{laterPattern}“) liegt innerhalb von „{earlier}“ („{earlierPattern}“), das zuerst deklariert ist und ebenfalls „{space}“ vollständig beansprucht',
556
+ // ─────────────────────────────────────────────────────────────────────────
425
557
  // CMS — details pane (slot + sidebar fields)
426
558
  // ─────────────────────────────────────────────────────────────────────────
427
559
  'cms.details.slot': 'Slot',