@hyperscale0/hsx 2.1.1 → 2.2.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 (110) hide show
  1. package/CHANGELOG.md +38 -7
  2. package/dist/src/cli.d.ts +1 -1
  3. package/dist/src/cli.d.ts.map +1 -1
  4. package/dist/src/cli.js +39 -5
  5. package/dist/src/cli.js.map +1 -1
  6. package/dist/src/compile.d.ts +0 -1
  7. package/dist/src/compile.d.ts.map +1 -1
  8. package/dist/src/compile.js +15 -1
  9. package/dist/src/compile.js.map +1 -1
  10. package/dist/src/cost.d.ts +3 -3
  11. package/dist/src/cost.d.ts.map +1 -1
  12. package/dist/src/cost.js +22 -12
  13. package/dist/src/cost.js.map +1 -1
  14. package/dist/src/parse.d.ts.map +1 -1
  15. package/dist/src/parse.js +10 -14
  16. package/dist/src/parse.js.map +1 -1
  17. package/dist/src/std-bundle.d.ts.map +1 -1
  18. package/dist/src/std-bundle.js +20 -84
  19. package/dist/src/std-bundle.js.map +1 -1
  20. package/dist/src/typecheck.d.ts.map +1 -1
  21. package/dist/src/typecheck.js +171 -39
  22. package/dist/src/typecheck.js.map +1 -1
  23. package/dist/src/version.d.ts +1 -1
  24. package/dist/src/version.js +1 -1
  25. package/docs/README.md +4 -2
  26. package/docs/guide/01-first-program.md +1 -1
  27. package/docs/guide/03-instruments.md +14 -1
  28. package/docs/guide/06-schedules.md +5 -1
  29. package/docs/guide/08-writing-a-module.md +48 -0
  30. package/docs/llms-full.txt +1966 -422
  31. package/docs/llms.txt +3 -4
  32. package/docs/reference/cli.md +6 -5
  33. package/docs/reference/diagnostics.md +9 -9
  34. package/docs/reference/grammar.md +2 -3
  35. package/docs/reference/std/advance.md +94 -17
  36. package/docs/reference/std/cancellable_booking.md +138 -17
  37. package/docs/reference/std/captured_payment.md +96 -24
  38. package/docs/reference/std/conditional_disbursement.md +82 -14
  39. package/docs/reference/std/credit_facility.md +89 -16
  40. package/docs/reference/std/held_payment.md +155 -55
  41. package/docs/reference/std/instant_transfer.md +79 -12
  42. package/docs/reference/std/metered.md +71 -9
  43. package/docs/reference/std/pooled_split.md +76 -7
  44. package/docs/reference/std/premium_forward.md +100 -20
  45. package/docs/reference/std/reconciled_payout.md +84 -14
  46. package/docs/reference/std/rotating_pool.md +112 -24
  47. package/docs/reference/std/scheduled.md +117 -32
  48. package/docs/reference/std/security_deposit.md +119 -27
  49. package/docs/reference/std/settlement_batch.md +105 -24
  50. package/docs/reference/std/swap.md +113 -26
  51. package/docs/reference/std/threshold_pool.md +120 -29
  52. package/docs/reference/std/weighted_distribution.md +117 -21
  53. package/docs/reference/types.md +39 -15
  54. package/docs/reference/udl-output.md +6 -6
  55. package/examples/01-first-program/README.md +1 -1
  56. package/examples/{02-imports-and-archetypes → 02-imports-and-modules}/README.md +1 -1
  57. package/examples/{02-imports-and-archetypes → 02-imports-and-modules}/photo-booth.hsx +1 -1
  58. package/examples/04-complete-product/README.md +1 -1
  59. package/examples/05-authored-instrument/README.md +5 -0
  60. package/examples/05-authored-instrument/payment.hsx +37 -0
  61. package/examples/05-watch-club/watch-club.hsx +0 -1
  62. package/examples/README.md +1 -2
  63. package/examples/advance/advance.udl +29 -6
  64. package/examples/cancellable_booking/cancellable_booking.udl +19 -0
  65. package/examples/captured_payment/captured_payment.hsx +0 -4
  66. package/examples/captured_payment/captured_payment.udl +6 -0
  67. package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -2
  68. package/examples/conditional_disbursement/conditional_disbursement.udl +4 -0
  69. package/examples/cost-table.json +136 -8
  70. package/examples/credit_facility/credit_facility.hsx +0 -3
  71. package/examples/credit_facility/credit_facility.udl +17 -1
  72. package/examples/held_payment/held_payment.udl +16 -0
  73. package/examples/instant_transfer/instant_transfer.udl +6 -0
  74. package/examples/metered/metered.udl +5 -1
  75. package/examples/pooled_split/pooled_split.udl +4 -0
  76. package/examples/premium_forward/premium_forward.udl +4 -0
  77. package/examples/reconciled_payout/reconciled_payout.udl +7 -0
  78. package/examples/rotating_pool/rotating_pool.udl +8 -0
  79. package/examples/scheduled/scheduled.udl +18 -3
  80. package/examples/security_deposit/security_deposit.udl +13 -0
  81. package/examples/settlement_batch/settlement_batch.udl +4 -0
  82. package/examples/swap/swap.udl +10 -0
  83. package/examples/threshold_pool/threshold_pool.udl +11 -0
  84. package/examples/weighted_distribution/weighted_distribution.udl +9 -0
  85. package/package.json +10 -10
  86. package/skills/hsx/SKILL.md +2 -36
  87. package/src/cli.ts +41 -5
  88. package/src/compile.ts +14 -6
  89. package/src/cost.ts +13 -16
  90. package/src/parse.ts +15 -10
  91. package/src/std-bundle.ts +21 -88
  92. package/src/typecheck.ts +207 -34
  93. package/src/version.ts +1 -1
  94. package/std/SEMANTICS.md +33 -128
  95. package/std/money_flows/advance.hsx +39 -26
  96. package/std/money_flows/cancellable_booking.hsx +275 -8
  97. package/std/money_flows/captured_payment.hsx +1 -9
  98. package/std/money_flows/conditional_disbursement.hsx +0 -6
  99. package/std/money_flows/credit_facility.hsx +0 -9
  100. package/std/money_flows/held_payment.hsx +39 -1
  101. package/std/money_flows/index.hsx +2 -1
  102. package/std/money_flows/metered.hsx +1 -5
  103. package/std/money_flows/scheduled.hsx +29 -119
  104. package/std/money_flows/threshold_pool.hsx +40 -5
  105. package/std/money_flows/weighted_distribution.hsx +47 -5
  106. package/docs/reference/std/recurring_collection.md +0 -25
  107. package/examples/recurring_collection/README.md +0 -3
  108. package/examples/recurring_collection/recurring_collection.hsx +0 -21
  109. package/examples/recurring_collection/recurring_collection.udl +0 -1135
  110. package/std/money_flows/recurring_collection.hsx +0 -72
