@hyperscale0/hsx 5.3.0 → 5.4.1

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +46 -63
  3. package/bin/hsx.ts +0 -2
  4. package/dist/bin/hsx.js +0 -2
  5. package/dist/bin/hsx.js.map +1 -1
  6. package/dist/src/cli.d.ts +0 -2
  7. package/dist/src/cli.d.ts.map +1 -1
  8. package/dist/src/cli.js +9 -4
  9. package/dist/src/cli.js.map +1 -1
  10. package/dist/src/compile.d.ts.map +1 -1
  11. package/dist/src/compile.js +22 -9
  12. package/dist/src/compile.js.map +1 -1
  13. package/dist/src/examples-bundle.d.ts.map +1 -1
  14. package/dist/src/examples-bundle.js +16 -15
  15. package/dist/src/examples-bundle.js.map +1 -1
  16. package/dist/src/header-source.d.ts.map +1 -1
  17. package/dist/src/header-source.js +5 -0
  18. package/dist/src/header-source.js.map +1 -1
  19. package/dist/src/std-bundle.js +12 -12
  20. package/dist/src/std-bundle.js.map +1 -1
  21. package/dist/src/version.d.ts +1 -2
  22. package/dist/src/version.d.ts.map +1 -1
  23. package/dist/src/version.js +1 -2
  24. package/dist/src/version.js.map +1 -1
  25. package/docs/README.md +181 -30
  26. package/docs/examples.md +1 -0
  27. package/docs/headers.md +1 -1
  28. package/examples/cards.hsx +4 -0
  29. package/examples/collections.hsx +5 -2
  30. package/examples/escrow.hsx +2 -0
  31. package/examples/financing.hsx +3 -0
  32. package/examples/insurance.hsx +3 -0
  33. package/examples/lending.hsx +5 -0
  34. package/examples/library.hsx +3 -3
  35. package/examples/marketplace.hsx +6 -0
  36. package/examples/money.hsx +1 -0
  37. package/examples/rental-deposit.hsx +60 -0
  38. package/examples/repair-approval.hsx +1 -0
  39. package/examples/reporting.hsx +4 -1
  40. package/examples/savings.hsx +2 -0
  41. package/examples/serviced.hsx +5 -4
  42. package/examples/travel.hsx +3 -0
  43. package/examples/wallet.hsx +2 -0
  44. package/package.json +3 -3
  45. package/src/cli.ts +12 -6
  46. package/src/compile.ts +32 -9
  47. package/src/examples-bundle.ts +16 -15
  48. package/src/header-source.ts +10 -0
  49. package/src/std-bundle.ts +12 -12
  50. package/src/version.ts +1 -2
  51. package/std/cards.hsx +16 -0
  52. package/std/collections.hsx +15 -0
  53. package/std/escrow.hsx +20 -2
  54. package/std/financing.hsx +38 -3
  55. package/std/insurance.hsx +24 -5
  56. package/std/lending.hsx +18 -0
  57. package/std/marketplace.hsx +18 -0
  58. package/std/money.hsx +17 -0
  59. package/std/reporting.hsx +14 -0
  60. package/std/savings.hsx +32 -11
  61. package/std/travel.hsx +27 -4
  62. package/std/wallet.hsx +13 -0
@@ -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,uhIAAuhI,CAAC;IACtiI,CAAC,iBAAiB,EAAE,ooKAAooK,CAAC;IACzpK,CAAC,YAAY,EAAE,o8DAAo8D,CAAC;IACp9D,CAAC,eAAe,EAAE,g5vDAAg5vD,CAAC;IACn6vD,CAAC,eAAe,EAAE,+0GAA+0G,CAAC;IACl2G,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"}
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,2oKAA2oK,CAAC;IAC1pK,CAAC,iBAAiB,EAAE,+rMAA+rM,CAAC;IACptM,CAAC,YAAY,EAAE,6jGAA6jG,CAAC;IAC7kG,CAAC,eAAe,EAAE,0j1DAA0j1D,CAAC;IAC7k1D,CAAC,eAAe,EAAE,ouJAAouJ,CAAC;IACvvJ,CAAC,aAAa,EAAE,4hSAA4hS,CAAC;IAC7iS,CAAC,iBAAiB,EAAE,ilSAAilS,CAAC;IACtmS,CAAC,WAAW,EAAE,kjUAAkjU,CAAC;IACjkU,CAAC,eAAe,EAAE,649CAA649C,CAAC;IACh69C,CAAC,aAAa,EAAE,mtJAAmtJ,CAAC;IACpuJ,CAAC,YAAY,EAAE,ssPAAssP,CAAC;IACttP,CAAC,YAAY,EAAE,kxFAAkxF,CAAC;CACnyF,CAAC,CAAC"}
@@ -1,3 +1,2 @@
1
- export declare const HSX_VERSION = "5.3.0";
2
- export declare const HSX_TARGET_UDL_VERSION = 4;
1
+ export declare const HSX_VERSION = "5.4.1";
3
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAC;AACnC,eAAO,MAAM,sBAAsB,IAAI,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,UAAU,CAAC"}
@@ -1,3 +1,2 @@
1
- export const HSX_VERSION = "5.3.0";
2
- export const HSX_TARGET_UDL_VERSION = 4;
1
+ export const HSX_VERSION = "5.4.1";
3
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AACnC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC"}
package/docs/README.md CHANGED
@@ -1,18 +1,107 @@
1
- # HSX 5.0.1
1
+ # HSX
2
2
 
