@hyperscale0/hsx 3.3.0 → 4.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/dist/src/ast.d.ts +17 -2
  4. package/dist/src/ast.d.ts.map +1 -1
  5. package/dist/src/ast.js.map +1 -1
  6. package/dist/src/cli.js +1 -1
  7. package/dist/src/compile.d.ts +6 -0
  8. package/dist/src/compile.d.ts.map +1 -1
  9. package/dist/src/compile.js +1162 -101
  10. package/dist/src/compile.js.map +1 -1
  11. package/dist/src/headers.d.ts +2 -2
  12. package/dist/src/headers.d.ts.map +1 -1
  13. package/dist/src/headers.js +2 -3
  14. package/dist/src/headers.js.map +1 -1
  15. package/dist/src/index.d.ts +3 -2
  16. package/dist/src/index.d.ts.map +1 -1
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/lex.d.ts +1 -1
  19. package/dist/src/lex.d.ts.map +1 -1
  20. package/dist/src/lex.js +5 -0
  21. package/dist/src/lex.js.map +1 -1
  22. package/dist/src/parse.js +87 -3
  23. package/dist/src/parse.js.map +1 -1
  24. package/dist/src/std-bundle.d.ts.map +1 -1
  25. package/dist/src/std-bundle.js +8 -9
  26. package/dist/src/std-bundle.js.map +1 -1
  27. package/dist/src/version.d.ts +2 -2
  28. package/dist/src/version.js +2 -2
  29. package/docs/README.md +45 -27
  30. package/docs/headers.md +43 -44
  31. package/examples/cost-table.json +8 -324
  32. package/examples/library.hsx +12 -64
  33. package/package.json +7 -5
  34. package/src/ast.ts +11 -1
  35. package/src/cli.ts +1 -1
  36. package/src/compile.ts +1588 -118
  37. package/src/headers.ts +2 -3
  38. package/src/index.ts +12 -1
  39. package/src/lex.ts +5 -0
  40. package/src/parse.ts +84 -3
  41. package/src/std-bundle.ts +8 -9
  42. package/src/version.ts +2 -2
  43. package/std/approvals.hsx +3 -3
  44. package/std/escrow.hsx +16 -14
  45. package/std/financing.hsx +59 -17
  46. package/std/insurance.hsx +4 -5
  47. package/std/lending.hsx +7 -8
  48. package/std/marketplace.hsx +2 -5
  49. package/std/money.hsx +15 -49
  50. package/std/travel.hsx +5 -6
  51. package/std/vehicles.hsx +0 -34
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
- export const HSX_VERSION = "3.3.0";
2
- export const HSX_TARGET_UDL_VERSION = 3;
1
+ export const HSX_VERSION = "4.0.0";
2
+ export const HSX_TARGET_UDL_VERSION = 4;
package/std/approvals.hsx CHANGED
@@ -1,17 +1,17 @@
1
1
  header approvals
2
2
 
