@hyperscale0/hsx 2.0.5 → 2.1.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 (76) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +17 -3
  3. package/dist/src/cli.d.ts +1 -1
  4. package/dist/src/compile.d.ts +2 -0
  5. package/dist/src/compile.d.ts.map +1 -1
  6. package/dist/src/compile.js +15 -2
  7. package/dist/src/compile.js.map +1 -1
  8. package/dist/src/cost.d.ts.map +1 -1
  9. package/dist/src/cost.js +22 -6
  10. package/dist/src/cost.js.map +1 -1
  11. package/dist/src/diagnostics.d.ts +2 -0
  12. package/dist/src/diagnostics.d.ts.map +1 -1
  13. package/dist/src/diagnostics.js +60 -0
  14. package/dist/src/diagnostics.js.map +1 -1
  15. package/dist/src/emit.d.ts +16 -1
  16. package/dist/src/emit.d.ts.map +1 -1
  17. package/dist/src/emit.js +49 -11
  18. package/dist/src/emit.js.map +1 -1
  19. package/dist/src/index.d.ts +2 -1
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js +1 -1
  22. package/dist/src/index.js.map +1 -1
  23. package/dist/src/ir.d.ts +8 -1
  24. package/dist/src/ir.d.ts.map +1 -1
  25. package/dist/src/ir.js.map +1 -1
  26. package/dist/src/limits.d.ts +2 -0
  27. package/dist/src/limits.d.ts.map +1 -1
  28. package/dist/src/limits.js +2 -0
  29. package/dist/src/limits.js.map +1 -1
  30. package/dist/src/typecheck.d.ts.map +1 -1
  31. package/dist/src/typecheck.js +123 -15
  32. package/dist/src/typecheck.js.map +1 -1
  33. package/dist/src/version.d.ts +1 -1
  34. package/dist/src/version.js +1 -1
  35. package/docs/assets/brand/hsx-horizontal-white.svg +1 -0
  36. package/docs/assets/brand/hsx-horizontal.svg +1 -0
  37. package/docs/assets/brand/hsx-stacked-white.svg +1 -0
  38. package/docs/assets/brand/hsx-stacked.svg +1 -0
  39. package/docs/assets/brand/manifest.json +30 -0
  40. package/docs/assets/hsx.svg +8 -15
  41. package/docs/llms-full.txt +51 -3
  42. package/docs/llms.txt +2 -2
  43. package/docs/reference/cli.md +2 -2
  44. package/docs/reference/diagnostics.md +49 -1
  45. package/docs/reference/grammar.md +2 -2
  46. package/docs/reference/std/advance.md +1 -1
  47. package/docs/reference/std/cancellable_booking.md +1 -1
  48. package/docs/reference/std/captured_payment.md +1 -1
  49. package/docs/reference/std/conditional_disbursement.md +1 -1
  50. package/docs/reference/std/credit_facility.md +1 -1
  51. package/docs/reference/std/held_payment.md +1 -1
  52. package/docs/reference/std/instant_transfer.md +1 -1
  53. package/docs/reference/std/metered.md +1 -1
  54. package/docs/reference/std/pooled_split.md +1 -1
  55. package/docs/reference/std/premium_forward.md +1 -1
  56. package/docs/reference/std/reconciled_payout.md +1 -1
  57. package/docs/reference/std/recurring_collection.md +1 -1
  58. package/docs/reference/std/rotating_pool.md +1 -1
  59. package/docs/reference/std/scheduled.md +1 -1
  60. package/docs/reference/std/security_deposit.md +1 -1
  61. package/docs/reference/std/settlement_batch.md +1 -1
  62. package/docs/reference/std/swap.md +1 -1
  63. package/docs/reference/std/threshold_pool.md +1 -1
  64. package/docs/reference/std/weighted_distribution.md +1 -1
  65. package/docs/reference/types.md +1 -1
  66. package/docs/reference/udl-output.md +1 -1
  67. package/package.json +3 -3
  68. package/src/compile.ts +17 -2
  69. package/src/cost.ts +32 -5
  70. package/src/diagnostics.ts +67 -0
  71. package/src/emit.ts +82 -15
  72. package/src/index.ts +6 -1
  73. package/src/ir.ts +15 -1
  74. package/src/limits.ts +2 -0
  75. package/src/typecheck.ts +147 -16
  76. package/src/version.ts +1 -1