3
- A program declares business objects and financial instruments. Objects contain
4
- optional metadata. Attachments expose named actions against an object.
3
+ HSX composes a company's objects, agreements and actions into a typed program.
4
+ A repair approval can change state without moving money. A rental can collect a
5
+ deposit and refund it under declared rules. Headers supply reusable instruments;
6
+ you can also write an instrument with its own fields, lifecycle and actions.
7
+ The compiler emits UDL, the Universal Domain Language contract an executor reads.
8
+ It does not run the business or move money. This release supports SAR only.
9
+
10
+ ## First program
11
+
12
+ The package supplies the `hsx` executable. Save this as `lesson.hsx`:
5
13
 
6
14
  ```hsx
7
- program cars "Cars"
8
- use escrow
9
- object car "Cars" {
10
- fields { make: text, model: text, year: integer }
11
- columns: [make, model, year]
12
- attach sale = escrow.hold { payer: actor, payee: owner, expose fund as sell }
15
+ program tutoring "Tutoring studio"
16
+ use money
17
+ object lesson "Lesson" {
18
+ fields { student: text }
19
+ columns: [student]
20
+ attach payment = money.transfer {
21
+ payer: owner, payee: operator, amount: 150 SAR
22
+ expose create as book_lesson
23
+ expose pay as pay_for_lesson
24
+ expose cancel as cancel_lesson
25
+ }
13
26
  }
14
27
  ```
15
28
 
29
+ An object is the business record. An instrument holds an agreement's state and
30
+ money rules. An attachment connects the instrument to an object kind. A tunable
31
+ is a header parameter, such as `amount`, fixed by this program. An exposed action
32
+ is a public name for an instrument action. Exposing it does not grant permission.
33
+
34
+ ```sh
35
+ hsx check lesson.hsx
36
+ hsx build lesson.hsx --out lesson.udl.json
37
+ hsx cost lesson.hsx
38
+ ```
39
+
40
+ `check` exits zero without output on success. `build` writes canonical UDL;
41
+ without `--out` it prints to stdout. `cost` prints instruction counts, described
42
+ below. `format` prints formatted source or writes it with `--out`. `headers --json`
43
+ prints the header manifest. These commands have no `--strict` or `--catalog` option.
44
+ Exit 1 means invalid source; exit 2 means a command or file error. Diagnostics name
45
+ the source, line, column, code, problem and suggested fix. UDL validation keeps
46
+ its UDL diagnostic codes; binding errors can have names such as
47
+ `subject_party_unbound`. Read the message and fix, not just the code.
48
+
49
+ At runtime, create a lesson object first, then call `book_lesson` to create its
50
+ agreement. Call `pay_for_lesson` on that agreement to move 150 SAR from its bound
51
+ owner to the operator. Object creation alone creates no agreement. Object
52
+ metadata such as `student` is optional until an action requires it. Account IDs
53
+ and party IDs are not metadata supplied by the caller.
54
+
55
+ ## A rental deposit in one hour
56
+
57
+ Open [rental-deposit.hsx](../examples/rental-deposit.hsx). It is a complete source
58
+ file for a business renting equipment from the program operator. Copy it to
59
+ `rental.hsx` and run the same check, build and cost commands against that file.
60
+ No header import is needed because it declares its own instrument.
61
+
62
+ The terms are a 1,000 SAR deposit and a one-time 50 SAR late fee. The supplied
63
+ `dueAt` timestamp is the cutoff: a return before it gets the whole deposit back;
64
+ a return at or after it gets 950 SAR back. The operator receives the other 50 SAR.
65
+ The late fee is not a daily charge and is not `financing.late_charge`, which
66
+ requires a financing plan and an overdue installment.
67
+
68
+ | Step | Public action | Actor and required values | Money effect |
69
+ | ---------------------------- | ----------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------- |
70
+ | Create the object | Host object creation | Optional `equipment` metadata | None |
71
+ | Agree terms | `agree_rental` | Bound renter; `dueAt`, for example `2027-01-10T12:00:00+03:00`, must be in the future | Creates the agreement's held account |
72
+ | Fund before the cutoff | `pay_deposit` | Bound renter with 1,000 SAR available | Renter pays 1,000 SAR into held |
73
+ | Record an on-time return | `return_equipment` | Operator; `returnReference` metadata | Held pays 1,000 SAR to renter |
74
+ | Or record a late return | `return_equipment_late` | Operator; `returnReference` metadata | Held pays 50 SAR to operator and 950 SAR to renter |
75
+ | Cancel an unfunded agreement | `cancel_rental` | Bound renter, while pending | None |
76
+
77
+ Choose one return action. Both finish the agreement, so the fee and refund cannot
78
+ repeat. The late action calculates the refund from the held balance before either
79
+ move runs. Both paths empty the held account. `returnReference` records the
80
+ operator's return evidence; it does not verify the physical return. Time means
81
+ the executor's current time when the action runs, not a caller-supplied return date.
82
+ A late `due` permits the operator action; it does not schedule that action.
83
+
84
+ This sample binds a declared business named `renter` in Product configuration.
85
+ It uses fixed parties because this compiler rejects parameterized instruments
86
+ inside a program. Changing the renter binding is a Build configuration decision,
87
+ not a different party ID on each request. Use header party parameters bound to
88
+ `owner` or `actor` for per-object participants. Reusable custom headers require a
89
+ host-supplied `StandardLibrary`; `use` cannot load a local file through this CLI.
90
+
91
+ Inspect `objects` in the compiled UDL for the attachment and public names, then
92
+ its instrument for fields, transitions, actors, requirements and ordered moves.
93
+ The `hide deposit.*` lines remove public names from the unattached declaration;
94
+ the attachment exposes its own copy. `money.hold` and `escrow.hold` both refund
95
+ whole amounts, so neither implements this partial-refund policy by itself.
96
+
97
+ A local compile gives you a contract. Executing it needs a host with authenticated
98
+ parties, object actions, funded accounts and a saved Product Build. A Build is the
99
+ frozen contract and configuration used by its agreements. Existing agreements
100
+ keep that Build when the program changes. The package includes no local money
101
+ executor. Compilation cannot establish balances, permissions or provider readiness.
102
+
103
+ ## Objects, metadata and parties
104
+
16
105
  Every attached party parameter binds to `owner`, `actor`, `operator`, a declared
