@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.
@@ -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,mnKAAmnK,CAAC;IACxoK,CAAC,YAAY,EAAE,s5DAAs5D,CAAC;IACt6D,CAAC,eAAe,EAAE,o5tDAAo5tD,CAAC;IACv6tD,CAAC,eAAe,EAAE,g7FAAg7F,CAAC;IACn8F,CAAC,aAAa,EAAE,wjPAAwjP,CAAC;IACzkP,CAAC,iBAAiB,EAAE,gxPAAgxP,CAAC;IACryP,CAAC,WAAW,EAAE,64QAA64Q,CAAC;IAC55Q,CAAC,eAAe,EAAE,gs5CAAgs5C,CAAC;IACnt5C,CAAC,aAAa,EAAE,yrGAAyrG,CAAC;IAC1sG,CAAC,YAAY,EAAE,4wLAA4wL,CAAC;IAC5xL,CAAC,YAAY,EAAE,m6DAAm6D,CAAC;CACp7D,CAAC,CAAC"}
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"}
@@ -1,3 +1,3 @@
1
- export declare const HSX_VERSION = "4.3.0";
1
+ export declare const HSX_VERSION = "5.0.0";
2
2
  export declare const HSX_TARGET_UDL_VERSION = 4;
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
- export const HSX_VERSION = "4.3.0";
1
+ export const HSX_VERSION = "5.0.0";
2
2
  export const HSX_TARGET_UDL_VERSION = 4;
3
3
  //# sourceMappingURL=version.js.map
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`, or `operator`.
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 and an unavailable action.
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 and link the existing escrow and limits attachments.
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 a complete authored program. Instrument creation stays internal; exposed actions operate on its agreements.
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 | Investors fund a small business loan from their wallets and share its recorded repayments. |
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. Expose the chosen business action; instrument creation stays internal.
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, 366)`, `starts: date`, `memberships: ref<savings.membership> = object(savings.membership)` |
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 |
@@ -1,4 +1,5 @@
1
- // Investors fund a small business loan from their wallets and share its recorded repayments.
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 { borrower: owner, per_borrower: 100000 SAR }
11
- attach budget = financing.portfolio_limit { limit: 2000000 SAR }
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": "4.3.0",
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.1.0",
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": "c02b50cd28968db4b64bededd947e74f62fe5a4b"
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
+ }