@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.
Files changed (403) hide show
  1. package/package.json +352 -0
  2. package/src/checks/index.ts +24 -0
  3. package/src/checks/lib/astro-site-url.ts +30 -0
  4. package/src/checks/lib/i18n.ts +30 -0
  5. package/src/checks/result-helpers.ts +102 -0
  6. package/src/checks/suppressions-config.ts +195 -0
  7. package/src/index.ts +14 -0
  8. package/src/integration/README.md +38 -0
  9. package/src/integration/adapters.ts +143 -0
  10. package/src/integration/crm-buffer.ts +491 -0
  11. package/src/integration/dispatch.ts +106 -0
  12. package/src/integration/funnel.ts +379 -0
  13. package/src/integration/index.ts +56 -0
  14. package/src/integration/lifecycle.ts +155 -0
  15. package/src/integration/orchestration.ts +439 -0
  16. package/src/integration/port-barrel.ts +124 -0
  17. package/src/integration/port.ts +143 -0
  18. package/src/integration/qstash.ts +134 -0
  19. package/src/integration/sharding.ts +133 -0
  20. package/src/integration/tests/deliver-event.test.ts +52 -0
  21. package/src/integration/tests/dispatch.test.ts +88 -0
  22. package/src/integration/tests/funnel-bridge.test.ts +46 -0
  23. package/src/integration/tests/hub-runtime.test.ts +128 -0
  24. package/src/integration/tests/lifecycle.test.ts +69 -0
  25. package/src/integration/tests/qstash.test.ts +83 -0
  26. package/src/integration/tests/sharding.test.ts +64 -0
  27. package/src/integration/vitest.config.ts +8 -0
  28. package/src/integration-adapter-supabase-crm/adapter.ts +437 -0
  29. package/src/integration-adapter-supabase-crm/client.ts +605 -0
  30. package/src/integration-adapter-supabase-crm/index.ts +28 -0
  31. package/src/integration-adapter-supabase-crm/pipedrive-sync-target.ts +482 -0
  32. package/src/integration-adapter-supabase-crm/supabase-rest.ts +89 -0
  33. package/src/integration-adapter-supabase-crm/tenant-registry.ts +334 -0
  34. package/src/integration-adapter-supabase-crm/tests/change-balance.pbt.test.ts +83 -0
  35. package/src/integration-adapter-supabase-crm/tests/funnel-persistence.test.ts +409 -0
  36. package/src/integration-adapter-supabase-crm/tests/lifecycle-sync.test.ts +288 -0
  37. package/src/integration-adapter-supabase-crm/tests/stage-map.test.ts +80 -0
  38. package/src/integration-adapter-supabase-crm/vitest.config.ts +8 -0
  39. package/src/integration-adapter-supabase-crm/worker.ts +223 -0
  40. package/src/observability/conventions.ts +93 -0
  41. package/src/observability/env-resolver.ts +57 -0
  42. package/src/observability/index.ts +57 -0
  43. package/src/observability/metric-registry.ts +184 -0
  44. package/src/observability/otlp-converter.ts +125 -0
  45. package/src/observability/otlp-json.ts +135 -0
  46. package/src/observability/otlp-transport.ts +68 -0
  47. package/src/observability/pusher.ts +142 -0
  48. package/src/observability/redact.ts +27 -0
  49. package/src/observability/tests/conventions.test.ts +100 -0
  50. package/src/observability/tests/metric-registry.test.ts +117 -0
  51. package/src/observability/tests/otlp-converter.pbt.test.ts +77 -0
  52. package/src/observability/tests/otlp-converter.test.ts +157 -0
  53. package/src/observability/tests/otlp-json.test.ts +120 -0
  54. package/src/observability/tests/pusher.test.ts +75 -0
  55. package/src/observability/tests/redact.pbt.test.ts +71 -0
  56. package/src/observability/tests/redact.test.ts +37 -0
  57. package/src/observability/typed-refs.ts +137 -0
  58. package/src/observability/vitest.config.ts +8 -0
  59. package/src/ontology/analytics/matomo-binding.yaml +172 -0
  60. package/src/ontology/analytics/matomo-fleet.registry.yaml +26 -0
  61. package/src/ontology/analytics/messkanon.yaml +151 -0
  62. package/src/ontology/archetype-registry.ts +61 -0
  63. package/src/ontology/archetypes/components/brand-label.yaml +19 -0
  64. package/src/ontology/archetypes/components/copyright.yaml +19 -0
  65. package/src/ontology/archetypes/components/currency-aware-price-display.yaml +19 -0
  66. package/src/ontology/archetypes/components/currency-selector.yaml +19 -0
  67. package/src/ontology/archetypes/components/footer-promo.yaml +19 -0
  68. package/src/ontology/archetypes/components/lang-switcher.yaml +19 -0
  69. package/src/ontology/archetypes/components/layout.yaml +19 -0
  70. package/src/ontology/archetypes/components/live-photo.yaml +20 -0
  71. package/src/ontology/archetypes/components/material-credit.yaml +20 -0
  72. package/src/ontology/archetypes/components/media.yaml +21 -0
  73. package/src/ontology/archetypes/components/nachweis-card.yaml +34 -0
  74. package/src/ontology/archetypes/components/nachweis-detail.yaml +43 -0
  75. package/src/ontology/archetypes/components/nachweis-list.yaml +37 -0
  76. package/src/ontology/archetypes/components/nachweis-verify.yaml +31 -0
  77. package/src/ontology/archetypes/components/not-found.yaml +22 -0
  78. package/src/ontology/archetypes/components/offer-capacity.yaml +21 -0
  79. package/src/ontology/archetypes/components/passport-header.yaml +19 -0
  80. package/src/ontology/archetypes/components/passport-provenance.yaml +20 -0
  81. package/src/ontology/archetypes/components/passport-score-grid.yaml +20 -0
  82. package/src/ontology/archetypes/components/passport-star-map.yaml +20 -0
  83. package/src/ontology/archetypes/components/person-profile.yaml +20 -0
  84. package/src/ontology/archetypes/components/pulsar.yaml +20 -0
  85. package/src/ontology/archetypes/components/responsive-image.yaml +19 -0
  86. package/src/ontology/archetypes/components/scroll-to-top.yaml +22 -0
  87. package/src/ontology/archetypes/components/social-meta.yaml +20 -0
  88. package/src/ontology/archetypes/components/structured-data.yaml +19 -0
  89. package/src/ontology/archetypes/index.json +1003 -0
  90. package/src/ontology/archetypes/index.yaml +821 -0
  91. package/src/ontology/archetypes/sections/approach.yaml +23 -0
  92. package/src/ontology/archetypes/sections/article-list.yaml +30 -0
  93. package/src/ontology/archetypes/sections/audience-cards.yaml +30 -0
  94. package/src/ontology/archetypes/sections/breadcrumbs.yaml +21 -0
  95. package/src/ontology/archetypes/sections/chat-widget.yaml +37 -0
  96. package/src/ontology/archetypes/sections/comparison-cards.yaml +31 -0
  97. package/src/ontology/archetypes/sections/controlled-responsibility-block.yaml +34 -0
  98. package/src/ontology/archetypes/sections/credits.yaml +29 -0
  99. package/src/ontology/archetypes/sections/donation-card.yaml +53 -0
  100. package/src/ontology/archetypes/sections/donation-use.yaml +23 -0
  101. package/src/ontology/archetypes/sections/dynamic-status-block.yaml +35 -0
  102. package/src/ontology/archetypes/sections/faq-list.yaml +26 -0
  103. package/src/ontology/archetypes/sections/final-cta.yaml +53 -0
  104. package/src/ontology/archetypes/sections/hero-decision-card.yaml +65 -0
  105. package/src/ontology/archetypes/sections/hero.yaml +50 -0
  106. package/src/ontology/archetypes/sections/impact.yaml +23 -0
  107. package/src/ontology/archetypes/sections/markdown.yaml +43 -0
  108. package/src/ontology/archetypes/sections/mountain-journey.yaml +43 -0
  109. package/src/ontology/archetypes/sections/navigation.yaml +20 -0
  110. package/src/ontology/archetypes/sections/notausgang-block.yaml +41 -0
  111. package/src/ontology/archetypes/sections/open-source-registry.yaml +26 -0
  112. package/src/ontology/archetypes/sections/ownership-block.yaml +57 -0
  113. package/src/ontology/archetypes/sections/people.yaml +48 -0
  114. package/src/ontology/archetypes/sections/price-card.yaml +50 -0
  115. package/src/ontology/archetypes/sections/problem.yaml +23 -0
  116. package/src/ontology/archetypes/sections/send-message.yaml +63 -0
  117. package/src/ontology/archetypes/sections/service-metadata-block.yaml +40 -0
  118. package/src/ontology/archetypes/sections/social-proof.yaml +27 -0
  119. package/src/ontology/archetypes/sections/transparency.yaml +32 -0
  120. package/src/ontology/archetypes/sections/trust-strip.yaml +42 -0
  121. package/src/ontology/archetypes/sections/video-section.yaml +27 -0
  122. package/src/ontology/archetypes/sections/women.yaml +29 -0
  123. package/src/ontology/biome-token-projection.ts +229 -0
  124. package/src/ontology/biomes/check-concrete-blueprint.yaml +118 -0
  125. package/src/ontology/biomes/handwerk-material-warm.yaml +135 -0
  126. package/src/ontology/biomes/nonprofit-trust.yaml +123 -0
  127. package/src/ontology/blueprints/offer.yaml +39 -0
  128. package/src/ontology/blueprints/prompts/local-market-signal.md +13 -0
  129. package/src/ontology/blueprints/prompts/website-local-narrative.md +24 -0
  130. package/src/ontology/blueprints/ratgeber.yaml +62 -0
  131. package/src/ontology/blueprints/website-local.yaml +309 -0
  132. package/src/ontology/blueprints/website-service.yaml +84 -0
  133. package/src/ontology/capabilities/lead.submit.yaml +44 -0
  134. package/src/ontology/cosmic/index.ts +48 -0
  135. package/src/ontology/cosmic/moon-catalog.ts +173 -0
  136. package/src/ontology/cosmic/planet-catalog.ts +82 -0
  137. package/src/ontology/cosmic/star-catalog.ts +337 -0
  138. package/src/ontology/enums.ts +159 -0
  139. package/src/ontology/external-surfaces/index.ts +158 -0
  140. package/src/ontology/external-surfaces/jsonld-types.yaml +135 -0
  141. package/src/ontology/external-surfaces/sitemap-shape.yaml +6 -0
  142. package/src/ontology/external-surfaces/url-schema.yaml +95 -0
  143. package/src/ontology/growth/events/contact-email-click.yaml +21 -0
  144. package/src/ontology/growth/events/contact-form-submit.yaml +25 -0
  145. package/src/ontology/growth/events/contact-phone-click.yaml +21 -0
  146. package/src/ontology/growth/events/contact-route-click.yaml +21 -0
  147. package/src/ontology/growth/events/contact-submit.yaml +28 -0
  148. package/src/ontology/growth/events/contact-whatsapp-click.yaml +21 -0
  149. package/src/ontology/growth/events/cta-click.yaml +30 -0
  150. package/src/ontology/growth/events/donation-confirmed.yaml +32 -0
  151. package/src/ontology/growth/events/donation-form-start.yaml +21 -0
  152. package/src/ontology/growth/events/donation-form-submit.yaml +29 -0
  153. package/src/ontology/growth/events/donation-intent.yaml +29 -0
  154. package/src/ontology/growth/events/form-error.yaml +27 -0
  155. package/src/ontology/growth/events/form-start.yaml +21 -0
  156. package/src/ontology/growth/events/form-submit.yaml +21 -0
  157. package/src/ontology/growth/events/outbound-click.yaml +25 -0
  158. package/src/ontology/growth/events/page-view.yaml +24 -0
  159. package/src/ontology/growth/events/passport-view.yaml +28 -0
  160. package/src/ontology/growth/events/star-map-navigate.yaml +30 -0
  161. package/src/ontology/index.ts +165 -0
  162. package/src/ontology/manifest.ts +344 -0
  163. package/src/ontology/operations/index.ts +1 -0
  164. package/src/ontology/schemas/biome.ts +303 -0
  165. package/src/ontology/schemas/capability.ts +104 -0
  166. package/src/ontology/schemas/constellation.ts +114 -0
  167. package/src/ontology/schemas/dns-records.ts +53 -0
  168. package/src/ontology/schemas/index.ts +95 -0
  169. package/src/ontology/schemas/manifest-resolver.ts +123 -0
  170. package/src/ontology/schemas/page-entry.ts +213 -0
  171. package/src/ontology/schemas/section-archetype.ts +109 -0
  172. package/src/ontology/schemas/site-family.ts +98 -0
  173. package/src/ontology/schemas/system/growth.ts +80 -0
  174. package/src/ontology/schemas/system/integrations.ts +143 -0
  175. package/src/ontology/schemas/system/manifest.ts +492 -0
  176. package/src/ontology/schemas/system/page-output.ts +99 -0
  177. package/src/ontology/schemas/system/release.ts +58 -0
  178. package/src/ontology/schemas/system/text.ts +58 -0
  179. package/src/ontology/schemas/system.ts +53 -0
  180. package/src/ontology/shared-section-props/body-fragments.ts +182 -0
  181. package/src/ontology/shared-section-props/catalog.ts +195 -0
  182. package/src/ontology/shared-section-props/common.ts +178 -0
  183. package/src/ontology/shared-section-props/index.ts +34 -0
  184. package/src/ontology/shared-section-props/visual-header.ts +163 -0
  185. package/src/ontology/site-families/charity-donation-trust/cultural-rules.yaml +6 -0
  186. package/src/ontology/site-families/charity-donation-trust/family.yaml +44 -0
  187. package/src/ontology/site-families/charity-donation-trust/linguistic-rules.yaml +6 -0
  188. package/src/ontology/site-families/check-quality-operator/cultural-rules.yaml +6 -0
  189. package/src/ontology/site-families/check-quality-operator/family.yaml +40 -0
  190. package/src/ontology/site-families/check-quality-operator/linguistic-rules.yaml +6 -0
  191. package/src/ontology/site-families/handwerk-trust-engineering/cultural-rules.yaml +6 -0
  192. package/src/ontology/site-families/handwerk-trust-engineering/family.yaml +53 -0
  193. package/src/ontology/site-families/handwerk-trust-engineering/linguistic-rules.yaml +6 -0
  194. package/src/ontology/tests/catalogs.pbt.test.ts +89 -0
  195. package/src/ontology/tests/enums.test.ts +203 -0
  196. package/src/ontology/tests/sternsystem-owner.test.ts +63 -0
  197. package/src/ontology/vitest.config.ts +8 -0
  198. package/src/passport/dht-sign.test.ts +99 -0
  199. package/src/passport/dht-sign.ts +73 -0
  200. package/src/passport/identity-sign.test.ts +134 -0
  201. package/src/passport/identity-sign.ts +85 -0
  202. package/src/passport/index.ts +53 -0
  203. package/src/passport/key-rotate.ts +133 -0
  204. package/src/passport/schema.ts +198 -0
  205. package/src/passport/sign.ts +225 -0
  206. package/src/passport/tests/schema.pbt.test.ts +93 -0
  207. package/src/passport/tests/schema.test.ts +222 -0
  208. package/src/passport/vitest.config.ts +8 -0
  209. package/src/share/agent/api-catalog.ts +77 -0
  210. package/src/share/agent/capability.ts +68 -0
  211. package/src/share/agent/dns-aid.test.ts +74 -0
  212. package/src/share/agent/dns-aid.ts +53 -0
  213. package/src/share/agent/fleet-catalog.ts +149 -0
  214. package/src/share/agent/index.ts +29 -0
  215. package/src/share/agent/knowledge.ts +96 -0
  216. package/src/share/agent/manifest.ts +126 -0
  217. package/src/share/agent/mcp-card.ts +63 -0
  218. package/src/share/agent/omit-empty.ts +77 -0
  219. package/src/share/agent/openapi.ts +157 -0
  220. package/src/share/agent/proof.ts +61 -0
  221. package/src/share/attribution-display.ts +86 -0
  222. package/src/share/check-hints.ts +38 -0
  223. package/src/share/content/entity-id.ts +65 -0
  224. package/src/share/content/index.ts +16 -0
  225. package/src/share/content/merge.ts +86 -0
  226. package/src/share/content/resolve-field-path.ts +39 -0
  227. package/src/share/content/substitute-deep.ts +47 -0
  228. package/src/share/content-discipline/index.ts +14 -0
  229. package/src/share/content-discipline/parsers.ts +66 -0
  230. package/src/share/content-discipline/types.ts +118 -0
  231. package/src/share/content-reference.ts +333 -0
  232. package/src/share/counter-utils.ts +75 -0
  233. package/src/share/css-value-normalize.ts +48 -0
  234. package/src/share/dev-props-validator.ts +131 -0
  235. package/src/share/entitlement.ts +172 -0
  236. package/src/share/env.d.ts +73 -0
  237. package/src/share/feature-policy.ts +367 -0
  238. package/src/share/formula-eval.ts +364 -0
  239. package/src/share/fs/index.ts +96 -0
  240. package/src/share/i18n/localization.ts +57 -0
  241. package/src/share/image-provider.ts +314 -0
  242. package/src/share/index.ts +86 -0
  243. package/src/share/knowledge/claim.ts +129 -0
  244. package/src/share/knowledge/derivation.ts +55 -0
  245. package/src/share/knowledge/freshness.ts +127 -0
  246. package/src/share/knowledge/index.ts +23 -0
  247. package/src/share/knowledge/ledger.ts +222 -0
  248. package/src/share/knowledge/plan.ts +122 -0
  249. package/src/share/knowledge/source.ts +170 -0
  250. package/src/share/legal/translation-policy.ts +133 -0
  251. package/src/share/material-credits.ts +181 -0
  252. package/src/share/middleware/language-redirect.ts +49 -0
  253. package/src/share/offer-capacity.ts +149 -0
  254. package/src/share/onboarding-yaml/index.ts +162 -0
  255. package/src/share/page.ts +351 -0
  256. package/src/share/redirects.test.ts +62 -0
  257. package/src/share/redirects.ts +54 -0
  258. package/src/share/rfc0042-utils.ts +95 -0
  259. package/src/share/runtime-context.ts +70 -0
  260. package/src/share/schemas/claim-records.ts +69 -0
  261. package/src/share/schemas/claims.ts +150 -0
  262. package/src/share/schemas/effects.ts +212 -0
  263. package/src/share/schemas/features.ts +147 -0
  264. package/src/share/schemas/horizontal-align.ts +22 -0
  265. package/src/share/schemas/icon-color.ts +27 -0
  266. package/src/share/schemas/index.ts +43 -0
  267. package/src/share/schemas/live-photo.ts +53 -0
  268. package/src/share/schemas/material-credit.ts +292 -0
  269. package/src/share/schemas/media.ts +220 -0
  270. package/src/share/schemas/navigation.ts +74 -0
  271. package/src/share/schemas/page-base.ts +33 -0
  272. package/src/share/schemas/participant.ts +247 -0
  273. package/src/share/schemas/person.ts +24 -0
  274. package/src/share/schemas/print.ts +93 -0
  275. package/src/share/schemas/section-background.ts +106 -0
  276. package/src/share/schemas/section-body.ts +138 -0
  277. package/src/share/schemas/section-cards.ts +37 -0
  278. package/src/share/schemas/section-cta.ts +74 -0
  279. package/src/share/schemas/section-header.ts +77 -0
  280. package/src/share/schemas/section-image.ts +77 -0
  281. package/src/share/schemas/section-motion.ts +52 -0
  282. package/src/share/schemas/section-shell.ts +63 -0
  283. package/src/share/schemas/section-stats.ts +37 -0
  284. package/src/share/schemas/site-background.ts +78 -0
  285. package/src/share/schemas/standard-list-item.ts +38 -0
  286. package/src/share/scripts/external-links.ts +45 -0
  287. package/src/share/scripts/gsap-counter.ts +168 -0
  288. package/src/share/scripts/gsap-mountain-journey.ts +236 -0
  289. package/src/share/scripts/gsap-parallax.ts +50 -0
  290. package/src/share/scripts/gsap-reveal.ts +65 -0
  291. package/src/share/scripts/gsap-stagger.ts +54 -0
  292. package/src/share/scripts/index.ts +25 -0
  293. package/src/share/scripts/inline-number-animation.ts +120 -0
  294. package/src/share/scripts/lenis.ts +109 -0
  295. package/src/share/scripts/live-photos.ts +250 -0
  296. package/src/share/scripts/lordicon.ts +94 -0
  297. package/src/share/scripts/orchestrator.ts +138 -0
  298. package/src/share/scripts/scheduler.ts +53 -0
  299. package/src/share/scripts/video-player.ts +116 -0
  300. package/src/share/semantic/ai.ts +140 -0
  301. package/src/share/semantic/block-extraction.ts +52 -0
  302. package/src/share/semantic/block-extractors/index.ts +502 -0
  303. package/src/share/semantic/breadcrumbs.ts +120 -0
  304. package/src/share/semantic/build-page.ts +309 -0
  305. package/src/share/semantic/business-projection.ts +278 -0
  306. package/src/share/semantic/derived-prices-loader.ts +36 -0
  307. package/src/share/semantic/extract.ts +118 -0
  308. package/src/share/semantic/feed.ts +124 -0
  309. package/src/share/semantic/ids.ts +76 -0
  310. package/src/share/semantic/image-sitemap.ts +153 -0
  311. package/src/share/semantic/index.ts +36 -0
  312. package/src/share/semantic/jsonld/article.ts +47 -0
  313. package/src/share/semantic/jsonld/breadcrumb.ts +47 -0
  314. package/src/share/semantic/jsonld/collection-list.ts +30 -0
  315. package/src/share/semantic/jsonld/context.ts +46 -0
  316. package/src/share/semantic/jsonld/faq.ts +49 -0
  317. package/src/share/semantic/jsonld/initiative.ts +53 -0
  318. package/src/share/semantic/jsonld/organization.ts +101 -0
  319. package/src/share/semantic/jsonld/person.ts +71 -0
  320. package/src/share/semantic/jsonld/service.ts +80 -0
  321. package/src/share/semantic/jsonld/shared.ts +31 -0
  322. package/src/share/semantic/jsonld/types.ts +18 -0
  323. package/src/share/semantic/jsonld/webpage.ts +98 -0
  324. package/src/share/semantic/jsonld/website.ts +27 -0
  325. package/src/share/semantic/jsonld.ts +75 -0
  326. package/src/share/semantic/llms-policy.ts +74 -0
  327. package/src/share/semantic/llms.ts +394 -0
  328. package/src/share/semantic/markdown-hygiene.ts +68 -0
  329. package/src/share/semantic/markdown-twin-provenance.ts +242 -0
  330. package/src/share/semantic/models.ts +402 -0
  331. package/src/share/semantic/organization-profile.ts +160 -0
  332. package/src/share/semantic/output-projection.ts +131 -0
  333. package/src/share/semantic/page-builders/markdown-page.ts +84 -0
  334. package/src/share/semantic/page-markdown.ts +234 -0
  335. package/src/share/semantic/page-utils.ts +125 -0
  336. package/src/share/semantic/price-marker-resolver.ts +92 -0
  337. package/src/share/semantic/robots.ts +127 -0
  338. package/src/share/semantic/update-stamp.ts +136 -0
  339. package/src/share/shared-context.ts +177 -0
  340. package/src/share/string-utils.ts +19 -0
  341. package/src/share/tests/api-catalog.test.ts +97 -0
  342. package/src/share/tests/breadcrumbs.test.ts +126 -0
  343. package/src/share/tests/build-page-price-markers.test.ts +160 -0
  344. package/src/share/tests/capability.test.ts +107 -0
  345. package/src/share/tests/claim.test.ts +90 -0
  346. package/src/share/tests/content-ref-index.test.ts +328 -0
  347. package/src/share/tests/create-dev-props-validator.test.ts +38 -0
  348. package/src/share/tests/derivation.test.ts +39 -0
  349. package/src/share/tests/dev-props-validator.test.ts +88 -0
  350. package/src/share/tests/fleet-catalog.test.ts +134 -0
  351. package/src/share/tests/formula-eval.test.ts +619 -0
  352. package/src/share/tests/freshness.test.ts +60 -0
  353. package/src/share/tests/hero-section-numbering-parity.test.ts +51 -0
  354. package/src/share/tests/image-sitemap.test.ts +101 -0
  355. package/src/share/tests/jsonld-service.test.ts +282 -0
  356. package/src/share/tests/knowledge.test.ts +86 -0
  357. package/src/share/tests/ledger.test.ts +125 -0
  358. package/src/share/tests/llms-0789.test.ts +63 -0
  359. package/src/share/tests/manifest.test.ts +100 -0
  360. package/src/share/tests/markdown-twin-provenance.test.ts +108 -0
  361. package/src/share/tests/mcp-card.test.ts +57 -0
  362. package/src/share/tests/onboarding-yaml.test.ts +111 -0
  363. package/src/share/tests/ontology-role-by-cosmic-name.test.ts +28 -0
  364. package/src/share/tests/openapi.test.ts +116 -0
  365. package/src/share/tests/organization-jsonld.test.ts +74 -0
  366. package/src/share/tests/output-projection.test.ts +61 -0
  367. package/src/share/tests/plan.test.ts +97 -0
  368. package/src/share/tests/price-marker-resolver.test.ts +141 -0
  369. package/src/share/tests/proof.test.ts +72 -0
  370. package/src/share/tests/resolve-field-path.test.ts +52 -0
  371. package/src/share/tests/source.test.ts +81 -0
  372. package/src/share/tests/substitute-deep.test.ts +65 -0
  373. package/src/share/tests/text-normalize.pbt.test.ts +143 -0
  374. package/src/share/tests/text-normalize.test.ts +285 -0
  375. package/src/share/tests/twin-path.test.ts +43 -0
  376. package/src/share/text-normalize.ts +548 -0
  377. package/src/share/text-position.ts +19 -0
  378. package/src/share/types/hls-js-light.d.ts +16 -0
  379. package/src/share/visibility.ts +167 -0
  380. package/src/share/vitest.config.ts +8 -0
  381. package/src/share/wrap-inline-numbers.ts +139 -0
  382. package/src/surface/blueprint-schema.ts +314 -0
  383. package/src/surface/blueprint-types.ts +256 -0
  384. package/src/surface/blueprint.ts +300 -0
  385. package/src/surface/breaker.ts +57 -0
  386. package/src/surface/decision-composer.ts +156 -0
  387. package/src/surface/eligibility.ts +372 -0
  388. package/src/surface/evidence-records.ts +97 -0
  389. package/src/surface/fleet.ts +60 -0
  390. package/src/surface/geo.ts +170 -0
  391. package/src/surface/governance.ts +146 -0
  392. package/src/surface/index.ts +216 -0
  393. package/src/surface/io/index.ts +18 -0
  394. package/src/surface/io/surface-module-context-io.ts +60 -0
  395. package/src/surface/io/visibility-outcomes-io.ts +46 -0
  396. package/src/surface/module-context.ts +120 -0
  397. package/src/surface/substance.ts +161 -0
  398. package/src/surface/tests/eligibility.pbt.test.ts +137 -0
  399. package/src/surface/tests/eligibility.test.ts +302 -0
  400. package/src/surface/types.ts +289 -0
  401. package/src/surface/visibility.ts +57 -0
  402. package/src/surface/vitest.config.ts +8 -0
  403. package/tsconfig.json +17 -0
