@hyperscale0/hsx 2.4.0 → 2.4.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 (35) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/src/cli.d.ts +1 -1
  3. package/dist/src/typecheck.d.ts.map +1 -1
  4. package/dist/src/typecheck.js +114 -85
  5. package/dist/src/typecheck.js.map +1 -1
  6. package/dist/src/version.d.ts +1 -1
  7. package/dist/src/version.js +1 -1
  8. package/docs/llms-full.txt +2 -2
  9. package/docs/llms.txt +2 -2
  10. package/docs/reference/cli.md +2 -2
  11. package/docs/reference/diagnostics.md +1 -1
  12. package/docs/reference/grammar.md +1 -1
  13. package/docs/reference/std/advance.md +1 -1
  14. package/docs/reference/std/cancellable_booking.md +1 -1
  15. package/docs/reference/std/captured_payment.md +1 -1
  16. package/docs/reference/std/conditional_disbursement.md +1 -1
  17. package/docs/reference/std/credit_facility.md +1 -1
  18. package/docs/reference/std/held_payment.md +1 -1
  19. package/docs/reference/std/instant_transfer.md +1 -1
  20. package/docs/reference/std/metered.md +1 -1
  21. package/docs/reference/std/pooled_split.md +1 -1
  22. package/docs/reference/std/premium_forward.md +1 -1
  23. package/docs/reference/std/reconciled_payout.md +1 -1
  24. package/docs/reference/std/rotating_pool.md +1 -1
  25. package/docs/reference/std/scheduled.md +1 -1
  26. package/docs/reference/std/security_deposit.md +1 -1
  27. package/docs/reference/std/settlement_batch.md +1 -1
  28. package/docs/reference/std/swap.md +1 -1
  29. package/docs/reference/std/threshold_pool.md +1 -1
  30. package/docs/reference/std/weighted_distribution.md +1 -1
  31. package/docs/reference/types.md +1 -1
  32. package/docs/reference/udl-output.md +1 -1
  33. package/package.json +2 -2
  34. package/src/typecheck.ts +221 -87
  35. package/src/version.ts +1 -1
package/src/typecheck.ts CHANGED
@@ -284,6 +284,30 @@ export function checkGeneralProgram(
284
284
  } else templates.set(decl.name.name, decl);
285
285
  }
286
286
 
287
+ const prefixByInstrument = new Map<string, string>();
288
+ if (options.publishedCatalog) {
289
+ for (const inst of options.publishedCatalog.instruments) {
290
+ if (inst.idPrefix) {
291
+ prefixByInstrument.set(inst.id, inst.idPrefix);
292
+ }
293
+ }
294
+ }
295
+ for (const [name, decl] of templates) {
296
+ const prefix =
297
+ stringSlot(decl.body, "idPrefix", "id_prefix") ?? prefixFor(name);
298
+ prefixByInstrument.set(name, prefix);
299
+ }
300
+ for (const decl of program.decls) {
301
+ if (decl.kind === "instrument_apply" && decl.metadata) {
302
+ const prefix = stringSlot(decl.metadata, "idPrefix", "id_prefix");
303
+ if (prefix) {
304
+ prefixByInstrument.set(decl.name.name, prefix);
305
+ }
306
+ }
307
+ }
308
+ const resolvePrefix = (target: string): string | undefined =>
309
+ prefixByInstrument.get(target);
310
+
287
311
  const concrete: ConcreteInstrument[] = [];
288
312
  const reachedPorts = new Set<PortDecl>();
289
313
  const droppedPorts = new Set<PortDecl>();