17
106
  business, or a declared staff party with a supported role. Configure declared
18
107
  business participants per Build. Declared person parties cannot bind attachments.
@@ -42,6 +131,8 @@ Creation accepts `{}`. Every normalized field, including adapter requirements,
42
131
  is optional at creation. Only a named action requires its subject metadata.
43
132
  Use `attach` inside the object block to configure its financial instruments.
44
133
 
134
+ ## Headers and tunables
135
+
45
136
  The header defines the available policies. The company chooses percentages, amounts, durations,
46
137
  parties and typed references. `funds: sale` links the stored records through the
47
138
  library's declared reference type. References can name a child, such as
@@ -49,8 +140,16 @@ library's declared reference type. References can name a child, such as
49
140
  objects of type T. `on: [plan_3, plan_6]` creates one late-charge or collections
50
141
  policy. Its references can point into either plan; selections can span both.
51
142
  A plain `ref<T>` accepts one object only.
143
+ Tunable constructors are `enum(choices)` and `integer(minimum, maximum)`.
144
+ Other constructors, including `party(business)` and bounded `money(...)`, are
145
+ rejected as tunable types. A default selector such as `payer: party = party(person)`
146
+ is different: its type is plain `party`, and the call chooses a default binding.
147
+ Use explicit `payer: owner` or `payer: actor` bindings when the participant comes
148
+ from the object or session.
52
149
  Imports are `use header`; there are no file imports, macros or executable strings.
53
150
 
151
+ ## Values and money
152
+
54
153
  Amounts use `750 SAR`; percentages use `2.5%`; durations use `48h`, `3d`, or `1w`.
55
154
  Dates use `2026-11-28` or a timestamp with an explicit offset. Money has at most two
56
155
  decimal places and percentages at most two. The compiler uses integer minor units
@@ -78,10 +177,6 @@ same book and key within the Product. Providers remain outside parties.
78
177
  A missing binding refuses before money moves. Reference paths such as
79
178
  `self.cover.insurer` use the referenced agreement's account.
80
179
 
81
- Insurance collection credits the premium to the operator, then moves the premium
82
- net of commission to the insurer account. Refunds reverse those two portions.
83
- Claims reserve from the cover's insurer account. These moves record ledger money;
84
- external confirmation still needs the boundary protocol.
85
180
  `account(buyer, claim, contra, "debt")` declares the borrower's claim contra account.
86
181
  Provider confirmation belongs on a reserved move with `boundary adapter`,
87
182
  followed by instruction-bound evidence and a post or void. External account
