@hyperscale0/hsx 1.0.125 → 1.0.128
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/README.md +2 -2
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +62 -14
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.js +8 -8
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/headers.d.ts +1 -1
- package/dist/src/headers.d.ts.map +1 -1
- package/dist/src/headers.js +1 -0
- package/dist/src/headers.js.map +1 -1
- package/dist/src/std-bundle.d.ts.map +1 -1
- package/dist/src/std-bundle.js +7 -6
- package/dist/src/std-bundle.js.map +1 -1
- package/docs/README.md +74 -47
- package/examples/collections.hsx +1 -1
- package/examples/financing.hsx +1 -1
- package/examples/insurance.hsx +4 -4
- package/examples/lending.hsx +1 -1
- package/examples/library.hsx +1 -1
- package/examples/rental-deposit.hsx +1 -1
- package/examples/reporting.hsx +1 -1
- package/examples/serviced.hsx +1 -1
- package/package.json +4 -4
- package/src/compile.ts +80 -20
- package/src/examples-bundle.ts +8 -8
- package/src/headers.ts +1 -0
- package/src/std-bundle.ts +7 -6
- package/std/booking.hsx +75 -0
- package/std/escrow.hsx +24 -7
- package/std/financing.hsx +83 -26
- package/std/insurance.hsx +9 -5
- package/std/marketplace.hsx +3 -3
- package/std/money.hsx +3 -3
- package/std/wallet.hsx +17 -6
package/README.md
CHANGED
|
@@ -21,8 +21,8 @@ Start with the [first program and rental guide](docs/README.md). Save the
|
|
|
21
21
|
It holds 1,000 SAR, charges a one-time 50 SAR late fee, and returns the remainder.
|
|
22
22
|
The guide explains the fixed business binding and the host needed to execute it.
|
|
23
23
|
|
|
24
|
-
`npx hsx headers` prints every std instrument and its tunables from the
|
|
25
|
-
headers. The [samples](examples/) include both complete action paths and
|
|
24
|
+
`npx hsx headers --json` prints every std instrument and its tunables from the
|
|
25
|
+
bundled headers as JSON. The [samples](examples/) include both complete action paths and
|
|
26
26
|
compositions that still need child-record actions or adapter bindings. A compile
|
|
27
27
|
pass does not prove that a flow is publicly executable or funded.
|
|
28
28
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AAOA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAgBL,KAAK,WAAW,EAQjB,MAAM,kBAAkB,CAAC;AAU1B,OAAO,EAIL,KAAK,UAAU,EAKf,KAAK,IAAI,EACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACpC;AACD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AACD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAClE;AACD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,WAAW,CAAC;QACtB,YAAY,EAAE,eAAe,CAAC;QAC9B,SAAS,EAAE,qBAAqB,EAAE,CAAC;KACpC,CAAC;CACH;AAqTD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AAOA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAgBL,KAAK,WAAW,EAQjB,MAAM,kBAAkB,CAAC;AAU1B,OAAO,EAIL,KAAK,UAAU,EAKf,KAAK,IAAI,EACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACpC;AACD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AACD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAClE;AACD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,WAAW,CAAC;QACtB,YAAY,EAAE,eAAe,CAAC;QAC9B,SAAS,EAAE,qBAAqB,EAAE,CAAC;KACpC,CAAC;CACH;AAqTD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAqrHf"}
|
package/dist/src/compile.js
CHANGED
|
@@ -349,10 +349,6 @@ export function compile(source, options = {}) {
|
|
|
349
349
|
for (const decl of program.decls)
|
|
350
350
|
if (decl.kind === "instrument")
|
|
351
351
|
templates.set(decl.name, decl);
|
|
352
|
-
for (const decl of program.decls) {
|
|
353
|
-
if (decl.kind === "instrument" && decl.parameters.length)
|
|
354
|
-
fail(decl, "This compiler cannot instantiate a parameterized instrument declared inside a program.", "For this version, specialize the instrument with fixed bindings and remove its parameters. Custom reusable headers require a host-supplied library.");
|
|
355
|
-
}
|
|
356
352
|
const document = {
|
|
357
353
|
udl: UDL_FORMAT_VERSION,
|
|
358
354
|
version: 1,
|
|
@@ -600,7 +596,11 @@ export function compile(source, options = {}) {
|
|
|
600
596
|
const fallback = param.value.kind === "default" ? param.value.value : undefined;
|
|
601
597
|
const typeName = type.kind === "type" || type.kind === "call" ? type.name : text(type);
|
|
602
598
|
const partyParameter = attachmentInfo && typeName === "party";
|
|
599
|
+
const optional = type.kind === "type" && type.optional;
|
|
600
|
+
// An optional party stays unset unless the attachment binds it, so a
|
|
601
|
+
// program party that shares its name does not switch it on.
|
|
603
602
|
const byName = partyParameter &&
|
|
603
|
+
!optional &&
|
|
604
604
|
(subjectPartyRoles.includes(param.key) ||
|
|
605
605
|
document.parties[param.key])
|
|
606
606
|
? { kind: "name", value: param.key, span: origin }
|
|
@@ -612,7 +612,7 @@ export function compile(source, options = {}) {
|
|
|
612
612
|
source: declarationSources.get(decl) ?? "program",
|
|
613
613
|
});
|
|
614
614
|
if (!actual) {
|
|
615
|
-
if (
|
|
615
|
+
if (optional)
|
|
616
616
|
continue;
|
|
617
617
|
parameterErrors.push({
|
|
618
618
|
span: origin,
|
|
@@ -678,7 +678,9 @@ export function compile(source, options = {}) {
|
|
|
678
678
|
const suppliedValue = supplied.get(key);
|
|
679
679
|
const requirements = decl.body.entries
|
|
680
680
|
.filter((entry) => entry.key.startsWith("action "))
|
|
681
|
-
.flatMap((entry) =>
|
|
681
|
+
.flatMap((entry) =>
|
|
682
|
+
// Read subject rows directly: entries() refuses repeated when branches.
|
|
683
|
+
asBlock(asBlock(entry.value).entries.find((row) => row.key === "subject")?.value).entries);
|
|
682
684
|
const moneyFields = [
|
|
683
685
|
...new Set(requirements
|
|
684
686
|
.filter((entry) => {
|
|
@@ -1653,10 +1655,10 @@ export function compile(source, options = {}) {
|
|
|
1653
1655
|
};
|
|
1654
1656
|
}
|
|
1655
1657
|
}
|
|
1656
|
-
const
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1658
|
+
const from = slots.get("from");
|
|
1659
|
+
// A policy member may sit in a state list such as `[funded, dispute.refund_after]`.
|
|
1660
|
+
const fromBinding = (from?.kind === "list" ? from.items : [from]).find((item) => item?.kind === "name" && item.value.includes("."));
|
|
1661
|
+
if (fromBinding?.kind === "name" && slots.has("moves")) {
|
|
1660
1662
|
const [parameter, member] = fromBinding.value.split(".");
|
|
1661
1663
|
const suppliedPolicy = supplied.get(parameter);
|
|
1662
1664
|
if (suppliedPolicy?.kind === "block") {
|
|
@@ -1693,6 +1695,7 @@ export function compile(source, options = {}) {
|
|
|
1693
1695
|
...(actionSubject ? { subject: actionSubject } : {}),
|
|
1694
1696
|
};
|
|
1695
1697
|
authored.set(a, row.value);
|
|
1698
|
+
const actionFeeCalculations = [];
|
|
1696
1699
|
for (const binding of boundaryBindings)
|
|
1697
1700
|
if (!a.subject?.adapters.find((entry) => entry.binding === binding)
|
|
1698
1701
|
?.snapshot)
|
|
@@ -1920,12 +1923,21 @@ export function compile(source, options = {}) {
|
|
|
1920
1923
|
if (rate.kind !== "percent")
|
|
1921
1924
|
fail(rate, "fee rate needs a percentage", "write a percentage such as 1%");
|
|
1922
1925
|
const prefix = `${name}_${key}`;
|
|
1926
|
+
// Instrument calculations run at create, before any action
|
|
1927
|
+
// input exists, so a fee on an input amount runs with its action.
|
|
1928
|
+
const perAction = "field" in transfer.amount &&
|
|
1929
|
+
transfer.amount.field.startsWith("input.");
|
|
1923
1930
|
const derived = (suffix, label, calculation) => {
|
|
1924
1931
|
const target = prefix + "_" + suffix;
|
|
1925
1932
|
if (fields.some((f) => f.name === target))
|
|
1926
1933
|
fail(move, `generated fee field ${target} conflicts with a field`, "rename the authored field or move key");
|
|
1927
|
-
fields.push({
|
|
1928
|
-
|
|
1934
|
+
fields.push({
|
|
1935
|
+
name: target,
|
|
1936
|
+
type: "money",
|
|
1937
|
+
label,
|
|
1938
|
+
...(perAction ? { optional: true } : {}),
|
|
1939
|
+
});
|
|
1940
|
+
(perAction ? actionFeeCalculations : calculations).push({
|
|
1929
1941
|
...calculation,
|
|
1930
1942
|
target,
|
|
1931
1943
|
});
|
|
@@ -2026,6 +2038,8 @@ export function compile(source, options = {}) {
|
|
|
2026
2038
|
else
|
|
2027
2039
|
a[key] = data(expr);
|
|
2028
2040
|
}
|
|
2041
|
+
if (actionFeeCalculations.length)
|
|
2042
|
+
a.calculate = [...(a.calculate ?? []), ...actionFeeCalculations];
|
|
2029
2043
|
if (attachmentInfo && !attachmentInfo.child) {
|
|
2030
2044
|
if (attachmentInfo.exposed.has(name)) {
|
|
2031
2045
|
if (automatic(a.actor))
|
|
@@ -2377,7 +2391,9 @@ export function compile(source, options = {}) {
|
|
|
2377
2391
|
});
|
|
2378
2392
|
};
|
|
2379
2393
|
for (const decl of program.decls) {
|
|
2380
|
-
|
|
2394
|
+
// A parameterized program instrument is a template, like a header
|
|
2395
|
+
// instrument: each attachment instantiates it with its own bindings.
|
|
2396
|
+
if (decl.kind === "instrument" && !decl.parameters.length) {
|
|
2381
2397
|
addInstrument(decl, decl.name, emptyBlock, decl.span);
|
|
2382
2398
|
}
|
|
2383
2399
|
if (decl.kind === "assignment") {
|
|
@@ -2632,7 +2648,9 @@ export function compile(source, options = {}) {
|
|
|
2632
2648
|
const checkParty = (name, at, inst) => {
|
|
2633
2649
|
const names = parties(inst);
|
|
2634
2650
|
if (!names.includes(name))
|
|
2635
|
-
fail(at, `\`${name}\` is not a declared party.${didYouMean(name, names)}`,
|
|
2651
|
+
fail(at, `\`${name}\` is not a declared party.${didYouMean(name, names)}`, subjectPartyRoles.includes(name)
|
|
2652
|
+
? `Declare a party parameter such as \`holder: party\` on the instrument, use it here, and bind \`holder: ${name}\` where you attach it.`
|
|
2653
|
+
: `Declare it with \`party ${name}: business\`, or name a declared party (${names.join(", ")}).`);
|
|
2636
2654
|
};
|
|
2637
2655
|
const checkInput = (name, at, label, input) => {
|
|
2638
2656
|
const names = input.map((field) => field.name);
|
|
@@ -2685,9 +2703,39 @@ export function compile(source, options = {}) {
|
|
|
2685
2703
|
inst,
|
|
2686
2704
|
label: declarations.get(inst).name,
|
|
2687
2705
|
});
|
|
2706
|
+
// A family member drops an optional reference field its binding left
|
|
2707
|
+
// unset (a borrower-funded plan has no `funds`), so it cannot match and
|
|
2708
|
+
// leaves the selection. The header must still declare the field.
|
|
2709
|
+
const matchable = (selection) => {
|
|
2710
|
+
const members = [selection.instrument].flat();
|
|
2711
|
+
const header = selection.family
|
|
2712
|
+
? templates.get(`${selection.family.module}.${selection.family.exportPath}`)
|
|
2713
|
+
: undefined;
|
|
2714
|
+
if (!header ||
|
|
2715
|
+
!entries(asBlock(entries(header.body).get("fields"))).has(selection.reference))
|
|
2716
|
+
return members;
|
|
2717
|
+
return members.filter((id) => resolveField(document, byId.get(id), `self.${selection.reference}`));
|
|
2718
|
+
};
|
|
2719
|
+
// A count over no matchable member is zero.
|
|
2720
|
+
for (const inst of document.instruments)
|
|
2721
|
+
for (const action of Object.values(inst.actions))
|
|
2722
|
+
action.requires = action.requires.map((requirement) => requirement.kind === "aggregate" &&
|
|
2723
|
+
requirement.measure === "count" &&
|
|
2724
|
+
!matchable(requirement.selection).length
|
|
2725
|
+
? {
|
|
2726
|
+
kind: "compare",
|
|
2727
|
+
left: { literal: 0 },
|
|
2728
|
+
operator: requirement.operator,
|
|
2729
|
+
right: requirement.value,
|
|
2730
|
+
}
|
|
2731
|
+
: requirement);
|
|
2688
2732
|
const checkSelection = (selection, at, scope) => {
|
|
2689
2733
|
walk(selection.anchor, "anchor", site(selection, "anchor", at), scope);
|
|
2690
2734
|
walk(selection.where, "where", site(selection, "where", at), scope);
|
|
2735
|
+
const members = matchable(selection);
|
|
2736
|
+
if (members.length &&
|
|
2737
|
+
members.length < [selection.instrument].flat().length)
|
|
2738
|
+
selection.instrument = members;
|
|
2691
2739
|
for (const id of [selection.instrument].flat()) {
|
|
2692
2740
|
checkTarget(id, site(selection, "instrument", at));
|
|
2693
2741
|
const target = scopeOf(byId.get(id));
|