@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,379 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>RFC-0188: Visitor Sales Funnel state-machine contracts. The PLATFORM owns the canonical
|
|
4
|
+
funnel stage catalog, the typed event model, and the transition graph; UChat is the conversation
|
|
5
|
+
runtime that renders the funnel and REQUESTS transitions — it never defines the graph, owns no
|
|
6
|
+
pricing, and writes no canonical state. Make.com is excluded everywhere in the funnel path. Pure
|
|
7
|
+
types + constants + pure helpers — no I/O, no astro:env, no vendor SDK.</purpose>
|
|
8
|
+
<non-goals>
|
|
9
|
+
<item>Do not let a chat vendor own the graph — UChat requests transitions; this module is the authority.</item>
|
|
10
|
+
<item>Do not reference Make.com or carry any legacy UChat goto/stage string as architecture.</item>
|
|
11
|
+
<item>Do not import astro:env, a Supabase SDK, or a vendor SDK — callers inject persistence.</item>
|
|
12
|
+
</non-goals>
|
|
13
|
+
</MODULE_CONTRACT>
|
|
14
|
+
<CHANGE_SUMMARY>
|
|
15
|
+
<item>RFC-0188 Phase 2 (contract): initial funnel stage/event/transition contracts.</item>
|
|
16
|
+
<item>RFC-0219: add FUNNEL_SYSTEM_TRIGGERS, FunnelTransitionTrigger, FUNNEL_TRANSITION_TRIGGERS — trigger overlay for the state-chart generator.</item>
|
|
17
|
+
</CHANGE_SUMMARY>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Current canonical funnel contract version. Bump on a breaking stage/event change. */
|
|
21
|
+
export const FUNNEL_VERSION = "1.0.0";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Stage catalog (closed, platform-owned)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Closed catalog of canonical funnel stages (RFC-0188). Names describe the
|
|
29
|
+
* PLATFORM process, never UChat GUI nodes or Pipedrive stage ids. UChat mirrors
|
|
30
|
+
* the current stage (e.g. in a custom field synced from Lagebild) but the catalog
|
|
31
|
+
* is owned here. Extend only by updating this array AND the transition graph.
|
|
32
|
+
*/
|
|
33
|
+
export const VISITOR_FUNNEL_STAGES = [
|
|
34
|
+
"new_session",
|
|
35
|
+
"privacy_acknowledged",
|
|
36
|
+
"intent_selected",
|
|
37
|
+
"organization_selected",
|
|
38
|
+
"qualification_priority",
|
|
39
|
+
"qualification_company",
|
|
40
|
+
"qualification_service",
|
|
41
|
+
"qualification_region",
|
|
42
|
+
"offer_presented",
|
|
43
|
+
"payment_pending",
|
|
44
|
+
"payment_confirmed",
|
|
45
|
+
"start_choice_pending",
|
|
46
|
+
"start_deferred",
|
|
47
|
+
"buyer_type_pending",
|
|
48
|
+
"b2b_start_consent_pending",
|
|
49
|
+
"b2c_withdrawal_consent_pending",
|
|
50
|
+
"start_approved",
|
|
51
|
+
"legal_data_requested",
|
|
52
|
+
"materials_requested",
|
|
53
|
+
"production_ready",
|
|
54
|
+
"change_balance_checked",
|
|
55
|
+
"change_payment_pending",
|
|
56
|
+
"change_description_requested",
|
|
57
|
+
"operator_review",
|
|
58
|
+
"won",
|
|
59
|
+
"lost",
|
|
60
|
+
] as const;
|
|
61
|
+
|
|
62
|
+
export type VisitorFunnelStage = (typeof VISITOR_FUNNEL_STAGES)[number];
|
|
63
|
+
|
|
64
|
+
/** The single entry stage every visitor session starts at. */
|
|
65
|
+
export const FUNNEL_ENTRY_STAGE: VisitorFunnelStage = "new_session";
|
|
66
|
+
|
|
67
|
+
/** Terminal stages — no outbound transition is allowed from these. */
|
|
68
|
+
export const FUNNEL_TERMINAL_STAGES: readonly VisitorFunnelStage[] = ["won", "lost"];
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Event model (typed business payloads on top of IntegrationEvent)
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
/** Closed catalog of typed funnel event kinds (RFC-0188). */
|
|
75
|
+
export const VISITOR_FUNNEL_EVENT_KINDS = [
|
|
76
|
+
"session.started",
|
|
77
|
+
"privacy.acknowledged",
|
|
78
|
+
"language.selected",
|
|
79
|
+
"intent.selected",
|
|
80
|
+
"organization.selected",
|
|
81
|
+
"qualification.answered",
|
|
82
|
+
"offer.selected",
|
|
83
|
+
"payment.link.requested",
|
|
84
|
+
"payment.confirmed",
|
|
85
|
+
"start.choice.selected",
|
|
86
|
+
"buyer.type.selected",
|
|
87
|
+
"legal.consent.recorded",
|
|
88
|
+
"material.submitted",
|
|
89
|
+
"change.requested",
|
|
90
|
+
"operator.note.added",
|
|
91
|
+
] as const;
|
|
92
|
+
|
|
93
|
+
export type VisitorFunnelEventKind = (typeof VISITOR_FUNNEL_EVENT_KINDS)[number];
|
|
94
|
+
|
|
95
|
+
/** Visitor intents — the top-level branch chosen after the welcome. */
|
|
96
|
+
export const VISITOR_FUNNEL_INTENTS = ["create_site", "change_site", "ask_question"] as const;
|
|
97
|
+
export type VisitorFunnelIntent = (typeof VISITOR_FUNNEL_INTENTS)[number];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Allowed funnel event SOURCES. UChat is the conversation runtime; Stripe is the
|
|
101
|
+
* payment-confirmation source (direct webhook, no Make.com); operator covers manual
|
|
102
|
+
* actions; send-message is the first-party contact form. Make.com is never a source.
|
|
103
|
+
*/
|
|
104
|
+
export const VISITOR_FUNNEL_SOURCES = ["uchat", "stripe", "operator", "send-message"] as const;
|
|
105
|
+
export type VisitorFunnelSource = (typeof VISITOR_FUNNEL_SOURCES)[number];
|
|
106
|
+
|
|
107
|
+
/** Buyer type — drives the legal-consent branch (B2B start vs B2C withdrawal). */
|
|
108
|
+
export const VISITOR_BUYER_TYPES = ["business", "consumer"] as const;
|
|
109
|
+
export type VisitorBuyerType = (typeof VISITOR_BUYER_TYPES)[number];
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Typed funnel event payload (RFC-0188). Carried as the `payload` of a normalized
|
|
113
|
+
* IntegrationEvent so the existing inbound/dedup machinery (RFC-0176) is reused.
|
|
114
|
+
* Offer snapshots are captured at selection time so a later offer.md change never
|
|
115
|
+
* re-prices a historical deal; legal-consent payloads are append-only.
|
|
116
|
+
*/
|
|
117
|
+
export interface VisitorFunnelEventPayload {
|
|
118
|
+
funnelVersion: string;
|
|
119
|
+
eventKind: VisitorFunnelEventKind;
|
|
120
|
+
stage?: VisitorFunnelStage;
|
|
121
|
+
previousStage?: VisitorFunnelStage;
|
|
122
|
+
intent?: VisitorFunnelIntent;
|
|
123
|
+
locale: string;
|
|
124
|
+
contact?: { name?: string; email?: string; phone?: string };
|
|
125
|
+
organization?: { id?: string; name?: string };
|
|
126
|
+
qualification?: {
|
|
127
|
+
priority?: "new_customers" | "professional_presence" | "online_presence" | "all";
|
|
128
|
+
companyName?: string;
|
|
129
|
+
serviceOrIndustry?: string;
|
|
130
|
+
region?: string;
|
|
131
|
+
};
|
|
132
|
+
offer?: {
|
|
133
|
+
plan?: "digital_foundation_monthly" | "digital_foundation_yearly";
|
|
134
|
+
growthModules?: string[];
|
|
135
|
+
/** Captured at selection time (deal-time snapshot) — never recomputed later. */
|
|
136
|
+
priceSnapshot?: Record<string, string>;
|
|
137
|
+
};
|
|
138
|
+
legal?: {
|
|
139
|
+
buyerType?: VisitorBuyerType;
|
|
140
|
+
startBeforeWithdrawalPeriod?: boolean;
|
|
141
|
+
withdrawalExpiryAcknowledged?: boolean;
|
|
142
|
+
};
|
|
143
|
+
changeRequest?: {
|
|
144
|
+
includedChangesAvailable?: number;
|
|
145
|
+
description?: string;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Transition graph (platform-owned authority)
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The valid transition graph (RFC-0188). UChat may REQUEST a transition; this graph
|
|
155
|
+
* is the authority that validates it. `lost` (abandon) is reachable from every
|
|
156
|
+
* non-terminal stage. Free-question side-conversations are NOT transitions — they
|
|
157
|
+
* are an orthogonal resume mechanism (the visitor returns to the SAME stage), so
|
|
158
|
+
* they intentionally do not appear here.
|
|
159
|
+
*/
|
|
160
|
+
export const FUNNEL_TRANSITIONS: Readonly<
|
|
161
|
+
Record<VisitorFunnelStage, readonly VisitorFunnelStage[]>
|
|
162
|
+
> = {
|
|
163
|
+
new_session: ["privacy_acknowledged", "lost"],
|
|
164
|
+
privacy_acknowledged: ["intent_selected", "lost"],
|
|
165
|
+
// create_site → organization_selected; change_site → change_balance_checked.
|
|
166
|
+
intent_selected: ["organization_selected", "change_balance_checked", "lost"],
|
|
167
|
+
organization_selected: ["qualification_priority", "lost"],
|
|
168
|
+
qualification_priority: ["qualification_company", "lost"],
|
|
169
|
+
qualification_company: ["qualification_service", "lost"],
|
|
170
|
+
qualification_service: ["qualification_region", "lost"],
|
|
171
|
+
qualification_region: ["offer_presented", "lost"],
|
|
172
|
+
offer_presented: ["payment_pending", "lost"],
|
|
173
|
+
payment_pending: ["payment_confirmed", "lost"],
|
|
174
|
+
payment_confirmed: ["start_choice_pending", "lost"],
|
|
175
|
+
// start now → buyer_type_pending; in 14 days → start_deferred (keeps the place).
|
|
176
|
+
start_choice_pending: ["start_deferred", "buyer_type_pending", "lost"],
|
|
177
|
+
start_deferred: ["buyer_type_pending", "lost"],
|
|
178
|
+
buyer_type_pending: ["b2b_start_consent_pending", "b2c_withdrawal_consent_pending", "lost"],
|
|
179
|
+
b2b_start_consent_pending: ["start_approved", "lost"],
|
|
180
|
+
b2c_withdrawal_consent_pending: ["start_approved", "lost"],
|
|
181
|
+
start_approved: ["legal_data_requested", "lost"],
|
|
182
|
+
legal_data_requested: ["materials_requested", "lost"],
|
|
183
|
+
materials_requested: ["production_ready", "lost"],
|
|
184
|
+
production_ready: ["operator_review", "won", "lost"],
|
|
185
|
+
// Change journey: balance check → (no changes left) pay → describe, or describe directly.
|
|
186
|
+
change_balance_checked: ["change_payment_pending", "change_description_requested", "lost"],
|
|
187
|
+
change_payment_pending: ["change_description_requested", "lost"],
|
|
188
|
+
change_description_requested: ["operator_review", "production_ready", "won", "lost"],
|
|
189
|
+
operator_review: ["won", "lost", "production_ready"],
|
|
190
|
+
won: [],
|
|
191
|
+
lost: [],
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const STAGE_SET: ReadonlySet<string> = new Set(VISITOR_FUNNEL_STAGES);
|
|
195
|
+
|
|
196
|
+
/** True when `value` is a canonical funnel stage. */
|
|
197
|
+
export function isValidFunnelStage(value: string): value is VisitorFunnelStage {
|
|
198
|
+
return STAGE_SET.has(value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** True when `to` is a declared next stage of `from` in the transition graph. */
|
|
202
|
+
export function canTransition(from: VisitorFunnelStage, to: VisitorFunnelStage): boolean {
|
|
203
|
+
return FUNNEL_TRANSITIONS[from]?.includes(to) ?? false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** The declared next stages of `from` (empty for terminal stages). */
|
|
207
|
+
export function nextStages(from: VisitorFunnelStage): readonly VisitorFunnelStage[] {
|
|
208
|
+
return FUNNEL_TRANSITIONS[from] ?? [];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Every stage reachable from `start` (default: the entry stage) by following the
|
|
213
|
+
* transition graph. Used by funnel.stage.validate to prove no canonical stage is
|
|
214
|
+
* stranded (unreachable) from the funnel entry.
|
|
215
|
+
*/
|
|
216
|
+
export function reachableStages(
|
|
217
|
+
start: VisitorFunnelStage = FUNNEL_ENTRY_STAGE,
|
|
218
|
+
): ReadonlySet<VisitorFunnelStage> {
|
|
219
|
+
const seen = new Set<VisitorFunnelStage>();
|
|
220
|
+
const queue: VisitorFunnelStage[] = [start];
|
|
221
|
+
while (queue.length > 0) {
|
|
222
|
+
const stage = queue.shift()!;
|
|
223
|
+
if (seen.has(stage)) continue;
|
|
224
|
+
seen.add(stage);
|
|
225
|
+
for (const next of FUNNEL_TRANSITIONS[stage] ?? []) {
|
|
226
|
+
if (!seen.has(next)) queue.push(next);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return seen;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Trigger overlay (RFC-0219)
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Reserved non-funnel-event triggers for abandon / operator / system-advance edges.
|
|
238
|
+
* `system.timeout` covers every unforced transition to `lost` (visitor abandons the
|
|
239
|
+
* conversation). `system.advance` covers automatic state advances with no explicit
|
|
240
|
+
* visitor input. `operator.won` / `operator.lost` are operator-initiated closures.
|
|
241
|
+
*/
|
|
242
|
+
export const FUNNEL_SYSTEM_TRIGGERS = [
|
|
243
|
+
"system.timeout",
|
|
244
|
+
"system.advance",
|
|
245
|
+
"operator.won",
|
|
246
|
+
"operator.lost",
|
|
247
|
+
] as const;
|
|
248
|
+
export type FunnelSystemTrigger = (typeof FUNNEL_SYSTEM_TRIGGERS)[number];
|
|
249
|
+
|
|
250
|
+
/** A trigger label: either a typed funnel event kind or a reserved system trigger. */
|
|
251
|
+
export type FunnelTransitionTrigger = VisitorFunnelEventKind | FunnelSystemTrigger;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* One entry per directed edge in `FUNNEL_TRANSITIONS` (RFC-0219). Labels each
|
|
255
|
+
* `(from, to)` pair with exactly one trigger. The bijection invariant — checked by
|
|
256
|
+
* `funnel.statechart.validate` — keeps this overlay and the transition map in exact
|
|
257
|
+
* lock-step: the edge sets must be equal and every `on` must be a valid trigger.
|
|
258
|
+
*
|
|
259
|
+
* Ordering follows `FUNNEL_TRANSITIONS` key order, then target order within each key,
|
|
260
|
+
* so the generator produces a deterministic document that can be drift-guarded
|
|
261
|
+
* byte-for-byte.
|
|
262
|
+
*/
|
|
263
|
+
export const FUNNEL_TRANSITION_TRIGGERS: ReadonlyArray<{
|
|
264
|
+
from: VisitorFunnelStage;
|
|
265
|
+
to: VisitorFunnelStage;
|
|
266
|
+
on: FunnelTransitionTrigger;
|
|
267
|
+
}> = [
|
|
268
|
+
{ from: "new_session", to: "privacy_acknowledged", on: "privacy.acknowledged" },
|
|
269
|
+
{ from: "new_session", to: "lost", on: "system.timeout" },
|
|
270
|
+
{ from: "privacy_acknowledged", to: "intent_selected", on: "intent.selected" },
|
|
271
|
+
{ from: "privacy_acknowledged", to: "lost", on: "system.timeout" },
|
|
272
|
+
// create_site branch → org selection; change_site branch → change balance check.
|
|
273
|
+
{ from: "intent_selected", to: "organization_selected", on: "organization.selected" },
|
|
274
|
+
{ from: "intent_selected", to: "change_balance_checked", on: "change.requested" },
|
|
275
|
+
{ from: "intent_selected", to: "lost", on: "system.timeout" },
|
|
276
|
+
{ from: "organization_selected", to: "qualification_priority", on: "qualification.answered" },
|
|
277
|
+
{ from: "organization_selected", to: "lost", on: "system.timeout" },
|
|
278
|
+
{ from: "qualification_priority", to: "qualification_company", on: "qualification.answered" },
|
|
279
|
+
{ from: "qualification_priority", to: "lost", on: "system.timeout" },
|
|
280
|
+
{ from: "qualification_company", to: "qualification_service", on: "qualification.answered" },
|
|
281
|
+
{ from: "qualification_company", to: "lost", on: "system.timeout" },
|
|
282
|
+
{ from: "qualification_service", to: "qualification_region", on: "qualification.answered" },
|
|
283
|
+
{ from: "qualification_service", to: "lost", on: "system.timeout" },
|
|
284
|
+
{ from: "qualification_region", to: "offer_presented", on: "qualification.answered" },
|
|
285
|
+
{ from: "qualification_region", to: "lost", on: "system.timeout" },
|
|
286
|
+
{ from: "offer_presented", to: "payment_pending", on: "offer.selected" },
|
|
287
|
+
{ from: "offer_presented", to: "lost", on: "system.timeout" },
|
|
288
|
+
{ from: "payment_pending", to: "payment_confirmed", on: "payment.confirmed" },
|
|
289
|
+
{ from: "payment_pending", to: "lost", on: "system.timeout" },
|
|
290
|
+
// After Stripe confirms payment the platform auto-advances to the start-choice screen.
|
|
291
|
+
{ from: "payment_confirmed", to: "start_choice_pending", on: "system.advance" },
|
|
292
|
+
{ from: "payment_confirmed", to: "lost", on: "system.timeout" },
|
|
293
|
+
// start now → buyer_type_pending; in 14 days → start_deferred.
|
|
294
|
+
{ from: "start_choice_pending", to: "start_deferred", on: "start.choice.selected" },
|
|
295
|
+
{ from: "start_choice_pending", to: "buyer_type_pending", on: "start.choice.selected" },
|
|
296
|
+
{ from: "start_choice_pending", to: "lost", on: "system.timeout" },
|
|
297
|
+
{ from: "start_deferred", to: "buyer_type_pending", on: "start.choice.selected" },
|
|
298
|
+
{ from: "start_deferred", to: "lost", on: "system.timeout" },
|
|
299
|
+
{ from: "buyer_type_pending", to: "b2b_start_consent_pending", on: "buyer.type.selected" },
|
|
300
|
+
{
|
|
301
|
+
from: "buyer_type_pending",
|
|
302
|
+
to: "b2c_withdrawal_consent_pending",
|
|
303
|
+
on: "buyer.type.selected",
|
|
304
|
+
},
|
|
305
|
+
{ from: "buyer_type_pending", to: "lost", on: "system.timeout" },
|
|
306
|
+
{ from: "b2b_start_consent_pending", to: "start_approved", on: "legal.consent.recorded" },
|
|
307
|
+
{ from: "b2b_start_consent_pending", to: "lost", on: "system.timeout" },
|
|
308
|
+
{
|
|
309
|
+
from: "b2c_withdrawal_consent_pending",
|
|
310
|
+
to: "start_approved",
|
|
311
|
+
on: "legal.consent.recorded",
|
|
312
|
+
},
|
|
313
|
+
{ from: "b2c_withdrawal_consent_pending", to: "lost", on: "system.timeout" },
|
|
314
|
+
// After both consent branches reach start_approved, the platform auto-advances.
|
|
315
|
+
{ from: "start_approved", to: "legal_data_requested", on: "system.advance" },
|
|
316
|
+
{ from: "start_approved", to: "lost", on: "system.timeout" },
|
|
317
|
+
{ from: "legal_data_requested", to: "materials_requested", on: "material.submitted" },
|
|
318
|
+
{ from: "legal_data_requested", to: "lost", on: "system.timeout" },
|
|
319
|
+
{ from: "materials_requested", to: "production_ready", on: "material.submitted" },
|
|
320
|
+
{ from: "materials_requested", to: "lost", on: "system.timeout" },
|
|
321
|
+
{ from: "production_ready", to: "operator_review", on: "operator.note.added" },
|
|
322
|
+
{ from: "production_ready", to: "won", on: "operator.won" },
|
|
323
|
+
{ from: "production_ready", to: "lost", on: "system.timeout" },
|
|
324
|
+
// Change journey: no credits left → payment link; credits available → describe directly.
|
|
325
|
+
{
|
|
326
|
+
from: "change_balance_checked",
|
|
327
|
+
to: "change_payment_pending",
|
|
328
|
+
on: "payment.link.requested",
|
|
329
|
+
},
|
|
330
|
+
{ from: "change_balance_checked", to: "change_description_requested", on: "system.advance" },
|
|
331
|
+
{ from: "change_balance_checked", to: "lost", on: "system.timeout" },
|
|
332
|
+
{ from: "change_payment_pending", to: "change_description_requested", on: "payment.confirmed" },
|
|
333
|
+
{ from: "change_payment_pending", to: "lost", on: "system.timeout" },
|
|
334
|
+
{ from: "change_description_requested", to: "operator_review", on: "change.requested" },
|
|
335
|
+
{ from: "change_description_requested", to: "production_ready", on: "system.advance" },
|
|
336
|
+
{ from: "change_description_requested", to: "won", on: "operator.won" },
|
|
337
|
+
{ from: "change_description_requested", to: "lost", on: "system.timeout" },
|
|
338
|
+
{ from: "operator_review", to: "won", on: "operator.won" },
|
|
339
|
+
{ from: "operator_review", to: "lost", on: "operator.lost" },
|
|
340
|
+
{ from: "operator_review", to: "production_ready", on: "system.advance" },
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
344
|
+
// Legacy denylist + Make.com exclusion (governance teeth)
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Legacy UChat stage strings from the retired 10-flow export. These are reference
|
|
349
|
+
* only and MUST NOT appear as canonical stages or in funnel config/copy. The
|
|
350
|
+
* validators hard-fail on any of these (RFC-0188 nonGoals).
|
|
351
|
+
*/
|
|
352
|
+
export const LEGACY_FUNNEL_STAGES: readonly string[] = [
|
|
353
|
+
"q_website_tier",
|
|
354
|
+
"new_chat",
|
|
355
|
+
"q_new",
|
|
356
|
+
"q_what_important",
|
|
357
|
+
"q_company",
|
|
358
|
+
"q_industry",
|
|
359
|
+
"q_region",
|
|
360
|
+
"after_start_now_delay",
|
|
361
|
+
"start_now_or_later",
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Pure scanner: report every Make.com reference in `text` (line, matched token).
|
|
366
|
+
* The funnel path — config, content, and code — must contain ZERO references, in
|
|
367
|
+
* any mode, transitional or permanent (RFC-0188 Decision §5). Integromat is the
|
|
368
|
+
* legacy name for Make.com and is denied too.
|
|
369
|
+
*/
|
|
370
|
+
export function scanForMakeComReferences(text: string): Array<{ line: number; match: string }> {
|
|
371
|
+
const pattern = /\b(?:hook\.(?:[a-z0-9-]+\.)*)?make\.com\b|\bintegromat\b/gi;
|
|
372
|
+
const hits: Array<{ line: number; match: string }> = [];
|
|
373
|
+
text.split(/\r?\n/).forEach((raw, index) => {
|
|
374
|
+
for (const m of raw.matchAll(pattern)) {
|
|
375
|
+
hits.push({ line: index + 1, match: m[0] });
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
return hits;
|
|
379
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
RFC-0168: Integration Port barrel. Re-exports types from port.ts, crm-buffer.ts,
|
|
5
|
+
funnel.ts, lifecycle.ts, sharding.ts, dispatch.ts, qstash.ts, and runtime
|
|
6
|
+
orchestration (registries + fan-out) from orchestration.ts. Type-only consumers
|
|
7
|
+
(agent-gate, supabase-crm tests) import from `./port-barrel.ts` (`@warpgogol/werkstatt-shared/share/integration/port`)
|
|
8
|
+
to avoid transitively pulling in adapter implementations; consumers needing
|
|
9
|
+
runtime logic import from here — the orchestration module is re-exported transparently.
|
|
10
|
+
</purpose>
|
|
11
|
+
<non-goals>
|
|
12
|
+
<item>Do not define logic here — pure re-export barrel.</item>
|
|
13
|
+
<item>Do not import astro:env — the caller injects the secrets bag.</item>
|
|
14
|
+
</non-goals>
|
|
15
|
+
</MODULE_CONTRACT>
|
|
16
|
+
<CHANGE_SUMMARY>
|
|
17
|
+
<item>RFC-0168: initial implementation.</item>
|
|
18
|
+
<item>Deepening: split orchestration (registries + fan-out) into orchestration.ts; index.ts is now a pure barrel.</item>
|
|
19
|
+
</CHANGE_SUMMARY>
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// Port-barrel re-exports all type/contract modules explicitly.
|
|
23
|
+
export * from "./port-barrel.ts";
|
|
24
|
+
|
|
25
|
+
// Runtime modules — explicit re-exports to keep the public API surface clear.
|
|
26
|
+
export type {
|
|
27
|
+
DeliverResult,
|
|
28
|
+
AdapterReadiness,
|
|
29
|
+
RouteResult,
|
|
30
|
+
DeliverEventResult,
|
|
31
|
+
QueueBinding,
|
|
32
|
+
KvDedupStore,
|
|
33
|
+
} from "./orchestration.ts";
|
|
34
|
+
export {
|
|
35
|
+
CHANNEL_ADAPTERS,
|
|
36
|
+
CRM_ADAPTERS,
|
|
37
|
+
CHANNEL_ADAPTER_IDS,
|
|
38
|
+
CRM_ADAPTER_IDS,
|
|
39
|
+
INTEGRATION_ADAPTER_SECRETS,
|
|
40
|
+
deliverLead,
|
|
41
|
+
auditIntegrationReadiness,
|
|
42
|
+
DESTINATION_ADAPTERS,
|
|
43
|
+
EXTERNAL_DESTINATION_VENDORS,
|
|
44
|
+
EXTERNAL_DESTINATION_SECRETS,
|
|
45
|
+
DESTINATION_VENDORS_BY_KIND,
|
|
46
|
+
DESTINATION_ADAPTER_SECRETS,
|
|
47
|
+
routeEvent,
|
|
48
|
+
IntegrationEventSchema,
|
|
49
|
+
authenticateInbound,
|
|
50
|
+
routeEventToReady,
|
|
51
|
+
deliverEvent,
|
|
52
|
+
kvDedup,
|
|
53
|
+
enqueueEvent,
|
|
54
|
+
consumeIntegrationBatch,
|
|
55
|
+
upsertLead,
|
|
56
|
+
} from "./orchestration.ts";
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>RFC-0191: Client Lifecycle & Stripe Billing event contracts. A sibling catalog to the
|
|
4
|
+
RFC-0188 funnel event kinds — the post-sale lifecycle (subscriptions, invoices, dunning, churn)
|
|
5
|
+
carried on the same normalized IntegrationEvent. Stripe is the billing authority; these types are
|
|
6
|
+
the canonical mirror. Pure types + constants + a zod payload schema — no I/O, no astro:env, no
|
|
7
|
+
Stripe SDK.</purpose>
|
|
8
|
+
<non-goals>
|
|
9
|
+
<item>Do not import the Stripe SDK or astro:env — the adapter injects secrets and maps events.</item>
|
|
10
|
+
<item>Do not let UChat or Pipedrive own billing state — Stripe is the authority, Lagebild the mirror.</item>
|
|
11
|
+
<item>Do not reference Make.com anywhere in the billing path (RFC-0188/0191).</item>
|
|
12
|
+
</non-goals>
|
|
13
|
+
</MODULE_CONTRACT>
|
|
14
|
+
<CHANGE_SUMMARY>
|
|
15
|
+
<item>RFC-0191: initial lifecycle event + payload contracts.</item>
|
|
16
|
+
<item>RFC-0219: add SUBSCRIPTION_TRANSITIONS and SUBSCRIPTION_TRANSITION_TRIGGERS — subscription lifecycle graph for the state-chart generator.</item>
|
|
17
|
+
</CHANGE_SUMMARY>
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
/** Closed catalog of post-sale lifecycle event kinds (RFC-0191). */
|
|
23
|
+
export const LIFECYCLE_EVENT_KINDS = [
|
|
24
|
+
"invoice.paid",
|
|
25
|
+
"invoice.payment_failed",
|
|
26
|
+
"subscription.created",
|
|
27
|
+
"subscription.updated",
|
|
28
|
+
"subscription.canceled",
|
|
29
|
+
"payment.refunded",
|
|
30
|
+
] as const;
|
|
31
|
+
export type LifecycleEventKind = (typeof LIFECYCLE_EVENT_KINDS)[number];
|
|
32
|
+
|
|
33
|
+
/** Subscription health states (mirror Stripe subscription.status, condensed). */
|
|
34
|
+
export const SUBSCRIPTION_STATUSES = ["active", "past_due", "canceled", "paused"] as const;
|
|
35
|
+
export type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];
|
|
36
|
+
|
|
37
|
+
/** What an invoice is for — drives the P4/lifecycle Pipedrive routing. */
|
|
38
|
+
export const INVOICE_KINDS = ["setup", "cycle", "change", "adhoc"] as const;
|
|
39
|
+
export type InvoiceKind = (typeof INVOICE_KINDS)[number];
|
|
40
|
+
|
|
41
|
+
/** Invoice payment state (mirror Stripe invoice.status, condensed). */
|
|
42
|
+
export const INVOICE_STATUSES = ["open", "paid", "uncollectible", "void"] as const;
|
|
43
|
+
export type InvoiceStatus = (typeof INVOICE_STATUSES)[number];
|
|
44
|
+
|
|
45
|
+
/** The plan a subscription runs on (mirrors the offer in business/offer.md). */
|
|
46
|
+
export const SUBSCRIPTION_PLANS = [
|
|
47
|
+
"digital_foundation_monthly",
|
|
48
|
+
"digital_foundation_yearly",
|
|
49
|
+
] as const;
|
|
50
|
+
export type SubscriptionPlan = (typeof SUBSCRIPTION_PLANS)[number];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Typed lifecycle payload, carried in `IntegrationEvent.payload.lifecycle`. The Stripe
|
|
54
|
+
* adapter (RFC-0191) maps each Stripe webhook event onto this shape; amounts are in the
|
|
55
|
+
* smallest currency unit (cents) — Stripe is the authority, the buffer mirrors cents and
|
|
56
|
+
* never recomputes prices. The Organization is resolved via `stripeCustomerId` (RFC-0190).
|
|
57
|
+
*/
|
|
58
|
+
export interface LifecycleEventPayload {
|
|
59
|
+
eventKind: LifecycleEventKind;
|
|
60
|
+
/** Stripe's own event id — the idempotency key (dedup on redelivery). */
|
|
61
|
+
stripeEventId: string;
|
|
62
|
+
stripeCustomerId: string;
|
|
63
|
+
stripeSubscriptionId?: string;
|
|
64
|
+
stripeInvoiceId?: string;
|
|
65
|
+
plan?: SubscriptionPlan;
|
|
66
|
+
subscriptionStatus?: SubscriptionStatus;
|
|
67
|
+
invoiceKind?: InvoiceKind;
|
|
68
|
+
invoiceStatus?: InvoiceStatus;
|
|
69
|
+
amountCents?: number;
|
|
70
|
+
currency?: string;
|
|
71
|
+
/** ISO-8601 end of the current billing period (for renewals). */
|
|
72
|
+
currentPeriodEnd?: string;
|
|
73
|
+
// Spec §08 (Stripe metadata contract): Lagebild refs resolved from the Stripe object's
|
|
74
|
+
// metadata so a lifecycle event maps back to the Organization (RFC-0190) and Deal.
|
|
75
|
+
lagebildOrganizationId?: string;
|
|
76
|
+
lagebildDealId?: string;
|
|
77
|
+
siteKey?: string;
|
|
78
|
+
/** Line-item price ids (for invoice_kind inference + module/MRR reasoning). */
|
|
79
|
+
priceIds?: string[];
|
|
80
|
+
/**
|
|
81
|
+
* True when a cycle invoice has no `lagebild_*` metadata of its own and must be resolved
|
|
82
|
+
* via its Subscription's metadata (spec §08).
|
|
83
|
+
*/
|
|
84
|
+
needsSubscriptionLookup?: boolean;
|
|
85
|
+
/** Monthly recurring revenue (cents), normalized from the subscription's items (spec §08). */
|
|
86
|
+
mrrCents?: number;
|
|
87
|
+
/** Per-cycle included-change allowance, from Subscription metadata `included_changes_per_cycle`. */
|
|
88
|
+
includedChangesPerCycle?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Zod schema the Stripe adapter validates its mapping against before emitting. */
|
|
92
|
+
export const LifecycleEventPayloadSchema = z.object({
|
|
93
|
+
eventKind: z.enum(LIFECYCLE_EVENT_KINDS),
|
|
94
|
+
stripeEventId: z.string().min(1),
|
|
95
|
+
stripeCustomerId: z.string().min(1),
|
|
96
|
+
stripeSubscriptionId: z.string().optional(),
|
|
97
|
+
stripeInvoiceId: z.string().optional(),
|
|
98
|
+
plan: z.enum(SUBSCRIPTION_PLANS).optional(),
|
|
99
|
+
subscriptionStatus: z.enum(SUBSCRIPTION_STATUSES).optional(),
|
|
100
|
+
invoiceKind: z.enum(INVOICE_KINDS).optional(),
|
|
101
|
+
invoiceStatus: z.enum(INVOICE_STATUSES).optional(),
|
|
102
|
+
amountCents: z.number().int().nonnegative().optional(),
|
|
103
|
+
currency: z.string().optional(),
|
|
104
|
+
currentPeriodEnd: z.string().optional(),
|
|
105
|
+
lagebildOrganizationId: z.string().optional(),
|
|
106
|
+
lagebildDealId: z.string().optional(),
|
|
107
|
+
siteKey: z.string().optional(),
|
|
108
|
+
priceIds: z.array(z.string()).optional(),
|
|
109
|
+
needsSubscriptionLookup: z.boolean().optional(),
|
|
110
|
+
mrrCents: z.number().int().nonnegative().optional(),
|
|
111
|
+
includedChangesPerCycle: z.number().int().nonnegative().optional(),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/** True when `value` is a known lifecycle event kind. */
|
|
115
|
+
export function isLifecycleEventKind(value: string): value is LifecycleEventKind {
|
|
116
|
+
return (LIFECYCLE_EVENT_KINDS as readonly string[]).includes(value);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Subscription lifecycle graph (RFC-0219)
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Valid subscription status transitions (RFC-0219). Symmetric with `FUNNEL_TRANSITIONS`:
|
|
125
|
+
* the platform owns the allowed moves; Stripe is the billing authority that fires the
|
|
126
|
+
* events, but the transition graph itself is declared here.
|
|
127
|
+
*/
|
|
128
|
+
export const SUBSCRIPTION_TRANSITIONS: Readonly<
|
|
129
|
+
Record<SubscriptionStatus, readonly SubscriptionStatus[]>
|
|
130
|
+
> = {
|
|
131
|
+
active: ["past_due", "paused", "canceled"],
|
|
132
|
+
past_due: ["active", "canceled"],
|
|
133
|
+
paused: ["active", "canceled"],
|
|
134
|
+
canceled: [],
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* One entry per directed edge in `SUBSCRIPTION_TRANSITIONS` (RFC-0219). Labels each
|
|
139
|
+
* `(from, to)` pair with exactly one `LifecycleEventKind` trigger. The bijection
|
|
140
|
+
* invariant — checked by `funnel.statechart.validate` — keeps this overlay and the
|
|
141
|
+
* transition map in exact lock-step.
|
|
142
|
+
*/
|
|
143
|
+
export const SUBSCRIPTION_TRANSITION_TRIGGERS: ReadonlyArray<{
|
|
144
|
+
from: SubscriptionStatus;
|
|
145
|
+
to: SubscriptionStatus;
|
|
146
|
+
on: LifecycleEventKind;
|
|
147
|
+
}> = [
|
|
148
|
+
{ from: "active", to: "past_due", on: "invoice.payment_failed" },
|
|
149
|
+
{ from: "active", to: "paused", on: "subscription.updated" },
|
|
150
|
+
{ from: "active", to: "canceled", on: "subscription.canceled" },
|
|
151
|
+
{ from: "past_due", to: "active", on: "invoice.paid" },
|
|
152
|
+
{ from: "past_due", to: "canceled", on: "subscription.canceled" },
|
|
153
|
+
{ from: "paused", to: "active", on: "subscription.updated" },
|
|
154
|
+
{ from: "paused", to: "canceled", on: "subscription.canceled" },
|
|
155
|
+
];
|