@hyperscale0/hsx 2.4.1 → 3.0.1

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 (249) hide show
  1. package/CHANGELOG.md +4 -406
  2. package/README.md +3 -92
  3. package/dist/src/ast.d.ts +99 -310
  4. package/dist/src/ast.d.ts.map +1 -1
  5. package/dist/src/ast.js +16 -69
  6. package/dist/src/ast.js.map +1 -1
  7. package/dist/src/cli.d.ts +6 -19
  8. package/dist/src/cli.d.ts.map +1 -1
  9. package/dist/src/cli.js +77 -315
  10. package/dist/src/cli.js.map +1 -1
  11. package/dist/src/compile.d.ts +23 -73
  12. package/dist/src/compile.d.ts.map +1 -1
  13. package/dist/src/compile.js +1113 -148
  14. package/dist/src/compile.js.map +1 -1
  15. package/dist/src/cost.d.ts +8 -77
  16. package/dist/src/cost.d.ts.map +1 -1
  17. package/dist/src/cost.js +13 -422
  18. package/dist/src/cost.js.map +1 -1
  19. package/dist/src/format.d.ts +8 -7
  20. package/dist/src/format.d.ts.map +1 -1
  21. package/dist/src/format.js +9 -175
  22. package/dist/src/format.js.map +1 -1
  23. package/dist/src/headers.d.ts +35 -0
  24. package/dist/src/headers.d.ts.map +1 -0
  25. package/dist/src/headers.js +108 -0
  26. package/dist/src/headers.js.map +1 -0
  27. package/dist/src/index.d.ts +6 -12
  28. package/dist/src/index.d.ts.map +1 -1
  29. package/dist/src/index.js +4 -9
  30. package/dist/src/index.js.map +1 -1
  31. package/dist/src/lex.d.ts +8 -28
  32. package/dist/src/lex.d.ts.map +1 -1
  33. package/dist/src/lex.js +88 -146
  34. package/dist/src/lex.js.map +1 -1
  35. package/dist/src/parse.d.ts +4 -17
  36. package/dist/src/parse.d.ts.map +1 -1
  37. package/dist/src/parse.js +402 -1040
  38. package/dist/src/parse.js.map +1 -1
  39. package/dist/src/std-bundle.d.ts.map +1 -1
  40. package/dist/src/std-bundle.js +12 -20
  41. package/dist/src/std-bundle.js.map +1 -1
  42. package/dist/src/std-library.d.ts +1 -1
  43. package/dist/src/std-library.d.ts.map +1 -1
  44. package/dist/src/std-library.js +1 -5
  45. package/dist/src/std-library.js.map +1 -1
  46. package/dist/src/tunables.d.ts +7 -0
  47. package/dist/src/tunables.d.ts.map +1 -0
  48. package/dist/src/tunables.js +26 -0
  49. package/dist/src/tunables.js.map +1 -0
  50. package/dist/src/version.d.ts +2 -4
  51. package/dist/src/version.d.ts.map +1 -1
  52. package/dist/src/version.js +2 -4
  53. package/dist/src/version.js.map +1 -1
  54. package/docs/README.md +179 -41
  55. package/docs/headers.md +44 -0
  56. package/examples/cost-table.json +99 -751
  57. package/examples/library.hsx +59 -0
  58. package/package.json +9 -17
  59. package/src/ast.ts +82 -441
  60. package/src/cli.ts +78 -362
  61. package/src/compile.ts +1535 -247
  62. package/src/cost.ts +28 -671
  63. package/src/format.ts +12 -256
  64. package/src/headers.ts +126 -0
  65. package/src/index.ts +7 -39
  66. package/src/lex.ts +99 -195
  67. package/src/parse.ts +403 -1209
  68. package/src/std-bundle.ts +12 -20
  69. package/src/std-library.ts +2 -7
  70. package/src/tunables.ts +31 -0
  71. package/src/version.ts +2 -5
  72. package/std/approvals.hsx +17 -0
  73. package/std/cards.hsx +63 -0
  74. package/std/collections.hsx +31 -0
  75. package/std/escrow.hsx +43 -0
  76. package/std/financing.hsx +372 -0
  77. package/std/insurance.hsx +59 -0
  78. package/std/lending.hsx +115 -0
  79. package/std/marketplace.hsx +24 -0
  80. package/std/money.hsx +195 -0
  81. package/std/savings.hsx +44 -0
  82. package/std/travel.hsx +98 -0
  83. package/std/wallet.hsx +41 -0
  84. package/dist/src/diagnostics.d.ts +0 -13
  85. package/dist/src/diagnostics.d.ts.map +0 -1
  86. package/dist/src/diagnostics.js +0 -587
  87. package/dist/src/diagnostics.js.map +0 -1
  88. package/dist/src/emit.d.ts +0 -51
  89. package/dist/src/emit.d.ts.map +0 -1
  90. package/dist/src/emit.js +0 -192
  91. package/dist/src/emit.js.map +0 -1
  92. package/dist/src/entry-overrides.d.ts +0 -58
  93. package/dist/src/entry-overrides.d.ts.map +0 -1
  94. package/dist/src/entry-overrides.js +0 -284
  95. package/dist/src/entry-overrides.js.map +0 -1
  96. package/dist/src/ir.d.ts +0 -73
  97. package/dist/src/ir.d.ts.map +0 -1
  98. package/dist/src/ir.js +0 -15
  99. package/dist/src/ir.js.map +0 -1
  100. package/dist/src/limits.d.ts +0 -23
  101. package/dist/src/limits.d.ts.map +0 -1
  102. package/dist/src/limits.js +0 -23
  103. package/dist/src/limits.js.map +0 -1
  104. package/dist/src/lsp/server.d.ts +0 -32
  105. package/dist/src/lsp/server.d.ts.map +0 -1
  106. package/dist/src/lsp/server.js +0 -391
  107. package/dist/src/lsp/server.js.map +0 -1
  108. package/dist/src/modules.d.ts +0 -38
  109. package/dist/src/modules.d.ts.map +0 -1
  110. package/dist/src/modules.js +0 -372
  111. package/dist/src/modules.js.map +0 -1
  112. package/dist/src/typecheck.d.ts +0 -8
  113. package/dist/src/typecheck.d.ts.map +0 -1
  114. package/dist/src/typecheck.js +0 -4160
  115. package/dist/src/typecheck.js.map +0 -1
  116. package/docs/guide/01-first-program.md +0 -24
  117. package/docs/guide/02-money.md +0 -25
  118. package/docs/guide/03-instruments.md +0 -93
  119. package/docs/guide/04-lifecycles.md +0 -40
  120. package/docs/guide/05-fees-and-splits.md +0 -45
  121. package/docs/guide/06-schedules.md +0 -26
  122. package/docs/guide/07-composition.md +0 -52
  123. package/docs/guide/08-writing-a-module.md +0 -85
  124. package/docs/guide/09-cost.md +0 -24
  125. package/docs/guide/10-diagnostics.md +0 -27
  126. package/docs/llms-full.txt +0 -3684
  127. package/docs/llms.txt +0 -44
  128. package/docs/piece-plans.md +0 -120
  129. package/docs/playground.md +0 -55
  130. package/docs/reference/cli.md +0 -36
  131. package/docs/reference/diagnostics.md +0 -901
  132. package/docs/reference/grammar.md +0 -38
  133. package/docs/reference/std/advance.md +0 -112
  134. package/docs/reference/std/cancellable_booking.md +0 -159
  135. package/docs/reference/std/captured_payment.md +0 -115
  136. package/docs/reference/std/conditional_disbursement.md +0 -101
  137. package/docs/reference/std/credit_facility.md +0 -108
  138. package/docs/reference/std/held_payment.md +0 -173
  139. package/docs/reference/std/instant_transfer.md +0 -97
  140. package/docs/reference/std/metered.md +0 -89
  141. package/docs/reference/std/pooled_split.md +0 -95
  142. package/docs/reference/std/premium_forward.md +0 -119
  143. package/docs/reference/std/reconciled_payout.md +0 -103
  144. package/docs/reference/std/rotating_pool.md +0 -130
  145. package/docs/reference/std/scheduled.md +0 -136
  146. package/docs/reference/std/security_deposit.md +0 -138
  147. package/docs/reference/std/settlement_batch.md +0 -123
  148. package/docs/reference/std/swap.md +0 -133
  149. package/docs/reference/std/threshold_pool.md +0 -138
  150. package/docs/reference/std/weighted_distribution.md +0 -135
  151. package/docs/reference/types.md +0 -61
  152. package/docs/reference/udl-output.md +0 -13
  153. package/examples/01-first-program/README.md +0 -45
  154. package/examples/01-first-program/tip-jar.hsx +0 -17
  155. package/examples/02-imports-and-modules/README.md +0 -96
  156. package/examples/02-imports-and-modules/photo-booth.hsx +0 -37
  157. package/examples/03-diagnostics/README.md +0 -68
  158. package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
  159. package/examples/03-diagnostics/corner-shop.hsx +0 -19
  160. package/examples/04-complete-product/README.md +0 -84
  161. package/examples/04-complete-product/study-hall.hsx +0 -67
  162. package/examples/05-authored-instrument/README.md +0 -5
  163. package/examples/05-authored-instrument/payment.hsx +0 -37
  164. package/examples/05-watch-club/README.md +0 -6
  165. package/examples/05-watch-club/watch-club.hsx +0 -123
  166. package/examples/README.md +0 -48
  167. package/examples/advance/README.md +0 -3
  168. package/examples/advance/advance.hsx +0 -13
  169. package/examples/advance/advance.udl +0 -367
  170. package/examples/cancellable_booking/README.md +0 -3
  171. package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
  172. package/examples/cancellable_booking/cancellable_booking.udl +0 -413
  173. package/examples/captured_payment/README.md +0 -3
  174. package/examples/captured_payment/captured_payment.hsx +0 -17
  175. package/examples/captured_payment/captured_payment.udl +0 -686
  176. package/examples/conditional_disbursement/README.md +0 -3
  177. package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -15
  178. package/examples/conditional_disbursement/conditional_disbursement.udl +0 -418
  179. package/examples/credit_facility/README.md +0 -3
  180. package/examples/credit_facility/credit_facility.hsx +0 -24
  181. package/examples/credit_facility/credit_facility.udl +0 -1396
  182. package/examples/held_payment/README.md +0 -3
  183. package/examples/held_payment/held_payment.hsx +0 -13
  184. package/examples/held_payment/held_payment.udl +0 -569
  185. package/examples/instant_transfer/README.md +0 -3
  186. package/examples/instant_transfer/instant_transfer.hsx +0 -16
  187. package/examples/instant_transfer/instant_transfer.udl +0 -376
  188. package/examples/metered/README.md +0 -3
  189. package/examples/metered/metered.hsx +0 -13
  190. package/examples/metered/metered.udl +0 -253
  191. package/examples/pooled_split/README.md +0 -3
  192. package/examples/pooled_split/pooled_split.hsx +0 -15
  193. package/examples/pooled_split/pooled_split.udl +0 -432
  194. package/examples/premium_forward/README.md +0 -3
  195. package/examples/premium_forward/premium_forward.hsx +0 -19
  196. package/examples/premium_forward/premium_forward.udl +0 -604
  197. package/examples/reconciled_payout/README.md +0 -3
  198. package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
  199. package/examples/reconciled_payout/reconciled_payout.udl +0 -312
  200. package/examples/rotating_pool/README.md +0 -3
  201. package/examples/rotating_pool/rotating_pool.hsx +0 -18
  202. package/examples/rotating_pool/rotating_pool.udl +0 -4370
  203. package/examples/scheduled/README.md +0 -3
  204. package/examples/scheduled/scheduled.hsx +0 -12
  205. package/examples/scheduled/scheduled.udl +0 -355
  206. package/examples/security_deposit/README.md +0 -3
  207. package/examples/security_deposit/security_deposit.hsx +0 -21
  208. package/examples/security_deposit/security_deposit.udl +0 -320
  209. package/examples/settlement_batch/README.md +0 -3
  210. package/examples/settlement_batch/settlement_batch.hsx +0 -19
  211. package/examples/settlement_batch/settlement_batch.udl +0 -757
  212. package/examples/swap/README.md +0 -3
  213. package/examples/swap/swap.hsx +0 -19
  214. package/examples/swap/swap.udl +0 -892
  215. package/examples/threshold_pool/README.md +0 -3
  216. package/examples/threshold_pool/threshold_pool.hsx +0 -16
  217. package/examples/threshold_pool/threshold_pool.udl +0 -680
  218. package/examples/weighted_distribution/README.md +0 -3
  219. package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
  220. package/examples/weighted_distribution/weighted_distribution.udl +0 -348
  221. package/skills/hsx/SKILL.md +0 -449
  222. package/src/diagnostics.ts +0 -620
  223. package/src/emit.ts +0 -282
  224. package/src/entry-overrides.ts +0 -428
  225. package/src/ir.ts +0 -113
  226. package/src/limits.ts +0 -22
  227. package/src/lsp/server.ts +0 -460
  228. package/src/modules.ts +0 -452
  229. package/src/typecheck.ts +0 -5515
  230. package/std/SEMANTICS.md +0 -44
  231. package/std/money_flows/advance.hsx +0 -235
  232. package/std/money_flows/cancellable_booking.hsx +0 -402
  233. package/std/money_flows/captured_payment.hsx +0 -220
  234. package/std/money_flows/conditional_disbursement.hsx +0 -226
  235. package/std/money_flows/credit_facility.hsx +0 -214
  236. package/std/money_flows/held_payment.hsx +0 -812
  237. package/std/money_flows/index.hsx +0 -4
  238. package/std/money_flows/instant_transfer.hsx +0 -231
  239. package/std/money_flows/metered.hsx +0 -104
  240. package/std/money_flows/pooled_split.hsx +0 -109
  241. package/std/money_flows/premium_forward.hsx +0 -307
  242. package/std/money_flows/reconciled_payout.hsx +0 -119
  243. package/std/money_flows/rotating_pool.hsx +0 -305
  244. package/std/money_flows/scheduled.hsx +0 -599
  245. package/std/money_flows/security_deposit.hsx +0 -248
  246. package/std/money_flows/settlement_batch.hsx +0 -173
  247. package/std/money_flows/swap.hsx +0 -1192
  248. package/std/money_flows/threshold_pool.hsx +0 -307
  249. package/std/money_flows/weighted_distribution.hsx +0 -237