@@ -0,0 +1,49 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/faq.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not validate FAQ input data.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added FAQPage JSON-LD support.</item>
10
+ <item>Moved from app semantic/jsonld/faq to packages/share.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ import type { JsonLdContext } from "./context.ts";
15
+ import type { JsonLdNode } from "./types.ts";
16
+ import type { SemanticFaqEntry } from "../models.ts";
17
+
18
+ function buildQuestionNode(entry: SemanticFaqEntry): JsonLdNode {
19
+ return {
20
+ "@type": "Question",
21
+ name: entry.question,
22
+ acceptedAnswer: {
23
+ "@type": "Answer",
24
+ text: entry.answer,
25
+ },
26
+ };
27
+ }
28
+
29
+ export function buildFaqPageNode(context: JsonLdContext): JsonLdNode | null {
30
+ const entries = context.page.faqEntries;
31
+ if (!entries || entries.length === 0) {
32
+ return null;
33
+ }
34
+
35
+ return {
36
+ "@type": "FAQPage",
37
+ "@id": context.faqPageId,
38
+ mainEntity: entries.map((entry) => buildQuestionNode(entry)),
39
+ };
40
+ }
41
+
42
+ export function buildFaqNodes(context: JsonLdContext): JsonLdNode[] {
43
+ // RFC-0506: suppress FAQPage on ratgeber depth-1 article pages.
44
+ if (context.page.surfaceId === "ratgeber" && context.page.depth === 1) {
45
+ return [];
46
+ }
47
+ const faqPage = buildFaqPageNode(context);
48
+ return faqPage ? [faqPage] : [];
49
+ }
@@ -0,0 +1,53 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/initiative.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not validate initiative input data.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added initiatives list node plus initiative item-position wiring.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ import type { JsonLdContext } from "./context.ts";
14
+ import type { JsonLdNode } from "./types.ts";
15
+
16
+ function buildInitiativeNode(
17
+ context: JsonLdContext,
18
+ initiative: { name: string; summary: string },
19
+ ): JsonLdNode {
20
+ const { ids } = context;
21
+
22
+ return {
23
+ "@type": ["Thing", "Project"],
24
+ "@id": ids.initiative(initiative.name),
25
+ name: initiative.name,
26
+ description: initiative.summary,
27
+ };
28
+ }
29
+
30
+ export function buildInitiativeNodes(context: JsonLdContext): JsonLdNode[] {
31
+ return (context.page.initiatives ?? []).map((initiative) =>
32
+ buildInitiativeNode(context, initiative),
33
+ );
34
+ }
35
+
36
+ export function buildInitiativesListNode(context: JsonLdContext): JsonLdNode | null {
37
+ const { page, ids, initiativesListId } = context;
38
+
39
+ if (!page.initiatives?.length) {
40
+ return null;
41
+ }
42
+
43
+ return {
44
+ "@type": "ItemList",
45
+ "@id": initiativesListId,
46
+ itemListElement: page.initiatives.map((initiative, index) => ({
47
+ "@type": "ListItem",
48
+ position: index + 1,
49
+ item: { "@id": ids.initiative(initiative.name) },
50
+ name: initiative.name,
51
+ })),
52
+ };
53
+ }
@@ -0,0 +1,101 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/organization.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not validate input context.</item>
6
+ <item>Do not fetch external data.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>Added Organization JSON-LD generation.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ import type { JsonLdContext } from "./context.ts";
15
+ import type { JsonLdNode } from "./types.ts";
16
+
17
+ export function buildOrganizationNode(context: JsonLdContext): JsonLdNode {
18
+ const { page, ids } = context;
19
+
20
+ return {
21
+ // RFC-0242: a Bodenstation studio deployment overrides this to ["Organization", "ProfessionalService"].
22
+ "@type": page.organization.schemaType ?? ["Organization", "NGO"],
23
+ "@id": ids.organization,
24
+ name: page.organization.name,
25
+ legalName: page.organization.legalName,
26
+ description: page.organization.description,
27
+ url: page.organization.url,
28
+ email: page.organization.email,
29
+ ...(page.organization.foundingYear ? { foundingDate: page.organization.foundingYear } : {}),
30
+ ...(page.organization.registration ? { identifier: page.organization.registration } : {}),
31
+ ...(page.organization.founders?.length
32
+ ? {
33
+ founder: page.organization.founders.map((person) => ({
34
+ "@id": ids.person(person.name),
35
+ })),
36
+ }
37
+ : {}),
38
+ ...(page.organization.boardMembers?.length
39
+ ? {
40
+ member: page.organization.boardMembers.map((person) => ({
41
+ "@id": ids.person(person.name),
42
+ })),
43
+ }
44
+ : {}),
45
+ ...(page.organization.contactPoints?.length
46
+ ? {
47
+ contactPoint: page.organization.contactPoints.map((contactPoint) => ({
48
+ "@type": "ContactPoint",
49
+ contactType: contactPoint.contactType,
50
+ email: contactPoint.email,
51
+ })),
52
+ }
53
+ : {}),
54
+ ...(page.organization.address
55
+ ? {
56
+ address: {
57
+ "@type": "PostalAddress",
58
+ streetAddress: page.organization.address.streetAddress,
59
+ postalCode: page.organization.address.postalCode,
60
+ addressLocality: page.organization.address.addressLocality,
61
+ addressCountry: page.organization.address.addressCountry,
62
+ },
63
+ }
64
+ : {}),
65
+ ...(page.organization.areaServed?.length ? { areaServed: page.organization.areaServed } : {}),
66
+ ...(page.organization.location?.serviceArea?.length && !page.organization.areaServed?.length
67
+ ? { areaServed: page.organization.location.serviceArea }
68
+ : {}),
69
+ ...(page.organization.team?.length
70
+ ? {
71
+ // RFC-0148: public team members as schema.org employees.
72
+ employee: page.organization.team.map((member) => ({
73
+ "@type": "Person",
74
+ name: member.name,
75
+ ...(member.description ? { description: member.description } : {}),
76
+ })),
77
+ }
78
+ : {}),
79
+ ...(page.organization.offer?.prices?.length
80
+ ? {
81
+ // RFC-0147: project the canonical offer as schema.org Offer nodes.
82
+ // RFC-0745: emit priceCurrency from canonical source currency when available.
83
+ makesOffer: page.organization.offer.prices.map((price) => ({
84
+ "@type": "Offer",
85
+ name: price.label,
86
+ priceSpecification: {
87
+ "@type": "PriceSpecification",
88
+ price: price.amount,
89
+ },
90
+ ...(price.currency ? { priceCurrency: price.currency } : {}),
91
+ })),
92
+ }
93
+ : {}),
94
+ // RFC-0163: entity-grounding signals for Google Knowledge Graph + LLM disambiguation.
95
+ ...(page.organization.sameAs?.length ? { sameAs: page.organization.sameAs } : {}),
96
+ ...(page.organization.logo
97
+ ? { logo: { "@type": "ImageObject", url: page.organization.logo } }
98
+ : {}),
99
+ ...(page.organization.image ? { image: page.organization.image } : {}),
100
+ };
101
+ }
@@ -0,0 +1,71 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/person.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not validate person attributes.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added Person JSON-LD generation for organization members.</item>
10
+ <item>Moved from app semantic/jsonld/person to packages/share.</item>
11
+ <item>RFC-0512: emit address, knowsAbout, affiliation when present on SemanticPerson.</item>
12
+ </CHANGE_SUMMARY>
13
+ */
14
+
15
+ import type { JsonLdContext } from "./context.ts";
16
+ import type { SemanticPerson } from "../models.ts";
17
+ import type { JsonLdNode } from "./types.ts";
18
+
19
+ function buildPersonNode(context: JsonLdContext, person: SemanticPerson): JsonLdNode {
20
+ const { ids } = context;
21
+
22
+ return {
23
+ "@type": "Person",
24
+ "@id": ids.person(person.name),
25
+ name: person.name,
26
+ ...(person.role ? { jobTitle: person.role } : {}),
27
+ ...(person.description ? { description: person.description } : {}),
28
+ // RFC-0200: lifespan, portrait, social grounding, and the profile-page URL.
29
+ ...(person.birthDate ? { birthDate: person.birthDate } : {}),
30
+ ...(person.deathDate ? { deathDate: person.deathDate } : {}),
31
+ ...(person.image ? { image: person.image } : {}),
32
+ ...(person.sameAs?.length ? { sameAs: person.sameAs } : {}),
33
+ ...(person.profileUrl ? { url: person.profileUrl } : {}),
34
+ // RFC-0512: extended Person fields for team profile pages.
35
+ ...(person.address
36
+ ? {
37
+ address: {
38
+ "@type": "PostalAddress",
39
+ addressLocality: person.address.addressLocality,
40
+ ...(person.address.addressRegion
41
+ ? { addressRegion: person.address.addressRegion }
42
+ : {}),
43
+ addressCountry: person.address.addressCountry,
44
+ },
45
+ }
46
+ : {}),
47
+ ...(person.knowsAbout?.length ? { knowsAbout: person.knowsAbout } : {}),
48
+ ...(person.affiliation
49
+ ? {
50
+ affiliation: {
51
+ "@type": "Organization",
52
+ name: person.affiliation.name,
53
+ ...(person.affiliation.url ? { url: person.affiliation.url } : {}),
54
+ },
55
+ }
56
+ : {}),
57
+ worksFor: { "@id": ids.organization },
58
+ };
59
+ }
60
+
61
+ export function buildPersonNodes(context: JsonLdContext): JsonLdNode[] {
62
+ const people = [
63
+ ...(context.page.organization.founders ?? []),
64
+ ...(context.page.organization.boardMembers ?? []),
65
+ ...(context.page.people ?? []),
66
+ ];
67
+
68
+ const uniquePeople = Array.from(new Map(people.map((person) => [person.name, person])).values());
69
+
70
+ return uniquePeople.map((person) => buildPersonNode(context, person));
71
+ }
@@ -0,0 +1,80 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/service.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not validate service input data.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added Service JSON-LD generation.</item>
10
+ <item>Moved from app semantic/jsonld/service to packages/share.</item>
11
+ <item>RFC-0498: extend buildServiceNodes to gate Service emission by per-depth policy for all surface depths (website-local depth-1, website-service depth-1, website-local depth-5).</item>
12
+ </CHANGE_SUMMARY>
13
+ */
14
+
15
+ import type { JsonLdContext } from "./context.ts";
16
+ import type { JsonLdNode } from "./types.ts";
17
+ import type { SemanticService } from "../models.ts";
18
+
19
+ function buildServiceNode(context: JsonLdContext, service: SemanticService): JsonLdNode {
20
+ const { ids } = context;
21
+
22
+ return {
23
+ "@type": "Service",
24
+ "@id": ids.service(service.name),
25
+ name: service.name,
26
+ ...(service.description ? { description: service.description } : {}),
27
+ provider: { "@id": ids.organization },
28
+ };
29
+ }
30
+
31
+ /**
32
+ * RFC-0492: build the industry-specific Service node for depth-1 `website-local` pages.
33
+ * Replaces the organization-level Service nodes with a single node carrying
34
+ * `serviceType`, `audience`, and `provider`.
35
+ */
36
+ function buildIndustryServiceNode(context: JsonLdContext): JsonLdNode | null {
37
+ const { page, ids } = context;
38
+ if (!page.industryService) return null;
39
+
40
+ return {
41
+ "@type": "Service",
42
+ "@id": `${ids.organization}/service-industry`,
43
+ name: page.industryService.serviceType,
44
+ serviceType: page.industryService.serviceType,
45
+ provider: { "@id": ids.organization },
46
+ ...(page.industryService.audience
47
+ ? { audience: { "@type": "Audience", name: page.industryService.audience } }
48
+ : {}),
49
+ ...(page.industryService.description ? { description: page.industryService.description } : {}),
50
+ ...(page.industryService.areaServed
51
+ ? { areaServed: { "@type": "City", name: page.industryService.areaServed } }
52
+ : {}),
53
+ };
54
+ }
55
+
56
+ /**
57
+ * RFC-0492/RFC-0498: for surface pages where Service is required (website-local depth-1,
58
+ * website-service depth-1, website-local depth-5), suppress organization-level Service nodes
59
+ * and emit a single industry-specific Service node instead.
60
+ * For surface pages where Service is prohibited (depth-0, 2, 3, 4), emit no Service nodes.
61
+ * For non-surface pages, emit organization-level Service nodes as before.
62
+ */
63
+ export function buildServiceNodes(context: JsonLdContext): JsonLdNode[] {
64
+ const { page } = context;
65
+
66
+ // RFC-0498: surface pages with industryService get the industry-specific Service node.
67
+ // This covers website-local depth-1, website-service depth-1, and website-local depth-5.
68
+ if (page.industryService) {
69
+ const industryNode = buildIndustryServiceNode(context);
70
+ return industryNode ? [industryNode] : [];
71
+ }
72
+
73
+ // RFC-0498: surface pages without industryService and where Service is prohibited emit no Service nodes.
74
+ // Depth-0, 2, 3, 4 in website-local have Service in prohibitedTypes.
75
+ if (page.surfaceId && page.depth !== undefined && page.depth !== 1 && page.depth !== 5) {
76
+ return [];
77
+ }
78
+
79
+ return (page.organization.services ?? []).map((service) => buildServiceNode(context, service));
80
+ }
@@ -0,0 +1,31 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/shared.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not modify node structure.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added graph deduplication utility keyed by @id.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ import type { JsonLdNode } from "./types.ts";
14
+
15
+ export function dedupeGraph(nodes: JsonLdNode[]): JsonLdNode[] {
16
+ const seenIds = new Set<string>();
17
+
18
+ return nodes.filter((node) => {
19
+ const nodeId = typeof node["@id"] === "string" ? node["@id"] : null;
20
+ if (!nodeId) {
21
+ return true;
22
+ }
23
+
24
+ if (seenIds.has(nodeId)) {
25
+ return false;
26
+ }
27
+
28
+ seenIds.add(nodeId);
29
+ return true;
30
+ });
31
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/types.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.</purpose>
4
+ <non-goals>
5
+ <item>Do not implement JSON-LD processing logic.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Moved from app semantic/jsonld/types to packages/share.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ export type JsonLdNode = Record<string, unknown>;
14
+
15
+ export type JsonLdDocument = {
16
+ "@context": "https://schema.org";
17
+ "@graph": JsonLdNode[];
18
+ };
@@ -0,0 +1,98 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/webpage.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.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added type expansion for donate/faq/news page variants.</item>
10
+ <item>RFC-0372: speakable reads from unified page.blocks instead of answerBlocks.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ import type { JsonLdContext } from "./context.ts";
15
+ import type { SemanticPageModel } from "../models.ts";
16
+ import type { JsonLdNode } from "./types.ts";
17
+
18
+ function getWebPageTypes(page: SemanticPageModel): string[] {
19
+ switch (page.type) {
20
+ case "about":
21
+ return ["WebPage", "AboutPage"];
22
+ case "projects":
23
+ return ["WebPage", "CollectionPage"];
24
+ case "openSource":
25
+ return ["WebPage", "CollectionPage"];
26
+ case "donationContact":
27
+ return ["WebPage", "ContactPage"];
28
+ case "person":
29
+ // RFC-0200: a per-member profile page is a schema.org ProfilePage.
30
+ return ["WebPage", "ProfilePage"];
31
+ case "collection":
32
+ // RFC-0490: a pillar hub page is a schema.org CollectionPage.
33
+ return ["WebPage", "CollectionPage"];
34
+ default:
35
+ return ["WebPage"];
36
+ }
37
+ }
38
+
39
+ export function buildWebPageNode(context: JsonLdContext): JsonLdNode {
40
+ const { page, ids, webpageId, breadcrumbId, initiativesListId, servicesListId } = context;
41
+
42
+ return {
43
+ "@type": getWebPageTypes(page),
44
+ "@id": webpageId,
45
+ url: page.url,
46
+ name: page.title,
47
+ headline: page.heading ?? page.title,
48
+ description: page.description,
49
+ inLanguage: page.lang,
50
+ isPartOf: { "@id": ids.website },
51
+ about: { "@id": ids.organization },
52
+ publisher: { "@id": ids.organization },
53
+ ...(page.type === "about" ? { mainEntity: { "@id": ids.organization } } : {}),
54
+ ...(page.type === "projects" && page.initiatives?.length
55
+ ? { mainEntity: { "@id": initiativesListId } }
56
+ : {}),
57
+ // RFC-0200: a profile page's subject is the profiled Person (attached to page.people).
58
+ // RFC-0512: for team profile pages, prefer the extended Person/SoftwareApplication node
59
+ // from extraGraphNodes as mainEntity — this covers AI-agent profiles (where page.people
60
+ // is empty) and human profiles (where the extended Person node has a different @id).
61
+ ...(page.type === "person" && page.extraGraphNodes?.[0]
62
+ ? { mainEntity: { "@id": page.extraGraphNodes[0]["@id"] } }
63
+ : page.type === "person" && page.people?.[0]
64
+ ? { mainEntity: { "@id": ids.person(page.people[0].name) } }
65
+ : {}),
66
+ ...(page.organization.services?.length && servicesListId
67
+ ? { mentions: { "@id": servicesListId } }
68
+ : {}),
69
+ ...(page.breadcrumbs.length > 1 ? { breadcrumb: { "@id": breadcrumbId } } : {}),
70
+ ...(page.lead ? { abstract: page.lead } : {}),
71
+ // RFC-0163: primary image of the page (consumed from RFC-0162 primaryImage).
72
+ ...(page.primaryImage
73
+ ? {
74
+ primaryImageOfPage: {
75
+ "@type": "ImageObject",
76
+ url: page.primaryImage.url,
77
+ ...(page.primaryImage.width ? { width: page.primaryImage.width } : {}),
78
+ ...(page.primaryImage.height ? { height: page.primaryImage.height } : {}),
79
+ },
80
+ }
81
+ : {}),
82
+ // RFC-0227: link credited materials from the page entity for licensable-images treatment.
83
+ ...(page.materialCreditAtIds?.length
84
+ ? { associatedMedia: page.materialCreditAtIds.map((id) => ({ "@id": id })) }
85
+ : {}),
86
+ // RFC-0163: speakable answer surface for voice assistants / AI Overviews. The
87
+ // page heading and lead are always-present, extractable answer anchors.
88
+ // RFC-0372: reads from unified page.blocks (any block with a heading counts as speakable).
89
+ ...(page.blocks.some((b) => b.heading)
90
+ ? {
91
+ speakable: {
92
+ "@type": "SpeakableSpecification",
93
+ cssSelector: page.lead ? ["h1", ".section-header__lead"] : ["h1"],
94
+ },
95
+ }
96
+ : {}),
97
+ };
98
+ }
@@ -0,0 +1,27 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Maintains packages/share/src/semantic/jsonld/website.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.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>Added WebSite JSON-LD generation.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ import type { JsonLdContext } from "./context.ts";
14
+ import type { JsonLdNode } from "./types.ts";
15
+
16
+ export function buildWebSiteNode(context: JsonLdContext): JsonLdNode {
17
+ const { page, ids } = context;
18
+
19
+ return {
20
+ "@type": "WebSite",
21
+ "@id": ids.website,
22
+ url: page.organization.url,
23
+ name: page.organization.name,
24
+ inLanguage: page.lang,
25
+ publisher: { "@id": ids.organization },
26
+ };
27
+ }
@@ -0,0 +1,75 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Assembles a complete JSON-LD document from a SemanticPageModel by composing all entity-specific nodes.</purpose>
4
+ <non-goals>
5
+ <item>Do not handle raw content parsing.</item>
6
+ <item>Do not manage external API interactions.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>Added initiative list graph node, breadcrumb fallback, and shared dedupe utility.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ import { buildArticleNode } from "./jsonld/article.ts";
15
+ import { buildBreadcrumbNode } from "./jsonld/breadcrumb.ts";
16
+ import { buildCollectionListNode } from "./jsonld/collection-list.ts";
17
+ import { createJsonLdContext } from "./jsonld/context.ts";
18
+ import { buildFaqNodes } from "./jsonld/faq.ts";
19
+ import { buildInitiativesListNode, buildInitiativeNodes } from "./jsonld/initiative.ts";
20
+ import { buildOrganizationNode } from "./jsonld/organization.ts";
21
+ import { buildPersonNodes } from "./jsonld/person.ts";
22
+ import { buildServiceNodes } from "./jsonld/service.ts";
23
+ import { dedupeGraph } from "./jsonld/shared.ts";
24
+ import type { JsonLdDocument } from "./jsonld/types.ts";
25
+ import { buildWebPageNode } from "./jsonld/webpage.ts";
26
+ import { buildWebSiteNode } from "./jsonld/website.ts";
27
+ import type { SemanticPageModel } from "./models.ts";
28
+
29
+ export type { JsonLdDocument } from "./jsonld/types.ts";
30
+ export type { JsonLdContext } from "./jsonld/context.ts";
31
+
32
+ export function buildJsonLd(page: SemanticPageModel): JsonLdDocument {
33
+ const context = createJsonLdContext(page);
34
+ const articleNode = buildArticleNode(context);
35
+ const breadcrumbNode = buildBreadcrumbNode(context);
36
+ const collectionListNode = buildCollectionListNode(context);
37
+ const initiativesListNode = buildInitiativesListNode(context);
38
+ // RFC-0498: suppress the org-level services ItemList for all surface pages.
39
+ // Surface pages emit industry-specific Service nodes (depth-1, website-service, depth-5)
40
+ // or no Service nodes at all (depth-0, 2, 3, 4) — never org-level services.
41
+ const suppressOrgServices = page.surfaceId !== undefined;
42
+ const servicesListNode =
43
+ !suppressOrgServices && context.servicesListId
44
+ ? {
45
+ "@type": "ItemList" as const,
46
+ "@id": context.servicesListId,
47
+ itemListElement: (context.page.organization.services ?? []).map((service, index) => ({
48
+ "@type": "ListItem",
49
+ position: index + 1,
50
+ item: { "@id": context.ids.service(service.name) },
51
+ name: service.name,
52
+ })),
53
+ }
54
+ : null;
55
+
56
+ return {
57
+ "@context": "https://schema.org",
58
+ "@graph": dedupeGraph([
59
+ buildOrganizationNode(context),
60
+ buildWebSiteNode(context),
61
+ buildWebPageNode(context),
62
+ ...buildPersonNodes(context),
63
+ ...buildServiceNodes(context),
64
+ ...buildFaqNodes(context),
65
+ ...buildInitiativeNodes(context),
66
+ ...(initiativesListNode ? [initiativesListNode] : []),
67
+ ...(servicesListNode ? [servicesListNode] : []),
68
+ ...(collectionListNode ? [collectionListNode] : []),
69
+ ...(articleNode ? [articleNode] : []),
70
+ ...(breadcrumbNode ? [breadcrumbNode] : []),
71
+ // RFC-0512: extra nodes from team profile pages (SoftwareApplication, CollectionPage).
72
+ ...(page.extraGraphNodes ?? []),
73
+ ]),
74
+ };
75
+ }