@@ -291,10 +315,10 @@ export function checkGeneralProgram(
291
315
  declarationScope: readonly ApplicationScopeDecl[],
292
316
  ) => {
293
317
  const scopedAliases = new Map(aliases);
294
- const scopedConstants = resolveConstants([
295
- ...program.decls,
296
- ...declarationScope,
297
- ]);
318
+ const scopedConstants = resolveConstants(
319
+ [...program.decls, ...declarationScope],
320
+ resolvePrefix,
321
+ );
298
322
  const scopedParties = new Set(parties);
299
323
  const scopedPorts = new Map(ports);
300
324
  const scopedTemplates = new Map(templates);
@@ -305,6 +329,10 @@ export function checkGeneralProgram(
305
329
  if (local.kind === "port") scopedPorts.set(local.name.name, local);
306
330
  if (local.kind === "instrument") {
307
331
  scopedTemplates.set(local.name.name, local);
332
+ const prefix =
333
+ stringSlot(local.body, "idPrefix", "id_prefix") ??
334
+ prefixFor(local.name.name);
335
+ prefixByInstrument.set(local.name.name, prefix);
308
336
  }
309
337
  }
310
338
  checkPortDeclarations(scopedPorts, scopedParties);
@@ -350,24 +378,37 @@ export function checkGeneralProgram(
350
378
  values,
351
379
  diagnostics,
352
380
  dependencies,
381
+ [],
382
+ resolvePrefix,
353
383
  );
354
384
  const body = expandComprehensions(
355
385
  selected,
356
386
  diagnostics,
357
387
  values,
358
388
  dependencies,
389
+ resolvePrefix,
359
390
  );
391
+ const constructed = constructedInstruments(
392
+ decl.name,
393
+ body,
394
+ diagnostics,
395
+ resolvePrefix,
396
+ );
397
+ for (const candidate of constructed) {
398
+ const prefix =
399
+ stringSlot(candidate.body, "idPrefix", "id_prefix") ??
400
+ prefixFor(candidate.name.name);
401
+ prefixByInstrument.set(candidate.name.name, prefix);
402
+ }
360
403
  concrete.push(
361
- ...bindActionPorts(
362
- constructedInstruments(decl.name, body, diagnostics),
363
- scoped.ports,
364
- reachedPorts,
365
- ).map((candidate) => ({
366
- ...candidate,
367
- aliases: scoped.aliases,
368
- callee: candidate.name.name,
369
- parties: scoped.parties,
370
- })),
404
+ ...bindActionPorts(constructed, scoped.ports, reachedPorts).map(
405
+ (candidate) => ({
406
+ ...candidate,
407
+ aliases: scoped.aliases,
408
+ callee: candidate.name.name,
409
+ parties: scoped.parties,
410
+ }),
411
+ ),
371
412
  );
372
413
  }
373
414
  if (decl.kind !== "instrument_apply") continue;
@@ -402,32 +443,43 @@ export function checkGeneralProgram(
402
443
  scoped.aliases,
403
444
  scoped.constants,
404
445
  boundPorts,
446
+ resolvePrefix,
405
447
  );
406
448
  if (body) {
407
449
  const applicationPorts = new Set<PortDecl>();
408
450
  const merged = decl.metadata
409
451
  ? mergeApplicationMetadata(body, decl.metadata, diagnostics)
410
452
  : body;
453
+ const constructed = constructedInstruments(
454
+ decl.name,
455
+ merged,
456
+ diagnostics,
457
+ resolvePrefix,
458
+ );
459
+ for (const candidate of constructed) {
460
+ const prefix =
461
+ stringSlot(candidate.body, "idPrefix", "id_prefix") ??
462
+ prefixFor(candidate.name.name);
463
+ prefixByInstrument.set(candidate.name.name, prefix);
464
+ }
411
465
  concrete.push(
412
- ...bindActionPorts(
413
- constructedInstruments(decl.name, merged, diagnostics),
414
- boundPorts,
415
- applicationPorts,
416
- ).map((candidate) => {
417
- let candidateCallee = callee;
418
- if (
419
- candidate.name.name !== decl.name.name &&
420
- candidate.name.name.startsWith(`${decl.name.name}_`)
421
- ) {
422
- candidateCallee = `${callee}${candidate.name.name.slice(decl.name.name.length)}`;
423
- }
424
- return {
425
- ...candidate,
426
- aliases: scoped.aliases,
427
- callee: candidateCallee,
428
- parties: scoped.parties,
429
- };
430
- }),
466
+ ...bindActionPorts(constructed, boundPorts, applicationPorts).map(
467
+ (candidate) => {
468
+ let candidateCallee = callee;
469
+ if (
470
+ candidate.name.name !== decl.name.name &&
471
+ candidate.name.name.startsWith(`${decl.name.name}_`)
472
+ ) {
473
+ candidateCallee = `${callee}${candidate.name.name.slice(decl.name.name.length)}`;
474
+ }
475
+ return {
476
+ ...candidate,
477
+ aliases: scoped.aliases,
478
+ callee: candidateCallee,
479
+ parties: scoped.parties,
480
+ };
481
+ },
482
+ ),
431
483
  );
432
484
  for (const port of boundPorts.values()) {
433
485
  if (applicationPorts.has(port)) reachedPorts.add(port);
@@ -457,27 +509,12 @@ export function checkGeneralProgram(
457
509
  );
458
510
  }
459
511
 
460
- const prefixByInstrument = new Map<string, string>();
461
- if (options.publishedCatalog) {
462
- for (const inst of options.publishedCatalog.instruments) {
463
- if (inst.idPrefix) {
464
- prefixByInstrument.set(inst.id, inst.idPrefix);
465
- }
466
- }
467
- }
468
- for (const [name, decl] of templates) {
469
- const prefix =
470
- stringSlot(decl.body, "idPrefix", "id_prefix") ?? prefixFor(name);
471
- prefixByInstrument.set(name, prefix);
472
- }
473
512
  for (const candidate of allocated) {
474
513
  const prefix =
475
514
  stringSlot(candidate.body, "idPrefix", "id_prefix") ??
476
515
  prefixFor(candidate.name.name);
477
516
  prefixByInstrument.set(candidate.name.name, prefix);
478
517
  }
479
- const resolvePrefix = (target: string): string | undefined =>
480
- prefixByInstrument.get(target);
481
518
 
482
519
  const instruments: TypedInstrument[] = [];
483
520
  const ids = new Set<string>();
@@ -501,7 +538,13 @@ export function checkGeneralProgram(
501
538
  );
502
539
  continue;
503
540
  }
