@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,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>RFC-0142: pure resolution of per-page llms inclusion depth. Maps the raw `output.llms` declaration from system.md to an effective SemanticLlmsPolicy, applying a conservative default-by-semanticType map.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not read files or content — pure function only.</item>
|
|
6
|
+
<item>Do not format llms output — that lives in llms.ts.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>RFC-0142: Initial implementation.</item>
|
|
11
|
+
<item>RFC-0328: Added legal-page default llms depth "exclude".</item>
|
|
12
|
+
</CHANGE_SUMMARY>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { SemanticLlmsDepth, SemanticLlmsPolicy } from "./models.ts";
|
|
16
|
+
|
|
17
|
+
/** Raw per-page llms declaration as authored in system.md (`output.llms`). */
|
|
18
|
+
export type RawLlmsPolicy =
|
|
19
|
+
SemanticLlmsDepth | { depth?: SemanticLlmsDepth; sections?: { exclude?: string[] } };
|
|
20
|
+
|
|
21
|
+
const VALID_DEPTHS: ReadonlySet<string> = new Set<SemanticLlmsDepth>([
|
|
22
|
+
"full",
|
|
23
|
+
"summary",
|
|
24
|
+
"index-only",
|
|
25
|
+
"exclude",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* RFC-0142 / RFC-0328: conservative default depth by semanticType. `openSource`
|
|
30
|
+
* is unambiguously low-signal across all apps and is demoted by default. Legal
|
|
31
|
+
* pages are first-class statutory pages and are excluded from the public AI
|
|
32
|
+
* feed by default.
|
|
33
|
+
*/
|
|
34
|
+
export const LLMS_DEPTH_BY_SEMANTIC_TYPE: Readonly<Record<string, SemanticLlmsDepth>> = {
|
|
35
|
+
openSource: "index-only",
|
|
36
|
+
legal: "exclude",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const DEFAULT_DEPTH: SemanticLlmsDepth = "full";
|
|
40
|
+
|
|
41
|
+
function isValidDepth(value: unknown): value is SemanticLlmsDepth {
|
|
42
|
+
return typeof value === "string" && VALID_DEPTHS.has(value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the effective llms policy for a page.
|
|
47
|
+
*
|
|
48
|
+
* Precedence: explicit `output.llms` (string or object) > default-by-type > `full`.
|
|
49
|
+
* An unrecognized explicit depth falls back to the type default (callers may warn).
|
|
50
|
+
*/
|
|
51
|
+
export function resolveLlmsPolicy(
|
|
52
|
+
raw: RawLlmsPolicy | undefined,
|
|
53
|
+
semanticType: string,
|
|
54
|
+
): SemanticLlmsPolicy {
|
|
55
|
+
const typeDefault = LLMS_DEPTH_BY_SEMANTIC_TYPE[semanticType] ?? DEFAULT_DEPTH;
|
|
56
|
+
|
|
57
|
+
if (raw === undefined || raw === null) {
|
|
58
|
+
return { depth: typeDefault };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// String shorthand: `output.llms: index-only`
|
|
62
|
+
if (typeof raw === "string") {
|
|
63
|
+
return { depth: isValidDepth(raw) ? raw : typeDefault };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Object form: `output.llms: { depth, sections }`
|
|
67
|
+
const depth = isValidDepth(raw.depth) ? raw.depth : typeDefault;
|
|
68
|
+
const excludeIds = raw.sections?.exclude?.filter((id) => typeof id === "string" && id.length > 0);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
depth,
|
|
72
|
+
...(excludeIds && excludeIds.length > 0 ? { sections: { exclude: excludeIds } } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Maintains packages/share/src/semantic/llms.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not parse raw content or validate input models.</item>
|
|
6
|
+
<item>Do not manage external data fetching.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>Added full llms.txt generation and optional title-aware page labels.</item>
|
|
11
|
+
<item>RFC-0184: canonical Markdown links, blockquoted summary, absolute URLs, llms-full.txt reference, and empty-section filtering.</item>
|
|
12
|
+
<item>RFC-0372: formatBlocks reads from unified page.blocks instead of answerBlocks + contentBlocks.</item>
|
|
13
|
+
</CHANGE_SUMMARY>
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { toPathname } from "./ids.ts";
|
|
17
|
+
import {
|
|
18
|
+
canonicalizeGeneratedMarkdownText,
|
|
19
|
+
formatGeneratedMarkdownListItem,
|
|
20
|
+
normalizeGeneratedMarkdownText,
|
|
21
|
+
} from "./markdown-hygiene.ts";
|
|
22
|
+
import type { SemanticLlmsDepth, SemanticPageModel, SemanticSiteModel } from "./models.ts";
|
|
23
|
+
|
|
24
|
+
function canonicalStaticUrl(artifactPath: string, opts: { baseUrl: string }): string {
|
|
25
|
+
const base = opts.baseUrl.replace(/\/+$/, "");
|
|
26
|
+
const clean = artifactPath.replace(/^\/+/, "");
|
|
27
|
+
return clean === "" ? `${base}/` : `${base}/${clean}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** RFC-0142: effective llms depth for a page. Defaults to `full` when unresolved. */
|
|
31
|
+
function pageDepth(page: SemanticPageModel): SemanticLlmsDepth {
|
|
32
|
+
return page.output?.llms?.depth ?? "full";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** RFC-0142: pages that contribute a row to the llms.txt index. */
|
|
36
|
+
function inIndex(page: SemanticPageModel): boolean {
|
|
37
|
+
return pageDepth(page) !== "exclude";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** RFC-0142: pages that contribute a body section to llms-full.txt. */
|
|
41
|
+
function inFull(page: SemanticPageModel): boolean {
|
|
42
|
+
const depth = pageDepth(page);
|
|
43
|
+
return depth === "full" || depth === "summary";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function pageLabel(page: SemanticPageModel): string {
|
|
47
|
+
return page.heading ?? page.title;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function pageSummary(page: SemanticPageModel): string {
|
|
51
|
+
return canonicalizePageText(page, page.lead ?? page.description);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function canonicalizePageText(page: SemanticPageModel, text: string | undefined): string {
|
|
55
|
+
return canonicalizeGeneratedMarkdownText(text, {
|
|
56
|
+
baseUrl: page.url ? new URL(page.url).origin : undefined,
|
|
57
|
+
defaultLanguage: page.defaultLanguage,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function formatBlocks(page: SemanticPageModel): string {
|
|
62
|
+
const excludeIds = new Set(page.output?.llms?.sections?.exclude ?? []);
|
|
63
|
+
return page.blocks
|
|
64
|
+
.filter((block) => block.heading && !excludeIds.has(block.id))
|
|
65
|
+
.filter((block) => block.summary || block.body || block.facts?.length || block.items?.length)
|
|
66
|
+
.map((block) => {
|
|
67
|
+
const parts = [`### ${block.heading}`];
|
|
68
|
+
if (block.summary) {
|
|
69
|
+
parts.push(canonicalizePageText(page, block.summary));
|
|
70
|
+
}
|
|
71
|
+
if (block.body) {
|
|
72
|
+
parts.push(canonicalizePageText(page, block.body));
|
|
73
|
+
}
|
|
74
|
+
if (block.facts?.length) {
|
|
75
|
+
parts.push(
|
|
76
|
+
...block.facts.flatMap((fact) =>
|
|
77
|
+
formatGeneratedMarkdownListItem(canonicalizePageText(page, fact)),
|
|
78
|
+
),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (block.items?.length) {
|
|
82
|
+
for (const item of block.items) {
|
|
83
|
+
const desc = item.description ? `: ${item.description}` : "";
|
|
84
|
+
parts.push(`- ${item.title}${desc}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return parts.join("\n\n");
|
|
88
|
+
})
|
|
89
|
+
.join("\n\n");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatPeople(page: SemanticPageModel): string {
|
|
93
|
+
if (!page.people?.length) {
|
|
94
|
+
return "";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return [
|
|
98
|
+
"### People",
|
|
99
|
+
...page.people.map((person) => {
|
|
100
|
+
const details = [person.role, person.description].filter(Boolean).join(" — ");
|
|
101
|
+
return details ? `- ${person.name}: ${details}` : `- ${person.name}`;
|
|
102
|
+
}),
|
|
103
|
+
].join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function formatInitiatives(page: SemanticPageModel): string {
|
|
107
|
+
if (!page.initiatives?.length) {
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return [
|
|
112
|
+
"### Initiatives",
|
|
113
|
+
...page.initiatives.map((initiative) => {
|
|
114
|
+
const facts = initiative.facts?.length ? ` (${initiative.facts.join("; ")})` : "";
|
|
115
|
+
return `- ${initiative.name}: ${initiative.summary}${facts}`;
|
|
116
|
+
}),
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function formatOrganizationFacts(site: SemanticSiteModel): string[] {
|
|
121
|
+
return [
|
|
122
|
+
`- Name: ${site.organization.legalName ?? site.organization.name}`,
|
|
123
|
+
...(site.organization.foundingYear ? [`- Founded: ${site.organization.foundingYear}`] : []),
|
|
124
|
+
...(site.organization.registration
|
|
125
|
+
? [`- Registration: ${site.organization.registration}`]
|
|
126
|
+
: []),
|
|
127
|
+
...(site.organization.representative
|
|
128
|
+
? [`- Representative: ${site.organization.representative}`]
|
|
129
|
+
: []),
|
|
130
|
+
...(site.organization.email ? [`- Email: ${site.organization.email}`] : []),
|
|
131
|
+
...(site.organization.areaServed?.length
|
|
132
|
+
? [`- Areas served: ${site.organization.areaServed.join("; ")}`]
|
|
133
|
+
: []),
|
|
134
|
+
...(site.organization.address
|
|
135
|
+
? [
|
|
136
|
+
`- Address: ${[
|
|
137
|
+
site.organization.address.streetAddress,
|
|
138
|
+
site.organization.address.postalCode,
|
|
139
|
+
site.organization.address.addressLocality,
|
|
140
|
+
site.organization.address.addressCountry,
|
|
141
|
+
]
|
|
142
|
+
.filter(Boolean)
|
|
143
|
+
.join(", ")}`,
|
|
144
|
+
]
|
|
145
|
+
: []),
|
|
146
|
+
...(site.organization.founders?.length
|
|
147
|
+
? [`- Founders: ${site.organization.founders.map((person) => person.name).join("; ")}`]
|
|
148
|
+
: []),
|
|
149
|
+
...(site.organization.boardMembers?.length
|
|
150
|
+
? [
|
|
151
|
+
`- Board: ${site.organization.boardMembers
|
|
152
|
+
.map((person) => (person.role ? `${person.role} — ${person.name}` : person.name))
|
|
153
|
+
.join("; ")}`,
|
|
154
|
+
]
|
|
155
|
+
: []),
|
|
156
|
+
...(site.organization.contactPoints?.length
|
|
157
|
+
? [
|
|
158
|
+
`- Contact points: ${site.organization.contactPoints
|
|
159
|
+
.map((contactPoint) =>
|
|
160
|
+
[contactPoint.contactType, contactPoint.email].filter(Boolean).join(" — "),
|
|
161
|
+
)
|
|
162
|
+
.join("; ")}`,
|
|
163
|
+
]
|
|
164
|
+
: []),
|
|
165
|
+
...(site.organization.donationAccount
|
|
166
|
+
? (() => {
|
|
167
|
+
const { accountHolder, bankName, iban, bic } = site.organization.donationAccount;
|
|
168
|
+
const hasReal = (v: string | undefined) =>
|
|
169
|
+
typeof v === "string" && v.length > 0 && !v.startsWith("NEED_THIS_");
|
|
170
|
+
if (!hasReal(iban) || !hasReal(bic)) {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
const parts = [accountHolder, bankName, iban, bic].filter(hasReal);
|
|
174
|
+
return [`- Donation account: ${parts.join("; ")}`];
|
|
175
|
+
})()
|
|
176
|
+
: []),
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** RFC-0147: render the projected offer (prices + written guarantees + growth modules + terms). */
|
|
181
|
+
function formatOffer(site: SemanticSiteModel): string[] {
|
|
182
|
+
const offer = site.organization.offer;
|
|
183
|
+
if (
|
|
184
|
+
!offer ||
|
|
185
|
+
(!offer.prices?.length &&
|
|
186
|
+
!offer.guarantees?.length &&
|
|
187
|
+
!offer.growthModules?.length &&
|
|
188
|
+
!offer.changePrice &&
|
|
189
|
+
!offer.hourlyRate &&
|
|
190
|
+
!offer.billingDay)
|
|
191
|
+
) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const lines: string[] = ["## Offer"];
|
|
195
|
+
if (offer.prices?.length) {
|
|
196
|
+
lines.push(...offer.prices.map((price) => `- ${price.label}: ${price.amount}`));
|
|
197
|
+
}
|
|
198
|
+
if (offer.changePrice || offer.hourlyRate || offer.billingDay) {
|
|
199
|
+
const terms: string[] = [];
|
|
200
|
+
if (offer.changePrice) terms.push(`- Change price: ${offer.changePrice} EUR per change`);
|
|
201
|
+
if (offer.hourlyRate) terms.push(`- Hourly rate: ${offer.hourlyRate} EUR per hour`);
|
|
202
|
+
if (offer.billingDay) terms.push(`- Billing day: ${offer.billingDay}`);
|
|
203
|
+
lines.push("", "### Terms", ...terms);
|
|
204
|
+
}
|
|
205
|
+
if (offer.guarantees?.length) {
|
|
206
|
+
lines.push("", "### Guarantees");
|
|
207
|
+
lines.push(
|
|
208
|
+
...offer.guarantees.map((g) => (g.detail ? `- ${g.label}: ${g.detail}` : `- ${g.label}`)),
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (offer.growthModules?.length) {
|
|
212
|
+
lines.push("", "### Growth modules");
|
|
213
|
+
lines.push(
|
|
214
|
+
...offer.growthModules.map((m) => {
|
|
215
|
+
const desc = m.description
|
|
216
|
+
? `: ${normalizeGeneratedMarkdownText(m.description).replace(/\n+/g, " ")}`
|
|
217
|
+
: "";
|
|
218
|
+
const price = m.price
|
|
219
|
+
? ` (${normalizeGeneratedMarkdownText(m.price).replace(/\n+/g, " ")})`
|
|
220
|
+
: "";
|
|
221
|
+
return `- ${m.label}${desc}${price}`;
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (offer.capacity) {
|
|
226
|
+
lines.push(
|
|
227
|
+
"",
|
|
228
|
+
"### Capacity policy",
|
|
229
|
+
`- Wave cadence: ${offer.capacity.cadence}`,
|
|
230
|
+
`- Starts at: ${offer.capacity.startsAt}`,
|
|
231
|
+
`- Timezone: ${offer.capacity.timezone}`,
|
|
232
|
+
`- Slot range: ${offer.capacity.slotRange.min}-${offer.capacity.slotRange.max}`,
|
|
233
|
+
`- Max slots per wave: ${offer.capacity.maxSlotsPerWave}`,
|
|
234
|
+
`- Availability status: ${offer.capacity.availabilityStatus}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
lines.push("");
|
|
238
|
+
return lines;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** RFC-0373: render the projected business service catalog. */
|
|
242
|
+
function formatServices(site: SemanticSiteModel): string[] {
|
|
243
|
+
const services = site.organization.services;
|
|
244
|
+
if (!services?.length) return [];
|
|
245
|
+
const lines: string[] = ["## Services"];
|
|
246
|
+
for (const service of services) {
|
|
247
|
+
const desc = service.description ? `: ${service.description}` : "";
|
|
248
|
+
lines.push(`- ${service.name}${desc}`);
|
|
249
|
+
}
|
|
250
|
+
lines.push("");
|
|
251
|
+
return lines;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** RFC-0148: render the projected location (NAP + service area). */
|
|
255
|
+
function formatLocation(site: SemanticSiteModel): string[] {
|
|
256
|
+
const location = site.organization.location;
|
|
257
|
+
if (!location) return [];
|
|
258
|
+
const facts = [
|
|
259
|
+
...(location.locality ? [`- Locality: ${location.locality}`] : []),
|
|
260
|
+
...(location.region ? [`- Region: ${location.region}`] : []),
|
|
261
|
+
...(location.country ? [`- Country: ${location.country}`] : []),
|
|
262
|
+
...(location.serviceArea?.length ? [`- Service area: ${location.serviceArea.join("; ")}`] : []),
|
|
263
|
+
];
|
|
264
|
+
if (facts.length === 0) return [];
|
|
265
|
+
return ["## Location", ...facts, ""];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** RFC-0148: render the projected public team (name, role, bio). */
|
|
269
|
+
function formatTeam(site: SemanticSiteModel): string[] {
|
|
270
|
+
const team = site.organization.team;
|
|
271
|
+
if (!team?.length) return [];
|
|
272
|
+
const lines: string[] = ["## Team"];
|
|
273
|
+
for (const member of team) {
|
|
274
|
+
const role = member.role ? ` (${member.role})` : "";
|
|
275
|
+
const bio = member.description
|
|
276
|
+
? `: ${member.description.replace(/\s*\n\s*/g, " ").trim()}`
|
|
277
|
+
: "";
|
|
278
|
+
lines.push(`- ${member.name}${role}${bio}`);
|
|
279
|
+
}
|
|
280
|
+
lines.push("");
|
|
281
|
+
return lines;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function toAbsoluteUrl(site: SemanticSiteModel, pathname: string): string {
|
|
285
|
+
const rawBase = site.organization.url ?? site.baseUrl;
|
|
286
|
+
const base = (() => {
|
|
287
|
+
try {
|
|
288
|
+
return new URL(rawBase).origin;
|
|
289
|
+
} catch {
|
|
290
|
+
return rawBase.replace(/\/$/, "");
|
|
291
|
+
}
|
|
292
|
+
})();
|
|
293
|
+
const defaultLang = site.defaultLanguage ?? "de";
|
|
294
|
+
const normalizedPathname =
|
|
295
|
+
site.lang === defaultLang
|
|
296
|
+
? pathname.replace(new RegExp(`^/${defaultLang}(?=/|$)`), "") || "/"
|
|
297
|
+
: pathname;
|
|
298
|
+
// RFC-0317: ensure trailing slash to match canonicalPageUrl output.
|
|
299
|
+
const withTrailingSlash =
|
|
300
|
+
normalizedPathname === "/" ? "/" : normalizedPathname.replace(/\/?$/, "/");
|
|
301
|
+
return `${base}${withTrailingSlash}`;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** RFC-0184: Format a Markdown link row for llms.txt. */
|
|
305
|
+
function formatMarkdownLinkRow(site: SemanticSiteModel, page: SemanticPageModel): string {
|
|
306
|
+
const title = pageLabel(page);
|
|
307
|
+
const url = toAbsoluteUrl(site, toPathname(page.url));
|
|
308
|
+
const summary = pageSummary(page);
|
|
309
|
+
return `- [${title}](${url}): ${summary}`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function buildLlmsIndex(site: SemanticSiteModel): string {
|
|
313
|
+
const indexPages = site.pages.filter(inIndex);
|
|
314
|
+
const llmsFullUrl = canonicalStaticUrl("/llms-full.txt", { baseUrl: site.baseUrl });
|
|
315
|
+
const siteDescription = site.organization.description ?? "";
|
|
316
|
+
|
|
317
|
+
// RFC-0789: agent discovery links — omitted when agent.enabled is false.
|
|
318
|
+
const agentEnabled = site.agent?.enabled !== false;
|
|
319
|
+
const agentLinks = agentEnabled
|
|
320
|
+
? [
|
|
321
|
+
`> Machine-readable Agent Surface (structured knowledge + capabilities): [agent.json](${canonicalStaticUrl("/.well-known/agent.json", { baseUrl: site.baseUrl })}).`,
|
|
322
|
+
`> API discovery catalog (RFC 9727): [api-catalog](${canonicalStaticUrl("/.well-known/api-catalog", { baseUrl: site.baseUrl })}).`,
|
|
323
|
+
`> MCP Server Card (SEP-1649): [server-card.json](${canonicalStaticUrl("/.well-known/mcp/server-card.json", { baseUrl: site.baseUrl })}).`,
|
|
324
|
+
`> OpenAPI 3.1 specification: [agent.openapi.json](${canonicalStaticUrl("/.well-known/agent.openapi.json", { baseUrl: site.baseUrl })}).`,
|
|
325
|
+
]
|
|
326
|
+
: [];
|
|
327
|
+
|
|
328
|
+
// RFC-0184: canonical Markdown link rows with absolute URLs
|
|
329
|
+
const primarySources = indexPages.map((page) => formatMarkdownLinkRow(site, page));
|
|
330
|
+
|
|
331
|
+
return [
|
|
332
|
+
`# ${site.organization.name}`,
|
|
333
|
+
// RFC-0184: blockquoted site description with llms-full.txt reference
|
|
334
|
+
...(siteDescription ? [`> ${siteDescription}`] : []),
|
|
335
|
+
`> For complete documentation in a single file, see [llms-full.txt](${llmsFullUrl}).`,
|
|
336
|
+
...agentLinks,
|
|
337
|
+
"",
|
|
338
|
+
"## Primary sources",
|
|
339
|
+
...primarySources,
|
|
340
|
+
"",
|
|
341
|
+
"## Organization",
|
|
342
|
+
...formatOrganizationFacts(site),
|
|
343
|
+
].join("\n");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function buildLlmsFull(site: SemanticSiteModel): string {
|
|
347
|
+
const pages = site.pages
|
|
348
|
+
.filter(inFull)
|
|
349
|
+
.map((page) => {
|
|
350
|
+
// RFC-0184: absolute URL in the header
|
|
351
|
+
const header = [
|
|
352
|
+
`## ${pageLabel(page)}`,
|
|
353
|
+
`URL: ${toAbsoluteUrl(site, toPathname(page.url))}`,
|
|
354
|
+
`Description: ${canonicalizePageText(page, page.description)}`,
|
|
355
|
+
...(page.lead ? [`Lead: ${canonicalizePageText(page, page.lead)}`] : []),
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
// RFC-0142: `summary` depth emits only the page header (description / lead),
|
|
359
|
+
// skipping the answer-block dump and entity sections.
|
|
360
|
+
if (pageDepth(page) === "summary") {
|
|
361
|
+
return header.join("\n");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// RFC-0184: filter empty body sections to avoid blank placeholder gaps
|
|
365
|
+
const bodySections = [formatBlocks(page), formatPeople(page), formatInitiatives(page)].filter(
|
|
366
|
+
(s) => s.length > 0,
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
if (bodySections.length === 0) {
|
|
370
|
+
return header.join("\n");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return [...header, "", ...bodySections].join("\n");
|
|
374
|
+
})
|
|
375
|
+
.join("\n\n");
|
|
376
|
+
|
|
377
|
+
// RFC-0184: filter empty organization-level sections
|
|
378
|
+
const orgSections = [
|
|
379
|
+
...formatOffer(site),
|
|
380
|
+
...formatServices(site),
|
|
381
|
+
...formatLocation(site),
|
|
382
|
+
...formatTeam(site),
|
|
383
|
+
].filter((s) => s.length > 0);
|
|
384
|
+
|
|
385
|
+
return [
|
|
386
|
+
`# ${site.organization.name}`,
|
|
387
|
+
site.organization.description,
|
|
388
|
+
"",
|
|
389
|
+
"## Organization facts",
|
|
390
|
+
...formatOrganizationFacts(site),
|
|
391
|
+
...(orgSections.length > 0 ? ["", ...orgSections] : []),
|
|
392
|
+
...(pages.length > 0 ? ["", pages] : []),
|
|
393
|
+
].join("\n");
|
|
394
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Normalize generated Markdown fragments before they enter public text artifacts.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not parse or reformat arbitrary Markdown documents.</item>
|
|
6
|
+
<item>Do not mutate source content records.</item>
|
|
7
|
+
</non-goals>
|
|
8
|
+
</MODULE_CONTRACT>
|
|
9
|
+
<CHANGE_SUMMARY>
|
|
10
|
+
<item>RFC-0316: Added public text artifact Markdown hygiene helpers.</item>
|
|
11
|
+
</CHANGE_SUMMARY>
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function normalizeLine(line: string): string {
|
|
15
|
+
return line
|
|
16
|
+
.replace(/^- ---\s*$/, "---")
|
|
17
|
+
.replace(/^- -\s+/, "- ")
|
|
18
|
+
.replace(/^- (#{1,6})\s+/, "$1 ")
|
|
19
|
+
.replace(/^- (\d+\.)\s+/, "$1 ");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function normalizeGeneratedMarkdownText(text: string | undefined): string {
|
|
23
|
+
if (!text) return "";
|
|
24
|
+
return text
|
|
25
|
+
.replace(/\r\n?/g, "\n")
|
|
26
|
+
.replace(/<br\s*\/?>/gi, "\n")
|
|
27
|
+
.replace(/\b(20\d{2})\/(\d{1,2})\/(\d{1,2})\b/g, (_match, year, month, day) => {
|
|
28
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
29
|
+
})
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map(normalizeLine)
|
|
32
|
+
.join("\n")
|
|
33
|
+
.replace(/[ \t]+\n/g, "\n")
|
|
34
|
+
.trim();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function escapeRegExp(value: string): string {
|
|
38
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function canonicalizeGeneratedMarkdownText(
|
|
42
|
+
text: string | undefined,
|
|
43
|
+
options: { baseUrl?: string; defaultLanguage?: string } = {},
|
|
44
|
+
): string {
|
|
45
|
+
let normalized = normalizeGeneratedMarkdownText(text);
|
|
46
|
+
const { baseUrl, defaultLanguage } = options;
|
|
47
|
+
if (baseUrl && defaultLanguage) {
|
|
48
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
49
|
+
normalized = normalized.replace(
|
|
50
|
+
new RegExp(`${escapeRegExp(base)}/${escapeRegExp(defaultLanguage)}(?=/)`, "g"),
|
|
51
|
+
base,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return normalized;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function looksLikeMarkdownBlock(text: string): boolean {
|
|
58
|
+
return text.includes("\n") || /^(#{1,6}\s+|[-*]\s+|\d+\.\s+|---$|>\s+)/.test(text.trim());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function formatGeneratedMarkdownListItem(text: string): string[] {
|
|
62
|
+
const normalized = normalizeGeneratedMarkdownText(text);
|
|
63
|
+
if (!normalized) return [];
|
|
64
|
+
if (looksLikeMarkdownBlock(normalized)) {
|
|
65
|
+
return normalized.split("\n");
|
|
66
|
+
}
|
|
67
|
+
return [`- ${normalized}`];
|
|
68
|
+
}
|