@@ -23,7 +23,6 @@ Choose the module whose lifecycle and money path already match the product. Do n
23
23
  - `cancellable_booking` quotes a cancellation penalty before commitment.
24
24
  - `captured_payment` reserves, captures, settles, voids, or corrects a payment.
25
25
  - `scheduled` expands finite installments or a bounded obligation.
26
- - `recurring_collection` records collection periods.
27
26
  - `advance` disburses now against finite repayment or a referenced release.
28
27
  - `credit_facility` controls draws against a referenced obligation.
29
28
  - `conditional_disbursement` releases a capped amount on stored evidence.
@@ -107,10 +106,6 @@ settlement card_payment = captured_payment {
107
106
  reserve_until: reserveUntil
108
107
  correction: port correct_capture
109
108
  external_reversal: port reverse_capture within P14D
110
- capture_mode: partial_then_full
111
- correction_mode: full_only
112
- negative_position: reject
113
- timeout: reject
114
109
  }
115
110
  port correct_capture { allowed: [payee] }
116
111
  port reverse_capture {
@@ -132,8 +127,6 @@ settlement claim_payment = conditional_disbursement {
132
127
  cap: policyLimit: money(SAR)
133
128
  amount: approvedAmount: money(SAR)
134
129
  decision: port approve_claim
135
- reopen_policy: refuse
136
- recovery_policy: separate_transfer
137
130
  }
138
131
  port approve_claim {
139
132
  allowed: [source]
@@ -167,9 +160,6 @@ settlement facility = credit_facility {
167
160
  limit: facilityLimit: money(SAR)
168
161
  expires_at: expiresAt
169
162
  obligation: repayment.obligation
170
- availability_policy: revolving
171
- expiry_policy: freeze_draws
172
- close_policy: no_open_draws
173
163
  }
174
164
  ```
175
165
 
@@ -339,32 +329,6 @@ settlement supplier_payout = reconciled_payout {
339
329
  }
340
330
  ```
341
331
 
342
- <!-- hsx-brick: recurring_collection -->
343
-
344
- ```hsx
345
- program recurring_collection_example "Recurring collection example"
346
- import { recurring_collection, scheduled } from "std/money_flows"
347
- party debtor: person
348
- party repayment_source: business
349
- party recipient: business
350
- settlement obligation = scheduled {
351
- mode: obligation
352
- payer: repayment_source
353
- payee: recipient
354
- debtor: debtor
355
- amount: principal: money(SAR)
356
- count: 2
357
- every: P30D
358
- first_due: firstDueAt
359
- mandate: port mandate_evidence
360
- }
361
- settlement collection = recurring_collection {}
362
- port mandate_evidence {
363
- allowed: [repayment_source]
364
- shape: { evidenceReference: text }
365
- }
366
- ```
367
-
368
332
  <!-- hsx-brick: rotating_pool -->
369
333
 
370
334
  ```hsx
@@ -481,3 +445,5 @@ port snapshot_entitlements {
481
445
  ## Output boundary
482
446
 
483
447
  Use `compile` or the Hyperscale CLI to obtain canonical UDL, the origin map, and the cost manifest. The engine reads UDL and never HSX. When a task needs direct UDL work, use the UDL skill instead.
448
+
449
+ Programs that `use` published instruments need the selected catalogue when checked outside the engine: `hsx check --strict --catalog catalog.udl company.hsx`. Obtain that canonical UDL from the catalogue publisher. The CLI does not fetch or choose a revision.
package/src/cli.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  * asserting on a subprocess's scrollback.
11
11
  */
12
12
 
13
+ import { parseUdl, type UdlDocument } from "@hyperscale0/udl";
13
14
  import { fileURLToPath } from "node:url";
14
15
  import { compile, type CompileResult } from "./compile.ts";
15
16
  import type { UdlCostManifest, UdlCostTables } from "./cost.ts";
@@ -39,9 +40,9 @@ const USAGE = 2;
39
40
  export const USAGE_TEXT = `hsx ${HSX_VERSION}, the HSX compiler
40
41
 
41
42
  Usage:
42
- hsx check <file.hsx> [--strict]
43
- hsx build <file.hsx> [--out <file.json>] [--strict]
44
- hsx cost <file.hsx> [--json] [--out <file.json>] [--strict]
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]
45
46
  hsx explain <HSX####>
46
47
  hsx format <file.hsx>
47
48
  hsx lsp
@@ -57,6 +58,7 @@ Commands:
57
58
  lsp Run the language server over stdin and stdout.
58
59
 
59
60
  Options:
61
+ --catalog <file> Read the published instrument catalogue as canonical UDL JSON.
60
62
  --json Print the cost manifest as JSON instead of a table.
61
63
  --out <file> Write build or cost JSON to this path instead of stdout.
62
64
  --strict Treat warning-severity diagnostics as failures.
@@ -134,7 +136,21 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
134
136
  io.err(`hsx: cannot read the packaged cost table: ${messageOf(cause)}`);
135
137
  return USAGE;
136
138
  }
