@elytracms/core 0.0.6
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/LICENSE +21 -0
- package/dist/clipboard/build.d.ts +77 -0
- package/dist/clipboard/build.js +128 -0
- package/dist/clipboard/build.js.map +1 -0
- package/dist/clipboard/clipboard.d.ts +33 -0
- package/dist/clipboard/clipboard.js +45 -0
- package/dist/clipboard/clipboard.js.map +1 -0
- package/dist/clipboard/fragment.d.ts +112 -0
- package/dist/clipboard/fragment.js +79 -0
- package/dist/clipboard/fragment.js.map +1 -0
- package/dist/clipboard/html.d.ts +26 -0
- package/dist/clipboard/html.js +82 -0
- package/dist/clipboard/html.js.map +1 -0
- package/dist/clipboard/index.d.ts +15 -0
- package/dist/clipboard/index.js +16 -0
- package/dist/clipboard/index.js.map +1 -0
- package/dist/clipboard/references.d.ts +50 -0
- package/dist/clipboard/references.js +0 -0
- package/dist/clipboard/references.js.map +1 -0
- package/dist/clipboard/serialize.d.ts +69 -0
- package/dist/clipboard/serialize.js +130 -0
- package/dist/clipboard/serialize.js.map +1 -0
- package/dist/cms-core/collections.d.ts +82 -0
- package/dist/cms-core/collections.js +187 -0
- package/dist/cms-core/collections.js.map +1 -0
- package/dist/cms-core/documents.d.ts +71 -0
- package/dist/cms-core/documents.js +67 -0
- package/dist/cms-core/documents.js.map +1 -0
- package/dist/cms-core/envelopes.d.ts +80 -0
- package/dist/cms-core/envelopes.js +124 -0
- package/dist/cms-core/envelopes.js.map +1 -0
- package/dist/cms-core/fields.d.ts +220 -0
- package/dist/cms-core/fields.js +250 -0
- package/dist/cms-core/fields.js.map +1 -0
- package/dist/cms-core/fixtures.d.ts +92 -0
- package/dist/cms-core/fixtures.js +357 -0
- package/dist/cms-core/fixtures.js.map +1 -0
- package/dist/cms-core/hierarchy.d.ts +113 -0
- package/dist/cms-core/hierarchy.js +223 -0
- package/dist/cms-core/hierarchy.js.map +1 -0
- package/dist/cms-core/index.d.ts +25 -0
- package/dist/cms-core/index.js +26 -0
- package/dist/cms-core/index.js.map +1 -0
- package/dist/cms-core/infer.d.ts +103 -0
- package/dist/cms-core/infer.js +57 -0
- package/dist/cms-core/infer.js.map +1 -0
- package/dist/cms-core/issues.d.ts +92 -0
- package/dist/cms-core/issues.js +74 -0
- package/dist/cms-core/issues.js.map +1 -0
- package/dist/cms-core/json-schema.d.ts +25 -0
- package/dist/cms-core/json-schema.js +110 -0
- package/dist/cms-core/json-schema.js.map +1 -0
- package/dist/cms-core/localization.d.ts +51 -0
- package/dist/cms-core/localization.js +89 -0
- package/dist/cms-core/localization.js.map +1 -0
- package/dist/cms-core/routes.d.ts +76 -0
- package/dist/cms-core/routes.js +220 -0
- package/dist/cms-core/routes.js.map +1 -0
- package/dist/cms-core/self-type.d.ts +41 -0
- package/dist/cms-core/self-type.js +191 -0
- package/dist/cms-core/self-type.js.map +1 -0
- package/dist/cms-core/url-for-document.d.ts +39 -0
- package/dist/cms-core/url-for-document.js +138 -0
- package/dist/cms-core/url-for-document.js.map +1 -0
- package/dist/cms-core/validate-document.d.ts +121 -0
- package/dist/cms-core/validate-document.js +871 -0
- package/dist/cms-core/validate-document.js.map +1 -0
- package/dist/cms-core/versions.d.ts +75 -0
- package/dist/cms-core/versions.js +97 -0
- package/dist/cms-core/versions.js.map +1 -0
- package/dist/collaboration/approval.d.ts +68 -0
- package/dist/collaboration/approval.js +104 -0
- package/dist/collaboration/approval.js.map +1 -0
- package/dist/collaboration/collaboration.d.ts +49 -0
- package/dist/collaboration/collaboration.js +56 -0
- package/dist/collaboration/collaboration.js.map +1 -0
- package/dist/collaboration/comments.d.ts +72 -0
- package/dist/collaboration/comments.js +118 -0
- package/dist/collaboration/comments.js.map +1 -0
- package/dist/collaboration/core.d.ts +25 -0
- package/dist/collaboration/core.js +26 -0
- package/dist/collaboration/core.js.map +1 -0
- package/dist/collaboration/index.d.ts +14 -0
- package/dist/collaboration/index.js +15 -0
- package/dist/collaboration/index.js.map +1 -0
- package/dist/collaboration/presence.d.ts +62 -0
- package/dist/collaboration/presence.js +85 -0
- package/dist/collaboration/presence.js.map +1 -0
- package/dist/collaboration/publishing.d.ts +60 -0
- package/dist/collaboration/publishing.js +93 -0
- package/dist/collaboration/publishing.js.map +1 -0
- package/dist/collaboration/versions.d.ts +52 -0
- package/dist/collaboration/versions.js +81 -0
- package/dist/collaboration/versions.js.map +1 -0
- package/dist/component-registry/index.d.ts +3 -0
- package/dist/component-registry/index.js +4 -0
- package/dist/component-registry/index.js.map +1 -0
- package/dist/component-registry/issues.d.ts +6 -0
- package/dist/component-registry/issues.js +2 -0
- package/dist/component-registry/issues.js.map +1 -0
- package/dist/component-registry/manifest.d.ts +164 -0
- package/dist/component-registry/manifest.js +129 -0
- package/dist/component-registry/manifest.js.map +1 -0
- package/dist/component-registry/registry.d.ts +33 -0
- package/dist/component-registry/registry.js +90 -0
- package/dist/component-registry/registry.js.map +1 -0
- package/dist/content/__fixtures__/filterable-collections.d.ts +14 -0
- package/dist/content/__fixtures__/filterable-collections.js +15 -0
- package/dist/content/__fixtures__/filterable-collections.js.map +1 -0
- package/dist/content/__fixtures__/sample-accessor-types.d.ts +56 -0
- package/dist/content/__fixtures__/sample-accessor-types.js +5 -0
- package/dist/content/__fixtures__/sample-accessor-types.js.map +1 -0
- package/dist/content/__fixtures__/sample-delivery-types.d.ts +122 -0
- package/dist/content/__fixtures__/sample-delivery-types.js +5 -0
- package/dist/content/__fixtures__/sample-delivery-types.js.map +1 -0
- package/dist/content/assets.d.ts +53 -0
- package/dist/content/assets.js +38 -0
- package/dist/content/assets.js.map +1 -0
- package/dist/content/binding-sources.d.ts +53 -0
- package/dist/content/binding-sources.js +73 -0
- package/dist/content/binding-sources.js.map +1 -0
- package/dist/content/client.d.ts +90 -0
- package/dist/content/client.js +383 -0
- package/dist/content/client.js.map +1 -0
- package/dist/content/codegen.d.ts +54 -0
- package/dist/content/codegen.js +305 -0
- package/dist/content/codegen.js.map +1 -0
- package/dist/content/context.d.ts +38 -0
- package/dist/content/context.js +21 -0
- package/dist/content/context.js.map +1 -0
- package/dist/content/cursor.d.ts +33 -0
- package/dist/content/cursor.js +104 -0
- package/dist/content/cursor.js.map +1 -0
- package/dist/content/index.d.ts +28 -0
- package/dist/content/index.js +29 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/locale.d.ts +30 -0
- package/dist/content/locale.js +26 -0
- package/dist/content/locale.js.map +1 -0
- package/dist/content/perspective.d.ts +29 -0
- package/dist/content/perspective.js +31 -0
- package/dist/content/perspective.js.map +1 -0
- package/dist/content/populate.d.ts +25 -0
- package/dist/content/populate.js +22 -0
- package/dist/content/populate.js.map +1 -0
- package/dist/content/query.d.ts +122 -0
- package/dist/content/query.js +257 -0
- package/dist/content/query.js.map +1 -0
- package/dist/content/raw.d.ts +13 -0
- package/dist/content/raw.js +14 -0
- package/dist/content/raw.js.map +1 -0
- package/dist/content/resolve.d.ts +97 -0
- package/dist/content/resolve.js +261 -0
- package/dist/content/resolve.js.map +1 -0
- package/dist/content/serialize.d.ts +30 -0
- package/dist/content/serialize.js +57 -0
- package/dist/content/serialize.js.map +1 -0
- package/dist/content/tags.d.ts +54 -0
- package/dist/content/tags.js +40 -0
- package/dist/content/tags.js.map +1 -0
- package/dist/data-binding/fixtures.d.ts +20 -0
- package/dist/data-binding/fixtures.js +47 -0
- package/dist/data-binding/fixtures.js.map +1 -0
- package/dist/data-binding/index.d.ts +14 -0
- package/dist/data-binding/index.js +15 -0
- package/dist/data-binding/index.js.map +1 -0
- package/dist/data-binding/issues.d.ts +45 -0
- package/dist/data-binding/issues.js +46 -0
- package/dist/data-binding/issues.js.map +1 -0
- package/dist/data-binding/resolve.d.ts +87 -0
- package/dist/data-binding/resolve.js +204 -0
- package/dist/data-binding/resolve.js.map +1 -0
- package/dist/data-binding/sample.d.ts +21 -0
- package/dist/data-binding/sample.js +23 -0
- package/dist/data-binding/sample.js.map +1 -0
- package/dist/data-binding/sources.d.ts +225 -0
- package/dist/data-binding/sources.js +154 -0
- package/dist/data-binding/sources.js.map +1 -0
- package/dist/data-binding/tokens.d.ts +62 -0
- package/dist/data-binding/tokens.js +150 -0
- package/dist/data-binding/tokens.js.map +1 -0
- package/dist/design-tokens/css.d.ts +34 -0
- package/dist/design-tokens/css.js +51 -0
- package/dist/design-tokens/css.js.map +1 -0
- package/dist/design-tokens/index.d.ts +10 -0
- package/dist/design-tokens/index.js +11 -0
- package/dist/design-tokens/index.js.map +1 -0
- package/dist/design-tokens/style-guide.d.ts +30 -0
- package/dist/design-tokens/style-guide.js +31 -0
- package/dist/design-tokens/style-guide.js.map +1 -0
- package/dist/design-tokens/tokens.d.ts +89 -0
- package/dist/design-tokens/tokens.js +112 -0
- package/dist/design-tokens/tokens.js.map +1 -0
- package/dist/export-sync/builder-source.d.ts +85 -0
- package/dist/export-sync/builder-source.js +124 -0
- package/dist/export-sync/builder-source.js.map +1 -0
- package/dist/export-sync/check.d.ts +61 -0
- package/dist/export-sync/check.js +126 -0
- package/dist/export-sync/check.js.map +1 -0
- package/dist/export-sync/cli.d.ts +89 -0
- package/dist/export-sync/cli.js +323 -0
- package/dist/export-sync/cli.js.map +1 -0
- package/dist/export-sync/core.d.ts +58 -0
- package/dist/export-sync/core.js +41 -0
- package/dist/export-sync/core.js.map +1 -0
- package/dist/export-sync/eject.d.ts +28 -0
- package/dist/export-sync/eject.js +21 -0
- package/dist/export-sync/eject.js.map +1 -0
- package/dist/export-sync/fixtures.d.ts +25 -0
- package/dist/export-sync/fixtures.js +87 -0
- package/dist/export-sync/fixtures.js.map +1 -0
- package/dist/export-sync/generate.d.ts +7 -0
- package/dist/export-sync/generate.js +505 -0
- package/dist/export-sync/generate.js.map +1 -0
- package/dist/export-sync/index.d.ts +37 -0
- package/dist/export-sync/index.js +39 -0
- package/dist/export-sync/index.js.map +1 -0
- package/dist/export-sync/init.d.ts +123 -0
- package/dist/export-sync/init.js +234 -0
- package/dist/export-sync/init.js.map +1 -0
- package/dist/export-sync/manifest-host.d.ts +48 -0
- package/dist/export-sync/manifest-host.js +73 -0
- package/dist/export-sync/manifest-host.js.map +1 -0
- package/dist/export-sync/node.d.ts +20 -0
- package/dist/export-sync/node.js +101 -0
- package/dist/export-sync/node.js.map +1 -0
- package/dist/export-sync/push.d.ts +76 -0
- package/dist/export-sync/push.js +197 -0
- package/dist/export-sync/push.js.map +1 -0
- package/dist/export-sync/registry-sync-client.d.ts +59 -0
- package/dist/export-sync/registry-sync-client.js +97 -0
- package/dist/export-sync/registry-sync-client.js.map +1 -0
- package/dist/export-sync/sync.d.ts +47 -0
- package/dist/export-sync/sync.js +47 -0
- package/dist/export-sync/sync.js.map +1 -0
- package/dist/export-sync/typegen.d.ts +40 -0
- package/dist/export-sync/typegen.js +45 -0
- package/dist/export-sync/typegen.js.map +1 -0
- package/dist/export-sync/validate.d.ts +19 -0
- package/dist/export-sync/validate.js +102 -0
- package/dist/export-sync/validate.js.map +1 -0
- package/dist/export-sync/watch.d.ts +66 -0
- package/dist/export-sync/watch.js +70 -0
- package/dist/export-sync/watch.js.map +1 -0
- package/dist/operations/assets.d.ts +198 -0
- package/dist/operations/assets.js +75 -0
- package/dist/operations/assets.js.map +1 -0
- package/dist/operations/authorization.d.ts +49 -0
- package/dist/operations/authorization.js +128 -0
- package/dist/operations/authorization.js.map +1 -0
- package/dist/operations/changesets.d.ts +160 -0
- package/dist/operations/changesets.js +442 -0
- package/dist/operations/changesets.js.map +1 -0
- package/dist/operations/client.d.ts +49 -0
- package/dist/operations/client.js +57 -0
- package/dist/operations/client.js.map +1 -0
- package/dist/operations/core.d.ts +238 -0
- package/dist/operations/core.js +269 -0
- package/dist/operations/core.js.map +1 -0
- package/dist/operations/documents.d.ts +432 -0
- package/dist/operations/documents.js +344 -0
- package/dist/operations/documents.js.map +1 -0
- package/dist/operations/graph.d.ts +138 -0
- package/dist/operations/graph.js +78 -0
- package/dist/operations/graph.js.map +1 -0
- package/dist/operations/index.d.ts +19 -0
- package/dist/operations/index.js +20 -0
- package/dist/operations/index.js.map +1 -0
- package/dist/operations/members.d.ts +87 -0
- package/dist/operations/members.js +56 -0
- package/dist/operations/members.js.map +1 -0
- package/dist/operations/publishing.d.ts +89 -0
- package/dist/operations/publishing.js +57 -0
- package/dist/operations/publishing.js.map +1 -0
- package/dist/operations/references.d.ts +222 -0
- package/dist/operations/references.js +177 -0
- package/dist/operations/references.js.map +1 -0
- package/dist/operations/schema.d.ts +413 -0
- package/dist/operations/schema.js +138 -0
- package/dist/operations/schema.js.map +1 -0
- package/dist/operations/tokens.d.ts +79 -0
- package/dist/operations/tokens.js +102 -0
- package/dist/operations/tokens.js.map +1 -0
- package/dist/persistence/adapter.d.ts +79 -0
- package/dist/persistence/adapter.js +55 -0
- package/dist/persistence/adapter.js.map +1 -0
- package/dist/persistence/assets.d.ts +120 -0
- package/dist/persistence/assets.js +141 -0
- package/dist/persistence/assets.js.map +1 -0
- package/dist/persistence/backend-validation.d.ts +43 -0
- package/dist/persistence/backend-validation.js +57 -0
- package/dist/persistence/backend-validation.js.map +1 -0
- package/dist/persistence/cli-tokens.d.ts +50 -0
- package/dist/persistence/cli-tokens.js +66 -0
- package/dist/persistence/cli-tokens.js.map +1 -0
- package/dist/persistence/cms.d.ts +148 -0
- package/dist/persistence/cms.js +232 -0
- package/dist/persistence/cms.js.map +1 -0
- package/dist/persistence/contract-scenarios.d.ts +73 -0
- package/dist/persistence/contract-scenarios.js +496 -0
- package/dist/persistence/contract-scenarios.js.map +1 -0
- package/dist/persistence/core.d.ts +96 -0
- package/dist/persistence/core.js +110 -0
- package/dist/persistence/core.js.map +1 -0
- package/dist/persistence/graph.d.ts +61 -0
- package/dist/persistence/graph.js +98 -0
- package/dist/persistence/graph.js.map +1 -0
- package/dist/persistence/index.d.ts +22 -0
- package/dist/persistence/index.js +23 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/members.d.ts +70 -0
- package/dist/persistence/members.js +0 -0
- package/dist/persistence/members.js.map +1 -0
- package/dist/persistence/publishing.d.ts +59 -0
- package/dist/persistence/publishing.js +95 -0
- package/dist/persistence/publishing.js.map +1 -0
- package/dist/persistence/reference-extraction.d.ts +44 -0
- package/dist/persistence/reference-extraction.js +204 -0
- package/dist/persistence/reference-extraction.js.map +1 -0
- package/dist/persistence/reference-indexing.d.ts +68 -0
- package/dist/persistence/reference-indexing.js +112 -0
- package/dist/persistence/reference-indexing.js.map +1 -0
- package/dist/persistence/references.d.ts +257 -0
- package/dist/persistence/references.js +0 -0
- package/dist/persistence/references.js.map +1 -0
- package/dist/persistence/seed.d.ts +55 -0
- package/dist/persistence/seed.js +102 -0
- package/dist/persistence/seed.js.map +1 -0
- package/dist/persistence/self-maintaining-adapter.d.ts +41 -0
- package/dist/persistence/self-maintaining-adapter.js +79 -0
- package/dist/persistence/self-maintaining-adapter.js.map +1 -0
- package/dist/plugins/asset-storage.d.ts +76 -0
- package/dist/plugins/asset-storage.js +104 -0
- package/dist/plugins/asset-storage.js.map +1 -0
- package/dist/plugins/component-package.d.ts +54 -0
- package/dist/plugins/component-package.js +92 -0
- package/dist/plugins/component-package.js.map +1 -0
- package/dist/plugins/data-sources.d.ts +78 -0
- package/dist/plugins/data-sources.js +99 -0
- package/dist/plugins/data-sources.js.map +1 -0
- package/dist/plugins/examples.d.ts +47 -0
- package/dist/plugins/examples.js +205 -0
- package/dist/plugins/examples.js.map +1 -0
- package/dist/plugins/export-targets.d.ts +47 -0
- package/dist/plugins/export-targets.js +78 -0
- package/dist/plugins/export-targets.js.map +1 -0
- package/dist/plugins/field-types.d.ts +86 -0
- package/dist/plugins/field-types.js +93 -0
- package/dist/plugins/field-types.js.map +1 -0
- package/dist/plugins/hooks.d.ts +60 -0
- package/dist/plugins/hooks.js +94 -0
- package/dist/plugins/hooks.js.map +1 -0
- package/dist/plugins/index.d.ts +27 -0
- package/dist/plugins/index.js +28 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/marketplace.d.ts +90 -0
- package/dist/plugins/marketplace.js +110 -0
- package/dist/plugins/marketplace.js.map +1 -0
- package/dist/plugins/plugin.d.ts +107 -0
- package/dist/plugins/plugin.js +122 -0
- package/dist/plugins/plugin.js.map +1 -0
- package/dist/plugins/templates.d.ts +66 -0
- package/dist/plugins/templates.js +74 -0
- package/dist/plugins/templates.js.map +1 -0
- package/dist/project-graph/binding.d.ts +68 -0
- package/dist/project-graph/binding.js +39 -0
- package/dist/project-graph/binding.js.map +1 -0
- package/dist/project-graph/container.d.ts +126 -0
- package/dist/project-graph/container.js +221 -0
- package/dist/project-graph/container.js.map +1 -0
- package/dist/project-graph/edit.d.ts +66 -0
- package/dist/project-graph/edit.js +201 -0
- package/dist/project-graph/edit.js.map +1 -0
- package/dist/project-graph/fixtures.d.ts +51 -0
- package/dist/project-graph/fixtures.js +224 -0
- package/dist/project-graph/fixtures.js.map +1 -0
- package/dist/project-graph/ids.d.ts +17 -0
- package/dist/project-graph/ids.js +19 -0
- package/dist/project-graph/ids.js.map +1 -0
- package/dist/project-graph/index.d.ts +12 -0
- package/dist/project-graph/index.js +13 -0
- package/dist/project-graph/index.js.map +1 -0
- package/dist/project-graph/issues.d.ts +61 -0
- package/dist/project-graph/issues.js +44 -0
- package/dist/project-graph/issues.js.map +1 -0
- package/dist/project-graph/json.d.ts +6 -0
- package/dist/project-graph/json.js +10 -0
- package/dist/project-graph/json.js.map +1 -0
- package/dist/project-graph/node.d.ts +48 -0
- package/dist/project-graph/node.js +42 -0
- package/dist/project-graph/node.js.map +1 -0
- package/dist/project-graph/normalize.d.ts +17 -0
- package/dist/project-graph/normalize.js +110 -0
- package/dist/project-graph/normalize.js.map +1 -0
- package/dist/project-graph/serialize.d.ts +36 -0
- package/dist/project-graph/serialize.js +51 -0
- package/dist/project-graph/serialize.js.map +1 -0
- package/dist/project-graph/structure.d.ts +40 -0
- package/dist/project-graph/structure.js +36 -0
- package/dist/project-graph/structure.js.map +1 -0
- package/dist/project-graph/validate.d.ts +84 -0
- package/dist/project-graph/validate.js +158 -0
- package/dist/project-graph/validate.js.map +1 -0
- package/dist/rich-text/adapter.d.ts +43 -0
- package/dist/rich-text/adapter.js +24 -0
- package/dist/rich-text/adapter.js.map +1 -0
- package/dist/rich-text/dom-editor.d.ts +101 -0
- package/dist/rich-text/dom-editor.js +482 -0
- package/dist/rich-text/dom-editor.js.map +1 -0
- package/dist/rich-text/embed.d.ts +70 -0
- package/dist/rich-text/embed.js +70 -0
- package/dist/rich-text/embed.js.map +1 -0
- package/dist/rich-text/fixtures.d.ts +25 -0
- package/dist/rich-text/fixtures.js +115 -0
- package/dist/rich-text/fixtures.js.map +1 -0
- package/dist/rich-text/html.d.ts +5 -0
- package/dist/rich-text/html.js +309 -0
- package/dist/rich-text/html.js.map +1 -0
- package/dist/rich-text/index.d.ts +19 -0
- package/dist/rich-text/index.js +20 -0
- package/dist/rich-text/index.js.map +1 -0
- package/dist/rich-text/markdown.d.ts +7 -0
- package/dist/rich-text/markdown.js +235 -0
- package/dist/rich-text/markdown.js.map +1 -0
- package/dist/rich-text/portable-text.d.ts +50 -0
- package/dist/rich-text/portable-text.js +223 -0
- package/dist/rich-text/portable-text.js.map +1 -0
- package/dist/rich-text/registry.d.ts +48 -0
- package/dist/rich-text/registry.js +16 -0
- package/dist/rich-text/registry.js.map +1 -0
- package/dist/rich-text/render.d.ts +69 -0
- package/dist/rich-text/render.js +205 -0
- package/dist/rich-text/render.js.map +1 -0
- package/dist/rich-text/schema.d.ts +86 -0
- package/dist/rich-text/schema.js +80 -0
- package/dist/rich-text/schema.js.map +1 -0
- package/dist/rich-text/test-support.d.ts +7 -0
- package/dist/rich-text/test-support.js +8 -0
- package/dist/rich-text/test-support.js.map +1 -0
- package/dist/runtime-renderer/client.d.ts +14 -0
- package/dist/runtime-renderer/client.js +15 -0
- package/dist/runtime-renderer/client.js.map +1 -0
- package/dist/runtime-renderer/condition.d.ts +8 -0
- package/dist/runtime-renderer/condition.js +48 -0
- package/dist/runtime-renderer/condition.js.map +1 -0
- package/dist/runtime-renderer/content-client.d.ts +62 -0
- package/dist/runtime-renderer/content-client.js +37 -0
- package/dist/runtime-renderer/content-client.js.map +1 -0
- package/dist/runtime-renderer/context.d.ts +185 -0
- package/dist/runtime-renderer/context.js +7 -0
- package/dist/runtime-renderer/context.js.map +1 -0
- package/dist/runtime-renderer/fallback.d.ts +39 -0
- package/dist/runtime-renderer/fallback.js +45 -0
- package/dist/runtime-renderer/fallback.js.map +1 -0
- package/dist/runtime-renderer/index.d.ts +13 -0
- package/dist/runtime-renderer/index.js +13 -0
- package/dist/runtime-renderer/index.js.map +1 -0
- package/dist/runtime-renderer/primitives.d.ts +85 -0
- package/dist/runtime-renderer/primitives.js +442 -0
- package/dist/runtime-renderer/primitives.js.map +1 -0
- package/dist/runtime-renderer/render.d.ts +138 -0
- package/dist/runtime-renderer/render.js +825 -0
- package/dist/runtime-renderer/render.js.map +1 -0
- package/dist/runtime-renderer/rich-text.d.ts +26 -0
- package/dist/runtime-renderer/rich-text.js +113 -0
- package/dist/runtime-renderer/rich-text.js.map +1 -0
- package/dist/starter-kits/index.d.ts +13 -0
- package/dist/starter-kits/index.js +14 -0
- package/dist/starter-kits/index.js.map +1 -0
- package/dist/starter-kits/instantiate.d.ts +29 -0
- package/dist/starter-kits/instantiate.js +24 -0
- package/dist/starter-kits/instantiate.js.map +1 -0
- package/dist/starter-kits/kit.d.ts +61 -0
- package/dist/starter-kits/kit.js +37 -0
- package/dist/starter-kits/kit.js.map +1 -0
- package/dist/starter-kits/kits.d.ts +7 -0
- package/dist/starter-kits/kits.js +201 -0
- package/dist/starter-kits/kits.js.map +1 -0
- package/dist/starter-kits/manifests.d.ts +7 -0
- package/dist/starter-kits/manifests.js +111 -0
- package/dist/starter-kits/manifests.js.map +1 -0
- package/dist/starter-kits/upgrade.d.ts +39 -0
- package/dist/starter-kits/upgrade.js +54 -0
- package/dist/starter-kits/upgrade.js.map +1 -0
- package/dist/starter-kits/validate.d.ts +16 -0
- package/dist/starter-kits/validate.js +77 -0
- package/dist/starter-kits/validate.js.map +1 -0
- package/dist/studio-core/context.d.ts +27 -0
- package/dist/studio-core/context.js +44 -0
- package/dist/studio-core/context.js.map +1 -0
- package/dist/studio-core/errors.d.ts +58 -0
- package/dist/studio-core/errors.js +74 -0
- package/dist/studio-core/errors.js.map +1 -0
- package/dist/studio-core/fixtures.d.ts +18 -0
- package/dist/studio-core/fixtures.js +83 -0
- package/dist/studio-core/fixtures.js.map +1 -0
- package/dist/studio-core/ids.d.ts +22 -0
- package/dist/studio-core/ids.js +33 -0
- package/dist/studio-core/ids.js.map +1 -0
- package/dist/studio-core/index.d.ts +11 -0
- package/dist/studio-core/index.js +12 -0
- package/dist/studio-core/index.js.map +1 -0
- package/dist/studio-core/issues.d.ts +50 -0
- package/dist/studio-core/issues.js +33 -0
- package/dist/studio-core/issues.js.map +1 -0
- package/dist/studio-core/metadata.d.ts +109 -0
- package/dist/studio-core/metadata.js +74 -0
- package/dist/studio-core/metadata.js.map +1 -0
- package/dist/studio-core/repository.d.ts +73 -0
- package/dist/studio-core/repository.js +202 -0
- package/dist/studio-core/repository.js.map +1 -0
- package/dist/studio-core/sections.d.ts +73 -0
- package/dist/studio-core/sections.js +68 -0
- package/dist/studio-core/sections.js.map +1 -0
- package/dist/studio-core/validate.d.ts +15 -0
- package/dist/studio-core/validate.js +119 -0
- package/dist/studio-core/validate.js.map +1 -0
- package/dist/studio-core/workspace-config.d.ts +587 -0
- package/dist/studio-core/workspace-config.js +434 -0
- package/dist/studio-core/workspace-config.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createElement, Fragment } from 'react';
|
|
3
|
+
import { DEFAULT_SLOT, OUTLET_SLOT } from '@elytracms/core/project-graph';
|
|
4
|
+
import { manifestSlots, propValueValidator } from '@elytracms/core/component-registry';
|
|
5
|
+
import { isAssetNode, readDocumentRef } from '@elytracms/core/cms-core';
|
|
6
|
+
import { getImplementation } from './context';
|
|
7
|
+
import { ContentClientProvider } from './content-client';
|
|
8
|
+
import { evaluateCondition } from './condition';
|
|
9
|
+
import { MissingComponentFallback, MissingSlotPlaceholder, RenderErrorBoundary, RenderErrorFallback, } from './fallback';
|
|
10
|
+
/**
|
|
11
|
+
* Render a single component node to React. Never throws on a node: missing components,
|
|
12
|
+
* invalid props, missing required slots, and resolver errors all degrade to visible
|
|
13
|
+
* fallbacks (brief §4.11).
|
|
14
|
+
*/
|
|
15
|
+
export function renderNode(node, ctx) {
|
|
16
|
+
// Condition gate — render nothing when false.
|
|
17
|
+
if (node.condition && !evaluateCondition(node.condition, ctx.resolveBinding, ctx.item)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const manifest = ctx.registry.getManifest(node.componentId);
|
|
21
|
+
const implementation = getImplementation(ctx.implementations, node.componentId);
|
|
22
|
+
// Missing component: no manifest OR no implementation.
|
|
23
|
+
if (!manifest || !implementation) {
|
|
24
|
+
return decorate(ctx, node, _jsx(MissingComponentFallback, { nodeId: node.id, componentId: node.componentId }));
|
|
25
|
+
}
|
|
26
|
+
let resolved;
|
|
27
|
+
try {
|
|
28
|
+
resolved = resolveRender(node, manifest, ctx);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
// A thrown resolver (binding/prop) error yields a fallback, not a crash.
|
|
32
|
+
return decorate(ctx, node, _jsx(RenderErrorFallback, { nodeId: node.id, message: messageOf(error) }));
|
|
33
|
+
}
|
|
34
|
+
const element = createElement(implementation, { ...resolved.props, ...resolved.slotProps, key: node.id }, resolved.children);
|
|
35
|
+
// Wrap each node so a throw during the implementation's own render degrades on the client.
|
|
36
|
+
return decorate(ctx, node, _jsx(RenderErrorBoundary, { nodeId: node.id, children: element }, node.id));
|
|
37
|
+
}
|
|
38
|
+
/** Apply the host's optional per-node decorator (Builder selection markers). */
|
|
39
|
+
function decorate(ctx, node, rendered) {
|
|
40
|
+
return ctx.decorateNode ? ctx.decorateNode(node, rendered) : rendered;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Bind a `RichTextEmbedRenderer` (EC-189) to a `RenderContext`: each embedded
|
|
44
|
+
* composition node renders through `renderComposition` with the SAME context, so an
|
|
45
|
+
* embed reuses the EC-015 visible fallbacks, the injected binding resolver, the
|
|
46
|
+
* content client, and any active item scope exactly as on a page.
|
|
47
|
+
*
|
|
48
|
+
* The bound renderer is a FUNCTION closed over `ctx`, so it is injected into the
|
|
49
|
+
* consuming component as a **prop** (see `resolveRender`), never through a
|
|
50
|
+
* `'use client'` React-context provider: a function cannot cross the RSC
|
|
51
|
+
* server→client boundary into a client component, so the provider approach
|
|
52
|
+
* 500s a server-rendered (host) page. A server→server prop is fine, and the
|
|
53
|
+
* studio's client render injects the identical prop the same way.
|
|
54
|
+
*/
|
|
55
|
+
export function createEmbedRenderer(ctx) {
|
|
56
|
+
return (node) => renderComposition(node, ctx);
|
|
57
|
+
}
|
|
58
|
+
/** Whether a manifest declares a `richText` prop — i.e. it may render embeds (EC-189). */
|
|
59
|
+
function manifestConsumesEmbeds(manifest) {
|
|
60
|
+
return Object.values(manifest.props).some((field) => field.type === 'richText');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Bind a blocks renderer to a `RenderContext` (EC-254 transclusion): renders a
|
|
64
|
+
* populated relation target's `blocks` field value as a composition through the
|
|
65
|
+
* SAME `renderComposition` pipeline (EC-015 fallbacks, the injected resolvers, any
|
|
66
|
+
* active item scope), so a referenced document's sub-blocks compose inline exactly
|
|
67
|
+
* like a page's. Like {@link createEmbedRenderer}, it is a FUNCTION closed over
|
|
68
|
+
* `ctx` injected as a PROP (`renderBlocks`), never a `'use client'` context
|
|
69
|
+
* provider — a function cannot cross the RSC boundary into a client component.
|
|
70
|
+
*/
|
|
71
|
+
export function createBlocksRenderer(ctx) {
|
|
72
|
+
return (value) => renderComposition(value, ctx);
|
|
73
|
+
}
|
|
74
|
+
/** Whether a manifest declares a `relation` prop — i.e. it populates/transcludes (EC-254). */
|
|
75
|
+
function manifestConsumesRelations(manifest) {
|
|
76
|
+
return Object.values(manifest.props).some((field) => field.type === 'relation');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A `{ collection, id }` relation reference, or `null` when the value is not one.
|
|
80
|
+
* EC-277: accepts the self-typed envelope (`{ type:'reference', collection, id }`)
|
|
81
|
+
* and legacy bare `{ collection, id }` via the shared tolerant reader.
|
|
82
|
+
*/
|
|
83
|
+
function relationRefOf(value) {
|
|
84
|
+
return readDocumentRef(value) ?? null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The asset id a prop value points at, or `undefined`. EC-277: a self-typed asset
|
|
88
|
+
* envelope (`{ type:'asset', id }`) OR a legacy bare id string — but NOT a literal
|
|
89
|
+
* URL string, which the renderer passes through as a direct src (URL-passthrough).
|
|
90
|
+
*/
|
|
91
|
+
function assetIdOfProp(value) {
|
|
92
|
+
if (isAssetNode(value))
|
|
93
|
+
return value.id;
|
|
94
|
+
if (typeof value === 'string' && value !== '' && !isUrlLike(value))
|
|
95
|
+
return value;
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/** Stable cycle-guard key for a relation target. */
|
|
99
|
+
function relationKey(ref) {
|
|
100
|
+
return `${ref.collection}:${ref.id}`;
|
|
101
|
+
}
|
|
102
|
+
/** The cycle-guard keys (`collection:id`) of a node's static relation props (EC-254). */
|
|
103
|
+
function nodeRelationKeys(node, manifest) {
|
|
104
|
+
const keys = [];
|
|
105
|
+
for (const [name, value] of Object.entries(node.props ?? {})) {
|
|
106
|
+
if (value.kind !== 'static')
|
|
107
|
+
continue;
|
|
108
|
+
const spec = manifest.props[name];
|
|
109
|
+
if (spec?.type !== 'relation')
|
|
110
|
+
continue;
|
|
111
|
+
const refs = spec.cardinality === 'many' ? (Array.isArray(value.value) ? value.value : []) : [value.value];
|
|
112
|
+
for (const raw of refs) {
|
|
113
|
+
const ref = relationRefOf(raw);
|
|
114
|
+
if (ref)
|
|
115
|
+
keys.push(relationKey(ref));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return keys;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Populate a static `relation` prop's stored value to delivery shape (EC-254).
|
|
122
|
+
* `one` → the single populated target, or `undefined` (omit the prop) when the
|
|
123
|
+
* ref is malformed, unresolved (missing/invisible target), or cyclic. `many` →
|
|
124
|
+
* the array of populated targets with such entries dropped. A ref already on the
|
|
125
|
+
* transclude path is skipped — the cycle guard. Mirrors the asset branch's
|
|
126
|
+
* "resolved object or graceful omission" contract; the caller only assigns a
|
|
127
|
+
* defined result.
|
|
128
|
+
*/
|
|
129
|
+
function resolveRelationProp(cardinality, value, ctx) {
|
|
130
|
+
const resolveOne = (raw) => {
|
|
131
|
+
const ref = relationRefOf(raw);
|
|
132
|
+
if (!ref)
|
|
133
|
+
return null;
|
|
134
|
+
if (ctx.transcludePath?.includes(relationKey(ref)))
|
|
135
|
+
return null; // reference cycle
|
|
136
|
+
return ctx.resolveRelation?.(ref) ?? null;
|
|
137
|
+
};
|
|
138
|
+
if (cardinality === 'many') {
|
|
139
|
+
const refs = Array.isArray(value) ? value : [];
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const raw of refs) {
|
|
142
|
+
const target = resolveOne(raw);
|
|
143
|
+
if (target)
|
|
144
|
+
out.push(target);
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
return resolveOne(value) ?? undefined;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Wrap a composite render in the `ContentClientProvider` when a content client is
|
|
152
|
+
* injected (EC-191), so self-fetching components reach it via `useContentClient()`.
|
|
153
|
+
* Nested wraps are harmless (inner provider wins with an equivalent value).
|
|
154
|
+
*
|
|
155
|
+
* Note (EC-189): the rich-text embed renderer is NOT wrapped here — it is a
|
|
156
|
+
* function and a `'use client'` provider cannot receive it across the RSC
|
|
157
|
+
* boundary (it threw "Functions cannot be passed directly to Client Components"
|
|
158
|
+
* on every server-rendered page). It is injected as a prop in `resolveRender`.
|
|
159
|
+
*/
|
|
160
|
+
function withContent(ctx, rendered) {
|
|
161
|
+
if (ctx.content)
|
|
162
|
+
return _jsx(ContentClientProvider, { client: ctx.content, children: rendered });
|
|
163
|
+
return rendered;
|
|
164
|
+
}
|
|
165
|
+
/** Resolve a node's props and slot children. Throws only if a resolver throws (caught upstream). */
|
|
166
|
+
function resolveRender(node, manifest, ctx) {
|
|
167
|
+
const props = resolveProps(node, manifest, ctx);
|
|
168
|
+
// EC-189: a component with a `richText` prop (the RichText primitive) renders
|
|
169
|
+
// `componentEmbed` nodes through the renderer. Inject the embed renderer as a
|
|
170
|
+
// prop — server-safe (a server→server function prop), unlike a `'use client'`
|
|
171
|
+
// context provider, which cannot receive a function across the RSC boundary.
|
|
172
|
+
if (manifestConsumesEmbeds(manifest))
|
|
173
|
+
props.renderEmbed = createEmbedRenderer(ctx);
|
|
174
|
+
// EC-254: a component with a `relation` prop may transclude a populated target's
|
|
175
|
+
// `blocks` field (e.g. `project.Reusable` rendering the referenced `reusable`'s
|
|
176
|
+
// `content`). Inject a composition renderer — server-safe like `renderEmbed` —
|
|
177
|
+
// closed over a context whose cycle-guard path includes THIS node's relation
|
|
178
|
+
// targets, so a transcluded sub-block that references an ancestor target degrades
|
|
179
|
+
// instead of looping. Population above ran with `ctx` (the un-extended path), so
|
|
180
|
+
// the node's OWN targets are populated, never pre-blocked.
|
|
181
|
+
if (manifestConsumesRelations(manifest)) {
|
|
182
|
+
props.renderBlocks = createBlocksRenderer({
|
|
183
|
+
...ctx,
|
|
184
|
+
transcludePath: [...(ctx.transcludePath ?? []), ...nodeRelationKeys(node, manifest)],
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// EC-255: a component whose manifest declares a `listing` renders documents from
|
|
188
|
+
// a LIVE query (a dynamic grid), not a static pick. Build the query from its
|
|
189
|
+
// filter-source prop and inject the resolved documents as the declared prop — the
|
|
190
|
+
// host runs the query (Next bakes it, the studio runs it live); the renderer never
|
|
191
|
+
// executes one. No resolver wired, or no usable pick, yields an empty array, so the
|
|
192
|
+
// block shows its own empty state rather than listing the whole collection.
|
|
193
|
+
if (manifest.listing) {
|
|
194
|
+
const query = listingQueryOf(node, manifest.listing);
|
|
195
|
+
props[manifest.listing.prop] = (query && ctx.resolveListing?.(query)) || [];
|
|
196
|
+
}
|
|
197
|
+
// Repeater nodes iterate their items prop over the item slot template.
|
|
198
|
+
if (manifest.iterate) {
|
|
199
|
+
return resolveRepeater(node, manifest, manifest.iterate, props, ctx);
|
|
200
|
+
}
|
|
201
|
+
// Composition hosts render a block-field value (a ComponentNode tree) as children.
|
|
202
|
+
if (manifest.composition) {
|
|
203
|
+
return resolveComposition(manifest.composition, props, ctx);
|
|
204
|
+
}
|
|
205
|
+
const { children, slotProps } = renderSlots(node, manifest, ctx);
|
|
206
|
+
return { props, slotProps, children };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Render a composition host: the resolved `valueProp` holds a stored composition
|
|
210
|
+
* (a `ComponentNode` tree), which becomes the component's children via
|
|
211
|
+
* `renderComposition` — so a `blocks` field value composes inside a page/template
|
|
212
|
+
* exactly like authored nodes (EC-188, AD-12). Mirrors `resolveRepeater`: the raw
|
|
213
|
+
* value stays in `props` (harmless; the implementation typically ignores it) and the
|
|
214
|
+
* rendered tree is handed over as `children`.
|
|
215
|
+
*/
|
|
216
|
+
function resolveComposition(spec, props, ctx) {
|
|
217
|
+
return { props, slotProps: {}, children: renderComposition(props[spec.valueProp], ctx) };
|
|
218
|
+
}
|
|
219
|
+
/** Resolve every prop of a node into a concrete runtime value. */
|
|
220
|
+
function resolveProps(node, manifest, ctx) {
|
|
221
|
+
const out = {};
|
|
222
|
+
const entries = Object.entries(node.props ?? {});
|
|
223
|
+
const isSpread = (value) => value.kind === 'binding' && value.binding.mode === 'spread';
|
|
224
|
+
// Apply spread bindings first, then explicit props — so a named static/value prop always wins
|
|
225
|
+
// over a same-named spread key, predictably and independent of authoring order (EC-089).
|
|
226
|
+
for (const [name, value] of entries)
|
|
227
|
+
if (isSpread(value))
|
|
228
|
+
resolveProp(name, value, manifest, ctx, out);
|
|
229
|
+
for (const [name, value] of entries)
|
|
230
|
+
if (!isSpread(value))
|
|
231
|
+
resolveProp(name, value, manifest, ctx, out);
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* A value an `asset` prop can carry verbatim instead of an asset id: an absolute
|
|
236
|
+
* URL (`https://…`, protocol-relative `//…`), a root-relative path (`/…`), or an
|
|
237
|
+
* inline `data:`/`blob:` source. Lets the Image primitive keep accepting plain
|
|
238
|
+
* URLs (back-compat: `Image.src` was a free `text` prop before EC-195) and keeps
|
|
239
|
+
* the renderer usable with no asset store wired.
|
|
240
|
+
*/
|
|
241
|
+
function isUrlLike(value) {
|
|
242
|
+
return (/^(https?:)?\/\//.test(value) ||
|
|
243
|
+
value.startsWith('/') ||
|
|
244
|
+
value.startsWith('data:') ||
|
|
245
|
+
value.startsWith('blob:'));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Collect the asset ids a node tree resolves through `resolveAsset` (EC-205): the
|
|
249
|
+
* STATIC `asset`-typed props holding a real asset id (not a passthrough URL/path).
|
|
250
|
+
* Mirrors {@link resolveProp}'s asset branch exactly, so the set is COMPLETE (no
|
|
251
|
+
* referenced asset is missed → no broken image) and MINIMAL (only ids the renderer
|
|
252
|
+
* would resolve). Bound asset props resolve via bindings, and document asset FIELDS
|
|
253
|
+
* resolve separately — neither uses the baked asset list, so neither is collected.
|
|
254
|
+
* Lets a delivery host page-scope the baked assets to exactly what a page uses.
|
|
255
|
+
*/
|
|
256
|
+
export function collectReferencedAssetIds(node, registry, into = new Set()) {
|
|
257
|
+
const manifest = registry.getManifest(node.componentId);
|
|
258
|
+
if (manifest) {
|
|
259
|
+
for (const [name, value] of Object.entries(node.props ?? {})) {
|
|
260
|
+
if (value.kind !== 'static')
|
|
261
|
+
continue;
|
|
262
|
+
const spec = manifest.props[name];
|
|
263
|
+
if (spec?.type === 'asset') {
|
|
264
|
+
const assetId = assetIdOfProp(value.value);
|
|
265
|
+
if (assetId !== undefined)
|
|
266
|
+
into.add(assetId);
|
|
267
|
+
}
|
|
268
|
+
else if (spec?.type === 'object') {
|
|
269
|
+
// EC-253: a gallery/repeater prop nests asset ids — collect them too, so the
|
|
270
|
+
// delivery host bakes every referenced asset and no nested image is missed.
|
|
271
|
+
collectObjectPropAssetIds(spec.fields, spec.cardinality, value.value, into);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
for (const children of Object.values(node.slots ?? {})) {
|
|
276
|
+
for (const child of children)
|
|
277
|
+
collectReferencedAssetIds(child, registry, into);
|
|
278
|
+
}
|
|
279
|
+
return into;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Walk a composition (a page's blocks + its layout root) and POPULATE every static
|
|
283
|
+
* `relation` prop it references into a serializable `{ "collection:id": target }`
|
|
284
|
+
* map (EC-254) — the relation analog of {@link collectReferencedAssetIds}. A
|
|
285
|
+
* delivery host bakes this into its (serializable) render outcome and rebuilds
|
|
286
|
+
* `RenderContext.resolveRelation` from the map inside the RSC, exactly as it does
|
|
287
|
+
* with the page-scoped asset list — because a function (the content client) can
|
|
288
|
+
* ride neither the data cache nor the RSC server→client boundary.
|
|
289
|
+
*
|
|
290
|
+
* Transclusion-aware + cycle-safe: a populated target's composition-shaped fields
|
|
291
|
+
* (a `reusable`'s `content` blocks, which a component renders inline via
|
|
292
|
+
* `renderBlocks`) are recursively walked so those blocks' own relations are
|
|
293
|
+
* populated too; a target already in the map is never re-walked, so a reference
|
|
294
|
+
* cycle terminates. `resolveRelation` is the injected populate (depth-1) — passed
|
|
295
|
+
* in, never imported, so this stays free of the content/persistence packages.
|
|
296
|
+
*
|
|
297
|
+
* Optional `refKeys` records the `collection:id` of EVERY referenced pick — even
|
|
298
|
+
* one that resolved to `null` (an unpublished/missing target) — so a delivery host
|
|
299
|
+
* can cache-tag the page by it (EC-254/EC-146): editing or publishing a referenced
|
|
300
|
+
* document then invalidates the embedding page, including a previously-unpublished
|
|
301
|
+
* pick that should appear the moment it publishes.
|
|
302
|
+
*/
|
|
303
|
+
export function collectReferencedRelations(nodes, registry, resolveRelation, into = {}, refKeys) {
|
|
304
|
+
for (const node of nodes) {
|
|
305
|
+
const manifest = registry.getManifest(node.componentId);
|
|
306
|
+
if (manifest) {
|
|
307
|
+
for (const [name, value] of Object.entries(node.props ?? {})) {
|
|
308
|
+
if (value.kind !== 'static')
|
|
309
|
+
continue;
|
|
310
|
+
const spec = manifest.props[name];
|
|
311
|
+
if (spec?.type === 'relation') {
|
|
312
|
+
const refs = spec.cardinality === 'many'
|
|
313
|
+
? Array.isArray(value.value)
|
|
314
|
+
? value.value
|
|
315
|
+
: []
|
|
316
|
+
: [value.value];
|
|
317
|
+
for (const raw of refs) {
|
|
318
|
+
const ref = relationRefOf(raw);
|
|
319
|
+
if (!ref)
|
|
320
|
+
continue;
|
|
321
|
+
const key = relationKey(ref);
|
|
322
|
+
refKeys?.add(key); // tag every pick, even an unresolved one (negative dependency)
|
|
323
|
+
if (key in into)
|
|
324
|
+
continue; // already populated → also terminates cycles
|
|
325
|
+
const target = resolveRelation(ref);
|
|
326
|
+
if (!target)
|
|
327
|
+
continue;
|
|
328
|
+
into[key] = target;
|
|
329
|
+
// Transclusion: recurse into the target's composition-shaped fields so a
|
|
330
|
+
// reusable's inlined `content` blocks get their own relations populated.
|
|
331
|
+
for (const fieldValue of Object.values(target)) {
|
|
332
|
+
const childNodes = compositionRoots(fieldValue);
|
|
333
|
+
if (childNodes.length > 0) {
|
|
334
|
+
collectReferencedRelations(childNodes, registry, resolveRelation, into, refKeys);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else if (spec?.type === 'object' && value.value != null) {
|
|
340
|
+
// EC-256: a `relation` nested in an `object` prop (a link object's `page`
|
|
341
|
+
// ref) is baked + tagged like a top-level pick, so renaming the target page
|
|
342
|
+
// invalidates the embedding page. Depth-1, NO transclusion — a link target
|
|
343
|
+
// is read for its path, not inlined (else link-following bakes the whole
|
|
344
|
+
// linked-site relation graph).
|
|
345
|
+
collectObjectPropRelations(spec.fields, spec.cardinality, value.value, resolveRelation, into, refKeys);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// A composition host stores its composed blocks in `node.props[valueProp]` (a
|
|
349
|
+
// blocks VALUE), NOT `node.slots` — and `manifestSlots` excludes the valueProp
|
|
350
|
+
// — so the slot recursion below misses them. Walk those composed nodes too, so
|
|
351
|
+
// a relation prop on a block nested inside a composition is collected (and thus
|
|
352
|
+
// baked for the host), matching what the render path resolves at depth.
|
|
353
|
+
if (manifest.composition) {
|
|
354
|
+
const composed = node.props?.[manifest.composition.valueProp];
|
|
355
|
+
if (composed?.kind === 'static') {
|
|
356
|
+
collectReferencedRelations(compositionRoots(composed.value), registry, resolveRelation, into, refKeys);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const children of Object.values(node.slots ?? {})) {
|
|
361
|
+
collectReferencedRelations(children, registry, resolveRelation, into, refKeys);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return into;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Build the {@link ListingQuery} a node's `listing` manifest declares (EC-255),
|
|
368
|
+
* or `null` when its filter-source prop carries no usable pick — the block then
|
|
369
|
+
* renders empty rather than listing the whole collection. Reads the `fromProp`
|
|
370
|
+
* STATIC pick (a relation value `{ collection, id }` reduced to its id, or a bare
|
|
371
|
+
* id string) and binds it to the listed collection's filter `field`. Pure (node
|
|
372
|
+
* props + manifest only; no content import), so the Next bake and the studio live
|
|
373
|
+
* resolver build the IDENTICAL query — and thus agree on the baked cache key.
|
|
374
|
+
*/
|
|
375
|
+
export function listingQueryOf(node, spec) {
|
|
376
|
+
const pick = node.props?.[spec.filter.fromProp];
|
|
377
|
+
if (!pick || pick.kind !== 'static')
|
|
378
|
+
return null;
|
|
379
|
+
const ref = relationRefOf(pick.value);
|
|
380
|
+
const id = ref ? ref.id : typeof pick.value === 'string' && pick.value !== '' ? pick.value : null;
|
|
381
|
+
if (id === null)
|
|
382
|
+
return null;
|
|
383
|
+
const query = { collection: spec.collection, where: { [spec.filter.field]: id } };
|
|
384
|
+
if (spec.sort)
|
|
385
|
+
query.sort = spec.sort;
|
|
386
|
+
if (spec.limit !== undefined)
|
|
387
|
+
query.limit = spec.limit;
|
|
388
|
+
return query;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Deterministic cache key for a {@link ListingQuery} (EC-255): a stable JSON shape
|
|
392
|
+
* (where-keys sorted, sort/limit normalized) so the Next host can key its baked
|
|
393
|
+
* `query → docs` map and the rebuilt resolver looks the listing up by the SAME key
|
|
394
|
+
* the renderer's {@link listingQueryOf} produces. Mirrors `@elytracms/core/content`'s
|
|
395
|
+
* `serializeListQuery`, kept local so the renderer takes no content dependency.
|
|
396
|
+
*/
|
|
397
|
+
export function serializeListingQuery(query) {
|
|
398
|
+
const where = {};
|
|
399
|
+
for (const key of Object.keys(query.where).sort()) {
|
|
400
|
+
const value = query.where[key];
|
|
401
|
+
if (value !== undefined)
|
|
402
|
+
where[key] = value;
|
|
403
|
+
}
|
|
404
|
+
return JSON.stringify({
|
|
405
|
+
collection: query.collection,
|
|
406
|
+
where,
|
|
407
|
+
sort: query.sort ? { field: query.sort.field, direction: query.sort.direction ?? 'asc' } : null,
|
|
408
|
+
limit: query.limit ?? null,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Walk a composition (a page's blocks + its layout root) and resolve every
|
|
413
|
+
* `listing` a node declares into a serializable `{ serializedQuery: docs[] }` map
|
|
414
|
+
* (EC-255) — the listing analog of {@link collectReferencedRelations}. A delivery
|
|
415
|
+
* host bakes this into its (serializable) render outcome and rebuilds
|
|
416
|
+
* `RenderContext.resolveListing` from the map inside the RSC, exactly as it does
|
|
417
|
+
* with relations and the page-scoped asset list, because the content client (which
|
|
418
|
+
* runs the query) can ride neither the data cache nor the RSC server→client
|
|
419
|
+
* boundary.
|
|
420
|
+
*
|
|
421
|
+
* `runListing` is the injected query executor (the host's `listDocuments`) — passed
|
|
422
|
+
* in, never imported, so this stays free of the content/persistence packages. The
|
|
423
|
+
* key is {@link serializeListingQuery}, so two nodes with the same query share a
|
|
424
|
+
* single execution.
|
|
425
|
+
*
|
|
426
|
+
* Transclusion-aware + cycle-safe, exactly like {@link collectReferencedRelations}:
|
|
427
|
+
* a composition host's composed blocks, child slots, AND a populated relation
|
|
428
|
+
* target's composition-shaped fields (a `reusable`'s `content` a block transcludes
|
|
429
|
+
* via `renderBlocks`) are all walked — so a listing nested inside a transcluded
|
|
430
|
+
* reusable is baked too, matching what the render path resolves at depth. Without
|
|
431
|
+
* this the Next host would render such a grid empty (the studio's live resolver
|
|
432
|
+
* still works) AND drop its collection-membership cache tag. `resolveRelation` is
|
|
433
|
+
* the injected populate (depth-1) used to reach those transcluded fields; a target
|
|
434
|
+
* already visited is never re-walked, so a reference cycle terminates.
|
|
435
|
+
*/
|
|
436
|
+
export function collectReferencedListings(nodes, registry, runListing, resolveRelation, into = {}, visitedRelations = new Set()) {
|
|
437
|
+
for (const node of nodes) {
|
|
438
|
+
const manifest = registry.getManifest(node.componentId);
|
|
439
|
+
if (manifest?.listing) {
|
|
440
|
+
const query = listingQueryOf(node, manifest.listing);
|
|
441
|
+
if (query) {
|
|
442
|
+
const key = serializeListingQuery(query);
|
|
443
|
+
if (!(key in into))
|
|
444
|
+
into[key] = runListing(query);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (manifest) {
|
|
448
|
+
// Transclusion: a static `relation` prop (e.g. `Reusable.element`) inlines its
|
|
449
|
+
// target's composition-shaped fields via `renderBlocks` at render — walk them
|
|
450
|
+
// so a listing nested in a transcluded reusable's content is baked too (the
|
|
451
|
+
// listing analog of `collectReferencedRelations`' transclusion recursion).
|
|
452
|
+
for (const [name, value] of Object.entries(node.props ?? {})) {
|
|
453
|
+
if (value.kind !== 'static')
|
|
454
|
+
continue;
|
|
455
|
+
const spec = manifest.props[name];
|
|
456
|
+
if (spec?.type !== 'relation')
|
|
457
|
+
continue;
|
|
458
|
+
const refs = spec.cardinality === 'many' ? (Array.isArray(value.value) ? value.value : []) : [value.value];
|
|
459
|
+
for (const raw of refs) {
|
|
460
|
+
const ref = relationRefOf(raw);
|
|
461
|
+
if (!ref)
|
|
462
|
+
continue;
|
|
463
|
+
const relKey = relationKey(ref);
|
|
464
|
+
if (visitedRelations.has(relKey))
|
|
465
|
+
continue; // already walked → terminates cycles
|
|
466
|
+
visitedRelations.add(relKey);
|
|
467
|
+
const target = resolveRelation(ref);
|
|
468
|
+
if (!target)
|
|
469
|
+
continue;
|
|
470
|
+
for (const fieldValue of Object.values(target)) {
|
|
471
|
+
const childNodes = compositionRoots(fieldValue);
|
|
472
|
+
if (childNodes.length > 0) {
|
|
473
|
+
collectReferencedListings(childNodes, registry, runListing, resolveRelation, into, visitedRelations);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (manifest?.composition) {
|
|
480
|
+
const composed = node.props?.[manifest.composition.valueProp];
|
|
481
|
+
if (composed?.kind === 'static') {
|
|
482
|
+
collectReferencedListings(compositionRoots(composed.value), registry, runListing, resolveRelation, into, visitedRelations);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
for (const children of Object.values(node.slots ?? {})) {
|
|
486
|
+
collectReferencedListings(children, registry, runListing, resolveRelation, into, visitedRelations);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return into;
|
|
490
|
+
}
|
|
491
|
+
/** Collect the asset ids nested inside an `object` prop value (mirrors {@link resolveObjectProps}). */
|
|
492
|
+
function collectObjectPropAssetIds(fields, cardinality, value, into) {
|
|
493
|
+
const items = cardinality === 'many' ? (Array.isArray(value) ? value : []) : [value];
|
|
494
|
+
for (const item of items) {
|
|
495
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
496
|
+
continue;
|
|
497
|
+
const record = item;
|
|
498
|
+
for (const sub of fields) {
|
|
499
|
+
const sv = record[sub.name];
|
|
500
|
+
if (sub.type === 'asset') {
|
|
501
|
+
const assetId = assetIdOfProp(sv);
|
|
502
|
+
if (assetId !== undefined)
|
|
503
|
+
into.add(assetId);
|
|
504
|
+
}
|
|
505
|
+
else if (sub.type === 'object' && sv != null)
|
|
506
|
+
collectObjectPropAssetIds(sub.fields, sub.cardinality, sv, into);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Populate (depth-1) the `relation` refs nested inside an `object` prop value into
|
|
512
|
+
* the relations bake map + refKeys (EC-256) — the relation analog of
|
|
513
|
+
* {@link collectObjectPropAssetIds}, so a `page` ref inside a link object
|
|
514
|
+
* (`buttons[].link.page`) is baked and cache-tagged exactly like a top-level pick.
|
|
515
|
+
* Recurses into nested `object` subfields. UNLIKE the top-level relation walk it
|
|
516
|
+
* does NOT transclude the target's composition-shaped fields: a link's target page
|
|
517
|
+
* is read for its path (depth-1), never inlined — following it would bake the whole
|
|
518
|
+
* linked-site relation graph into every embedding page. `refKeys` tags every pick
|
|
519
|
+
* (even an unresolved one) so publishing/renaming the target invalidates the page.
|
|
520
|
+
*/
|
|
521
|
+
function collectObjectPropRelations(fields, cardinality, value, resolveRelation, into, refKeys) {
|
|
522
|
+
const items = cardinality === 'many' ? (Array.isArray(value) ? value : []) : [value];
|
|
523
|
+
for (const item of items) {
|
|
524
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
525
|
+
continue;
|
|
526
|
+
const record = item;
|
|
527
|
+
for (const sub of fields) {
|
|
528
|
+
const sv = record[sub.name];
|
|
529
|
+
if (sub.type === 'relation' && sv != null) {
|
|
530
|
+
const refs = sub.cardinality === 'many' ? (Array.isArray(sv) ? sv : []) : [sv];
|
|
531
|
+
for (const raw of refs) {
|
|
532
|
+
const ref = relationRefOf(raw);
|
|
533
|
+
if (!ref)
|
|
534
|
+
continue;
|
|
535
|
+
const key = relationKey(ref);
|
|
536
|
+
refKeys?.add(key);
|
|
537
|
+
if (key in into)
|
|
538
|
+
continue;
|
|
539
|
+
const target = resolveRelation(ref);
|
|
540
|
+
if (target)
|
|
541
|
+
into[key] = target; // depth-1 only — never transclude a link target
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
else if (sub.type === 'object' && sv != null) {
|
|
545
|
+
collectObjectPropRelations(sub.fields, sub.cardinality, sv, resolveRelation, into, refKeys);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Resolve the `asset` and `relation` subfields nested inside an `object` / repeater
|
|
552
|
+
* prop value to delivery shape (EC-253 assets, EC-256 relations). The top-level
|
|
553
|
+
* branches of {@link resolveProp} only resolve a TOP-LEVEL `asset`/`relation` prop,
|
|
554
|
+
* so an asset nested in an `object` (a gallery's `images[].image`) or a relation
|
|
555
|
+
* nested in one (a link object's `buttons[].link.page`) would otherwise reach the
|
|
556
|
+
* implementation as a raw id/ref. This walks the prop's declared subfields:
|
|
557
|
+
*
|
|
558
|
+
* - `asset` → `ctx.resolveAsset` (url-like values pass through; an unresolved id
|
|
559
|
+
* drops the subfield), exactly as the top level does.
|
|
560
|
+
* - `relation` → `ctx.resolveRelation` depth-1 via {@link resolveRelationProp}, so
|
|
561
|
+
* the implementation reads the target's fields (e.g. a link's `page.slug` → a live
|
|
562
|
+
* path). No resolver → the raw ref passes through (graceful degradation); an
|
|
563
|
+
* unresolved `one` drops the subfield. Depth-1 only — a nested relation is never
|
|
564
|
+
* transcluded (a link target is read, not inlined).
|
|
565
|
+
*
|
|
566
|
+
* and recurses into nested `object` subfields. Pure; non-object items pass through.
|
|
567
|
+
*/
|
|
568
|
+
function resolveObjectProps(fields, cardinality, value, ctx) {
|
|
569
|
+
const items = cardinality === 'many' ? (Array.isArray(value) ? value : []) : [value];
|
|
570
|
+
const resolvedItems = items.map((item) => {
|
|
571
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
572
|
+
return item;
|
|
573
|
+
const next = { ...item };
|
|
574
|
+
for (const sub of fields) {
|
|
575
|
+
const sv = next[sub.name];
|
|
576
|
+
if (sub.type === 'asset') {
|
|
577
|
+
const assetId = assetIdOfProp(sv);
|
|
578
|
+
if (assetId === undefined)
|
|
579
|
+
continue; // URL string / empty / not an asset → leave as-is
|
|
580
|
+
const resolved = ctx.resolveAsset?.(assetId);
|
|
581
|
+
if (resolved)
|
|
582
|
+
next[sub.name] = resolved;
|
|
583
|
+
else
|
|
584
|
+
delete next[sub.name];
|
|
585
|
+
}
|
|
586
|
+
else if (sub.type === 'relation' && sv != null) {
|
|
587
|
+
if (!ctx.resolveRelation)
|
|
588
|
+
continue; // no resolver → leave the raw ref (degrade)
|
|
589
|
+
const resolved = resolveRelationProp(sub.cardinality, sv, ctx);
|
|
590
|
+
if (resolved !== undefined)
|
|
591
|
+
next[sub.name] = resolved;
|
|
592
|
+
else
|
|
593
|
+
delete next[sub.name]; // unresolved `one` → drop the subfield
|
|
594
|
+
}
|
|
595
|
+
else if (sub.type === 'object' && sv != null) {
|
|
596
|
+
next[sub.name] = resolveObjectProps(sub.fields, sub.cardinality, sv, ctx);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return next;
|
|
600
|
+
});
|
|
601
|
+
return cardinality === 'many' ? resolvedItems : resolvedItems[0];
|
|
602
|
+
}
|
|
603
|
+
/** Resolve a single prop into `out`, applying invalid-static-prop fallbacks and spread. */
|
|
604
|
+
function resolveProp(name, value, manifest, ctx, out) {
|
|
605
|
+
const spec = manifest.props[name];
|
|
606
|
+
if (value.kind === 'static') {
|
|
607
|
+
// Validate against the prop's field-def (EC-190); on failure use the field's
|
|
608
|
+
// `default` or omit — the EC-015 invalid-static-prop visible fallback.
|
|
609
|
+
const validate = propValueValidator(manifest, name);
|
|
610
|
+
if (validate && !validate(value.value)) {
|
|
611
|
+
if (spec?.default !== undefined)
|
|
612
|
+
out[name] = spec.default;
|
|
613
|
+
// else: omit the prop entirely and continue.
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
// EC-195: an `asset`-typed prop stores an asset id; resolve it to a usable
|
|
617
|
+
// asset (url + intrinsic dimensions + alt) so an uploaded image renders
|
|
618
|
+
// optimized. A raw URL/path value passes through (back-compat, and so the
|
|
619
|
+
// renderer never *requires* an asset store). A missing asset omits the prop
|
|
620
|
+
// — the implementation shows its own empty/fallback state, never a crash.
|
|
621
|
+
if (spec?.type === 'asset') {
|
|
622
|
+
// EC-277: an asset id arrives self-typed (`{ type:'asset', id }`) or as a legacy
|
|
623
|
+
// bare id; a literal URL/path string passes through unchanged (back-compat).
|
|
624
|
+
const assetId = assetIdOfProp(value.value);
|
|
625
|
+
if (assetId === undefined) {
|
|
626
|
+
if (typeof value.value === 'string')
|
|
627
|
+
out[name] = value.value;
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const resolved = ctx.resolveAsset?.(assetId);
|
|
631
|
+
if (resolved)
|
|
632
|
+
out[name] = resolved;
|
|
633
|
+
// else omit — unresolved asset id → visible fallback in the implementation.
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
// EC-253/EC-256: an `object` / repeater prop may nest `asset` subfields (a
|
|
637
|
+
// gallery's `images[].image`) or `relation` subfields (a link object's
|
|
638
|
+
// `buttons[].link.page`); resolve those to delivery shape so the implementation
|
|
639
|
+
// reads `item.image.url` / `link.page.slug`, mirroring the top-level branches at
|
|
640
|
+
// every depth.
|
|
641
|
+
if (spec?.type === 'object' && value.value != null) {
|
|
642
|
+
out[name] = resolveObjectProps(spec.fields, spec.cardinality, value.value, ctx);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
// EC-254: a `relation` prop stores a `{ collection, id }` ref (or an array for
|
|
646
|
+
// `many`) — the editor's document pick on a block. Populate it to the target's
|
|
647
|
+
// depth-1 delivery shape so the implementation reads resolved fields, not a raw
|
|
648
|
+
// ref. Mirrors the asset branch: unresolved → omit (`one`) / drop (`many`); no
|
|
649
|
+
// resolver wired → the raw ref passes through (the same graceful degradation).
|
|
650
|
+
if (spec?.type === 'relation') {
|
|
651
|
+
if (!ctx.resolveRelation) {
|
|
652
|
+
out[name] = value.value;
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const resolved = resolveRelationProp(spec.cardinality, value.value, ctx);
|
|
656
|
+
if (resolved !== undefined)
|
|
657
|
+
out[name] = resolved;
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
out[name] = value.value;
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
// Bound prop — resolve through the injected resolver (may throw; caught by caller).
|
|
664
|
+
// NB (EC-254): relation population is intentionally STATIC-only — a relation prop
|
|
665
|
+
// is the editor's document pick (v1 props are static, AD-12; prop bindings are
|
|
666
|
+
// parked). A bound value passes through as the resolver returns it, never through
|
|
667
|
+
// `resolveRelationProp`, so a relation prop should not be bound until prop bindings
|
|
668
|
+
// and their population are designed together.
|
|
669
|
+
const resolved = ctx.resolveBinding(value.binding, ctx.item);
|
|
670
|
+
if (value.binding.mode === 'spread') {
|
|
671
|
+
if (resolved && typeof resolved === 'object' && !Array.isArray(resolved)) {
|
|
672
|
+
Object.assign(out, resolved);
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
out[name] = resolved;
|
|
677
|
+
}
|
|
678
|
+
/** Render the named slots of a node: `children` maps to React children, others to props. */
|
|
679
|
+
function renderSlots(node, manifest, ctx) {
|
|
680
|
+
const slots = node.slots ?? {};
|
|
681
|
+
const slotProps = {};
|
|
682
|
+
let children = renderSlotChildren(slots[DEFAULT_SLOT], ctx);
|
|
683
|
+
// EC-190: `blocks` props are slots too (named child regions); `manifestSlots`
|
|
684
|
+
// folds them in (a no-op for manifests without blocks props).
|
|
685
|
+
const declaredSlots = manifestSlots(manifest);
|
|
686
|
+
for (const slotSpec of declaredSlots) {
|
|
687
|
+
if (slotSpec.name === DEFAULT_SLOT)
|
|
688
|
+
continue;
|
|
689
|
+
const nodes = slots[slotSpec.name];
|
|
690
|
+
if ((!nodes || nodes.length === 0) && slotSpec.required) {
|
|
691
|
+
// Missing required named slot — visible placeholder in that slot position.
|
|
692
|
+
slotProps[slotSpec.name] = _jsx(MissingSlotPlaceholder, { nodeId: node.id, slot: slotSpec.name });
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
slotProps[slotSpec.name] = renderSlotChildren(nodes, ctx);
|
|
696
|
+
}
|
|
697
|
+
// Pass through any node slots not declared on the manifest (e.g. a Switch's dynamic
|
|
698
|
+
// `case:*` slots), so dynamic-slot components receive their children as props.
|
|
699
|
+
const declared = new Set(declaredSlots.map((s) => s.name));
|
|
700
|
+
for (const [name, nodes] of Object.entries(slots)) {
|
|
701
|
+
if (name === DEFAULT_SLOT || declared.has(name))
|
|
702
|
+
continue;
|
|
703
|
+
slotProps[name] = renderSlotChildren(nodes, ctx);
|
|
704
|
+
}
|
|
705
|
+
// Missing required default slot — placeholder as children.
|
|
706
|
+
const defaultSpec = declaredSlots.find((s) => s.name === DEFAULT_SLOT);
|
|
707
|
+
const defaultNodes = slots[DEFAULT_SLOT];
|
|
708
|
+
if (defaultSpec?.required && (!defaultNodes || defaultNodes.length === 0)) {
|
|
709
|
+
children = _jsx(MissingSlotPlaceholder, { nodeId: node.id, slot: DEFAULT_SLOT });
|
|
710
|
+
}
|
|
711
|
+
return { children, slotProps };
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Render an ordered list of slot child nodes (array order preserved). Each
|
|
715
|
+
* entry is wrapped in a Fragment keyed by the node id: `renderNode`'s return
|
|
716
|
+
* value may be an unkeyed fallback element (missing component, render error)
|
|
717
|
+
* or have its key stripped by a host `decorateNode`, and unkeyed entries in
|
|
718
|
+
* this array would trigger React's `unique "key" prop` warning on every page.
|
|
719
|
+
*/
|
|
720
|
+
function renderSlotChildren(nodes, ctx) {
|
|
721
|
+
if (!nodes || nodes.length === 0)
|
|
722
|
+
return null;
|
|
723
|
+
return (_jsx(Fragment, { children: nodes.map((child) => (_jsx(Fragment, { children: renderNode(child, ctx) }, child.id))) }));
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Render a repeater: resolve `itemsProp` to an array and render the `itemSlot` template
|
|
727
|
+
* once per item, with `ItemContext` set so `repeaterItem`-mode bindings resolve against
|
|
728
|
+
* the item. The repeater implementation receives the rendered items as `children`.
|
|
729
|
+
*/
|
|
730
|
+
function resolveRepeater(node, manifest, iterate, props, ctx) {
|
|
731
|
+
const itemsValue = props[iterate.itemsProp];
|
|
732
|
+
const items = Array.isArray(itemsValue) ? itemsValue : [];
|
|
733
|
+
const template = node.slots?.[iterate.itemSlot];
|
|
734
|
+
const slotSpec = manifest.slots.find((s) => s.name === iterate.itemSlot);
|
|
735
|
+
if ((!template || template.length === 0) && slotSpec?.required) {
|
|
736
|
+
// No item template but the slot is required — visible placeholder, no iteration.
|
|
737
|
+
return {
|
|
738
|
+
props,
|
|
739
|
+
slotProps: {},
|
|
740
|
+
children: _jsx(MissingSlotPlaceholder, { nodeId: node.id, slot: iterate.itemSlot }),
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
const rendered = items.map((item, index) => {
|
|
744
|
+
const itemCtx = { ...ctx, item: { item, index } };
|
|
745
|
+
return (_jsx(Fragment, { children: (template ?? []).map((child) => (
|
|
746
|
+
// Keyed per template node for the same reason as renderSlotChildren.
|
|
747
|
+
_jsx(Fragment, { children: renderNode(child, itemCtx) }, child.id))) }, index));
|
|
748
|
+
});
|
|
749
|
+
return { props, slotProps: {}, children: _jsx(Fragment, { children: rendered }) };
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Render a composition FIELD VALUE (EC-188, AD-12): one `ComponentNode` root or an
|
|
753
|
+
* ordered list of them — a `blocks` field's stored value. Each root renders through
|
|
754
|
+
* `renderNode` with the SAME `RenderContext`, so the EC-015 visible fallbacks, the
|
|
755
|
+
* injected binding resolver, and any active item scope all apply exactly as on a
|
|
756
|
+
* page. An empty/absent value renders nothing (the host component owns its own empty
|
|
757
|
+
* state). Roots are keyed by id — `renderNode` may return an unkeyed fallback —
|
|
758
|
+
* mirroring `renderSlotChildren`.
|
|
759
|
+
*
|
|
760
|
+
* This is the shared delivery primitive for every composition surface: the block
|
|
761
|
+
* field (here), and later the page collection (EC-187) and rich-text embeds (EC-189).
|
|
762
|
+
*/
|
|
763
|
+
export function renderComposition(value, ctx) {
|
|
764
|
+
const roots = compositionRoots(value);
|
|
765
|
+
if (roots.length === 0)
|
|
766
|
+
return null;
|
|
767
|
+
return withContent(ctx, _jsx(Fragment, { children: roots.map((root) => (_jsx(Fragment, { children: renderNode(root, ctx) }, root.id))) }));
|
|
768
|
+
}
|
|
769
|
+
/** Coerce a stored composition value (one node, many, or absent) to a root list. */
|
|
770
|
+
function compositionRoots(value) {
|
|
771
|
+
if (Array.isArray(value))
|
|
772
|
+
return value.filter(isComponentNode);
|
|
773
|
+
if (isComponentNode(value))
|
|
774
|
+
return [value];
|
|
775
|
+
return [];
|
|
776
|
+
}
|
|
777
|
+
/** Structural ComponentNode guard — the stored value is opaque at the type level. */
|
|
778
|
+
function isComponentNode(value) {
|
|
779
|
+
return (typeof value === 'object' &&
|
|
780
|
+
value !== null &&
|
|
781
|
+
typeof value.id === 'string' &&
|
|
782
|
+
typeof value.componentId === 'string');
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Render a composition (`blocks`) field value composed INTO a graph layout
|
|
786
|
+
* (EC-187): the page collection's delivery primitive. The composition renders
|
|
787
|
+
* via `renderComposition` (the same per-node pipeline + EC-015 fallbacks), and
|
|
788
|
+
* the result is injected into the layout root's `outlet` slot — so a
|
|
789
|
+
* page-collection document's `body` composes inside its layout chrome.
|
|
790
|
+
*
|
|
791
|
+
* Degrades, never crashes: an `undefined`/unknown `layoutId`, or a graph with no
|
|
792
|
+
* matching layout, renders the bare composition (no layout chrome) rather than
|
|
793
|
+
* blanking the page. `withContent` wraps the output so a `ContentClient` on
|
|
794
|
+
* `ctx` propagates to self-fetching blocks (EC-191).
|
|
795
|
+
*/
|
|
796
|
+
export function renderCompositionInLayout(graph, layoutId, value, ctx) {
|
|
797
|
+
const layout = layoutId ? graph.layouts.find((l) => l.id === layoutId) : undefined;
|
|
798
|
+
// No layout selected, or the selected layout is absent from the graph: render
|
|
799
|
+
// the bare composition rather than blanking the page.
|
|
800
|
+
if (!layout)
|
|
801
|
+
return withContent(ctx, renderComposition(value, ctx));
|
|
802
|
+
// With a layout, inject the composition's roots into the layout root's outlet
|
|
803
|
+
// slot, so the composition renders through the layout's chrome via the
|
|
804
|
+
// identical per-node pipeline. `compositionRoots` coerces the stored value
|
|
805
|
+
// (one node, many, or absent) to the outlet's `ComponentNode[]`.
|
|
806
|
+
const root = injectNodesIntoOutlet(layout.root, compositionRoots(value));
|
|
807
|
+
return withContent(ctx, renderNode(root, ctx));
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Produce a layout tree with `nodes` inserted into the layout root's `outlet`
|
|
811
|
+
* slot. The original graph is never mutated.
|
|
812
|
+
*/
|
|
813
|
+
function injectNodesIntoOutlet(root, nodes) {
|
|
814
|
+
return {
|
|
815
|
+
...root,
|
|
816
|
+
slots: {
|
|
817
|
+
...(root.slots ?? {}),
|
|
818
|
+
[OUTLET_SLOT]: nodes,
|
|
819
|
+
},
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
function messageOf(error) {
|
|
823
|
+
return error instanceof Error ? error.message : String(error);
|
|
824
|
+
}
|
|
825
|
+
//# sourceMappingURL=render.js.map
|