@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,364 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>RFC-0570 formula evaluation module — extracts numeric values from field strings,
4
+ scans text for =(...) formula expressions, and evaluates arithmetic over content references
5
+ using a sandboxed math parser (expr-eval). RFC-0729: pipe syntax for post-evaluation formatting,
6
+ plugin-registration formatter registry, and money formatter built on Intl.NumberFormat.</purpose>
7
+ <non-goals>
8
+ <item>Do not resolve content references directly — use resolveReference from @warpgogol/werkstatt-shared/share/content-reference.</item>
9
+ <item>Do not support non-arithmetic expressions (string concatenation, conditionals, date math).</item>
10
+ <item>Do not add formatters other than money — date, percent, unit formatters are follow-up RFCs.</item>
11
+ </non-goals>
12
+ </MODULE_CONTRACT>
13
+ <CHANGE_SUMMARY>
14
+ <item>RFC-0570: Initial implementation of formula evaluation for content references.</item>
15
+ <item>RFC-0729: Add pipe syntax for post-evaluation formatting, formatter registry, and money formatter.</item>
16
+ <item>RFC-0730: Add formatRecurrence utility for ISO 8601 duration → locale-specific suffix mapping.</item>
17
+ <item>RFC-0731: Add sourceRef optional parameter for this. self-reference expansion in formulas.</item>
18
+ </CHANGE_SUMMARY>
19
+ */
20
+
21
+ import { Parser } from "expr-eval";
22
+ import type { ContentRefIndex, SourceRef } from "./content-reference.ts";
23
+ import { resolveReference } from "./content-reference.ts";
24
+
25
+ export interface FormulaResolution {
26
+ value: string;
27
+ resolved: boolean;
28
+ error?: string;
29
+ }
30
+
31
+ export interface FormulaMatch {
32
+ start: number;
33
+ end: number;
34
+ expression: string;
35
+ }
36
+
37
+ export type PipeFormatter = (
38
+ value: number,
39
+ params: Record<string, string>,
40
+ context: PipeFormatterContext,
41
+ ) => string;
42
+
43
+ export interface PipeFormatterContext {
44
+ lang: string;
45
+ defaultLang: string;
46
+ }
47
+
48
+ const pipeFormatterRegistry = new Map<string, PipeFormatter>();
49
+
50
+ export function registerPipeFormatter(name: string, formatter: PipeFormatter): void {
51
+ pipeFormatterRegistry.set(name, formatter);
52
+ }
53
+
54
+ export function getPipeFormatter(name: string): PipeFormatter | undefined {
55
+ return pipeFormatterRegistry.get(name);
56
+ }
57
+
58
+ registerPipeFormatter("money", (value, params, context) => {
59
+ const currency = params.currency ?? "EUR";
60
+ const locale = params.locale ?? context.lang;
61
+ const targetCurrency = params.targetCurrency;
62
+ const rate = params.rate ? Number(params.rate) : undefined;
63
+
64
+ let amount = value;
65
+ let code = currency;
66
+ if (targetCurrency && rate !== undefined && Number.isFinite(rate)) {
67
+ amount = value * rate;
68
+ code = targetCurrency;
69
+ }
70
+
71
+ return new Intl.NumberFormat(locale, {
72
+ style: "currency",
73
+ currency: code,
74
+ currencyDisplay: "narrowSymbol",
75
+ minimumFractionDigits: 0,
76
+ maximumFractionDigits: 2,
77
+ }).format(amount);
78
+ });
79
+
80
+ const parser = new Parser();
81
+
82
+ const REF_IN_FORMULA_PATTERN = /[a-z][a-z-]*\.[a-z0-9-/]+\.[a-zA-Z0-9_.-]+/g;
83
+ const THIS_IN_FORMULA_PATTERN = /this\.[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*/g;
84
+ const THIS_PREFIX = "this.";
85
+
86
+ const FORMULA_PREFIX = "=(";
87
+
88
+ /**
89
+ * Extracts a numeric value from a field string.
90
+ * Handles: leading number, thin-space (\u202f) / period / comma thousands separators,
91
+ * comma decimal separator (German), negative numbers.
92
+ * Strips non-numeric prefix/suffix.
93
+ * Returns null for non-numeric values.
94
+ */
95
+ export function extractNumeric(value: unknown): number | null {
96
+ if (value === null || value === undefined) return null;
97
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
98
+ if (typeof value === "boolean") return null;
99
+ if (typeof value !== "string") return null;
100
+
101
+ const trimmed = value.trim();
102
+ if (trimmed === "") return null;
103
+
104
+ // Remove currency symbols, units, and non-numeric prefix/suffix
105
+ // Keep: digits, period, comma, minus, plus, thin space, regular space
106
+ const cleaned = trimmed
107
+ .replace(/^[^0-9\-+]+/, "") // strip non-numeric prefix
108
+ .replace(/[^0-9.,\-\u202f\s+]+$/, "") // strip non-numeric suffix
109
+ .replace(/\u202f/g, "") // thin space thousands separator
110
+ .replace(/\s/g, "") // regular space thousands separator
111
+ .trim();
112
+
113
+ if (cleaned === "" || cleaned === "-" || cleaned === "+") return null;
114
+
115
+ // Detect format: if both period and comma are present, period=thousands, comma=decimal (German)
116
+ // If only comma is present, it's a decimal separator (German)
117
+ // If only period is present, it's a decimal separator (English)
118
+ let normalized: string;
119
+ const hasPeriod = cleaned.includes(".");
120
+ const hasComma = cleaned.includes(",");
121
+
122
+ if (hasPeriod && hasComma) {
123
+ // Period = thousands, comma = decimal (German format: 1.040,50)
124
+ normalized = cleaned.replace(/\./g, "").replace(",", ".");
125
+ } else if (hasComma) {
126
+ // Comma = decimal (German format: 70,50)
127
+ normalized = cleaned.replace(",", ".");
128
+ } else if (hasPeriod) {
129
+ // Period only: could be thousands (German: 1.040) or decimal (English: 70.50)
130
+ // Heuristic: if the period is followed by exactly 3 digits and nothing after,
131
+ // treat as thousands separator. Otherwise treat as decimal.
132
+ const periodIndex = cleaned.lastIndexOf(".");
133
+ const afterPeriod = cleaned.slice(periodIndex + 1);
134
+ if (/^\d{3}$/.test(afterPeriod) && cleaned.indexOf(".") === periodIndex) {
135
+ // Single period followed by exactly 3 digits → thousands separator
136
+ normalized = cleaned.replace(/\./g, "");
137
+ } else {
138
+ normalized = cleaned;
139
+ }
140
+ } else {
141
+ normalized = cleaned;
142
+ }
143
+
144
+ const result = Number.parseFloat(normalized);
145
+ return Number.isFinite(result) ? result : null;
146
+ }
147
+
148
+ /**
149
+ * Scans text for =(...) formula patterns using a paren-depth counter
150
+ * to handle nested parentheses: =(a + (b * c)) is matched correctly.
151
+ * Returns an array of { start, end, expression } for each formula found.
152
+ * Fast path: returns [] immediately if text does not contain "=(".
153
+ */
154
+ export function scanFormulas(text: string): FormulaMatch[] {
155
+ if (!text.includes(FORMULA_PREFIX)) return [];
156
+
157
+ const matches: FormulaMatch[] = [];
158
+ let i = 0;
159
+
160
+ while (i < text.length - 1) {
161
+ // Look for "=(" prefix
162
+ if (text[i] === "=" && text[i + 1] === "(") {
163
+ const start = i;
164
+ let depth = 1;
165
+ let j = i + 2;
166
+
167
+ while (j < text.length && depth > 0) {
168
+ if (text[j] === "(") {
169
+ depth++;
170
+ } else if (text[j] === ")") {
171
+ depth--;
172
+ }
173
+ j++;
174
+ }
175
+
176
+ if (depth === 0) {
177
+ const expression = text.slice(start + 2, j - 1);
178
+ matches.push({ start, end: j, expression });
179
+ i = j;
180
+ } else {
181
+ // Unbalanced parens — skip this match
182
+ i = start + 1;
183
+ }
184
+ } else {
185
+ i++;
186
+ }
187
+ }
188
+
189
+ return matches;
190
+ }
191
+
192
+ /**
193
+ * Evaluates a formula expression after substituting all content references.
194
+ * Uses expr-eval for sandboxed arithmetic.
195
+ * RFC-0729: Supports pipe syntax `arithmetic | formatter key=value` for post-evaluation formatting.
196
+ * Returns { value, resolved, error? } with REF-06..10 error codes.
197
+ */
198
+ export function resolveFormula(
199
+ index: ContentRefIndex,
200
+ expression: string,
201
+ lang: string,
202
+ defaultLang: string,
203
+ sourceRef?: SourceRef,
204
+ ): FormulaResolution {
205
+ const pipeIndex = expression.indexOf("|");
206
+ const hasPipe = pipeIndex !== -1;
207
+ const arithmeticExpr = hasPipe ? expression.slice(0, pipeIndex) : expression;
208
+ const formatterSpec = hasPipe ? expression.slice(pipeIndex + 1) : "";
209
+
210
+ // RFC-0731: Expand this. references before REF_IN_FORMULA_PATTERN matching
211
+ let expandedArithmeticExpr = arithmeticExpr;
212
+ if (sourceRef && arithmeticExpr.includes(THIS_PREFIX)) {
213
+ const thisPattern = new RegExp(THIS_IN_FORMULA_PATTERN.source, "g");
214
+ expandedArithmeticExpr = expandedArithmeticExpr.replace(thisPattern, (match) => {
215
+ const fieldPath = match.slice(THIS_PREFIX.length);
216
+ return `${sourceRef.collection}.${sourceRef.file}.${fieldPath}`;
217
+ });
218
+ } else if (!sourceRef && arithmeticExpr.includes(THIS_PREFIX)) {
219
+ return {
220
+ value: "",
221
+ resolved: false,
222
+ error: `REF-12: this. reference used without sourceRef context`,
223
+ };
224
+ }
225
+
226
+ // Find all content references in the arithmetic expression
227
+ const refs: string[] = [];
228
+ const refPattern = new RegExp(REF_IN_FORMULA_PATTERN.source, "g");
229
+ let match: RegExpExecArray | null;
230
+
231
+ while ((match = refPattern.exec(expandedArithmeticExpr)) !== null) {
232
+ const candidate = match[0];
233
+ const collectionMatch = candidate.match(/^([a-z][a-z-]*)\./);
234
+ if (!collectionMatch) continue;
235
+ const collection = collectionMatch[1];
236
+ if (!index.entries[collection]) continue;
237
+ refs.push(candidate);
238
+ }
239
+
240
+ // Resolve each reference and extract numeric value
241
+ let substitutedExpression = expandedArithmeticExpr;
242
+
243
+ for (const ref of refs) {
244
+ const result = resolveReference(index, ref, lang, defaultLang);
245
+ if (!result.resolved) {
246
+ return {
247
+ value: "",
248
+ resolved: false,
249
+ error: `REF-06: Formula reference unresolved: ${ref}`,
250
+ };
251
+ }
252
+
253
+ // RFC-0723: If the expression is a single reference (no arithmetic, no pipe),
254
+ // return the string value directly — enables =(ref) for string interpolation.
255
+ // This must be checked BEFORE extractNumeric to avoid extracting numbers from
256
+ // string values like "Fertig in 12 Werktagen" → 12.
257
+ if (!hasPipe && refs.length === 1 && expandedArithmeticExpr.trim() === ref) {
258
+ return {
259
+ value: String(result.value),
260
+ resolved: true,
261
+ };
262
+ }
263
+
264
+ const numeric = extractNumeric(result.value);
265
+ if (numeric === null) {
266
+ return {
267
+ value: "",
268
+ resolved: false,
269
+ error: `REF-07: Formula operand not numeric: ${ref} resolved to "${String(result.value)}"`,
270
+ };
271
+ }
272
+
273
+ substitutedExpression = substitutedExpression.replaceAll(ref, String(numeric));
274
+ }
275
+
276
+ // Evaluate the arithmetic expression
277
+ try {
278
+ const expr = parser.parse(substitutedExpression);
279
+ const result = expr.evaluate();
280
+
281
+ if (typeof result !== "number" || !Number.isFinite(result)) {
282
+ // Check for division by zero (expr-eval returns Infinity, not an error)
283
+ if (typeof result === "number" && !Number.isFinite(result)) {
284
+ return {
285
+ value: "",
286
+ resolved: false,
287
+ error: `REF-09: Formula division by zero: ${expression}`,
288
+ };
289
+ }
290
+ return {
291
+ value: "",
292
+ resolved: false,
293
+ error: `REF-08: Formula produced non-numeric result: ${expression}`,
294
+ };
295
+ }
296
+
297
+ // No pipe — return numeric string as before (RFC-0570, RFC-0723)
298
+ if (!hasPipe) {
299
+ return {
300
+ value: String(result),
301
+ resolved: true,
302
+ };
303
+ }
304
+
305
+ // RFC-0729: Apply pipe formatter
306
+ const specTokens = formatterSpec
307
+ .trim()
308
+ .split(/\s+/)
309
+ .filter((t) => t.length > 0);
310
+ const formatterName = specTokens[0] ?? "";
311
+ const formatter = getPipeFormatter(formatterName);
312
+ if (!formatter) {
313
+ return {
314
+ value: "",
315
+ resolved: false,
316
+ error: `REF-10: Unknown pipe formatter: ${formatterName}`,
317
+ };
318
+ }
319
+
320
+ const params: Record<string, string> = {};
321
+ for (const token of specTokens.slice(1)) {
322
+ const eqIndex = token.indexOf("=");
323
+ if (eqIndex > 0) {
324
+ params[token.slice(0, eqIndex)] = token.slice(eqIndex + 1);
325
+ }
326
+ }
327
+
328
+ const context: PipeFormatterContext = { lang, defaultLang };
329
+ const formatted = formatter(result, params, context);
330
+
331
+ return {
332
+ value: formatted,
333
+ resolved: true,
334
+ };
335
+ } catch (err) {
336
+ const message = err instanceof Error ? err.message : String(err);
337
+
338
+ if (message.includes("divide") && message.includes("zero")) {
339
+ return {
340
+ value: "",
341
+ resolved: false,
342
+ error: `REF-09: Formula division by zero: ${expression}`,
343
+ };
344
+ }
345
+
346
+ return {
347
+ value: "",
348
+ resolved: false,
349
+ error: `REF-08: Formula syntax error: ${message}`,
350
+ };
351
+ }
352
+ }
353
+
354
+ const RECURRENCE_SUFFIXES: Record<string, Record<string, string>> = {
355
+ P1M: { de: "/ Monat", uk: "/ місяць" },
356
+ P1Y: { de: "/ Jahr", uk: "/ рік" },
357
+ };
358
+
359
+ export function formatRecurrence(recurrence: string | undefined, lang: string): string {
360
+ if (!recurrence || recurrence.trim() === "") return "";
361
+ const suffixes = RECURRENCE_SUFFIXES[recurrence.trim()];
362
+ if (!suffixes) return "";
363
+ return suffixes[lang] ?? suffixes["de"] ?? "";
364
+ }
@@ -0,0 +1,96 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Canonical Node build-time filesystem primitives shared across every packages/os/* command and validator: recursive directory collection, existence checks, JSON reads.</purpose>
4
+ <non-goals>
5
+ <item>Do not write files — all writes flow through writeFileAtomic / WorkspaceIO (RFC-0258/RFC-0267).</item>
6
+ <item>Do not run in the browser — this module is server-only (Node fs/promises).</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>RFC-0303: extracted as the single canonical home for fs helpers previously duplicated across packages/os/*.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ // Server-only. Do not import from browser/client scripts.
15
+
16
+ import { readdir, readFile, stat } from "node:fs/promises";
17
+ import { join } from "node:path";
18
+ import { parse as yamlParse } from "yaml";
19
+
20
+ export interface CollectFilesOptions {
21
+ /** Only include files whose name ends with one of these (e.g. [".md", ".astro"]). Omit = all files. */
22
+ extensions?: string[];
23
+ /** Return true to skip a directory or file entry by name. Default: name starts with "-" or "old-". */
24
+ ignore?: (name: string) => boolean;
25
+ /** Include directory paths in the result too. Default false (files only). */
26
+ withDirs?: boolean;
27
+ }
28
+
29
+ function defaultIgnore(name: string): boolean {
30
+ return name.startsWith("-") || name.startsWith("old-");
31
+ }
32
+
33
+ /**
34
+ * Recursively collects file paths under `root`.
35
+ * Swallows readdir errors per-branch by returning no entries for that branch.
36
+ */
37
+ export async function collectFiles(
38
+ root: string,
39
+ options: CollectFilesOptions = {},
40
+ ): Promise<string[]> {
41
+ const { extensions, ignore = defaultIgnore, withDirs = false } = options;
42
+ const results: string[] = [];
43
+
44
+ async function walk(dir: string): Promise<void> {
45
+ let entries;
46
+ try {
47
+ entries = await readdir(dir, { withFileTypes: true });
48
+ } catch {
49
+ return;
50
+ }
51
+
52
+ for (const entry of entries) {
53
+ if (ignore(entry.name)) continue;
54
+
55
+ const full = join(dir, entry.name);
56
+
57
+ if (entry.isDirectory()) {
58
+ if (withDirs) results.push(full);
59
+ await walk(full);
60
+ continue;
61
+ }
62
+
63
+ if (!entry.isFile()) continue;
64
+
65
+ if (extensions && !extensions.some((ext) => entry.name.endsWith(ext))) continue;
66
+
67
+ results.push(full);
68
+ }
69
+ }
70
+
71
+ await walk(root);
72
+ return results;
73
+ }
74
+
75
+ /**
76
+ * Best-effort existence check for a path (file or directory).
77
+ * Returns `false` on any filesystem error (ENOENT, EACCES, etc.).
78
+ */
79
+ export async function fileExists(path: string): Promise<boolean> {
80
+ try {
81
+ await stat(path);
82
+ return true;
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
87
+
88
+ export async function readJsonFile<T = unknown>(path: string): Promise<T> {
89
+ const raw = await readFile(path, "utf-8");
90
+ return JSON.parse(raw) as T;
91
+ }
92
+
93
+ export async function readYamlFile<T = unknown>(path: string): Promise<T> {
94
+ const raw = await readFile(path, "utf-8");
95
+ return yamlParse(raw) as T;
96
+ }
@@ -0,0 +1,57 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>
4
+ App-agnostic i18n helpers: a factory that creates type-safe localization utilities
5
+ bound to any language mapping. Each app supplies its own mapping constant; this module
6
+ provides the reusable logic without hard-coding any language list.
7
+ </purpose>
8
+ <non-goals>
9
+ <item>Do not hard-code any language list — each app owns its own LANGUAGE_MAPPING constant.</item>
10
+ <item>Do not import from apps/* or astro:content.</item>
11
+ <item>Do not manage language preferences, user sessions, or UI rendering.</item>
12
+ </non-goals>
13
+ </MODULE_CONTRACT>
14
+ <CHANGE_SUMMARY>
15
+ <item>Initial extraction from app utils/localization into packages/share.</item>
16
+ <item>Refactored to factory pattern so LANGUAGE_MAPPING stays in each app and the shared logic is truly app-agnostic.</item>
17
+ </CHANGE_SUMMARY>
18
+ */
19
+
20
+ // [DNA-02][DNA-10] App-agnostic localization utilities.
21
+ // Each app calls createLocalizationHelpers(LANGUAGE_MAPPING) to get type-safe i18n helpers
22
+ // bound to its own language list.
23
+
24
+ export function createLocalizationHelpers<TMapping extends Record<string, string>>(
25
+ languageMapping: TMapping,
26
+ ) {
27
+ type SupportedLanguage = keyof TMapping & string;
28
+
29
+ function isLanguageCode(code: string): code is SupportedLanguage {
30
+ return code in languageMapping;
31
+ }
32
+
33
+ function getSupportedLanguageCodes(): SupportedLanguage[] {
34
+ return Object.keys(languageMapping) as SupportedLanguage[];
35
+ }
36
+
37
+ function getLocalizedUrl(currentPath: string, targetLang: string): string {
38
+ const cleanPath = currentPath.startsWith("/") ? currentPath.slice(1) : currentPath;
39
+
40
+ if (!cleanPath) {
41
+ return `/${targetLang}/`;
42
+ }
43
+
44
+ const parts = cleanPath.split("/");
45
+
46
+ // If the first segment is an existing language code, replace it.
47
+ if (parts[0] && parts[0] in languageMapping) {
48
+ parts[0] = targetLang;
49
+ return `/${parts.join("/")}`;
50
+ }
51
+
52
+ // Otherwise, prepend the target language.
53
+ return `/${targetLang}/${cleanPath}`;
54
+ }
55
+
56
+ return { isLanguageCode, getSupportedLanguageCodes, getLocalizedUrl } as const;
57
+ }