@hyperscale0/hsx 4.0.0 → 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 +8 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +46 -148
- 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/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +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 +4 -4
- package/src/compile.ts +57 -214
- 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/src/version.ts +1 -1
- 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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# HSX 4.1.0
|
|
2
|
+
|
|
3
|
+
Second-person approval is deleted from the grammar and the standard library. `financing.portfolio_limit` is a top-level instrument, a plan's `funds` hold is optional, and `insurance.cover` and `financing.credit_line` name their provider with `adapter: text` instead of a party. A car financing programme compiles from the standard library and is covered by its own spec.
|
|
4
|
+
|
|
5
|
+
# HSX 4.0.1
|
|
6
|
+
|
|
7
|
+
The compiler resolves list targets in field blocks and child record export paths, refuses duplicate child export path suffixes naming both candidates, and no longer restricts aggregate limit bounds at compile time; `<= self.<field>` bounds resolve at runtime. No grammar change.
|
|
8
|
+
|
|
1
9
|
# HSX 4.0.0
|
|
2
10
|
|
|
3
11
|
HSX adds authored object kinds and attachments with party bindings to owner, actor or operator. Action subject requirements are collected when an attached action runs. The vehicles header is deleted, public instrument create actions are gone, and UDL 3 is rejected. Programs compile to UDL 4; recreate development estates.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAcL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAC;AAG1B,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;CAC/C;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;AA2TD,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":"AACA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAcL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAC;AAG1B,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;CAC/C;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;AA2TD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAs+Ef"}
|
package/dist/src/compile.js
CHANGED
|
@@ -368,8 +368,6 @@ export function compile(source, options = {}) {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
const origins = [];
|
|
371
|
-
const materialApprovals = new Set();
|
|
372
|
-
const implicitDecisions = new Map();
|
|
373
371
|
const resolveFamily = (rawPath, expr, required = true) => {
|
|
374
372
|
const parts = rawPath.split(".");
|
|
375
373
|
if (parts.length < 2) {
|
|
@@ -419,7 +417,7 @@ export function compile(source, options = {}) {
|
|
|
419
417
|
}
|
|
420
418
|
return { module: moduleName, exportPath, revision };
|
|
421
419
|
};
|
|
422
|
-
const addInstrument = (decl, id, arguments_, origin, inherited = new Map(),
|
|
420
|
+
const addInstrument = (decl, id, arguments_, origin, inherited = new Map(), inheritedEnums = new Map(), attachmentInfo, familyDeclaration) => {
|
|
423
421
|
const resolveFamilyInstruments = (family, currentInstId, expr) => {
|
|
424
422
|
const found = new Set();
|
|
425
423
|
if (currentInstId && familyDeclaration) {
|
|
@@ -459,26 +457,35 @@ export function compile(source, options = {}) {
|
|
|
459
457
|
}
|
|
460
458
|
return result;
|
|
461
459
|
};
|
|
462
|
-
const
|
|
460
|
+
const collectChildExportPaths = (parentDecl, suffix) => {
|
|
463
461
|
const recs = entries(asBlock(entries(parentDecl.body).get("records")));
|
|
462
|
+
const matches = [];
|
|
464
463
|
for (const [recName] of recs) {
|
|
465
464
|
if (suffix === recName)
|
|
466
|
-
|
|
465
|
+
matches.push(recName);
|
|
467
466
|
}
|
|
468
467
|
for (const [recName, recBlock] of recs) {
|
|
469
468
|
if (suffix.startsWith(`${recName}_`)) {
|
|
470
|
-
const
|
|
469
|
+
const nested = collectChildExportPaths({
|
|
471
470
|
kind: "instrument",
|
|
472
471
|
name: recName,
|
|
473
472
|
parameters: [],
|
|
474
473
|
body: asBlock(recBlock),
|
|
475
474
|
span: parentDecl.span,
|
|
476
475
|
}, suffix.slice(recName.length + 1));
|
|
477
|
-
|
|
478
|
-
|
|
476
|
+
for (const rest of nested) {
|
|
477
|
+
matches.push(`${recName}.${rest}`);
|
|
478
|
+
}
|
|
479
479
|
}
|
|
480
480
|
}
|
|
481
|
-
return
|
|
481
|
+
return matches;
|
|
482
|
+
};
|
|
483
|
+
const resolveChildExportPath = (parentDecl, suffix) => {
|
|
484
|
+
const matches = collectChildExportPaths(parentDecl, suffix);
|
|
485
|
+
if (matches.length > 1) {
|
|
486
|
+
failWithCode(parentDecl, "HSX1001", `ambiguous child export path suffix '${suffix}': multiple candidates (${matches.join(", ")})`, "rename conflicting records to remove duplicate export path suffixes");
|
|
487
|
+
}
|
|
488
|
+
return matches[0];
|
|
482
489
|
};
|
|
483
490
|
const getInstrumentFamily = (targetId) => {
|
|
484
491
|
const existing = document.instruments.find((i) => i.id === targetId);
|
|
@@ -542,7 +549,6 @@ export function compile(source, options = {}) {
|
|
|
542
549
|
if (type.kind === "call" && type.name === "enum")
|
|
543
550
|
enums.set(parameter.key, type.args.map(text));
|
|
544
551
|
}
|
|
545
|
-
const approvers = new Set(inheritedApprovers);
|
|
546
552
|
const supplied = new Map();
|
|
547
553
|
for (const entry of arguments_.entries) {
|
|
548
554
|
if (supplied.has(entry.key))
|
|
@@ -554,7 +560,7 @@ export function compile(source, options = {}) {
|
|
|
554
560
|
const type = param.value.kind === "default" ? param.value.type : param.value;
|
|
555
561
|
const fallback = param.value.kind === "default" ? param.value.value : undefined;
|
|
556
562
|
const typeName = type.kind === "type" || type.kind === "call" ? type.name : text(type);
|
|
557
|
-
const partyParameter = attachmentInfo &&
|
|
563
|
+
const partyParameter = attachmentInfo && typeName === "party";
|
|
558
564
|
const byName = partyParameter &&
|
|
559
565
|
(subjectPartyRoles.includes(param.key) ||
|
|
560
566
|
document.parties[param.key])
|
|
@@ -662,7 +668,7 @@ export function compile(source, options = {}) {
|
|
|
662
668
|
const type = t.kind === "type" || t.kind === "call" ? t.name : text(t);
|
|
663
669
|
const v = (supplied.has(param.key) && !attachmentInfo) || type === "enum"
|
|
664
670
|
? actual
|
|
665
|
-
: resolve(actual, new Set(), !!attachmentInfo &&
|
|
671
|
+
: resolve(actual, new Set(), !!attachmentInfo && type === "party");
|
|
666
672
|
environment.set(param.key, v);
|
|
667
673
|
if (type === "enum" && t.kind === "call") {
|
|
668
674
|
if (v.kind !== "name" || !t.args.some((a) => text(a) === v.value))
|
|
@@ -672,24 +678,18 @@ export function compile(source, options = {}) {
|
|
|
672
678
|
if (v.kind !== "list")
|
|
673
679
|
fail(v, `${param.key} needs a list`, "write [value, value]");
|
|
674
680
|
}
|
|
675
|
-
else if (type === "party"
|
|
681
|
+
else if (type === "party") {
|
|
676
682
|
if (v.kind !== "name" || !isParty(v.value))
|
|
677
683
|
failWithCode(actual, attachmentInfo ? "subject_party_unbound" : "HSX1001", `${param.key} needs a declared party`, "declare a party and use its name here");
|
|
678
684
|
const party = document.parties[v.value];
|
|
679
|
-
if ((
|
|
680
|
-
(
|
|
681
|
-
|
|
682
|
-
(attachmentInfo && party?.kind === "person"))))
|
|
683
|
-
failWithCode(actual, "party_kind_mismatch", `${param.key} cannot bind ${v.value}`, type === "approval"
|
|
684
|
-
? "use a declared staff party with a role"
|
|
685
|
-
: "use a subject role or declared business");
|
|
685
|
+
if ((party?.kind === "staff" && !party.role) ||
|
|
686
|
+
(attachmentInfo && party?.kind === "person"))
|
|
687
|
+
failWithCode(actual, "party_kind_mismatch", `${param.key} cannot bind ${v.value}`, "use a subject role, declared business, or staff with a role");
|
|
686
688
|
resolvedParties.add(param.key);
|
|
687
689
|
if (attachmentInfo)
|
|
688
690
|
attachmentInfo.parties[param.key] = subjectPartyRoles.includes(v.value)
|
|
689
691
|
? { role: v.value }
|
|
690
692
|
: { party: v.value };
|
|
691
|
-
if (type === "approval")
|
|
692
|
-
approvers.add(text(v));
|
|
693
693
|
}
|
|
694
694
|
else if (type === "ref") {
|
|
695
695
|
const values = v.kind === "list" ? v.items : [v];
|
|
@@ -716,6 +716,14 @@ export function compile(source, options = {}) {
|
|
|
716
716
|
fail(value, "duplicate reference", "list each object once");
|
|
717
717
|
seen.add(value.value);
|
|
718
718
|
}
|
|
719
|
+
// A many-reference tunable is a list even when one object is bound,
|
|
720
|
+
// so report datasets and other value positions never see a bare name.
|
|
721
|
+
if (t.kind === "type" && t.many && v.kind !== "list")
|
|
722
|
+
environment.set(param.key, {
|
|
723
|
+
kind: "list",
|
|
724
|
+
items: [v],
|
|
725
|
+
span: v.span,
|
|
726
|
+
});
|
|
719
727
|
}
|
|
720
728
|
else if (type === "fee" || type === "split" || type === "policy") {
|
|
721
729
|
if (v.kind !== "block")
|
|
@@ -1000,6 +1008,12 @@ export function compile(source, options = {}) {
|
|
|
1000
1008
|
fail(row, "reference needs a target", "write ref<object>");
|
|
1001
1009
|
const [root, ...tail] = target.split(".");
|
|
1002
1010
|
const resolved = environment.get(root);
|
|
1011
|
+
// An optional field pointing at an unsupplied optional tunable
|
|
1012
|
+
// has nothing to point at, so the instrument drops it.
|
|
1013
|
+
if (!resolved &&
|
|
1014
|
+
f.optional &&
|
|
1015
|
+
decl.parameters.some((parameter) => parameter.key === root))
|
|
1016
|
+
continue;
|
|
1003
1017
|
const resolvedTargets = resolved?.kind === "list"
|
|
1004
1018
|
? resolved.items
|
|
1005
1019
|
: resolved
|
|
@@ -1263,9 +1277,14 @@ export function compile(source, options = {}) {
|
|
|
1263
1277
|
const adapterList = [];
|
|
1264
1278
|
for (const entry of subjectBlock.entries) {
|
|
1265
1279
|
if (entry.key === "adapter") {
|
|
1280
|
+
// A tunable names the adapter; anything else is the binding itself.
|
|
1281
|
+
const adapterName = (item) => item.kind === "name" &&
|
|
1282
|
+
decl.parameters.some((parameter) => parameter.key === item.value)
|
|
1283
|
+
? text(resolve(item))
|
|
1284
|
+
: text(item);
|
|
1266
1285
|
const bindingNames = entry.value.kind === "list"
|
|
1267
|
-
? entry.value.items.map(
|
|
1268
|
-
: [
|
|
1286
|
+
? entry.value.items.map(adapterName)
|
|
1287
|
+
: [adapterName(entry.value)];
|
|
1269
1288
|
for (const bindingName of bindingNames) {
|
|
1270
1289
|
const target = options.adapterRegistry?.[bindingName];
|
|
1271
1290
|
if (target) {
|
|
@@ -1617,14 +1636,6 @@ export function compile(source, options = {}) {
|
|
|
1617
1636
|
fail(move, `unknown transfer instruction ${op}`, "choose create, reserve, post, or void from internal_transfer");
|
|
1618
1637
|
}
|
|
1619
1638
|
}
|
|
1620
|
-
else if (key === "approval") {
|
|
1621
|
-
const approval = data(expr);
|
|
1622
|
-
if (approval && approval.input === "material") {
|
|
1623
|
-
approval.input = {};
|
|
1624
|
-
materialApprovals.add(a);
|
|
1625
|
-
}
|
|
1626
|
-
a.approval = approval;
|
|
1627
|
-
}
|
|
1628
1639
|
else
|
|
1629
1640
|
a[key] = data(expr);
|
|
1630
1641
|
}
|
|
@@ -1662,26 +1673,6 @@ export function compile(source, options = {}) {
|
|
|
1662
1673
|
checkSubjectPaths(a.invoke, row.span);
|
|
1663
1674
|
currentAction = undefined;
|
|
1664
1675
|
currentActionName = undefined;
|
|
1665
|
-
for (const requirement of a.requires ?? []) {
|
|
1666
|
-
if (requirement.kind !== "approval" ||
|
|
1667
|
-
!approvers.has(requirement.party))
|
|
1668
|
-
continue;
|
|
1669
|
-
const action = requirement.action ?? name;
|
|
1670
|
-
const key = `${id}_${action}_decision`;
|
|
1671
|
-
const previous = implicitDecisions.get(key);
|
|
1672
|
-
if (previous &&
|
|
1673
|
-
(previous.party !== requirement.party ||
|
|
1674
|
-
previous.protectedRequest !==
|
|
1675
|
-
(requirement.protectedRequest ?? "self")))
|
|
1676
|
-
fail({ span: origin }, `action ${action} has conflicting approval parties or protected requests`, "use a separate decision action for each party");
|
|
1677
|
-
implicitDecisions.set(key, {
|
|
1678
|
-
target: id,
|
|
1679
|
-
action,
|
|
1680
|
-
party: requirement.party,
|
|
1681
|
-
protectedRequest: requirement.protectedRequest ?? "self",
|
|
1682
|
-
origin,
|
|
1683
|
-
});
|
|
1684
|
-
}
|
|
1685
1676
|
for (const requirement of a.subject?.requirements ?? []) {
|
|
1686
1677
|
const entry = asBlock(subjectExpr).entries.find((entry) => entry.key === requirement.field.name) ??
|
|
1687
1678
|
asBlock(subjectExpr).entries.find((entry) => entry.key === "adapter");
|
|
@@ -1730,7 +1721,7 @@ export function compile(source, options = {}) {
|
|
|
1730
1721
|
addInstrument(child, `${id}_${key}`, emptyBlock, origin, new Map([
|
|
1731
1722
|
...environment,
|
|
1732
1723
|
["parent", { kind: "name", value: id, span: origin }],
|
|
1733
|
-
]),
|
|
1724
|
+
]), enums, attachmentInfo
|
|
1734
1725
|
? { ...attachmentInfo, exposed: new Map() }
|
|
1735
1726
|
: undefined, childFamily);
|
|
1736
1727
|
}
|
|
@@ -1803,7 +1794,7 @@ export function compile(source, options = {}) {
|
|
|
1803
1794
|
span: entry.value.span,
|
|
1804
1795
|
};
|
|
1805
1796
|
const templateFamily = resolveFamily(targetTemplate, entry, false);
|
|
1806
|
-
addInstrument(template, instId, tunableBlock, entry.span, new Map(), new
|
|
1797
|
+
addInstrument(template, instId, tunableBlock, entry.span, new Map(), new Map(), {
|
|
1807
1798
|
subjectKindId: decl.name,
|
|
1808
1799
|
attachmentName,
|
|
1809
1800
|
renames,
|
|
@@ -1892,7 +1883,7 @@ export function compile(source, options = {}) {
|
|
|
1892
1883
|
if (!template)
|
|
1893
1884
|
fail(decl, `unknown instrument ${decl.target}`, `add use ${decl.target.split(".")[0]} and choose a declared instrument`);
|
|
1894
1885
|
const templateFamily = resolveFamily(decl.target, decl, false);
|
|
1895
|
-
addInstrument(template, decl.name, decl.body, decl.span, new Map(), new
|
|
1886
|
+
addInstrument(template, decl.name, decl.body, decl.span, new Map(), new Map(), undefined, templateFamily ? { ...templateFamily } : undefined);
|
|
1896
1887
|
}
|
|
1897
1888
|
if (decl.kind === "object") {
|
|
1898
1889
|
compileObject(decl);
|
|
@@ -2061,99 +2052,6 @@ export function compile(source, options = {}) {
|
|
|
2061
2052
|
: field.target).some((id) => eliminatedStates.get(id)?.has(state)));
|
|
2062
2053
|
}
|
|
2063
2054
|
}
|
|
2064
|
-
// Approval tunables instantiate the same library record as an explicit decision.
|
|
2065
|
-
if (implicitDecisions.size) {
|
|
2066
|
-
const content = (options.standardLibrary ?? bundledStandardLibrary).source("approvals");
|
|
2067
|
-
const parsed = content ? parseProgram(content) : undefined;
|
|
2068
|
-
const template = parsed?.program.decls.find((decl) => decl.kind === "instrument" && decl.name === "decision");
|
|
2069
|
-
if (!template ||
|
|
2070
|
-
template.kind !== "instrument" ||
|
|
2071
|
-
parsed?.diagnostics.length)
|
|
2072
|
-
fail(program, "implicit decisions need the approvals header", "provide the standard approvals header");
|
|
2073
|
-
for (const [id, decision] of implicitDecisions) {
|
|
2074
|
-
if (!document.instruments.find((inst) => inst.id === decision.target)
|
|
2075
|
-
?.actions[decision.action])
|
|
2076
|
-
continue;
|
|
2077
|
-
const existing = document.instruments.some((inst) => Object.values(inst.actions).some((action) => action.approval?.action === decision.action &&
|
|
2078
|
-
action.approval.party === decision.party &&
|
|
2079
|
-
inst.fields.some((field) => {
|
|
2080
|
-
if (field.type !== "ref" ||
|
|
2081
|
-
field.target !== decision.target ||
|
|
2082
|
-
`self.${field.name}` !== action.approval?.target)
|
|
2083
|
-
return false;
|
|
2084
|
-
const [root, name, ...tail] = decision.protectedRequest.split(".");
|
|
2085
|
-
const input = name && action.approval.input[name];
|
|
2086
|
-
const request = root === "self"
|
|
2087
|
-
? [action.approval.target, name, ...tail]
|
|
2088
|
-
.filter(Boolean)
|
|
2089
|
-
.join(".")
|
|
2090
|
-
: root === "input" && name
|
|
2091
|
-
? input && "field" in input
|
|
2092
|
-
? [input.field, ...tail].join(".")
|
|
2093
|
-
: materialApprovals.has(action)
|
|
2094
|
-
? [`self.material_${name}`, ...tail].join(".")
|
|
2095
|
-
: undefined
|
|
2096
|
-
: undefined;
|
|
2097
|
-
return (request !== undefined &&
|
|
2098
|
-
action.approval.protectedRequest === request);
|
|
2099
|
-
})));
|
|
2100
|
-
if (existing)
|
|
2101
|
-
continue;
|
|
2102
|
-
if (document.instruments.some((inst) => inst.id === id))
|
|
2103
|
-
fail({ span: decision.origin }, `implicit decision name ${id} is already used`, "rename the conflicting object");
|
|
2104
|
-
const protectedRequest = decision.protectedRequest === "self"
|
|
2105
|
-
? "self.target"
|
|
2106
|
-
: decision.protectedRequest.startsWith("self.")
|
|
2107
|
-
? `self.target.${decision.protectedRequest.slice(5)}`
|
|
2108
|
-
: fail({ span: decision.origin }, "implicit approval needs a stored protected request", "declare an explicit decision for an input-based protected request");
|
|
2109
|
-
addInstrument(template, id, {
|
|
2110
|
-
kind: "block",
|
|
2111
|
-
span: decision.origin,
|
|
2112
|
-
entries: Object.entries({
|
|
2113
|
-
for: decision.target,
|
|
2114
|
-
approved_by: decision.party,
|
|
2115
|
-
action: decision.action,
|
|
2116
|
-
protected_request: protectedRequest,
|
|
2117
|
-
}).map(([key, value]) => ({
|
|
2118
|
-
key,
|
|
2119
|
-
value: {
|
|
2120
|
-
kind: key === "action" || key === "protected_request"
|
|
2121
|
-
? "text"
|
|
2122
|
-
: "name",
|
|
2123
|
-
value,
|
|
2124
|
-
span: decision.origin,
|
|
2125
|
-
},
|
|
2126
|
-
span: decision.origin,
|
|
2127
|
-
})),
|
|
2128
|
-
}, decision.origin, new Map(), new Set(), new Map(), document.instruments.find((instrument) => instrument.id === decision.target)?.subject
|
|
2129
|
-
? {
|
|
2130
|
-
subjectKindId: document.instruments.find((instrument) => instrument.id === decision.target).subject,
|
|
2131
|
-
attachmentName: id,
|
|
2132
|
-
parties: {},
|
|
2133
|
-
renames: new Map(),
|
|
2134
|
-
exposed: new Map(),
|
|
2135
|
-
}
|
|
2136
|
-
: undefined);
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
// Material approval fields are inferred from the selected action's typed input.
|
|
2140
|
-
for (const inst of document.instruments)
|
|
2141
|
-
for (const action of Object.values(inst.actions)) {
|
|
2142
|
-
if (!materialApprovals.has(action) || !action.approval)
|
|
2143
|
-
continue;
|
|
2144
|
-
const targetField = inst.fields.find((f) => `self.${f.name}` === action.approval.target);
|
|
2145
|
-
if (targetField?.type !== "ref")
|
|
2146
|
-
continue;
|
|
2147
|
-
const target = document.instruments.find((i) => i.id === targetField.target)?.actions[action.approval.action];
|
|
2148
|
-
if (!target)
|
|
2149
|
-
continue;
|
|
2150
|
-
for (const field of target.input) {
|
|
2151
|
-
const name = `material_${field.name}`;
|
|
2152
|
-
if (!inst.fields.some((f) => f.name === name))
|
|
2153
|
-
inst.fields.push({ ...field, name });
|
|
2154
|
-
action.approval.input[field.name] = { field: `self.${name}` };
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
2055
|
const changed = new Set();
|
|
2158
2056
|
for (const decl of program.decls)
|
|
2159
2057
|
if (decl.kind === "hide" || decl.kind === "expose") {
|