3
- instrument decision(for: ref, approved_by: party, action: text = "approve") {
3
+ instrument decision(for: ref, approved_by: approval, action: text = "approve", protected_request: text = "self.target") {
4
4
  summary: "One authenticated decision bound to an immutable target and expiry."
5
5
  fields { target: ref<for>, expiresAt: date }
6
6
  lifecycle { states: [pending, approved, declined, expired], initial: pending }
7
7
  action create {}
8
8
  action approve {
9
9
  from: pending, to: approved, actor: { party: approved_by }, deadline: { at: self.expiresAt }
10
- approval: { target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "approved" }
10
+ approval: { protectedRequest: protected_request, target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "approved" }
11
11
  }
12
12
  action decline {
13
13
  from: pending, to: declined, actor: { party: approved_by }, deadline: { at: self.expiresAt }
14
- approval: { target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "declined" }
14
+ approval: { protectedRequest: protected_request, target: self.target, action: action, party: approved_by, expires: self.expiresAt, input: "material", decision: "declined" }
15
15
  }
16
16
  action expire { from: pending, to: expired, actor: clock, due: { at: self.expiresAt } }
17
17
  }
package/std/escrow.hsx CHANGED
@@ -1,27 +1,26 @@
1
1
  header escrow
2
2
 
3
- instrument hold(payer: party, payee: party, for: ref<marketplace.order>, accept_within: duration = 48h, fee: fee = { seller: 0%, tax: 0% }, dispute: policy = { refund_after: return_verified }) {
3
+ instrument hold(payer: party = party(person), payee: party = programOperator, accept_within: duration = 48h, fee: fee = { seller: 0%, tax: 0% }, dispute: policy = { refund_after: return_verified }) {
4
4
  revisioned: true
5
- summary: "Hold an order's price through delivery, acceptance and verified return."
6
- fields { payer: account of payer, payee: account of payee, order: ref<for>, price: money, held: account of self, deliveredAt: date?, buyerTopUp: money? }
5
+ summary: "Hold the subject price through delivery, acceptance and verified return."
6
+ fields {
7
+ payer: account of payer, payee: account of payee
8
+ held: account of self, deliveredAt: date?
9
+ price: money = subject.price
10
+ }
7
11
  lifecycle { states: [pending, funded, delivered, disputed, return_verified, released, refunded, cancelled], initial: pending }
8
12
  action create {
9
- requires self.payer == self.order.buyer; requires self.payee == self.order.listing.seller; requires self.price == self.order.price
13
+ subject { price: money }
14
+ actor: { party: payer }
10
15
  }
11
16
  action fund {
12
17
  from: pending, to: funded, actor: { party: payer }
13
- requires self.order in [quoted, placed]
14
- invoke: [{ reference: self.order, action: commit, input: {} }, { selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.order.listing, states: [active], limit: 1, where: { buyer: { field: self.payer } } }, action: credit, input: { funds: { field: self.id }, contribution: { field: self.price } } }, { reference: self.id, action: fund_remainder, input: {} }]
15
- }
16
- action fund_remainder {
17
- from: funded, to: funded, actor: { parent: current() }
18
- calculate: [{ target: buyerTopUp, op: subtract, base: { field: self.price }, subtract: [{ field: self.held.balance }] }]
19
- moves self.buyerTopUp from payer to self.held
18
+ subject { price: money }
19
+ moves subject.price from payer to self.held
20
20
  }
21
21
  action confirm {
22
22
  from: pending, to: funded
23
23
  requires self.held.balance == self.price
24
- invoke: [{ reference: self.order, action: commit, input: {} }]
25
24
  }
26
25
  action deliver {
27
26
  from: funded, to: delivered, actor: { party: payee }
@@ -29,7 +28,7 @@ instrument hold(payer: party, payee: party, for: ref<marketplace.order>, accept_
29
28
  }
30
29
  action accept {
31
30
  from: delivered, to: released, actor: { party: payer }
32
- input { acceptanceReference: text, titleReference: text, coverReference: text }
31
+ input { acceptanceReference: text }
33
32
  deadline: { at: self.deliveredAt, offset: accept_within }
34
33
  moves self.price from self.held to payee fee fee
35
34
  }
@@ -41,7 +40,10 @@ instrument hold(payer: party, payee: party, for: ref<marketplace.order>, accept_
41
40
  from: delivered, to: disputed, actor: { party: payer }
42
41
  deadline: { at: self.deliveredAt, offset: accept_within }
43
42
  }
44
- action verify_return { from: disputed, to: return_verified, input { returnReference: text } }
43
+ action verify_return {
44
+ from: disputed, to: return_verified
45
+ input { returnEvidence: text }
46
+ }
45
47
  action refund {
46
48
  from: dispute.refund_after, to: refunded
47
49
  moves self.price from self.held to payer
package/std/financing.hsx CHANGED
@@ -1,12 +1,13 @@
1
1
  header financing
2
2
 
3
- instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3, profit: percent, disburse_to: enum(funds, borrower) = funds, profit_earned: enum(on_payment, by_schedule, at_disbursement) = on_payment, apply: enum(fines_profit_principal, principal_profit, pro_rata) = fines_profit_principal, payoff_rebate: percent = 100%, write_off_after: duration = 90d, max_extension: duration = 90d, max_amendments: integer(0, 366) = 2, waiver_limit: percent = 100%, amendment_expiry: duration = 7d, payoff_quote_expiry: duration = 1h, allow_overdue: enum(allowed, blocked) = allowed, assessed_fines: enum(carry, require_waiver) = carry, down_payment: percent = 0%, funds: ref, approval: approval, borrower: party = party(person), capital: party = programOperator, profit_to: party = programOperator, limits: ref<financing.limits> = object(financing.limits)) {
3
+ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3, profit: percent, disburse_to: enum(funds, borrower) = funds, profit_earned: enum(on_payment, by_schedule, at_disbursement) = on_payment, apply: enum(fines_profit_principal, principal_profit, pro_rata) = fines_profit_principal, payoff_rebate: percent = 100%, write_off_after: duration = 90d, max_extension: duration = 90d, max_amendments: integer(0, 366) = 2, waiver_limit: percent = 100%, amendment_expiry: duration = 7d, payoff_quote_expiry: duration = 1h, allow_overdue: enum(allowed, blocked) = allowed, assessed_fines: enum(carry, require_waiver) = carry, down_payment: percent = 0%, funds: ref = object(escrow.hold), approval: approval = programOperator, borrower: party = party(person), capital: party = programOperator, share: percent = 0%, limits: ref<financing.limits> = object(financing.limits)) {
4
4
  revisioned: true
5
+ familyRevision: 1
5
6
  summary: "Funding, profit recognition and payment order chosen by the program."
6
7
  fields {
7
8
  funds: ref<funds>, borrower: account of borrower, settlementIdentity: text, borrowerLimit: ref<limits>, portfolioLimit: ref<limits.portfolio>
8
9
  months: integer = months, dates: list(date, months)
9
- capital: account(capital, cash, "capital"), profitIncome: account(profit_to, cash, "profitIncome"), loss: account(capital, claim, "loss")
10
+ allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), loss: account(capital, claim, "loss")
10
11
  downPaymentRemaining: money?, downPaymentCredit: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money?
11
12
  price: money = self.funds.price
12
13
  downPayment: money = rate(self.price, down_payment)
@@ -25,16 +26,14 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
25
26
  action sign {
26
27
  from: quoted, to: signed, actor: { party: borrower }, input { signedOffer: text, affordabilityReference: text }
27
28
  requires count of { instrument: slice, reference: "plan", anchor: self.id, states: [draft], limit: 366 } == self.months
28
- when funds has order { invoke: [{ reference: self.funds.order, action: commit, input: {} }] }
29
29
  }
30
30
  action disburse {
31
31
  from: signed, to: active
32
- requires approval by approval
32
+ requires approval by approval protectedRequest self differentFromInitiator true
33
33
  requires self.borrowerLimit in [approved]
34
34
  requires self.portfolioLimit in [approved]
35
35
  when disburse_to is funds {
36
36
  requires self.funds in [pending]
37
- when funds has order { invoke: [{ selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.funds.order.listing, states: [active], limit: 1, where: { buyer: { field: self.borrower } } }, action: credit, input: { funds: { field: self.funds }, contribution: { field: self.downPayment } } }] }
38
37
  invoke: [{ reference: self.id, action: fund_down_payment, input: {} }]
39
38
  }
40
39
  when disburse_to is borrower { moves self.principal from self.capital to self.borrower }
@@ -71,11 +70,11 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
71
70
  invoke: [{ instrument: settlement, action: create, input: { plan: { field: self.id }, identity: { field: self.settlementIdentity }, principal: { field: self.principalReturned }, profit: { field: self.profitReturned }, loss: { literal: "0" }, kind: { literal: "cash" } } }]
72
71
  }
73
72
  action write_off {
74
- from: active, to: written_off
73
+ from: active, to: written_off, actor: { parent: write_off_request }
75
74
  input { overdueSlice: ref<slice> }
76
75
  calculate: [{ target: "lossRecorded", op: "aggregate", selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due], limit: 366 }, measure: { sum: "principalReceivable.balance" } }]
77
76
  due: { at: input.overdueSlice.lossEligibleAt }
78
- requires approval by approval; requires input.overdueSlice.plan == self.id; requires input.overdueSlice.principalReceivable.balance > "0"
77
+ requires input.overdueSlice.plan == self.id; requires input.overdueSlice.principalReceivable.balance > "0"
79
78
  invoke: [{ selection: { instrument: slice, reference: "plan", anchor: self.id, states: [pending, due], limit: 366, order: [position] }, action: write_off, input: {} }, { instrument: settlement, action: create, input: { plan: { field: self.id }, identity: { field: self.settlementIdentity }, principal: { literal: "0" }, profit: { literal: "0" }, loss: { field: self.lossRecorded }, kind: { literal: "loss" } } }]
80
79
  }
81
80
  action record_payment {
@@ -86,6 +85,17 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
86
85
  action cancel { from: quoted, to: cancelled }
87
86
  action void { from: signed, to: cancelled, actor: { party: borrower } }
88
87
  records {
88
+ write_off_request: {
89
+ fields { plan: ref<parent>, overdueSlice: ref<slice> }
90
+ lifecycle { states: [requested, applied, cancelled], initial: requested }
91
+ action create { requires self.overdueSlice.plan == self.plan; requires self.plan in [active] }
92
+ action apply {
93
+ from: requested, to: applied
94
+ requires approval by approval protectedRequest self differentFromInitiator true
95
+ invoke: [{ reference: self.plan, action: write_off, input: { overdueSlice: { field: self.overdueSlice } } }]
96
+ }
97
+ action cancel { from: requested, to: cancelled }
98
+ }
89
99
  payoff_quote: {
90
100
  fields {
91
101
  plan: ref<parent>, revision: text?, effectiveAt: date?, expiresAt: date = after(self.createdAt, payoff_quote_expiry)
@@ -146,7 +156,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
146
156
  action accept {
147
157
  from: proposed, to: applied, actor: { party: borrower }, deadline: { at: self.expiresAt }
148
158
  requires self.plan in [active]; requires self.revision == self.productRevision
149
- requires approval by approval
159
+ requires approval by approval protectedRequest self differentFromInitiator true
150
160
  requires count of { instrument: current(), reference: "plan", anchor: self.plan, states: [applied], limit: 366 } < self.maximum
151
161
  set: { acceptedAt: { field: self.now } }
152
162
  when assessed_fines is require_waiver { requires self.waiverPercent == 10000 }
@@ -172,13 +182,19 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
172
182
  action create { actor: { parent: slice }, input { amendment: ref<amendment>, slice: ref<slice>, oldDate: date, newDate: date }, requires unique "amended_slice" on [self.amendment, self.slice] }
173
183
  }
174
184
  settlement: {
175
- fields { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss)
176
- capital: account(capital, cash, "capital"), profitIncome: account(profit_to, cash, "profitIncome"), lossAccount: account(capital, claim, "loss") }
177
- lifecycle { states: [recorded], initial: recorded }
185
+ fields { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss), share: money?, retainedProfit: money?
186
+ allocatedPayable: account(capital, cash, "allocatedProfitPayable"), capital: account(capital, cash, "capital"), profitIncome: account(capital, cash, "profitIncome"), lossAccount: account(capital, claim, "loss") }
187
+ lifecycle { states: [recorded, refunded], initial: recorded }
178
188
  action create {
179
189
  actor: { parent: parent }
180
190
  input { plan: ref<parent>, identity: text, principal: money, profit: money, loss: money, kind: enum(cash, loss) }
181
191
  requires unique "settlement_identity" on [self.plan, self.identity]
192
+ calculate: [{ target: "share", op: "rate", base: { field: self.profit }, bps: { literal: share }, rounding: "floor" }, { target: retainedProfit, op: "subtract", base: { field: self.profit }, subtract: [{ field: self.share }] }]
193
+ moves self.share from self.profitIncome to self.allocatedPayable
194
+ }
195
+ action refund {
196
+ from: recorded, to: refunded, actor: { parent: payment }
197
+ moves self.share from self.allocatedPayable to self.profitIncome
182
198
  }
183
199
  }
184
200
  slice: {
@@ -332,12 +348,10 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
332
348
  payment: {
333
349
  revisioned: true
334
350
  fields { plan: ref<parent>, amount: money, paymentIdentity: text, held: account of self
335
- capital: account(capital, cash, "capital"), profitIncome: account(profit_to, cash, "profitIncome"), loss: account(capital, claim, "loss")
336
351
  outstandingPrincipal: money?, outstandingProfit: money?, outstandingFines: money?, outstandingCosts: money?, outstandingTotal: money?, principalReturned: money?, profitReturned: money?, lossRecorded: money? }
337
352
 
338
353
  lifecycle { states: [pending, paid, refunded], initial: pending }
339
354
  action create { input { plan: ref<parent>, amount: money, paymentIdentity: text } requires self.plan in [active]; requires unique "payment_identity" on [self.plan, self.paymentIdentity] }
340
- // 366 slices * (1 + 3 fines * 3 + 3 costs * 3 + 2 pieces + 1 completion), plus recording.
341
355
  action pay {
342
356
  expansionLimit: 8192
343
357
  from: pending, to: paid
@@ -372,6 +386,8 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
372
386
  action refund {
373
387
  from: paid, to: refunded
374
388
  requires unique "settlement_consumption" on [self.plan, self.paymentIdentity]; requires self.plan in [active, paid]
389
+ requires count of { instrument: settlement, reference: "plan", anchor: self.plan, states: [recorded], limit: 366, where: { identity: { field: self.paymentIdentity } } } == 1
390
+ invoke: [{ selection: { instrument: settlement, reference: "plan", anchor: self.plan, states: [recorded], limit: 366, where: { identity: { field: self.paymentIdentity } } }, action: refund, input: {} }]
375
391
  when apply is fines_profit_principal {
376
392
  invoke: [{ selection: { instrument: all(financing.late_charge), reference: "plan", anchor: self.plan, states: [assessed, collected], limit: 366, order: [overdueAt] }, action: refund, input: { payment: { field: self.id } } }, { selection: { instrument: all(financing.late_charge.cost), reference: "plan", anchor: self.plan, states: [assessed, collected], limit: 366, order: [overdueAt] }, action: refund, input: { payment: { field: self.id } } }]
377
393
  }
@@ -419,6 +435,7 @@ instrument installments(months: integer(1, 366), charge_limit: integer(1, 3) = 3
419
435
 
420
436
  instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d, fine: money = 50 SAR, cap: money = 25 SAR, fines_to: party = programOperator, costs_to: party = programOperator, approval: approval = programOperator, borrower: party = party(person)) {
421
437
  revisioned: true
438
+ familyRevision: 1
422
439
  summary: "Separate approved fine and evidenced recovery claims on an overdue installment."
423
440
  fields {
424
441
  plan: ref<on>, slice: ref<on.slice>, borrower: account of borrower, fine: money = fine, overdueAt: date?
@@ -428,7 +445,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
428
445
  action create { calculate: [{ target: overdueAt, op: shift, date: { field: self.slice.dueAt }, milliseconds: { literal: grace }, direction: after }], requires unique "late_assessment" on [self.slice, self.overdueAt]; requires self.borrower == self.slice.plan.borrower; requires self.plan == self.slice.plan }
429
446
  action assess {
430
447
  from: proposed, to: assessed, due: { at: self.overdueAt }
431
- requires self.slice.principalReceivable.balance > "0"; requires approval by approval
448
+ requires self.slice.principalReceivable.balance > "0"; requires approval by approval protectedRequest self differentFromInitiator true
432
449
  moves self.fine from self.debt to self.receivable
433
450
  }
434
451
  action collect {
@@ -450,7 +467,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
450
467
  requires input.amendment.plan == self.plan; requires input.amendment in [applied]
451
468
  invoke: [{ instrument: waiver, action: create, input: { assessment: { field: self.id }, amendment: { field: input.amendment } } }]
452
469
  }
453
- action waive { from: [assessed, collected], to: waived, input { reason: text, evidence: text }, requires approval by approval, moves self.receivable.balance from self.receivable to self.debt }
470
+ action waive { from: [assessed, collected], to: waived, actor: { parent: waiver_request }, input { reason: text, evidence: text }, moves self.receivable.balance from self.receivable to self.debt }
454
471
  action refund {
455
472
  from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
456
473
  requires input.payment in [paid, refunded]
@@ -458,6 +475,17 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
458
475
  invoke: [{ selection: { instrument: receipt, reference: "payment", anchor: input.payment, states: [paid], limit: 1, where: { assessment: { field: self.id } } }, action: refund, input: {} }, { selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [assessed], limit: 1, where: { id: { field: self.id }, receivable.balance: { literal: "0" } } }, action: finish, input: {} }]
459
476
  }
460
477
  records {
478
+ waiver_request: {
479
+ fields { assessment: ref<parent>, reason: text, evidence: text }
480
+ lifecycle { states: [requested, applied, cancelled], initial: requested }
481
+ action create { requires self.assessment in [assessed, collected] }
482
+ action apply {
483
+ from: requested, to: applied, input { reason: text, evidence: text }
484
+ requires approval by approval protectedRequest self differentFromInitiator true
485
+ invoke: [{ reference: self.assessment, action: waive, input: { reason: { field: input.reason }, evidence: { field: input.evidence } } }]
486
+ }
487
+ action cancel { from: requested, to: cancelled }
488
+ }
461
489
  waiver: {
462
490
  fields { assessment: ref<parent>, amendment: ref<on.amendment>, amount: money? }
463
491
  lifecycle { states: [recorded], initial: recorded }
@@ -485,7 +513,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
485
513
  action create { calculate: [{ target: overdueAt, op: shift, date: { field: self.assessment.overdueAt }, milliseconds: { literal: 0 }, direction: after }], requires unique "recovery_assessment" on [self.assessment]; requires self.plan == self.assessment.plan }
486
514
  action assess {
487
515
  from: proposed, to: assessed, due: { at: self.assessment.overdueAt }
488
- requires self.assessment.slice.principalReceivable.balance > "0"; requires approval by approval
516
+ requires self.assessment.slice.principalReceivable.balance > "0"; requires approval by approval protectedRequest self differentFromInitiator true
489
517
  moves self.amount from self.debt to self.receivable
490
518
  }
491
519
  action collect {
@@ -507,7 +535,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
507
535
  requires input.amendment.plan == self.plan; requires input.amendment in [applied]
508
536
  invoke: [{ instrument: waiver, action: create, input: { assessment: { field: self.id }, amendment: { field: input.amendment } } }]
509
537
  }
510
- action waive { from: [assessed, collected], to: waived, input { reason: text, evidence: text }, requires approval by approval, moves self.receivable.balance from self.receivable to self.debt }
538
+ action waive { from: [assessed, collected], to: waived, actor: { parent: waiver_request }, input { reason: text, evidence: text }, moves self.receivable.balance from self.receivable to self.debt }
511
539
  action refund {
512
540
  from: [assessed, collected], to: assessed, input { payment: ref<on.payment> }
513
541
  requires input.payment in [paid, refunded]
@@ -515,6 +543,17 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
515
543
  invoke: [{ selection: { instrument: receipt, reference: "payment", anchor: input.payment, states: [paid], limit: 1, where: { assessment: { field: self.id } } }, action: refund, input: {} }, { selection: { instrument: current(), reference: "plan", anchor: self.plan, states: [assessed], limit: 1, where: { id: { field: self.id }, receivable.balance: { literal: "0" } } }, action: finish, input: {} }]
516
544
  }
517
545
  records {
546
+ waiver_request: {
547
+ fields { assessment: ref<parent>, reason: text, evidence: text }
548
+ lifecycle { states: [requested, applied, cancelled], initial: requested }
549
+ action create { requires self.assessment in [assessed, collected] }
550
+ action apply {
551
+ from: requested, to: applied, input { reason: text, evidence: text }
552
+ requires approval by approval protectedRequest self differentFromInitiator true
553
+ invoke: [{ reference: self.assessment, action: waive, input: { reason: { field: input.reason }, evidence: { field: input.evidence } } }]
554
+ }
555
+ action cancel { from: requested, to: cancelled }
556
+ }
518
557
  waiver: {
519
558
  fields { assessment: ref<parent>, amendment: ref<on.amendment>, amount: money? }
520
559
  lifecycle { states: [recorded], initial: recorded }
@@ -537,6 +576,7 @@ instrument late_charge(on: ref<financing.installments>[], grace: duration = 3d,
537
576
  }
538
577
 
539
578
  instrument limits(per_borrower: money, portfolio: money, borrower: party = party(person), active_plans: integer = 1) {
579
+ familyRevision: 1
540
580
  summary: "One borrower ceiling and one shared portfolio ceiling."
541
581
  fields { borrower: account of borrower, limit: money = per_borrower, activePlans: integer = active_plans }
542
582
  lifecycle { states: [pending, approved], initial: pending }
@@ -553,6 +593,7 @@ instrument limits(per_borrower: money, portfolio: money, borrower: party = party
553
593
  }
554
594
 
555
595
  instrument credit_line(borrower: party, lender: party, limit: money, expires: date) {
596
+ familyRevision: 1
556
597
  summary: "A revolving facility; each draw is a separate immutable advance."
557
598
  fields { borrower: account of borrower, lender: account of lender, debt: account(borrower, claim, contra, "debt"), limit: money = limit, expiresAt: date = expires }
558
599
  lifecycle { states: [pending, active, suspended, closed], initial: pending }
@@ -564,6 +605,7 @@ instrument credit_line(borrower: party, lender: party, limit: money, expires: da
564
605
  }
565
606
 
566
607
  instrument advance(line: ref<financing.credit_line>, amount: money = runtime) {
608
+ familyRevision: 1
567
609
  summary: "A single facility draw with an immutable principal and full repayment."
568
610
  fields { line: ref<line>, amount: money = amount, principalReceivable: account(self, claim) }
569
611
  lifecycle { states: [pending, drawn, repaid, cancelled], initial: pending }
package/std/insurance.hsx CHANGED
@@ -1,11 +1,11 @@
1
1
  header insurance
2
2
 
3
- instrument cover(holder: party, insurer: party, commission: percent = 0%, covers: ref, premium: money = runtime) {
3
+ instrument cover(holder: party, insurer: party, approval: approval = programOperator, commission: percent = 0%, covers: ref, premium: money = runtime) {
4
4
  summary: "Recurring cover with separate premium slices and commission clawback."
5
5
  fields { covers: ref<covers>, holder: account of holder, insurer: account of insurer, expiresAt: date }
6
6
  lifecycle { states: [pending, active, cancelled, expired], initial: pending }
7
7
  action create {}
8
- action activate { from: pending, to: active, actor: { party: insurer } }
8
+ action activate { from: pending, to: active, requires approval by approval protectedRequest self differentFromInitiator true }
9
9
  action cancel {
10
10
  from: active, to: cancelled
11
11
  invoke: [{ selection: { instrument: slice, reference: "cover", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
@@ -17,7 +17,6 @@ instrument cover(holder: party, insurer: party, commission: percent = 0%, covers
17
17
  fields {
18
18
  cover: ref<parent>, covers: ref<covers>, premium: money = premium, startsAt: date, endsAt: date
19
19
  commission: money = rate(self.premium, commission)
20
- carrierNet: money = subtract(self.premium, self.commission)
21
20
  }
22
21
  lifecycle { states: [pending, paid, active, expired, refunded], initial: pending }
23
22
  action create {
@@ -43,12 +42,12 @@ instrument cover(holder: party, insurer: party, commission: percent = 0%, covers
43
42
  }
44
43
 
45
44
  instrument claim(cover: ref<insurance.cover>, approved_by: approval) {
46
- summary: "A carrier decision reserves one claim payment before settlement."
45
+ summary: "A claim decision reserves one claim payment before settlement."
47
46
  fields { cover: ref<cover>, amount: money, expiresAt: date, evidence: text, reservation: text? }
48
47
  lifecycle { states: [submitted, approved, paid, denied, expired], initial: submitted }
49
48
  action create { requires self.cover in [active] }
50
49
  action approve {
51
- from: submitted, to: approved, actor: { party: approved_by }
50
+ from: submitted, to: approved
52
51
  requires approval by approved_by
53
52
  deadline: { at: self.expiresAt }
54
53
  moves reserve self.amount from self.cover.insurer to self.cover.holder capture "reservation"
package/std/lending.hsx CHANGED
@@ -1,15 +1,14 @@
1
1
  header lending
2
2
 
3
- instrument round(borrower: party, capital: party, plan: ref<financing.installments> = object(financing.installments), months: integer(1, 366) = 3, profit: percent = 3%, minimum_ticket: money = 100 SAR, investor_cap: percent = 20%, commitments: ref<lending.commitment> = object(lending.commitment)) {
3
+ instrument round(borrower: party, plan: ref<financing.installments> = object(financing.installments), minimum_ticket: money = 100 SAR, investor_cap: percent = 20%, commitments: ref<lending.commitment> = object(lending.commitment)) {
4
4
  summary: "A threshold funding round; failed rounds return each original wallet contribution."
5
5
  fields {
6
- borrower: account of borrower, capital: account(capital, cash, "capital"), plan: ref<plan>, price: money, closesAt: date
7
- months: integer = months, profit: money = rate(self.price, profit)
6
+ borrower: account of borrower, plan: ref<plan>, price: money = self.plan.principal, closesAt: date
8
7
  minimumTicket: money = minimum_ticket, maximumTicket: money = rate(self.price, investor_cap)
9
8
  held: account of self
10
9
  }
11
10
  lifecycle { states: [open, funded, failed], initial: open }
12
- action create { requires unique "funding_plan" on [self.plan]; requires self.months == self.plan.months; requires self.profit == self.plan.profit; requires self.price == self.plan.principal; requires self.borrower == self.plan.funds.payer }
11
+ action create { requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.funds.payer }
13
12
  action close {
14
13
  from: open, to: funded
15
14
  requires sum "amount" of { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 } == self.price
@@ -58,13 +57,13 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
58
57
  held: account of self, losses: account(self, claim), residualLoss: account(residual_to, claim, "loss")
59
58
  }
60
59
  lifecycle { states: [pending, cash_ready, loss_ready, distributed], initial: pending }
61
- action create { requires self.round in [funded]; requires self.source.plan == self.round.plan; requires unique "settlement_consumption" on [self.source.plan, self.source.identity] }
60
+ action create { requires self.round in [funded]; requires self.source in [recorded]; requires self.source.plan == self.round.plan; requires unique "settlement_consumption" on [self.source.plan, self.source.identity] }
62
61
  action prepare_cash {
63
62
  from: pending, to: cash_ready
64
- requires self.mode == "cash"; requires self.source.kind == "cash"
63
+ requires self.mode == "cash"; requires self.source.kind == "cash"; requires self.source in [recorded]
65
64
  calculate: [
66
65
  { target: "principal", op: "sum", values: [{ field: self.source.principal }] }
67
- { target: "profit", op: "sum", values: [{ field: self.source.profit }] }
66
+ { target: "profit", op: "sum", values: [{ field: self.source.retainedProfit }] }
68
67
  { target: "charge", op: "rate", base: { field: self.profit }, bps: { literal: fee }, rounding: "floor" }
69
68
  { target: "vat", op: "rate", base: { field: self.charge }, bps: { literal: tax }, rounding: "floor" }
70
69
  { target: "grossTotal", op: "sum", values: [{ field: self.principal }, { field: self.profit }] }
@@ -73,7 +72,7 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
73
72
  }
74
73
  action prepare_loss {
75
74
  from: pending, to: loss_ready
76
- requires self.mode == "loss"; requires self.source.kind == "loss"
75
+ requires self.mode == "loss"; requires self.source.kind == "loss"; requires self.source in [recorded]
77
76
  calculate: [{ target: "net", op: "sum", values: [{ field: self.source.loss }] }]
78
77
  }
79
78
  action distribute_cash {
@@ -1,6 +1,6 @@
1
1
  header marketplace
2
2
 
3
- instrument listing(seller: party, vehicle_facts: enum(optional, required) = optional) {
3
+ instrument listing(seller: party) {
4
4
  revisioned: true
5
5
  summary: "A seller's offer and its asking price."
6
6
  fields { seller: account of seller, title: text, price: money }
@@ -8,8 +8,6 @@ instrument listing(seller: party, vehicle_facts: enum(optional, required) = opti
8
8
  action create { actor: { party: seller } }
9
9
  action publish {
10
10
  from: draft, to: active, actor: { party: seller }
11
- when vehicle_facts is required { requires count of { instrument: all(vehicles.vehicle), reference: "listing", anchor: self.id, states: [draft], limit: 1 } == 1 }
12
- invoke: [{ selection: { instrument: all(vehicles.vehicle), reference: "listing", anchor: self.id, states: [draft], limit: 1 }, action: publish, input: {} }]
13
11
  }
14
12
  action withdraw { from: active, to: withdrawn, actor: { party: seller }, requires count of { instrument: all(marketplace.reservation), reference: "listing", anchor: self.id, states: [active], limit: 366 } == 0 }
15
13
  action sell { from: active, to: sold, actor: { party: seller }, requires count of { instrument: all(marketplace.reservation), reference: "listing", anchor: self.id, states: [active], limit: 366 } == 0 }
@@ -76,13 +74,12 @@ instrument reservation(listing: ref<marketplace.listing>, funds: ref<escrow.hold
76
74
  action credit {
77
75
  from: active, to: converted, actor: { parent: converters }, deadline: { at: self.expiresAt }
78
76
  input { funds: ref<funds>, contribution: money }
79
- requires input.funds == self.funds; requires input.funds in [pending, funded]; requires input.funds.order.listing == self.listing; requires input.funds.payer == self.buyer; requires input.funds.price == self.price
77
+ requires input.funds == self.funds; requires input.funds in [pending, funded]; requires input.funds.payer == self.buyer; requires input.funds.price == self.price
80
78
  requires input.contribution <= self.price
81
79
  calculate: [{ target: credit, op: minimum, values: [{ field: self.held.balance }, { field: input.contribution }] }, { target: refund, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.credit }] }]
82
80
  set: { dispositionAt: { field: self.now } }
83
81
  moves self.credit from self.held to input.funds.held
84
82
  moves self.refund from self.held to self.buyer
85
- invoke: [{ reference: input.funds.order, action: commit, input: {} }]
86
83
  }
87
84
  action cancel {
88
85
  from: active, to: cancelled, actor: { party: buyer }, input { reason: text }
package/std/money.hsx CHANGED
@@ -130,23 +130,24 @@ instrument deposit(payer: party, payee: party, amount: money, expires: date, app
130
130
  action cancel { from: pending, to: cancelled }
131
131
  }
132
132
 
133
- instrument payout(payer: party, payee: party, amount: money = runtime, reconcile_within: duration = 3d) {
134
- summary: "A bank-linked transfer with confirmation bound to its captured identity."
135
- fields { amount: money = amount, bank: account(payee, cash, external, "bank"), receipt: text?, expectedAt: date = after(self.createdAt, reconcile_within) }
136
- lifecycle { states: [pending, instructed, settled, exception, reversed], initial: pending }
133
+ instrument payout(payer: party, payee: party, adapter: text, max_age: duration, amount: money = runtime) {
134
+ summary: "Reserve funds until matching terminal boundary evidence permits posting."
135
+ fields { amount: money = amount, receipt: text? }
136
+ lifecycle { states: [pending, instructed, settled, rejected], initial: pending }
137
137
  action create {}
138
- action instruct { from: pending, to: instructed, actor: { party: payer }, humanApproval: "distinct_member", moves self.amount from payer to self.bank capture "receipt" }
139
- action confirm {
140
- from: [instructed, exception], to: settled
141
- requires self.receipt.status == "settled"
138
+ action instruct {
139
+ from: pending, to: instructed, actor: { party: payer }, humanApproval: "distinct_member"
140
+ moves reserve self.amount from payer to payee capture "receipt" boundary adapter
142
141
  }
143
- action expire {
144
- from: instructed, to: exception, actor: clock, due: { at: self.expectedAt }
145
- requires self.receipt.status != "settled"
142
+ action confirm {
143
+ from: instructed, to: settled
144
+ requires evidence self.id family boundary check outcome result confirmed maxAge max_age instruction self.receipt
145
+ moves post self.receipt
146
146
  }
147
- action reverse {
148
- from: [instructed, exception], to: reversed
149
- requires self.receipt.status == "reversed"
147
+ action reject {
148
+ from: instructed, to: rejected
149
+ requires evidence self.id family boundary check outcome result rejected maxAge max_age instruction self.receipt
150
+ moves void self.receipt
150
151
  }
151
152
  }
152
153
 
@@ -158,38 +159,3 @@ instrument metered(payer: party, payee: party, unit_price: money, units: integer
158
159
  action bill { from: recorded, to: billed, moves self.amount from payer to payee fee fee }
159
160
  action cancel { from: recorded, to: cancelled }
160
161
  }
161
-
162
- instrument batch(payer: party, payee: party, closes: date, approval: approval, reconcile_within: duration = 3d) {
163
- summary: "A frozen positive net of credit and debit entries, paid once."
164
- fields { closesAt: date = closes, credits: money, debits: money, amount: money = subtract(self.credits, self.debits), bank: account(payee, cash, external, "bank"), receipt: text?, instructedAt: date?, expectedAt: date? }
165
- lifecycle { states: [open, closed, approved, instructed, exception, paid, reversed], initial: open }
166
- action create {}
167
- action close {
168
- from: open, to: closed, actor: clock, due: { at: self.closesAt }
169
- requires sum "amount" of { instrument: credit, reference: "batch", anchor: self.id, states: [recorded], limit: 366 } == self.credits
170
- requires sum "amount" of { instrument: debit, reference: "batch", anchor: self.id, states: [recorded], limit: 366 } == self.debits
171
- requires self.amount > "0"
172
- }
173
- action approve { from: closed, to: approved, requires approval by approval }
174
- action pay {
175
- from: approved, to: instructed
176
- set: { instructedAt: { field: self.now } }
177
- calculate: [{ target: "expectedAt", op: "shift", date: { field: self.now }, milliseconds: { literal: reconcile_within }, direction: "after" }]
178
- moves self.amount from payer to self.bank capture "receipt"
179
- }
180
- action confirm { from: [instructed, exception], to: paid, requires self.receipt.status == "settled" }
181
- action expire { from: instructed, to: exception, actor: clock, due: { at: self.expectedAt }, requires self.receipt.status != "settled" }
182
- action reverse { from: [instructed, exception], to: reversed, requires self.receipt.status == "reversed" }
183
- records {
184
- credit: {
185
- fields { batch: ref<parent>, identity: text, amount: money }
186
- lifecycle { states: [recorded], initial: recorded }
187
- action create { requires self.batch in [open]; requires unique "batch_credit" on [self.identity] }
188
- }
189
- debit: {
190
- fields { batch: ref<parent>, identity: text, amount: money }
191
- lifecycle { states: [recorded], initial: recorded }
192
- action create { requires self.batch in [open]; requires unique "batch_debit" on [self.identity] }
193
- }
194
- }
195
- }
package/std/travel.hsx CHANGED
@@ -10,11 +10,11 @@ instrument package(price: money, supplier_cost: money, departure: date) {
10
10
  action close { from: published, to: closed, actor: clock, due: { at: self.departure } }
11
11
  }
12
12
 
13
- instrument booking(package: ref<travel.package>, buyer: party, supplier: approval, cover: ref<insurance.cover>, deposit: percent = 30%, balance_before: duration = 14d, confirm_within: duration = 24h, tax: percent = 15%, early_before: duration = 30d, middle_before: duration = 15d, middle_penalty: percent = 10%, late_penalty: percent = 30%) {
13
+ instrument booking(package: ref<travel.package>, buyer: party, supplier: party, approval: approval = programOperator, cover: ref<insurance.cover>, deposit: percent = 30%, balance_before: duration = 14d, confirm_within: duration = 24h, tax: percent = 15%, early_before: duration = 30d, middle_before: duration = 15d, middle_penalty: percent = 10%, late_penalty: percent = 30%) {
14
14
  constraints { early_before: greater_than(middle_before) }
15
- summary: "A deposited booking with three cancellation bands and supplier confirmation."
15
+ summary: "A deposited booking with three cancellation bands and confirmation."
16
16
  fields {
17
- package: ref<package>, bookingReference: text, buyer: account of buyer, supplier: account of supplier
17
+ package: ref<package>, buyer: account of buyer, supplier: account of supplier
18
18
  price: money = self.package.price, supplierCost: money = self.package.supplierCost
19
19
  deposit: money = rate(self.price, deposit), balance: money = subtract(self.price, self.deposit)
20
20
  margin: money = subtract(self.price, self.supplierCost)
@@ -32,7 +32,6 @@ instrument booking(package: ref<travel.package>, buyer: party, supplier: approva
32
32
  lifecycle { states: [created, deposit_paid, paid, completed, cancelled, refunded], initial: created }
33
33
  action create {
34
34
  requires self.package in [published]
35
- requires unique "booking_reference" on [self.bookingReference]
36
35
  }
37
36
  action deposit {
38
37
  from: created, to: deposit_paid, actor: { party: buyer }, deadline: { at: self.balanceAt }
@@ -45,9 +44,9 @@ instrument booking(package: ref<travel.package>, buyer: party, supplier: approva
45
44
  moves self.balance from buyer to self.held
46
45
  }
47
46
  action confirm {
48
- from: paid, to: completed, actor: { party: supplier }
47
+ from: paid, to: completed
49
48
  due: { at: self.package.departure }, deadline: { at: self.confirmBy }
50
- requires approval by supplier
49
+ requires approval by approval protectedRequest self differentFromInitiator true
51
50
  moves self.supplierCost from self.held to supplier
52
51
  moves self.tax from self.held to programTax
53
52
  moves self.operatorNet from self.held to programOperator
package/std/vehicles.hsx DELETED
@@ -1,34 +0,0 @@
1
- header vehicles
2
-
3
- instrument vehicle(listing: ref<marketplace.listing>, seller: party) {
4
- summary: "Seller-declared vehicle identity and kilometre odometer, corrected only before publication."
5
- fields {
6
- listing: ref<listing>, seller: account of seller
7
- vin: text(17, 17, "^[A-HJ-NPR-Z0-9]{17}$"), make: text(1, 80), model: text(1, 80)
8
- year: integer(1886, 2200), odometer: integer(0, 10000000), odometerUnit: enum(kilometres) = kilometres
9
- correctedAt: date?, correctionEvidence: text?
10
- }
11
- lifecycle { states: [draft, published], initial: draft }
12
- action create {
13
- actor: { party: seller }
14
- requires self.listing in [draft]; requires self.seller == self.listing.seller
15
- requires unique "listing_vehicle" on [self.listing]
16
- }
17
- action publish {
18
- from: draft, to: published, actor: { parent: listing }
19
- requires self.listing in [active]
20
- requires unique "vehicle_identity" on [self.vin]
21
- }
22
- action correct {
23
- from: draft, to: draft, actor: { party: seller }
24
- input { make: text(1, 80), model: text(1, 80), year: integer(1886, 2200), odometer: integer(0, 10000000), evidence: text }
25
- requires self.listing in [draft]; requires self.seller == self.listing.seller
26
- set: { make: { field: input.make }, model: { field: input.model }, year: { field: input.year }, odometer: { field: input.odometer }, correctionEvidence: { field: input.evidence }, correctedAt: { field: self.now } }
27
- }
28
- action correct_identity {
29
- from: draft, to: draft, actor: { party: seller }
30
- input { vin: text(17, 17, "^[A-HJ-NPR-Z0-9]{17}$"), evidence: text }
31
- requires self.listing in [draft]; requires self.seller == self.listing.seller
32
- set: { vin: { field: input.vin }, correctionEvidence: { field: input.evidence }, correctedAt: { field: self.now } }
33
- }
34
- }