@@ -111,6 +206,14 @@ The four transfer instructions are create, reserve, post and void under
111
206
  reversed or voided status. Voided means a released reservation. Settled means
112
207
  provider confirmation, not an internal account balance change.
113
208
 
209
+ ## Authored instruments and actions
210
+
211
+ An action is the business instruction: its actor, state, requirements and effects
212
+ define when it is valid. The executor still checks those conditions on each call.
213
+ The four transfer instructions below that action are its money effects. There is
214
+ no general-purpose `instruction` declaration; `instruction` in an evidence clause
215
+ binds external evidence to a captured boundary instruction.
216
+
114
217
  For a new record type, declare `instrument name { fields { ... } lifecycle { ... }
115
218
  action create { ... } }`. Fields are `money`, `account of buyer`, `ref<order>`,
116
219
  `date`, `duration`, `text`, `integer`, `percent`, `boolean`, `enum(a, b)` or
@@ -142,7 +245,7 @@ maxAge 1d` requires a recent completed provider check.
142
245
  `capture`, `key` and `boundary` modifiers describe a move. `boundary` applies
143
246
  only to reservations. Fees settle with create moves, never reservations. Repeated clauses keep
144
247
  their declaration order. The JSON-like clause form remains accepted and lowers
145
- to the same [UDL clauses](../../udl/spec/README.md).
248
+ to the same [UDL clauses](https://github.com/hyperscale0/hyperscale-udl/blob/main/spec/README.md).
146
249
  They cannot add kernel instructions. `at(list, position)` reads a dated list;
147
250
  `aggregate(selection, "amount")` sums selected money; `ratio(amount, weight, total)`
148
251
  floors a weighted share. `records` declares child types. `invoke` can create a child
@@ -237,25 +340,26 @@ unsupported percentage semantics and repair. They do not change the tunable type
237
340
  UDL refusals retain their UDL codes. A terminal-money refusal includes the owned
238
341
  account and the prover's action path in related diagnostic information.
239
342
 
240
- Build with `hsx build company.hsx --out company.udl.json`; check with
241
- `hsx check company.hsx`; print instruction counts with `hsx cost company.hsx`.
242
- Print the compiler-owned object manifest with `hsx headers --json`.
243
- The compiler API is `compile(source)`. A valid result contains `artifacts.document`,
244
- `costManifest` and source origins. Instruction counts are not bank tariffs or a
245
- promise of provider execution cost.
343
+ ## Instruction cost
246
344
 
247
- The [header inventory](headers.md) is generated from the declarations. The
248
- [object example](../examples/library.hsx) shows authenticated role bindings.
249
- The [playground](../playground/index.html) compiles locally in the browser.
345
+ `hsx cost company.hsx` reports `transfers`, `accounts` and `invocations` per
346
+ instrument action after lowering. Transfers count declared move instructions,
347
+ including reserve, post and void. Accounts count self-owned accounts created by
348
+ `create`, not every party account. Bounded invocations include child costs up to
349
+ the selection limit, so a count is an upper bound rather than observed usage.
350
+ Zero-valued moves can cost fewer actual transfers than their static count.
250
351
 
251
- ## Financing attachments
352
+ The rental attachment's create declares one owned account, fund declares one
353
+ transfer, on-time return one, and late return two. These are counts, not SAR
354
+ prices. The executor's tariff supplies commercial prices and actual usage.
252
355
 
253
- Financing retains its authored borrower and portfolio limits. Bind every party
254
- parameter to a subject role, declared business or staff party with a supported
255
- role, and link the existing escrow and limits attachments.
256
- No limit is inferred from object metadata.
356
+ The compiler API is `compile(source)`. On success, `result.verdict` is `valid`
357
+ and `result.artifacts` contains `document`, `costManifest` and `originMap`.
358
+ On refusal, inspect `result.diagnostics`; no artifact is returned.
257
359
 
258
- See the complete [financing object example](../examples/library.hsx).
360
+ The [header inventory](headers.md) is generated from the declarations. The
361
+ [object example](../examples/library.hsx) shows authenticated role bindings.
362
+ The [playground](../playground/index.html) compiles locally in the browser.
259
363
 
260
364
  ## Standard-library behavior
261
365
 
@@ -271,14 +375,61 @@ adapters are bound, participants have funds, or the flow can finish.
271
375
  | `lending.distribution` | Cash distribution needs an eligible recorded settlement, `prepare_cash`, one share record per funded commitment, then `distribute_cash`. Attaching it moves nothing. |
272
376
  | `insurance.cover.slice` | `collect` credits premium net of commission to the insurer account. The tenant retains commission. `refund` returns both portions; claims reserve insurer funds. External settlement uses the boundary protocol. |
273
377
  | `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. |
