lazycodex-ai 5.1.45 → 5.1.46

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 (236) hide show
  1. package/README.de.md +0 -5
  2. package/README.es.md +0 -5
  3. package/README.fr.md +0 -5
  4. package/README.it.md +0 -5
  5. package/README.ja.md +0 -5
  6. package/README.ko.md +0 -5
  7. package/README.md +4 -5
  8. package/README.pt-br.md +0 -5
  9. package/README.ru.md +0 -6
  10. package/README.zh-cn.md +0 -5
  11. package/dist/cli/index.js +76 -156
  12. package/dist/cli-node/index.js +76 -156
  13. package/package.json +1 -1
  14. package/packages/lsp-daemon/dist/cli.js +3 -3
  15. package/packages/lsp-daemon/dist/client.js +3 -3
  16. package/packages/lsp-daemon/dist/index.js +3 -3
  17. package/packages/lsp-tools-mcp/dist/cli.js +3 -3
  18. package/packages/lsp-tools-mcp/dist/mcp.js +3 -3
  19. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  20. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +22 -12
  21. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  22. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  24. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  26. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  28. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  30. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  31. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  33. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  34. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  35. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  36. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  37. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  38. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  39. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  43. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  65. package/packages/omo-codex/plugin/package-lock.json +12 -12
  66. package/packages/omo-codex/plugin/package.json +1 -1
  67. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +8 -3
  68. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +0 -2
  69. package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
  70. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  71. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
  72. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  73. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +14 -2
  74. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +36 -11
  75. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +73 -13
  76. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
  77. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
  78. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  79. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +21 -4
  80. package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
  81. package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
  82. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  83. package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
  84. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +27 -6
  85. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  86. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  87. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  88. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  89. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  90. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  91. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
  92. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
  93. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
  94. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  95. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  96. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  97. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  98. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
  99. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  100. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  101. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  102. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
  103. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  104. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  105. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  106. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +28 -8
  107. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
  108. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
  109. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
  110. package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  111. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +6 -5
  112. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
  113. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  114. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  115. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
  116. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  117. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
  118. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
  119. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
  120. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  121. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  122. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  123. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  124. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  125. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  126. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  127. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  128. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +4 -4
  129. package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
  130. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +7 -3
  131. package/packages/omo-codex/scripts/install-dist/install-local.mjs +26 -16
  132. package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
  133. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  134. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
  135. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  136. package/packages/shared-skills/skills/ulw-research/AGENTS.md +14 -2
  137. package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
  138. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +73 -13
  139. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
  140. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
  141. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  142. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +21 -4
  143. package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
  144. package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
  145. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  146. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
  147. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
  148. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
  149. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +27 -6
  150. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +3 -0
  151. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
  152. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
  153. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  154. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  155. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  156. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  157. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
  158. package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  159. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  160. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
  161. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +9 -2
  162. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
  163. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +18 -3
  164. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
  165. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
  166. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
  167. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +2 -0
  168. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  169. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
  170. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  171. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
  172. package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  173. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
  174. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
  175. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  176. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
  177. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
  178. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  179. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  180. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
  181. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  182. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
  183. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  184. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
  185. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  186. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
  187. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  188. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
  189. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +28 -8
  190. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
  191. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
  192. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
  193. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
  194. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  195. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
  196. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +6 -5
  197. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
  198. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
  199. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  200. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
  201. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  202. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
  203. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
  204. package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
  205. package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  206. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
  207. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
  208. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
  209. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
  210. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  211. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
  212. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
  213. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
  214. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
  215. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
  216. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
  217. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
  218. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
  219. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
  220. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
  221. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
  222. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
  223. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
  224. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
  225. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
  226. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
  227. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
  228. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
  229. package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  230. package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  231. package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  232. package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  233. package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  234. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  235. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  236. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +4 -4