@@ -543,4 +543,71 @@ instrument probe {
543
543
  fix: "Correct compile-time block keys or parameter bindings.",
544
544
  example: instrument('title: "__hsx_none__";'),
545
545
  },
546
+ {
547
+ code: "HSX1610",
548
+ stage: "typecheck",
549
+ title: "Invalid piece partition",
550
+ fix: "Bind immutable money and account fields to one declared partition.",
551
+ example: null,
552
+ reason:
553
+ "The shared UDL action-plan validator supplies the code and source path.",
554
+ },
555
+ {
556
+ code: "HSX1611",
557
+ stage: "typecheck",
558
+ title: "Invalid piece stage",
559
+ fix: "Use a declared piece stage and let the compiler derive pieceId.",
560
+ example: null,
561
+ reason:
562
+ "The shared UDL action-plan validator supplies the code and source path.",
563
+ },
564
+ {
565
+ code: "HSX1612",
566
+ stage: "typecheck",
567
+ title: "Invalid action graph",
568
+ fix: "Declare an acyclic action graph within the expansion bound.",
569
+ example: null,
570
+ reason:
571
+ "The shared UDL action-plan validator supplies the code and source path.",
572
+ },
573
+ {
574
+ code: "HSX1613",
575
+ stage: "typecheck",
576
+ title: "Invalid static call binding",
577
+ fix: "Bind declared typed targets with unique captures and money consumption.",
578
+ example: null,
579
+ reason:
580
+ "The shared UDL action-plan validator supplies the code and source path.",
581
+ },
582
+ {
583
+ code: "HSX1614",
584
+ stage: "typecheck",
585
+ title: "Incompatible action boundary",
586
+ fix: "Keep the parent principal, approval and recovery boundary.",
587
+ example: null,
588
+ reason:
589
+ "The shared UDL action-plan validator supplies the code and source path.",
590
+ },
591
+ {
592
+ code: "HSX1615",
593
+ stage: "typecheck",
594
+ title: "Invalid leaf evidence or effects",
595
+ fix: "Declare evidence and the exact effects of each expanded leaf.",
596
+ example: null,
597
+ reason:
598
+ "The shared UDL action-plan validator supplies the code and source path.",
599
+ },
546
600
  ] as const;
601
+
602
+ /** Preserve the UDL diagnostic family when reporting a typed clause. */
603
+ export function hsxClauseDiagnosticCode(code: string): string {
604
+ const codes: Readonly<Record<string, string>> = {
605
+ UDL4002: "HSX1610",
606
+ UDL5013: "HSX1611",
607
+ UDL2010: "HSX1612",
608
+ UDL2011: "HSX1613",
609
+ UDL2012: "HSX1614",
610
+ UDL2013: "HSX1615",
611
+ };
612
+ return codes[code] ?? "HSX1602";
613
+ }
package/src/emit.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  import {
2
2
  validateUdl,
3
+ resolveUdlActionPlans,
4
+ udlClauseVocabulary,
3
5
  type UdlDocument,
4
6
  type UdlIssueCode,
5
7
  } from "@hyperscale0/udl";
6
- import type { TypedInstrument, TypedProgram } from "./ir.ts";
8
+ import type {
9
+ JsonValue,
10
+ TypedInstrument,
11
+ TypedProgram,
12
+ ResolvedProgramActionPlan,
13
+ } from "./ir.ts";
14
+
15
+ import { hsxClauseDiagnosticCode } from "./diagnostics.ts";
7
16
 
