@hyperscale0/hsx 5.3.0 → 5.4.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.
- package/CHANGELOG.md +22 -0
- package/README.md +2 -2
- package/bin/hsx.ts +0 -2
- package/dist/bin/hsx.js +0 -2
- package/dist/bin/hsx.js.map +1 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +9 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +22 -9
- package/dist/src/compile.js.map +1 -1
- package/dist/src/header-source.d.ts.map +1 -1
- package/dist/src/header-source.js +5 -0
- package/dist/src/header-source.js.map +1 -1
- package/dist/src/std-bundle.js +12 -12
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/version.d.ts +1 -2
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -2
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +49 -4
- package/docs/headers.md +1 -1
- package/package.json +3 -3
- package/src/cli.ts +12 -6
- package/src/compile.ts +32 -9
- package/src/header-source.ts +10 -0
- package/src/std-bundle.ts +12 -12
- package/src/version.ts +1 -2
- package/std/cards.hsx +12 -0
- package/std/collections.hsx +11 -0
- package/std/escrow.hsx +16 -2
- package/std/financing.hsx +34 -3
- package/std/insurance.hsx +20 -5
- package/std/lending.hsx +14 -0
- package/std/marketplace.hsx +14 -0
- package/std/money.hsx +13 -0
- package/std/reporting.hsx +10 -0
- package/std/savings.hsx +28 -11
- package/std/travel.hsx +22 -4
- package/std/wallet.hsx +9 -0
|
@@ -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,
|
|
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,k6JAAk6J,CAAC;IACj7J,CAAC,iBAAiB,EAAE,s8LAAs8L,CAAC;IAC39L,CAAC,YAAY,EAAE,00FAA00F,CAAC;IAC11F,CAAC,eAAe,EAAE,2z0DAA2z0D,CAAC;IAC900D,CAAC,eAAe,EAAE,0+IAA0+I,CAAC;IAC7/I,CAAC,aAAa,EAAE,oyRAAoyR,CAAC;IACrzR,CAAC,iBAAiB,EAAE,y1RAAy1R,CAAC;IAC92R,CAAC,WAAW,EAAE,0yTAA0yT,CAAC;IACzzT,CAAC,eAAe,EAAE,uq9CAAuq9C,CAAC;IAC1r9C,CAAC,aAAa,EAAE,o+IAAo+I,CAAC;IACr/I,CAAC,YAAY,EAAE,s6OAAs6O,CAAC;IACt7O,CAAC,YAAY,EAAE,qjFAAqjF,CAAC;CACtkF,CAAC,CAAC"}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAC
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAC"}
|
package/dist/src/version.js
CHANGED
package/dist/src/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC"}
|
package/docs/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# HSX
|
|
1
|
+
# HSX
|
|
2
2
|
|
|
3
3
|
A program declares business objects and financial instruments. Objects contain
|
|
4
4
|
optional metadata. Attachments expose named actions against an object.
|
|
@@ -49,6 +49,9 @@ library's declared reference type. References can name a child, such as
|
|
|
49
49
|
objects of type T. `on: [plan_3, plan_6]` creates one late-charge or collections
|
|
50
50
|
policy. Its references can point into either plan; selections can span both.
|
|
51
51
|
A plain `ref<T>` accepts one object only.
|
|
52
|
+
Tunable constructors are `enum(choices)` and `integer(minimum, maximum)`.
|
|
53
|
+
Other constructors, including `party(business)` and bounded `money(...)`, are
|
|
54
|
+
rejected at the header declaration. Their arguments do not restrict bindings.
|
|
52
55
|
Imports are `use header`; there are no file imports, macros or executable strings.
|
|
53
56
|
|
|
54
57
|
Amounts use `750 SAR`; percentages use `2.5%`; durations use `48h`, `3d`, or `1w`.
|
|
@@ -78,8 +81,9 @@ same book and key within the Product. Providers remain outside parties.
|
|
|
78
81
|
A missing binding refuses before money moves. Reference paths such as
|
|
79
82
|
`self.cover.insurer` use the referenced agreement's account.
|
|
80
83
|
|
|
81
|
-
Insurance collection
|
|
82
|
-
|
|
84
|
+
Insurance collection pays the premium net of commission to the insurer account
|
|
85
|
+
and pays commission separately to the bound broker, which defaults to the operator.
|
|
86
|
+
Refunds return those two portions directly from insurer and broker to holder.
|
|
83
87
|
Claims reserve from the cover's insurer account. These moves record ledger money;
|
|
84
88
|
external confirmation still needs the boundary protocol.
|
|
85
89
|
`account(buyer, claim, contra, "debt")` declares the borrower's claim contra account.
|
|
@@ -271,7 +275,7 @@ adapters are bound, participants have funds, or the flow can finish.
|
|
|
271
275
|
| `lending.distribution` | Cash distribution needs an eligible recorded settlement, `prepare_cash`, one share record per funded commitment, then `distribute_cash`. Attaching it moves nothing. |
|
|
272
276
|
| `insurance.cover.slice` | `collect` credits premium net of commission to the insurer account. The tenant retains commission. `refund` returns both portions; claims reserve insurer funds. External settlement uses the boundary protocol. |
|
|
273
277
|
| `travel.booking` | From `deposit_paid` or `paid`, early cancellation returns the full held balance, middle returns held balance minus deposit, and late has no buyer refund transfer. State, time and balance requirements still apply. A deposit-only early cancellation refunds the deposit; a deposit-only middle cancellation refunds zero. |
|
|
274
|
-
| `financing.installments`, `savings.circle` | Supply explicit date lists when creating agreements. A term count does not generate a monthly calendar. Savings supports at most 60 distinct member
|
|
278
|
+
| `financing.installments`, `savings.circle` | Supply explicit date lists when creating agreements. A term count does not generate a monthly calendar. Savings supports at most 60 distinct member seats. |
|
|
275
279
|
| `escrow.hold` | `fund` takes the whole price. Financing into pending escrow collects the remaining down payment and adds capital principal at disbursement; `fund` is not a down-payment checkout. |
|
|
276
280
|
| `escrow.hold` | Acceptance timeout enters `disputed` without paying the seller. Delivery and return verification belong to `payee`; rebinding it also changes who receives accepted funds. |
|
|
277
281
|
| `financing.late_charge` | `fine` is a fixed money amount, not a percentage of overdue principal. |
|
|
@@ -282,3 +286,44 @@ commitment creation, and cash prepare/distribute actions. Its repayment and shar
|
|
|
282
286
|
child actions still lack a public execution path. It is a composition example,
|
|
283
287
|
not a complete public repayment flow. Callers still need dates, agreement inputs,
|
|
284
288
|
funded wallets, eligible settlements and the distribution's share records.
|
|
289
|
+
|
|
290
|
+
## Checkout and reversals
|
|
291
|
+
|
|
292
|
+
For financed checkout, expose `installments.collect_down_payment` after `create`
|
|
293
|
+
and `sign`. It credits the linked pending escrow with the missing down payment.
|
|
294
|
+
It does not disburse principal or activate the plan. The later `disburse` action
|
|
295
|
+
uses that credit before collecting any remainder. Borrower-directed financing
|
|
296
|
+
has no checkout collection action. The plan and escrow must agree on payer and
|
|
297
|
+
price.
|
|
298
|
+
|
|
299
|
+
Expose escrow `cancel` to return money credited before full funding. Plan `void`
|
|
300
|
+
cancels the signed financing agreement but does not cancel its escrow. After
|
|
301
|
+
funding and delivery, the default return path needs three separate aliases for
|
|
302
|
+
`dispute`, `verify_return` and `refund`. The payee supplies return evidence and
|
|
303
|
+
returns the full price to the payer. `accept` pays the payee instead.
|
|
304
|
+
Marketplace order fulfillment alone does not execute any of these money actions.
|
|
305
|
+
|
|
306
|
+
Expose `late_charge.create` to create an assessment with matching plan, slice
|
|
307
|
+
and borrower. Only that existing assessment can reach clock-owned `assess` after
|
|
308
|
+
grace. A payment can collect part of a fine without settling the rest. A recovery
|
|
309
|
+
cost assessment is a separate record.
|
|
310
|
+
|
|
311
|
+
## Separate payments and states
|
|
312
|
+
|
|
313
|
+
Bind `insurance.cover.broker` to the agency that receives commission. Collection
|
|
314
|
+
pays the insurer's net premium and the broker's commission as two moves. Refunds
|
|
315
|
+
reverse those same portions. The default broker is the program operator.
|
|
316
|
+
`claim.deny` accepts only `submitted`; after approval, `pay` or expiry consumes
|
|
317
|
+
the reservation.
|
|
318
|
+
|
|
319
|
+
A booking cancellation refunds only its own held cash. Expose `cover.cancel`
|
|
320
|
+
separately for eligible premium refunds before the paid period starts. Active
|
|
321
|
+
and expired premium periods are excluded. A card transaction needs a captured
|
|
322
|
+
authorization and its own record before refund, but no authorization-expiry wait.
|
|
323
|
+
|
|
324
|
+
A savings membership becomes `received` after its pot payout. Its contribution
|
|
325
|
+
records become `paid` independently. Future contributions can remain pending
|
|
326
|
+
after the member receives the pot; paying a contribution does not advance their
|
|
327
|
+
payout seat. Late contributions remain eligible while the membership is
|
|
328
|
+
`active` or `received` and the circle is `active`. These guards do not promise
|
|
329
|
+
scheduler retries or permit a payout larger than the available cash.
|
package/docs/headers.md
CHANGED
|
@@ -29,7 +29,7 @@ Bind party parameters to owner, actor, operator or declared parties. Configure d
|
|
|
29
29
|
| lending.round | `borrower: party`, `plan: ref<financing.installments> = object(financing.installments)`, `operator: party = programOperator`, `minimum_ticket: money = 100 SAR`, `investor_cap: percent = 20%`, `commitments: ref<lending.commitment> = object(lending.commitment)` |
|
|
30
30
|
| lending.commitment | `round: ref<lending.round>`, `wallet: ref<wallet.balance>`, `investor: party = party(person)` |
|
|
31
31
|
| lending.distribution | `round: ref<lending.round>`, `receipt: ref`, `commitments: ref<lending.commitment> = object(lending.commitment)`, `mode: enum(cash, loss) = cash`, `residual_to: party = programOperator`, `operator: party = programOperator`, `fee: percent = 1%`, `tax: percent = 15%` |
|
|
32
|
-
| insurance.cover | `holder: party`, `adapter: text`, `commission: percent = 0%`, `covers: ref`, `premium: money = runtime` |
|
|
32
|
+
| insurance.cover | `holder: party`, `adapter: text`, `commission: percent = 0%`, `broker: party = programOperator`, `covers: ref`, `premium: money = runtime` |
|
|
33
33
|
| insurance.claim | `cover: ref<insurance.cover>`, `inspector: party` |
|
|
34
34
|
| collections.case | `on: ref<financing.installments>[]`, `agency: party`, `capital: party = programOperator`, `overdue: duration = 3d`, `fee: percent = 20%` |
|
|
35
35
|
| collections.contact | `case: ref<collections.case>[]`, `agency: party`, `max_contacts: integer = 10`, `window: duration = 30d`, `contact_from: integer(0, 23) = 8`, `contact_until: integer(1, 23) = 20`, `timezone: text = "Asia/Riyadh"`; contact_from less_than contact_until |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperscale0/hsx",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.0",
|
|
4
4
|
"description": "The strictly typed HSX language: compile total financial programs into canonical UDL.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hsx",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@hyperscale0/adl": "1.0.0-beta.8",
|
|
89
|
-
"@hyperscale0/udl": "4.
|
|
89
|
+
"@hyperscale0/udl": "4.5.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": "
|
|
101
|
+
"gitHead": "4141f2296d9427b30d04933c735be4af1ba88dec"
|
|
102
102
|
}
|
package/src/cli.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { HSX_VERSION } from "./version.ts";
|
|
|
5
5
|
import { compile } from "./compile.ts";
|
|
6
6
|
import { format } from "./format.ts";
|
|
7
7
|
import { serializeUdl } from "@hyperscale0/udl";
|
|
8
|
+
const usage =
|
|
9
|
+
"hsx build|format|cost <file> [--out path]\nhsx check <file>\nhsx headers --json";
|
|
8
10
|
const standardLibrary = {
|
|
9
11
|
source: (header: string) => {
|
|
10
12
|
for (const base of ["../std/", "../../std/"]) {
|
|
@@ -20,8 +22,6 @@ export interface Io {
|
|
|
20
22
|
err(line: string): void;
|
|
21
23
|
readFile(path: string): Promise<string>;
|
|
22
24
|
writeFile(path: string, text: string): Promise<void>;
|
|
23
|
-
stdin?: NodeJS.ReadableStream;
|
|
24
|
-
stdout?: NodeJS.WritableStream;
|
|
25
25
|
}
|
|
26
26
|
export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
27
27
|
const [command, file, ...options] = argv;
|
|
@@ -34,7 +34,7 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
|
34
34
|
return 0;
|
|
35
35
|
}
|
|
36
36
|
if (["--help", "help", "-h"].includes(command ?? "")) {
|
|
37
|
-
io.out(
|
|
37
|
+
io.out(usage);
|
|
38
38
|
return 0;
|
|
39
39
|
}
|
|
40
40
|
if (command === "--version") {
|
|
@@ -46,14 +46,20 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
|
46
46
|
!file ||
|
|
47
47
|
!["build", "check", "format", "cost"].includes(command)
|
|
48
48
|
) {
|
|
49
|
-
io.err(
|
|
49
|
+
io.err(`usage:\n${usage}`);
|
|
50
50
|
return 2;
|
|
51
51
|
}
|
|
52
52
|
let output: string | undefined;
|
|
53
53
|
for (let i = 0; i < options.length; i++) {
|
|
54
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
command !== "check" &&
|
|
56
|
+
options[i] === "--out" &&
|
|
57
|
+
options[i + 1] &&
|
|
58
|
+
!options[i + 1]!.startsWith("--") &&
|
|
59
|
+
output === undefined
|
|
60
|
+
)
|
|
55
61
|
output = options[++i];
|
|
56
|
-
else
|
|
62
|
+
else {
|
|
57
63
|
io.err(`unknown option ${options[i]}`);
|
|
58
64
|
return 2;
|
|
59
65
|
}
|
package/src/compile.ts
CHANGED
|
@@ -1099,11 +1099,16 @@ export function compile(
|
|
|
1099
1099
|
(binding.kind === "name" && binding.value === expr.value))
|
|
1100
1100
|
)
|
|
1101
1101
|
return expr;
|
|
1102
|
-
// A resolved party parameter
|
|
1102
|
+
// A resolved party parameter or an own parameter bound to a value takes
|
|
1103
|
+
// precedence over a same-named sibling attachment; `limits: limits`
|
|
1104
|
+
// binds by identity and still names the sibling.
|
|
1103
1105
|
if (attachmentInfo && !resolvedParties.has(expr.value)) {
|
|
1104
1106
|
const [local, ...tail] = expr.value.split(".");
|
|
1107
|
+
const own = environment.get(local!);
|
|
1108
|
+
const ownValue =
|
|
1109
|
+
own !== undefined && !(own.kind === "name" && own.value === local);
|
|
1105
1110
|
const target = `${attachmentInfo.subjectKindId}_${local}`;
|
|
1106
|
-
if (attachmentSubjects.has(target))
|
|
1111
|
+
if (!ownValue && attachmentSubjects.has(target))
|
|
1107
1112
|
return { ...expr, value: [target, ...tail].join("_") };
|
|
1108
1113
|
}
|
|
1109
1114
|
if (expr.value.startsWith("party.")) {
|
|
@@ -2629,7 +2634,7 @@ export function compile(
|
|
|
2629
2634
|
}
|
|
2630
2635
|
}
|
|
2631
2636
|
if (automatic(a.actor)) delete a.publicAction;
|
|
2632
|
-
const checkSubjectPaths = (obj: unknown,
|
|
2637
|
+
const checkSubjectPaths = (obj: unknown, key = "") => {
|
|
2633
2638
|
if (typeof obj === "string") {
|
|
2634
2639
|
if (
|
|
2635
2640
|
[
|
|
@@ -2649,7 +2654,7 @@ export function compile(
|
|
|
2649
2654
|
);
|
|
2650
2655
|
if (!req) {
|
|
2651
2656
|
failWithCode(
|
|
2652
|
-
|
|
2657
|
+
row,
|
|
2653
2658
|
"subject_field_unknown",
|
|
2654
2659
|
`subject.${subField} names no declared subject requirement in action ${name}`,
|
|
2655
2660
|
`declare ${subField} in subject { ... }`,
|
|
@@ -2657,15 +2662,15 @@ export function compile(
|
|
|
2657
2662
|
}
|
|
2658
2663
|
}
|
|
2659
2664
|
} else if (Array.isArray(obj)) {
|
|
2660
|
-
for (const item of obj) checkSubjectPaths(item,
|
|
2665
|
+
for (const item of obj) checkSubjectPaths(item, key);
|
|
2661
2666
|
} else if (obj !== null && typeof obj === "object") {
|
|
2662
2667
|
for (const [key, val] of Object.entries(obj))
|
|
2663
|
-
checkSubjectPaths(val,
|
|
2668
|
+
checkSubjectPaths(val, key);
|
|
2664
2669
|
}
|
|
2665
2670
|
};
|
|
2666
|
-
checkSubjectPaths(a.requires
|
|
2667
|
-
checkSubjectPaths(a.set
|
|
2668
|
-
checkSubjectPaths(a.invoke
|
|
2671
|
+
checkSubjectPaths(a.requires);
|
|
2672
|
+
checkSubjectPaths(a.set);
|
|
2673
|
+
checkSubjectPaths(a.invoke);
|
|
2669
2674
|
currentAction = undefined;
|
|
2670
2675
|
currentActionName = undefined;
|
|
2671
2676
|
for (const requirement of a.subject?.requirements ?? []) {
|
|
@@ -3394,6 +3399,24 @@ export function compile(
|
|
|
3394
3399
|
);
|
|
3395
3400
|
else action!.publicAction = decl.name!;
|
|
3396
3401
|
}
|
|
3402
|
+
for (const decl of program.decls) {
|
|
3403
|
+
if (decl.kind !== "expose") continue;
|
|
3404
|
+
const parts = decl.target.split(".");
|
|
3405
|
+
parts.pop();
|
|
3406
|
+
const instrument = document.instruments.find(
|
|
3407
|
+
(item) => item.id === parts.join("_"),
|
|
3408
|
+
)!;
|
|
3409
|
+
if (
|
|
3410
|
+
Object.values(instrument.actions).filter(
|
|
3411
|
+
(action) => action.publicAction === decl.name,
|
|
3412
|
+
).length > 1
|
|
3413
|
+
)
|
|
3414
|
+
fail(
|
|
3415
|
+
decl,
|
|
3416
|
+
`public action ${decl.name} is used more than once on ${parts.join(".")}`,
|
|
3417
|
+
"give each exposed action a distinct public name",
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3397
3420
|
const usedParties = new Set<string>();
|
|
3398
3421
|
const collectParties = (value: unknown): void => {
|
|
3399
3422
|
if (typeof value === "string") {
|
package/src/header-source.ts
CHANGED
|
@@ -38,6 +38,16 @@ export function parseHeader(source: string, name: string) {
|
|
|
38
38
|
"declare each parameter once",
|
|
39
39
|
);
|
|
40
40
|
parameters.add(parameter.key);
|
|
41
|
+
const type =
|
|
42
|
+
parameter.value.kind === "default"
|
|
43
|
+
? parameter.value.type
|
|
44
|
+
: parameter.value;
|
|
45
|
+
if (type.kind === "call" && !["enum", "integer"].includes(type.name))
|
|
46
|
+
fail(
|
|
47
|
+
type,
|
|
48
|
+
`unsupported tunable constructor ${type.name}`,
|
|
49
|
+
"Only enum(choices) and integer(minimum, maximum) are supported tunable constructors. This constructor's restrictions cannot be enforced.",
|
|
50
|
+
);
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
53
|
return program;
|