@@ -0,0 +1,420 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "rows": [
4
+ {
5
+ "fact_id": "F1",
6
+ "kind": "measured",
7
+ "raw": "320 m",
8
+ "quote": "The bridge span is up to 320 m long.",
9
+ "extracted_by": "tool:source-snapshot@1",
10
+ "observed_at": "2026-10-01T00:00:00Z",
11
+ "label": "span_len",
12
+ "value": "320",
13
+ "unit": "m",
14
+ "value_span": [
15
+ 25,
16
+ 30
17
+ ],
18
+ "source_id": "S4",
19
+ "locator": {
20
+ "artifact": "text.txt",
21
+ "char_start": 0,
22
+ "char_end": 36
23
+ },
24
+ "locate_hint": null,
25
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
26
+ "valid_at": "2026",
27
+ "qualifier": "upper_inclusive",
28
+ "entity": "Bridge",
29
+ "sentence_id": "S4:s1",
30
+ "observation_id": "O1",
31
+ "fuzzy": false,
32
+ "outside_floor": false,
33
+ "population": null,
34
+ "missing_rule": null,
35
+ "accounting_basis": null,
36
+ "snapshot_window": null,
37
+ "revision": null,
38
+ "exact": {
39
+ "num": "320",
40
+ "den": "1"
41
+ },
42
+ "render": null,
43
+ "operation": null,
44
+ "operands": [],
45
+ "derived_from": [],
46
+ "formula": null,
47
+ "factor": null,
48
+ "rationale": null,
49
+ "status": "active"
50
+ },
51
+ {
52
+ "fact_id": "F2",
53
+ "kind": "measured",
54
+ "raw": "1,237",
55
+ "quote": "Units sold reached 1,237 in 2026.",
56
+ "extracted_by": "tool:source-snapshot@1",
57
+ "observed_at": "2026-10-01T00:00:00Z",
58
+ "label": "units_sold",
59
+ "value": "1237",
60
+ "unit": null,
61
+ "value_span": [
62
+ 19,
63
+ 24
64
+ ],
65
+ "source_id": "S4",
66
+ "locator": {
67
+ "artifact": "text.txt",
68
+ "char_start": 37,
69
+ "char_end": 70
70
+ },
71
+ "locate_hint": null,
72
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
73
+ "valid_at": "2026",
74
+ "qualifier": null,
75
+ "entity": null,
76
+ "sentence_id": "S4:s2",
77
+ "observation_id": "O1",
78
+ "fuzzy": false,
79
+ "outside_floor": false,
80
+ "population": null,
81
+ "missing_rule": null,
82
+ "accounting_basis": null,
83
+ "snapshot_window": null,
84
+ "revision": null,
85
+ "exact": {
86
+ "num": "1237",
87
+ "den": "1"
88
+ },
89
+ "render": null,
90
+ "operation": null,
91
+ "operands": [],
92
+ "derived_from": [],
93
+ "formula": null,
94
+ "factor": null,
95
+ "rationale": null,
96
+ "status": "active"
97
+ },
98
+ {
99
+ "fact_id": "F3",
100
+ "kind": "measured",
101
+ "raw": "12%",
102
+ "quote": "Revenue grew 12% year over year.",
103
+ "extracted_by": "tool:source-snapshot@1",
104
+ "observed_at": "2026-10-01T00:00:00Z",
105
+ "label": "rate_yoy",
106
+ "value": "12",
107
+ "unit": "%",
108
+ "value_span": [
109
+ 13,
110
+ 16
111
+ ],
112
+ "source_id": "S4",
113
+ "locator": {
114
+ "artifact": "text.txt",
115
+ "char_start": 71,
116
+ "char_end": 103
117
+ },
118
+ "locate_hint": null,
119
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
120
+ "valid_at": "2026 YoY",
121
+ "qualifier": null,
122
+ "entity": null,
123
+ "sentence_id": "S4:s3",
124
+ "observation_id": "O2",
125
+ "fuzzy": false,
126
+ "outside_floor": false,
127
+ "population": null,
128
+ "missing_rule": null,
129
+ "accounting_basis": null,
130
+ "snapshot_window": null,
131
+ "revision": null,
132
+ "exact": {
133
+ "num": "12",
134
+ "den": "1"
135
+ },
136
+ "render": null,
137
+ "operation": null,
138
+ "operands": [],
139
+ "derived_from": [],
140
+ "formula": null,
141
+ "factor": null,
142
+ "rationale": null,
143
+ "status": "active"
144
+ },
145
+ {
146
+ "fact_id": "F4",
147
+ "kind": "measured",
148
+ "raw": "12%",
149
+ "quote": "Revenue grew 12% quarter over quarter.",
150
+ "extracted_by": "tool:source-snapshot@1",
151
+ "observed_at": "2026-10-01T00:00:00Z",
152
+ "label": "rate_qoq",
153
+ "value": "12",
154
+ "unit": "%",
155
+ "value_span": [
156
+ 13,
157
+ 16
158
+ ],
159
+ "source_id": "S4",
160
+ "locator": {
161
+ "artifact": "text.txt",
162
+ "char_start": 104,
163
+ "char_end": 142
164
+ },
165
+ "locate_hint": null,
166
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
167
+ "valid_at": "2026 Q3 QoQ",
168
+ "qualifier": null,
169
+ "entity": null,
170
+ "sentence_id": "S4:s4",
171
+ "observation_id": "O3",
172
+ "fuzzy": false,
173
+ "outside_floor": false,
174
+ "population": null,
175
+ "missing_rule": null,
176
+ "accounting_basis": null,
177
+ "snapshot_window": null,
178
+ "revision": null,
179
+ "exact": {
180
+ "num": "12",
181
+ "den": "1"
182
+ },
183
+ "render": null,
184
+ "operation": null,
185
+ "operands": [],
186
+ "derived_from": [],
187
+ "formula": null,
188
+ "factor": null,
189
+ "rationale": null,
190
+ "status": "active"
191
+ },
192
+ {
193
+ "fact_id": "F5",
194
+ "kind": "measured",
195
+ "raw": "12%",
196
+ "quote": "Segment A held 12% of the market and segment B held 27%.",
197
+ "extracted_by": "tool:source-snapshot@1",
198
+ "observed_at": "2026-10-01T00:00:00Z",
199
+ "label": "seg_a",
200
+ "value": "12",
201
+ "unit": "%",
202
+ "value_span": [
203
+ 15,
204
+ 18
205
+ ],
206
+ "source_id": "S4",
207
+ "locator": {
208
+ "artifact": "text.txt",
209
+ "char_start": 143,
210
+ "char_end": 199
211
+ },
212
+ "locate_hint": null,
213
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
214
+ "valid_at": null,
215
+ "qualifier": null,
216
+ "entity": null,
217
+ "sentence_id": "S4:s5",
218
+ "observation_id": null,
219
+ "fuzzy": false,
220
+ "outside_floor": false,
221
+ "population": null,
222
+ "missing_rule": null,
223
+ "accounting_basis": null,
224
+ "snapshot_window": null,
225
+ "revision": null,
226
+ "exact": {
227
+ "num": "12",
228
+ "den": "1"
229
+ },
230
+ "render": null,
231
+ "operation": null,
232
+ "operands": [],
233
+ "derived_from": [],
234
+ "formula": null,
235
+ "factor": null,
236
+ "rationale": null,
237
+ "status": "active"
238
+ },
239
+ {
240
+ "fact_id": "F6",
241
+ "kind": "measured",
242
+ "raw": "27%",
243
+ "quote": "Segment A held 12% of the market and segment B held 27%.",
244
+ "extracted_by": "tool:source-snapshot@1",
245
+ "observed_at": "2026-10-01T00:00:00Z",
246
+ "label": "seg_b",
247
+ "value": "27",
248
+ "unit": "%",
249
+ "value_span": [
250
+ 52,
251
+ 55
252
+ ],
253
+ "source_id": "S4",
254
+ "locator": {
255
+ "artifact": "text.txt",
256
+ "char_start": 143,
257
+ "char_end": 199
258
+ },
259
+ "locate_hint": null,
260
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
261
+ "valid_at": null,
262
+ "qualifier": null,
263
+ "entity": null,
264
+ "sentence_id": "S4:s5",
265
+ "observation_id": null,
266
+ "fuzzy": false,
267
+ "outside_floor": false,
268
+ "population": null,
269
+ "missing_rule": null,
270
+ "accounting_basis": null,
271
+ "snapshot_window": null,
272
+ "revision": null,
273
+ "exact": {
274
+ "num": "27",
275
+ "den": "1"
276
+ },
277
+ "render": null,
278
+ "operation": null,
279
+ "operands": [],
280
+ "derived_from": [],
281
+ "formula": null,
282
+ "factor": null,
283
+ "rationale": null,
284
+ "status": "active"
285
+ },
286
+ {
287
+ "fact_id": "F7",
288
+ "kind": "measured",
289
+ "raw": "42 passed",
290
+ "quote": "42 passed",
291
+ "extracted_by": "tool:source-snapshot@1",
292
+ "observed_at": "2026-10-01T00:00:00Z",
293
+ "label": "test_pass",
294
+ "value": "42",
295
+ "unit": null,
296
+ "value_span": [
297
+ 0,
298
+ 9
299
+ ],
300
+ "source_id": "S5",
301
+ "locator": {
302
+ "artifact": "text.txt",
303
+ "char_start": 0,
304
+ "char_end": 9
305
+ },
306
+ "locate_hint": null,
307
+ "snapshot_sha256": "e6d47cc01ac7d20e7ba7acd6d1ad113573c1991f02c5af5f0f64a0572e0607f1",
308
+ "valid_at": null,
309
+ "qualifier": null,
310
+ "entity": null,
311
+ "sentence_id": "S5:s1",
312
+ "observation_id": null,
313
+ "fuzzy": false,
314
+ "outside_floor": false,
315
+ "population": null,
316
+ "missing_rule": null,
317
+ "accounting_basis": null,
318
+ "snapshot_window": null,
319
+ "revision": null,
320
+ "exact": {
321
+ "num": "42",
322
+ "den": "1"
323
+ },
324
+ "render": null,
325
+ "operation": null,
326
+ "operands": [],
327
+ "derived_from": [],
328
+ "formula": null,
329
+ "factor": null,
330
+ "rationale": null,
331
+ "status": "active"
332
+ },
333
+ {
334
+ "fact_id": "Q1",
335
+ "kind": "quote",
336
+ "raw": "we estimate completion no earlier than 2027",
337
+ "quote": "we estimate completion no earlier than 2027",
338
+ "extracted_by": "tool:source-snapshot@1",
339
+ "observed_at": "2026-10-01T00:00:00Z",
340
+ "label": "completion",
341
+ "value": null,
342
+ "unit": null,
343
+ "value_span": null,
344
+ "source_id": "S4",
345
+ "locator": {
346
+ "artifact": "text.txt",
347
+ "char_start": 215,
348
+ "char_end": 258
349
+ },
350
+ "locate_hint": null,
351
+ "snapshot_sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
352
+ "valid_at": null,
353
+ "qualifier": null,
354
+ "entity": "Operator",
355
+ "sentence_id": "S4:s6",
356
+ "observation_id": null,
357
+ "fuzzy": false,
358
+ "outside_floor": false,
359
+ "population": null,
360
+ "missing_rule": null,
361
+ "accounting_basis": null,
362
+ "snapshot_window": null,
363
+ "revision": null,
364
+ "exact": null,
365
+ "render": null,
366
+ "operation": null,
367
+ "operands": [],
368
+ "derived_from": [],
369
+ "formula": null,
370
+ "factor": null,
371
+ "rationale": null,
372
+ "status": "active"
373
+ },
374
+ {
375
+ "fact_id": "F8",
376
+ "kind": "converted",
377
+ "raw": "320 m",
378
+ "quote": "",
379
+ "extracted_by": "tool:source-snapshot@1",
380
+ "observed_at": "2026-10-01T00:00:00Z",
381
+ "label": "span_mm",
382
+ "value": "320000",
383
+ "unit": "mm",
384
+ "value_span": null,
385
+ "source_id": null,
386
+ "locator": null,
387
+ "locate_hint": null,
388
+ "snapshot_sha256": null,
389
+ "valid_at": null,
390
+ "qualifier": null,
391
+ "entity": null,
392
+ "sentence_id": null,
393
+ "observation_id": null,
394
+ "fuzzy": false,
395
+ "outside_floor": false,
396
+ "population": null,
397
+ "missing_rule": null,
398
+ "accounting_basis": null,
399
+ "snapshot_window": null,
400
+ "revision": null,
401
+ "exact": {
402
+ "num": "320000",
403
+ "den": "1"
404
+ },
405
+ "render": "320,000 mm (320 m)",
406
+ "operation": "convert",
407
+ "operands": [
408
+ "F1"
409
+ ],
410
+ "derived_from": [
411
+ "F1"
412
+ ],
413
+ "formula": "F1 * 1000",
414
+ "factor": "1000",
415
+ "rationale": null,
416
+ "status": "active"
417
+ }
418
+ ],
419
+ "events": []
420
+ }
@@ -0,0 +1,19 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head><meta charset="utf-8"><title>Bridge report</title></head>
4
+ <body>
5
+ <h1>Bridge report</h1>
6
+ <h2>Findings</h2>
7
+ <p>Revenue grew 12% [S4].</p>
8
+ <p>The span is <data class="fact" value="320" data-fact="F1" data-unit="m" data-lineage="MEASURED">up to 320 mm</data> long [S4].</p>
9
+ <p>Units sold reached <data class="fact" value="1237" data-fact="F2" data-lineage="MEASURED">1,273</data> [S4].</p>
10
+ <p>The operator wrote <q data-fact="Q1" data-quote-sel="full">we estimate completion no earlier than 2028</q> [S4].</p>
11
+ <p>Next year brings {{F9:growth}} more traffic [S4].</p>
12
+ <p>The bridge is popular with tourists.</p>
13
+ <p data-claim="C99">The design is the safest in the region.</p>
14
+ <h2>Sources</h2>
15
+ <ol>
16
+ <li id="s4">[S4] Bridge authority page, retrieved 2026-10-01.</li>
17
+ </ol>
18
+ </body>
19
+ </html>
@@ -0,0 +1,39 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head><meta charset="utf-8"><title>Bridge report</title></head>
4
+ <body>
5
+ <h1>Bridge report</h1>
6
+ <h2>Findings</h2>
7
+ <p>The span is <data class="fact" value="320" data-fact="F1" data-unit="m" data-lineage="MEASURED">up to 320 m</data> long [S4].</p>
8
+ <p>In millimetres the span is <data class="fact" value="320000" data-fact="F8" data-unit="mm" data-lineage="DERIVED">320,000 mm (320 m)</data> [S4].</p>
9
+ <p>Units sold reached <data class="fact" value="1237" data-fact="F2" data-lineage="MEASURED">1,237</data> [S4].</p>
10
+ <p>The operator wrote <q data-fact="Q1" data-quote-sel="full">we estimate completion no earlier than 2027</q> [S4].</p>
11
+ <p>The schedule note opens with <q data-fact="Q1" data-quote-sel="lead=4">we estimate completion no…</q> [S4].</p>
12
+ <p>The opening date is <q data-fact="Q1" data-quote-sel="from=no earlier;to=2027">…no earlier than 2027</q> [S4].</p>
13
+ <p>The test suite ran with <data class="fact" value="42" data-fact="F7" data-claim-kind="verified" data-lineage="MEASURED">42 passed</data> [S5].</p>
14
+ <p data-kind="opinion">This looks like a sound plan.</p>
15
+ <p data-claim="C1">The design follows the published standard.</p>
16
+ <figure data-chart-source="assets/share.csv">
17
+ <svg viewBox="0 0 200 100" role="img"><g class="ticks" data-tick-step="10" data-tick-min="0" data-tick-max="30"><text x="0" y="90">0</text><text x="0" y="60">10</text><text x="0" y="30">20</text><text x="0" y="5">30</text></g><text x="40" y="50">12%</text><text x="120" y="20">27%</text></svg>
18
+ <figcaption>Market share by segment, in percent [S4].</figcaption>
19
+ </figure>
20
+ <table>
21
+ <thead><tr><th>Metric</th><th>Value</th></tr></thead>
22
+ <tbody>
23
+ <tr><td>Span</td><td><data class="fact" value="320" data-fact="F1" data-unit="m" data-lineage="MEASURED">320 m</data></td></tr>
24
+ <tr><td>Units sold</td><td><data class="fact" value="1237" data-fact="F2" data-lineage="MEASURED">1,237</data></td></tr>
25
+ </tbody>
26
+ </table>
27
+ <h2>Appendix</h2>
28
+ <p>Units sold in the base year were <data class="fact" value="1237" data-fact="F2" data-lineage="MEASURED">1,237</data> [S4].</p>
29
+ <h2>Gaps and contradictions</h2>
30
+ <p data-claim="C3">An early schedule claim was refuted.</p>
31
+ <h2>How this report was made</h2>
32
+ <p>Collected from the cited public page and one recorded test run [S4] [S5].</p>
33
+ <h2>Sources</h2>
34
+ <ol>
35
+ <li id="s4">[S4] Bridge authority page, retrieved 2026-10-01.</li>
36
+ <li id="s5">[S5] Test run output, recorded 2026-10-01.</li>
37
+ </ol>
38
+ </body>
39
+ </html>
@@ -0,0 +1,9 @@
1
+ {
2
+ "source_id": "S4",
3
+ "url": "https://example.org/bridge",
4
+ "observed_at": "2026-10-01T00:00:00Z",
5
+ "sha256": "89abfafc79d0e2c13a5ccad6843c020dc65e86f1eca385f85e7f58153960cdfe",
6
+ "extractor": "tool:source-snapshot@1",
7
+ "revision": null,
8
+ "kind": "page"
9
+ }
@@ -0,0 +1,7 @@
1
+ {"sid":"S4:s1","start":0,"end":36,"text":"The bridge span is up to 320 m long."}
2
+ {"sid":"S4:s2","start":37,"end":70,"text":"Units sold reached 1,237 in 2026."}
3
+ {"sid":"S4:s3","start":71,"end":103,"text":"Revenue grew 12% year over year."}
4
+ {"sid":"S4:s4","start":104,"end":142,"text":"Revenue grew 12% quarter over quarter."}
5
+ {"sid":"S4:s5","start":143,"end":199,"text":"Segment A held 12% of the market and segment B held 27%."}
6
+ {"sid":"S4:s6","start":200,"end":258,"text":"Operators said we estimate completion no earlier than 2027"}
7
+ {"sid":"S4:s7","start":259,"end":299,"text":"High Bandwidth Memory (HBM) demand rose."}
@@ -0,0 +1,7 @@
1
+ The bridge span is up to 320 m long.
2
+ Units sold reached 1,237 in 2026.
3
+ Revenue grew 12% year over year.
4
+ Revenue grew 12% quarter over quarter.
5
+ Segment A held 12% of the market and segment B held 27%.
6
+ Operators said we estimate completion no earlier than 2027
7
+ High Bandwidth Memory (HBM) demand rose.
@@ -0,0 +1,19 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head><title>Harbor Bridge progress note</title><style>p { margin: 0 }</style></head>
4
+ <body>
5
+ <h1>Harbor Bridge progress note</h1>
6
+ <p>Span length: 320 m</p>
7
+ <p>The deck uses High Bandwidth Memory (HBM) sensors for strain logging.</p>
8
+ <p>Main span cost: 12 million USD, and approach span cost: 4 million USD.</p>
9
+ <!-- internal draft comment -->
10
+ <p>After the delay, we estimate completion no earlier than 2027.</p>
11
+ <table id="dimensions">
12
+ <caption>Bridge dimensions</caption>
13
+ <tr><th>Metric</th><th>Length</th></tr>
14
+ <tr><td>Span</td><td>320 m</td></tr>
15
+ <tr><td>Approach</td><td>85 m</td></tr>
16
+ </table>
17
+ <script>console.log("not prose")</script>
18
+ </body>
19
+ </html>
@@ -0,0 +1,9 @@
1
+ {
2
+ "source_id": "S5",
3
+ "url": null,
4
+ "observed_at": "2026-10-01T00:00:00Z",
5
+ "sha256": "e6d47cc01ac7d20e7ba7acd6d1ad113573c1991f02c5af5f0f64a0572e0607f1",
6
+ "extractor": "tool:source-snapshot@1",
7
+ "revision": null,
8
+ "kind": "command"
9
+ }
@@ -0,0 +1 @@
1
+ {"sid":"S5:s1","start":0,"end":9,"text":"42 passed"}
@@ -0,0 +1 @@
1
+ 북미 클라우드향 HBM3E 매출은 2026년 3분기 연결 기준 최대 4조 2,000억 원에 달할 것으로 전망된다.
@@ -0,0 +1,101 @@
1
+ // Canonical factor algebra. Exponents are bigint; aliases enter only through normalizeInput.
2
+ import { fromDecimal, mul, sigFigsOf, toDecimal } from "./rational.mjs"
3
+ import { dimensionOf, normalizeUnit } from "./units.mjs"
4
+ import { refuse } from "./unit-errors.mjs"
5
+ export { refuse, RuleError } from "./unit-errors.mjs"
6
+
7
+ const SCALED = new Set(["bp", "basispoint", "basispoints"])
8
+ export const isPercentish = (unit) => unit === "%" || unit === "%p"
9
+ const scaled = (text) => SCALED.has(text.replace(/\s+/g, "").toLowerCase())
10
+
11
+ function atomOf(text) {
12
+ if (text === "1") return null
13
+ const atom = scaled(text) ? "%p" : normalizeUnit(text) ?? text
14
+ if (dimensionOf(atom) === "scale") refuse(`"${text}" is a scale word (${atom}), not a unit`, undefined, "scale_atom", "a counted unit with scale in the numeral")
15
+ return atom
16
+ }
17
+
18
+ const combine = (a, b, sign) => {
19
+ for (const [atom, power] of b) a.set(atom, (a.get(atom) ?? 0n) + sign * power)
20
+ return a
21
+ }
22
+
23
+ /** Iterative unit parser: factor maps plus the input's decimal scaling exponent. */
24
+ function parse(raw) {
25
+ if (raw === null) return { factors: new Map(), shift: 0n }
26
+ if (typeof raw !== "string" || !raw.trim()) refuse("unit must be text or explicit null", undefined, "unit_text", "canonical unit text or null")
27
+ const tokens = raw.match(/[*/()^]|[^*/()^]+/g).map((x) => x.trim()).filter(Boolean)
28
+ const frames = [{ factors: new Map(), shift: 0n, sign: 1n, need: true }]
29
+ let affine = false
30
+ const apply = (value, i) => {
31
+ if (tokens[i] === "^") {
32
+ const power = tokens[i + 1]
33
+ if (!/^[+-]?\d+$/.test(power ?? "")) refuse(`unit "${raw}" has a bad exponent`, undefined, "exponent", "an integer exponent")
34
+ const n = BigInt(power)
35
+ value = { factors: new Map([...value.factors].map(([a, p]) => [a, p * n])), shift: value.shift * n }
36
+ i += 2
37
+ }
38
+ const frame = frames.at(-1)
39
+ combine(frame.factors, value.factors, frame.sign)
40
+ frame.shift += frame.sign * value.shift
41
+ frame.need = false
42
+ return i
43
+ }
44
+ for (let i = 0; i < tokens.length;) {
45
+ const frame = frames.at(-1)
46
+ const token = tokens[i]
47
+ if (frame.need) {
48
+ if (token === "(") {
49
+ frames.push({ factors: new Map(), shift: 0n, sign: 1n, need: true })
50
+ i++
51
+ } else {
52
+ if (/^[*/()^]$/.test(token)) refuse(`unexpected "${token}" in unit "${raw}"`)
53
+ const atom = atomOf(token)
54
+ affine ||= dimensionOf(atom) === "temperature"
55
+ i = apply({ factors: new Map(atom === null ? [] : [[atom, 1n]]), shift: scaled(token) ? -2n : 0n }, i + 1)
56
+ }
57
+ } else if (token === ")" && frames.length > 1) {
58
+ const value = frames.pop()
59
+ i = apply(value, i + 1)
60
+ } else if (token === "*" || token === "/") {
61
+ frame.sign = token === "*" ? 1n : -1n
62
+ frame.need = true
63
+ i++
64
+ } else refuse(`unexpected "${token}" in unit "${raw}"`)
65
+ }
66
+ if (frames.length !== 1 || frames[0].need) refuse(`unit "${raw}" ends early or is missing a closing parenthesis`)
67
+ // Refuse affine composites before cancellation loses the atom that made them unsafe.
68
+ if (affine && (tokens.length !== 1 || frames[0].shift !== 0n)) refuse("affine (temperature) units cannot occur in composites", undefined, "affine_units", "a non-affine composite or bare temperature for convert")
69
+ return frames[0]
70
+ }
71
+
72
+ function format(factors) {
73
+ const side = (sign) => [...factors].filter(([, p]) => sign * p > 0n).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
74
+ .map(([a, p]) => { const n = sign * p; return n === 1n ? a : `${a}^${n}` })
75
+ const top = side(1n)
76
+ const bottom = side(-1n)
77
+ if (!top.length && !bottom.length) return null
78
+ return `${top.join("*") || "1"}${bottom.length ? `/${bottom.length === 1 ? bottom[0] : `(${bottom.join("*")})`}` : ""}`
79
+ }
80
+
81
+ export const canonicalUnit = (raw) => format(parse(raw).factors)
82
+ export function inputScale(raw) {
83
+ const { shift } = parse(raw)
84
+ if (shift === 0n) return null
85
+ if (shift > 10000n || shift < -10000n) refuse("scaled alias exponent exceeds exact input capacity", undefined, "scale_range", "an absolute decimal scale exponent <= 10000")
86
+ return shift > 0n ? `1${"0".repeat(Number(shift))}` : `0.${"0".repeat(Number(-shift - 1n))}1`
87
+ }
88
+
89
+ export function normalizeInput(unit, value) {
90
+ const canonical = canonicalUnit(unit)
91
+ const factor = inputScale(unit)
92
+ return { unit: canonical, value: factor ? toDecimal(mul(fromDecimal(value), fromDecimal(factor)), sigFigsOf(value)) : value }
93
+ }
94
+
95
+ export const unitTimes = (a, b) => format(combine(parse(a).factors, parse(b).factors, 1n))
96
+ export const unitOver = (a, b) => format(combine(parse(a).factors, parse(b).factors, -1n))
97
+ export const isAffine = (unit) => typeof unit === "string" && dimensionOf(unit) === "temperature"
98
+ export function isCurrencyRate(unit) {
99
+ const currencies = [...parse(unit).factors].filter(([a, n]) => n !== 0n && dimensionOf(a)?.startsWith("currency:"))
100
+ return currencies.some(([, n]) => n > 0n) && currencies.some(([, n]) => n < 0n)
101
+ }