package/src/cli.ts CHANGED
@@ -1,383 +1,99 @@
1
- /**
2
- * The `hsx` command line over the one compiler entry point and catalog.
3
- *
4
- * `check` prints diagnostics and says nothing else; `build` writes the
5
- * compiled artifacts as JSON. Neither reads the environment, neither touches
6
- * a network, and neither writes outside the path the caller names.
7
- *
8
- * Everything here is a pure function of `argv` plus the injected `Io`, so the
9
- * spec drives the real code paths with an in-memory filesystem instead of
10
- * asserting on a subprocess's scrollback.
11
- */
12
-
13
- import { parseUdl, type UdlDocument } from "@hyperscale0/udl";
14
- import { fileURLToPath } from "node:url";
15
- import { compile, type CompileResult } from "./compile.ts";
16
- import type { UdlCostManifest, UdlCostTables } from "./cost.ts";
17
- import { hsxDiagnostics } from "./diagnostics.ts";
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { headerManifest } from "./headers.ts";
4
+ import { HSX_VERSION } from "./version.ts";
5
+ import { compile } from "./compile.ts";
18
6
  import { format } from "./format.ts";
19
- import { startLspServer } from "./lsp/server.ts";
20
- import { HSX_TARGET_UDL_VERSION, HSX_VERSION } from "./version.ts";
7
+ import { serializeUdl } from "@hyperscale0/udl";
8
+ const standardLibrary = {
9
+ source: (header: string) => {
10
+ for (const base of ["../std/", "../../std/"]) {
11
+ const file = new URL(`${base}${header}.hsx`, import.meta.url);
12
+ if (existsSync(file)) return readFileSync(file, "utf8");
13
+ }
14
+ return undefined;
15
+ },
16
+ };
21
17
 
