@hyperscale0/hsx 4.0.1 → 4.1.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 +4 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +31 -142
- package/dist/src/compile.js.map +1 -1
- package/dist/src/headers.d.ts +2 -2
- package/dist/src/headers.d.ts.map +1 -1
- package/dist/src/headers.js +1 -2
- package/dist/src/headers.js.map +1 -1
- package/dist/src/lex.d.ts +1 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +0 -1
- package/dist/src/lex.js.map +1 -1
- package/dist/src/parse.js +0 -17
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts.map +1 -1
- package/dist/src/std-bundle.js +10 -11
- package/dist/src/std-bundle.js.map +1 -1
- package/docs/README.md +0 -10
- package/docs/headers.md +15 -16
- package/examples/cost-table.json +8 -144
- package/examples/library.hsx +4 -4
- package/package.json +3 -3
- package/src/compile.ts +32 -208
- package/src/headers.ts +1 -2
- package/src/lex.ts +0 -1
- package/src/parse.ts +0 -14
- package/src/std-bundle.ts +10 -11
- package/std/cards.hsx +10 -11
- package/std/collections.hsx +3 -3
- package/std/escrow.hsx +2 -2
- package/std/financing.hsx +56 -53
- package/std/insurance.hsx +17 -16
- package/std/lending.hsx +8 -7
- package/std/money.hsx +8 -18
- package/std/reporting.hsx +0 -4
- package/std/travel.hsx +5 -6
- package/std/wallet.hsx +3 -3
- package/std/approvals.hsx +0 -17
package/src/compile.ts
CHANGED
|
@@ -545,17 +545,6 @@ export function compile(
|
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
const origins: CompileOriginMapEntry[] = [];
|
|
548
|
-
const materialApprovals = new Set<UdlAction>();
|
|
549
|
-
const implicitDecisions = new Map<
|
|
550
|
-
string,
|
|
551
|
-
{
|
|
552
|
-
target: string;
|
|
553
|
-
action: string;
|
|
554
|
-
party: string;
|
|
555
|
-
protectedRequest: string;
|
|
556
|
-
origin: Span;
|
|
557
|
-
}
|
|
558
|
-
>();
|
|
559
548
|
const resolveFamily = (
|
|
560
549
|
rawPath: string,
|
|
561
550
|
expr: { span: Span; source?: string },
|
|
@@ -629,7 +618,6 @@ export function compile(
|
|
|
629
618
|
arguments_: BlockExpr,
|
|
630
619
|
origin: Span,
|
|
631
620
|
inherited = new Map<string, Expr>(),
|
|
632
|
-
inheritedApprovers = new Set<string>(),
|
|
633
621
|
inheritedEnums = new Map<string, string[]>(),
|
|
634
622
|
attachmentInfo?: {
|
|
635
623
|
subjectKindId: string;
|
|
@@ -822,7 +810,6 @@ export function compile(
|
|
|
822
810
|
if (type.kind === "call" && type.name === "enum")
|
|
823
811
|
enums.set(parameter.key, type.args.map(text));
|
|
824
812
|
}
|
|
825
|
-
const approvers = new Set(inheritedApprovers);
|
|
826
813
|
const supplied = new Map<string, Expr>();
|
|
827
814
|
for (const entry of arguments_.entries) {
|
|
828
815
|
if (supplied.has(entry.key))
|
|
@@ -841,8 +828,7 @@ export function compile(
|
|
|
841
828
|
param.value.kind === "default" ? param.value.value : undefined;
|
|
842
829
|
const typeName =
|
|
843
830
|
type.kind === "type" || type.kind === "call" ? type.name : text(type);
|
|
844
|
-
const partyParameter =
|
|
845
|
-
attachmentInfo && (typeName === "party" || typeName === "approval");
|
|
831
|
+
const partyParameter = attachmentInfo && typeName === "party";
|
|
846
832
|
const byName: Expr | undefined =
|
|
847
833
|
partyParameter &&
|
|
848
834
|
(subjectPartyRoles.includes(param.key as SubjectPartyRole) ||
|
|
@@ -1000,11 +986,7 @@ export function compile(
|
|
|
1000
986
|
const v =
|
|
1001
987
|
(supplied.has(param.key) && !attachmentInfo) || type === "enum"
|
|
1002
988
|
? actual
|
|
1003
|
-
: resolve(
|
|
1004
|
-
actual,
|
|
1005
|
-
new Set(),
|
|
1006
|
-
!!attachmentInfo && (type === "party" || type === "approval"),
|
|
1007
|
-
);
|
|
989
|
+
: resolve(actual, new Set(), !!attachmentInfo && type === "party");
|
|
1008
990
|
environment.set(param.key, v);
|
|
1009
991
|
if (type === "enum" && t.kind === "call") {
|
|
1010
992
|
if (v.kind !== "name" || !t.args.some((a) => text(a) === v.value))
|
|
@@ -1016,7 +998,7 @@ export function compile(
|
|
|
1016
998
|
} else if (type === "list") {
|
|
1017
999
|
if (v.kind !== "list")
|
|
1018
1000
|
fail(v, `${param.key} needs a list`, "write [value, value]");
|
|
1019
|
-
} else if (type === "party"
|
|
1001
|
+
} else if (type === "party") {
|
|
1020
1002
|
if (v.kind !== "name" || !isParty(v.value))
|
|
1021
1003
|
failWithCode(
|
|
1022
1004
|
actual,
|
|
@@ -1026,18 +1008,14 @@ export function compile(
|
|
|
1026
1008
|
);
|
|
1027
1009
|
const party = document.parties[v.value];
|
|
1028
1010
|
if (
|
|
1029
|
-
(
|
|
1030
|
-
(
|
|
1031
|
-
(party?.kind === "staff" ||
|
|
1032
|
-
(attachmentInfo && party?.kind === "person")))
|
|
1011
|
+
(party?.kind === "staff" && !party.role) ||
|
|
1012
|
+
(attachmentInfo && party?.kind === "person")
|
|
1033
1013
|
)
|
|
1034
1014
|
failWithCode(
|
|
1035
1015
|
actual,
|
|
1036
1016
|
"party_kind_mismatch",
|
|
1037
1017
|
`${param.key} cannot bind ${v.value}`,
|
|
1038
|
-
|
|
1039
|
-
? "use a declared staff party with a role"
|
|
1040
|
-
: "use a subject role or declared business",
|
|
1018
|
+
"use a subject role, declared business, or staff with a role",
|
|
1041
1019
|
);
|
|
1042
1020
|
resolvedParties.add(param.key);
|
|
1043
1021
|
if (attachmentInfo)
|
|
@@ -1046,7 +1024,6 @@ export function compile(
|
|
|
1046
1024
|
)
|
|
1047
1025
|
? { role: v.value as SubjectPartyRole }
|
|
1048
1026
|
: { party: v.value };
|
|
1049
|
-
if (type === "approval") approvers.add(text(v));
|
|
1050
1027
|
} else if (type === "ref") {
|
|
1051
1028
|
const values = v.kind === "list" ? v.items : [v];
|
|
1052
1029
|
if (v.kind === "list" && (t.kind !== "type" || !t.many))
|
|
@@ -1092,6 +1069,14 @@ export function compile(
|
|
|
1092
1069
|
fail(value, "duplicate reference", "list each object once");
|
|
1093
1070
|
seen.add(value.value);
|
|
1094
1071
|
}
|
|
1072
|
+
// A many-reference tunable is a list even when one object is bound,
|
|
1073
|
+
// so report datasets and other value positions never see a bare name.
|
|
1074
|
+
if (t.kind === "type" && t.many && v.kind !== "list")
|
|
1075
|
+
environment.set(param.key, {
|
|
1076
|
+
kind: "list",
|
|
1077
|
+
items: [v],
|
|
1078
|
+
span: v.span,
|
|
1079
|
+
});
|
|
1095
1080
|
} else if (type === "fee" || type === "split" || type === "policy") {
|
|
1096
1081
|
if (v.kind !== "block")
|
|
1097
1082
|
fail(v, `${param.key} needs a block`, `write ${param.key} { ... }`);
|
|
@@ -1457,6 +1442,14 @@ export function compile(
|
|
|
1457
1442
|
fail(row, "reference needs a target", "write ref<object>");
|
|
1458
1443
|
const [root, ...tail] = target.split(".");
|
|
1459
1444
|
const resolved = environment.get(root!);
|
|
1445
|
+
// An optional field pointing at an unsupplied optional tunable
|
|
1446
|
+
// has nothing to point at, so the instrument drops it.
|
|
1447
|
+
if (
|
|
1448
|
+
!resolved &&
|
|
1449
|
+
f.optional &&
|
|
1450
|
+
decl.parameters.some((parameter) => parameter.key === root)
|
|
1451
|
+
)
|
|
1452
|
+
continue;
|
|
1460
1453
|
const resolvedTargets =
|
|
1461
1454
|
resolved?.kind === "list"
|
|
1462
1455
|
? resolved.items
|
|
@@ -1788,10 +1781,18 @@ export function compile(
|
|
|
1788
1781
|
const adapterList: UdlActionSubject["adapters"] = [];
|
|
1789
1782
|
for (const entry of subjectBlock.entries) {
|
|
1790
1783
|
if (entry.key === "adapter") {
|
|
1784
|
+
// A tunable names the adapter; anything else is the binding itself.
|
|
1785
|
+
const adapterName = (item: Expr): string =>
|
|
1786
|
+
item.kind === "name" &&
|
|
1787
|
+
decl.parameters.some(
|
|
1788
|
+
(parameter) => parameter.key === item.value,
|
|
1789
|
+
)
|
|
1790
|
+
? text(resolve(item))
|
|
1791
|
+
: text(item);
|
|
1791
1792
|
const bindingNames =
|
|
1792
1793
|
entry.value.kind === "list"
|
|
1793
|
-
? entry.value.items.map(
|
|
1794
|
-
: [
|
|
1794
|
+
? entry.value.items.map(adapterName)
|
|
1795
|
+
: [adapterName(entry.value)];
|
|
1795
1796
|
for (const bindingName of bindingNames) {
|
|
1796
1797
|
const target = options.adapterRegistry?.[bindingName];
|
|
1797
1798
|
if (target) {
|
|
@@ -2284,13 +2285,6 @@ export function compile(
|
|
|
2284
2285
|
"choose create, reserve, post, or void from internal_transfer",
|
|
2285
2286
|
);
|
|
2286
2287
|
}
|
|
2287
|
-
} else if (key === "approval") {
|
|
2288
|
-
const approval = data(expr) as UdlAction["approval"];
|
|
2289
|
-
if (approval && (approval.input as unknown) === "material") {
|
|
2290
|
-
approval.input = {};
|
|
2291
|
-
materialApprovals.add(a);
|
|
2292
|
-
}
|
|
2293
|
-
a.approval = approval;
|
|
2294
2288
|
} else (a as unknown as Record<string, unknown>)[key] = data(expr);
|
|
2295
2289
|
}
|
|
2296
2290
|
if (attachmentInfo) {
|
|
@@ -2328,34 +2322,6 @@ export function compile(
|
|
|
2328
2322
|
checkSubjectPaths(a.invoke, row.span);
|
|
2329
2323
|
currentAction = undefined;
|
|
2330
2324
|
currentActionName = undefined;
|
|
2331
|
-
for (const requirement of a.requires ?? []) {
|
|
2332
|
-
if (
|
|
2333
|
-
requirement.kind !== "approval" ||
|
|
2334
|
-
!approvers.has(requirement.party)
|
|
2335
|
-
)
|
|
2336
|
-
continue;
|
|
2337
|
-
const action = requirement.action ?? name;
|
|
2338
|
-
const key = `${id}_${action}_decision`;
|
|
2339
|
-
const previous = implicitDecisions.get(key);
|
|
2340
|
-
if (
|
|
2341
|
-
previous &&
|
|
2342
|
-
(previous.party !== requirement.party ||
|
|
2343
|
-
previous.protectedRequest !==
|
|
2344
|
-
(requirement.protectedRequest ?? "self"))
|
|
2345
|
-
)
|
|
2346
|
-
fail(
|
|
2347
|
-
{ span: origin },
|
|
2348
|
-
`action ${action} has conflicting approval parties or protected requests`,
|
|
2349
|
-
"use a separate decision action for each party",
|
|
2350
|
-
);
|
|
2351
|
-
implicitDecisions.set(key, {
|
|
2352
|
-
target: id,
|
|
2353
|
-
action,
|
|
2354
|
-
party: requirement.party,
|
|
2355
|
-
protectedRequest: requirement.protectedRequest ?? "self",
|
|
2356
|
-
origin,
|
|
2357
|
-
});
|
|
2358
|
-
}
|
|
2359
2325
|
for (const requirement of a.subject?.requirements ?? []) {
|
|
2360
2326
|
const entry =
|
|
2361
2327
|
asBlock(subjectExpr).entries.find(
|
|
@@ -2420,7 +2386,6 @@ export function compile(
|
|
|
2420
2386
|
...environment,
|
|
2421
2387
|
["parent", { kind: "name", value: id, span: origin } as Expr],
|
|
2422
2388
|
]),
|
|
2423
|
-
approvers,
|
|
2424
2389
|
enums,
|
|
2425
2390
|
attachmentInfo
|
|
2426
2391
|
? { ...attachmentInfo, exposed: new Map() }
|
|
@@ -2525,7 +2490,6 @@ export function compile(
|
|
|
2525
2490
|
tunableBlock,
|
|
2526
2491
|
entry.span,
|
|
2527
2492
|
new Map(),
|
|
2528
|
-
new Set(),
|
|
2529
2493
|
new Map(),
|
|
2530
2494
|
{
|
|
2531
2495
|
subjectKindId: decl.name,
|
|
@@ -2662,7 +2626,6 @@ export function compile(
|
|
|
2662
2626
|
decl.body,
|
|
2663
2627
|
decl.span,
|
|
2664
2628
|
new Map(),
|
|
2665
|
-
new Set(),
|
|
2666
2629
|
new Map(),
|
|
2667
2630
|
undefined,
|
|
2668
2631
|
templateFamily ? { ...templateFamily } : undefined,
|
|
@@ -2880,145 +2843,6 @@ export function compile(
|
|
|
2880
2843
|
);
|
|
2881
2844
|
}
|
|
2882
2845
|
}
|
|
2883
|
-
// Approval tunables instantiate the same library record as an explicit decision.
|
|
2884
|
-
if (implicitDecisions.size) {
|
|
2885
|
-
const content = (
|
|
2886
|
-
options.standardLibrary ?? bundledStandardLibrary
|
|
2887
|
-
).source("approvals");
|
|
2888
|
-
const parsed = content ? parseProgram(content) : undefined;
|
|
2889
|
-
const template = parsed?.program.decls.find(
|
|
2890
|
-
(decl) => decl.kind === "instrument" && decl.name === "decision",
|
|
2891
|
-
);
|
|
2892
|
-
if (
|
|
2893
|
-
!template ||
|
|
2894
|
-
template.kind !== "instrument" ||
|
|
2895
|
-
parsed?.diagnostics.length
|
|
2896
|
-
)
|
|
2897
|
-
fail(
|
|
2898
|
-
program,
|
|
2899
|
-
"implicit decisions need the approvals header",
|
|
2900
|
-
"provide the standard approvals header",
|
|
2901
|
-
);
|
|
2902
|
-
for (const [id, decision] of implicitDecisions) {
|
|
2903
|
-
if (
|
|
2904
|
-
!document.instruments.find((inst) => inst.id === decision.target)
|
|
2905
|
-
?.actions[decision.action]
|
|
2906
|
-
)
|
|
2907
|
-
continue;
|
|
2908
|
-
const existing = document.instruments.some((inst) =>
|
|
2909
|
-
Object.values(inst.actions).some(
|
|
2910
|
-
(action) =>
|
|
2911
|
-
action.approval?.action === decision.action &&
|
|
2912
|
-
action.approval.party === decision.party &&
|
|
2913
|
-
inst.fields.some((field) => {
|
|
2914
|
-
if (
|
|
2915
|
-
field.type !== "ref" ||
|
|
2916
|
-
field.target !== decision.target ||
|
|
2917
|
-
`self.${field.name}` !== action.approval?.target
|
|
2918
|
-
)
|
|
2919
|
-
return false;
|
|
2920
|
-
const [root, name, ...tail] =
|
|
2921
|
-
decision.protectedRequest.split(".");
|
|
2922
|
-
const input = name && action.approval.input[name];
|
|
2923
|
-
const request =
|
|
2924
|
-
root === "self"
|
|
2925
|
-
? [action.approval.target, name, ...tail]
|
|
2926
|
-
.filter(Boolean)
|
|
2927
|
-
.join(".")
|
|
2928
|
-
: root === "input" && name
|
|
2929
|
-
? input && "field" in input
|
|
2930
|
-
? [input.field, ...tail].join(".")
|
|
2931
|
-
: materialApprovals.has(action)
|
|
2932
|
-
? [`self.material_${name}`, ...tail].join(".")
|
|
2933
|
-
: undefined
|
|
2934
|
-
: undefined;
|
|
2935
|
-
return (
|
|
2936
|
-
request !== undefined &&
|
|
2937
|
-
action.approval.protectedRequest === request
|
|
2938
|
-
);
|
|
2939
|
-
}),
|
|
2940
|
-
),
|
|
2941
|
-
);
|
|
2942
|
-
if (existing) continue;
|
|
2943
|
-
if (document.instruments.some((inst) => inst.id === id))
|
|
2944
|
-
fail(
|
|
2945
|
-
{ span: decision.origin },
|
|
2946
|
-
`implicit decision name ${id} is already used`,
|
|
2947
|
-
"rename the conflicting object",
|
|
2948
|
-
);
|
|
2949
|
-
const protectedRequest =
|
|
2950
|
-
decision.protectedRequest === "self"
|
|
2951
|
-
? "self.target"
|
|
2952
|
-
: decision.protectedRequest.startsWith("self.")
|
|
2953
|
-
? `self.target.${decision.protectedRequest.slice(5)}`
|
|
2954
|
-
: fail(
|
|
2955
|
-
{ span: decision.origin },
|
|
2956
|
-
"implicit approval needs a stored protected request",
|
|
2957
|
-
"declare an explicit decision for an input-based protected request",
|
|
2958
|
-
);
|
|
2959
|
-
addInstrument(
|
|
2960
|
-
template,
|
|
2961
|
-
id,
|
|
2962
|
-
{
|
|
2963
|
-
kind: "block",
|
|
2964
|
-
span: decision.origin,
|
|
2965
|
-
entries: Object.entries({
|
|
2966
|
-
for: decision.target,
|
|
2967
|
-
approved_by: decision.party,
|
|
2968
|
-
action: decision.action,
|
|
2969
|
-
protected_request: protectedRequest,
|
|
2970
|
-
}).map(([key, value]) => ({
|
|
2971
|
-
key,
|
|
2972
|
-
value: {
|
|
2973
|
-
kind:
|
|
2974
|
-
key === "action" || key === "protected_request"
|
|
2975
|
-
? "text"
|
|
2976
|
-
: "name",
|
|
2977
|
-
value,
|
|
2978
|
-
span: decision.origin,
|
|
2979
|
-
},
|
|
2980
|
-
span: decision.origin,
|
|
2981
|
-
})),
|
|
2982
|
-
},
|
|
2983
|
-
decision.origin,
|
|
2984
|
-
new Map(),
|
|
2985
|
-
new Set(),
|
|
2986
|
-
new Map(),
|
|
2987
|
-
document.instruments.find(
|
|
2988
|
-
(instrument) => instrument.id === decision.target,
|
|
2989
|
-
)?.subject
|
|
2990
|
-
? {
|
|
2991
|
-
subjectKindId: document.instruments.find(
|
|
2992
|
-
(instrument) => instrument.id === decision.target,
|
|
2993
|
-
)!.subject!,
|
|
2994
|
-
attachmentName: id,
|
|
2995
|
-
parties: {},
|
|
2996
|
-
renames: new Map(),
|
|
2997
|
-
exposed: new Map(),
|
|
2998
|
-
}
|
|
2999
|
-
: undefined,
|
|
3000
|
-
);
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
// Material approval fields are inferred from the selected action's typed input.
|
|
3004
|
-
for (const inst of document.instruments)
|
|
3005
|
-
for (const action of Object.values(inst.actions)) {
|
|
3006
|
-
if (!materialApprovals.has(action) || !action.approval) continue;
|
|
3007
|
-
const targetField = inst.fields.find(
|
|
3008
|
-
(f) => `self.${f.name}` === action.approval!.target,
|
|
3009
|
-
);
|
|
3010
|
-
if (targetField?.type !== "ref") continue;
|
|
3011
|
-
const target = document.instruments.find(
|
|
3012
|
-
(i) => i.id === targetField.target,
|
|
3013
|
-
)?.actions[action.approval.action];
|
|
3014
|
-
if (!target) continue;
|
|
3015
|
-
for (const field of target.input) {
|
|
3016
|
-
const name = `material_${field.name}`;
|
|
3017
|
-
if (!inst.fields.some((f) => f.name === name))
|
|
3018
|
-
inst.fields.push({ ...field, name });
|
|
3019
|
-
action.approval.input[field.name] = { field: `self.${name}` };
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
2846
|
const changed = new Set<string>();
|
|
3023
2847
|
for (const decl of program.decls)
|
|
3024
2848
|
if (decl.kind === "hide" || decl.kind === "expose") {
|
package/src/headers.ts
CHANGED
|
@@ -11,7 +11,6 @@ export const HEADER_NAMES = [
|
|
|
11
11
|
"financing",
|
|
12
12
|
"lending",
|
|
13
13
|
"insurance",
|
|
14
|
-
"approvals",
|
|
15
14
|
"collections",
|
|
16
15
|
"travel",
|
|
17
16
|
"cards",
|
|
@@ -144,7 +143,7 @@ export function headerManifest(
|
|
|
144
143
|
: [];
|
|
145
144
|
})(),
|
|
146
145
|
parties: tunables
|
|
147
|
-
.filter((t) =>
|
|
146
|
+
.filter((t) => t.type === "party")
|
|
148
147
|
.map((t) => t.name),
|
|
149
148
|
actions: actions(decl.body),
|
|
150
149
|
};
|
package/src/lex.ts
CHANGED
package/src/parse.ts
CHANGED
|
@@ -330,20 +330,6 @@ class Parser {
|
|
|
330
330
|
return this.node(operator.text);
|
|
331
331
|
}
|
|
332
332
|
private requirement(): BlockExpr {
|
|
333
|
-
if (this.eat("approval")) {
|
|
334
|
-
this.expect("by");
|
|
335
|
-
const party = this.atom();
|
|
336
|
-
const values: Record<string, Expr> = {
|
|
337
|
-
kind: this.node("approval"),
|
|
338
|
-
party,
|
|
339
|
-
decision: this.node("approved"),
|
|
340
|
-
};
|
|
341
|
-
if (this.eat("for")) values.action = this.atom();
|
|
342
|
-
if (this.eat("is")) values.decision = this.atom();
|
|
343
|
-
for (const key of ["protectedRequest", "differentFromInitiator"])
|
|
344
|
-
if (this.eat(key)) values[key] = this.atom();
|
|
345
|
-
return this.record(values);
|
|
346
|
-
}
|
|
347
333
|
if (this.eat("unique")) {
|
|
348
334
|
const namespace = this.atom();
|
|
349
335
|
this.expect("on");
|