@starci/hfs 4.7.32 → 4.7.33

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 (473) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +13 -6
  3. package/emit/contracts.mjs +4 -3
  4. package/emit/form-bounds-reader.ts.partial +58 -0
  5. package/emit/form-bounds.mjs +155 -0
  6. package/emit/graphql-input-bounds.mjs +97 -0
  7. package/emit/input-bounds.mjs +44 -0
  8. package/emit/operation-schemas.mjs +137 -0
  9. package/emit/operations-worker.mjs +2 -2
  10. package/emit/operations.mjs +52 -16
  11. package/emit/schema-worker.mjs +17 -4
  12. package/emit/type-schema.mjs +17 -5
  13. package/package.json +9 -2
  14. package/release/gate.mjs +18 -23
  15. package/release/index.mjs +1 -1
  16. package/release/push.mjs +4 -5
  17. package/runtime/engine/admission.mjs +331 -0
  18. package/runtime/engine/canonical-json.mjs +14 -0
  19. package/runtime/engine/db/blob.mjs +380 -0
  20. package/runtime/engine/db/event-compact.mjs +73 -0
  21. package/runtime/engine/db/ledger-open.mjs +38 -0
  22. package/runtime/engine/db/ledger-paths.mjs +87 -0
  23. package/runtime/engine/db/ledger.mjs +1068 -0
  24. package/runtime/engine/db/machine-connection.mjs +150 -0
  25. package/runtime/engine/db/machine-open.mjs +60 -0
  26. package/runtime/engine/db/machine-schema.mjs +105 -0
  27. package/runtime/engine/db/machine.mjs +1317 -0
  28. package/runtime/engine/db/provider-reservations.mjs +127 -0
  29. package/runtime/engine/db/provision-authority.mjs +76 -0
  30. package/runtime/engine/db/provision-lock.mjs +29 -0
  31. package/runtime/engine/db/provision-operations.mjs +146 -0
  32. package/runtime/engine/db/ref-value.mjs +67 -0
  33. package/runtime/engine/db/schema/machine.sql +920 -0
  34. package/runtime/engine/db/schema/runtime.sql +1113 -0
  35. package/runtime/engine/refuse.mjs +11 -0
  36. package/runtime/knowledge/hfs/canon-pins.yaml +65 -9
  37. package/runtime/knowledge/hfs/design-truths.yaml +149 -0
  38. package/runtime/knowledge/hfs/fe-stack.yaml +23 -5
  39. package/runtime/knowledge/hfs/rules.yaml +269 -48
  40. package/runtime/knowledge/hfs/slots.yaml +113 -53
  41. package/runtime/knowledge/patterns/be/api.yaml +46 -20
  42. package/runtime/knowledge/patterns/be/event-bus.yaml +19 -12
  43. package/runtime/knowledge/patterns/be/idempotency.yaml +265 -0
  44. package/runtime/knowledge/patterns/be/jobs.yaml +17 -12
  45. package/runtime/knowledge/patterns/be/projections.yaml +2 -2
  46. package/runtime/knowledge/patterns/be/queues.yaml +57 -22
  47. package/runtime/knowledge/patterns/be/reactors.yaml +1 -1
  48. package/runtime/knowledge/patterns/be/realtime.yaml +326 -27
  49. package/runtime/knowledge/patterns/be/saga.yaml +2 -2
  50. package/runtime/knowledge/sonar-gate.yaml +1 -1
  51. package/runtime/modules/kernel/command-policy.yaml +249 -0
  52. package/runtime/modules/kernel/failure-codes.yaml +211 -52
  53. package/runtime/modules/kernel/hook-input.yaml +6 -0
  54. package/runtime/modules/kernel/protected-zone.yaml +81 -0
  55. package/runtime/modules/kernel/removed-vocabulary.yaml +62 -0
  56. package/runtime/modules/kernel/roles.yaml +405 -0
  57. package/runtime/modules/models/registry.yaml +3 -1
  58. package/runtime/modules/models/runtimes.yaml +2 -0
  59. package/runtime/modules/schemas/application-stacks.schema.yaml +10 -2
  60. package/runtime/packages/cli/src/catalog.generated.mjs +46 -59
  61. package/runtime/packages/grammar/scripts/build-stamp.mjs +98 -0
  62. package/runtime/scripts/agent/codex-app-server.mjs +79 -0
  63. package/runtime/scripts/agent/codex-mirror-source.mjs +34 -0
  64. package/runtime/scripts/agent/host-agents.mjs +80 -0
  65. package/runtime/scripts/agent/launch-trust-policy.mjs +53 -0
  66. package/runtime/scripts/agent/trust-launch.mjs +191 -0
  67. package/runtime/scripts/agent/trust.mjs +523 -0
  68. package/runtime/scripts/api/fs/rename-over.mjs +21 -0
  69. package/runtime/scripts/api/fs/temp-path.mjs +8 -0
  70. package/runtime/scripts/api/fs/write-json-file.mjs +10 -0
  71. package/runtime/scripts/api/git/diff-names.mjs +8 -0
  72. package/runtime/scripts/api/git/head-time.mjs +10 -0
  73. package/runtime/scripts/api/git/index-lock.mjs +78 -0
  74. package/runtime/scripts/api/git/is-ancestor.mjs +5 -0
  75. package/runtime/scripts/api/git/log.mjs +5 -0
  76. package/runtime/scripts/api/git/merge-base-query.mjs +5 -0
  77. package/runtime/scripts/api/git/merge-tree.mjs +5 -0
  78. package/runtime/scripts/api/git/rev-list.mjs +5 -0
  79. package/runtime/scripts/api/git/rev-parse.mjs +13 -0
  80. package/runtime/scripts/api/git/status-query.mjs +5 -0
  81. package/runtime/scripts/api/git/worktree-list-query.mjs +5 -0
  82. package/runtime/scripts/api/node/spawn-node.mjs +6 -0
  83. package/runtime/scripts/api/npm/dist-tag.mjs +8 -0
  84. package/runtime/scripts/api/npm/lib.mjs +32 -0
  85. package/runtime/scripts/api/npm/pack-dry-run.mjs +9 -0
  86. package/runtime/scripts/api/npm/run-npm.mjs +6 -0
  87. package/runtime/scripts/api/npm/view.mjs +30 -0
  88. package/runtime/scripts/api/npm/whoami.mjs +8 -0
  89. package/runtime/scripts/api/process/hide-child-windows.mjs +31 -0
  90. package/runtime/scripts/api/process/kill-tree.mjs +22 -0
  91. package/runtime/scripts/api/process/lib.mjs +125 -0
  92. package/runtime/scripts/api/process/probe-guard-command.mjs +74 -0
  93. package/runtime/scripts/api/process/process-list.mjs +28 -0
  94. package/runtime/scripts/api/process/system-tool.mjs +37 -0
  95. package/runtime/scripts/gates/canon-digest.mjs +81 -0
  96. package/runtime/scripts/gates/canon-pins.mjs +16 -0
  97. package/runtime/scripts/gates/commit-proof.mjs +53 -0
  98. package/runtime/scripts/gates/gate-input.mjs +98 -0
  99. package/runtime/scripts/gates/gate.mjs +669 -0
  100. package/runtime/scripts/gates/grammar-dist.mjs +206 -0
  101. package/runtime/scripts/gates/runtime-package-selection.mjs +27 -0
  102. package/runtime/scripts/gates/test-world-run.mjs +254 -0
  103. package/runtime/scripts/gates/type-impact.mjs +277 -0
  104. package/runtime/scripts/gates/type-program.mjs +113 -0
  105. package/runtime/scripts/guards/assigned-command.mjs +12 -0
  106. package/runtime/scripts/guards/call-rights.mjs +47 -0
  107. package/runtime/scripts/guards/command-guard.mjs +468 -0
  108. package/runtime/scripts/guards/command-policy.mjs +342 -0
  109. package/runtime/scripts/guards/critic-reach.mjs +82 -0
  110. package/runtime/scripts/guards/deps-guard.mjs +150 -0
  111. package/runtime/scripts/guards/env-dump-verdict.mjs +89 -0
  112. package/runtime/scripts/guards/git-pathspec.mjs +225 -0
  113. package/runtime/scripts/guards/git-policy.mjs +355 -0
  114. package/runtime/scripts/guards/guards-root.mjs +27 -0
  115. package/runtime/scripts/guards/hook-install.mjs +343 -0
  116. package/runtime/scripts/guards/hook-io.mjs +76 -0
  117. package/runtime/scripts/guards/hook-verdict.mjs +12 -0
  118. package/runtime/scripts/guards/install-verdict.mjs +41 -0
  119. package/runtime/scripts/guards/kernel-seat.mjs +63 -0
  120. package/runtime/scripts/guards/launch-verdict.mjs +74 -0
  121. package/runtime/scripts/guards/op-starci.mjs +16 -0
  122. package/runtime/scripts/guards/op-write-scope.mjs +27 -0
  123. package/runtime/scripts/guards/orca-self-lifecycle.mjs +39 -0
  124. package/runtime/scripts/guards/process-kill-verdict.mjs +106 -0
  125. package/runtime/scripts/guards/protected-zone.mjs +56 -0
  126. package/runtime/scripts/guards/refusals.mjs +27 -0
  127. package/runtime/scripts/guards/release-definition.mjs +185 -0
  128. package/runtime/scripts/guards/release-push-gate.mjs +72 -0
  129. package/runtime/scripts/guards/release-record.mjs +58 -0
  130. package/runtime/scripts/guards/release-suite-mode.mjs +15 -0
  131. package/runtime/scripts/guards/rights.mjs +268 -0
  132. package/runtime/scripts/guards/seat-tools.mjs +47 -0
  133. package/runtime/scripts/guards/shell-commands.mjs +328 -0
  134. package/runtime/scripts/guards/supervisor-seat.mjs +80 -0
  135. package/runtime/scripts/guards/verdict-process.mjs +81 -0
  136. package/runtime/scripts/hfs/architecture/dead-exports.mjs +10 -10
  137. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +1 -1
  138. package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +53 -0
  139. package/runtime/scripts/hfs/architecture/reachability.mjs +1 -1
  140. package/runtime/scripts/hfs/architecture/sql-owner.mjs +17 -6
  141. package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +105 -0
  142. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +38 -52
  143. package/runtime/scripts/hfs/architecture/supabase-keys.mjs +261 -0
  144. package/runtime/scripts/hfs/architecture/transport-owner.mjs +49 -3
  145. package/runtime/scripts/hfs/architecture/typescript.mjs +1 -1
  146. package/runtime/scripts/hfs/check.mjs +6 -4
  147. package/runtime/scripts/hfs/coverage-scope.mjs +28 -1
  148. package/runtime/scripts/hfs/db-restamp-verb.mjs +54 -14
  149. package/runtime/scripts/hfs/declaration-shape.mjs +4 -4
  150. package/runtime/scripts/hfs/declaration-slots.mjs +14 -3
  151. package/runtime/scripts/hfs/manifest-shape.mjs +10 -6
  152. package/runtime/scripts/hfs/rule-params-shape.mjs +18 -6
  153. package/runtime/scripts/hfs/rules/a15-findings.mjs +4 -1
  154. package/runtime/scripts/hfs/rules/bypass-markers.mjs +22 -7
  155. package/runtime/scripts/hfs/rules/database-migrations.mjs +69 -14
  156. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +9 -7
  157. package/runtime/scripts/hfs/rules/database-queues.mjs +68 -18
  158. package/runtime/scripts/hfs/rules/edition.mjs +56 -12
  159. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -10
  160. package/runtime/scripts/hfs/rules/fe-stack.mjs +12 -7
  161. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +1 -1
  162. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +75 -9
  163. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +1 -1
  164. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +15 -4
  165. package/runtime/scripts/hfs/rules/text-integrity.mjs +73 -0
  166. package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +74 -0
  167. package/runtime/scripts/hfs/runtime-rules/release-notes.mjs +44 -0
  168. package/runtime/scripts/hfs/sealed-members.mjs +3 -3
  169. package/runtime/scripts/hfs/slot-side-problems.mjs +3 -3
  170. package/runtime/scripts/hfs/slots.mjs +2 -1
  171. package/runtime/scripts/hfs/stack-deploy-config.mjs +22 -5
  172. package/runtime/scripts/hfs/stacks-gate.mjs +28 -7
  173. package/runtime/scripts/hfs/view.mjs +2 -1
  174. package/runtime/scripts/lib/app-data.mjs +11 -0
  175. package/runtime/scripts/lib/bypass-markers.mjs +272 -75
  176. package/runtime/scripts/lib/check-format.mjs +7 -0
  177. package/runtime/scripts/lib/cli-arg.mjs +120 -0
  178. package/runtime/scripts/lib/css-rules.mjs +220 -0
  179. package/runtime/scripts/lib/dom-tree.mjs +4 -0
  180. package/runtime/scripts/lib/escape.mjs +16 -0
  181. package/runtime/scripts/lib/event-items.mjs +22 -0
  182. package/runtime/scripts/lib/failure-code-template-errors.mjs +161 -0
  183. package/runtime/scripts/lib/git-dir.mjs +120 -0
  184. package/runtime/scripts/lib/grammar-vendor-parity.mjs +377 -0
  185. package/runtime/scripts/lib/guard-command.mjs +24 -0
  186. package/runtime/scripts/lib/hook-limits.mjs +5 -0
  187. package/runtime/scripts/lib/interrupt-cleanup.mjs +38 -0
  188. package/runtime/scripts/lib/js-lex.mjs +200 -0
  189. package/runtime/scripts/lib/json-formats.mjs +0 -1
  190. package/runtime/scripts/lib/language.mjs +94 -1
  191. package/runtime/scripts/lib/loosening-budgets.mjs +44 -0
  192. package/runtime/scripts/lib/loosening-config.mjs +361 -0
  193. package/runtime/scripts/lib/loosening-state.mjs +223 -0
  194. package/runtime/scripts/lib/menu-parts.mjs +11 -0
  195. package/runtime/scripts/lib/normalize.mjs +25 -0
  196. package/runtime/scripts/lib/op-gate.mjs +15 -0
  197. package/runtime/scripts/lib/redact.mjs +208 -0
  198. package/runtime/scripts/lib/runtime-location.mjs +21 -0
  199. package/runtime/scripts/lib/runtime-package-inventory.mjs +21 -0
  200. package/runtime/scripts/lib/runtime-package-manifests.mjs +84 -0
  201. package/runtime/scripts/lib/runtime-publication-tags.mjs +99 -0
  202. package/runtime/scripts/lib/seat-env.mjs +43 -0
  203. package/runtime/scripts/lib/secret-patterns.mjs +58 -9
  204. package/runtime/scripts/lib/shell-source.mjs +4 -0
  205. package/runtime/scripts/lib/source-phrases.mjs +34 -0
  206. package/runtime/scripts/lib/sqlite.mjs +21 -0
  207. package/runtime/scripts/lib/swarm-stack.mjs +2 -1
  208. package/runtime/scripts/lib/tar-files.mjs +39 -0
  209. package/runtime/scripts/lib/ts-analysis.mjs +10 -0
  210. package/runtime/scripts/lib/worktree-exclude.mjs +35 -0
  211. package/runtime/scripts/lib/worktree-record.mjs +14 -0
  212. package/runtime/scripts/machine/artifact-hold.mjs +52 -0
  213. package/runtime/scripts/machine/ask-receipts.mjs +113 -0
  214. package/runtime/scripts/machine/evidence-store.mjs +223 -0
  215. package/runtime/scripts/machine/home.mjs +216 -0
  216. package/runtime/scripts/machine/host-lock.mjs +184 -0
  217. package/runtime/scripts/machine/lock-recovery.mjs +55 -0
  218. package/runtime/scripts/machine/roles-contract.mjs +90 -0
  219. package/runtime/scripts/machine/runtime-change.mjs +20 -0
  220. package/runtime/scripts/machine/workflow-tree.mjs +122 -0
  221. package/runtime/scripts/reconciler/checklist-items.mjs +7 -0
  222. package/runtime/scripts/reconciler/guard-row.mjs +25 -0
  223. package/runtime/scripts/work/asset-slot.mjs +284 -0
  224. package/runtime/scripts/work/brand/brand-colour.mjs +125 -0
  225. package/runtime/scripts/work/brand/brand-tokens.mjs +167 -0
  226. package/runtime/scripts/work/brand/brand-value-source.mjs +106 -0
  227. package/runtime/scripts/work/brand/brand.mjs +892 -0
  228. package/runtime/scripts/work/draw/draw-dna-anatomy.mjs +60 -0
  229. package/runtime/scripts/work/draw/draw-dna-findings.mjs +256 -0
  230. package/runtime/scripts/work/draw/draw-dna-selector.mjs +38 -0
  231. package/runtime/scripts/work/draw/draw-dna.mjs +483 -0
  232. package/runtime/scripts/work/draw/draw-layer.mjs +305 -0
  233. package/runtime/scripts/work/draw/draw-rationale-page.mjs +237 -0
  234. package/runtime/scripts/work/draw/draw-rationale-ref.mjs +14 -0
  235. package/runtime/scripts/work/draw/draw-rationale.mjs +353 -0
  236. package/runtime/scripts/work/draw/draw-source.mjs +412 -0
  237. package/runtime/scripts/work/draw/draw-taste.mjs +278 -0
  238. package/runtime/scripts/work/draw/draw-typecheck.mjs +72 -0
  239. package/runtime/scripts/work/draw/rationale-findings.mjs +205 -0
  240. package/runtime/scripts/work/draw-grammar.mjs +175 -0
  241. package/runtime/scripts/work/draw-render-page.mjs +188 -0
  242. package/runtime/scripts/work/draw-render.mjs +626 -0
  243. package/runtime/scripts/work/grammar-proposal.mjs +279 -0
  244. package/runtime/scripts/work/png.mjs +237 -0
  245. package/runtime/scripts/work/render-tools.mjs +64 -0
  246. package/runtime/scripts/work/work-io.mjs +119 -0
  247. package/scaffold/add-inbox-lite.mjs +2 -2
  248. package/scaffold/add-plan.mjs +40 -13
  249. package/scaffold/add-queue-lite.mjs +372 -0
  250. package/scaffold/add.mjs +5 -2
  251. package/scaffold/app.mjs +1 -2
  252. package/scaffold/edition-gate.mjs +11 -7
  253. package/scaffold/lite-exports.mjs +10 -0
  254. package/scaffold/pattern-file-slots.mjs +33 -0
  255. package/scaffold/test-world.mjs +1 -1
  256. package/scaffold/yaml-block-edit.mjs +117 -0
  257. package/src/main.mjs +5 -4
  258. package/stack/deploy.mjs +7 -3
  259. package/stack/post-deploy.mjs +48 -0
  260. package/sync/index.mjs +10 -0
  261. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +1 -1
  262. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +1 -1
  263. package/templates/app/codegen-graphql.mjs.partial +59 -12
  264. package/templates/app/hooks/husky/pre-commit +2 -2
  265. package/templates/app/hooks/husky/pre-push +10 -3
  266. package/templates/app/package-scripts/redis-dev.json +3 -0
  267. package/templates/app/package-scripts-lite/package.json +1 -0
  268. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +36 -8
  269. package/templates/app/skeleton/.starcistacks/dev/README.md +1 -1
  270. package/templates/app/skeleton/.starcistacks/production/README.md +10 -9
  271. package/templates/app/skeleton/.starcistacks/production/infra/swarm/postgres-init.sh +21 -0
  272. package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +1 -1
  273. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +1 -0
  274. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +47 -5
  275. package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +5 -3
  276. package/templates/app/skeleton/README.md +2 -0
  277. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +5 -0
  278. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +2 -2
  279. package/templates/app/skeleton-lite/.starcistacks/production/README.md +1 -1
  280. package/templates/app/skeleton-lite/README.md +1 -1
  281. package/templates/be/inbox/platform/client-operation-outcomes.sql.tpl +18 -0
  282. package/templates/be/inbox/platform/event-bus/errors/event-bus.error.ts.tpl +16 -0
  283. package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +10 -0
  284. package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +1 -1
  285. package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +15 -3
  286. package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +11 -3
  287. package/templates/be/inbox/platform/event-bus/index.ts.tpl +2 -0
  288. package/templates/be/inbox/platform/event-bus/messages/event-bus.messages.ts.tpl +7 -0
  289. package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +2 -2
  290. package/templates/be/inbox/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
  291. package/templates/be/inbox/platform/idempotency/index.ts.tpl +2 -0
  292. package/templates/be/inbox/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
  293. package/templates/be/inbox/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
  294. package/templates/be/inbox/platform/inbox/client-operation.policy.ts.tpl +17 -0
  295. package/templates/be/inbox/platform/inbox/errors/inbox.error.ts.tpl +22 -0
  296. package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +21 -0
  297. package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +334 -11
  298. package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +55 -4
  299. package/templates/be/inbox/platform/inbox/index.ts.tpl +2 -0
  300. package/templates/be/inbox/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
  301. package/templates/be/inbox/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
  302. package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +26 -0
  303. package/templates/be/inbox/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
  304. package/templates/be/inbox/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
  305. package/templates/be/inbox/platform/migration.sql.tpl +1 -0
  306. package/templates/be/inbox/service.spec.ts.tpl +4 -4
  307. package/templates/be/inbox/service.ts.tpl +3 -1
  308. package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -2
  309. package/templates/be/inbox/webhook-door.ts.tpl +3 -2
  310. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +3 -0
  311. package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +10 -0
  312. package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +2 -2
  313. package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +16 -3
  314. package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +11 -3
  315. package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +139 -50
  316. package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +25 -2
  317. package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +139 -78
  318. package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +10 -6
  319. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +262 -159
  320. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +32 -5
  321. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +2 -0
  322. package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +5 -1
  323. package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +2 -0
  324. package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -4
  325. package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +1 -0
  326. package/templates/be/patterns/idempotency/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
  327. package/templates/be/patterns/idempotency/platform/idempotency/index.ts.tpl +2 -0
  328. package/templates/be/patterns/idempotency/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
  329. package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
  330. package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.ts.tpl +17 -0
  331. package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.spec.ts.tpl +66 -0
  332. package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.ts.tpl +24 -0
  333. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.spec.ts.tpl +60 -0
  334. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.ts.tpl +12 -0
  335. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.spec.ts.tpl +49 -0
  336. package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.ts.tpl +12 -0
  337. package/templates/be/patterns/idempotency/platform/inbox/effectful.decorators.ts.tpl +7 -0
  338. package/templates/be/patterns/idempotency/platform/inbox/errors/inbox.error.ts.tpl +22 -0
  339. package/templates/be/patterns/idempotency/platform/inbox/inbox.decorators.ts.tpl +17 -0
  340. package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +7 -0
  341. package/templates/be/patterns/idempotency/platform/inbox/inbox.module.ts.tpl +28 -0
  342. package/templates/be/patterns/idempotency/platform/inbox/inbox.options.ts.tpl +7 -0
  343. package/templates/be/patterns/idempotency/platform/inbox/inbox.port.ts.tpl +44 -0
  344. package/templates/be/patterns/idempotency/platform/inbox/inbox.service.spec.ts.tpl +388 -0
  345. package/templates/be/patterns/idempotency/platform/inbox/inbox.service.ts.tpl +77 -0
  346. package/templates/be/patterns/idempotency/platform/inbox/index.ts.tpl +10 -0
  347. package/templates/be/patterns/idempotency/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
  348. package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +9 -0
  349. package/templates/be/patterns/idempotency/platform/inbox/persistence/entities/inbox-claim.entity.ts.tpl +25 -0
  350. package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
  351. package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +39 -0
  352. package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1789800005000-create-inbox-claims.ts.tpl +21 -0
  353. package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1792800000000-client-operation-outcomes.ts.tpl +21 -0
  354. package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
  355. package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
  356. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +1 -0
  357. package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +4 -3
  358. package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +2 -0
  359. package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +20 -2
  360. package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +1 -0
  361. package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +4 -0
  362. package/templates/be/patterns/jobs/platform-lite/jobs.index.ts.tpl +10 -0
  363. package/templates/be/patterns/jobs/platform-lite/jobs.sql.tpl +44 -0
  364. package/templates/be/patterns/jobs/platform-lite/persistence.jobs.sql.ts.tpl +33 -0
  365. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +203 -18
  366. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +78 -22
  367. package/templates/be/patterns/queues/platform/errors/queue.error.ts.tpl +19 -0
  368. package/templates/be/patterns/queues/platform/messages/queue.messages.ts.tpl +13 -0
  369. package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +1 -0
  370. package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +4 -0
  371. package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +2 -0
  372. package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -4
  373. package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +14 -2
  374. package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +5 -2
  375. package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +72 -74
  376. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +1 -1
  377. package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +23 -8
  378. package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +29 -8
  379. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +7 -3
  380. package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +5 -0
  381. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +2 -0
  382. package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +4 -0
  383. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +2 -1
  384. package/templates/be/patterns/queues/platform/queue.port.ts.tpl +1 -1
  385. package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.spec.ts.tpl +417 -0
  386. package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.ts.tpl +172 -0
  387. package/templates/be/patterns/queues/platform-lite/persistence.queue.sql.ts.tpl +12 -0
  388. package/templates/be/patterns/queues/platform-lite/queue-outbox.sql.tpl +32 -0
  389. package/templates/be/patterns/queues/platform-lite/queue-relay.service.spec.ts.tpl +148 -0
  390. package/templates/be/patterns/queues/platform-lite/queue-relay.service.ts.tpl +51 -0
  391. package/templates/be/patterns/queues/platform-lite/queue-transport.port.ts.tpl +163 -0
  392. package/templates/be/patterns/queues/platform-lite/queue.config.ts.tpl +17 -0
  393. package/templates/be/patterns/queues/platform-lite/queue.error.ts.tpl +25 -0
  394. package/templates/be/patterns/queues/platform-lite/queue.index.ts.tpl +8 -0
  395. package/templates/be/patterns/queues/platform-lite/queue.messages.ts.tpl +21 -0
  396. package/templates/be/patterns/queues/platform-lite/queue.module.ts.tpl +74 -0
  397. package/templates/be/patterns/queues/platform-lite/queue.options.ts.tpl +38 -0
  398. package/templates/be/patterns/queues/queue.queue.ts.tpl +2 -2
  399. package/templates/be/patterns/reactors/application.contracts.ts.tpl +2 -0
  400. package/templates/be/patterns/reactors/consumer.ts.tpl +5 -1
  401. package/templates/be/platform/graphql/graphql.contracts.ts +2 -1
  402. package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +1 -1
  403. package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +6 -1
  404. package/templates/be/skeleton/apps/__app__/src/main.ts +7 -4
  405. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +4 -0
  406. package/templates/be/skeleton/apps/cli/src/main.ts +7 -4
  407. package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -1
  408. package/templates/be/skeleton/src/modules/platform/http/errors/http.error.ts +22 -0
  409. package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.spec.ts +235 -0
  410. package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.ts +85 -0
  411. package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.spec.ts +92 -0
  412. package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.ts +52 -0
  413. package/templates/be/skeleton/src/modules/platform/http/http.decorators.ts +9 -0
  414. package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +7 -0
  415. package/templates/be/skeleton/src/modules/platform/http/http.module.ts +19 -0
  416. package/templates/be/skeleton/src/modules/platform/http/http.options.ts +2 -0
  417. package/templates/be/skeleton/src/modules/platform/http/http.port.ts +40 -0
  418. package/templates/be/skeleton/src/modules/platform/http/index.ts +7 -0
  419. package/templates/be/skeleton/src/modules/platform/http/messages/http.messages.ts +15 -0
  420. package/templates/be/skeleton/src/modules/platform/ids/ids.decorators.ts +9 -0
  421. package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +7 -0
  422. package/templates/be/skeleton/src/modules/platform/ids/ids.module.ts +19 -0
  423. package/templates/be/skeleton/src/modules/platform/ids/ids.options.ts +2 -0
  424. package/templates/be/skeleton/src/modules/platform/ids/ids.port.ts +5 -0
  425. package/templates/be/skeleton/src/modules/platform/ids/index.ts +4 -0
  426. package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.spec.ts +15 -0
  427. package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.ts +10 -0
  428. package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.spec.ts +59 -0
  429. package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.ts +21 -0
  430. package/templates/be/skeleton/src/modules/platform/logging/correlation.contracts.ts +16 -0
  431. package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.spec.ts +150 -0
  432. package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.ts +42 -0
  433. package/templates/be/skeleton/src/modules/platform/logging/correlation.port.ts +15 -0
  434. package/templates/be/skeleton/src/modules/platform/logging/correlation.service.spec.ts +23 -0
  435. package/templates/be/skeleton/src/modules/platform/logging/correlation.service.ts +28 -0
  436. package/templates/be/skeleton/src/modules/platform/logging/index.ts +14 -1
  437. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +39 -6
  438. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +15 -3
  439. package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +17 -2
  440. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +13 -1
  441. package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +13 -3
  442. package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.spec.ts +69 -0
  443. package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.ts +48 -0
  444. package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
  445. package/templates/be/skeleton/src/modules/platform/primitives/record.policy.spec.ts +12 -0
  446. package/templates/be/skeleton/src/modules/platform/primitives/record.policy.ts +3 -0
  447. package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +6 -1
  448. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +4 -0
  449. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
  450. package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +14 -1
  451. package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +12 -0
  452. package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
  453. package/templates/be/test-world/world/test-apps.options.ts.tpl +2 -0
  454. package/templates/be/test-world/world/test-world.config.ts.tpl +1 -0
  455. package/upgrade/check.mjs +12 -135
  456. package/upgrade/file-state.mjs +1 -16
  457. package/upgrade/index.mjs +34 -361
  458. package/runtime/scripts/lib/check-code-loosening.mjs +0 -128
  459. package/runtime/scripts/lib/gate-loosening.mjs +0 -292
  460. package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +0 -89
  461. package/templates/app/upgrade-full/.starcistacks/dev/README.md +0 -16
  462. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +0 -13
  463. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +0 -3
  464. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +0 -12
  465. package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +0 -7
  466. package/templates/be/skeleton-lite/apps/api/src/main.ts +0 -26
  467. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +0 -12
  468. package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +0 -7
  469. package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +0 -5
  470. package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +0 -9
  471. package/templates/be/upgrade-full/apps/cli/src/main.ts +0 -29
  472. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +0 -7
  473. package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +0 -23
