@texuli/blocks 0.0.0-stage → 0.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -2
  3. package/dist/code/CodeBlockView.svelte +135 -0
  4. package/dist/code/CodeBlockView.svelte.d.ts +5 -0
  5. package/dist/code/check.d.ts +15 -0
  6. package/dist/code/check.js +14 -0
  7. package/dist/code/code.css +201 -0
  8. package/dist/code/code.css.d.ts +1 -0
  9. package/dist/code/engine-entry.d.ts +10 -0
  10. package/dist/code/engine-entry.js +10 -0
  11. package/dist/code/engine.d.ts +18 -0
  12. package/dist/code/engine.js +18 -0
  13. package/dist/code/family-engine.d.ts +23 -0
  14. package/dist/code/family-engine.js +4 -0
  15. package/dist/code/family.d.ts +7 -0
  16. package/dist/code/family.js +5 -0
  17. package/dist/code/grammars.d.ts +12 -0
  18. package/dist/code/grammars.js +83 -0
  19. package/dist/code/highlight.d.ts +15 -0
  20. package/dist/code/highlight.js +53 -0
  21. package/dist/code/index.d.ts +13 -0
  22. package/dist/code/index.js +12 -0
  23. package/dist/code/labels.d.ts +27 -0
  24. package/dist/code/labels.js +24 -0
  25. package/dist/code/languages/bash.d.ts +3 -0
  26. package/dist/code/languages/bash.js +48 -0
  27. package/dist/code/languages/css.d.ts +10 -0
  28. package/dist/code/languages/css.js +67 -0
  29. package/dist/code/languages/html.d.ts +6 -0
  30. package/dist/code/languages/html.js +35 -0
  31. package/dist/code/languages/javascript.d.ts +3 -0
  32. package/dist/code/languages/javascript.js +113 -0
  33. package/dist/code/languages/json.d.ts +3 -0
  34. package/dist/code/languages/json.js +24 -0
  35. package/dist/code/languages/python.d.ts +3 -0
  36. package/dist/code/languages/python.js +35 -0
  37. package/dist/code/languages/shared.d.ts +22 -0
  38. package/dist/code/languages/shared.js +37 -0
  39. package/dist/code/languages/sql.d.ts +3 -0
  40. package/dist/code/languages/sql.js +32 -0
  41. package/dist/code/languages/yaml.d.ts +7 -0
  42. package/dist/code/languages/yaml.js +36 -0
  43. package/dist/code/renderers.d.ts +5 -0
  44. package/dist/code/renderers.js +5 -0
  45. package/dist/code/token-styles.d.ts +25 -0
  46. package/dist/code/token-styles.js +51 -0
  47. package/dist/code/tokenize-code.d.ts +17 -0
  48. package/dist/code/tokenize-code.js +34 -0
  49. package/dist/code/tokenizer.d.ts +76 -0
  50. package/dist/code/tokenizer.js +171 -0
  51. package/dist/core/catalog/entries.d.ts +10 -0
  52. package/dist/core/catalog/entries.js +589 -0
  53. package/dist/core/catalog/index.d.ts +4 -0
  54. package/dist/core/catalog/index.js +2 -0
  55. package/dist/core/catalog/types.d.ts +36 -0
  56. package/dist/core/catalog/types.js +1 -0
  57. package/dist/core/catalog/validate.d.ts +15 -0
  58. package/dist/core/catalog/validate.js +46 -0
  59. package/dist/core/formula/evaluate.d.ts +14 -0
  60. package/dist/core/formula/evaluate.js +181 -0
  61. package/dist/core/formula/index.d.ts +8 -0
  62. package/dist/core/formula/index.js +6 -0
  63. package/dist/core/formula/parse.d.ts +19 -0
  64. package/dist/core/formula/parse.js +452 -0
  65. package/dist/core/formula/types.d.ts +76 -0
  66. package/dist/core/formula/types.js +7 -0
  67. package/dist/core/index.d.ts +14 -0
  68. package/dist/core/index.js +13 -0
  69. package/dist/core/limits.d.ts +27 -0
  70. package/dist/core/limits.js +26 -0
  71. package/dist/core/safety.d.ts +30 -0
  72. package/dist/core/safety.js +110 -0
  73. package/dist/core/schema/blocks.d.ts +138 -0
  74. package/dist/core/schema/blocks.js +181 -0
  75. package/dist/core/schema/code-family.d.ts +4 -0
  76. package/dist/core/schema/code-family.js +75 -0
  77. package/dist/core/schema/line-spec.d.ts +7 -0
  78. package/dist/core/schema/line-spec.js +19 -0
  79. package/dist/core/schema/media-family.d.ts +4 -0
  80. package/dist/core/schema/media-family.js +260 -0
  81. package/dist/core/schema/parse-family.d.ts +14 -0
  82. package/dist/core/schema/parse-family.js +37 -0
  83. package/dist/core/schema/primitives.d.ts +73 -0
  84. package/dist/core/schema/primitives.js +140 -0
  85. package/dist/core/schema/text-family.d.ts +4 -0
  86. package/dist/core/schema/text-family.js +121 -0
  87. package/dist/core/schema/vocabulary.d.ts +20 -0
  88. package/dist/core/schema/vocabulary.js +33 -0
  89. package/dist/core/text.d.ts +31 -0
  90. package/dist/core/text.js +139 -0
  91. package/dist/core/untrusted.d.ts +20 -0
  92. package/dist/core/untrusted.js +377 -0
  93. package/dist/css-modules.d.ts +1 -0
  94. package/dist/css.d.ts +1 -0
  95. package/dist/data/ChartBlockView.svelte +77 -0
  96. package/dist/data/ChartBlockView.svelte.d.ts +10 -0
  97. package/dist/data/DataBlockView.svelte +109 -0
  98. package/dist/data/DataBlockView.svelte.d.ts +5 -0
  99. package/dist/data/check.d.ts +10 -0
  100. package/dist/data/check.js +9 -0
  101. package/dist/data/engine.d.ts +18 -0
  102. package/dist/data/engine.js +10 -0
  103. package/dist/data/family.d.ts +6 -0
  104. package/dist/data/family.js +4 -0
  105. package/dist/data/index.d.ts +9 -0
  106. package/dist/data/index.js +8 -0
  107. package/dist/data/labels.d.ts +8 -0
  108. package/dist/data/labels.js +8 -0
  109. package/dist/data/renderers.d.ts +2 -0
  110. package/dist/data/renderers.js +2 -0
  111. package/dist/data/schema.d.ts +84 -0
  112. package/dist/data/schema.js +385 -0
  113. package/dist/diagram/BoardDiagram.svelte +48 -0
  114. package/dist/diagram/BoardDiagram.svelte.d.ts +8 -0
  115. package/dist/diagram/DiagramFamily.svelte +46 -0
  116. package/dist/diagram/DiagramFamily.svelte.d.ts +10 -0
  117. package/dist/diagram/FlowDiagram.svelte +108 -0
  118. package/dist/diagram/FlowDiagram.svelte.d.ts +8 -0
  119. package/dist/diagram/TerminalDiagram.svelte +38 -0
  120. package/dist/diagram/TerminalDiagram.svelte.d.ts +8 -0
  121. package/dist/diagram/TreeDiagram.svelte +149 -0
  122. package/dist/diagram/TreeDiagram.svelte.d.ts +8 -0
  123. package/dist/diagram/diagram.css +287 -0
  124. package/dist/diagram/diagram.css.d.ts +1 -0
  125. package/dist/diagram/engine.d.ts +16 -0
  126. package/dist/diagram/engine.js +14 -0
  127. package/dist/diagram/family.d.ts +7 -0
  128. package/dist/diagram/family.js +5 -0
  129. package/dist/diagram/index.d.ts +10 -0
  130. package/dist/diagram/index.js +9 -0
  131. package/dist/diagram/layout.d.ts +26 -0
  132. package/dist/diagram/layout.js +165 -0
  133. package/dist/diagram/schema.d.ts +74 -0
  134. package/dist/diagram/schema.js +358 -0
  135. package/dist/diagram/tones.d.ts +6 -0
  136. package/dist/diagram/tones.js +16 -0
  137. package/dist/embed/EmbedApp.svelte +138 -0
  138. package/dist/embed/EmbedApp.svelte.d.ts +17 -0
  139. package/dist/embed/EmbedBlockView.svelte +32 -0
  140. package/dist/embed/EmbedBlockView.svelte.d.ts +5 -0
  141. package/dist/embed/LinkPreview.svelte +48 -0
  142. package/dist/embed/LinkPreview.svelte.d.ts +10 -0
  143. package/dist/embed/engine.d.ts +30 -0
  144. package/dist/embed/engine.js +17 -0
  145. package/dist/embed/family.d.ts +7 -0
  146. package/dist/embed/family.js +5 -0
  147. package/dist/embed/index.d.ts +5 -0
  148. package/dist/embed/index.js +3 -0
  149. package/dist/embed/renderers.d.ts +3 -0
  150. package/dist/embed/renderers.js +3 -0
  151. package/dist/embed/sandbox.d.ts +86 -0
  152. package/dist/embed/sandbox.js +114 -0
  153. package/dist/embed/schema.d.ts +30 -0
  154. package/dist/embed/schema.js +217 -0
  155. package/dist/index.d.ts +16 -0
  156. package/dist/index.js +15 -0
  157. package/dist/interactive/CalculatorBlock.svelte +61 -0
  158. package/dist/interactive/CalculatorBlock.svelte.d.ts +10 -0
  159. package/dist/interactive/ChecklistBlock.svelte +44 -0
  160. package/dist/interactive/ChecklistBlock.svelte.d.ts +10 -0
  161. package/dist/interactive/InteractiveBlockView.svelte +31 -0
  162. package/dist/interactive/InteractiveBlockView.svelte.d.ts +5 -0
  163. package/dist/interactive/PollBlock.svelte +111 -0
  164. package/dist/interactive/PollBlock.svelte.d.ts +10 -0
  165. package/dist/interactive/QuizBlock.svelte +75 -0
  166. package/dist/interactive/QuizBlock.svelte.d.ts +10 -0
  167. package/dist/interactive/check.d.ts +10 -0
  168. package/dist/interactive/check.js +9 -0
  169. package/dist/interactive/engine.d.ts +19 -0
  170. package/dist/interactive/engine.js +12 -0
  171. package/dist/interactive/family.d.ts +6 -0
  172. package/dist/interactive/family.js +4 -0
  173. package/dist/interactive/index.d.ts +13 -0
  174. package/dist/interactive/index.js +13 -0
  175. package/dist/interactive/interactive.css +97 -0
  176. package/dist/interactive/interactive.css.d.ts +1 -0
  177. package/dist/interactive/labels.d.ts +16 -0
  178. package/dist/interactive/labels.js +16 -0
  179. package/dist/interactive/renderers.d.ts +9 -0
  180. package/dist/interactive/renderers.js +9 -0
  181. package/dist/interactive/schema.d.ts +76 -0
  182. package/dist/interactive/schema.js +324 -0
  183. package/dist/map/MapBlockView.svelte +465 -0
  184. package/dist/map/MapBlockView.svelte.d.ts +5 -0
  185. package/dist/map/check.d.ts +11 -0
  186. package/dist/map/check.js +6 -0
  187. package/dist/map/engine.d.ts +19 -0
  188. package/dist/map/engine.js +8 -0
  189. package/dist/map/family.d.ts +7 -0
  190. package/dist/map/family.js +5 -0
  191. package/dist/map/index.d.ts +11 -0
  192. package/dist/map/index.js +10 -0
  193. package/dist/map/labels.d.ts +27 -0
  194. package/dist/map/labels.js +21 -0
  195. package/dist/map/renderers.d.ts +2 -0
  196. package/dist/map/renderers.js +2 -0
  197. package/dist/map/schema.d.ts +41 -0
  198. package/dist/map/schema.js +176 -0
  199. package/dist/map/view-model.d.ts +55 -0
  200. package/dist/map/view-model.js +168 -0
  201. package/dist/media/MediaBlockView.svelte +258 -0
  202. package/dist/media/MediaBlockView.svelte.d.ts +5 -0
  203. package/dist/media/check.d.ts +14 -0
  204. package/dist/media/check.js +13 -0
  205. package/dist/media/engine.d.ts +18 -0
  206. package/dist/media/engine.js +10 -0
  207. package/dist/media/family.d.ts +7 -0
  208. package/dist/media/family.js +5 -0
  209. package/dist/media/index.d.ts +11 -0
  210. package/dist/media/index.js +10 -0
  211. package/dist/media/labels.d.ts +7 -0
  212. package/dist/media/labels.js +12 -0
  213. package/dist/media/renderers.d.ts +5 -0
  214. package/dist/media/renderers.js +5 -0
  215. package/dist/media/resolve.d.ts +6 -0
  216. package/dist/media/resolve.js +15 -0
  217. package/dist/structure/NestedBlocks.svelte +65 -0
  218. package/dist/structure/NestedBlocks.svelte.d.ts +11 -0
  219. package/dist/structure/SafeBody.svelte +13 -0
  220. package/dist/structure/SafeBody.svelte.d.ts +7 -0
  221. package/dist/structure/StructureFamily.svelte +157 -0
  222. package/dist/structure/StructureFamily.svelte.d.ts +5 -0
  223. package/dist/structure/check.d.ts +14 -0
  224. package/dist/structure/check.js +27 -0
  225. package/dist/structure/engine.d.ts +18 -0
  226. package/dist/structure/engine.js +18 -0
  227. package/dist/structure/family.d.ts +7 -0
  228. package/dist/structure/family.js +5 -0
  229. package/dist/structure/index.d.ts +11 -0
  230. package/dist/structure/index.js +10 -0
  231. package/dist/structure/labels.d.ts +11 -0
  232. package/dist/structure/labels.js +11 -0
  233. package/dist/structure/renderers.d.ts +5 -0
  234. package/dist/structure/renderers.js +5 -0
  235. package/dist/structure/schema.d.ts +71 -0
  236. package/dist/structure/schema.js +327 -0
  237. package/dist/structure/structure.css +182 -0
  238. package/dist/structure/structure.css.d.ts +1 -0
  239. package/dist/text/RichText.svelte +169 -0
  240. package/dist/text/RichText.svelte.d.ts +13 -0
  241. package/dist/text/TextBlockView.svelte +177 -0
  242. package/dist/text/TextBlockView.svelte.d.ts +5 -0
  243. package/dist/text/check.d.ts +14 -0
  244. package/dist/text/check.js +13 -0
  245. package/dist/text/engine.d.ts +17 -0
  246. package/dist/text/engine.js +10 -0
  247. package/dist/text/family.d.ts +7 -0
  248. package/dist/text/family.js +5 -0
  249. package/dist/text/index.d.ts +12 -0
  250. package/dist/text/index.js +11 -0
  251. package/dist/text/labels.d.ts +22 -0
  252. package/dist/text/labels.js +29 -0
  253. package/dist/text/markdown.d.ts +80 -0
  254. package/dist/text/markdown.js +361 -0
  255. package/dist/text/renderers.d.ts +6 -0
  256. package/dist/text/renderers.js +6 -0
  257. package/package.json +167 -4
