@hyperscale0/hsx 5.4.2 → 5.6.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.
@@ -1 +1 @@
1
- {"version":3,"file":"std-bundle.js","sourceRoot":"","sources":["../../src/std-bundle.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAAgC,IAAI,GAAG,CAAiB;IACpF,CAAC,WAAW,EAAE,2oKAA2oK,CAAC;IAC1pK,CAAC,iBAAiB,EAAE,+rMAA+rM,CAAC;IACptM,CAAC,YAAY,EAAE,6jGAA6jG,CAAC;IAC7kG,CAAC,eAAe,EAAE,0j1DAA0j1D,CAAC;IAC7k1D,CAAC,eAAe,EAAE,ouJAAouJ,CAAC;IACvvJ,CAAC,aAAa,EAAE,4hSAA4hS,CAAC;IAC7iS,CAAC,iBAAiB,EAAE,ilSAAilS,CAAC;IACtmS,CAAC,WAAW,EAAE,kjUAAkjU,CAAC;IACjkU,CAAC,eAAe,EAAE,649CAA649C,CAAC;IACh69C,CAAC,aAAa,EAAE,mtJAAmtJ,CAAC;IACpuJ,CAAC,YAAY,EAAE,ssPAAssP,CAAC;IACttP,CAAC,YAAY,EAAE,kxFAAkxF,CAAC;CACnyF,CAAC,CAAC"}
1
+ {"version":3,"file":"std-bundle.js","sourceRoot":"","sources":["../../src/std-bundle.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAAgC,IAAI,GAAG,CAAiB;IACpF,CAAC,WAAW,EAAE,2oKAA2oK,CAAC;IAC1pK,CAAC,iBAAiB,EAAE,2sNAA2sN,CAAC;IAChuN,CAAC,YAAY,EAAE,+qHAA+qH,CAAC;IAC/rH,CAAC,eAAe,EAAE,gq9DAAgq9D,CAAC;IACnr9D,CAAC,eAAe,EAAE,01KAA01K,CAAC;IAC72K,CAAC,aAAa,EAAE,qwTAAqwT,CAAC;IACtxT,CAAC,iBAAiB,EAAE,s+TAAs+T,CAAC;IAC3/T,CAAC,WAAW,EAAE,u1VAAu1V,CAAC;IACt2V,CAAC,eAAe,EAAE,649CAA649C,CAAC;IACh69C,CAAC,aAAa,EAAE,85JAA85J,CAAC;IAC/6J,CAAC,YAAY,EAAE,o7SAAo7S,CAAC;IACp8S,CAAC,YAAY,EAAE,2jGAA2jG,CAAC;CAC5kG,CAAC,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const HSX_VERSION = "5.4.2";
1
+ export declare const HSX_VERSION = "5.6.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,2 +1,2 @@
1
- export const HSX_VERSION = "5.4.2";
1
+ export const HSX_VERSION = "5.6.0";
2
2
  //# sourceMappingURL=version.js.map
package/docs/README.md CHANGED
@@ -246,6 +246,13 @@ maxAge 1d` requires a recent completed provider check.
246
246
  only to reservations. Fees settle with create moves, never reservations. Repeated clauses keep
247
247
  their declaration order. The JSON-like clause form remains accepted and lowers
248
248
  to the same [UDL clauses](https://github.com/hyperscale0/hyperscale-udl/blob/main/spec/README.md).
249
+ Use `economics { purpose: earning, sourceParty: merchant }` on a move to
250
+ classify posted money. Other purposes are `principal`, `participant_payout`,
251
+ `internal`, `prepaid_credit`, and `pass_through`. `reversalOf: self.originalTransfer` links a
252
+ reversal. The source party must own the source account, except when an earning
253
+ releases the retained payer’s money from an agreement-bound instrument account
254
+ to a company account outside the agreement. A reservation passes its economics
255
+ to its post. Fee shorthand accepts separate economics for each expanded leg.
249
256
  They cannot add kernel instructions. `at(list, position)` reads a dated list;
250
257
  `aggregate(selection, "amount")` sums selected money; `ratio(amount, weight, total)`
251
258
  floors a weighted share. `records` declares child types. `invoke` can create a child
@@ -433,3 +440,36 @@ after the member receives the pot; paying a contribution does not advance their
433
440
  payout seat. Late contributions remain eligible while the membership is
434
441
  `active` or `received` and the circle is `active`. These guards do not promise
435
442
  scheduler retries or permit a payout larger than the available cash.
443
+
444
+ ## Posted economics in the money flows
445
+
446
+ Held customer funding and its unspent return are `principal`. A release for the
447
+ company's sale is `earning`. A payment from company cash to another participant
448
+ is `participant_payout`. Claims, waivers, write-offs and own-account allocations
449
+ are `internal`; prepaid balance topups and returns are `prepaid_credit`. Tax is
450
+ `pass_through`, excluded from earnings and costs. Unpaid claims count as neither.
451
+ Refunds of paid profit, company charges and commission capture the original
452
+ transfer and use `reversalOf`, so refunds reduce the original purpose.
453
+
454
+ Fee shorthand accepts separate move economics: `economics { payee: { purpose:
455
+ earning, sourceParty: payer }, fee: { purpose: earning, sourceParty: payer },
456
+ tax: { purpose: pass_through, sourceParty: payer } }`. These describe authored
457
+ product fees. Automatic platform fees keep their commercial classification.
458
+
459
+ Any economics block can select by a resolved party: `economics { recipient:
460
+ recipient, company: { purpose: earning, sourceParty: payer }, participant:
461
+ { purpose: participant_payout, sourceParty: programOperator } }`. The compiler
462
+ chooses `company` for `operator`, `programOperator`, or a declared party with the
463
+ `program_operator` role; it chooses `participant` for other bound parties.
464
+ Both branches must be supplied. A branch may be `unclassified` when no purpose
465
+ is established. Outside financing-profit and recovery recipients refund directly
466
+ to the customer, not the company that funded the payout; those refund branches
467
+ remain unclassified because they cannot reverse that company payout. Std
468
+ recovery charges use this rule to avoid counting company receipts as participant
469
+ costs. Direct customer payments to
470
+ outside recipients are pass-through; payments from held company cash are costs.
471
+
472
+ Generic transfers and mixed principal/profit distributions remain unclassified
473
+ when their structure does not establish a purpose. Adapter-owned premium and
474
+ claim accounts and credit-line advances also lack an authored source party.
475
+ Their reports remain incomplete rather than guessing a company earning or cost.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperscale0/hsx",
3
- "version": "5.4.2",
3
+ "version": "5.6.0",
4
4
  "description": "The strictly typed HSX language: compile total financial programs into canonical UDL.",
5
5
  "keywords": [
6
6
  "hsx",
@@ -85,8 +85,8 @@
85
85
  "examples:bundle": "bun scripts/bundle-examples.ts"
86
86
  },
87
87
  "dependencies": {
88
- "@hyperscale0/adl": "1.0.0-beta.8",
89
- "@hyperscale0/udl": "4.6.0",
88
+ "@hyperscale0/adl": "1.0.0-beta.10",
89
+ "@hyperscale0/udl": "4.8.0",
90
90
  "fast-sha256": "1.3.0"
91
91
  },
92
92
  "devDependencies": {
@@ -98,5 +98,5 @@
98
98
  "Amir Ayub",
99
99
  "Sara AlBakaawi"
100
100
  ],
101
- "gitHead": "bb10d3ee33343ddd5a4328401f25177cd46be9f6"
101
+ "gitHead": "be567dd18eb45ea3e02adc1f33b8491aa05df135"
102
102
  }
package/src/compile.ts CHANGED
@@ -56,6 +56,7 @@ export interface CompileDiagnostic extends Diagnostic {
56
56
  export interface CompileOriginMapEntry {
57
57
  path: string;
58
58
  span: Span & { line: number; column: number };
59
+ standardBlock?: { key: string; recordPath: string; headerDigest: string };
59
60
  }
60
61
  export interface AdapterBindingTarget {
61
62
  adapter: ProviderAdapter;
@@ -428,6 +429,10 @@ export function compile(
428
429
  );
429
430
  const templates = new Map<string, InstrumentDecl>();
430
431
  const declarationSources = new Map<InstrumentDecl, string>();
432
+ const standardOrigins = new Map<
433
+ InstrumentDecl,
434
+ NonNullable<CompileOriginMapEntry["standardBlock"]>
435
+ >();
431
436
  const requirementOrigins = new Map<
432
437
  UdlSubjectRequirement,
433
438
  { source: string; span: Span; message: string }
@@ -447,6 +452,13 @@ export function compile(
447
452
  if (!content)
448
453
  fail(use, `unknown header ${use.name}`, "choose a published header");
449
454
  sources.set(use.name, content);
455
+ const bundled = bundledStandardLibrary.source(use.name);
456
+ const headerDigest =
457
+ bundled === content
458
+ ? Array.from(sha256(new TextEncoder().encode(content)), (byte) =>
459
+ byte.toString(16).padStart(2, "0"),
460
+ ).join("")
461
+ : undefined;
450
462
  let header;
451
463
  try {
452
464
  header = parseHeader(content, use.name);
@@ -475,6 +487,14 @@ export function compile(
475
487
  );
476
488
  templates.set(prefix, parentDecl);
477
489
  declarationSources.set(parentDecl, use.name);
490
+ if (headerDigest) {
491
+ const [, root, ...record] = prefix.split(".");
492
+ standardOrigins.set(parentDecl, {
493
+ key: `${use.name}.${root}`,
494
+ recordPath: record.join("."),
495
+ headerDigest,
496
+ });
497
+ }
478
498
  const recs = entries(asBlock(entries(parentDecl.body).get("records")));
479
499
  for (const [recName, recBlock] of recs) {
480
500
  const recDecl: InstrumentDecl = {
@@ -663,6 +683,7 @@ export function compile(
663
683
  exportPath: string;
664
684
  revision?: number | undefined;
665
685
  },
686
+ standardOrigin?: NonNullable<CompileOriginMapEntry["standardBlock"]>,
666
687
  ) => {
667
688
  const resolveFamilyInstruments = (
668
689
  family: UdlFamily,
@@ -2323,6 +2344,125 @@ export function compile(
2323
2344
  const key = parts.has("key")
2324
2345
  ? String(data(parts.get("key")!))
2325
2346
  : `move${index + 1}`;
2347
+ const lowerEconomics = (
2348
+ economicsExpr: Expr | undefined,
2349
+ ): UdlAction["moves"][number]["economics"] => {
2350
+ if (economicsExpr) {
2351
+ const resolved = resolve(economicsExpr);
2352
+ if (
2353
+ resolved.kind === "name" &&
2354
+ resolved.value === "unclassified"
2355
+ )
2356
+ return undefined;
2357
+ const choices = entries(asBlock(resolve(economicsExpr)));
2358
+ if (choices.has("recipient")) {
2359
+ for (const term of choices.keys())
2360
+ if (
2361
+ !["recipient", "company", "participant"].includes(term)
2362
+ )
2363
+ fail(
2364
+ economicsExpr,
2365
+ `unknown economics choice ${term}`,
2366
+ "use recipient, company and participant",
2367
+ );
2368
+ const recipient = String(data(choices.get("recipient")!));
2369
+ if (
2370
+ !isParty(recipient) ||
2371
+ !choices.has("company") ||
2372
+ !choices.has("participant")
2373
+ )
2374
+ fail(
2375
+ economicsExpr,
2376
+ "economics choice needs a bound recipient and both branches",
2377
+ "declare recipient, company and participant",
2378
+ );
2379
+ const company =
2380
+ recipient === "operator" ||
2381
+ recipient === "programOperator" ||
2382
+ document.parties[recipient]?.role === "program_operator";
2383
+ return lowerEconomics(
2384
+ choices.get(company ? "company" : "participant"),
2385
+ );
2386
+ }
2387
+ }
2388
+ const economicsParts = economicsExpr
2389
+ ? entries(asBlock(resolve(economicsExpr)))
2390
+ : undefined;
2391
+ if (economicsParts) {
2392
+ for (const term of economicsParts.keys())
2393
+ if (
2394
+ !["purpose", "sourceParty", "reversalOf"].includes(term)
2395
+ )
2396
+ fail(
2397
+ economicsExpr!,
2398
+ `unknown economics term ${term}`,
2399
+ "use purpose, sourceParty or reversalOf",
2400
+ );
2401
+ }
2402
+ const purpose = economicsParts?.get("purpose");
2403
+ const sourceParty = economicsParts?.get("sourceParty");
2404
+ if (economicsParts && (!purpose || !sourceParty))
2405
+ fail(
2406
+ economicsExpr!,
2407
+ "economics needs purpose and sourceParty",
2408
+ "declare both economics fields",
2409
+ );
2410
+ const purposeName = purpose ? String(data(purpose)) : undefined;
2411
+ if (
2412
+ purposeName &&
2413
+ ![
2414
+ "earning",
2415
+ "principal",
2416
+ "participant_payout",
2417
+ "internal",
2418
+ "prepaid_credit",
2419
+ "pass_through",
2420
+ ].includes(purposeName)
2421
+ )
2422
+ fail(
2423
+ purpose!,
2424
+ `unknown economic purpose ${purposeName}`,
2425
+ "choose a declared economic purpose",
2426
+ );
2427
+ const sourcePartyName = sourceParty
2428
+ ? String(data(sourceParty))
2429
+ : undefined;
2430
+ if (sourcePartyName && !isParty(sourcePartyName))
2431
+ fail(
2432
+ sourceParty!,
2433
+ `unknown source party ${sourcePartyName}`,
2434
+ "bind an authored party",
2435
+ );
2436
+ return economicsParts
2437
+ ? {
2438
+ purpose: purposeName as NonNullable<
2439
+ UdlAction["moves"][number]["economics"]
2440
+ >["purpose"],
2441
+ sourceParty: sourcePartyName!,
2442
+ ...(economicsParts.has("reversalOf")
2443
+ ? {
2444
+ reversalOf: path(economicsParts.get("reversalOf")!),
2445
+ }
2446
+ : {}),
2447
+ }
2448
+ : undefined;
2449
+ };
2450
+ const economicsExpr = parts.get("economics");
2451
+ const feeEconomics =
2452
+ parts.has("fee") && economicsExpr
2453
+ ? entries(asBlock(resolve(economicsExpr)))
2454
+ : undefined;
2455
+ if (feeEconomics)
2456
+ for (const term of feeEconomics.keys())
2457
+ if (!["payee", "fee", "tax"].includes(term))
2458
+ fail(
2459
+ economicsExpr!,
2460
+ `unknown fee economics leg ${term}`,
2461
+ "declare payee, fee or tax economics separately",
2462
+ );
2463
+ const economics = lowerEconomics(
2464
+ feeEconomics ? feeEconomics.get("payee") : economicsExpr,
2465
+ );
2326
2466
  if (
2327
2467
  op === "internal_transfer.create" ||
2328
2468
  op === "internal_transfer.reserve"
@@ -2418,6 +2558,7 @@ export function compile(
2418
2558
  a.moves.push({
2419
2559
  key: `${key}_${index + 1}`,
2420
2560
  operation: "internal_transfer.create",
2561
+ ...(economics ? { economics } : {}),
2421
2562
  amount: piece,
2422
2563
  from: path(from),
2423
2564
  to: `party.${recipient.value}`,
@@ -2440,6 +2581,7 @@ export function compile(
2440
2581
  const transfer = {
2441
2582
  key,
2442
2583
  operation: op,
2584
+ ...(economics ? { economics } : {}),
2443
2585
  ...(parts.has("capture")
2444
2586
  ? { capture: String(data(parts.get("capture")!)) }
2445
2587
  : {}),
@@ -2585,6 +2727,9 @@ export function compile(
2585
2727
  });
2586
2728
  a.moves.push({
2587
2729
  key: key + "Fee",
2730
+ ...(feeEconomics?.has("fee")
2731
+ ? { economics: lowerEconomics(feeEconomics.get("fee")) }
2732
+ : {}),
2588
2733
  operation: "internal_transfer.create",
2589
2734
  amount: charge,
2590
2735
  from: transfer.from,
@@ -2593,6 +2738,9 @@ export function compile(
2593
2738
  if (vat)
2594
2739
  a.moves.push({
2595
2740
  key: key + "Tax",
2741
+ ...(feeEconomics?.has("tax")
2742
+ ? { economics: lowerEconomics(feeEconomics.get("tax")) }
2743
+ : {}),
2596
2744
  operation: "internal_transfer.create",
2597
2745
  amount: vat,
2598
2746
  from: transfer.from,
@@ -2606,6 +2754,7 @@ export function compile(
2606
2754
  a.moves.push({
2607
2755
  key,
2608
2756
  operation: op,
2757
+ ...(economics ? { economics } : {}),
2609
2758
  transfer: path(parts.get("transfer")!),
2610
2759
  ...(parts.has("capture")
2611
2760
  ? { capture: String(data(parts.get("capture")!)) }
@@ -2719,6 +2868,7 @@ export function compile(
2719
2868
  origins.push({
2720
2869
  path: `$.instruments[${document.instruments.length}]`,
2721
2870
  span: { ...origin, ...lineColAt(source, origin.start) },
2871
+ ...(standardOrigin ? { standardBlock: standardOrigin } : {}),
2722
2872
  });
2723
2873
  if (familyDeclaration && familyDeclaration.revision !== undefined) {
2724
2874
  inst.family = {
@@ -2776,6 +2926,14 @@ export function compile(
2776
2926
  enums,
2777
2927
  childAttachment,
2778
2928
  childFamily,
2929
+ standardOrigin
2930
+ ? {
2931
+ ...standardOrigin,
2932
+ recordPath: [standardOrigin.recordPath, key]
2933
+ .filter(Boolean)
2934
+ .join("."),
2935
+ }
2936
+ : undefined,
2779
2937
  );
2780
2938
  }
2781
2939
  };
@@ -2907,6 +3065,7 @@ export function compile(
2907
3065
  attachments,
2908
3066
  },
2909
3067
  templateFamily ? { ...templateFamily } : undefined,
3068
+ standardOrigins.get(template),
2910
3069
  );
2911
3070
  } catch (error) {
2912
3071
  if (!(error instanceof CompileFailure)) throw error;
@@ -3052,6 +3211,7 @@ export function compile(
3052
3211
  new Map(),
3053
3212
  undefined,
3054
3213
  templateFamily ? { ...templateFamily } : undefined,
3214
+ standardOrigins.get(template),
3055
3215
  );
3056
3216
  }
3057
3217
  if (decl.kind === "object") {
package/src/parse.ts CHANGED
@@ -450,6 +450,7 @@ class Parser {
450
450
  }
451
451
  for (const key of ["fee", "capture", "key"])
452
452
  if (this.eat(key)) values[key] = this.atom();
453
+ if (this.eat("economics")) values.economics = this.block();
453
454
  if (this.eat("boundary"))
454
455
  values.boundary = this.record({ adapter: this.atom() });
455
456
  return this.record(values);