504
- const expandedBody = expandComprehensions(candidate.body, diagnostics);
541
+ const expandedBody = expandComprehensions(
542
+ candidate.body,
543
+ diagnostics,
544
+ new Map(),
545
+ new Map(),
546
+ resolvePrefix,
547
+ );
505
548
  const checked = checkInstrument(
506
549
  candidate.name,
507
550
  expandedBody,
@@ -1323,12 +1366,19 @@ function constructedInstruments(
1323
1366
  rootName: IdentExpr,
1324
1367
  body: BlockExpr,
1325
1368
  diagnostics: GeneralDiagnostic[],
1369
+ resolvePrefix?: (target: string) => string | undefined,
1326
1370
  ): {
1327
1371
  readonly body: BlockExpr;
1328
1372
  readonly generatedPrefix: boolean;
1329
1373
  readonly name: IdentExpr;
1330
1374
  }[] {
1331
- const expanded = expandComprehensions(body, diagnostics);
1375
+ const expanded = expandComprehensions(
1376
+ body,
1377
+ diagnostics,
1378
+ new Map(),
1379
+ new Map(),
1380
+ resolvePrefix,
1381
+ );
1332
1382
  const construction = entry(expanded, "instruments");
1333
1383
  const root = {
1334
1384
  body: {
@@ -1498,7 +1548,10 @@ function checkConstants(
1498
1548
  }
1499
1549
  }
1500
1550
 
1501
- function resolveConstants(decls: readonly Decl[]): ReadonlyMap<string, Expr> {
1551
+ function resolveConstants(
1552
+ decls: readonly Decl[],
1553
+ resolvePrefix?: (target: string) => string | undefined,
1554
+ ): ReadonlyMap<string, Expr> {
1502
1555
  const constants = new Map(
1503
1556
  decls
1504
1557
  .filter((decl): decl is ConstDecl => decl.kind === "const")
@@ -1506,7 +1559,7 @@ function resolveConstants(decls: readonly Decl[]): ReadonlyMap<string, Expr> {
1506
1559
  );
1507
1560
  for (let pass = 0; pass < constants.size; pass += 1) {
1508
1561
  for (const [name, value] of constants) {
1509
- constants.set(name, substituteExpr(value, constants));
1562
+ constants.set(name, substituteExpr(value, constants, resolvePrefix));
1510
1563
  }
1511
1564
  }
1512
1565
  return constants;
@@ -1539,6 +1592,7 @@ function instantiate(
1539
1592
  aliases: ReadonlyMap<string, Expr>,
1540
1593
  constants: ReadonlyMap<string, Expr>,
1541
1594
  boundPorts: Map<string, PortDecl>,
1595
+ resolvePrefix?: (target: string) => string | undefined,
1542
1596
  ): BlockExpr | undefined {
1543
1597
  const loweredPortShapes = new Map<string, BlockExpr>();
1544
1598
  const portDependencies = new Map<string, readonly string[]>();
@@ -1647,6 +1701,7 @@ function instantiate(
1647
1701
  const expectedExpr = substituteExpr(
1648
1702
  optionalInner(parameter.type) ?? parameter.type,
1649
1703
  values,
1704
+ resolvePrefix,
1650
1705
  );
1651
1706
  const expected = typeOf(expectedExpr, aliases);
1652
1707
  const actual = typeOf(value, aliases);
@@ -1704,6 +1759,7 @@ function instantiate(
1704
1759
  type: substituteExpr(
1705
1760
  optionalInner(parameter.type) ?? parameter.type,
1706
1761
  values,
1762
+ resolvePrefix,
1707
1763
  ),
1708
1764
  });
1709
1765
  values.set(parameter.name.name, value);
@@ -1724,6 +1780,7 @@ function instantiate(
1724
1780
  aliases,
1725
1781
  loweredPortShapes,
1726
1782
  portDependencies,
1783
+ resolvePrefix,
1727
1784
  );
1728
1785
  }
1729
1786
  }
@@ -1733,6 +1790,8 @@ function instantiate(
1733
1790
  values,
1734
1791
  diagnostics,
1735
1792
  portDependencies,
1793
+ [],
1794
+ resolvePrefix,
1736
1795
  );
1737
1796
  const dependencyDiagnosticStart = diagnostics.length;
1738
1797
  for (const [name, parameter] of missingOptional) {
@@ -1753,6 +1812,7 @@ function instantiate(
1753
1812
  diagnostics,
1754
1813
  values,
1755
1814
  portDependencies,
1815
+ resolvePrefix,
1756
1816
  );
1757
1817
  const binding: Entry = {
1758
1818
  key: { kind: "ident", name: "template_binding", span: application.span },
@@ -2041,6 +2101,7 @@ function lowerPortShapeField(
2041
2101
  row: Entry,
2042
2102
  aliases: ReadonlyMap<string, Expr>,
2043
2103
  diagnostics: GeneralDiagnostic[],
2104
+ resolvePrefix?: (target: string) => string | undefined,
2044
2105
  ): Entry | undefined {
2045
2106
  const type = typeOf(row.value, aliases);
2046
2107
  if (
@@ -2079,7 +2140,7 @@ function lowerPortShapeField(
2079
2140
  extras.max_length = 180;
2080
2141
  extras.min_length = 1;
2081
2142
  if (type.target)
2082
- extras.pattern = `^${prefixFor(type.target)}_(sandbox|live)_[a-z0-9]{8,64}$`;
2143
+ extras.pattern = `^${resolvePrefix?.(type.target) ?? prefixFor(type.target)}_(sandbox|live)_[a-z0-9]{8,64}$`;
2083
2144
  } else if (type.kind === "account") {
2084
2145
  extras.pattern = ACCOUNT_PATTERN;
2085
2146
  }
@@ -2152,6 +2213,7 @@ function lowerPortShape(
2152
2213
  rawShape: Expr,
2153
2214
  aliases: ReadonlyMap<string, Expr>,
2154
2215
  diagnostics: GeneralDiagnostic[],
2216
+ resolvePrefix?: (target: string) => string | undefined,
2155
2217
  ): BlockExpr {
2156
2218
  if (rawShape.kind !== "block") {
2157
2219
  diagnostics.push({
@@ -2177,7 +2239,13 @@ function lowerPortShape(
2177
2239
  continue;
2178
2240
  }
2179
2241
  names.add(row.key.name);
2180
- const lowered = lowerPortShapeField(port, row, aliases, diagnostics);
2242
+ const lowered = lowerPortShapeField(
2243
+ port,
2244
+ row,
2245
+ aliases,
2246
+ diagnostics,
2247
+ resolvePrefix,
2248
+ );
2181
2249
  if (lowered) entries.push(lowered);
2182
2250
  }
2183
2251
  return {
@@ -2284,6 +2352,7 @@ function bindPortCompileValues(
2284
2352
  aliases: ReadonlyMap<string, Expr> = new Map(),
2285
2353
  loweredPortShapes: Map<string, BlockExpr> = new Map(),
2286
2354
  dependencies: Map<string, readonly string[]> = new Map(),
2355
+ resolvePrefix?: (target: string) => string | undefined,
2287
2356
  ): void {
2288
2357
  const port = ports.get(value.name.name);
2289
2358
  if (!port) {
@@ -2308,7 +2377,13 @@ function bindPortCompileValues(
2308
2377
  span: port.body.span,
2309
2378
  };
2310
2379
  const shapeDiagnostics: GeneralDiagnostic[] = [];
2311
- shape = lowerPortShape(port, rawShape, aliases, shapeDiagnostics);
2380
+ shape = lowerPortShape(
2381
+ port,
2382
+ rawShape,
2383
+ aliases,
2384
+ shapeDiagnostics,
2385
+ resolvePrefix,
2386
+ );
2312
2387
  for (const diagnostic of shapeDiagnostics) {
2313
2388
  if (
2314
2389
  !diagnostics.some(
@@ -2336,7 +2411,11 @@ function bindPortCompileValues(
2336
2411
  if (value.deadline) values.set(`${binding}_deadline`, value.deadline);
2337
2412
  }
2338
2413
 
2339
- function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2414
+ function substituteExpr(
2415
+ expr: Expr,
2416
+ values: ReadonlyMap<string, Expr>,
2417
+ resolvePrefix?: (target: string) => string | undefined,
2418
+ ): Expr {
2340
2419
  if (expr.kind === "ident") {
2341
2420
  return values.get(expr.name) ?? substituteName(expr, values);
2342
2421
  }
@@ -2375,7 +2454,11 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2375
2454
  ? {
2376
2455
  iteration: {
2377
2456
  ...entry.iteration,
2378
- bound: substituteExpr(entry.iteration.bound, values),
2457
+ bound: substituteExpr(
2458
+ entry.iteration.bound,
2459
+ values,
2460
+ resolvePrefix,
2461
+ ),
2379
2462
  },
2380
2463
  }
2381
2464
  : {}),
@@ -2394,7 +2477,7 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2394
2477
  ? nameFromValue(qualifier, value)
2395
2478
  : substituteName(qualifier, nestedValues);
2396
2479
  }),
2397
- value: substituteExpr(entry.value, nestedValues),
2480
+ value: substituteExpr(entry.value, nestedValues, resolvePrefix),
2398
2481
  };
2399
2482
  }),
2400
2483
  };
@@ -2402,13 +2485,15 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2402
2485
  if (expr.kind === "list") {
2403
2486
  return {
2404
2487
  ...expr,
2405
- items: expr.items.map((item) => substituteExpr(item, values)),
2488
+ items: expr.items.map((item) =>
2489
+ substituteExpr(item, values, resolvePrefix),
2490
+ ),
2406
2491
  };
2407
2492
  }
2408
2493
  if (expr.kind === "type_apply") {
2409
2494
  return {
2410
2495
  ...expr,
2411
- args: expr.args.map((arg) => substituteExpr(arg, values)),
2496
+ args: expr.args.map((arg) => substituteExpr(arg, values, resolvePrefix)),
2412
2497
  };
2413
2498
  }
2414
2499
  if (expr.kind === "call") {
@@ -2417,7 +2502,9 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2417
2502
  );
2418
2503
  const substituted = {
2419
2504
  ...expr,
2420
- args: expr.args.map((arg) => substituteCompileArgument(arg, values)),
2505
+ args: expr.args.map((arg) =>
2506
+ substituteCompileArgument(arg, values, resolvePrefix),
2507
+ ),
2421
2508
  };
2422
2509
  const structural = new Set([
2423
2510
  "at",
@@ -2426,24 +2513,31 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2426
2513
  "keys_except",
2427
2514
  "kind",
2428
2515
  "len",
2516
+ "prefix",
2429
2517
  "values",
2430
2518
  ]).has(expr.callee.name);
2431
2519
  return hasUnbound && !structural
2432
2520
  ? substituted
2433
- : (evaluateCompileTimeCall(substituted, !hasUnbound) ?? substituted);
2521
+ : (evaluateCompileTimeCall(substituted, !hasUnbound, resolvePrefix) ??
2522
+ substituted);
2434
2523
  }
2435
2524
  if (expr.kind === "apply") {
2436
2525
  return {
2437
2526
  ...expr,
2438
- args: expr.args.map((arg) => substituteExpr(arg, values)),
2439
- typeArgs: expr.typeArgs.map((arg) => substituteExpr(arg, values)),
2527
+ args: expr.args.map((arg) => substituteExpr(arg, values, resolvePrefix)),
2528
+ typeArgs: expr.typeArgs.map((arg) =>
2529
+ substituteExpr(arg, values, resolvePrefix),
2530
+ ),
2440
2531
  };
2441
2532
  }
2442
2533
  if (expr.kind === "binding") {
2443
- return { ...expr, type: substituteExpr(expr.type, values) };
2534
+ return { ...expr, type: substituteExpr(expr.type, values, resolvePrefix) };
2444
2535
  }
2445
2536
  if (expr.kind === "decided_amount") {
2446
- return { ...expr, body: substituteExpr(expr.body, values) as BlockExpr };
2537
+ return {
2538
+ ...expr,
2539
+ body: substituteExpr(expr.body, values, resolvePrefix) as BlockExpr,
2540
+ };
2447
2541
  }
2448
2542
  return expr;
2449
2543
  }
@@ -2451,8 +2545,9 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
2451
2545
  function substituteCompileArgument(
2452
2546
  expr: Expr,
2453
2547
  values: ReadonlyMap<string, Expr>,
2548
+ resolvePrefix?: (target: string) => string | undefined,
2454
2549
  ): Expr {
2455
- if (expr.kind !== "ident") return substituteExpr(expr, values);
2550
+ if (expr.kind !== "ident") return substituteExpr(expr, values, resolvePrefix);
2456
2551
  const value = values.get(expr.name);
2457
2552
  if (!value) return expr;
2458
2553
  if (value.kind === "ident" || value.kind === "port_ref") {
@@ -2618,6 +2713,7 @@ function selectCompileTimeRows(
2618
2713
  diagnostics: GeneralDiagnostic[],
2619
2714
  dependencies: ReadonlyMap<string, readonly string[]> = new Map(),
2620
2715
  controls: readonly string[] = [],
2716
+ resolvePrefix?: (target: string) => string | undefined,
2621
2717
  ): BlockExpr {
2622
2718
  const entries: Entry[] = [];
2623
2719
  const locals = new Map(values);
@@ -2630,13 +2726,16 @@ function selectCompileTimeRows(
2630
2726
  binding.name,
2631
2727
  expressionPorts(row.value, localDependencies),
2632
2728
  );
2633
- locals.set(binding.name, substituteExpr(row.value, locals));
2729
+ locals.set(
2730
+ binding.name,
2731
+ substituteExpr(row.value, locals, resolvePrefix),
2732
+ );
2634
2733
  }
2635
2734
  entries.push(row);
2636
2735
  continue;
2637
2736
  }
2638
2737
  if (row.key.name === "unsupported") {
2639
- const value = substituteExpr(row.value, locals);
2738
+ const value = substituteExpr(row.value, locals, resolvePrefix);
2640
2739
  if (value.kind !== "block") {
2641
2740
  diagnostics.push({
2642
2741
  code: "HSX1405",
@@ -2679,6 +2778,7 @@ function selectCompileTimeRows(
2679
2778
  diagnostics,
2680
2779
  localDependencies,
2681
2780
  controls,
2781
+ resolvePrefix,
2682
2782
  )
2683
2783
  : row.value;
2684
2784
  const conditionPorts =
@@ -2755,6 +2855,7 @@ function selectCompileTimeRows(
2755
2855
  ...(subject ? expressionPorts(subject, localDependencies) : []),
2756
2856
  ]),
2757
2857
  ],
2858
+ resolvePrefix,
2758
2859
  ).entries,
2759
2860
  );
2760
2861
  }
@@ -2775,6 +2876,7 @@ function compileTimeTruthy(value: Expr | undefined): boolean {
2775
2876
  function evaluateCompileTimeCall(
2776
2877
  expr: Extract<Expr, { readonly kind: "call" }>,
2777
2878
  resolveMissing = true,
2879
+ resolvePrefix?: (target: string) => string | undefined,
2778
2880
  ): Expr | undefined {
2779
2881
  const [first, second, third, fourth] = expr.args;
2780
2882
  const number = (value: Expr | undefined): number | undefined => {
@@ -2962,7 +3064,9 @@ function evaluateCompileTimeCall(
2962
3064
  case "words":
2963
3065
  return first ? text(nameText(first).replaceAll("_", " ")) : undefined;
2964
3066
  case "prefix":
2965
- return first ? text(prefixFor(nameText(first))) : undefined;
3067
+ return first
3068
+ ? text(resolvePrefix?.(nameText(first)) ?? prefixFor(nameText(first)))
3069
+ : undefined;
2966
3070
  case "owner":
2967
3071
  return first?.kind === "settlement_ref"
2968
3072
  ? text(first.owner.name)
@@ -3039,6 +3143,7 @@ function expandComprehensions(
3039
3143
  diagnostics: GeneralDiagnostic[],
3040
3144
  values: ReadonlyMap<string, Expr> = new Map(),
3041
3145
  dependencies: ReadonlyMap<string, readonly string[]> = new Map(),
3146
+ resolvePrefix?: (target: string) => string | undefined,
3042
3147
  ): BlockExpr {
3043
3148
  const budget = { used: 0 };
3044
3149
  const expand = (
@@ -3069,7 +3174,10 @@ function expandComprehensions(
3069
3174
  binding.name,
3070
3175
  expressionPorts(row.value, localDependencies),
3071
3176
  );
3072
- locals.set(binding.name, substituteExpr(row.value, locals));
3177
+ locals.set(
3178
+ binding.name,
3179
+ substituteExpr(row.value, locals, resolvePrefix),
3180
+ );
3073
3181
  continue;
3074
3182
  }
3075
3183
  if (
@@ -3108,7 +3216,7 @@ function expandComprehensions(
3108
3216
  ? nameFromValue(qualifier, value)
3109
3217
  : substituteName(qualifier, locals);
3110
3218
  }),
3111
- value: substituteExpr(row.value, locals),
3219
+ value: substituteExpr(row.value, locals, resolvePrefix),
3112
3220
  };
3113
3221
  entries.push({
3114
3222
  ...substituted,
@@ -3120,8 +3228,8 @@ function expandComprehensions(
3120
3228
  continue;
3121
3229
  }
3122
3230
  if (row.value.kind !== "block") continue;
3123
- const bound = substituteExpr(row.iteration.bound, locals);
3124
- const values = finiteIterationValues(bound);
3231
+ const bound = substituteExpr(row.iteration.bound, locals, resolvePrefix);
3232
+ const values = finiteIterationValues(bound, resolvePrefix);
3125
3233
  if (!values) {
3126
3234
  diagnostics.push({
3127
3235
  code: "HSX1403",
@@ -3154,6 +3262,7 @@ function expandComprehensions(
3154
3262
  diagnostics,
3155
3263
  localDependencies,
3156
3264
  row.conditionPorts,
3265
+ resolvePrefix,
3157
3266
  );
3158
3267
  entries.push(...expand(body, substitutions, localDependencies).entries);
3159
3268
  }
@@ -3163,10 +3272,15 @@ function expandComprehensions(
3163
3272
  return expand(block, values);
3164
3273
  }
3165
3274
 
3166
- function finiteIterationValues(bound: Expr): readonly Expr[] | undefined {
3275
+ function finiteIterationValues(
3276
+ bound: Expr,
3277
+ resolvePrefix?: (target: string) => string | undefined,
3278
+ ): readonly Expr[] | undefined {
3167
3279
  if (bound.kind === "call") {
3168
- const evaluated = evaluateCompileTimeCall(bound);
3169
- return evaluated ? finiteIterationValues(evaluated) : undefined;
3280
+ const evaluated = evaluateCompileTimeCall(bound, true, resolvePrefix);
3281
+ return evaluated
3282
+ ? finiteIterationValues(evaluated, resolvePrefix)
3283
+ : undefined;
3170
3284
  }
3171
3285
  if (bound.kind === "list") return bound.items;
3172
3286
  if (bound.kind !== "number" || bound.raw.includes(".")) return undefined;
@@ -3406,6 +3520,7 @@ function checkInstrument(
3406
3520
  endpointFields,
3407
3521
  holdDestinations,
3408
3522
  diagnostics,
3523
+ resolvePrefix,
3409
3524
  ),
3410
3525
  ];
3411
3526
  })
@@ -3771,6 +3886,8 @@ function lowerField(
3771
3886
  },
3772
3887
  new Map(),
3773
3888
  diagnostics,
3889
+ false,
3890
+ resolvePrefix,
3774
3891
  );
3775
3892
  }
3776
3893
  const type = typeOf(typeExpr, aliases);
@@ -3938,6 +4055,7 @@ function checkAction(
3938
4055
  endpointFields: ReadonlyMap<string, string>,
3939
4056
  holdDestinations: ReadonlyMap<string, string>,
3940
4057
  diagnostics: GeneralDiagnostic[],
4058
+ resolvePrefix?: (target: string) => string | undefined,
3941
4059
  ): TypedAction {
3942
4060
  const slots: Record<string, JsonValue> = {};
3943
4061
  let omitsPublicAction = false;
@@ -3999,6 +4117,7 @@ function checkAction(
3999
4117
  row.value,
4000
4118
  definition.target,
4001
4119
  diagnostics,
4120
+ resolvePrefix,
4002
4121
  );
4003
4122
  const moveOffset = Array.isArray(slots.moves) ? slots.moves.length : 0;
4004
4123
  const value =
@@ -4993,11 +5112,12 @@ function blockToObject(
4993
5112
  substitutions: ReadonlyMap<string, Expr>,
4994
5113
  diagnostics: GeneralDiagnostic[],
4995
5114
  literalKeys = false,
5115
+ resolvePrefix?: (target: string) => string | undefined,
4996
5116
  ): Record<string, JsonValue> {
4997
5117
  if (!block) return {};
4998
5118
  const result: Record<string, JsonValue> = {};
4999
5119
  for (const row of block.entries) {
5000
- const value = substituteExpr(row.value, substitutions);
5120
+ const value = substituteExpr(row.value, substitutions, resolvePrefix);
5001
5121
  if (twoArgumentMoneyCall(value)) {
5002
5122
  diagnostics.push({
5003
5123
  code: "HSX1104",
@@ -5009,7 +5129,7 @@ function blockToObject(
5009
5129
  continue;
5010
5130
  }
5011
5131
  result[literalKeys || row.key.quoted ? row.key.name : camel(row.key.name)] =
5012
- exprToJson(value, diagnostics, literalKeys);
5132
+ exprToJson(value, diagnostics, literalKeys, resolvePrefix);
5013
5133
  }
5014
5134
  return result;
5015
5135
  }
@@ -5018,6 +5138,7 @@ function exprToJsonForUdlSlot(
5018
5138
  expr: Expr,
5019
5139
  slot: string,
5020
5140
  diagnostics: GeneralDiagnostic[],
5141
+ resolvePrefix?: (target: string) => string | undefined,
5021
5142
  ): JsonValue {
5022
5143
  if (twoArgumentMoneyCall(expr)) {
5023
5144
  diagnostics.push({
@@ -5032,7 +5153,7 @@ function exprToJsonForUdlSlot(
5032
5153
  return Object.fromEntries(
5033
5154
  expr.entries.map((row) => [
5034
5155
  row.key.name,
5035
- exprToJson(row.value, diagnostics),
5156
+ exprToJson(row.value, diagnostics, false, resolvePrefix),
5036
5157
  ]),
5037
5158
  );
5038
5159
  }
@@ -5043,13 +5164,14 @@ function exprToJsonForUdlSlot(
5043
5164
  "literalKeys" in clause &&
5044
5165
  clause.literalKeys === true,
5045
5166
  );
5046
- return exprToJson(expr, diagnostics, literalKeys);
5167
+ return exprToJson(expr, diagnostics, literalKeys, resolvePrefix);
5047
5168
  }
5048
5169
 
5049
5170
  function exprToJson(
5050
5171
  expr: Expr,
5051
5172
  diagnostics: GeneralDiagnostic[],
5052
5173
  literalKeys = false,
5174
+ resolvePrefix?: (target: string) => string | undefined,
5053
5175
  ): JsonValue {
5054
5176
  switch (expr.kind) {
5055
5177
  case "boolean":
@@ -5096,18 +5218,30 @@ function exprToJson(
5096
5218
  return `${expr.owner.name}.${expr.member.name}`;
5097
5219
  case "list":
5098
5220
  return expr.items.map((item) =>
5099
- exprToJson(item, diagnostics, literalKeys),
5221
+ exprToJson(item, diagnostics, literalKeys, resolvePrefix),
5100
5222
  );
5101
5223
  case "block":
5102
- return blockToObject(expr, new Map(), diagnostics, literalKeys);
5224
+ return blockToObject(
5225
+ expr,
5226
+ new Map(),
5227
+ diagnostics,
5228
+ literalKeys,
5229
+ resolvePrefix,
5230
+ );
5103
5231
  case "call":
5104
- return `${expr.callee.name}(${expr.args.map((arg) => String(exprToJson(arg, diagnostics))).join(",")})`;
5232
+ return `${expr.callee.name}(${expr.args.map((arg) => String(exprToJson(arg, diagnostics, false, resolvePrefix))).join(",")})`;
5105
5233
  case "type_apply":
5106
- return `${expr.callee.name}<${expr.args.map((arg) => String(exprToJson(arg, diagnostics))).join(",")}>`;
5234
+ return `${expr.callee.name}<${expr.args.map((arg) => String(exprToJson(arg, diagnostics, false, resolvePrefix))).join(",")}>`;
5107
5235
  case "binding":
5108
- return exprToJson(expr.type, diagnostics);
5236
+ return exprToJson(expr.type, diagnostics, false, resolvePrefix);
5109
5237
  case "decided_amount":
5110
- return blockToObject(expr.body, new Map(), diagnostics);
5238
+ return blockToObject(
5239
+ expr.body,
5240
+ new Map(),
5241
+ diagnostics,
5242
+ false,
5243
+ resolvePrefix,
5244
+ );
5111
5245
  case "port_ref":
5112
5246
  return expr.name.name;
5113
5247
  case "apply":