@hyperscale0/hsx 5.0.1 → 5.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 +12 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +90 -17
- package/dist/src/compile.js.map +1 -1
- package/dist/src/std-bundle.js +5 -5
- 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 +26 -18
- package/package.json +3 -3
- package/src/compile.ts +116 -17
- package/src/std-bundle.ts +5 -5
- package/src/version.ts +1 -1
- package/std/cards.hsx +2 -0
- package/std/financing.hsx +1 -1
- package/std/lending.hsx +3 -2
- package/std/money.hsx +1 -0
- package/std/reporting.hsx +32 -4
|
@@ -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,uhIAAuhI,CAAC;IACtiI,CAAC,iBAAiB,EAAE,ooKAAooK,CAAC;IACzpK,CAAC,YAAY,EAAE,o8DAAo8D,CAAC;IACp9D,CAAC,eAAe,EAAE,o6vDAAo6vD,CAAC;IACv7vD,CAAC,eAAe,EAAE,g7FAAg7F,CAAC;IACn8F,CAAC,aAAa,EAAE,mvPAAmvP,CAAC;IACpwP,CAAC,iBAAiB,EAAE,m0PAAm0P,CAAC;IACx1P,CAAC,WAAW,EAAE,y1RAAy1R,CAAC;IACx2R,CAAC,eAAe,EAAE,w77CAAw77C,CAAC;IAC387C,CAAC,aAAa,EAAE,wrGAAwrG,CAAC;IACzsG,CAAC,YAAY,EAAE,8kMAA8kM,CAAC;IAC9lM,CAAC,YAAY,EAAE,m6DAAm6D,CAAC;CACp7D,CAAC,CAAC"}
|
package/dist/src/version.d.ts
CHANGED
package/dist/src/version.js
CHANGED
package/docs/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# HSX
|
|
1
|
+
# HSX 5.0.1
|
|
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.
|
|
@@ -70,7 +70,9 @@ Accounts use cash or claim books. A move stays in one book. `account of buyer`
|
|
|
70
70
|
aliases the default cash account; `account of self` provisions an owned account.
|
|
71
71
|
`account(lender, cash, "capital")` binds a named account.
|
|
72
72
|
`account(buyer, claim, contra, "debt")` declares the borrower's claim contra account.
|
|
73
|
-
|
|
73
|
+
Provider confirmation belongs on a reserved move with `boundary adapter`,
|
|
74
|
+
followed by instruction-bound evidence and a post or void. External account
|
|
75
|
+
mode is not supported.
|
|
74
76
|
Outstanding debt is an account balance. The library pairs cash repayment with
|
|
75
77
|
claim reduction and represents receipts as immutable child records. Cash and loss
|
|
76
78
|
shares round down; the declared residual account receives leftover minor units.
|
|
@@ -123,7 +125,8 @@ maxAge 1d` requires a recent completed provider check.
|
|
|
123
125
|
`moves reserve amount from payer to payee capture receipt` reserves a transfer;
|
|
124
126
|
`moves post self.receipt` posts it and `moves void self.receipt` releases it.
|
|
125
127
|
`moves amount from payer shares shares` expands a declared split. Optional `fee`,
|
|
126
|
-
`capture` and `
|
|
128
|
+
`capture`, `key` and `boundary` modifiers describe a move. `boundary` applies
|
|
129
|
+
only to reservations. Fees settle with create moves, never reservations. Repeated clauses keep
|
|
127
130
|
their declaration order. The JSON-like clause form remains accepted and lowers
|
|
128
131
|
to the same [UDL clauses](../../udl/spec/README.md).
|
|
129
132
|
They cannot add kernel instructions. `at(list, position)` reads a dated list;
|
|
@@ -140,16 +143,16 @@ and a correction. No artifact is returned with diagnostics.
|
|
|
140
143
|
activation. The company chooses where funding goes and when profit becomes earned.
|
|
141
144
|
All choices use accounts, calculations and moves in the same two books.
|
|
142
145
|
|
|
143
|
-
| Tunable | Default | Selected behavior
|
|
144
|
-
| ---------------------------------- | ------------------------ |
|
|
145
|
-
| `disburse_to` | `funds` | `funds` pays down payment and principal into the hold and confirms it. `borrower` credits principal to the borrower's balance.
|
|
146
|
-
| `profit_earned` | `on_payment` | `on_payment` collects unearned profit on payment. `by_schedule` moves unearned claims to earned claims at each slice's date. `at_disbursement` makes that move at activation. Cash arrives only on payment.
|
|
147
|
-
| `apply` | `fines_profit_principal` |
|
|
148
|
-
| `payoff_rebate` | `100%` | Reverse this share of unearned profit to borrower debt. Collect the remainder and all earned profit with outstanding principal.
|
|
149
|
-
| `late_charge.fines_to` | `programOperator` | Receive fine cash and fund its refunds.
|
|
150
|
-
| `late_charge.costs_to` | `programOperator` | Receive evidenced recovery cash and fund its refunds.
|
|
151
|
-
| `collections.case.fee` | `20%` | After payment succeeds, transfer this share of the payment amount from plan capital to the agency.
|
|
152
|
-
| `lending.distribution.residual_to` | `programOperator` | Receive the cash remainder and own the claim loss remainder after weighted distribution.
|
|
146
|
+
| Tunable | Default | Selected behavior |
|
|
147
|
+
| ---------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
148
|
+
| `disburse_to` | `funds` | `funds` pays down payment and principal into the hold and confirms it. `borrower` credits principal to the borrower's balance. |
|
|
149
|
+
| `profit_earned` | `on_payment` | `on_payment` collects unearned profit on payment. `by_schedule` moves unearned claims to earned claims at each slice's date. `at_disbursement` makes that move at activation. Cash arrives only on payment. |
|
|
150
|
+
| `apply` | `fines_profit_principal` | Visit each slice in position order; within that slice collect assessed fines by overdue date, evidenced costs, profit, then principal. `principal_profit` collects each slice in position order, principal before profit. `pro_rata` uses the slice's fixed principal:profit ratio, caps each side by its collectible balance, and assigns the remainder without stranding cash. |
|
|
151
|
+
| `payoff_rebate` | `100%` | Reverse this share of unearned profit to borrower debt. Collect the remainder and all earned profit with outstanding principal. |
|
|
152
|
+
| `late_charge.fines_to` | `programOperator` | Receive fine cash and fund its refunds. |
|
|
153
|
+
| `late_charge.costs_to` | `programOperator` | Receive evidenced recovery cash and fund its refunds. |
|
|
154
|
+
| `collections.case.fee` | `20%` | After payment succeeds, transfer this share of the payment amount from plan capital to the agency. |
|
|
155
|
+
| `lending.distribution.residual_to` | `programOperator` | Receive the cash remainder and own the claim loss remainder after weighted distribution. |
|
|
153
156
|
|
|
154
157
|
Each slice owns separate unearned and earned claim accounts. Scheduled recognition
|
|
155
158
|
uses a dated child record so a partial payment cannot disable the maturity clock.
|
|
@@ -159,7 +162,8 @@ costs stay assessed until their receivable reaches zero. Immutable receipts let
|
|
|
159
162
|
a refund restore only what that payment collected. The plan's payment receipt
|
|
160
163
|
records actual principal and profit after all ordered collections finish.
|
|
161
164
|
Write-off moves principal and earned profit claims to loss and reverses unearned
|
|
162
|
-
profit to debt.
|
|
165
|
+
profit to debt. `write_off_after` sets how long a slice must be overdue before a capital-authorized
|
|
166
|
+
write-off request can apply. There is no provision policy.
|
|
163
167
|
|
|
164
168
|
## Header branches and bounds
|
|
165
169
|
|
|
@@ -173,9 +177,7 @@ when disburse_to is borrower {
|
|
|
173
177
|
|
|
174
178
|
`when <reference tunable> has <field>` includes its clauses only when the bound
|
|
175
179
|
object declares that field. The compiler checks the object's declared shape,
|
|
176
|
-
regardless of declaration order, and emits no runtime branch.
|
|
177
|
-
this to commit marketplace orders for order-backed holds while plain money
|
|
178
|
-
holds need no order relation.
|
|
180
|
+
regardless of declaration order, and emits no runtime branch.
|
|
179
181
|
|
|
180
182
|
`when <enum tunable> is <value>` accepts requirements, calculations, moves and
|
|
181
183
|
invocations, including nested branches. The compiler emits only the selected
|
|
@@ -253,7 +255,7 @@ adapters are bound, participants have funds, or the flow can finish.
|
|
|
253
255
|
| `financing.limits` | `per_borrower` caps outstanding principal. Both borrower and portfolio limits must be approved before disbursement; attaching them does not approve them. |
|
|
254
256
|
| `lending.round` | The target is the linked plan's principal. Closing requires commitments and held funds to equal that amount. |
|
|
255
257
|
| `lending.distribution` | Cash distribution needs an eligible recorded settlement, `prepare_cash`, one share record per funded commitment, then `distribute_cash`. Attaching it moves nothing. |
|
|
256
|
-
| `insurance.cover.slice` | `
|
|
258
|
+
| `insurance.cover.slice` | `collect` credits the whole premium to the tenant. The required insurer payout of premium minus commission is blocked on generic adapter-owned account binding. This header does not yet implement insurer settlement. |
|
|
257
259
|
| `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. |
|
|
258
260
|
| `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 positions. |
|
|
259
261
|
| `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. |
|
|
@@ -266,3 +268,9 @@ commitment creation, and cash prepare/distribute actions. Its repayment and shar
|
|
|
266
268
|
child actions still lack a public execution path. It is a composition example,
|
|
267
269
|
not a complete public repayment flow. Callers still need dates, agreement inputs,
|
|
268
270
|
funded wallets, eligible settlements and the distribution's share records.
|
|
271
|
+
|
|
272
|
+
Insurance providers are adapters, never party declarations. The tenant must retain
|
|
273
|
+
commission and pay the insurer the remaining premium through a confirmed move.
|
|
274
|
+
The current account ABI cannot bind an adapter-owned destination. Do not treat
|
|
275
|
+
`insurance.cover` or `insurance.claim` as a complete insurer integration; claims
|
|
276
|
+
currently reserve tenant funds.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperscale0/hsx",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.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.3.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": "5bb82154896feeb7fa002d3bff439a6f90ed91e1"
|
|
102
102
|
}
|
package/src/compile.ts
CHANGED
|
@@ -643,6 +643,8 @@ export function compile(
|
|
|
643
643
|
renames: Map<string, string>;
|
|
644
644
|
exposed: Map<string, string>;
|
|
645
645
|
parties: Record<string, AttachmentPartyBinding>;
|
|
646
|
+
attachments: UdlObjectAttachment[];
|
|
647
|
+
child?: boolean;
|
|
646
648
|
},
|
|
647
649
|
familyDeclaration?: {
|
|
648
650
|
module: string;
|
|
@@ -2549,7 +2551,7 @@ export function compile(
|
|
|
2549
2551
|
}
|
|
2550
2552
|
} else (a as unknown as Record<string, unknown>)[key] = data(expr);
|
|
2551
2553
|
}
|
|
2552
|
-
if (attachmentInfo) {
|
|
2554
|
+
if (attachmentInfo && !attachmentInfo.child) {
|
|
2553
2555
|
if (attachmentInfo.exposed.has(name)) {
|
|
2554
2556
|
a.publicAction = attachmentInfo.exposed.get(name)!;
|
|
2555
2557
|
} else {
|
|
@@ -2619,6 +2621,21 @@ export function compile(
|
|
|
2619
2621
|
}
|
|
2620
2622
|
document.instruments.push(inst);
|
|
2621
2623
|
for (const [key, definition] of records) {
|
|
2624
|
+
const childAttachment = attachmentInfo
|
|
2625
|
+
? {
|
|
2626
|
+
...attachmentInfo,
|
|
2627
|
+
attachmentName: `${attachmentInfo.attachmentName}_${key}`,
|
|
2628
|
+
child: true,
|
|
2629
|
+
exposed: new Map<string, string>(),
|
|
2630
|
+
}
|
|
2631
|
+
: undefined;
|
|
2632
|
+
if (childAttachment)
|
|
2633
|
+
childAttachment.attachments.push({
|
|
2634
|
+
name: childAttachment.attachmentName,
|
|
2635
|
+
parent: attachmentInfo!.attachmentName,
|
|
2636
|
+
instrument: `${id}_${key}`,
|
|
2637
|
+
parties: childAttachment.parties,
|
|
2638
|
+
});
|
|
2622
2639
|
const child: InstrumentDecl = {
|
|
2623
2640
|
kind: "instrument",
|
|
2624
2641
|
name: key,
|
|
@@ -2649,9 +2666,7 @@ export function compile(
|
|
|
2649
2666
|
["parent", { kind: "name", value: id, span: origin } as Expr],
|
|
2650
2667
|
]),
|
|
2651
2668
|
enums,
|
|
2652
|
-
|
|
2653
|
-
? { ...attachmentInfo, exposed: new Map() }
|
|
2654
|
-
: undefined,
|
|
2669
|
+
childAttachment,
|
|
2655
2670
|
childFamily,
|
|
2656
2671
|
);
|
|
2657
2672
|
}
|
|
@@ -2746,6 +2761,7 @@ export function compile(
|
|
|
2746
2761
|
};
|
|
2747
2762
|
|
|
2748
2763
|
const templateFamily = resolveFamily(targetTemplate, entry, false);
|
|
2764
|
+
const firstAttachedInstrument = document.instruments.length;
|
|
2749
2765
|
try {
|
|
2750
2766
|
addInstrument(
|
|
2751
2767
|
template,
|
|
@@ -2760,6 +2776,7 @@ export function compile(
|
|
|
2760
2776
|
renames,
|
|
2761
2777
|
exposed,
|
|
2762
2778
|
parties,
|
|
2779
|
+
attachments,
|
|
2763
2780
|
},
|
|
2764
2781
|
templateFamily ? { ...templateFamily } : undefined,
|
|
2765
2782
|
);
|
|
@@ -2769,17 +2786,24 @@ export function compile(
|
|
|
2769
2786
|
continue;
|
|
2770
2787
|
}
|
|
2771
2788
|
const attachedInst = document.instruments.find((i) => i.id === instId);
|
|
2772
|
-
|
|
2789
|
+
for (const createdInst of document.instruments
|
|
2790
|
+
.slice(firstAttachedInstrument)
|
|
2791
|
+
.filter(
|
|
2792
|
+
(instrument) =>
|
|
2793
|
+
instrument.id === instId ||
|
|
2794
|
+
instrument.actions.create?.publicAction,
|
|
2795
|
+
)) {
|
|
2796
|
+
if (!createdInst.actions.create) continue;
|
|
2773
2797
|
const owned = new Set(
|
|
2774
|
-
|
|
2798
|
+
createdInst.calculate.map((node) => node.target),
|
|
2775
2799
|
);
|
|
2776
|
-
for (const action of Object.values(
|
|
2800
|
+
for (const action of Object.values(createdInst.actions)) {
|
|
2777
2801
|
for (const node of action.calculate ?? []) owned.add(node.target);
|
|
2778
2802
|
for (const move of action.moves)
|
|
2779
2803
|
if ("capture" in move && move.capture) owned.add(move.capture);
|
|
2780
2804
|
}
|
|
2781
|
-
const create =
|
|
2782
|
-
for (const field of
|
|
2805
|
+
const create = createdInst.actions.create;
|
|
2806
|
+
for (const field of createdInst.fields) {
|
|
2783
2807
|
if (
|
|
2784
2808
|
field.type === "account" ||
|
|
2785
2809
|
(field.type === "ref" && field.targetKind === "instrument") ||
|
|
@@ -2788,7 +2812,8 @@ export function compile(
|
|
|
2788
2812
|
field.targetKind === "instrument") ||
|
|
2789
2813
|
field.optional ||
|
|
2790
2814
|
"value" in field ||
|
|
2791
|
-
owned.has(field.name)
|
|
2815
|
+
owned.has(field.name) ||
|
|
2816
|
+
create.input.some((input) => input.name === field.name)
|
|
2792
2817
|
)
|
|
2793
2818
|
continue;
|
|
2794
2819
|
create.subject ??= { requirements: [], adapters: [] };
|
|
@@ -2800,7 +2825,7 @@ export function compile(
|
|
|
2800
2825
|
failWithCode(
|
|
2801
2826
|
entry,
|
|
2802
2827
|
"subject_field_conflict",
|
|
2803
|
-
`${
|
|
2828
|
+
`${createdInst.id}.create.subject.${field.name} conflicts with its instrument field`,
|
|
2804
2829
|
"use the instrument field's type and constraints",
|
|
2805
2830
|
);
|
|
2806
2831
|
continue;
|
|
@@ -2814,7 +2839,7 @@ export function compile(
|
|
|
2814
2839
|
requirementOrigins.set(requirement, {
|
|
2815
2840
|
source: declarationSources.get(template) ?? "program",
|
|
2816
2841
|
span: entry.span,
|
|
2817
|
-
message: `${
|
|
2842
|
+
message: `${createdInst.id}.create.fields.${field.name}`,
|
|
2818
2843
|
});
|
|
2819
2844
|
}
|
|
2820
2845
|
}
|
|
@@ -2913,16 +2938,15 @@ export function compile(
|
|
|
2913
2938
|
.sort((a, b) => a.span.start - b.span.start)
|
|
2914
2939
|
.map((d) => diagnostic(d, "check")),
|
|
2915
2940
|
};
|
|
2916
|
-
// Propagate
|
|
2941
|
+
// Propagate invoked requirements with the conditions on each invocation path.
|
|
2917
2942
|
let changedInvocations = true;
|
|
2918
2943
|
let invocationIterations = 0;
|
|
2919
2944
|
while (changedInvocations && invocationIterations < 32) {
|
|
2920
2945
|
changedInvocations = false;
|
|
2921
2946
|
invocationIterations++;
|
|
2922
2947
|
for (const inst of document.instruments) {
|
|
2923
|
-
for (const action of Object.
|
|
2948
|
+
for (const [actionName, action] of Object.entries(inst.actions)) {
|
|
2924
2949
|
for (const call of action.invoke ?? []) {
|
|
2925
|
-
if (call.guard) continue;
|
|
2926
2950
|
let targetIds: string[] = [];
|
|
2927
2951
|
if ("instrument" in call) {
|
|
2928
2952
|
targetIds = [call.instrument];
|
|
@@ -2962,7 +2986,9 @@ export function compile(
|
|
|
2962
2986
|
if (!action.subject) {
|
|
2963
2987
|
action.subject = { requirements: [], adapters: [] };
|
|
2964
2988
|
}
|
|
2965
|
-
for (const adapter of
|
|
2989
|
+
for (const adapter of call.guard
|
|
2990
|
+
? []
|
|
2991
|
+
: targetAction.subject.adapters) {
|
|
2966
2992
|
if (
|
|
2967
2993
|
!action.subject.adapters.some(
|
|
2968
2994
|
(existing) => existing.binding === adapter.binding,
|
|
@@ -2979,9 +3005,65 @@ export function compile(
|
|
|
2979
3005
|
(cr) =>
|
|
2980
3006
|
(cr.objectField ?? cr.field.name) === targetObjFieldName,
|
|
2981
3007
|
);
|
|
3008
|
+
const guardValue = (value: UdlValue): UdlValue => {
|
|
3009
|
+
if (
|
|
3010
|
+
!("field" in value) ||
|
|
3011
|
+
!value.field.startsWith("subject.")
|
|
3012
|
+
)
|
|
3013
|
+
return value;
|
|
3014
|
+
const requirement = action.subject?.requirements.find(
|
|
3015
|
+
(item) => item.field.name === value.field.slice(8),
|
|
3016
|
+
);
|
|
3017
|
+
return {
|
|
3018
|
+
field: `subject.${requirement?.objectField ?? requirement?.field.name ?? value.field.slice(8)}`,
|
|
3019
|
+
};
|
|
3020
|
+
};
|
|
3021
|
+
const guardType =
|
|
3022
|
+
call.guard &&
|
|
3023
|
+
[call.guard.left, call.guard.right]
|
|
3024
|
+
.flatMap((value) =>
|
|
3025
|
+
"field" in value
|
|
3026
|
+
? [
|
|
3027
|
+
resolveField(
|
|
3028
|
+
document,
|
|
3029
|
+
inst,
|
|
3030
|
+
value.field,
|
|
3031
|
+
action.input,
|
|
3032
|
+
action,
|
|
3033
|
+
)?.type,
|
|
3034
|
+
]
|
|
3035
|
+
: [],
|
|
3036
|
+
)
|
|
3037
|
+
.find((type) => type === "money" || type === "date");
|
|
3038
|
+
const condition:
|
|
3039
|
+
| NonNullable<UdlSubjectRequirement["when"]>[number][number]
|
|
3040
|
+
| undefined = call.guard
|
|
3041
|
+
? {
|
|
3042
|
+
instrument: inst.id,
|
|
3043
|
+
action: actionName,
|
|
3044
|
+
guard: {
|
|
3045
|
+
...call.guard,
|
|
3046
|
+
left: guardValue(call.guard.left),
|
|
3047
|
+
right: guardValue(call.guard.right),
|
|
3048
|
+
},
|
|
3049
|
+
...(guardType === "money" || guardType === "date"
|
|
3050
|
+
? { valueType: guardType }
|
|
3051
|
+
: {}),
|
|
3052
|
+
}
|
|
3053
|
+
: undefined;
|
|
3054
|
+
const when = condition
|
|
3055
|
+
? (targetReq.when ?? [[]]).map((path) => [
|
|
3056
|
+
condition,
|
|
3057
|
+
...path.filter(
|
|
3058
|
+
(item) =>
|
|
3059
|
+
canonicalJson(item) !== canonicalJson(condition),
|
|
3060
|
+
),
|
|
3061
|
+
])
|
|
3062
|
+
: targetReq.when;
|
|
2982
3063
|
if (!existing) {
|
|
2983
|
-
const inherited = {
|
|
3064
|
+
const inherited: UdlSubjectRequirement = {
|
|
2984
3065
|
field: { ...targetReq.field, name: targetObjFieldName },
|
|
3066
|
+
...(when ? { when: when.map((path) => [...path]) } : {}),
|
|
2985
3067
|
};
|
|
2986
3068
|
action.subject.requirements.push(inherited);
|
|
2987
3069
|
requirementOrigins.set(
|
|
@@ -3002,6 +3084,23 @@ export function compile(
|
|
|
3002
3084
|
source: first.source,
|
|
3003
3085
|
related: [first, second],
|
|
3004
3086
|
});
|
|
3087
|
+
} else if (existing.when) {
|
|
3088
|
+
if (!when) {
|
|
3089
|
+
delete existing.when;
|
|
3090
|
+
changedInvocations = true;
|
|
3091
|
+
} else {
|
|
3092
|
+
for (const path of when) {
|
|
3093
|
+
if (
|
|
3094
|
+
!existing.when.some(
|
|
3095
|
+
(known) =>
|
|
3096
|
+
canonicalJson(known) === canonicalJson(path),
|
|
3097
|
+
)
|
|
3098
|
+
) {
|
|
3099
|
+
existing.when.push(path);
|
|
3100
|
+
changedInvocations = true;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3005
3104
|
}
|
|
3006
3105
|
}
|
|
3007
3106
|
}
|