@warpgogol/werkstatt-shared 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +352 -0
- package/src/checks/index.ts +24 -0
- package/src/checks/lib/astro-site-url.ts +30 -0
- package/src/checks/lib/i18n.ts +30 -0
- package/src/checks/result-helpers.ts +102 -0
- package/src/checks/suppressions-config.ts +195 -0
- package/src/index.ts +14 -0
- package/src/integration/README.md +38 -0
- package/src/integration/adapters.ts +143 -0
- package/src/integration/crm-buffer.ts +491 -0
- package/src/integration/dispatch.ts +106 -0
- package/src/integration/funnel.ts +379 -0
- package/src/integration/index.ts +56 -0
- package/src/integration/lifecycle.ts +155 -0
- package/src/integration/orchestration.ts +439 -0
- package/src/integration/port-barrel.ts +124 -0
- package/src/integration/port.ts +143 -0
- package/src/integration/qstash.ts +134 -0
- package/src/integration/sharding.ts +133 -0
- package/src/integration/tests/deliver-event.test.ts +52 -0
- package/src/integration/tests/dispatch.test.ts +88 -0
- package/src/integration/tests/funnel-bridge.test.ts +46 -0
- package/src/integration/tests/hub-runtime.test.ts +128 -0
- package/src/integration/tests/lifecycle.test.ts +69 -0
- package/src/integration/tests/qstash.test.ts +83 -0
- package/src/integration/tests/sharding.test.ts +64 -0
- package/src/integration/vitest.config.ts +8 -0
- package/src/integration-adapter-supabase-crm/adapter.ts +437 -0
- package/src/integration-adapter-supabase-crm/client.ts +605 -0
- package/src/integration-adapter-supabase-crm/index.ts +28 -0
- package/src/integration-adapter-supabase-crm/pipedrive-sync-target.ts +482 -0
- package/src/integration-adapter-supabase-crm/supabase-rest.ts +89 -0
- package/src/integration-adapter-supabase-crm/tenant-registry.ts +334 -0
- package/src/integration-adapter-supabase-crm/tests/change-balance.pbt.test.ts +83 -0
- package/src/integration-adapter-supabase-crm/tests/funnel-persistence.test.ts +409 -0
- package/src/integration-adapter-supabase-crm/tests/lifecycle-sync.test.ts +288 -0
- package/src/integration-adapter-supabase-crm/tests/stage-map.test.ts +80 -0
- package/src/integration-adapter-supabase-crm/vitest.config.ts +8 -0
- package/src/integration-adapter-supabase-crm/worker.ts +223 -0
- package/src/observability/conventions.ts +93 -0
- package/src/observability/env-resolver.ts +57 -0
- package/src/observability/index.ts +57 -0
- package/src/observability/metric-registry.ts +184 -0
- package/src/observability/otlp-converter.ts +125 -0
- package/src/observability/otlp-json.ts +135 -0
- package/src/observability/otlp-transport.ts +68 -0
- package/src/observability/pusher.ts +142 -0
- package/src/observability/redact.ts +27 -0
- package/src/observability/tests/conventions.test.ts +100 -0
- package/src/observability/tests/metric-registry.test.ts +117 -0
- package/src/observability/tests/otlp-converter.pbt.test.ts +77 -0
- package/src/observability/tests/otlp-converter.test.ts +157 -0
- package/src/observability/tests/otlp-json.test.ts +120 -0
- package/src/observability/tests/pusher.test.ts +75 -0
- package/src/observability/tests/redact.pbt.test.ts +71 -0
- package/src/observability/tests/redact.test.ts +37 -0
- package/src/observability/typed-refs.ts +137 -0
- package/src/observability/vitest.config.ts +8 -0
- package/src/ontology/analytics/matomo-binding.yaml +172 -0
- package/src/ontology/analytics/matomo-fleet.registry.yaml +26 -0
- package/src/ontology/analytics/messkanon.yaml +151 -0
- package/src/ontology/archetype-registry.ts +61 -0
- package/src/ontology/archetypes/components/brand-label.yaml +19 -0
- package/src/ontology/archetypes/components/copyright.yaml +19 -0
- package/src/ontology/archetypes/components/currency-aware-price-display.yaml +19 -0
- package/src/ontology/archetypes/components/currency-selector.yaml +19 -0
- package/src/ontology/archetypes/components/footer-promo.yaml +19 -0
- package/src/ontology/archetypes/components/lang-switcher.yaml +19 -0
- package/src/ontology/archetypes/components/layout.yaml +19 -0
- package/src/ontology/archetypes/components/live-photo.yaml +20 -0
- package/src/ontology/archetypes/components/material-credit.yaml +20 -0
- package/src/ontology/archetypes/components/media.yaml +21 -0
- package/src/ontology/archetypes/components/nachweis-card.yaml +34 -0
- package/src/ontology/archetypes/components/nachweis-detail.yaml +43 -0
- package/src/ontology/archetypes/components/nachweis-list.yaml +37 -0
- package/src/ontology/archetypes/components/nachweis-verify.yaml +31 -0
- package/src/ontology/archetypes/components/not-found.yaml +22 -0
- package/src/ontology/archetypes/components/offer-capacity.yaml +21 -0
- package/src/ontology/archetypes/components/passport-header.yaml +19 -0
- package/src/ontology/archetypes/components/passport-provenance.yaml +20 -0
- package/src/ontology/archetypes/components/passport-score-grid.yaml +20 -0
- package/src/ontology/archetypes/components/passport-star-map.yaml +20 -0
- package/src/ontology/archetypes/components/person-profile.yaml +20 -0
- package/src/ontology/archetypes/components/pulsar.yaml +20 -0
- package/src/ontology/archetypes/components/responsive-image.yaml +19 -0
- package/src/ontology/archetypes/components/scroll-to-top.yaml +22 -0
- package/src/ontology/archetypes/components/social-meta.yaml +20 -0
- package/src/ontology/archetypes/components/structured-data.yaml +19 -0
- package/src/ontology/archetypes/index.json +1003 -0
- package/src/ontology/archetypes/index.yaml +821 -0
- package/src/ontology/archetypes/sections/approach.yaml +23 -0
- package/src/ontology/archetypes/sections/article-list.yaml +30 -0
- package/src/ontology/archetypes/sections/audience-cards.yaml +30 -0
- package/src/ontology/archetypes/sections/breadcrumbs.yaml +21 -0
- package/src/ontology/archetypes/sections/chat-widget.yaml +37 -0
- package/src/ontology/archetypes/sections/comparison-cards.yaml +31 -0
- package/src/ontology/archetypes/sections/controlled-responsibility-block.yaml +34 -0
- package/src/ontology/archetypes/sections/credits.yaml +29 -0
- package/src/ontology/archetypes/sections/donation-card.yaml +53 -0
- package/src/ontology/archetypes/sections/donation-use.yaml +23 -0
- package/src/ontology/archetypes/sections/dynamic-status-block.yaml +35 -0
- package/src/ontology/archetypes/sections/faq-list.yaml +26 -0
- package/src/ontology/archetypes/sections/final-cta.yaml +53 -0
- package/src/ontology/archetypes/sections/hero-decision-card.yaml +65 -0
- package/src/ontology/archetypes/sections/hero.yaml +50 -0
- package/src/ontology/archetypes/sections/impact.yaml +23 -0
- package/src/ontology/archetypes/sections/markdown.yaml +43 -0
- package/src/ontology/archetypes/sections/mountain-journey.yaml +43 -0
- package/src/ontology/archetypes/sections/navigation.yaml +20 -0
- package/src/ontology/archetypes/sections/notausgang-block.yaml +41 -0
- package/src/ontology/archetypes/sections/open-source-registry.yaml +26 -0
- package/src/ontology/archetypes/sections/ownership-block.yaml +57 -0
- package/src/ontology/archetypes/sections/people.yaml +48 -0
- package/src/ontology/archetypes/sections/price-card.yaml +50 -0
- package/src/ontology/archetypes/sections/problem.yaml +23 -0
- package/src/ontology/archetypes/sections/send-message.yaml +63 -0
- package/src/ontology/archetypes/sections/service-metadata-block.yaml +40 -0
- package/src/ontology/archetypes/sections/social-proof.yaml +27 -0
- package/src/ontology/archetypes/sections/transparency.yaml +32 -0
- package/src/ontology/archetypes/sections/trust-strip.yaml +42 -0
- package/src/ontology/archetypes/sections/video-section.yaml +27 -0
- package/src/ontology/archetypes/sections/women.yaml +29 -0
- package/src/ontology/biome-token-projection.ts +229 -0
- package/src/ontology/biomes/check-concrete-blueprint.yaml +118 -0
- package/src/ontology/biomes/handwerk-material-warm.yaml +135 -0
- package/src/ontology/biomes/nonprofit-trust.yaml +123 -0
- package/src/ontology/blueprints/offer.yaml +39 -0
- package/src/ontology/blueprints/prompts/local-market-signal.md +13 -0
- package/src/ontology/blueprints/prompts/website-local-narrative.md +24 -0
- package/src/ontology/blueprints/ratgeber.yaml +62 -0
- package/src/ontology/blueprints/website-local.yaml +309 -0
- package/src/ontology/blueprints/website-service.yaml +84 -0
- package/src/ontology/capabilities/lead.submit.yaml +44 -0
- package/src/ontology/cosmic/index.ts +48 -0
- package/src/ontology/cosmic/moon-catalog.ts +173 -0
- package/src/ontology/cosmic/planet-catalog.ts +82 -0
- package/src/ontology/cosmic/star-catalog.ts +337 -0
- package/src/ontology/enums.ts +159 -0
- package/src/ontology/external-surfaces/index.ts +158 -0
- package/src/ontology/external-surfaces/jsonld-types.yaml +135 -0
- package/src/ontology/external-surfaces/sitemap-shape.yaml +6 -0
- package/src/ontology/external-surfaces/url-schema.yaml +95 -0
- package/src/ontology/growth/events/contact-email-click.yaml +21 -0
- package/src/ontology/growth/events/contact-form-submit.yaml +25 -0
- package/src/ontology/growth/events/contact-phone-click.yaml +21 -0
- package/src/ontology/growth/events/contact-route-click.yaml +21 -0
- package/src/ontology/growth/events/contact-submit.yaml +28 -0
- package/src/ontology/growth/events/contact-whatsapp-click.yaml +21 -0
- package/src/ontology/growth/events/cta-click.yaml +30 -0
- package/src/ontology/growth/events/donation-confirmed.yaml +32 -0
- package/src/ontology/growth/events/donation-form-start.yaml +21 -0
- package/src/ontology/growth/events/donation-form-submit.yaml +29 -0
- package/src/ontology/growth/events/donation-intent.yaml +29 -0
- package/src/ontology/growth/events/form-error.yaml +27 -0
- package/src/ontology/growth/events/form-start.yaml +21 -0
- package/src/ontology/growth/events/form-submit.yaml +21 -0
- package/src/ontology/growth/events/outbound-click.yaml +25 -0
- package/src/ontology/growth/events/page-view.yaml +24 -0
- package/src/ontology/growth/events/passport-view.yaml +28 -0
- package/src/ontology/growth/events/star-map-navigate.yaml +30 -0
- package/src/ontology/index.ts +165 -0
- package/src/ontology/manifest.ts +344 -0
- package/src/ontology/operations/index.ts +1 -0
- package/src/ontology/schemas/biome.ts +303 -0
- package/src/ontology/schemas/capability.ts +104 -0
- package/src/ontology/schemas/constellation.ts +114 -0
- package/src/ontology/schemas/dns-records.ts +53 -0
- package/src/ontology/schemas/index.ts +95 -0
- package/src/ontology/schemas/manifest-resolver.ts +123 -0
- package/src/ontology/schemas/page-entry.ts +213 -0
- package/src/ontology/schemas/section-archetype.ts +109 -0
- package/src/ontology/schemas/site-family.ts +98 -0
- package/src/ontology/schemas/system/growth.ts +80 -0
- package/src/ontology/schemas/system/integrations.ts +143 -0
- package/src/ontology/schemas/system/manifest.ts +492 -0
- package/src/ontology/schemas/system/page-output.ts +99 -0
- package/src/ontology/schemas/system/release.ts +58 -0
- package/src/ontology/schemas/system/text.ts +58 -0
- package/src/ontology/schemas/system.ts +53 -0
- package/src/ontology/shared-section-props/body-fragments.ts +182 -0
- package/src/ontology/shared-section-props/catalog.ts +195 -0
- package/src/ontology/shared-section-props/common.ts +178 -0
- package/src/ontology/shared-section-props/index.ts +34 -0
- package/src/ontology/shared-section-props/visual-header.ts +163 -0
- package/src/ontology/site-families/charity-donation-trust/cultural-rules.yaml +6 -0
- package/src/ontology/site-families/charity-donation-trust/family.yaml +44 -0
- package/src/ontology/site-families/charity-donation-trust/linguistic-rules.yaml +6 -0
- package/src/ontology/site-families/check-quality-operator/cultural-rules.yaml +6 -0
- package/src/ontology/site-families/check-quality-operator/family.yaml +40 -0
- package/src/ontology/site-families/check-quality-operator/linguistic-rules.yaml +6 -0
- package/src/ontology/site-families/handwerk-trust-engineering/cultural-rules.yaml +6 -0
- package/src/ontology/site-families/handwerk-trust-engineering/family.yaml +53 -0
- package/src/ontology/site-families/handwerk-trust-engineering/linguistic-rules.yaml +6 -0
- package/src/ontology/tests/catalogs.pbt.test.ts +89 -0
- package/src/ontology/tests/enums.test.ts +203 -0
- package/src/ontology/tests/sternsystem-owner.test.ts +63 -0
- package/src/ontology/vitest.config.ts +8 -0
- package/src/passport/dht-sign.test.ts +99 -0
- package/src/passport/dht-sign.ts +73 -0
- package/src/passport/identity-sign.test.ts +134 -0
- package/src/passport/identity-sign.ts +85 -0
- package/src/passport/index.ts +53 -0
- package/src/passport/key-rotate.ts +133 -0
- package/src/passport/schema.ts +198 -0
- package/src/passport/sign.ts +225 -0
- package/src/passport/tests/schema.pbt.test.ts +93 -0
- package/src/passport/tests/schema.test.ts +222 -0
- package/src/passport/vitest.config.ts +8 -0
- package/src/share/agent/api-catalog.ts +77 -0
- package/src/share/agent/capability.ts +68 -0
- package/src/share/agent/dns-aid.test.ts +74 -0
- package/src/share/agent/dns-aid.ts +53 -0
- package/src/share/agent/fleet-catalog.ts +149 -0
- package/src/share/agent/index.ts +29 -0
- package/src/share/agent/knowledge.ts +96 -0
- package/src/share/agent/manifest.ts +126 -0
- package/src/share/agent/mcp-card.ts +63 -0
- package/src/share/agent/omit-empty.ts +77 -0
- package/src/share/agent/openapi.ts +157 -0
- package/src/share/agent/proof.ts +61 -0
- package/src/share/attribution-display.ts +86 -0
- package/src/share/check-hints.ts +38 -0
- package/src/share/content/entity-id.ts +65 -0
- package/src/share/content/index.ts +16 -0
- package/src/share/content/merge.ts +86 -0
- package/src/share/content/resolve-field-path.ts +39 -0
- package/src/share/content/substitute-deep.ts +47 -0
- package/src/share/content-discipline/index.ts +14 -0
- package/src/share/content-discipline/parsers.ts +66 -0
- package/src/share/content-discipline/types.ts +118 -0
- package/src/share/content-reference.ts +333 -0
- package/src/share/counter-utils.ts +75 -0
- package/src/share/css-value-normalize.ts +48 -0
- package/src/share/dev-props-validator.ts +131 -0
- package/src/share/entitlement.ts +172 -0
- package/src/share/env.d.ts +73 -0
- package/src/share/feature-policy.ts +367 -0
- package/src/share/formula-eval.ts +364 -0
- package/src/share/fs/index.ts +96 -0
- package/src/share/i18n/localization.ts +57 -0
- package/src/share/image-provider.ts +314 -0
- package/src/share/index.ts +86 -0
- package/src/share/knowledge/claim.ts +129 -0
- package/src/share/knowledge/derivation.ts +55 -0
- package/src/share/knowledge/freshness.ts +127 -0
- package/src/share/knowledge/index.ts +23 -0
- package/src/share/knowledge/ledger.ts +222 -0
- package/src/share/knowledge/plan.ts +122 -0
- package/src/share/knowledge/source.ts +170 -0
- package/src/share/legal/translation-policy.ts +133 -0
- package/src/share/material-credits.ts +181 -0
- package/src/share/middleware/language-redirect.ts +49 -0
- package/src/share/offer-capacity.ts +149 -0
- package/src/share/onboarding-yaml/index.ts +162 -0
- package/src/share/page.ts +351 -0
- package/src/share/redirects.test.ts +62 -0
- package/src/share/redirects.ts +54 -0
- package/src/share/rfc0042-utils.ts +95 -0
- package/src/share/runtime-context.ts +70 -0
- package/src/share/schemas/claim-records.ts +69 -0
- package/src/share/schemas/claims.ts +150 -0
- package/src/share/schemas/effects.ts +212 -0
- package/src/share/schemas/features.ts +147 -0
- package/src/share/schemas/horizontal-align.ts +22 -0
- package/src/share/schemas/icon-color.ts +27 -0
- package/src/share/schemas/index.ts +43 -0
- package/src/share/schemas/live-photo.ts +53 -0
- package/src/share/schemas/material-credit.ts +292 -0
- package/src/share/schemas/media.ts +220 -0
- package/src/share/schemas/navigation.ts +74 -0
- package/src/share/schemas/page-base.ts +33 -0
- package/src/share/schemas/participant.ts +247 -0
- package/src/share/schemas/person.ts +24 -0
- package/src/share/schemas/print.ts +93 -0
- package/src/share/schemas/section-background.ts +106 -0
- package/src/share/schemas/section-body.ts +138 -0
- package/src/share/schemas/section-cards.ts +37 -0
- package/src/share/schemas/section-cta.ts +74 -0
- package/src/share/schemas/section-header.ts +77 -0
- package/src/share/schemas/section-image.ts +77 -0
- package/src/share/schemas/section-motion.ts +52 -0
- package/src/share/schemas/section-shell.ts +63 -0
- package/src/share/schemas/section-stats.ts +37 -0
- package/src/share/schemas/site-background.ts +78 -0
- package/src/share/schemas/standard-list-item.ts +38 -0
- package/src/share/scripts/external-links.ts +45 -0
- package/src/share/scripts/gsap-counter.ts +168 -0
- package/src/share/scripts/gsap-mountain-journey.ts +236 -0
- package/src/share/scripts/gsap-parallax.ts +50 -0
- package/src/share/scripts/gsap-reveal.ts +65 -0
- package/src/share/scripts/gsap-stagger.ts +54 -0
- package/src/share/scripts/index.ts +25 -0
- package/src/share/scripts/inline-number-animation.ts +120 -0
- package/src/share/scripts/lenis.ts +109 -0
- package/src/share/scripts/live-photos.ts +250 -0
- package/src/share/scripts/lordicon.ts +94 -0
- package/src/share/scripts/orchestrator.ts +138 -0
- package/src/share/scripts/scheduler.ts +53 -0
- package/src/share/scripts/video-player.ts +116 -0
- package/src/share/semantic/ai.ts +140 -0
- package/src/share/semantic/block-extraction.ts +52 -0
- package/src/share/semantic/block-extractors/index.ts +502 -0
- package/src/share/semantic/breadcrumbs.ts +120 -0
- package/src/share/semantic/build-page.ts +309 -0
- package/src/share/semantic/business-projection.ts +278 -0
- package/src/share/semantic/derived-prices-loader.ts +36 -0
- package/src/share/semantic/extract.ts +118 -0
- package/src/share/semantic/feed.ts +124 -0
- package/src/share/semantic/ids.ts +76 -0
- package/src/share/semantic/image-sitemap.ts +153 -0
- package/src/share/semantic/index.ts +36 -0
- package/src/share/semantic/jsonld/article.ts +47 -0
- package/src/share/semantic/jsonld/breadcrumb.ts +47 -0
- package/src/share/semantic/jsonld/collection-list.ts +30 -0
- package/src/share/semantic/jsonld/context.ts +46 -0
- package/src/share/semantic/jsonld/faq.ts +49 -0
- package/src/share/semantic/jsonld/initiative.ts +53 -0
- package/src/share/semantic/jsonld/organization.ts +101 -0
- package/src/share/semantic/jsonld/person.ts +71 -0
- package/src/share/semantic/jsonld/service.ts +80 -0
- package/src/share/semantic/jsonld/shared.ts +31 -0
- package/src/share/semantic/jsonld/types.ts +18 -0
- package/src/share/semantic/jsonld/webpage.ts +98 -0
- package/src/share/semantic/jsonld/website.ts +27 -0
- package/src/share/semantic/jsonld.ts +75 -0
- package/src/share/semantic/llms-policy.ts +74 -0
- package/src/share/semantic/llms.ts +394 -0
- package/src/share/semantic/markdown-hygiene.ts +68 -0
- package/src/share/semantic/markdown-twin-provenance.ts +242 -0
- package/src/share/semantic/models.ts +402 -0
- package/src/share/semantic/organization-profile.ts +160 -0
- package/src/share/semantic/output-projection.ts +131 -0
- package/src/share/semantic/page-builders/markdown-page.ts +84 -0
- package/src/share/semantic/page-markdown.ts +234 -0
- package/src/share/semantic/page-utils.ts +125 -0
- package/src/share/semantic/price-marker-resolver.ts +92 -0
- package/src/share/semantic/robots.ts +127 -0
- package/src/share/semantic/update-stamp.ts +136 -0
- package/src/share/shared-context.ts +177 -0
- package/src/share/string-utils.ts +19 -0
- package/src/share/tests/api-catalog.test.ts +97 -0
- package/src/share/tests/breadcrumbs.test.ts +126 -0
- package/src/share/tests/build-page-price-markers.test.ts +160 -0
- package/src/share/tests/capability.test.ts +107 -0
- package/src/share/tests/claim.test.ts +90 -0
- package/src/share/tests/content-ref-index.test.ts +328 -0
- package/src/share/tests/create-dev-props-validator.test.ts +38 -0
- package/src/share/tests/derivation.test.ts +39 -0
- package/src/share/tests/dev-props-validator.test.ts +88 -0
- package/src/share/tests/fleet-catalog.test.ts +134 -0
- package/src/share/tests/formula-eval.test.ts +619 -0
- package/src/share/tests/freshness.test.ts +60 -0
- package/src/share/tests/hero-section-numbering-parity.test.ts +51 -0
- package/src/share/tests/image-sitemap.test.ts +101 -0
- package/src/share/tests/jsonld-service.test.ts +282 -0
- package/src/share/tests/knowledge.test.ts +86 -0
- package/src/share/tests/ledger.test.ts +125 -0
- package/src/share/tests/llms-0789.test.ts +63 -0
- package/src/share/tests/manifest.test.ts +100 -0
- package/src/share/tests/markdown-twin-provenance.test.ts +108 -0
- package/src/share/tests/mcp-card.test.ts +57 -0
- package/src/share/tests/onboarding-yaml.test.ts +111 -0
- package/src/share/tests/ontology-role-by-cosmic-name.test.ts +28 -0
- package/src/share/tests/openapi.test.ts +116 -0
- package/src/share/tests/organization-jsonld.test.ts +74 -0
- package/src/share/tests/output-projection.test.ts +61 -0
- package/src/share/tests/plan.test.ts +97 -0
- package/src/share/tests/price-marker-resolver.test.ts +141 -0
- package/src/share/tests/proof.test.ts +72 -0
- package/src/share/tests/resolve-field-path.test.ts +52 -0
- package/src/share/tests/source.test.ts +81 -0
- package/src/share/tests/substitute-deep.test.ts +65 -0
- package/src/share/tests/text-normalize.pbt.test.ts +143 -0
- package/src/share/tests/text-normalize.test.ts +285 -0
- package/src/share/tests/twin-path.test.ts +43 -0
- package/src/share/text-normalize.ts +548 -0
- package/src/share/text-position.ts +19 -0
- package/src/share/types/hls-js-light.d.ts +16 -0
- package/src/share/visibility.ts +167 -0
- package/src/share/vitest.config.ts +8 -0
- package/src/share/wrap-inline-numbers.ts +139 -0
- package/src/surface/blueprint-schema.ts +314 -0
- package/src/surface/blueprint-types.ts +256 -0
- package/src/surface/blueprint.ts +300 -0
- package/src/surface/breaker.ts +57 -0
- package/src/surface/decision-composer.ts +156 -0
- package/src/surface/eligibility.ts +372 -0
- package/src/surface/evidence-records.ts +97 -0
- package/src/surface/fleet.ts +60 -0
- package/src/surface/geo.ts +170 -0
- package/src/surface/governance.ts +146 -0
- package/src/surface/index.ts +216 -0
- package/src/surface/io/index.ts +18 -0
- package/src/surface/io/surface-module-context-io.ts +60 -0
- package/src/surface/io/visibility-outcomes-io.ts +46 -0
- package/src/surface/module-context.ts +120 -0
- package/src/surface/substance.ts +161 -0
- package/src/surface/tests/eligibility.pbt.test.ts +137 -0
- package/src/surface/tests/eligibility.test.ts +302 -0
- package/src/surface/types.ts +289 -0
- package/src/surface/visibility.ts +57 -0
- package/src/surface/vitest.config.ts +8 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
[RFC-0202] Canonical LivePhoto config — the content contract that turns a static authored
|
|
5
|
+
image into a "living photo": a looping, muted, decorative <video> overlaid on the existing
|
|
6
|
+
<ResponsiveImage> (RFC-0152), which always remains the resting state / poster / fallback.
|
|
7
|
+
This schema configures WHETHER a photo is live and HOW it plays. It deliberately names NO
|
|
8
|
+
file: the clip is the sibling `<image-name>.webm`, resolved by convention (resolveVideo).
|
|
9
|
+
</purpose>
|
|
10
|
+
<non-goals>
|
|
11
|
+
<item>Do not carry a video/poster path — the clip is derived from the image token.</item>
|
|
12
|
+
<item>Do not implement playback or resolution — that is @warpgogol/werkstatt-site/ui + resolveVideo.</item>
|
|
13
|
+
</non-goals>
|
|
14
|
+
</MODULE_CONTRACT>
|
|
15
|
+
<CHANGE_SUMMARY>
|
|
16
|
+
<item>RFC-0202: introduced the LivePhoto content contract.</item>
|
|
17
|
+
</CHANGE_SUMMARY>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
/** How the loop starts. */
|
|
23
|
+
export const livePhotoTriggerSchema = z.enum(["in-viewport", "tap", "autoplay"]);
|
|
24
|
+
export type LivePhotoTrigger = z.infer<typeof livePhotoTriggerSchema>;
|
|
25
|
+
|
|
26
|
+
export const livePhotoSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
/** Turn the image into a living photo. Default true (presence implies intent). */
|
|
29
|
+
enabled: z.boolean().default(true),
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* When the loop starts:
|
|
33
|
+
* - "in-viewport" (default): plays while scrolled into view, pauses when out (IntersectionObserver).
|
|
34
|
+
* - "tap": stays a static photo until the visitor activates the play control.
|
|
35
|
+
* - "autoplay": plays immediately on load via native attributes — no JavaScript.
|
|
36
|
+
*/
|
|
37
|
+
trigger: livePhotoTriggerSchema.optional(),
|
|
38
|
+
|
|
39
|
+
/** Loop the clip continuously. Default true. false = play once, then rest on the last frame. */
|
|
40
|
+
loop: z.boolean().optional(),
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* What a tap does once the clip is running:
|
|
44
|
+
* - "toggle" (default): tap pauses a playing clip / resumes a paused one.
|
|
45
|
+
* - "play-only": tap can start the clip but never pauses it (the control only kick-starts).
|
|
46
|
+
*/
|
|
47
|
+
tapBehavior: z.enum(["toggle", "play-only"]).optional(),
|
|
48
|
+
|
|
49
|
+
/** Media preload hint. Default "metadata" (spec-recommended baseline). */
|
|
50
|
+
preload: z.enum(["none", "metadata", "auto"]).optional(),
|
|
51
|
+
})
|
|
52
|
+
.strict();
|
|
53
|
+
export type LivePhoto = z.infer<typeof livePhotoSchema>;
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
RFC-0220 Material Credits contract. Defines the closed, app-agnostic schema for
|
|
5
|
+
credit/provenance sidecars that describe published materials without conflating
|
|
6
|
+
human authorship with AI tooling.
|
|
7
|
+
</purpose>
|
|
8
|
+
<non-goals>
|
|
9
|
+
<item>Do not discover sidecar files; validators and UI loaders own their input maps.</item>
|
|
10
|
+
<item>Do not own language catalogs, localized copy, or default rights notices.</item>
|
|
11
|
+
</non-goals>
|
|
12
|
+
</MODULE_CONTRACT>
|
|
13
|
+
<CHANGE_SUMMARY>
|
|
14
|
+
<item>RFC-0220: initial Material Credits schema and formatting helpers.</item>
|
|
15
|
+
<item>RFC-0223: acquireLicensePage, copyrightYear, CreditParty.version/generatedAt.</item>
|
|
16
|
+
<item>RFC-0228: target.intent "editorial"|"decorative" optional marker.</item>
|
|
17
|
+
<item>RFC-0231: optional per-asset `display` visibility override.</item>
|
|
18
|
+
<item>RFC-0488: status, usageBasis, aiUsage fields; new sourceType/role values; label mapping helpers.</item>
|
|
19
|
+
</CHANGE_SUMMARY>
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
|
|
24
|
+
export const materialKindSchema = z.enum([
|
|
25
|
+
"image",
|
|
26
|
+
"video",
|
|
27
|
+
"audio",
|
|
28
|
+
"document",
|
|
29
|
+
"article",
|
|
30
|
+
"prose",
|
|
31
|
+
"external",
|
|
32
|
+
]);
|
|
33
|
+
export type MaterialKind = z.infer<typeof materialKindSchema>;
|
|
34
|
+
|
|
35
|
+
export const materialSourceTypeSchema = z.enum([
|
|
36
|
+
"human-made",
|
|
37
|
+
"ai-assisted",
|
|
38
|
+
"ai-generated",
|
|
39
|
+
"composite",
|
|
40
|
+
"third-party",
|
|
41
|
+
"commissioned",
|
|
42
|
+
"licensed-third-party",
|
|
43
|
+
"customer-supplied",
|
|
44
|
+
"public-domain",
|
|
45
|
+
"screenshot",
|
|
46
|
+
]);
|
|
47
|
+
export type MaterialSourceType = z.infer<typeof materialSourceTypeSchema>;
|
|
48
|
+
|
|
49
|
+
export const creditPartyKindSchema = z.enum([
|
|
50
|
+
"Person",
|
|
51
|
+
"Organization",
|
|
52
|
+
"AIAgent",
|
|
53
|
+
"AIModel",
|
|
54
|
+
"AIPlatform",
|
|
55
|
+
"Workflow",
|
|
56
|
+
"SourceMaterial",
|
|
57
|
+
]);
|
|
58
|
+
export type CreditPartyKind = z.infer<typeof creditPartyKindSchema>;
|
|
59
|
+
|
|
60
|
+
export const creditRoleSchema = z.enum([
|
|
61
|
+
"creator",
|
|
62
|
+
"coCreator",
|
|
63
|
+
"commissionedBy",
|
|
64
|
+
"producer",
|
|
65
|
+
"promptAuthor",
|
|
66
|
+
"workflowAuthor",
|
|
67
|
+
"aiAgent",
|
|
68
|
+
"aiModel",
|
|
69
|
+
"aiPlatform",
|
|
70
|
+
"sourceMaterial",
|
|
71
|
+
"reviewer",
|
|
72
|
+
"approver",
|
|
73
|
+
"rightsHolder",
|
|
74
|
+
"editor",
|
|
75
|
+
"contributor",
|
|
76
|
+
"photographer",
|
|
77
|
+
"illustrator",
|
|
78
|
+
]);
|
|
79
|
+
export type CreditRole = z.infer<typeof creditRoleSchema>;
|
|
80
|
+
|
|
81
|
+
export const materialTargetSchema = z
|
|
82
|
+
.object({
|
|
83
|
+
kind: materialKindSchema,
|
|
84
|
+
id: z.string().min(1),
|
|
85
|
+
domain: z.enum(["pages", "prose", "business", "people", "site", "surface"]).optional(),
|
|
86
|
+
lang: z.string().min(2).optional(),
|
|
87
|
+
locator: z.string().min(1).optional(),
|
|
88
|
+
/** RFC-0228: editorial requires a credit; decorative is excluded from the required-credit gate. */
|
|
89
|
+
intent: z.enum(["editorial", "decorative"]).optional(),
|
|
90
|
+
})
|
|
91
|
+
.strict();
|
|
92
|
+
export type MaterialTarget = z.infer<typeof materialTargetSchema>;
|
|
93
|
+
export type MaterialTargetDomain = NonNullable<MaterialTarget["domain"]>;
|
|
94
|
+
|
|
95
|
+
export const creditPartySchema = z
|
|
96
|
+
.object({
|
|
97
|
+
role: creditRoleSchema,
|
|
98
|
+
name: z.string().min(1),
|
|
99
|
+
kind: creditPartyKindSchema,
|
|
100
|
+
url: z.string().url().optional(),
|
|
101
|
+
/** RFC-0223: model/platform/tool version, meaningful for AI and source parties. */
|
|
102
|
+
version: z.string().min(1).optional(),
|
|
103
|
+
/** RFC-0223: ISO date the AI/source material was generated. */
|
|
104
|
+
generatedAt: z.string().min(1).optional(),
|
|
105
|
+
note: z.string().min(1).optional(),
|
|
106
|
+
})
|
|
107
|
+
.strict();
|
|
108
|
+
export type CreditParty = z.infer<typeof creditPartySchema>;
|
|
109
|
+
|
|
110
|
+
export const materialLicenseSchema = z
|
|
111
|
+
.object({
|
|
112
|
+
label: z.string().min(1),
|
|
113
|
+
url: z.string().url().optional(),
|
|
114
|
+
/** RFC-0223: page where a license can be obtained (Google licensable images). */
|
|
115
|
+
acquireLicensePage: z.string().url().optional(),
|
|
116
|
+
copyrightNotice: z.string().min(1).optional(),
|
|
117
|
+
/** RFC-0223: structured copyright year. */
|
|
118
|
+
copyrightYear: z.number().int().optional(),
|
|
119
|
+
rightsStatement: z.string().min(1).optional(),
|
|
120
|
+
})
|
|
121
|
+
.strict();
|
|
122
|
+
export type MaterialLicense = z.infer<typeof materialLicenseSchema>;
|
|
123
|
+
|
|
124
|
+
export const materialCreditStatusSchema = z.enum([
|
|
125
|
+
"active",
|
|
126
|
+
"orphaned",
|
|
127
|
+
"needs-review",
|
|
128
|
+
"blocked",
|
|
129
|
+
"expired",
|
|
130
|
+
]);
|
|
131
|
+
export type MaterialCreditStatus = z.infer<typeof materialCreditStatusSchema>;
|
|
132
|
+
|
|
133
|
+
export const usageBasisTypeSchema = z.enum([
|
|
134
|
+
"internal-commissioned",
|
|
135
|
+
"express-permission",
|
|
136
|
+
"license",
|
|
137
|
+
"customer-supplied",
|
|
138
|
+
"public-domain",
|
|
139
|
+
"statutory-exception",
|
|
140
|
+
"quotation-right",
|
|
141
|
+
"unverified",
|
|
142
|
+
]);
|
|
143
|
+
export type UsageBasisType = z.infer<typeof usageBasisTypeSchema>;
|
|
144
|
+
|
|
145
|
+
export const usageBasisSchema = z
|
|
146
|
+
.object({
|
|
147
|
+
type: usageBasisTypeSchema,
|
|
148
|
+
/** Internal evidence reference (contract, license receipt, permission email). Opaque string — never shown to visitors, not validated for existence. */
|
|
149
|
+
evidenceRef: z.string().min(1).optional(),
|
|
150
|
+
/** Human-readable note about the basis, shown in the details section. */
|
|
151
|
+
note: z.string().min(1).optional(),
|
|
152
|
+
})
|
|
153
|
+
.strict();
|
|
154
|
+
export type UsageBasis = z.infer<typeof usageBasisSchema>;
|
|
155
|
+
|
|
156
|
+
export const aiUsageSchema = z
|
|
157
|
+
.object({
|
|
158
|
+
/** Whether the output is fully AI-generated or AI-assisted. */
|
|
159
|
+
kind: z.enum(["ai-generated", "ai-assisted"]),
|
|
160
|
+
/** Human creative contribution description. */
|
|
161
|
+
humanContribution: z.string().min(1),
|
|
162
|
+
/** Whether copyright is claimed for the output. */
|
|
163
|
+
copyrightClaimed: z.boolean(),
|
|
164
|
+
/** Internal generation record reference. Opaque string — never shown to visitors, not validated for existence. */
|
|
165
|
+
generationRecordRef: z.string().min(1).optional(),
|
|
166
|
+
})
|
|
167
|
+
.strict();
|
|
168
|
+
export type AiUsage = z.infer<typeof aiUsageSchema>;
|
|
169
|
+
|
|
170
|
+
export const materialCreditSchema = z
|
|
171
|
+
.object({
|
|
172
|
+
id: z.string().min(1),
|
|
173
|
+
target: materialTargetSchema,
|
|
174
|
+
sourceType: materialSourceTypeSchema,
|
|
175
|
+
/** RFC-0231: per-asset visibility override (level 1). Absent ⇒ policy decides. */
|
|
176
|
+
display: z.enum(["shown", "hidden"]).optional(),
|
|
177
|
+
/** RFC-0488: lifecycle status of the credit record. */
|
|
178
|
+
status: materialCreditStatusSchema.optional(),
|
|
179
|
+
/** RFC-0488: legal basis for using the material, distinct from the license label. */
|
|
180
|
+
usageBasis: usageBasisSchema.optional(),
|
|
181
|
+
/** RFC-0488: AI-specific provenance, required when sourceType is ai-generated or ai-assisted. */
|
|
182
|
+
aiUsage: aiUsageSchema.optional(),
|
|
183
|
+
title: z.string().min(1).optional(),
|
|
184
|
+
creditLine: z.string().min(1).optional(),
|
|
185
|
+
parties: z.array(creditPartySchema).min(1),
|
|
186
|
+
license: materialLicenseSchema,
|
|
187
|
+
createdAt: z.string().min(1).optional(),
|
|
188
|
+
reviewedAt: z.string().min(1).optional(),
|
|
189
|
+
reviewNote: z.string().min(1).optional(),
|
|
190
|
+
c2paManifestUrl: z.string().url().optional(),
|
|
191
|
+
iptcMetadataStatus: z.enum(["preserved", "stripped", "not-applicable", "unknown"]).optional(),
|
|
192
|
+
})
|
|
193
|
+
.strict();
|
|
194
|
+
export type MaterialCredit = z.infer<typeof materialCreditSchema>;
|
|
195
|
+
|
|
196
|
+
export const materialCreditLabelsSchema = z
|
|
197
|
+
.object({
|
|
198
|
+
summaryLabel: z.string().min(1),
|
|
199
|
+
pageTitle: z.string().min(1),
|
|
200
|
+
pageDescription: z.string().min(1),
|
|
201
|
+
emptyMessage: z.string().min(1),
|
|
202
|
+
detailsLabel: z.string().min(1),
|
|
203
|
+
createdBy: z.string().min(1),
|
|
204
|
+
createdWith: z.string().min(1),
|
|
205
|
+
promptBy: z.string().min(1),
|
|
206
|
+
reviewedBy: z.string().min(1),
|
|
207
|
+
license: z.string().min(1),
|
|
208
|
+
copyrightLabel: z.string().min(1),
|
|
209
|
+
rightsHolder: z.string().min(1),
|
|
210
|
+
sourceType: z.string().min(1),
|
|
211
|
+
/** RFC-0488: per-sourceType localized labels. */
|
|
212
|
+
sourceTypeLabels: z.record(materialSourceTypeSchema, z.string().min(1)),
|
|
213
|
+
/** RFC-0488: per-status localized labels. */
|
|
214
|
+
statusLabels: z.record(materialCreditStatusSchema, z.string().min(1)),
|
|
215
|
+
/** RFC-0488: per-usageBasis.type localized labels. */
|
|
216
|
+
usageBasisLabels: z.record(usageBasisTypeSchema, z.string().min(1)),
|
|
217
|
+
/** RFC-0488: AI-specific localized labels. */
|
|
218
|
+
aiUsageLabels: z.object({
|
|
219
|
+
aiGenerated: z.string().min(1),
|
|
220
|
+
aiAssisted: z.string().min(1),
|
|
221
|
+
humanContribution: z.string().min(1),
|
|
222
|
+
copyrightClaimed: z.string().min(1),
|
|
223
|
+
copyrightNotClaimed: z.string().min(1),
|
|
224
|
+
}),
|
|
225
|
+
/** RFC-0488: "Used on" label for usage locations list. */
|
|
226
|
+
usedOnLabel: z.string().min(1),
|
|
227
|
+
/** RFC-0488: "Verified at" label. */
|
|
228
|
+
verifiedAtLabel: z.string().min(1),
|
|
229
|
+
/** RFC-0488: placeholder text when no preview is available. */
|
|
230
|
+
noPreviewLabel: z.string().min(1),
|
|
231
|
+
/** RFC-0488: explanatory text rendered once at the bottom of the credits page. */
|
|
232
|
+
copyrightExplanation: z.string().min(1),
|
|
233
|
+
})
|
|
234
|
+
.strict();
|
|
235
|
+
export type MaterialCreditLabels = z.infer<typeof materialCreditLabelsSchema>;
|
|
236
|
+
|
|
237
|
+
function namesFor(credit: MaterialCredit, roles: CreditRole[]): string[] {
|
|
238
|
+
return credit.parties.filter((party) => roles.includes(party.role)).map((party) => party.name);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function labelForMaterialCreditRole(role: CreditRole, labels: MaterialCreditLabels): string {
|
|
242
|
+
if (role === "creator" || role === "coCreator") return labels.createdBy;
|
|
243
|
+
if (role === "aiAgent" || role === "aiModel" || role === "aiPlatform") return labels.createdWith;
|
|
244
|
+
if (role === "promptAuthor" || role === "workflowAuthor") return labels.promptBy;
|
|
245
|
+
if (role === "reviewer" || role === "approver") return labels.reviewedBy;
|
|
246
|
+
if (role === "rightsHolder") return labels.rightsHolder;
|
|
247
|
+
return role;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** RFC-0488: map a sourceType enum value to a localized human-readable label. */
|
|
251
|
+
export function labelForSourceType(
|
|
252
|
+
sourceType: MaterialSourceType,
|
|
253
|
+
labels: MaterialCreditLabels,
|
|
254
|
+
): string {
|
|
255
|
+
return labels.sourceTypeLabels[sourceType] ?? sourceType;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** RFC-0488: map a status enum value to a localized human-readable label. */
|
|
259
|
+
export function labelForStatus(status: MaterialCreditStatus, labels: MaterialCreditLabels): string {
|
|
260
|
+
return labels.statusLabels[status] ?? status;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** RFC-0488: map a usageBasis.type enum value to a localized human-readable label. */
|
|
264
|
+
export function labelForUsageBasis(type: UsageBasisType, labels: MaterialCreditLabels): string {
|
|
265
|
+
return labels.usageBasisLabels[type] ?? type;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function formatMaterialCreditLine(
|
|
269
|
+
credit: MaterialCredit,
|
|
270
|
+
labels: MaterialCreditLabels,
|
|
271
|
+
): string {
|
|
272
|
+
if (credit.creditLine) return credit.creditLine;
|
|
273
|
+
|
|
274
|
+
const parts: string[] = [];
|
|
275
|
+
const creators = namesFor(credit, ["creator", "coCreator"]);
|
|
276
|
+
const aiTools = namesFor(credit, ["aiAgent", "aiModel", "aiPlatform"]);
|
|
277
|
+
const promptAuthors = namesFor(credit, ["promptAuthor", "workflowAuthor"]);
|
|
278
|
+
const reviewers = namesFor(credit, ["reviewer", "approver"]);
|
|
279
|
+
|
|
280
|
+
if (creators.length > 0) parts.push(`${labels.createdBy}: ${creators.join(", ")}`);
|
|
281
|
+
if (aiTools.length > 0) parts.push(`${labels.createdWith}: ${aiTools.join(", ")}`);
|
|
282
|
+
if (promptAuthors.length > 0) parts.push(`${labels.promptBy}: ${promptAuthors.join(", ")}`);
|
|
283
|
+
if (reviewers.length > 0) parts.push(`${labels.reviewedBy}: ${reviewers.join(", ")}`);
|
|
284
|
+
parts.push(`${labels.license}: ${credit.license.label}`);
|
|
285
|
+
parts.push(`${labels.sourceType}: ${labelForSourceType(credit.sourceType, labels)}`);
|
|
286
|
+
|
|
287
|
+
return parts.join(" · ");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function materialTargetKey(target: MaterialTarget, lang?: string): string {
|
|
291
|
+
return [target.kind, target.domain ?? "", lang ?? target.lang ?? "", target.id].join(":");
|
|
292
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
[RFC-0210] Canonical media playback contract — the single, source-agnostic config that drives
|
|
5
|
+
every <video> on the platform through ONE primitive (<Media>) and ONE delivery policy
|
|
6
|
+
(HLS-first → progressive MP4 → optional WebM; native-only for decorative loops). The contract
|
|
7
|
+
is "not a file format" but a profile-driven policy: `feature` (content video, controls, HLS
|
|
8
|
+
ABR ladder, captions), `background` (muted loop bg), and `ambient` (living photo, RFC-0202,
|
|
9
|
+
folded in). It also defines the GENERATED video manifest shape the build-time generator emits
|
|
10
|
+
and <Media> reads synchronously (the video analogue of RFC-0204's image-variant manifest).
|
|
11
|
+
</purpose>
|
|
12
|
+
<non-goals>
|
|
13
|
+
<item>Do not resolve files — that is @warpgogol/werkstatt-site/content-source (resolveMedia) + the generated manifest.</item>
|
|
14
|
+
<item>Do not implement playback or transcoding — that is @warpgogol/werkstatt-site/ui + the kernel command.</item>
|
|
15
|
+
</non-goals>
|
|
16
|
+
</MODULE_CONTRACT>
|
|
17
|
+
<CHANGE_SUMMARY>
|
|
18
|
+
<item>RFC-0210: introduced the unified media playback contract + video manifest types.</item>
|
|
19
|
+
<item>RFC-0525: added av1 field to VideoManifestSources for AV1 progressive delivery.</item>
|
|
20
|
+
<item>RFC-0525: added opt-in av1 boolean to mediaSchema (default false) to skip slow AV1 encoding.</item>
|
|
21
|
+
<item>RFC-0591: added maxSizeMb field to mediaSchema for two-pass bitrate-capped MP4 encoding.</item>
|
|
22
|
+
</CHANGE_SUMMARY>
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
import type { LivePhoto } from "./live-photo.ts";
|
|
27
|
+
|
|
28
|
+
/** The three delivery profiles. Profile drives generated formats, transport, player JS, and a11y. */
|
|
29
|
+
export const mediaProfileSchema = z.enum(["feature", "background", "ambient"]);
|
|
30
|
+
export type MediaProfile = z.infer<typeof mediaProfileSchema>;
|
|
31
|
+
|
|
32
|
+
/** When the loop starts (ambient/background) — mirrors RFC-0202 livePhotoTriggerSchema. */
|
|
33
|
+
export const mediaTriggerSchema = z.enum(["in-viewport", "tap", "autoplay"]);
|
|
34
|
+
export type MediaTrigger = z.infer<typeof mediaTriggerSchema>;
|
|
35
|
+
|
|
36
|
+
/** Source addressing. Exactly one of `name` (explicit token) or `fromImage` (ambient convention). */
|
|
37
|
+
export const mediaSourceSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
/** Bare media token (RFC-0053 style). Resolved like resolveImage: <lang>/assets/<name>.<ext>. */
|
|
40
|
+
name: z.string().min(1).optional(),
|
|
41
|
+
/** Ambient only: derive the clip from a sibling image token (RFC-0202 convention). */
|
|
42
|
+
fromImage: z.string().min(1).optional(),
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
export type MediaSource = z.infer<typeof mediaSourceSchema>;
|
|
46
|
+
|
|
47
|
+
/** One caption/subtitle track (WCAG 1.2.2). VTT resolved by convention (<token>.<lang>.vtt) or named. */
|
|
48
|
+
export const mediaCaptionSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
lang: z.string().min(2),
|
|
51
|
+
label: z.string().optional(),
|
|
52
|
+
/** Default true: this track is shown by default for its language. */
|
|
53
|
+
default: z.boolean().optional(),
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
export type MediaCaption = z.infer<typeof mediaCaptionSchema>;
|
|
57
|
+
|
|
58
|
+
export const mediaSchema = z
|
|
59
|
+
.object({
|
|
60
|
+
profile: mediaProfileSchema.default("feature"),
|
|
61
|
+
source: mediaSourceSchema,
|
|
62
|
+
|
|
63
|
+
/** Poster image token. Defaults: sibling <token> raster, else generated first-frame poster. */
|
|
64
|
+
poster: z.string().optional(),
|
|
65
|
+
/**
|
|
66
|
+
* Seconds offset for the auto-generated first-frame poster (ffmpeg `-ss`). Default 1.
|
|
67
|
+
* Use to skip an intro/title card and capture a representative, fully-rendered frame.
|
|
68
|
+
*/
|
|
69
|
+
posterTime: z.number().nonnegative().optional(),
|
|
70
|
+
/** Required by the validator for feature; ambient/background <video> stays aria-hidden. */
|
|
71
|
+
alt: z.string().optional(),
|
|
72
|
+
|
|
73
|
+
/** feature: ABR ladder depth. "auto" picks renditions by source height; [] = single rendition. */
|
|
74
|
+
ladder: z.union([z.literal("auto"), z.array(z.number().int().positive())]).optional(),
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* feature: opt-in AV1 progressive delivery via libsvtav1 (RFC-0525). AV1 is the slowest encoder
|
|
78
|
+
* in the pipeline; default false keeps build times fast. Set to true only for high-traffic
|
|
79
|
+
* feature videos where AV1 bandwidth savings justify the encoding cost.
|
|
80
|
+
*/
|
|
81
|
+
av1: z.boolean().optional(),
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Maximum size of the progressive MP4 file in MiB. When set, MP4 encoding switches
|
|
85
|
+
* to two-pass with a target bitrate calculated from the source duration. Default: 24
|
|
86
|
+
* (1 MiB safety margin under Cloudflare's 25 MiB per-asset limit). Set to 0 to disable
|
|
87
|
+
* two-pass and use CRF 17 (no size guarantee — may exceed the Cloudflare limit).
|
|
88
|
+
*/
|
|
89
|
+
maxSizeMb: z.number().nonnegative().optional(),
|
|
90
|
+
|
|
91
|
+
/** Playback behavior (profile-clamped at render: ambient/background force muted+loop, never controls). */
|
|
92
|
+
autoplay: z.boolean().optional(),
|
|
93
|
+
loop: z.boolean().optional(),
|
|
94
|
+
muted: z.boolean().optional(),
|
|
95
|
+
controls: z.boolean().optional(),
|
|
96
|
+
preload: z.enum(["none", "metadata", "auto"]).optional(),
|
|
97
|
+
|
|
98
|
+
/** ambient/background only (RFC-0202): when the loop starts and what a tap does. */
|
|
99
|
+
trigger: mediaTriggerSchema.optional(),
|
|
100
|
+
tapBehavior: z.enum(["toggle", "play-only"]).optional(),
|
|
101
|
+
|
|
102
|
+
/** feature: prerecorded-speech accessibility. */
|
|
103
|
+
captions: z.array(mediaCaptionSchema).optional(),
|
|
104
|
+
/** contentRef to a prose transcript rendered under the video and exposed to llms/SEO. */
|
|
105
|
+
transcriptRef: z.string().optional(),
|
|
106
|
+
|
|
107
|
+
lang: z.string().optional(),
|
|
108
|
+
subPath: z.string().optional(),
|
|
109
|
+
})
|
|
110
|
+
.strict()
|
|
111
|
+
.superRefine((m, ctx) => {
|
|
112
|
+
const hasName = !!m.source.name;
|
|
113
|
+
const hasFromImage = !!m.source.fromImage;
|
|
114
|
+
if (hasName === hasFromImage) {
|
|
115
|
+
ctx.addIssue({
|
|
116
|
+
code: "custom",
|
|
117
|
+
message: "media.source needs exactly one of name | fromImage",
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (m.profile !== "ambient" && hasFromImage) {
|
|
121
|
+
ctx.addIssue({
|
|
122
|
+
code: "custom",
|
|
123
|
+
message: "source.fromImage is only valid for the ambient profile",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (m.profile === "feature" && !m.alt) {
|
|
127
|
+
ctx.addIssue({ code: "custom", message: "feature media requires alt text" });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
export type Media = z.infer<typeof mediaSchema>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* [RFC-0210] Project a legacy RFC-0202 LivePhoto config (paired with its host image token) onto
|
|
134
|
+
* an equivalent ambient Media config. This is the seam that lets existing `live:` authoring keep
|
|
135
|
+
* working while resolving through the single media contract — no content migration required.
|
|
136
|
+
* Returns null when the photo is not live (enabled === false).
|
|
137
|
+
*/
|
|
138
|
+
export function livePhotoToMedia(live: LivePhoto | undefined, imageName: string): Media | null {
|
|
139
|
+
if (!live || live.enabled === false) return null;
|
|
140
|
+
return {
|
|
141
|
+
profile: "ambient",
|
|
142
|
+
source: { fromImage: imageName },
|
|
143
|
+
trigger: live.trigger,
|
|
144
|
+
loop: live.loop,
|
|
145
|
+
tapBehavior: live.tapBehavior,
|
|
146
|
+
preload: live.preload,
|
|
147
|
+
} as Media;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ─── GENERATED video manifest (RFC-0210 — written by video.variants.generate) ────────────────
|
|
151
|
+
|
|
152
|
+
/** One playable caption track in the generated manifest. */
|
|
153
|
+
export interface VideoCaptionTrack {
|
|
154
|
+
lang: string;
|
|
155
|
+
label?: string;
|
|
156
|
+
default?: boolean;
|
|
157
|
+
url: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Derived-format URLs for one source video, keyed by container/transport. */
|
|
161
|
+
export interface VideoManifestSources {
|
|
162
|
+
/** HLS master playlist (feature only). */
|
|
163
|
+
hls?: string;
|
|
164
|
+
/** Progressive H.264/AAC MP4 — the universal fallback. */
|
|
165
|
+
mp4?: string;
|
|
166
|
+
/** Optional VP9 WebM enhancement. */
|
|
167
|
+
webm?: string;
|
|
168
|
+
/** AV1-in-WebM progressive (RFC-0525). Feature only. */
|
|
169
|
+
av1?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface VideoManifestEntry {
|
|
173
|
+
/** Content-relative source path key, e.g. /src/content/pages/uk/assets/promo.mp4. */
|
|
174
|
+
origin: string;
|
|
175
|
+
profile: MediaProfile;
|
|
176
|
+
/** Poster URL (authored sibling raster or generated first-frame). */
|
|
177
|
+
poster?: string;
|
|
178
|
+
intrinsicWidth?: number;
|
|
179
|
+
intrinsicHeight?: number;
|
|
180
|
+
durationSec?: number;
|
|
181
|
+
hasAudio?: boolean;
|
|
182
|
+
sources: VideoManifestSources;
|
|
183
|
+
captions?: VideoCaptionTrack[];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface VideoManifest {
|
|
187
|
+
version: 1;
|
|
188
|
+
/** Primary lookup: content-relative source path → entry. */
|
|
189
|
+
byOrigin: Record<string, VideoManifestEntry>;
|
|
190
|
+
/** Secondary lookup: `<lang>/<token>` → byOrigin key (render-side token resolution). */
|
|
191
|
+
byToken: Record<string, string>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* [RFC-0234] Derived delivery set for one living-photo / ambient clip. The author ships exactly one
|
|
196
|
+
* source file (`<token>.webm` OR `<token>.mp4`, never both); the build derives the cross-device set
|
|
197
|
+
* so the clip plays everywhere. `webm` is the desktop/Android source (alpha preserved); `mp4` is the
|
|
198
|
+
* iOS-playable H.264 fallback. Since H.264 cannot carry alpha, a TRANSPARENT source is flattened
|
|
199
|
+
* over the site background colour (`mp4Bg`) so the iOS clip blends into the page instead of showing
|
|
200
|
+
* a dirty box; an opaque source is transcoded/copied as-is. `alpha` records WebM transparency so the
|
|
201
|
+
* desktop render stays transparent.
|
|
202
|
+
*/
|
|
203
|
+
export interface LiveVideoManifestEntry {
|
|
204
|
+
/** Content-relative source path key, e.g. /src/content/people/de/assets/maria-calderon.webm. */
|
|
205
|
+
origin: string;
|
|
206
|
+
/** Desktop/Android VP9 WebM URL (served from public/_video/live; alpha preserved). */
|
|
207
|
+
webm?: string;
|
|
208
|
+
/** iOS-playable progressive H.264 MP4 URL (transparent sources flattened over `mp4Bg`). */
|
|
209
|
+
mp4?: string;
|
|
210
|
+
/** True when the source WebM carries an alpha channel (transparent subject). */
|
|
211
|
+
alpha: boolean;
|
|
212
|
+
/** ffmpeg `0xRRGGBB` background colour the alpha source was flattened over for the iOS MP4. */
|
|
213
|
+
mp4Bg?: string;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface LiveVideoManifest {
|
|
217
|
+
version: 1;
|
|
218
|
+
/** Lookup: `<lang>/<token>` → derived entry. */
|
|
219
|
+
byToken: Record<string, LiveVideoManifestEntry>;
|
|
220
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Maintains packages/share/src/schemas/navigation.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not handle runtime resolution or feature graph integration.</item>
|
|
6
|
+
<item>Do not contain business logic.</item>
|
|
7
|
+
<item>Do not handle backward compatibility (RFC-0046: no backward compatibility needed).</item>
|
|
8
|
+
</non-goals>
|
|
9
|
+
</MODULE_CONTRACT>
|
|
10
|
+
<CHANGE_SUMMARY>
|
|
11
|
+
<item>Implement RFC-0046: Move navigation schema to shared package with configurable groups.</item>
|
|
12
|
+
</CHANGE_SUMMARY>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a Zod enum for navigation groups from an array of allowed group names.
|
|
19
|
+
* Apps call this in their content.config.ts or navigation.ts with their specific groups.
|
|
20
|
+
*
|
|
21
|
+
* @param groups - Array of allowed navigation group names for the app
|
|
22
|
+
* @returns Zod enum schema for navigation groups
|
|
23
|
+
*/
|
|
24
|
+
export function createNavigationGroupEnum(groups: string[]) {
|
|
25
|
+
return z.enum(groups as [string, ...string[]]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Default groups for backward compatibility with existing apps
|
|
29
|
+
export const defaultNavigationGroups = ["navigation", "legal", "contact"] as const;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Creates a navigation target schema with configurable group validation.
|
|
33
|
+
*
|
|
34
|
+
* @param groupEnum - Zod enum schema for allowed navigation groups
|
|
35
|
+
* @returns Zod schema for a single navigation target
|
|
36
|
+
*/
|
|
37
|
+
export const navigationTargetSchema = (groupEnum: z.ZodEnum<Record<string, string>>) =>
|
|
38
|
+
z.object({
|
|
39
|
+
id: z.string(),
|
|
40
|
+
label: z.string(),
|
|
41
|
+
semanticTarget: z.union([
|
|
42
|
+
z.object({
|
|
43
|
+
kind: z.literal("internal"),
|
|
44
|
+
pageId: z.string(),
|
|
45
|
+
// RFC-0048: anchor may be a plain string (language-neutral) or a
|
|
46
|
+
// language-keyed record (e.g. { de: "unser-ansatz", en: "our-approach" })
|
|
47
|
+
anchor: z.union([z.string(), z.record(z.string(), z.string())]).optional(),
|
|
48
|
+
}),
|
|
49
|
+
z.object({
|
|
50
|
+
kind: z.literal("external"),
|
|
51
|
+
href: z.url(),
|
|
52
|
+
}),
|
|
53
|
+
]),
|
|
54
|
+
routeSlug: z.string().optional(),
|
|
55
|
+
group: groupEnum.optional(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a navigation schema with configurable group validation.
|
|
60
|
+
*
|
|
61
|
+
* @param groupEnum - Zod enum schema for allowed navigation groups
|
|
62
|
+
* @returns Zod schema for the full navigation structure
|
|
63
|
+
*/
|
|
64
|
+
export const navigationSchema = (groupEnum: z.ZodEnum<Record<string, string>>) =>
|
|
65
|
+
z.object({
|
|
66
|
+
targets: z.array(navigationTargetSchema(groupEnum)),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Type helper for navigation target.
|
|
71
|
+
* The group field is loosely typed (string) here; per-app strict validation comes
|
|
72
|
+
* from the runtime Zod schema returned by navigationTargetSchema(groupEnum).
|
|
73
|
+
*/
|
|
74
|
+
export type NavigationTarget = z.infer<ReturnType<typeof navigationTargetSchema>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
Shared base Zod fields consumed by all page schemas across apps/*. Extracted to packages/share
|
|
5
|
+
so every app re-uses the same componentOverrides contract without copy-pasting.
|
|
6
|
+
Depends only on the zod peer dependency; no Astro or app-specific imports.
|
|
7
|
+
</purpose>
|
|
8
|
+
<non-goals>
|
|
9
|
+
<item>Do not define page-specific or app-specific fields here.</item>
|
|
10
|
+
<item>Do not import from apps/* or astro:content.</item>
|
|
11
|
+
<item>Do not perform runtime data fetching or API interactions.</item>
|
|
12
|
+
</non-goals>
|
|
13
|
+
</MODULE_CONTRACT>
|
|
14
|
+
<CHANGE_SUMMARY>
|
|
15
|
+
<item>Initial extraction from app content schemas/pages/base into packages/share.</item>
|
|
16
|
+
<item>Switched import from "astro/zod" to "zod" so the package is Astro-agnostic.</item>
|
|
17
|
+
</CHANGE_SUMMARY>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// [RFC-0004] Shared base fields for all page schemas.
|
|
21
|
+
// Extracted to packages/share to prevent copy-paste across apps.
|
|
22
|
+
// Import z from "zod" — not "astro/zod" — so this package stays framework-neutral.
|
|
23
|
+
|
|
24
|
+
import { z } from "zod";
|
|
25
|
+
|
|
26
|
+
// componentOverrides: a map of component paths to partial content objects.
|
|
27
|
+
// Pages use this to override default component copy from frontmatter,
|
|
28
|
+
// keeping .astro route files thin and copy-free.
|
|
29
|
+
export const componentOverridesSchema = z
|
|
30
|
+
.record(z.string(), z.record(z.string(), z.unknown()))
|
|
31
|
+
.optional();
|
|
32
|
+
|
|
33
|
+
export type ComponentOverrides = z.infer<typeof componentOverridesSchema>;
|