@hyperscale0/hsx 4.3.0 → 5.0.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 +17 -0
- package/README.md +5 -4
- package/dist/src/binding-contract.d.ts +22 -0
- package/dist/src/binding-contract.d.ts.map +1 -0
- package/dist/src/binding-contract.js +74 -0
- package/dist/src/binding-contract.js.map +1 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +340 -112
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.d.ts.map +1 -1
- package/dist/src/examples-bundle.js +2 -1
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/headers.d.ts +13 -0
- package/dist/src/headers.d.ts.map +1 -1
- package/dist/src/headers.js +3 -0
- package/dist/src/headers.js.map +1 -1
- package/dist/src/std-bundle.js +8 -8
- 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 +78 -3
- package/docs/examples.md +3 -2
- package/docs/headers.md +2 -2
- package/examples/lending.hsx +14 -3
- package/examples/repair-approval.hsx +43 -0
- package/package.json +3 -3
- package/src/binding-contract.ts +104 -0
- package/src/compile.ts +476 -171
- package/src/examples-bundle.ts +2 -1
- package/src/headers.ts +8 -0
- package/src/std-bundle.ts +8 -8
- package/src/version.ts +1 -1
- package/std/collections.hsx +1 -1
- package/std/escrow.hsx +1 -1
- package/std/financing.hsx +37 -23
- package/std/lending.hsx +6 -6
- package/std/marketplace.hsx +3 -3
- package/std/money.hsx +6 -6
- package/std/savings.hsx +1 -1
- package/std/travel.hsx +7 -7
|
@@ -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,i8HAAi8H,CAAC;IACh9H,CAAC,iBAAiB,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,i8HAAi8H,CAAC;IACh9H,CAAC,iBAAiB,EAAE,ooKAAooK,CAAC;IACzpK,CAAC,YAAY,EAAE,o8DAAo8D,CAAC;IACp9D,CAAC,eAAe,EAAE,m5vDAAm5vD,CAAC;IACt6vD,CAAC,eAAe,EAAE,g7FAAg7F,CAAC;IACn8F,CAAC,aAAa,EAAE,8pPAA8pP,CAAC;IAC/qP,CAAC,iBAAiB,EAAE,m0PAAm0P,CAAC;IACx1P,CAAC,WAAW,EAAE,ytRAAytR,CAAC;IACxuR,CAAC,eAAe,EAAE,gs5CAAgs5C,CAAC;IACnt5C,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
|
@@ -13,7 +13,9 @@ object car "Cars" {
|
|
|
13
13
|
}
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Every attached party parameter binds to `owner`, `actor`,
|
|
16
|
+
Every attached party parameter binds to `owner`, `actor`, `operator`, a declared
|
|
17
|
+
business, or a declared staff party with a supported role. Configure declared
|
|
18
|
+
business participants per Build. Declared person parties cannot bind attachments.
|
|
17
19
|
`owner` is the object's resolved customer or business, `actor` is the authenticated
|
|
18
20
|
caller, and `operator` is the program operator. A parameter with one of these
|
|
19
21
|
names binds by name. Other party parameters require an explicit binding.
|
|
@@ -24,7 +26,10 @@ Object `fields` are authored metadata. `columns` names up to eight normalized
|
|
|
24
26
|
fields. An action's `subject { price: money }` declares its metadata requirement;
|
|
25
27
|
`subject { adapter: verification }` inherits a selected ADL declaration. The
|
|
26
28
|
compiler freezes the adapter identity, digest and requirements. An unavailable
|
|
27
|
-
declaration leaves a null snapshot
|
|
29
|
+
declaration leaves a null snapshot in compiled output. Product creation and
|
|
30
|
+
recomposition reject unresolved or changed declarations during Build admission.
|
|
31
|
+
Execution checks the frozen declaration too; later adapter unavailability can
|
|
32
|
+
block actions on an already saved Build.
|
|
28
33
|
|
|
29
34
|
An attachment can use `rename { price: salePrice }` to separate requirements with
|
|
30
35
|
different meanings. Matching names must match types and constraints. The compiler
|
|
@@ -70,6 +75,22 @@ Outstanding debt is an account balance. The library pairs cash repayment with
|
|
|
70
75
|
claim reduction and represents receipts as immutable child records. Cash and loss
|
|
71
76
|
shares round down; the declared residual account receives leftover minor units.
|
|
72
77
|
|
|
78
|
+
Money moves require a positive amount by default. An action may declare
|
|
79
|
+
`allowZero: true` when a calculated piece can be zero, such as an exhausted
|
|
80
|
+
repayment slice, a floored fee or a rounding remainder. A zero create move in
|
|
81
|
+
that action posts no transfer and captures no receipt. The permission belongs
|
|
82
|
+
to that action only and does not pass to invoked actions. Reserve, post and
|
|
83
|
+
void still require real reservation receipts. Keep a positive requirement on
|
|
84
|
+
an authored total when only its calculated pieces may be zero.
|
|
85
|
+
|
|
86
|
+
```hsx
|
|
87
|
+
action collect {
|
|
88
|
+
from: pending, to: paid
|
|
89
|
+
allowZero: true
|
|
90
|
+
moves self.calculatedPiece from payer to payee
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
73
94
|
The four transfer instructions are create, reserve, post and void under
|
|
74
95
|
`internal_transfer`. A captured transfer exposes reserved, posted, settled,
|
|
75
96
|
reversed or voided status. Voided means a released reservation. Settled means
|
|
@@ -173,6 +194,33 @@ requires a valid hours interval. `at_most` and `greater_than` are also supported
|
|
|
173
194
|
A refusal names the tunable. Empty `all(type)` selections lower to zero aggregates
|
|
174
195
|
or no invocations, so a plan does not require a late-charge object.
|
|
175
196
|
|
|
197
|
+
Headers can declare conditional bindings and parameter policy diagnostics:
|
|
198
|
+
|
|
199
|
+
```hsx
|
|
200
|
+
dependencies {
|
|
201
|
+
funds {
|
|
202
|
+
selector: destination, is: held
|
|
203
|
+
message: "Attachment `{attachment}` needs a funds binding."
|
|
204
|
+
fix: "Bind funds to a hold attachment."
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
parameterDiagnostics {
|
|
208
|
+
amount {
|
|
209
|
+
accepts: "a fixed amount"
|
|
210
|
+
percentage: "a percentage-based charge"
|
|
211
|
+
fix: "Author a rate calculation when the policy varies with a base amount."
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Each dependency names a declared tunable, an enum selector and one of its choices.
|
|
217
|
+
The compiler checks selected dependencies before lowering and the manifest retains
|
|
218
|
+
them. `{attachment}` in the message expands to the authored attachment name.
|
|
219
|
+
Parameter diagnostics name a declared tunable and explain its accepted value,
|
|
220
|
+
unsupported percentage semantics and repair. They do not change the tunable type.
|
|
221
|
+
UDL refusals retain their UDL codes. A terminal-money refusal includes the owned
|
|
222
|
+
account and the prover's action path in related diagnostic information.
|
|
223
|
+
|
|
176
224
|
Build with `hsx build company.hsx --out company.udl.json`; check with
|
|
177
225
|
`hsx check company.hsx`; print instruction counts with `hsx cost company.hsx`.
|
|
178
226
|
Print the compiler-owned object manifest with `hsx headers --json`.
|
|
@@ -187,7 +235,34 @@ The [playground](../playground/index.html) compiles locally in the browser.
|
|
|
187
235
|
## Financing attachments
|
|
188
236
|
|
|
189
237
|
Financing retains its authored borrower and portfolio limits. Bind every party
|
|
190
|
-
parameter to a subject role
|
|
238
|
+
parameter to a subject role, declared business or staff party with a supported
|
|
239
|
+
role, and link the existing escrow and limits attachments.
|
|
191
240
|
No limit is inferred from object metadata.
|
|
192
241
|
|
|
193
242
|
See the complete [financing object example](../examples/library.hsx).
|
|
243
|
+
|
|
244
|
+
## Standard-library behavior
|
|
245
|
+
|
|
246
|
+
Read the instrument's states, time gates and moves before promising a money
|
|
247
|
+
outcome. A compiler pass does not prove that required actions are exposed,
|
|
248
|
+
adapters are bound, participants have funds, or the flow can finish.
|
|
249
|
+
|
|
250
|
+
| Instrument | Behavior |
|
|
251
|
+
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
252
|
+
| `financing.installments` | `share` allocates collected profit to a cash payable owned by `capital`. It does not itself pay operator income. |
|
|
253
|
+
| `financing.limits` | `per_borrower` caps outstanding principal. Both borrower and portfolio limits must be approved before disbursement; attaching them does not approve them. |
|
|
254
|
+
| `lending.round` | The target is the linked plan's principal. Closing requires commitments and held funds to equal that amount. |
|
|
255
|
+
| `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` | `commission` is calculated, but `collect` sends the whole premium to `programOperator`; that action makes no broker commission transfer. |
|
|
257
|
+
| `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
|
+
| `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
|
+
| `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. |
|
|
260
|
+
| `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. |
|
|
261
|
+
| `financing.late_charge` | `fine` is a fixed money amount, not a percentage of overdue principal. |
|
|
262
|
+
| `cards.card` | `spend_limit` is a per-authorization ceiling, not a monthly aggregate. |
|
|
263
|
+
|
|
264
|
+
The [lending sample](../examples/lending.hsx) exposes limit approvals, funding and
|
|
265
|
+
commitment creation, and cash prepare/distribute actions. Its repayment and share
|
|
266
|
+
child actions still lack a public execution path. It is a composition example,
|
|
267
|
+
not a complete public repayment flow. Callers still need dates, agreement inputs,
|
|
268
|
+
funded wallets, eligible settlements and the distribution's share records.
|
package/docs/examples.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sample programs
|
|
2
2
|
|
|
3
|
-
Each file is
|
|
3
|
+
Each file is an authored program. Object creation enters no agreement; an attachment may expose its instrument create action by name. Compilation alone does not prove a complete business flow.
|
|
4
4
|
|
|
5
5
|
| Program | Headers | Business |
|
|
6
6
|
| --- | --- | --- |
|
|
@@ -9,10 +9,11 @@ Each file is a complete authored program. Instrument creation stays internal; ex
|
|
|
9
9
|
| [Used device sales](../examples/escrow.hsx) | escrow | Buyers fund a used device purchase, then accept delivery or raise a dispute before release. |
|
|
10
10
|
| [Tuition instalments](../examples/financing.hsx) | financing | A tuition lender funds a student's fees over three interest-free instalments with borrowing limits. |
|
|
11
11
|
| [Device insurance](../examples/insurance.hsx) | insurance, money | A device retailer collects the purchase price and attaches cover with a claim review process. |
|
|
12
|
-
| [Community business lending](../examples/lending.hsx) | lending, financing, wallet |
|
|
12
|
+
| [Community business lending](../examples/lending.hsx) | lending, financing, wallet | A lending composition with limit approvals, wallet commitments and cash distribution transitions. Repayment and distribution-share child actions still need an execution path; this is not a complete public repayment flow. |
|
|
13
13
|
| [Financed car sales](../examples/library.hsx) | escrow, financing | A minimal financed car sale connects escrow to a three-instalment plan and lending limits. |
|
|
14
14
|
| [Freelance marketplace](../examples/marketplace.hsx) | marketplace, escrow | Freelancers publish offers and clients place orders with funds held until work is accepted. |
|
|
15
15
|
| [Tutoring studio](../examples/money.hsx) | money | A tutoring studio collects a fixed fee for each lesson and can cancel unpaid bookings. |
|
|
16
|
+
| [Repair approvals](../examples/repair-approval.hsx) | | A repair shop records inspection and approval evidence before it can complete a job. |
|
|
16
17
|
| [Equipment loan reports](../examples/reporting.hsx) | reporting, financing | An equipment lender keeps an ageing report over its financed purchases and recorded repayments. |
|
|
17
18
|
| [Savings circle](../examples/savings.hsx) | savings | Six members contribute to a shared pot and receive it in their agreed calendar order. |
|
|
18
19
|
| [Car financing](../examples/serviced.hsx) | escrow, financing, collections | A car finance company services loans with late charges, payment reminders and early repayment. |
|
package/docs/headers.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Header inventory
|
|
2
2
|
|
|
3
3
|
Attach library instruments inside authored object kinds. Each program selects policies through typed tunables.
|
|
4
|
-
Bind party parameters to owner, actor, operator or declared parties. Configure declared businesses per Build. The authoringTemplate source is an attach fragment for an object block.
|
|
4
|
+
Bind party parameters to owner, actor, operator or declared parties. Configure declared businesses per Build. The authoringTemplate source is an attach fragment for an object block. An attachment may expose create by name; clock and parent actions remain internal.
|
|
5
5
|
`ref<T>[]` accepts one reference or up to 16 references as one policy.
|
|
6
6
|
|
|
7
7
|
| Instrument | Tunables |
|
|
@@ -41,6 +41,6 @@ Bind party parameters to owner, actor, operator or declared parties. Configure d
|
|
|
41
41
|
| cards.authorization | `card: ref<cards.card>`, `merchant: party`, `issuer: party = programOperator` |
|
|
42
42
|
| cards.transaction | `authorization: ref<cards.authorization>` |
|
|
43
43
|
| cards.dispute | `transaction: ref<cards.transaction>`, `issuer: party = programOperator`, `within: duration = 90d` |
|
|
44
|
-
| savings.circle | `contribution: money`, `members: integer(1,
|
|
44
|
+
| savings.circle | `contribution: money`, `members: integer(1, 60)`, `starts: date`, `memberships: ref<savings.membership> = object(savings.membership)` |
|
|
45
45
|
| savings.membership | `circle: ref<savings.circle>`, `member: party` |
|
|
46
46
|
| reporting.portfolio | `on: ref<financing.installments>[]`, `default_days: integer(1, 3650) = 90`, `retention_years: integer(1, 100) = 10`, `aging_first_days: integer(1, 3650) = 30`, `aging_second_days: integer(1, 3650) = 60`, `aging_third_days: integer(1, 3650) = 90`, `ratio_scale: integer(1, 1000000) = 10000`, `ratio_rounding: enum(floor, halfUp) = floor`, `lock_wait_ms: integer(1, 2000) = 2000`, `capture_ms: integer(1, 10000) = 10000`, `max_rows: integer(1, 100000) = 10000`, `max_join_rows: integer(1, 1000000) = 100000`, `max_bytes: integer(1024, 16777216) = 8388608`; aging_first_days less_than aging_second_days, aging_second_days less_than aging_third_days |
|
package/examples/lending.hsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
//
|
|
1
|
+
// A lending composition with limit approvals, wallet commitments and cash distribution transitions.
|
|
2
|
+
// Repayment and distribution-share child actions still need an execution path; this is not a complete public repayment flow.
|
|
2
3
|
program community_business_lending "Community business lending"
|
|
3
4
|
use lending
|
|
4
5
|
use financing
|
|
@@ -7,8 +8,14 @@ use wallet
|
|
|
7
8
|
object business "Business funding" {
|
|
8
9
|
fields { businessName: text, purpose: text, price: money }
|
|
9
10
|
columns: [businessName, purpose, price]
|
|
10
|
-
attach allowance = financing.limits {
|
|
11
|
-
|
|
11
|
+
attach allowance = financing.limits {
|
|
12
|
+
borrower: owner, per_borrower: 100000 SAR
|
|
13
|
+
expose approve as approve_borrower
|
|
14
|
+
}
|
|
15
|
+
attach budget = financing.portfolio_limit {
|
|
16
|
+
limit: 2000000 SAR
|
|
17
|
+
expose approve as approve_budget
|
|
18
|
+
}
|
|
12
19
|
attach loan = financing.installments {
|
|
13
20
|
borrower: owner, capital: operator
|
|
14
21
|
months: 12, profit: 6%, disburse_to: borrower
|
|
@@ -23,13 +30,17 @@ object business "Business funding" {
|
|
|
23
30
|
}
|
|
24
31
|
attach funding = lending.round {
|
|
25
32
|
borrower: owner, plan: loan
|
|
33
|
+
expose create as open_funding
|
|
26
34
|
expose close as close_funding
|
|
27
35
|
}
|
|
28
36
|
attach investment = lending.commitment {
|
|
29
37
|
round: funding, wallet: investor_balance, investor: actor
|
|
38
|
+
expose create as invest
|
|
30
39
|
expose withdraw as withdraw_investment
|
|
31
40
|
}
|
|
32
41
|
attach returns = lending.distribution {
|
|
33
42
|
round: funding, receipt: loan.settlement
|
|
43
|
+
expose prepare_cash as prepare_returns
|
|
44
|
+
expose distribute_cash as distribute_returns
|
|
34
45
|
}
|
|
35
46
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// A repair shop records inspection and approval evidence before it can complete a job.
|
|
2
|
+
program repair_shop "Repair approvals"
|
|
3
|
+
|
|
4
|
+
instrument assessment {
|
|
5
|
+
summary: "Inspect the repair, collect approval, then record completion."
|
|
6
|
+
fields { inspectedAt: date?, approvedAt: date? }
|
|
7
|
+
lifecycle { states: [submitted, inspected, approved, completed, rejected], initial: submitted }
|
|
8
|
+
action create {}
|
|
9
|
+
action inspect {
|
|
10
|
+
from: submitted, to: inspected, actor: { party: programOperator }
|
|
11
|
+
subject { inspectionNotes: text }
|
|
12
|
+
set: { inspectedAt: { field: self.now } }
|
|
13
|
+
}
|
|
14
|
+
action approve {
|
|
15
|
+
from: inspected, to: approved, actor: { party: programOperator }
|
|
16
|
+
subject { approvalReference: text }
|
|
17
|
+
set: { approvedAt: { field: self.now } }
|
|
18
|
+
}
|
|
19
|
+
action reject {
|
|
20
|
+
from: inspected, to: rejected, actor: { party: programOperator }
|
|
21
|
+
}
|
|
22
|
+
action complete {
|
|
23
|
+
from: approved, to: completed, actor: { party: programOperator }
|
|
24
|
+
subject { completionNotes: text }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
object repair "Repair" {
|
|
29
|
+
fields { item: text, problem: text }
|
|
30
|
+
columns: [item, problem]
|
|
31
|
+
attach review = assessment {
|
|
32
|
+
expose inspect as inspect_repair
|
|
33
|
+
expose approve as approve_repair
|
|
34
|
+
expose reject as reject_repair
|
|
35
|
+
expose complete as complete_repair
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
hide assessment.create
|
|
40
|
+
hide assessment.inspect
|
|
41
|
+
hide assessment.approve
|
|
42
|
+
hide assessment.reject
|
|
43
|
+
hide assessment.complete
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperscale0/hsx",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.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.2.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": "2c4281aeb2cc0a10e13a91e8d67ae7abf608680f"
|
|
102
102
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Entry, Expr, InstrumentDecl } from "./ast.ts";
|
|
2
|
+
|
|
3
|
+
export class BindingContractError extends Error {
|
|
4
|
+
constructor(
|
|
5
|
+
readonly entry: Entry,
|
|
6
|
+
message: string,
|
|
7
|
+
) {
|
|
8
|
+
super(message);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function rows(value: Expr): Entry[] {
|
|
13
|
+
if (value.kind === "block") return value.entries;
|
|
14
|
+
throw new BindingContractError(
|
|
15
|
+
{ key: "contract", value, span: value.span },
|
|
16
|
+
"Binding contract needs a block.",
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function properties(entry: Entry, allowed: string[]): Map<string, string> {
|
|
20
|
+
const result = new Map<string, string>();
|
|
21
|
+
for (const slot of rows(entry.value)) {
|
|
22
|
+
if (
|
|
23
|
+
!allowed.includes(slot.key) ||
|
|
24
|
+
result.has(slot.key) ||
|
|
25
|
+
(slot.value.kind !== "text" && slot.value.kind !== "name")
|
|
26
|
+
)
|
|
27
|
+
throw new BindingContractError(
|
|
28
|
+
slot,
|
|
29
|
+
"Invalid binding contract property.",
|
|
30
|
+
);
|
|
31
|
+
result.set(slot.key, slot.value.value);
|
|
32
|
+
}
|
|
33
|
+
if (allowed.some((key) => !result.get(key)))
|
|
34
|
+
throw new BindingContractError(
|
|
35
|
+
entry,
|
|
36
|
+
`Binding contract needs ${allowed.join(", ")}.`,
|
|
37
|
+
);
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
function contractEntries(decl: InstrumentDecl, name: string): Entry[] {
|
|
41
|
+
const blocks = decl.body.entries.filter((entry) => entry.key === name);
|
|
42
|
+
if (blocks.length > 1)
|
|
43
|
+
throw new BindingContractError(blocks[1]!, `Duplicate ${name}.`);
|
|
44
|
+
const entries = blocks[0] ? rows(blocks[0].value) : [];
|
|
45
|
+
const seen = new Set<string>();
|
|
46
|
+
for (const entry of entries) {
|
|
47
|
+
if (
|
|
48
|
+
seen.has(entry.key) ||
|
|
49
|
+
!decl.parameters.some((parameter) => parameter.key === entry.key)
|
|
50
|
+
)
|
|
51
|
+
throw new BindingContractError(
|
|
52
|
+
entry,
|
|
53
|
+
"Binding contract must name each declared tunable once.",
|
|
54
|
+
);
|
|
55
|
+
seen.add(entry.key);
|
|
56
|
+
}
|
|
57
|
+
return entries;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Conditional requirements are header data, independent of module names. */
|
|
61
|
+
export function bindingDependencies(decl: InstrumentDecl) {
|
|
62
|
+
return contractEntries(decl, "dependencies").map((entry) => {
|
|
63
|
+
const values = properties(entry, ["selector", "is", "message", "fix"]);
|
|
64
|
+
const selector = decl.parameters.find(
|
|
65
|
+
(parameter) => parameter.key === values.get("selector"),
|
|
66
|
+
);
|
|
67
|
+
const type =
|
|
68
|
+
selector?.value.kind === "default"
|
|
69
|
+
? selector.value.type
|
|
70
|
+
: selector?.value;
|
|
71
|
+
if (
|
|
72
|
+
type?.kind !== "call" ||
|
|
73
|
+
type.name !== "enum" ||
|
|
74
|
+
!type.args.some(
|
|
75
|
+
(arg) => arg.kind === "name" && arg.value === values.get("is"),
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
throw new BindingContractError(
|
|
79
|
+
entry,
|
|
80
|
+
"Dependency must select a declared enum choice.",
|
|
81
|
+
);
|
|
82
|
+
return {
|
|
83
|
+
binding: entry.key,
|
|
84
|
+
when: values.get("selector")!,
|
|
85
|
+
is: values.get("is")!,
|
|
86
|
+
message: values.get("message")!,
|
|
87
|
+
fix: values.get("fix")!,
|
|
88
|
+
span: entry.span,
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A header explains why a typed parameter cannot encode a proposed policy. */
|
|
94
|
+
export function parameterDiagnostics(decl: InstrumentDecl) {
|
|
95
|
+
return contractEntries(decl, "parameterDiagnostics").map((entry) => {
|
|
96
|
+
const values = properties(entry, ["accepts", "percentage", "fix"]);
|
|
97
|
+
return {
|
|
98
|
+
parameter: entry.key,
|
|
99
|
+
accepts: values.get("accepts")!,
|
|
100
|
+
percentage: values.get("percentage")!,
|
|
101
|
+
fix: values.get("fix")!,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|