@@ -0,0 +1,46 @@
1
+ import { safeParseBlocks } from "../schema/blocks.js";
2
+ import { toJsonPath } from "../schema/primitives.js";
3
+ import { getCatalogEntry } from "./entries.js";
4
+ /**
5
+ * Validate untrusted block author input and return path-addressed repair
6
+ * diagnostics. Never throws.
7
+ */
8
+ export function validateBlocksForAuthoring(input) {
9
+ const parsed = safeParseBlocks(input);
10
+ if (!parsed.success) {
11
+ return {
12
+ valid: false,
13
+ diagnostics: parsed.issues.map((issue) => ({
14
+ code: 'invalid-block',
15
+ path: issue.path,
16
+ jsonPath: toJsonPath(issue.path),
17
+ message: issue.message,
18
+ hint: 'Compare the value to BLOCK_CATALOG for this type (useWhen, limits and examples).',
19
+ })),
20
+ };
21
+ }
22
+ const diagnostics = [];
23
+ for (const [index, block] of parsed.data.entries()) {
24
+ const entry = getCatalogEntry(block.type);
25
+ if (!entry) {
26
+ diagnostics.push({
27
+ code: 'unknown-type',
28
+ path: [index, 'type'],
29
+ jsonPath: toJsonPath([index, 'type']),
30
+ message: `No catalog entry for type "${block.type}"`,
31
+ hint: 'Use a type listed in BLOCK_CATALOG.',
32
+ });
33
+ }
34
+ }
35
+ return { valid: diagnostics.length === 0, diagnostics };
36
+ }
37
+ /** Re-export parsed blocks when authoring validation succeeds. */
38
+ export function parseBlocksForAuthoring(input) {
39
+ const validation = validateBlocksForAuthoring(input);
40
+ if (!validation.valid)
41
+ return { success: false, validation };
42
+ const parsed = safeParseBlocks(input);
43
+ if (!parsed.success)
44
+ return { success: false, validation };
45
+ return { success: true, blocks: parsed.data };
46
+ }
@@ -0,0 +1,14 @@
1
+ import type { Condition, FormulaInputs, FormulaUnit, NumberFormula } from './types.js';
2
+ /** Rounds half away from zero to `digits` decimals (negative: tens, hundreds…). */
3
+ export declare function roundTo(value: number, digits?: number): number | null;
4
+ /** Evaluates a formula (validate first with parseFormula). Never throws. */
5
+ export declare function evaluateFormula(formula: NumberFormula, inputs: FormulaInputs): number | null;
6
+ /** Evaluates a condition on its own, with the same rules. */
7
+ export declare function evaluateCondition(test: Condition, inputs: FormulaInputs): boolean | null;
8
+ export interface FormatFormulaOptions {
9
+ currency?: string;
10
+ locale?: string;
11
+ maximumFractionDigits?: number;
12
+ empty?: string;
13
+ }
14
+ export declare function formatFormulaValue(value: number | null, unit?: FormulaUnit, options?: FormatFormulaOptions): string;
@@ -0,0 +1,181 @@
1
+ import { FORMULA_LIMITS } from "./parse.js";
2
+ /*
3
+ * Pure interpreter over the closed grammar. Never throws; null means undefined.
4
+ */
5
+ function finite(value) {
6
+ if (!Number.isFinite(value))
7
+ return null;
8
+ return value === 0 ? 0 : value;
9
+ }
10
+ function readInput(inputs, name) {
11
+ if (!Object.hasOwn(inputs, name))
12
+ return null;
13
+ const value = inputs[name];
14
+ return typeof value === 'number' ? finite(value) : null;
15
+ }
16
+ /** Rounds half away from zero to `digits` decimals (negative: tens, hundreds…). */
17
+ export function roundTo(value, digits = 0) {
18
+ const factor = 10 ** Math.abs(digits);
19
+ const scaled = digits >= 0 ? Math.abs(value) * factor : Math.abs(value) / factor;
20
+ const rounded = Math.round(scaled + scaled * Number.EPSILON);
21
+ const result = Math.sign(value) * (digits >= 0 ? rounded / factor : rounded * factor);
22
+ return finite(result);
23
+ }
24
+ function number(node, inputs, depth) {
25
+ if (node === null || typeof node !== 'object' || depth > FORMULA_LIMITS.depth)
26
+ return null;
27
+ const next = depth + 1;
28
+ switch (node.op) {
29
+ case 'num':
30
+ return typeof node.value === 'number' ? finite(node.value) : null;
31
+ case 'var':
32
+ return typeof node.name === 'string' ? readInput(inputs, node.name) : null;
33
+ case 'add':
34
+ case 'sub':
35
+ case 'mul':
36
+ case 'div': {
37
+ const left = number(node.left, inputs, next);
38
+ const right = number(node.right, inputs, next);
39
+ if (left === null || right === null)
40
+ return null;
41
+ if (node.op === 'add')
42
+ return finite(left + right);
43
+ if (node.op === 'sub')
44
+ return finite(left - right);
45
+ if (node.op === 'mul')
46
+ return finite(left * right);
47
+ return right === 0 ? null : finite(left / right);
48
+ }
49
+ case 'pow': {
50
+ const base = number(node.base, inputs, next);
51
+ const exponent = number(node.exponent, inputs, next);
52
+ if (base === null || exponent === null)
53
+ return null;
54
+ if (Math.abs(exponent) > FORMULA_LIMITS.exponent)
55
+ return null;
56
+ return finite(base ** exponent);
57
+ }
58
+ case 'min':
59
+ case 'max': {
60
+ if (!Array.isArray(node.args) || node.args.length === 0)
61
+ return null;
62
+ let result = null;
63
+ for (const arg of node.args.slice(0, FORMULA_LIMITS.args)) {
64
+ const value = number(arg, inputs, next);
65
+ if (value === null)
66
+ return null;
67
+ if (result === null)
68
+ result = value;
69
+ else
70
+ result = node.op === 'min' ? Math.min(result, value) : Math.max(result, value);
71
+ }
72
+ return result;
73
+ }
74
+ case 'round': {
75
+ const value = number(node.value, inputs, next);
76
+ const digits = node.digits ?? 0;
77
+ if (value === null || !Number.isInteger(digits))
78
+ return null;
79
+ if (Math.abs(digits) > FORMULA_LIMITS.roundDigits)
80
+ return null;
81
+ return roundTo(value, digits);
82
+ }
83
+ case 'clamp': {
84
+ const value = number(node.value, inputs, next);
85
+ const min = number(node.min, inputs, next);
86
+ const max = number(node.max, inputs, next);
87
+ if (value === null || min === null || max === null || min > max)
88
+ return null;
89
+ return Math.min(max, Math.max(min, value));
90
+ }
91
+ case 'if': {
92
+ const test = condition(node.test, inputs, next);
93
+ if (test === null)
94
+ return null;
95
+ return number(test ? node.ifTrue : node.ifFalse, inputs, next);
96
+ }
97
+ default:
98
+ return null;
99
+ }
100
+ }
101
+ function condition(node, inputs, depth) {
102
+ if (node === null || typeof node !== 'object' || depth > FORMULA_LIMITS.depth)
103
+ return null;
104
+ const next = depth + 1;
105
+ switch (node.op) {
106
+ case 'lt':
107
+ case 'lte':
108
+ case 'gt':
109
+ case 'gte':
110
+ case 'eq':
111
+ case 'neq': {
112
+ const left = number(node.left, inputs, next);
113
+ const right = number(node.right, inputs, next);
114
+ if (left === null || right === null)
115
+ return null;
116
+ if (node.op === 'lt')
117
+ return left < right;
118
+ if (node.op === 'lte')
119
+ return left <= right;
120
+ if (node.op === 'gt')
121
+ return left > right;
122
+ if (node.op === 'gte')
123
+ return left >= right;
124
+ if (node.op === 'eq')
125
+ return left === right;
126
+ return left !== right;
127
+ }
128
+ case 'and':
129
+ case 'or': {
130
+ if (!Array.isArray(node.args) || node.args.length === 0)
131
+ return null;
132
+ let unknown = false;
133
+ for (const arg of node.args.slice(0, FORMULA_LIMITS.args)) {
134
+ const value = condition(arg, inputs, next);
135
+ if (value === null)
136
+ unknown = true;
137
+ else if (node.op === 'and' && !value)
138
+ return false;
139
+ else if (node.op === 'or' && value)
140
+ return true;
141
+ }
142
+ return unknown ? null : node.op === 'and';
143
+ }
144
+ case 'not': {
145
+ const value = condition(node.value, inputs, next);
146
+ return value === null ? null : !value;
147
+ }
148
+ default:
149
+ return null;
150
+ }
151
+ }
152
+ /** Evaluates a formula (validate first with parseFormula). Never throws. */
153
+ export function evaluateFormula(formula, inputs) {
154
+ return number(formula, inputs, 1);
155
+ }
156
+ /** Evaluates a condition on its own, with the same rules. */
157
+ export function evaluateCondition(test, inputs) {
158
+ return condition(test, inputs, 1);
159
+ }
160
+ export function formatFormulaValue(value, unit = 'number', options = {}) {
161
+ if (value === null || !Number.isFinite(value))
162
+ return options.empty ?? '—';
163
+ const locale = options.locale ?? 'en-US';
164
+ const digits = options.maximumFractionDigits;
165
+ switch (unit) {
166
+ case 'cents':
167
+ return new Intl.NumberFormat(locale, {
168
+ style: 'currency',
169
+ currency: options.currency ?? 'USD',
170
+ }).format(value / 100);
171
+ case 'percent':
172
+ return new Intl.NumberFormat(locale, {
173
+ style: 'percent',
174
+ maximumFractionDigits: digits ?? 1,
175
+ }).format(value);
176
+ case 'compact':
177
+ return new Intl.NumberFormat(locale, { notation: 'compact' }).format(value);
178
+ default:
179
+ return new Intl.NumberFormat(locale, { maximumFractionDigits: digits ?? 2 }).format(value);
180
+ }
181
+ }
@@ -0,0 +1,8 @@
1
+ /// <reference path="../../css-modules.d.ts" />
2
+ /**
3
+ * `@texuli/blocks` formula — closed AST for calculator blocks.
4
+ * Formulas are data, never code: no string syntax, no host objects.
5
+ */
6
+ export { evaluateCondition, evaluateFormula, type FormatFormulaOptions, formatFormulaValue, roundTo, } from './evaluate.js';
7
+ export { FORMULA_LIMITS, formulaJsonPath, formulaVariables, type ParseFormulaOptions, parseFormula, } from './parse.js';
8
+ export type { ArithmeticOp, ComparisonOp, Condition, FormulaDiagnostic, FormulaInputs, FormulaParseResult, FormulaUnit, NumberFormula, } from './types.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@texuli/blocks` formula — closed AST for calculator blocks.
3
+ * Formulas are data, never code: no string syntax, no host objects.
4
+ */
5
+ export { evaluateCondition, evaluateFormula, formatFormulaValue, roundTo, } from "./evaluate.js";
6
+ export { FORMULA_LIMITS, formulaJsonPath, formulaVariables, parseFormula, } from "./parse.js";
@@ -0,0 +1,19 @@
1
+ import type { Condition, FormulaParseResult, NumberFormula } from './types.js';
2
+ /** Caps that keep parsing and evaluation small and linear. */
3
+ export declare const FORMULA_LIMITS: Readonly<{
4
+ nodes: 64;
5
+ depth: 12;
6
+ args: 8;
7
+ exponent: 64;
8
+ roundDigits: 6;
9
+ literal: 1000000000000000;
10
+ }>;
11
+ export declare function formulaJsonPath(path: readonly (string | number)[]): string;
12
+ /** Variable names a formula reads, sorted and unique. */
13
+ export declare function formulaVariables(formula: NumberFormula | Condition): string[];
14
+ export interface ParseFormulaOptions {
15
+ /** Known input names. When given, any other variable is an unknown-variable diagnostic. */
16
+ variables?: readonly string[];
17
+ }
18
+ /** Validates untrusted formula JSON. Never throws and never executes anything. */
19
+ export declare function parseFormula(input: unknown, options?: ParseFormulaOptions): FormulaParseResult;