274
- | `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. |
378
+ | `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 seats. |
275
379
  | `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. |
276
380
  | `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. |
277
381
  | `financing.late_charge` | `fine` is a fixed money amount, not a percentage of overdue principal. |
278
382
  | `cards.card` | `spend_limit` is a per-authorization ceiling, not a monthly aggregate. |
279
383
 
384
+ The financing, collections and reporting samples compose loan agreements; they
385
+ do not supply a complete public repayment path. Insurance and travel also need
386
+ premium-slice records and actions. Savings needs dated contribution records.
387
+ Create aliases in these samples cover the root attachments, not every child
388
+ record. Read each header's creation and lifecycle requirements before execution.
389
+
280
390
  The [lending sample](../examples/lending.hsx) exposes limit approvals, funding and
281
391
  commitment creation, and cash prepare/distribute actions. Its repayment and share
282
392
  child actions still lack a public execution path. It is a composition example,
283
393
  not a complete public repayment flow. Callers still need dates, agreement inputs,
284
394
  funded wallets, eligible settlements and the distribution's share records.
395
+
396
+ ## Checkout and reversals
397
+
398
+ For financed checkout, expose `installments.collect_down_payment` after `create`
399
+ and `sign`. It credits the linked pending escrow with the missing down payment.
400
+ It does not disburse principal or activate the plan. The later `disburse` action
401
+ uses that credit before collecting any remainder. Borrower-directed financing
402
+ has no checkout collection action. The plan and escrow must agree on payer and
403
+ price.
404
+
405
+ Expose escrow `cancel` to return money credited before full funding. Plan `void`
406
+ cancels the signed financing agreement but does not cancel its escrow. After
407
+ funding and delivery, the default return path needs three separate aliases for
408
+ `dispute`, `verify_return` and `refund`. The payee supplies return evidence and
409
+ returns the full price to the payer. `accept` pays the payee instead.
410
+ Marketplace order fulfillment alone does not execute any of these money actions.
411
+
412
+ Expose `late_charge.create` to create an assessment with matching plan, slice
413
+ and borrower. Only that existing assessment can reach clock-owned `assess` after
414
+ grace. A payment can collect part of a fine without settling the rest. A recovery
415
+ cost assessment is a separate record.
416
+
417
+ ## Separate payments and states
418
+
419
+ Bind `insurance.cover.broker` to the agency that receives commission. Collection
420
+ pays the insurer's net premium and the broker's commission as two moves. Refunds
421
+ reverse those same portions. The default broker is the program operator.
422
+ `claim.deny` accepts only `submitted`; after approval, `pay` or expiry consumes
423
+ the reservation.
424
+
425
+ A booking cancellation refunds only its own held cash. Expose `cover.cancel`
426
+ separately for eligible premium refunds before the paid period starts. Active
427
+ and expired premium periods are excluded. A card transaction needs a captured
428
+ authorization and its own record before refund, but no authorization-expiry wait.
429
+
430
+ A savings membership becomes `received` after its pot payout. Its contribution
431
+ records become `paid` independently. Future contributions can remain pending
432
+ after the member receives the pot; paying a contribution does not advance their
433
+ payout seat. Late contributions remain eligible while the membership is
434
+ `active` or `received` and the circle is `active`. These guards do not promise
435
+ scheduler retries or permit a payout larger than the available cash.
package/docs/examples.md CHANGED
@@ -13,6 +13,7 @@ Each file is an authored program. Object creation enters no agreement; an attach
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
+ | [Equipment rental](../examples/rental-deposit.hsx) | | A business rents equipment with a 1000 SAR deposit and a single 50 SAR late fee. On-time return refunds 1000 SAR; late return pays the operator 50 SAR and refunds 950 SAR. |
16
17
  | [Repair approvals](../examples/repair-approval.hsx) | | A repair shop records inspection and approval evidence before it can complete a job. |
17
18
  | [Equipment loan reports](../examples/reporting.hsx) | reporting, financing | An equipment lender keeps an ageing report over its financed purchases and recorded repayments. |
18
19
  | [Savings circle](../examples/savings.hsx) | savings | Six members contribute to a shared pot and receive it in their agreed calendar order. |
package/docs/headers.md CHANGED
@@ -29,7 +29,7 @@ Bind party parameters to owner, actor, operator or declared parties. Configure d
29
29
  | lending.round | `borrower: party`, `plan: ref<financing.installments> = object(financing.installments)`, `operator: party = programOperator`, `minimum_ticket: money = 100 SAR`, `investor_cap: percent = 20%`, `commitments: ref<lending.commitment> = object(lending.commitment)` |
30
30
  | lending.commitment | `round: ref<lending.round>`, `wallet: ref<wallet.balance>`, `investor: party = party(person)` |