@@ -0,0 +1,60 @@
1
+ // draw-dna-anatomy.mjs - the anatomy a capture measured (draw-render record `anatomy`) judged against the HeroUI Alert and
2
+ // Meter anatomy: DRAW_ALERT_ANATOMY and DRAW_METER_TRACK findings. draw-dna.mjs re-exports it.
3
+ export const DRAW_ALERT_ANATOMY = 'DRAW_ALERT_ANATOMY';
4
+ export const DRAW_METER_TRACK = 'DRAW_METER_TRACK';
5
+ /** The HeroUI Meter/Progress track height (meter.css `.meter__track` h-2), in CSS px. */
6
+ export const METER_TRACK_PX = 8;
7
+ /** The segmented Meter track height (grammar 0.5.2 `.starci-core-meter-segments` h-1), in CSS px. */
8
+ export const METER_SEGMENTED_TRACK_PX = 4;
9
+ /** The widest gap between two segments that still reads as one track (the grammar sets 0.25rem). */
10
+ export const METER_SEGMENT_GAP_MAX_PX = 8;
11
+ /** The indicator size at which an Alert glyph has become a tile (IconTile sm is 32px; HeroUI's glyph is size-4 + p-1). */
12
+ export const ALERT_INDICATOR_MAX_PX = 32;
13
+ /** A track at least this share of its band's content width spans it (sub-pixel rounding, borders). */
14
+ export const METER_FULL_WIDTH_SHARE = 0.95;
15
+
16
+ /**
17
+ * The anatomy a capture measured (draw-render record `anatomy`: {alerts:[{desc, background, surface, indicator,
18
+ * indicatorColor, titleColor}], meters:[{desc, track, band, segments}]}) judged: [{code, kind, detail}].
19
+ */
20
+ export function anatomyFindings(anatomy, { label = 'the capture' } = {}) {
21
+ const out = [];
22
+ const same = (a, b) => String(a ?? '').replace(/\s+/g, '') === String(b ?? '').replace(/\s+/g, '');
23
+ const clear = (c) => /^rgba\([^)]*,\s*0\)$/.test(String(c ?? '').replace(/\s+/g, '')) || c === 'transparent';
24
+ alertAnatomyFindings(Array.isArray(anatomy?.alerts) ? anatomy.alerts : [], label, out, same, clear);
25
+ meterAnatomyFindings(Array.isArray(anatomy?.meters) ? anatomy.meters : [], label, out);
26
+ return out;
27
+ }
28
+
29
+ function alertAnatomyFindings(alerts, label, out, same, clear) {
30
+ for (const a of alerts) {
31
+ const surface = a.surface && !clear(a.surface) ? a.surface : 'rgb(255, 255, 255)';
32
+ if (a.background && !clear(a.background) && !same(a.background, surface)) out.push({ code: DRAW_ALERT_ANATOMY, kind: 'tone-filled Alert', detail: `${label}: ${a.desc ?? 'an Alert'} renders background ${a.background}, not the surface ${surface}` });
33
+ const size = Math.max(Number(a.indicator?.width) || 0, Number(a.indicator?.height) || 0);
34
+ if (size >= ALERT_INDICATOR_MAX_PX) out.push({ code: DRAW_ALERT_ANATOMY, kind: 'Alert indicator as a tile', detail: `${label}: ${a.desc ?? 'an Alert'} indicator renders ${Math.round(size)}px (HeroUI: a size-4 glyph with p-1)` });
35
+ if (a.tile) out.push({ code: DRAW_ALERT_ANATOMY, kind: 'IconTile in an Alert', detail: `${label}: ${a.desc ?? 'an Alert'} renders an IconTile` });
36
+ if (a.indicatorColor && a.titleColor && !same(a.indicatorColor, a.titleColor)) out.push({ code: DRAW_ALERT_ANATOMY, kind: 'indicator and title in different tones', detail: `${label}: ${a.desc ?? 'an Alert'} indicator ${a.indicatorColor} vs title ${a.titleColor} - both are the tone's soft-foreground` });
37
+ }
38
+ }
39
+
40
+ function meterSegmentFindings(m, t, label, out) {
41
+ const segs = Array.isArray(m.segments) ? m.segments.filter((s) => Number(s.width) > 0).sort((x, y) => x.x - y.x) : [];
42
+ if (segs.length <= 1) return;
43
+ const widths = segs.map((s) => Number(s.width));
44
+ const gaps = segs.slice(1).map((s, i) => s.x - (segs[i].x + segs[i].width));
45
+ const covered = segs.at(-1).x + segs.at(-1).width - segs[0].x;
46
+ if (Math.max(...widths) - Math.min(...widths) > 2) out.push({ code: DRAW_METER_TRACK, kind: 'unequal Meter segments', detail: `${label}: ${m.desc ?? 'a Meter'} segments are ${widths.map(Math.round).join('/')}px - they divide the track equally` });
47
+ if (Math.max(...gaps) > METER_SEGMENT_GAP_MAX_PX || covered < Number(t.width) * METER_FULL_WIDTH_SHARE) out.push({ code: DRAW_METER_TRACK, kind: 'Meter segments do not fill the track', detail: `${label}: ${m.desc ?? 'a Meter'} segments cover ${Math.round(covered)}px of ${Math.round(t.width)}px with gaps up to ${Math.round(Math.max(...gaps))}px - small gaps, the full width` });
48
+ }
49
+
50
+ function meterAnatomyFindings(meters, label, out) {
51
+ for (const m of meters) {
52
+ const t = m.track;
53
+ if (!t) continue;
54
+ const want = m.segmented || (Array.isArray(m.segments) && m.segments.length > 1) ? METER_SEGMENTED_TRACK_PX : METER_TRACK_PX;
55
+ if (Math.abs(Number(t.height) - want) > 0.5) out.push({ code: DRAW_METER_TRACK, kind: 'Meter track off its height', detail: label + ': ' + (m.desc ?? 'a Meter') + ' track renders ' + t.height + 'px tall (' + (want === METER_TRACK_PX ? 'HeroUI h-2 = ' + METER_TRACK_PX + 'px' : 'segmented h-1 = ' + METER_SEGMENTED_TRACK_PX + 'px') + ')' });
56
+ const band = Number(m.band?.width) || 0;
57
+ if (band > 0 && Number(t.width) < band * METER_FULL_WIDTH_SHARE) out.push({ code: DRAW_METER_TRACK, kind: 'Meter as a stub', detail: `${label}: ${m.desc ?? 'a Meter'} track is ${Math.round(t.width)}px of its band's ${Math.round(band)}px - it spans the full width` });
58
+ meterSegmentFindings(m, t, label, out);
59
+ }
60
+ }
@@ -0,0 +1,256 @@
1
+ export function appendDnaFindings({ all, dna, proposals, label, assetRequests, tree, helpers }) {
2
+ const groups = new Map();
3
+ const add = (code, kind, el, why) => {
4
+ const key = `${code}|${kind}`;
5
+ if (!groups.has(key)) groups.set(key, { code, kind, items: [] });
6
+ groups.get(key).items.push(helpers.describe(el) + (why ? ' (' + why + ')' : ''));
7
+ };
8
+ mappingFindings(all, dna, proposals, add, helpers);
9
+ noticeFindings(all, add, helpers);
10
+ ratioFindings(all, add, helpers);
11
+ const rules = helpers.styleRulesOf(tree);
12
+ alertFindings(all, dna, rules, add, helpers);
13
+ meterTrackFindings(all, rules, add, helpers);
14
+ artworkFindings(all, assetRequests, add, helpers);
15
+ badgeDotFindings(all, rules, add, helpers);
16
+ return groupedFindings(groups, label);
17
+ }
18
+
19
+ function mappingFindings(all, dna, proposals, add, h) {
20
+ for (const el of all) mappingFindingFor(el, dna, proposals, add, h);
21
+ }
22
+
23
+ function mappingFindingFor(el, dna, proposals, add, h) {
24
+ if (h.insideSvg(el)) return;
25
+ const name = h.componentNameOf(el);
26
+ const part = (el.attrs[h.PART_ATTR] ?? '').trim();
27
+ const proposal = (el.attrs[h.PROPOSAL_ATTR] ?? '').trim();
28
+ if (!h.mappedSelf(el)) {
29
+ if (!(h.plainPhrasing(el) && el.parent && el.parent.tag !== '#root')) {
30
+ add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'unmapped element', el, 'no data-grammar-component, data-grammar-part or data-grammar-proposal');
31
+ }
32
+ return;
33
+ }
34
+ if (name && !dna.components.has(name)) add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'unknown DNA component', el, `"${name}" is not one of the ${dna.components.size} DNA components`);
35
+ if (proposal && !hasProposal(proposals, proposal)) {
36
+ add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'proposal without an entry', el, `data-grammar-proposal="${proposal}" has no complete entry (name, gap, anatomy, tokens, claims, isolated render) in grammar-proposal.md/.yaml`);
37
+ }
38
+ if (part && !h.proposalOf(el)) addUnknownPartFinding(el, part, dna, add, h);
39
+ const spec = name ? dna.components.get(name) : null;
40
+ if (spec) closedValueFindings(el, name, spec, add, h);
41
+ }
42
+
43
+ function hasProposal(proposals, proposal) {
44
+ if (proposals.has(proposal)) return true;
45
+ if (proposals.has(proposal.split('.')[0])) return true;
46
+ return [...proposals].some((p) => p.toLowerCase() === proposal.toLowerCase());
47
+ }
48
+
49
+ function addUnknownPartFinding(el, part, dna, add, h) {
50
+ const owners = [el, ...h.ancestorsOf(el)].map((a) => h.componentNameOf(a)).filter(Boolean).map((n) => dna.components.get(n)).filter(Boolean);
51
+ const judged = owners.filter((c) => c.parts.size);
52
+ if (judged.length && judged.length === owners.length && !judged.some((c) => c.parts.has(part))) {
53
+ add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'unknown anatomy part', el, `part "${part}" is none of ${[...new Set(judged.map((c) => c.name))].slice(0, 3).join('/')}'s anatomy`);
54
+ }
55
+ }
56
+
57
+ function closedValueFindings(el, name, spec, add, h) {
58
+ for (const [prop, closed] of spec.closed) {
59
+ const raw = el.attrs[`data-${h.kebab(prop)}`] ?? el.attrs[`data-grammar-${h.kebab(prop)}`];
60
+ if (raw == null || raw === '') continue;
61
+ if (!closedValueIsValid(closed, raw, h)) {
62
+ add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'closed value off DNA', el, `${name} ${prop}="${raw}" is not one of ${closed.values ? closed.values.join('|') : 'the PresentationState values'}`);
63
+ }
64
+ }
65
+ }
66
+
67
+ function closedValueIsValid(closed, raw, h) {
68
+ if (closed.values) return closed.values.includes(raw);
69
+ if (closed.type === 'PresentationState') return Boolean(h.presentationStateOf(raw));
70
+ return true;
71
+ }
72
+
73
+ function noticeFindings(all, add, h) {
74
+ for (const el of all.filter((e) => h.componentRootOf(e) === 'Alert')) {
75
+ if (!h.toneOf(el)) add(h.DRAW_NOTICE_NOT_ALERT, 'Alert without a tone', el, 'DNA Alert carries tone (PresentationState): data-tone="warning|success|danger|info|neutral"');
76
+ }
77
+ for (const el of all) noticeFindingFor(el, add, h);
78
+ }
79
+
80
+ function noticeFindingFor(el, add, h) {
81
+ if (h.insideSvg(el)) return;
82
+ const name = h.componentNameOf(el);
83
+ if (h.inComponent(el, ['Alert', 'AlertDialog', 'Toast', 'Toaster'])) return;
84
+ const container = h.CONTAINERS.has(name);
85
+ if (!container && !(!name && ['div', 'section', 'aside', 'article'].includes(el.tag))) return;
86
+ if (h.walkElements(el).some((d) => h.componentRootOf(d) === 'Alert')) return;
87
+ const reasons = noticeReasons(el, name, container, h);
88
+ if (reasons.length) add(h.DRAW_NOTICE_NOT_ALERT, 'notice posing as another component', el, `${reasons.join(', ')}: a notice is DNA Alert with tone + alert-actions, never ${name || 'a hand-built box'}`);
89
+ }
90
+
91
+ function noticeReasons(el, name, container, h) {
92
+ const role = String(el.attrs.role ?? '').toLowerCase();
93
+ const reasons = [];
94
+ if (['alert', 'status', 'alertdialog'].includes(role)) reasons.push(`role=${role}`);
95
+ if (el.attrs['aria-live'] && el.attrs['aria-live'] !== 'off') reasons.push(`aria-live=${el.attrs['aria-live']}`);
96
+ const noticeClass = h.classesOf(el).find((c) => h.NOTICE_CLASS.test(c));
97
+ if (noticeClass) reasons.push(`class ${noticeClass}`);
98
+ const tone = container ? h.toneOf(el) : null;
99
+ if (tone && tone.tone !== 'neutral') reasons.push(`a ${tone.raw}-toned ${name}`);
100
+ if (container) addOutcomeTileReason(el, reasons, h);
101
+ return reasons;
102
+ }
103
+
104
+ function addOutcomeTileReason(el, reasons, h) {
105
+ const tiles = h.walkElements(el).filter((d) => h.componentNameOf(d) === 'IconTile' && h.ancestorsOf(d).find((a) => h.CONTAINERS.has(h.componentNameOf(a))) === el);
106
+ const outcomeTile = tiles.find((t) => h.OUTCOME_STATES.has(h.toneOf(t)?.tone));
107
+ if (outcomeTile && h.walkElements(el).some((d) => h.isAction(d))) reasons.push(`an ${h.toneOf(outcomeTile).raw}-toned IconTile beside an action`);
108
+ }
109
+
110
+ function ratioFindings(all, add, h) {
111
+ for (const el of all) ratioFindingFor(el, add, h);
112
+ }
113
+
114
+ function ratioFindingFor(el, add, h) {
115
+ if (h.insideSvg(el)) return;
116
+ const name = h.componentNameOf(el);
117
+ const role = String(el.attrs.role ?? '').toLowerCase();
118
+ const inMeter = h.inComponent(el, ['Meter']);
119
+ if ((el.tag === 'meter' || role === 'meter') && !inMeter) add(h.DRAW_RATIO_NOT_METER, 'meter outside Meter', el, 'a measured ratio renders through DNA Meter');
120
+ if ((el.tag === 'progress' || role === 'progressbar') && !inMeter && !h.inComponent(el, ['Progress', 'ProgressCircle', 'Slider'])) add(h.DRAW_RATIO_NOT_METER, 'hand-made progress', el, 'a hand-built progress bar; a ratio is DNA Meter');
121
+ if (name === 'Progress' || name === 'ProgressCircle') progressRatioFinding(el, add, h);
122
+ if (!name && !inMeter && !h.inComponent(el, ['Progress', 'ProgressCircle', 'Slider', 'Rating']) && h.classesOf(el).some((c) => h.BAR_CLASS.test(c))) handMadeBarFinding(el, add, h);
123
+ if (h.componentRootOf(el) === 'Meter') meterComponentFindings(el, add, h);
124
+ }
125
+
126
+ function progressRatioFinding(el, add, h) {
127
+ const scope = el.parent ?? el;
128
+ if (h.RATIO_RX.test(h.textOf(scope))) add(h.DRAW_RATIO_NOT_METER, 'ratio as Progress', el, `"${h.RATIO_RX.exec(h.textOf(scope))[0]}" is a ratio: Meter, never Progress`);
129
+ }
130
+
131
+ function handMadeBarFinding(el, add, h) {
132
+ const scope = el.parent?.parent ?? el.parent ?? el;
133
+ if (h.RATIO_RX.test(h.textOf(scope))) add(h.DRAW_RATIO_NOT_METER, 'hand-made bar', el, `a bar beside the ratio "${h.RATIO_RX.exec(h.textOf(scope))[0]}": one DNA Meter`);
134
+ }
135
+
136
+ function meterComponentFindings(el, add, h) {
137
+ // Grammar 0.5.2: a segmented Meter is DNA `Meter segments` - valid when its segments carry the DNA anatomy
138
+ // (meter-segment part, starci-core-meter-segment class or the data-grammar-meter-segment hook); a hand-cut one fails.
139
+ const segments = h.walkElements(el).filter((d) => /segment/i.test(`${d.attrs[h.PART_ATTR] ?? ''} ${h.classesOf(d).join(' ')}`));
140
+ const handCut = segments.filter((d) => !h.dnaSegment(d) && (d.attrs[h.PROPOSAL_ATTR] ?? '').trim() !== h.METER_SEGMENTS_PROPOSAL);
141
+ if (handCut.length) add(h.DRAW_RATIO_NOT_METER, 'hand-segmented Meter', el, 'a segmented Meter is DNA Meter segments (2..12): its segments carry data-grammar-part="meter-segment", never hand-cut bars');
142
+ const meterEl = [el, ...h.walkElements(el)].find((d) => d.tag === 'meter' || String(d.attrs.role ?? '').toLowerCase() === 'meter');
143
+ if (!meterEl) add(h.DRAW_RATIO_NOT_METER, 'Meter without role=meter', el, 'one role=meter (or <meter>) carries the value (DNA claims A11Y-3)');
144
+ else if (meterEl.tag !== 'meter' && (meterEl.attrs['aria-valuenow'] == null || meterEl.attrs['aria-valuemax'] == null)) add(h.DRAW_RATIO_NOT_METER, 'Meter without its value', meterEl, 'role=meter needs aria-valuenow and aria-valuemax');
145
+ }
146
+
147
+ function alertFindings(all, dna, rules, add, h) {
148
+ for (const el of all.filter((e) => h.componentRootOf(e) === 'Alert')) alertFindingFor(el, dna, rules, add, h);
149
+ }
150
+
151
+ function alertFindingFor(el, dna, rules, add, h) {
152
+ const bg = h.declaredBackgroundsOf(el, rules).find((b) => !h.surfaceBackground(b.value));
153
+ if (bg) add(h.DRAW_ALERT_ANATOMY, 'tone-filled Alert', el, `${bg.via} sets background ${bg.value}: the HeroUI Alert is bg-surface (white) with shadow-surface - its tone lives in the indicator glyph and the title, never a fill`);
154
+ const inside = h.walkElements(el).filter((d) => !h.ancestorsOf(d).slice(0, h.ancestorsOf(d).indexOf(el)).some((a) => h.componentRootOf(a) === 'Alert'));
155
+ const tone = h.toneOf(el);
156
+ const wanted = tone ? h.alertActionVariantFor(tone.tone) : null;
157
+ for (const tile of inside.filter((d) => h.componentNameOf(d) === 'IconTile')) add(h.DRAW_ALERT_ANATOMY, 'IconTile in an Alert', tile, 'an Alert carries no IconTile: its indicator is the HeroUI size-4 glyph (alert__indicator)');
158
+ const partOf = (d) => (d.attrs[h.PART_ATTR] ?? '').trim();
159
+ const indicator = inside.find((d) => ['alert-indicator', 'indicator'].includes(partOf(d)));
160
+ const title = inside.find((d) => ['alert-title', 'title', 'alert-content', 'content'].includes(partOf(d)));
161
+ indicatorAndTitleFindings(el, inside, indicator, title, add, h);
162
+ alertActionFindings(inside, tone, wanted, dna, add, h);
163
+ }
164
+
165
+ function indicatorAndTitleFindings(el, inside, indicator, title, add, h) {
166
+ if (indicator) {
167
+ const px = Math.max(h.declaredPx(indicator, 'width') ?? 0, h.declaredPx(indicator, 'height') ?? 0, ...h.walkElements(indicator).map((d) => Math.max(h.declaredPx(d, 'width') ?? 0, h.declaredPx(d, 'height') ?? 0)));
168
+ if (px >= h.ALERT_INDICATOR_MAX_PX) add(h.DRAW_ALERT_ANATOMY, 'Alert indicator as a tile', indicator, `the indicator declares ${px}px: the HeroUI indicator is a size-4 glyph with p-1, under ${h.ALERT_INDICATOR_MAX_PX}px`);
169
+ }
170
+ if (title && !indicator) add(h.DRAW_ALERT_ANATOMY, 'Alert without its indicator', el, 'the HeroUI Alert shows its tone glyph (alert-indicator) on the left of the title');
171
+ if (title && indicator && (inside.indexOf(indicator) > inside.indexOf(title) || h.walkElements(title).includes(indicator))) add(h.DRAW_ALERT_ANATOMY, 'Alert indicator not left of the title', indicator, 'the indicator is the first item of the Alert row, left of the content (title, description)');
172
+ }
173
+
174
+ function alertActionFindings(inside, tone, wanted, dna, add, h) {
175
+ for (const el of inside) {
176
+ const name = h.componentNameOf(el);
177
+ const interactive = el.tag === 'button' || (el.tag === 'a' && el.attrs.href != null) || String(el.attrs.role ?? '').toLowerCase() === 'button';
178
+ if (interactive && !h.ACTION_COMPONENTS.has(name) && !h.inComponent(el, [...h.ACTION_COMPONENTS])) add(h.DRAW_ALERT_ANATOMY, 'hand-made Alert action', el, `an Alert action is the grammar Button (variant="${wanted ?? 'secondary'}" for this tone, as the grammar Alert renders it), never a hand-built control`);
179
+ if (name === 'Button') alertButtonVariantFinding(el, tone, wanted, dna, add, h);
180
+ }
181
+ }
182
+
183
+ function alertButtonVariantFinding(el, tone, wanted, dna, add, h) {
184
+ const allowed = dna.components.get('Button')?.closed.get('variant')?.values;
185
+ const vendor = h.classesOf(el).map((c) => /^button--([a-z-]+)$/.exec(c)?.[1]).find((v) => v && (!allowed || allowed.includes(v)));
186
+ const variant = el.attrs['data-variant'] ?? el.attrs['data-grammar-variant'] ?? vendor;
187
+ const toneWord = tone ? `${tone.raw} (${tone.tone})` : '';
188
+ if (variant != null && allowed && !allowed.includes(variant)) add(h.DRAW_ALERT_ANATOMY, 'Alert action variant off DNA', el, `Button variant="${variant}" is not one of ${allowed.join('|')}; the Alert action is variant="${wanted ?? 'secondary'}"`);
189
+ else if (variant != null && wanted && variant !== wanted) add(h.DRAW_ALERT_ANATOMY, 'Alert action variant off its tone', el, `a ${toneWord} Alert's action is Button variant="${wanted}" (grammar Alert 0.5.3, after HeroUI: informative -> primary, negative -> danger, else secondary), not "${variant}"`);
190
+ }
191
+
192
+ function meterTrackFindings(all, rules, add, h) {
193
+ for (const el of all.filter((e) => h.componentRootOf(e) === 'Meter')) meterTrackFindingFor(el, rules, add, h);
194
+ }
195
+
196
+ function meterTrackFindingFor(el, rules, add, h) {
197
+ const tracks = h.walkElements(el).filter((d) => /(^|-)track$/.test((d.attrs[h.PART_ATTR] ?? '').trim()) || h.classesOf(d).some((c) => /(^|[-_])track$/.test(c)));
198
+ const want = h.segmentedMeter(el) ? h.METER_SEGMENTED_TRACK_PX : h.METER_TRACK_PX;
199
+ for (const track of tracks) {
200
+ const height = h.declaredPx(track, 'height', rules);
201
+ if (height != null && Math.abs(height - want) > 0.5) add(h.DRAW_METER_TRACK, 'Meter track off its height', track, 'the track declares ' + height + 'px: the ' + (want === h.METER_TRACK_PX ? 'HeroUI Meter track is h-2 (' + h.METER_TRACK_PX + 'px)' : 'segmented Meter track is h-1 (' + h.METER_SEGMENTED_TRACK_PX + 'px)'));
202
+ }
203
+ for (const item of [el, ...tracks]) {
204
+ const width = h.declaredPx(item, 'width', rules) ?? h.declaredPx(item, 'max-width', rules);
205
+ if (width != null) add(h.DRAW_METER_TRACK, 'Meter as a stub', item, `a fixed ${width}px width: the Meter track spans the full width of its band (w-full), its segments dividing that width equally`);
206
+ }
207
+ }
208
+
209
+ function artworkFindings(all, assetRequests, add, h) {
210
+ for (const el of all) artworkFindingFor(el, assetRequests, add, h);
211
+ }
212
+
213
+ function artworkFindingFor(el, assetRequests, add, h) {
214
+ if (h.insideSvg(el) || !h.isArtwork(el)) return;
215
+ const slotEl = [el, ...h.ancestorsOf(el)].find((a) => (a.attrs?.[h.ASSET_SLOT_ATTR] ?? '').trim());
216
+ if (!slotEl) {
217
+ add(h.DRAW_ASSET_SLOT_UNDECLARED, 'artwork without an asset slot', el, `mark it ${h.ASSET_SLOT_ATTR}="<id>" (a placeholder is fine) and request it in asset-request.md: interface.asset owes the artwork, never a reused file`);
218
+ return;
219
+ }
220
+ const id = slotEl.attrs[h.ASSET_SLOT_ATTR].trim();
221
+ if (assetRequests && !assetRequests.has(id)) add(h.DRAW_ASSET_SLOT_UNDECLARED, 'asset slot without a request', slotEl, `${h.ASSET_SLOT_ATTR}="${id}" has no entry in the drawing's asset-request.md`);
222
+ }
223
+
224
+ function badgeDotFindings(all, rules, add, h) {
225
+ for (const el of all) badgeDotFindingFor(el, rules, add, h);
226
+ }
227
+
228
+ function badgeDotFindingFor(el, rules, add, h) {
229
+ if (h.insideSvg(el)) return;
230
+ const part = (el.attrs[h.PART_ATTR] ?? '').trim();
231
+ const cls = h.classesOf(el);
232
+ const dotLike = /(^|-)dot$/.test(part) || cls.some((c) => h.DOT_CLASS.test(c));
233
+ if (!dotLike) return;
234
+ const dnaDot = ['badge-dot', 'dot'].includes(part) || cls.includes(h.BADGE_DOT_CLASS);
235
+ const inBadge = h.inComponent(el, ['Badge']);
236
+ if (!dnaDot || !inBadge) {
237
+ add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'hand-made status dot', el, `a status dot is DNA Badge isDot (data-grammar-part="badge-dot" / ${h.BADGE_DOT_CLASS} inside a Badge), never a hand-drawn dot`);
238
+ return;
239
+ }
240
+ const inline = h.declsOf(el.attrs.style);
241
+ const ruled = rules.filter((r) => h.selectorMatches(r.selector, el)).map((r) => r.decls);
242
+ const ring = [inline, ...ruled].some((d) => ['box-shadow', 'outline', 'border'].some((key) => d[key] && !/^(none|0|0px)$/i.test(d[key].trim())))
243
+ || cls.some((c) => /^(ring|shadow|outline)(-|$)/.test(c) && !/^(ring|shadow|outline)-none$/.test(c));
244
+ if (ring) add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'Badge dot with a halo', el, 'the Badge dot is a plain 6px solid circle in the tone colour: no box-shadow, ring, outline or border halo');
245
+ const px = Math.max(h.declaredPx(el, 'width', rules) ?? 0, h.declaredPx(el, 'height', rules) ?? 0, Number(el.attrs.width) || 0, Number(el.attrs.height) || 0);
246
+ if (px && Math.abs(px - h.BADGE_DOT_PX) > 0.5) add(h.DRAW_OFF_GRAMMAR_COMPONENT, 'Badge dot off its size', el, `the dot declares ${px}px: the Badge dot is ${h.BADGE_DOT_PX}px (<CircleFill width={6} />), no size variants`);
247
+ }
248
+
249
+ function groupedFindings(groups, label) {
250
+ const out = [];
251
+ for (const group of groups.values()) {
252
+ out.push({ code: group.code, kind: group.kind, count: group.items.length, examples: group.items.slice(0, 8),
253
+ detail: label + ': ' + group.items.length + ' ' + group.kind + (group.items.length > 1 ? 's' : '') + ' - ' + group.items.slice(0, 4).join('; ') + (group.items.length > 4 ? ' (+' + (group.items.length - 4) + ')' : '') });
254
+ }
255
+ return out;
256
+ }
@@ -0,0 +1,38 @@
1
+ // draw-dna-selector.mjs - the attribute parts of a CSS compound selector, for draw-dna's matching of <style> rules.
2
+
3
+ // An attribute selector after its '[': the name, then (when a value follows) the operator and the value up to the first ']'.
4
+ const ATTRIBUTE_NAME_RX = /[\w:-]+/y;
5
+ const ATTRIBUTE_VALUE_RX = /\s*[~|^$*]?=\s*(?!\s)["']?([^"'\]]*)["']?\]/y;
6
+
7
+ /** The text without its [...] attribute blocks: each '[' with a later ']' is removed up to that first ']'. */
8
+ export function withoutAttributeBlocks(text) {
9
+ let out = '';
10
+ let at = 0;
11
+ for (let open = text.indexOf('['); open >= 0; open = text.indexOf('[', at)) {
12
+ const close = text.indexOf(']', open + 1);
13
+ if (close < 0) break;
14
+ out += text.slice(at, open);
15
+ at = close + 1;
16
+ }
17
+ return out + text.slice(at);
18
+ }
19
+
20
+ /** The attribute selectors of a compound selector, left to right: [{name, value}] with value null when none is given. */
21
+ export function attributeSelectorsOf(compound) {
22
+ const found = [];
23
+ const openAfter = (from) => compound.indexOf('[', from + 1);
24
+ let open = compound.indexOf('[');
25
+ while (open >= 0) {
26
+ ATTRIBUTE_NAME_RX.lastIndex = open + 1;
27
+ const name = ATTRIBUTE_NAME_RX.exec(compound)?.[0];
28
+ const afterName = ATTRIBUTE_NAME_RX.lastIndex;
29
+ ATTRIBUTE_VALUE_RX.lastIndex = afterName;
30
+ const valued = name === undefined ? null : ATTRIBUTE_VALUE_RX.exec(compound);
31
+ const closed = name !== undefined && (valued || compound[afterName] === ']');
32
+ if (closed) found.push({ name, value: valued ? valued[1] : null });
33
+ let resume = open;
34
+ if (closed) resume = valued ? ATTRIBUTE_VALUE_RX.lastIndex - 1 : afterName;
35
+ open = openAfter(resume);
36
+ }
37
+ return found;
38
+ }