137
- const result = compile(source, { costTable });
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
+ });
138
154
  for (const line of diagnosticLines(parsed.file, result)) io.err(line);
139
155
 
140
156
  const refused =
@@ -174,6 +190,7 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
174
190
  }
175
191
 
176
192
  interface Options {
193
+ readonly catalog?: string;
177
194
  readonly file: string;
178
195
  readonly json: boolean;
179
196
  readonly out?: string;
@@ -185,6 +202,7 @@ function parseOptions(
185
202
  command: "build" | "check" | "cost" | "format",
186
203
  ): Options | { readonly error: string } {
187
204
  let file: string | undefined;
205
+ let catalog: string | undefined;
188
206
  let json = false;
189
207
  let out: string | undefined;
190
208
  let strict = false;
@@ -203,6 +221,18 @@ function parseOptions(
203
221
  json = true;
204
222
  continue;
205
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
+ }
206
236
  if (argument === "--out") {
207
237
  if (command !== "build" && command !== "cost") {
208
238
  return { error: "--out belongs to hsx build or hsx cost" };
@@ -225,7 +255,13 @@ function parseOptions(
225
255
  }
226
256
 
227
257
  if (file === undefined) return { error: `hsx ${command} needs a file` };
228
- return { file, json, ...(out === undefined ? {} : { out }), strict };
258
+ return {
259
+ file,
260
+ json,
261
+ ...(catalog === undefined ? {} : { catalog }),
262
+ ...(out === undefined ? {} : { out }),
263
+ strict,
264
+ };
229
265
  }
230
266
 
231
267
  function explainDiagnostic(args: readonly string[], io: Io): number {
package/src/compile.ts CHANGED
@@ -66,7 +66,6 @@ interface CompileArtifacts {
66
66
  }
67
67
 
68
68
  export interface CompileOptions extends HsxCompilerHost {
69
- readonly composesCatalogBlueprint?: boolean;
70
69
  /** One table, or one per billing currency; the program's money fields pick. */
71
70
  readonly costTable?: UdlCostTables;
72
71
  }
@@ -136,6 +135,19 @@ export function compile(
136
135
  });
137
136
  };
138
137
 
138
+ const optionKeys = new Set([
139
+ "costTable",
140
+ "moduleName",
141
+ "publishedCatalog",
142
+ "resolveModule",
143
+ "standardLibrary",
144
+ ]);
145
+ for (const key of Object.keys(options)) {
146
+ if (optionKeys.has(key)) continue;
147
+ at("bind", "error", `unknown compile option ${key}`, { start: 0, end: 0 });
148
+ }
149
+ if (diagnostics.length > 0) return { diagnostics, verdict: "invalid" };
150
+
139
151
  const parsed = parseProgram(source);
140
152
  for (const diagnostic of parsed.diagnostics) {
141
153
  at(
@@ -185,11 +197,7 @@ export function compile(
185
197
  );
186
198
  }
187
199
  if (!checked.program) return { diagnostics, verdict: "invalid" };
188
- const cost = buildCostManifest(
189
- checked.program,
190
- options.costTable,
191
- options.composesCatalogBlueprint ?? false,
192
- );
200
+ const cost = buildCostManifest(checked.program, options.costTable);
193
201
  if (!cost.ok) {
194
202
  for (const diagnostic of cost.diagnostics) {
195
203
  at(
package/src/cost.ts CHANGED
@@ -90,7 +90,6 @@ export type CostManifestResult =
90
90
  export function buildCostManifest(
91
91
  program: TypedProgram,
92
92
  tables: UdlCostTables | undefined,
93
- composesCatalogBlueprint: boolean,
94
93
  ): CostManifestResult {
95
94
  const priced = tables === undefined ? [] : [tables].flat();
96
95
  if (priced.length === 0) {
@@ -164,7 +163,7 @@ export function buildCostManifest(
164
163
  }
165
164
  }
166
165
  const fixed = fixedCost(
167
- typedStructuralCounts(program, composesCatalogBlueprint),
166
+ typedStructuralCounts(program),
168
167
  table,
169
168
  program.origin,
170
169
  diagnostics,
@@ -222,11 +221,10 @@ function ledgerCostTable(
222
221
  export function computeUdlFixedCost(
223
222
  document: UdlDocument,
224
223
  table: UdlCostTable,
225
- composesCatalogBlueprint: boolean,
226
224
  ): UdlCostManifest["fixed"] {
227
225
  const diagnostics: GeneralDiagnostic[] = [];
228
226
  const fixed = fixedCost(
229
- udlStructuralCounts(document, composesCatalogBlueprint),
227
+ udlStructuralCounts(document),
230
228
  table,
231
229
  { start: 0, end: 0 },
232
230
  diagnostics,
@@ -238,7 +236,6 @@ export function computeUdlFixedCost(
238
236
  export function buildUdlCostManifest(
239
237
  document: UdlDocument,
240
238
  table: UdlCostTable,
241
- composesCatalogBlueprint: boolean,
242
239
  ): UdlCostManifest {
243
240
  if (!CURRENCY.test(table.currency)) {
244
241
  throw new Error(
@@ -286,11 +283,7 @@ export function buildUdlCostManifest(
286
283
  );
287
284
  }
288
285
  }
289
- return manifest(
290
- table,
291
- computeUdlFixedCost(document, table, composesCatalogBlueprint),
292
- actions,
293
- );
286
+ return manifest(table, computeUdlFixedCost(document, table), actions);
294
287
  }
295
288
 
296
289
  export function evaluateUdlCostManifest(
@@ -473,6 +466,16 @@ function fixedCost(
473
466
  ): UdlCostManifest["fixed"] {
474
467
  const components: UdlCostManifest["fixed"]["components"][number][] = [];
475
468
  let complexityScore = 0;
469
+ for (const weight of Object.keys(table.fixed.weights)) {
470
+ if (Object.hasOwn(counts, weight)) continue;
471
+ diagnostics.push({
472
+ code: "HSX1302",
473
+ fix: `remove the unknown ${weight} weight from cost table ${table.version}`,
474
+ message: `cost table ${table.version} has an unknown ${weight} complexity weight`,
475
+ severity: "error",
476
+ span,
477
+ });
478
+ }
476
479
  for (const [weight, count] of Object.entries(counts)) {
477
480
  if (count === 0) continue;
478
481
  const value = table.fixed.weights[weight];
@@ -518,7 +521,6 @@ function fixedCost(
518
521
 
519
522
  function typedStructuralCounts(
520
523
  program: TypedProgram,
521
- blueprint: boolean,
522
524
  ): Readonly<Record<string, number>> {
523
525
  const subjectKinds = new Set<string>();
524
526
  const decisions = new Set<string>();
@@ -562,13 +564,11 @@ function typedStructuralCounts(
562
564
  aggregate,
563
565
  gate,
564
566
  timer,
565
- blueprint,
566
567
  );
567
568
  }
568
569
 
569
570
  function udlStructuralCounts(
570
571
  document: UdlDocument,
571
- blueprint: boolean,
572
572
  ): Readonly<Record<string, number>> {
573
573
  const subjectKinds = new Set<string>();
574
574
  const decisions = new Set<string>();
@@ -611,7 +611,6 @@ function udlStructuralCounts(
611
611
  aggregate,
612
612
  gate,
613
613
  timer,
614
- blueprint,
615
614
  );
616
615
  }
617
616
 
@@ -625,7 +624,6 @@ function countRecord(
625
624
  aggregateInvariant: number,
626
625
  gate: number,
627
626
  timer: number,
628
- blueprint: boolean,
629
627
  ): Readonly<Record<string, number>> {
630
628
  return {
631
629
  instrument,
@@ -637,7 +635,6 @@ function countRecord(
637
635
  aggregate_invariant: aggregateInvariant,
638
636
  gate,
639
637
  timer,
640
- blueprint: blueprint ? 1 : 0,
641
638
  };
642
639
  }
643
640
 
package/src/parse.ts CHANGED
@@ -1,3 +1,11 @@
1
+ import { udlClauseVocabulary } from "@hyperscale0/udl";
2
+
3
+ const compoundClauseHeads = new Set(
4
+ udlClauseVocabulary
5
+ .filter((clause) => clause.spelling.includes(" "))
6
+ .map((clause) => clause.spelling.split(" ")[0]),
7
+ );
8
+
1
9
  /**
2
10
  * The HSX parser. Recursive descent over the lexer's token stream, total:
3
11
  * it always returns a best-effort `Program` plus diagnostics, recovering at
@@ -495,15 +503,15 @@ class Parser {
495
503
  if (
496
504
  !this.expectPunct(
497
505
  "=",
498
- `settlement ${name.name} instantiates an archetype: settlement ${name.name} = held_payment { ... }`,
506
+ `settlement ${name.name} instantiates a module: settlement ${name.name} = held_payment { ... }`,
499
507
  )
500
508
  ) {
501
509
  return undefined;
502
510
  }
503
- const archetype = this.expectIdent(
504
- `expected an archetype name for settlement ${name.name}`,
511
+ const moduleName = this.expectIdent(
512
+ `expected a module name for settlement ${name.name}`,
505
513
  );
506
- if (!archetype) return undefined;
514
+ if (!moduleName) return undefined;
507
515
  if (!this.atPunct("{")) {
508
516
  this.error(
509
517
  this.peek().span,
@@ -521,9 +529,9 @@ class Parser {
521
529
  span: row.value.span,
522
530
  type: row.value,
523
531
  })),
524
- callee: { kind: "path", parts: [archetype], span: archetype.span },
532
+ callee: { kind: "path", parts: [moduleName], span: moduleName.span },
525
533
  kind: "apply",
526
- span: { start: archetype.span.start, end: body.span.end },
534
+ span: { start: moduleName.span.start, end: body.span.end },
527
535
  typeArgs: [],
528
536
  },
529
537
  exported: false,
@@ -807,10 +815,7 @@ class Parser {
807
815
  }
808
816
  }
809
817
 
810
- if (
811
- (key.name === "requires" || key.name === "computes") &&
812
- this.isName(this.peek())
813
- ) {
818
+ if (compoundClauseHeads.has(key.name) && this.isName(this.peek())) {
814
819
  while (this.isName(this.peek())) qualifiers.push(this.parseIdent());
815
820
  }
816
821