31
31
  | lending.distribution | `round: ref<lending.round>`, `receipt: ref`, `commitments: ref<lending.commitment> = object(lending.commitment)`, `mode: enum(cash, loss) = cash`, `residual_to: party = programOperator`, `operator: party = programOperator`, `fee: percent = 1%`, `tax: percent = 15%` |
32
- | insurance.cover | `holder: party`, `adapter: text`, `commission: percent = 0%`, `covers: ref`, `premium: money = runtime` |
32
+ | insurance.cover | `holder: party`, `adapter: text`, `commission: percent = 0%`, `broker: party = programOperator`, `covers: ref`, `premium: money = runtime` |
33
33
  | insurance.claim | `cover: ref<insurance.cover>`, `inspector: party` |
34
34
  | collections.case | `on: ref<financing.installments>[]`, `agency: party`, `capital: party = programOperator`, `overdue: duration = 3d`, `fee: percent = 20%` |
35
35
  | collections.contact | `case: ref<collections.case>[]`, `agency: party`, `max_contacts: integer = 10`, `window: duration = 30d`, `contact_from: integer(0, 23) = 8`, `contact_until: integer(1, 23) = 20`, `timezone: text = "Asia/Riyadh"`; contact_from less_than contact_until |
@@ -7,23 +7,27 @@ object employee "Employee card" {
7
7
  fields { employeeName: text, department: text }
8
8
  columns: [employeeName, department]
9
9
  attach holder = cards.cardholder {
10
+ expose create as create_holder
10
11
  holder: owner
11
12
  expose activate as activate_employee
12
13
  expose suspend as suspend_employee
13
14
  }
14
15
  attach card = cards.card {
16
+ expose create as create_card
15
17
  holder: holder, spend_limit: 5000 SAR
16
18
  expose activate as activate_card
17
19
  expose freeze as freeze_card
18
20
  expose unfreeze as unfreeze_card
19
21
  }
20
22
  attach payment = cards.authorization {
23
+ expose create as create_payment
21
24
  card: card, merchant: supplier
22
25
  expose approve as approve_payment
23
26
  expose capture as capture_payment
24
27
  expose reverse as reverse_payment
25
28
  }
26
29
  attach receipt = cards.transaction {
30
+ expose create as create_receipt
27
31
  authorization: payment
28
32
  expose refund as refund_payment
29
33
  }
@@ -7,9 +7,10 @@ party agency: business
7
7
  object facility "Business loan" {
8
8
  fields { businessName: text, purpose: text, price: money }
9
9
  columns: [businessName, purpose, price]
10
- attach allowance = financing.limits { borrower: owner, per_borrower: 100000 SAR }
11
- attach budget = financing.portfolio_limit { limit: 2000000 SAR }
10
+ attach allowance = financing.limits { expose create as create_allowance, borrower: owner, per_borrower: 100000 SAR }
11
+ attach budget = financing.portfolio_limit { expose create as create_budget, limit: 2000000 SAR }
12
12
  attach loan = financing.installments {
13
+ expose create as create_loan
13
14
  borrower: owner, capital: operator
14
15
  months: 12, profit: 6%, disburse_to: borrower
15
16
  limits: allowance, portfolio: budget
@@ -17,12 +18,14 @@ object facility "Business loan" {
17
18
  expose disburse as release_loan
18
19
  }
19
20
  attach recovery = collections.case {
21
+ expose create as create_recovery
20
22
  on: loan, agency: agency
21
23
  expose assign as assign_case
22
24
  expose recover as recover_payment
23
25
  expose recall as recall_case
24
26
  }
25
27
  attach contact = collections.contact {
28
+ expose create as create_contact
26
29
  case: recovery, agency: agency
27
30
  }
28
31
  }
@@ -11,6 +11,7 @@ object device "Device" {
11
11
  }
12
12
  columns: [model, condition, price]
13
13
  attach sale = escrow.hold {
14
+ expose create as create_sale
14
15
  payer: actor, payee: owner
15
16
  expose fund as pay_for_device
16
17
  expose deliver as confirm_delivery
@@ -18,5 +19,6 @@ object device "Device" {
18
19
  expose dispute as report_problem
19
20
  expose verify_return as verify_return
20
21
  expose refund as refund_buyer
22
+ expose cancel as cancel_sale
21
23
  }
22
24
  }
@@ -6,14 +6,17 @@ object enrolment "Enrolment" {
6
6
  fields { student: text, course: text, price: money }
7
7
  columns: [student, course, price]
8
8
  attach allowance = financing.limits {
9
+ expose create as create_allowance
9
10
  borrower: owner, per_borrower: 12000 SAR
10
11
  expose approve as approve_student
11
12
  }
12
13
  attach budget = financing.portfolio_limit {
14
+ expose create as create_budget
13
15
  limit: 500000 SAR
14
16
  expose approve as approve_budget
15
17
  }
16
18
  attach tuition = financing.installments {
19
+ expose create as create_tuition
17
20
  borrower: owner, capital: operator
18
21
  months: 3, profit: 0%, disburse_to: borrower
19
22
  limits: allowance, portfolio: budget
@@ -8,16 +8,19 @@ object device "Insured device" {
8
8
  fields { model: text, serialNumber: text, purchaseDate: date }
9
9
  columns: [model, serialNumber, purchaseDate]
10
10
  attach purchase = money.transfer {
11
+ expose create as create_purchase
11
12
  payer: owner, payee: operator
12
13
  expose pay as pay_for_device
13
14
  }
14
15
  // Bind device_insurer to an ADL adapter before creating cover. Premiums net of commission go to its account.
15
16
  attach protection = insurance.cover {
17
+ expose create as create_protection
16
18
  holder: owner, adapter: "device_insurer", commission: 10%, covers: purchase
17
19
  expose activate as activate_cover
18
20
  expose cancel as cancel_cover
19
21
  }
20
22
  attach claim = insurance.claim {
23
+ expose create as create_claim
21
24
  cover: protection, inspector: inspector
22
25
  expose approve as approve_claim
23
26
  expose deny as deny_claim
@@ -9,14 +9,17 @@ object business "Business funding" {
9
9
  fields { businessName: text, purpose: text, price: money }
10
10
  columns: [businessName, purpose, price]
11
11
  attach allowance = financing.limits {
12
+ expose create as create_allowance
12
13
  borrower: owner, per_borrower: 100000 SAR
13
14
  expose approve as approve_borrower
14
15
  }
15
16
  attach budget = financing.portfolio_limit {
17
+ expose create as create_budget
16
18
  limit: 2000000 SAR
17
19
  expose approve as approve_budget
18
20
  }
19
21
  attach loan = financing.installments {
22
+ expose create as create_loan
20
23
  borrower: owner, capital: operator
21
24
  months: 12, profit: 6%, disburse_to: borrower
22
25
  limits: allowance, portfolio: budget
@@ -24,6 +27,7 @@ object business "Business funding" {
24
27
  expose disburse as release_loan
25
28
  }
26
29
  attach investor_balance = wallet.balance {
30
+ expose create as create_investor_balance
27
31
  holder: actor
28
32
  expose activate as open_wallet
29
33
  expose topup as add_funds
@@ -39,6 +43,7 @@ object business "Business funding" {
39
43
  expose withdraw as withdraw_investment
40
44
  }
41
45
  attach returns = lending.distribution {
46
+ expose create as create_returns
42
47
  round: funding, receipt: loan.settlement
43
48
  expose prepare_cash as prepare_returns
44
49
  expose distribute_cash as distribute_returns
@@ -4,9 +4,9 @@ use escrow
4
4
  use financing
5
5
  object car "Cars" {
6
6
  fields { make: text, model: text, year: integer }
7
- attach sale = escrow.hold { payer: actor, payee: owner, expose fund as sell }
8
- attach limits = financing.limits { borrower: actor, per_borrower: 60000 SAR }
9
- attach ceiling = financing.portfolio_limit { limit: 1500000 SAR }
7
+ attach sale = escrow.hold { expose create as create_sale, payer: actor, payee: owner, expose fund as sell }
8
+ attach limits = financing.limits { expose create as create_limits, borrower: actor, per_borrower: 60000 SAR }
9
+ attach ceiling = financing.portfolio_limit { expose create as create_ceiling, limit: 1500000 SAR }
10
10
  attach plan = financing.installments {
11
11
  borrower: actor, capital: operator, share: 25%
12
12
  months: 3, profit: 2.5%, funds: sale, limits: limits, portfolio: ceiling
@@ -7,20 +7,26 @@ object project "Project" {
7
7
  fields { brief: text, deliverable: text, price: money }
8
8
  columns: [brief, deliverable, price]
9
9
  attach offer = marketplace.listing {
10
+ expose create as create_offer
10
11
  seller: owner
11
12
  expose publish as publish_offer
12
13
  expose withdraw as withdraw_offer
13
14
  }
14
15
  attach order = marketplace.order {
16
+ expose create as create_order
15
17
  listing: offer, buyer: actor
16
18
  expose commit as place_order
17
19
  expose fulfill as finish_order
18
20
  }
19
21
  attach payment = escrow.hold {
22
+ expose create as create_payment
20
23
  payer: actor, payee: owner
21
24
  expose fund as fund_project
22
25
  expose deliver as submit_work
23
26
  expose accept as accept_work
24
27
  expose dispute as dispute_work
28
+ expose verify_return as verify_return
29
+ expose refund as refund_client
30
+ expose cancel as cancel_payment
25
31
  }
26
32
  }
@@ -10,6 +10,7 @@ object lesson "Lesson" {
10
10
  }
11
11
  columns: [student, tutor, startsAt]
12
12
  attach payment = money.transfer {
13
+ expose create as create_payment
13
14
  payer: actor, payee: operator, amount: 150 SAR
14
15
  expose pay as pay_for_lesson
15
16
  expose cancel as cancel_payment
@@ -0,0 +1,60 @@
1
+ // A business rents equipment with a 1000 SAR deposit and a single 50 SAR late fee.
2
+ // On-time return refunds 1000 SAR; late return pays the operator 50 SAR and refunds 950 SAR.
3
+ program equipment_rental "Equipment rental"
4
+ party renter: business
5
+
6
+ // Program-local instruments have fixed terms and parties, not tunable parameters.
7
+ instrument deposit {
8
+ summary: "Hold a rental deposit, then return it less any fixed late fee."
9
+ fields {
10
+ renter: account of renter
11
+ held: account of self
12
+ amount: money = 1000 SAR
13
+ lateFee: money = 50 SAR
14
+ dueAt: date
15
+ refund: money?
16
+ }
17
+ lifecycle { states: [pending, funded, returned, cancelled], initial: pending }
18
+ action create {
19
+ actor: { party: renter }
20
+ requires self.dueAt > self.now
21
+ }
22
+ action fund {
23
+ from: pending, to: funded, actor: { party: renter }
24
+ deadline: { at: self.dueAt }
25
+ moves self.amount from self.renter to self.held
26
+ }
27
+ action return_on_time {
28
+ from: funded, to: returned, actor: { party: programOperator }
29
+ deadline: { at: self.dueAt }
30
+ subject { returnReference: text }
31
+ moves self.held.balance from self.held to self.renter
32
+ }
33
+ action return_late {
34
+ from: funded, to: returned, actor: { party: programOperator }
35
+ due: { at: self.dueAt }
36
+ subject { returnReference: text }
37
+ calculate: [{ target: refund, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.lateFee }] }]
38
+ moves self.lateFee from self.held to programOperator
39
+ moves self.refund from self.held to self.renter
40
+ }
41
+ action cancel { from: pending, to: cancelled, actor: { party: renter } }
42
+ }
43
+
44
+ object rental "Equipment rental" {
45
+ fields { equipment: text }
46
+ columns: [equipment, dueAt]
47
+ attach security = deposit {
48
+ expose create as agree_rental
49
+ expose fund as pay_deposit
50
+ expose return_on_time as return_equipment
51
+ expose return_late as return_equipment_late
52
+ expose cancel as cancel_rental
53
+ }
54
+ }
55
+
56
+ hide deposit.create
57
+ hide deposit.fund
58
+ hide deposit.return_on_time
59
+ hide deposit.return_late
60
+ hide deposit.cancel
@@ -29,6 +29,7 @@ object repair "Repair" {
29
29
  fields { item: text, problem: text }
30
30
  columns: [item, problem]
31
31
  attach review = assessment {
32
+ expose create as create_review
32
33
  expose inspect as inspect_repair
33
34
  expose approve as approve_repair
34
35
  expose reject as reject_repair
@@ -7,19 +7,22 @@ object purchase "Equipment loan" {
7
7
  fields { businessName: text, equipment: text, price: money }
8
8
  columns: [businessName, equipment, price]
9
9
  attach allowance = financing.limits {
10
+ expose create as create_allowance
10
11
  borrower: owner, per_borrower: 200000 SAR
11
12
  expose approve as approve_business
12
13
  }
13
14
  attach budget = financing.portfolio_limit {
15
+ expose create as create_budget
14
16
  limit: 5000000 SAR
15
17
  expose approve as approve_budget
16
18
  }
17
19
  attach loan = financing.installments {
20
+ expose create as create_loan
18
21
  borrower: owner, capital: operator
19
22
  months: 12, profit: 6%, disburse_to: borrower
20
23
  limits: allowance, portfolio: budget
21
24
  expose sign as sign_purchase_loan
22
25
  expose disburse as fund_purchase
23
26
  }
24
- attach reports = reporting.portfolio { on: loan }
27
+ attach reports = reporting.portfolio { expose create as create_reports, on: loan }
25
28
  }
@@ -6,11 +6,13 @@ object group "Savings group" {
6
6
  fields { groupName: text, purpose: text }
7
7
  columns: [groupName, purpose]
8
8
  attach circle = savings.circle {
9
+ expose create as create_circle
9
10
  contribution: 500 SAR, members: 6, starts: 2027-01-01
10
11
  expose disband as disband_circle
11
12
  expose close as close_circle
12
13
  }
13
14
  attach membership = savings.membership {
15
+ expose create as create_membership
14
16
  circle: circle, member: actor
15
17
  expose activate as confirm_membership
16
18
  expose withdraw as leave_circle