22
- /** Filesystem and streams, injected so the CLI stays testable. */
23
18
  export interface Io {
24
- readonly err: (line: string) => void;
25
- readonly out: (line: string) => void;
26
- readonly readFile: (path: string) => Promise<string>;
27
- readonly writeFile: (path: string, contents: string) => Promise<void>;
28
- readonly stdin?: NodeJS.ReadableStream;
29
- readonly stdout?: NodeJS.WritableStream;
19
+ out(line: string): void;
20
+ err(line: string): void;
21
+ readFile(path: string): Promise<string>;
22
+ writeFile(path: string, text: string): Promise<void>;
23
+ stdin?: NodeJS.ReadableStream;
24
+ stdout?: NodeJS.WritableStream;
30
25
  }
31
-
32
- /**
33
- * Exit codes. `warning` exits 0 because a lint note is not a failure; pass
34
- * `--strict` to make it one.
35
- */
36
- const OK = 0;
37
- const REFUSED = 1;
38
- const USAGE = 2;
39
-
40
- export const USAGE_TEXT = `hsx ${HSX_VERSION}, the HSX compiler
41
-
42
- Usage:
43
- hsx check <file.hsx> [--catalog <catalog.udl>] [--strict]
44
- hsx build <file.hsx> [--catalog <catalog.udl>] [--out <file.json>] [--strict]
45
- hsx cost <file.hsx> [--catalog <catalog.udl>] [--json] [--out <file.json>] [--strict]
46
- hsx explain <HSX####>
47
- hsx format <file.hsx>
48
- hsx lsp
49
- hsx --version
50
- hsx --help
51
-
52
- Commands:
53
- check Compile and report diagnostics. Prints nothing when the program is clean.
54
- build Compile and write canonical UDL as JSON.
55
- cost Compile and print the version-pinned cost manifest as a table or JSON.
56
- explain Print one diagnostic title, fix, and source example.
57
- format Print the source in the one canonical HSX style.
58
- lsp Run the language server over stdin and stdout.
59
-
60
- Options:
61
- --catalog <file> Read the published instrument catalogue as canonical UDL JSON.
62
- --json Print the cost manifest as JSON instead of a table.
63
- --out <file> Write build or cost JSON to this path instead of stdout.
64
- --strict Treat warning-severity diagnostics as failures.
65
-
66
- Exit codes:
67
- 0 the program compiled (verdict valid, or warning without --strict)
68
- 1 the program was refused (verdict invalid, or warning with --strict)
69
- 2 the command line or the input file could not be used`;
70
-
71
26
  export async function runCli(argv: readonly string[], io: Io): Promise<number> {
72
- const [command, ...rest] = argv;
73
-
74
- if (command === undefined || command === "--help" || command === "-h") {
75
- io.out(USAGE_TEXT);
76
- return command === undefined ? USAGE : OK;
27
+ const [command, file, ...options] = argv;
28
+ if (command === "headers") {
29
+ if (file !== "--json" || options.length) {
30
+ io.err("usage: hsx headers --json");
31
+ return 2;
32
+ }
33
+ io.out(JSON.stringify(headerManifest(standardLibrary), null, 2));
34
+ return 0;
77
35
  }
78
- if (command === "--version" || command === "-v") {
79
- io.out(`${HSX_VERSION} (UDL version ${HSX_TARGET_UDL_VERSION})`);
80
- return OK;
36
+ if (["--help", "help", "-h"].includes(command ?? "")) {
37
+ io.out("hsx build|check|format|cost <file> [--out path]");
38
+ return 0;
81
39
  }
82
- if (command === "explain") return explainDiagnostic(rest, io);
83
- if (command === "lsp") {
84
- const input = io.stdin ?? process.stdin;
85
- const output = io.stdout ?? process.stdout;
86
- return new Promise<number>((resolve) => {
87
- startLspServer(input, output, {
88
- onExit: resolve,
89
- });
90
- });
40
+ if (command === "--version") {
41
+ io.out(`${HSX_VERSION} (UDL 3)`);
42
+ return 0;
91
43
  }
92
44
  if (
93
- command !== "check" &&
94
- command !== "build" &&
95
- command !== "cost" &&
96
- command !== "format"
45
+ !command ||
46
+ !file ||
47
+ !["build", "check", "format", "cost"].includes(command)
97
48
  ) {
98
- io.err(`hsx: unknown command "${command}"`);
99
- io.err(USAGE_TEXT);
100
- return USAGE;
101
- }
102
-
103
- const parsed = parseOptions(rest, command);
104
- if ("error" in parsed) {
105
- io.err(`hsx: ${parsed.error}`);
106
- return USAGE;
107
- }
108
-
109
- let source: string;
110
- try {
111
- source = await io.readFile(parsed.file);
112
- } catch (cause) {
113
- io.err(`hsx: cannot read ${parsed.file}: ${messageOf(cause)}`);
114
- return USAGE;
115
- }
116
-
117
- if (command === "format") {
118
- const result = format(source);
119
- if (!result.ok) {
120
- for (const diagnostic of result.diagnostics) {
121
- const codePrefix = diagnostic.code ? `${diagnostic.code}: ` : "";
122
- io.err(
123
- `${parsed.file}:1:1: error [parse] ${codePrefix}${diagnostic.message}`,
124
- );
125
- }
126
- return REFUSED;
49
+ io.err("usage: hsx build|check|format|cost <file> [--out path]");
50
+ return 2;
51
+ }
52
+ let output: string | undefined;
53
+ for (let i = 0; i < options.length; i++) {
54
+ if (options[i] === "--out" && options[i + 1] && output === undefined)
55
+ output = options[++i];
56
+ else if (!["--json", "--strict"].includes(options[i]!)) {
57
+ io.err(`unknown option ${options[i]}`);
58
+ return 2;
127
59
  }
128
- io.out(result.formatted.trimEnd());
129
- return OK;
130
60
  }
131
-
132
- let costTable: UdlCostTables;
133
61
  try {
134
- costTable = await readDefaultCostTable(io);
135
- } catch (cause) {
136
- io.err(`hsx: cannot read the packaged cost table: ${messageOf(cause)}`);
137
- return USAGE;
138
- }
139
- let publishedCatalog: UdlDocument | undefined;
140
- if (parsed.catalog !== undefined) {
141
- try {
142
- publishedCatalog = parseUdl(await io.readFile(parsed.catalog));
143
- } catch (cause) {
144
- io.err(
145
- `hsx: cannot read catalogue ${parsed.catalog}: ${messageOf(cause)}`,
146
- );
147
- return USAGE;
148
- }
149
- }
150
- const result = compile(source, {
151
- costTable,
152
- ...(publishedCatalog === undefined ? {} : { publishedCatalog }),
153
- });
154
- for (const line of diagnosticLines(parsed.file, result)) io.err(line);
155
-
156
- const refused =
157
- result.verdict === "invalid" ||
158
- (parsed.strict && result.verdict === "warning");
159
-
160
- if (command === "check") {
161
- return refused ? REFUSED : OK;
162
- }
163
-
164
- if (!result.artifacts) return REFUSED;
165
- if (command === "cost") {
166
- const json = `${JSON.stringify(result.artifacts.costManifest, null, 2)}\n`;
167
- if (parsed.out !== undefined) {
168
- const writeCode = await writeOutput(parsed.out, json, io);
169
- return writeCode === OK && refused ? REFUSED : writeCode;
170
- }
171
- io.out(
172
- parsed.json
173
- ? json.trimEnd()
174
- : renderCostTable(result.artifacts.costManifest),
175
- );
176
- return refused ? REFUSED : OK;
177
- }
178
- const json = `${JSON.stringify(result.artifacts.document, null, 2)}\n`;
179
- if (parsed.out === undefined) {
180
- io.out(json.trimEnd());
181
- } else {
182
- try {
183
- await io.writeFile(parsed.out, json);
184
- } catch (cause) {
185
- io.err(`hsx: cannot write ${parsed.out}: ${messageOf(cause)}`);
186
- return USAGE;
187
- }
188
- }
189
- return refused ? REFUSED : OK;
190
- }
191
-
192
- interface Options {
193
- readonly catalog?: string;
194
- readonly file: string;
195
- readonly json: boolean;
196
- readonly out?: string;
197
- readonly strict: boolean;
198
- }
199
-
200
- function parseOptions(
201
- args: readonly string[],
202
- command: "build" | "check" | "cost" | "format",
203
- ): Options | { readonly error: string } {
204
- let file: string | undefined;
205
- let catalog: string | undefined;
206
- let json = false;
207
- let out: string | undefined;
208
- let strict = false;
209
-
210
- for (let index = 0; index < args.length; index += 1) {
211
- const argument = args[index] as string;
212
- if (argument === "--strict") {
213
- if (command === "format") {
214
- return { error: "hsx format has no style options" };
62
+ const source = await io.readFile(file);
63
+ let text: string;
64
+ if (command === "format") {
65
+ const result = format(source);
66
+ if (!result.ok) {
67
+ result.diagnostics.forEach((d) => io.err(`${file}: ${d.message}`));
68
+ return 1;
215
69
  }
216
- strict = true;
217
- continue;
218
- }
219
- if (argument === "--json") {
220
- if (command !== "cost") return { error: "--json belongs to hsx cost" };
221
- json = true;
222
- continue;
223
- }
224
- if (argument === "--catalog") {
225
- if (command === "format")
226
- return { error: "--catalog belongs to check, build or cost" };
227
- const value = args[index + 1];
228
- if (value === undefined || value.startsWith("--"))
229
- return { error: "--catalog needs a file path" };
230
- if (catalog !== undefined)
231
- return { error: "--catalog may be supplied only once" };
232
- catalog = value;
233
- index += 1;
234
- continue;
235
- }
236
- if (argument === "--out") {
237
- if (command !== "build" && command !== "cost") {
238
- return { error: "--out belongs to hsx build or hsx cost" };
239
- }
240
- const value = args[index + 1];
241
- if (value === undefined || value.startsWith("--")) {
242
- return { error: "--out needs a file path" };
243
- }
244
- out = value;
245
- index += 1;
246
- continue;
247
- }
248
- if (argument.startsWith("-")) {
249
- return { error: `unknown option "${argument}"` };
250
- }
251
- if (file !== undefined) {
252
- return { error: `hsx ${command} takes one file, got "${argument}" too` };
253
- }
254
- file = argument;
255
- }
256
-
257
- if (file === undefined) return { error: `hsx ${command} needs a file` };
258
- return {
259
- file,
260
- json,
261
- ...(catalog === undefined ? {} : { catalog }),
262
- ...(out === undefined ? {} : { out }),
263
- strict,
264
- };
265
- }
266
-
267
- function explainDiagnostic(args: readonly string[], io: Io): number {
268
- if (args.length !== 1 || args[0]?.startsWith("-")) {
269
- io.err("hsx: explain needs one diagnostic code");
270
- return USAGE;
271
- }
272
- const diagnostic = hsxDiagnostics.find(({ code }) => code === args[0]);
273
- if (!diagnostic) {
274
- io.err(`hsx: unknown diagnostic code ${args[0]}`);
275
- return USAGE;
276
- }
277
- io.out(
278
- [
279
- `${diagnostic.code} ${diagnostic.title}`,
280
- `Stage: ${diagnostic.stage}`,
281
- `Fix: ${diagnostic.fix}`,
282
- diagnostic.example === null
283
- ? "Example: unavailable from source alone"
284
- : `Example:\n${diagnostic.example}`,
285
- ].join("\n"),
286
- );
287
- return OK;
288
- }
289
-
290
- function renderCostTable(manifest: UdlCostManifest): string {
291
- const rows = [
292
- `costTableVersion\t${manifest.costTableVersion}`,
293
- "instrument.action\teffect\tunit\tcount\ttotal\tpayer",
294
- ];
295
- for (const action of manifest.actions) {
296
- for (const component of action.components) {
297
- const fixed = BigInt(component.perEventMinor) * BigInt(component.count);
298
- const unit = [
299
- `${component.perEventMinor} ${manifest.currency} minor`,
300
- component.bps > 0 ? `${component.bps} bps` : undefined,
301
- ]
302
- .filter((part): part is string => part !== undefined)
303
- .join(" + ");
304
- const total =
305
- component.bps === 0
306
- ? `${fixed} ${manifest.currency} minor`
307
- : [
308
- fixed === 0n ? undefined : `${fixed} ${manifest.currency} minor`,
309
- `amount-dependent (${component.bps} bps)`,
310
- ]
311
- .filter((part): part is string => part !== undefined)
312
- .join(" + ");
313
- rows.push(
314
- [
315
- `${action.instrument}.${action.action}`,
316
- component.signature,
317
- unit,
318
- component.count,
319
- total,
320
- component.payer,
321
- ].join("\t"),
322
- );
323
- }
324
- }
325
- return rows.join("\n");
326
- }
327
-
328
- async function writeOutput(
329
- path: string,
330
- contents: string,
331
- io: Io,
332
- ): Promise<number> {
333
- try {
334
- await io.writeFile(path, contents);
335
- } catch (cause) {
336
- io.err(`hsx: cannot write ${path}: ${messageOf(cause)}`);
337
- return USAGE;
338
- }
339
- return OK;
340
- }
341
-
342
- async function readDefaultCostTable(io: Io): Promise<UdlCostTables> {
343
- const candidates = [
344
- fileURLToPath(new URL("../examples/cost-table.json", import.meta.url)),
345
- fileURLToPath(new URL("../../examples/cost-table.json", import.meta.url)),
346
- ];
347
- let lastError: unknown;
348
- for (const path of candidates) {
349
- try {
350
- return JSON.parse(await io.readFile(path)) as UdlCostTables;
351
- } catch (cause) {
352
- lastError = cause;
70
+ text = result.formatted;
71
+ } else {
72
+ const result = compile(source, { standardLibrary });
73
+ for (const d of result.diagnostics)
74
+ io.err(
75
+ `${file}:${d.line}:${d.column}: ${d.code} ${d.message}. ${d.fix}`,
76
+ );
77
+ if (!result.artifacts || result.verdict !== "valid") return 1;
78
+ if (command === "check") return 0;
79
+ text =
80
+ command === "cost"
81
+ ? JSON.stringify(result.artifacts.costManifest, null, 2) + "\n"
82
+ : serializeUdl(result.artifacts.document);
353
83
  }
84
+ if (output) await io.writeFile(output, text);
85
+ else io.out(text.trimEnd());
86
+ return 0;
87
+ } catch (error) {
88
+ io.err(error instanceof Error ? error.message : String(error));
89
+ return 2;
354
90
  }
355
- throw lastError;
356
- }
357
-
358
- /** `file:line:col: severity [stage] message`, the shape editors already parse. */
359
- function diagnosticLines(
360
- file: string,
361
- result: CompileResult,
362
- ): readonly string[] {
363
- return result.diagnostics.map((diagnostic) => {
364
- const codePrefix = diagnostic.code ? `${diagnostic.code}: ` : "";
365
- return `${diagnostic.file ?? file}:${diagnostic.line}:${diagnostic.column}: ${diagnostic.severity} [${diagnostic.stage}] ${codePrefix}${diagnostic.message}`;
366
- });
367
- }
368
-
369
- function messageOf(cause: unknown): string {
370
- return cause instanceof Error ? cause.message : String(cause);
371
91
  }
372
92
 
373
- if (import.meta.main) {
374
- const { readFile, writeFile } = await import("node:fs/promises");
93
+ if (import.meta.main)
375
94
  process.exitCode = await runCli(process.argv.slice(2), {
376
- err: (line) => process.stderr.write(`${line}\n`),
377
- out: (line) => process.stdout.write(`${line}\n`),
95
+ out: (line) => process.stdout.write(line + "\n"),
96
+ err: (line) => process.stderr.write(line + "\n"),
378
97
  readFile: (path) => readFile(path, "utf8"),
379
- writeFile: (path, contents) => writeFile(path, contents, "utf8"),
380
- stdin: process.stdin,
381
- stdout: process.stdout,
98
+ writeFile: (path, text) => writeFile(path, text, "utf8"),
382
99
  });
383
- }