@hyperscale0/hsx 5.4.2 → 5.5.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,i4SAAi4S,CAAC;IACj5S,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.5.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.5.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.5.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.7.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": "a238da0dbb6ed4e454852cf7f8dbdf1015520d18"
102
102
  }
package/src/compile.ts CHANGED
@@ -2323,6 +2323,125 @@ export function compile(
2323
2323
  const key = parts.has("key")
2324
2324
  ? String(data(parts.get("key")!))
2325
2325
  : `move${index + 1}`;
2326
+ const lowerEconomics = (
2327
+ economicsExpr: Expr | undefined,
2328
+ ): UdlAction["moves"][number]["economics"] => {
2329
+ if (economicsExpr) {
2330
+ const resolved = resolve(economicsExpr);
2331
+ if (
2332
+ resolved.kind === "name" &&
2333
+ resolved.value === "unclassified"
2334
+ )
2335
+ return undefined;
2336
+ const choices = entries(asBlock(resolve(economicsExpr)));
2337
+ if (choices.has("recipient")) {
2338
+ for (const term of choices.keys())
2339
+ if (
2340
+ !["recipient", "company", "participant"].includes(term)
2341
+ )
2342
+ fail(
2343
+ economicsExpr,
2344
+ `unknown economics choice ${term}`,
2345
+ "use recipient, company and participant",
2346
+ );
2347
+ const recipient = String(data(choices.get("recipient")!));
2348
+ if (
2349
+ !isParty(recipient) ||
2350
+ !choices.has("company") ||
2351
+ !choices.has("participant")
2352
+ )
2353
+ fail(
2354
+ economicsExpr,
2355
+ "economics choice needs a bound recipient and both branches",
2356
+ "declare recipient, company and participant",
2357
+ );
2358
+ const company =
2359
+ recipient === "operator" ||
2360
+ recipient === "programOperator" ||
2361
+ document.parties[recipient]?.role === "program_operator";
2362
+ return lowerEconomics(
2363
+ choices.get(company ? "company" : "participant"),
2364
+ );
2365
+ }
2366
+ }
2367
+ const economicsParts = economicsExpr
2368
+ ? entries(asBlock(resolve(economicsExpr)))
2369
+ : undefined;
2370
+ if (economicsParts) {
2371
+ for (const term of economicsParts.keys())
2372
+ if (
2373
+ !["purpose", "sourceParty", "reversalOf"].includes(term)
2374
+ )
2375
+ fail(
2376
+ economicsExpr!,
2377
+ `unknown economics term ${term}`,
2378
+ "use purpose, sourceParty or reversalOf",
2379
+ );
2380
+ }
2381
+ const purpose = economicsParts?.get("purpose");
2382
+ const sourceParty = economicsParts?.get("sourceParty");
2383
+ if (economicsParts && (!purpose || !sourceParty))
2384
+ fail(
2385
+ economicsExpr!,
2386
+ "economics needs purpose and sourceParty",
2387
+ "declare both economics fields",
2388
+ );
2389
+ const purposeName = purpose ? String(data(purpose)) : undefined;
2390
+ if (
2391
+ purposeName &&
2392
+ ![
2393
+ "earning",
2394
+ "principal",
2395
+ "participant_payout",
2396
+ "internal",
2397
+ "prepaid_credit",
2398
+ "pass_through",
2399
+ ].includes(purposeName)
2400
+ )
2401
+ fail(
2402
+ purpose!,
2403
+ `unknown economic purpose ${purposeName}`,
2404
+ "choose a declared economic purpose",
2405
+ );
2406
+ const sourcePartyName = sourceParty
2407
+ ? String(data(sourceParty))
2408
+ : undefined;
2409
+ if (sourcePartyName && !isParty(sourcePartyName))
2410
+ fail(
2411
+ sourceParty!,
2412
+ `unknown source party ${sourcePartyName}`,
2413
+ "bind an authored party",
2414
+ );
2415
+ return economicsParts
2416
+ ? {
2417
+ purpose: purposeName as NonNullable<
2418
+ UdlAction["moves"][number]["economics"]
2419
+ >["purpose"],
2420
+ sourceParty: sourcePartyName!,
2421
+ ...(economicsParts.has("reversalOf")
2422
+ ? {
2423
+ reversalOf: path(economicsParts.get("reversalOf")!),
2424
+ }
2425
+ : {}),
2426
+ }
2427
+ : undefined;
2428
+ };
2429
+ const economicsExpr = parts.get("economics");
2430
+ const feeEconomics =
2431
+ parts.has("fee") && economicsExpr
2432
+ ? entries(asBlock(resolve(economicsExpr)))
2433
+ : undefined;
2434
+ if (feeEconomics)
2435
+ for (const term of feeEconomics.keys())
2436
+ if (!["payee", "fee", "tax"].includes(term))
2437
+ fail(
2438
+ economicsExpr!,
2439
+ `unknown fee economics leg ${term}`,
2440
+ "declare payee, fee or tax economics separately",
2441
+ );
2442
+ const economics = lowerEconomics(
2443
+ feeEconomics ? feeEconomics.get("payee") : economicsExpr,
2444
+ );
2326
2445
  if (
2327
2446
  op === "internal_transfer.create" ||
2328
2447
  op === "internal_transfer.reserve"
@@ -2418,6 +2537,7 @@ export function compile(
2418
2537
  a.moves.push({
2419
2538
  key: `${key}_${index + 1}`,
2420
2539
  operation: "internal_transfer.create",
2540
+ ...(economics ? { economics } : {}),
2421
2541
  amount: piece,
2422
2542
  from: path(from),
2423
2543
  to: `party.${recipient.value}`,
@@ -2440,6 +2560,7 @@ export function compile(
2440
2560
  const transfer = {
2441
2561
  key,
2442
2562
  operation: op,
2563
+ ...(economics ? { economics } : {}),
2443
2564
  ...(parts.has("capture")
2444
2565
  ? { capture: String(data(parts.get("capture")!)) }
2445
2566
  : {}),
@@ -2585,6 +2706,9 @@ export function compile(
2585
2706
  });
2586
2707
  a.moves.push({
2587
2708
  key: key + "Fee",
2709
+ ...(feeEconomics?.has("fee")
2710
+ ? { economics: lowerEconomics(feeEconomics.get("fee")) }
2711
+ : {}),
2588
2712
  operation: "internal_transfer.create",
2589
2713
  amount: charge,
2590
2714
  from: transfer.from,
@@ -2593,6 +2717,9 @@ export function compile(
2593
2717
  if (vat)
2594
2718
  a.moves.push({
2595
2719
  key: key + "Tax",
2720
+ ...(feeEconomics?.has("tax")
2721
+ ? { economics: lowerEconomics(feeEconomics.get("tax")) }
2722
+ : {}),
2596
2723
  operation: "internal_transfer.create",
2597
2724
  amount: vat,
2598
2725
  from: transfer.from,
@@ -2606,6 +2733,7 @@ export function compile(
2606
2733
  a.moves.push({
2607
2734
  key,
2608
2735
  operation: op,
2736
+ ...(economics ? { economics } : {}),
2609
2737
  transfer: path(parts.get("transfer")!),
2610
2738
  ...(parts.has("capture")
2611
2739
  ? { capture: String(data(parts.get("capture")!)) }
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);