8
17
  export interface OriginMapEntry {
9
18
  readonly path: string;
@@ -25,24 +34,14 @@ export type GeneralLowerResult =
25
34
  readonly ok: true;
26
35
  readonly value: {
27
36
  readonly document: UdlDocument;
37
+ readonly actionPlans?: readonly ResolvedProgramActionPlan[];
28
38
  readonly originMap: readonly OriginMapEntry[];
29
39
  };
30
40
  };
31
41
 
32
42
  export function lowerGeneralProgram(program: TypedProgram): GeneralLowerResult {
33
43
  const candidate = {
34
- instruments: program.instruments.map((instrument) => ({
35
- ...instrument.slots,
36
- actionOrder: instrument.actions.map((action) => action.name),
37
- actions: Object.fromEntries(
38
- instrument.actions.map((action) => [action.name, action.slots]),
39
- ),
40
- fields: Object.fromEntries(
41
- instrument.fields.map((field) => [field.name, field.schema]),
42
- ),
43
- id: instrument.id,
44
- required: requiredFields(instrument),
45
- })),
44
+ instruments: program.instruments.map(generalInstrumentValue),
46
45
  product: program.name,
47
46
  subjects: program.subjects.map((subject) => ({
48
47
  declaredValue: subject.declaredValue,
@@ -79,8 +78,11 @@ export function lowerGeneralProgram(program: TypedProgram): GeneralLowerResult {
79
78
  issues: validation.issues.map((issue) => {
80
79
  const origin = originForUdlPath(issue.path, origins);
81
80
  return {
82
- code: issue.category === "invalid_semantics" ? "HSX1602" : "HSX1601",
83
- fix: "correct the named clause so it matches the targeted UDL definition",
81
+ code:
82
+ issue.category === "invalid_semantics"
83
+ ? hsxClauseDiagnosticCode(issue.code)
84
+ : "HSX1601",
85
+ fix: issue.fix,
84
86
  message: `${issue.path}: ${issue.message}`,
85
87
  path: issue.path,
86
88
  span: origin?.span ?? program.origin,
@@ -91,10 +93,17 @@ export function lowerGeneralProgram(program: TypedProgram): GeneralLowerResult {
91
93
  };
92
94
  }
93
95
  const document = validation.value;
96
+ const actionPlans = document.instruments.flatMap((instrument) =>
97
+ resolveUdlActionPlans(instrument).plans.map((plan) => ({
98
+ ...plan,
99
+ instrument: instrument.id,
100
+ })),
101
+ );
94
102
  return {
95
103
  ok: true,
96
104
  value: {
97
105
  document,
106
+ ...(actionPlans.length > 0 ? { actionPlans } : {}),
98
107
  originMap: originMapFor(program),
99
108
  },
100
109
  };
@@ -213,3 +222,61 @@ function originMapFor(program: TypedProgram): OriginMapEntry[] {
213
222
  });
214
223
  return entries;
215
224
  }
225
+
226
+ /** Emit a canonical clause value through the same generic HSX block syntax. */
227
+ export function emitUdlClause(
228
+ scope: "instrument" | "action",
229
+ target: string,
230
+ value: JsonValue,
231
+ ): string {
232
+ const clause = udlClauseVocabulary.find(
233
+ (entry) => entry.scope === scope && entry.target === target,
234
+ );
235
+ if (!clause) throw new Error(`Unknown ${scope} clause ${target}`);
236
+ return `${clause.spelling.replaceAll(" ", "_")}: ${clauseLiteral(value)};`;
237
+ }
238
+
239
+ function clauseLiteral(value: JsonValue): string {
240
+ if (Array.isArray(value)) return `[${value.map(clauseLiteral).join(", ")}]`;
241
+ if (value !== null && typeof value === "object") {
242
+ return `{ ${Object.entries(value)
243
+ .map(([key, item]) => `${JSON.stringify(key)}: ${clauseLiteral(item)};`)
244
+ .join(" ")} }`;
245
+ }
246
+ if (
247
+ value === null ||
248
+ (typeof value === "number" && !Number.isSafeInteger(value))
249
+ ) {
250
+ throw new Error(
251
+ "HSX clause literals require non-null values and safe integers",
252
+ );
253
+ }
254
+ return JSON.stringify(value);
255
+ }
256
+
257
+ export function hasActionPlanClauses(instrument: TypedInstrument): boolean {
258
+ return (
259
+ instrument.slots.piecePlan !== undefined ||
260
+ instrument.slots.actionLibrary !== undefined ||
261
+ instrument.actions.some(
262
+ (action) =>
263
+ action.slots.calls !== undefined ||
264
+ action.slots.pieceStage !== undefined,
265
+ )
266
+ );
267
+ }
268
+
269
+ export function generalInstrumentValue(instrument: TypedInstrument) {
270
+ return {
271
+ ...instrument.slots,
272
+ actionOrder: instrument.actions.map((action) => action.name),
273
+ actions: Object.fromEntries(
274
+ instrument.actions.map((action) => [action.name, action.slots]),
275
+ ),
276
+ fields: Object.fromEntries(
277
+ instrument.fields.map((field) => [field.name, field.schema]),
278
+ ),
279
+ id: instrument.id,
280
+ required: requiredFields(instrument),
281
+ };
282
+ }
package/src/index.ts CHANGED
@@ -28,7 +28,11 @@ export {
28
28
  type ProgramEntryOverrideValue,
29
29
  } from "./entry-overrides.ts";
30
30
  export { type OriginMapEntry } from "./emit.ts";
31
- export { lowerGeneralProgram, originForUdlPath } from "./emit.ts";
31
+ export {
32
+ lowerGeneralProgram,
33
+ originForUdlPath,
34
+ emitUdlClause,
35
+ } from "./emit.ts";
32
36
  export { parseProgram } from "./parse.ts";
33
37
  export { checkGeneralProgram, type GeneralCheckOptions } from "./typecheck.ts";
34
38
  export {
@@ -40,3 +44,4 @@ export {
40
44
  type UseDecl,
41
45
  } from "./ast.ts";
42
46
  export { bundledStandardLibrary, type StandardLibrary } from "./std-library.ts";
47
+ export type { ResolvedActionPlan, ResolvedProgramActionPlan } from "./ir.ts";
package/src/ir.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import type { Span } from "./ast.ts";
2
- import type { UdlEffectKind } from "@hyperscale0/udl";
2
+ import type {
3
+ resolveUdlActionPlans,
4
+ UdlEffectKind,
5
+ UdlIssueCode,
6
+ } from "@hyperscale0/udl";
3
7
 
4
8
  export type JsonValue =
5
9
  | boolean
@@ -59,7 +63,15 @@ export interface TypedAction {
59
63
  readonly slots: Readonly<Record<string, JsonValue>>;
60
64
  }
61
65
 
66
+ export type ResolvedActionPlan = ReturnType<
67
+ typeof resolveUdlActionPlans
68
+ >["plans"][number];
69
+ export type ResolvedProgramActionPlan = ResolvedActionPlan & {
70
+ readonly instrument: string;
71
+ };
72
+
62
73
  export interface TypedInstrument {
74
+ readonly actionPlans?: readonly ResolvedActionPlan[];
63
75
  readonly actions: readonly TypedAction[];
64
76
  readonly fields: readonly TypedField[];
65
77
  readonly id: string;
@@ -86,6 +98,8 @@ export interface TypedProgram {
86
98
  }
87
99
 
88
100
  export interface GeneralDiagnostic {
101
+ readonly path?: string;
102
+ readonly udlCode?: UdlIssueCode;
89
103
  readonly code: string;
90
104
  readonly fix: string;
91
105
  readonly message: string;
package/src/limits.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  * on. The deepest nests 5 levels; the largest is 13,934 bytes.
7
7
  */
8
8
  export const HSX_LIMITS = Object.freeze({
9
+ /** Shared ceiling for finite comprehensions and action-call expansion. */
10
+ maxExpansions: 256,
9
11
  /**
10
12
  * How deep the recursive-descent productions may nest. Measured on this
11
13
  * machine, the parser exhausted the call stack at 9,217 nested blocks and
package/src/typecheck.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  deriveUdlActionEffects,
3
+ resolveUdlActionPlans,
4
+ udlDocumentSchema,
3
5
  udlClauseVocabulary,
4
6
  type UdlAction,
5
7
  type UdlDocument,
@@ -24,7 +26,14 @@ import type {
24
26
  SubjectDecl,
25
27
  UseDecl,
26
28
  } from "./ast.ts";
27
- import { requiredFields } from "./emit.ts";
29
+ import {
30
+ requiredFields,
31
+ generalInstrumentValue,
32
+ hasActionPlanClauses,
33
+ lowerGeneralProgram,
34
+ } from "./emit.ts";
35
+ import { hsxClauseDiagnosticCode } from "./diagnostics.ts";
36
+ import { HSX_LIMITS } from "./limits.ts";
28
37
  import type {
29
38
  GeneralCheckResult,
30
39
  GeneralDiagnostic,
@@ -43,7 +52,7 @@ const CURRENCY = /^[A-Z]{3}$/;
43
52
  const MONEY_PATTERN = "^[1-9][0-9]{0,17}$";
44
53
  const OPTIONAL_MONEY_PATTERN = "^(0|[1-9][0-9]{0,17})$";
45
54
  const ACCOUNT_PATTERN = "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
46
- const MAX_COMPREHENSION_EXPANSIONS = 256;
55
+
47
56
  const NONE_SENTINEL = "__hsx_none__";
48
57
 
49
58
  export interface GeneralCheckOptions {
@@ -517,7 +526,7 @@ export function checkGeneralProgram(
517
526
  subjects,
518
527
  title: header?.title?.value ?? sentenceCase(name),
519
528
  };
520
- return { diagnostics, program: typed };
529
+ return prepareActionPlanProgram(typed, diagnostics);
521
530
  }
522
531
 
523
532
  function checkPublishedProgram(
@@ -766,7 +775,7 @@ function checkPublishedProgram(
766
775
  ],
767
776
  title: header?.title?.value ?? sentenceCase(name),
768
777
  };
769
- return { diagnostics, program: typed };
778
+ return prepareActionPlanProgram(typed, diagnostics);
770
779
  }
771
780
 
772
781
  function projectedPublishedInstrument(
@@ -3014,11 +3023,11 @@ function expandComprehensions(
3014
3023
  });
3015
3024
  continue;
3016
3025
  }
3017
- if (budget.used + values.length > MAX_COMPREHENSION_EXPANSIONS) {
3026
+ if (budget.used + values.length > HSX_LIMITS.maxExpansions) {
3018
3027
  diagnostics.push({
3019
3028
  code: "HSX1404",
3020
- fix: `reduce the fixed expansion to at most ${MAX_COMPREHENSION_EXPANSIONS} rows`,
3021
- message: `bounded comprehensions expand past the ${MAX_COMPREHENSION_EXPANSIONS}-row compiler limit`,
3029
+ fix: `reduce the fixed expansion to at most ${HSX_LIMITS.maxExpansions} rows`,
3030
+ message: `bounded comprehensions expand past the ${HSX_LIMITS.maxExpansions}-row compiler limit`,
3022
3031
  severity: "error",
3023
3032
  span: bound.span,
3024
3033
  });
@@ -3481,7 +3490,10 @@ function checkInstrument(
3481
3490
  ];
3482
3491
  }
3483
3492
 
3484
- return { actions, fields, id: name.name, origin: name.span, slots };
3493
+ return derivePieceInputs(
3494
+ { actions, fields, id: name.name, origin: name.span, slots },
3495
+ diagnostics,
3496
+ );
3485
3497
  }
3486
3498
 
3487
3499
  // Mirrors the catalog law agent_description_required: every action a caller
@@ -4787,6 +4799,7 @@ function blockToObject(
4787
4799
  block: BlockExpr | undefined,
4788
4800
  substitutions: ReadonlyMap<string, Expr>,
4789
4801
  diagnostics: GeneralDiagnostic[],
4802
+ literalKeys = false,
4790
4803
  ): Record<string, JsonValue> {
4791
4804
  if (!block) return {};
4792
4805
  const result: Record<string, JsonValue> = {};
@@ -4802,10 +4815,8 @@ function blockToObject(
4802
4815
  });
4803
4816
  continue;
4804
4817
  }
4805
- result[row.key.quoted ? row.key.name : camel(row.key.name)] = exprToJson(
4806
- value,
4807
- diagnostics,
4808
- );
4818
+ result[literalKeys || row.key.quoted ? row.key.name : camel(row.key.name)] =
4819
+ exprToJson(value, diagnostics, literalKeys);
4809
4820
  }
4810
4821
  return result;
4811
4822
  }
@@ -4832,10 +4843,21 @@ function exprToJsonForUdlSlot(
4832
4843
  ]),
4833
4844
  );
4834
4845
  }
4835
- return exprToJson(expr, diagnostics);
4846
+ // Declaration maps retain their authored keys; field-spelling aliases still normalize.
4847
+ const literalKeys = udlClauseVocabulary.some(
4848
+ (clause) =>
4849
+ clause.target === slot &&
4850
+ "literalKeys" in clause &&
4851
+ clause.literalKeys === true,
4852
+ );
4853
+ return exprToJson(expr, diagnostics, literalKeys);
4836
4854
  }
4837
4855
 
4838
- function exprToJson(expr: Expr, diagnostics: GeneralDiagnostic[]): JsonValue {
4856
+ function exprToJson(
4857
+ expr: Expr,
4858
+ diagnostics: GeneralDiagnostic[],
4859
+ literalKeys = false,
4860
+ ): JsonValue {
4839
4861
  switch (expr.kind) {
4840
4862
  case "boolean":
4841
4863
  return expr.value;
@@ -4880,9 +4902,11 @@ function exprToJson(expr: Expr, diagnostics: GeneralDiagnostic[]): JsonValue {
4880
4902
  case "settlement_ref":
4881
4903
  return `${expr.owner.name}.${expr.member.name}`;
4882
4904
  case "list":
4883
- return expr.items.map((item) => exprToJson(item, diagnostics));
4905
+ return expr.items.map((item) =>
4906
+ exprToJson(item, diagnostics, literalKeys),
4907
+ );
4884
4908
  case "block":
4885
- return blockToObject(expr, new Map(), diagnostics);
4909
+ return blockToObject(expr, new Map(), diagnostics, literalKeys);
4886
4910
  case "call":
4887
4911
  return `${expr.callee.name}(${expr.args.map((arg) => String(exprToJson(arg, diagnostics))).join(",")})`;
4888
4912
  case "type_apply":
@@ -5055,3 +5079,110 @@ function sentenceCase(value: string): string {
5055
5079
  const words = value.replaceAll("_", " ");
5056
5080
  return `${words[0]?.toUpperCase() ?? ""}${words.slice(1)}`;
5057
5081
  }
5082
+
5083
+ function prepareActionPlanProgram(
5084
+ program: TypedProgram,
5085
+ diagnostics: GeneralDiagnostic[],
5086
+ ): GeneralCheckResult {
5087
+ if (!program.instruments.some(hasActionPlanClauses))
5088
+ return { diagnostics, program };
5089
+ const instruments = program.instruments.map((instrument): TypedInstrument => {
5090
+ if (!hasActionPlanClauses(instrument)) return instrument;
5091
+ const parsed = udlDocumentSchema.shape.instruments.element.safeParse(
5092
+ generalInstrumentValue(instrument),
5093
+ );
5094
+ if (!parsed.success) {
5095
+ for (const issue of parsed.error.issues)
5096
+ diagnostics.push({
5097
+ code: "HSX1601",
5098
+ severity: "error",
5099
+ span: instrument.origin,
5100
+ message: `${instrument.id}.${issue.path.join(".")}: ${issue.message}`,
5101
+ fix: "match the typed UDL clause declaration",
5102
+ });
5103
+ return instrument;
5104
+ }
5105
+ const resolved = resolveUdlActionPlans(parsed.data);
5106
+ for (const issue of resolved.issues)
5107
+ diagnostics.push({
5108
+ code: hsxClauseDiagnosticCode(issue.code),
5109
+ severity: "error",
5110
+ span: instrument.origin,
5111
+ message: `${instrument.id}${issue.path}: ${issue.message}`,
5112
+ udlCode: issue.code,
5113
+ path: issue.path,
5114
+ fix: issue.fix,
5115
+ });
5116
+ return {
5117
+ ...instrument,
5118
+ actionPlans: resolved.plans,
5119
+ actions: instrument.actions.map((action) => {
5120
+ if (!action.slots.calls) return action;
5121
+ const plan = resolved.plans.find((plan) => plan.action === action.name);
5122
+ if (!plan) return action;
5123
+ const effects = plan.effects;
5124
+ return {
5125
+ ...action,
5126
+ effects,
5127
+ slots: { ...action.slots, effects: effects as JsonValue },
5128
+ };
5129
+ }),
5130
+ };
5131
+ });
5132
+ if (diagnostics.some((issue) => issue.severity === "error"))
5133
+ return { diagnostics };
5134
+ const prepared = { ...program, instruments };
5135
+ const lowered = lowerGeneralProgram(prepared);
5136
+ if (!lowered.ok) {
5137
+ for (const issue of lowered.issues)
5138
+ diagnostics.push({
5139
+ code: issue.code,
5140
+ fix: issue.fix,
5141
+ message: issue.message,
5142
+ udlCode: issue.udlCode,
5143
+ path: issue.path,
5144
+ severity: "error",
5145
+ span: issue.span,
5146
+ });
5147
+ return { diagnostics };
5148
+ }
5149
+ return { diagnostics, program: prepared };
5150
+ }
5151
+
5152
+ function derivePieceInputs(
5153
+ instrument: TypedInstrument,
5154
+ diagnostics: GeneralDiagnostic[],
5155
+ ): TypedInstrument {
5156
+ return {
5157
+ ...instrument,
5158
+ actions: instrument.actions.map((action) => {
5159
+ const stage = jsonObject(action.slots.pieceStage);
5160
+ if (!stage) return action;
5161
+ if (action.slots.input !== undefined) {
5162
+ diagnostics.push({
5163
+ code: "HSX1611",
5164
+ severity: "error",
5165
+ span: action.origin,
5166
+ message: `action ${action.name} overrides its compiler-derived pieceId input`,
5167
+ fix: "remove input; piece_stage derives the strict pieceId enum",
5168
+ });
5169
+ return action;
5170
+ }
5171
+ const plan = jsonObject(instrument.slots.piecePlan);
5172
+ const order = plan?.[`${stage.stage}_order`];
5173
+ if (!Array.isArray(order) || order.length === 0) return action;
5174
+ return {
5175
+ ...action,
5176
+ slots: {
5177
+ ...action.slots,
5178
+ input: {
5179
+ type: "object",
5180
+ additionalProperties: false,
5181
+ required: ["pieceId"],
5182
+ properties: { pieceId: { type: "string", enum: order } },
5183
+ },
5184
+ },
5185
+ };
5186
+ }),
5187
+ };
5188
+ }
package/src/version.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Package version reported by the command line. */
2
- export const HSX_VERSION = "2.0.5";
2
+ export const HSX_VERSION = "2.1.0";
3
3
 
4
4
  /** Canonical UDL contract version emitted by this compiler. */
5
5
  export const HSX_TARGET_UDL_VERSION = 1;