@hyperscale0/hsx 1.0.0 → 1.0.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -256
- package/LICENSE.md +356 -0
- package/NOTICE.md +8 -0
- package/README.md +44 -44
- package/bin/hsx.ts +0 -7
- package/dist/bin/hsx.d.ts +0 -4
- package/dist/bin/hsx.d.ts.map +1 -1
- package/dist/bin/hsx.js +0 -6
- package/dist/bin/hsx.js.map +1 -1
- package/dist/src/ast.d.ts +113 -308
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +7 -71
- package/dist/src/ast.js.map +1 -1
- package/dist/src/attachment-economics.d.ts +5 -0
- package/dist/src/attachment-economics.d.ts.map +1 -0
- package/dist/src/attachment-economics.js +55 -0
- package/dist/src/attachment-economics.js.map +1 -0
- package/dist/src/binding-contract.d.ts +22 -0
- package/dist/src/binding-contract.d.ts.map +1 -0
- package/dist/src/binding-contract.js +74 -0
- package/dist/src/binding-contract.js.map +1 -0
- package/dist/src/cli.d.ts +4 -19
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +89 -276
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +34 -66
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +2949 -117
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +8 -75
- package/dist/src/cost.d.ts.map +1 -1
- package/dist/src/cost.js +54 -364
- package/dist/src/cost.js.map +1 -1
- package/dist/src/diagnostics.d.ts +14 -9
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js +39 -416
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/examples-bundle.d.ts +3 -0
- package/dist/src/examples-bundle.d.ts.map +1 -0
- package/dist/src/examples-bundle.js +18 -0
- package/dist/src/examples-bundle.js.map +1 -0
- package/dist/src/examples.d.ts +10 -0
- package/dist/src/examples.d.ts.map +1 -0
- package/dist/src/examples.js +2 -0
- package/dist/src/examples.js.map +1 -0
- package/dist/src/format.d.ts +8 -7
- package/dist/src/format.d.ts.map +1 -1
- package/dist/src/format.js +9 -175
- package/dist/src/format.js.map +1 -1
- package/dist/src/header-source.d.ts +3 -0
- package/dist/src/header-source.d.ts.map +1 -0
- package/dist/src/header-source.js +41 -0
- package/dist/src/header-source.js.map +1 -0
- package/dist/src/headers.d.ts +64 -0
- package/dist/src/headers.d.ts.map +1 -0
- package/dist/src/headers.js +167 -0
- package/dist/src/headers.js.map +1 -0
- package/dist/src/index.d.ts +7 -11
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -9
- package/dist/src/index.js.map +1 -1
- package/dist/src/keywords.d.ts +56 -0
- package/dist/src/keywords.d.ts.map +1 -0
- package/dist/src/keywords.js +133 -0
- package/dist/src/keywords.js.map +1 -0
- package/dist/src/language.d.ts +26 -0
- package/dist/src/language.d.ts.map +1 -0
- package/dist/src/language.js +440 -0
- package/dist/src/language.js.map +1 -0
- package/dist/src/lex.d.ts +13 -28
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +119 -145
- package/dist/src/lex.js.map +1 -1
- package/dist/src/parse.d.ts +4 -17
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +484 -1036
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts.map +1 -1
- package/dist/src/std-bundle.js +11 -84
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/std-library.d.ts +1 -1
- package/dist/src/std-library.d.ts.map +1 -1
- package/dist/src/std-library.js +1 -5
- package/dist/src/std-library.js.map +1 -1
- package/dist/src/tunables.d.ts +7 -0
- package/dist/src/tunables.d.ts.map +1 -0
- package/dist/src/tunables.js +27 -0
- package/dist/src/tunables.js.map +1 -0
- package/docs/README.md +528 -26
- package/docs/assets/brand/hsx-horizontal-white.svg +1 -0
- package/docs/assets/brand/hsx-horizontal.svg +1 -0
- package/docs/assets/brand/hsx-stacked-white.svg +1 -0
- package/docs/assets/brand/hsx-stacked.svg +1 -0
- package/docs/assets/brand/manifest.json +30 -0
- package/docs/assets/hsx.svg +11 -0
- package/examples/cards.hsx +34 -0
- package/examples/collections.hsx +37 -0
- package/examples/cost-table.json +633 -385
- package/examples/escrow.hsx +24 -0
- package/examples/financing.hsx +32 -0
- package/examples/insurance.hsx +30 -0
- package/examples/lending.hsx +57 -0
- package/examples/library.hsx +15 -0
- package/examples/marketplace.hsx +32 -0
- package/examples/money.hsx +19 -0
- package/examples/rental-deposit.hsx +60 -0
- package/examples/repair-approval.hsx +44 -0
- package/examples/reporting.hsx +28 -0
- package/examples/savings.hsx +21 -0
- package/examples/serviced.hsx +25 -0
- package/examples/wallet.hsx +24 -0
- package/package.json +25 -23
- package/src/ast.ts +83 -440
- package/src/attachment-economics.ts +103 -0
- package/src/binding-contract.ts +104 -0
- package/src/cli.ts +95 -325
- package/src/compile.ts +4063 -209
- package/src/cost.ts +71 -574
- package/src/diagnostics.ts +57 -437
- package/src/examples-bundle.ts +19 -0
- package/src/examples.ts +9 -0
- package/src/format.ts +12 -256
- package/src/header-source.ts +54 -0
- package/src/headers.ts +195 -0
- package/src/index.ts +18 -33
- package/src/keywords.ts +134 -0
- package/src/language.ts +629 -0
- package/src/lex.ts +131 -192
- package/src/parse.ts +489 -1195
- package/src/std-bundle.ts +12 -88
- package/src/std-library.ts +2 -7
- package/src/tunables.ts +36 -0
- package/std/cards.hsx +85 -0
- package/std/collections.hsx +90 -0
- package/std/escrow.hsx +73 -0
- package/std/financing.hsx +683 -0
- package/std/insurance.hsx +89 -0
- package/std/lending.hsx +138 -0
- package/std/marketplace.hsx +123 -0
- package/std/money.hsx +173 -0
- package/std/reporting.hsx +753 -0
- package/std/savings.hsx +68 -0
- package/std/wallet.hsx +57 -0
- package/LICENSE +0 -661
- package/LICENSING.md +0 -52
- package/TRADEMARKS.md +0 -35
- package/dist/src/emit.d.ts +0 -34
- package/dist/src/emit.d.ts.map +0 -1
- package/dist/src/emit.js +0 -137
- package/dist/src/emit.js.map +0 -1
- package/dist/src/entry-overrides.d.ts +0 -58
- package/dist/src/entry-overrides.d.ts.map +0 -1
- package/dist/src/entry-overrides.js +0 -284
- package/dist/src/entry-overrides.js.map +0 -1
- package/dist/src/ir.d.ts +0 -66
- package/dist/src/ir.d.ts.map +0 -1
- package/dist/src/ir.js +0 -15
- package/dist/src/ir.js.map +0 -1
- package/dist/src/limits.d.ts +0 -21
- package/dist/src/limits.d.ts.map +0 -1
- package/dist/src/limits.js +0 -21
- package/dist/src/limits.js.map +0 -1
- package/dist/src/lsp/server.d.ts +0 -32
- package/dist/src/lsp/server.d.ts.map +0 -1
- package/dist/src/lsp/server.js +0 -391
- package/dist/src/lsp/server.js.map +0 -1
- package/dist/src/modules.d.ts +0 -38
- package/dist/src/modules.d.ts.map +0 -1
- package/dist/src/modules.js +0 -368
- package/dist/src/modules.js.map +0 -1
- package/dist/src/typecheck.d.ts +0 -8
- package/dist/src/typecheck.d.ts.map +0 -1
- package/dist/src/typecheck.js +0 -2958
- package/dist/src/typecheck.js.map +0 -1
- package/dist/src/version.d.ts +0 -5
- package/dist/src/version.d.ts.map +0 -1
- package/dist/src/version.js +0 -5
- package/dist/src/version.js.map +0 -1
- package/docs/guide/01-first-program.md +0 -24
- package/docs/guide/02-money.md +0 -25
- package/docs/guide/03-instruments.md +0 -22
- package/docs/guide/04-lifecycles.md +0 -25
- package/docs/guide/05-fees-and-splits.md +0 -45
- package/docs/guide/06-schedules.md +0 -22
- package/docs/guide/07-composition.md +0 -46
- package/docs/guide/08-writing-a-module.md +0 -26
- package/docs/guide/09-cost.md +0 -24
- package/docs/guide/10-diagnostics.md +0 -27
- package/docs/llms-full.txt +0 -1739
- package/docs/llms.txt +0 -45
- package/docs/playground.md +0 -54
- package/docs/reference/cli.md +0 -35
- package/docs/reference/diagnostics.md +0 -619
- package/docs/reference/grammar.md +0 -39
- package/docs/reference/std/advance.md +0 -35
- package/docs/reference/std/cancellable_booking.md +0 -35
- package/docs/reference/std/captured_payment.md +0 -43
- package/docs/reference/std/conditional_disbursement.md +0 -33
- package/docs/reference/std/credit_facility.md +0 -35
- package/docs/reference/std/held_payment.md +0 -64
- package/docs/reference/std/instant_transfer.md +0 -30
- package/docs/reference/std/metered.md +0 -27
- package/docs/reference/std/pooled_split.md +0 -26
- package/docs/reference/std/premium_forward.md +0 -39
- package/docs/reference/std/reconciled_payout.md +0 -33
- package/docs/reference/std/recurring_collection.md +0 -25
- package/docs/reference/std/rotating_pool.md +0 -42
- package/docs/reference/std/scheduled.md +0 -51
- package/docs/reference/std/security_deposit.md +0 -46
- package/docs/reference/std/settlement_batch.md +0 -42
- package/docs/reference/std/swap.md +0 -46
- package/docs/reference/std/threshold_pool.md +0 -47
- package/docs/reference/std/weighted_distribution.md +0 -39
- package/docs/reference/types.md +0 -20
- package/docs/reference/udl-output.md +0 -13
- package/docs/sessions/2026-09-02-two-instruments.hsx +0 -21
- package/docs/sessions/2026-09-02-two-instruments.md +0 -41
- package/examples/01-first-program/README.md +0 -45
- package/examples/01-first-program/tip-jar.hsx +0 -17
- package/examples/02-imports-and-archetypes/README.md +0 -96
- package/examples/02-imports-and-archetypes/photo-booth.hsx +0 -37
- package/examples/03-diagnostics/README.md +0 -68
- package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
- package/examples/03-diagnostics/corner-shop.hsx +0 -19
- package/examples/04-complete-product/README.md +0 -84
- package/examples/04-complete-product/study-hall.hsx +0 -67
- package/examples/05-watch-club/README.md +0 -9
- package/examples/05-watch-club/watch-club.hsx +0 -124
- package/examples/README.md +0 -18
- package/examples/advance/README.md +0 -3
- package/examples/advance/advance.hsx +0 -13
- package/examples/advance/advance.udl +0 -339
- package/examples/cancellable_booking/README.md +0 -3
- package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
- package/examples/cancellable_booking/cancellable_booking.udl +0 -391
- package/examples/captured_payment/README.md +0 -3
- package/examples/captured_payment/captured_payment.hsx +0 -21
- package/examples/captured_payment/captured_payment.udl +0 -677
- package/examples/conditional_disbursement/README.md +0 -3
- package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -17
- package/examples/conditional_disbursement/conditional_disbursement.udl +0 -412
- package/examples/credit_facility/README.md +0 -3
- package/examples/credit_facility/credit_facility.hsx +0 -27
- package/examples/credit_facility/credit_facility.udl +0 -1373
- package/examples/held_payment/README.md +0 -3
- package/examples/held_payment/held_payment.hsx +0 -13
- package/examples/held_payment/held_payment.udl +0 -507
- package/examples/instant_transfer/README.md +0 -3
- package/examples/instant_transfer/instant_transfer.hsx +0 -16
- package/examples/instant_transfer/instant_transfer.udl +0 -364
- package/examples/metered/README.md +0 -3
- package/examples/metered/metered.hsx +0 -13
- package/examples/metered/metered.udl +0 -247
- package/examples/pooled_split/README.md +0 -3
- package/examples/pooled_split/pooled_split.hsx +0 -15
- package/examples/pooled_split/pooled_split.udl +0 -425
- package/examples/premium_forward/README.md +0 -3
- package/examples/premium_forward/premium_forward.hsx +0 -19
- package/examples/premium_forward/premium_forward.udl +0 -594
- package/examples/reconciled_payout/README.md +0 -3
- package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
- package/examples/reconciled_payout/reconciled_payout.udl +0 -303
- package/examples/recurring_collection/README.md +0 -3
- package/examples/recurring_collection/recurring_collection.hsx +0 -21
- package/examples/recurring_collection/recurring_collection.udl +0 -1130
- package/examples/rotating_pool/README.md +0 -3
- package/examples/rotating_pool/rotating_pool.hsx +0 -18
- package/examples/rotating_pool/rotating_pool.udl +0 -4352
- package/examples/scheduled/README.md +0 -3
- package/examples/scheduled/scheduled.hsx +0 -12
- package/examples/scheduled/scheduled.udl +0 -335
- package/examples/security_deposit/README.md +0 -3
- package/examples/security_deposit/security_deposit.hsx +0 -21
- package/examples/security_deposit/security_deposit.udl +0 -293
- package/examples/settlement_batch/README.md +0 -3
- package/examples/settlement_batch/settlement_batch.hsx +0 -19
- package/examples/settlement_batch/settlement_batch.udl +0 -753
- package/examples/swap/README.md +0 -3
- package/examples/swap/swap.hsx +0 -19
- package/examples/swap/swap.udl +0 -876
- package/examples/threshold_pool/README.md +0 -3
- package/examples/threshold_pool/threshold_pool.hsx +0 -16
- package/examples/threshold_pool/threshold_pool.udl +0 -667
- package/examples/weighted_distribution/README.md +0 -3
- package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
- package/examples/weighted_distribution/weighted_distribution.udl +0 -337
- package/skills/hsx/SKILL.md +0 -483
- package/src/emit.ts +0 -201
- package/src/entry-overrides.ts +0 -428
- package/src/ir.ts +0 -99
- package/src/limits.ts +0 -20
- package/src/lsp/server.ts +0 -460
- package/src/modules.ts +0 -446
- package/src/typecheck.ts +0 -3876
- package/src/version.ts +0 -5
- package/std/SEMANTICS.md +0 -130
- package/std/settlements/advance.hsx +0 -177
- package/std/settlements/cancellable_booking.hsx +0 -87
- package/std/settlements/captured_payment.hsx +0 -144
- package/std/settlements/conditional_disbursement.hsx +0 -185
- package/std/settlements/credit_facility.hsx +0 -167
- package/std/settlements/held_payment.hsx +0 -578
- package/std/settlements/index.hsx +0 -3
- package/std/settlements/instant_transfer.hsx +0 -190
- package/std/settlements/metered.hsx +0 -67
- package/std/settlements/pooled_split.hsx +0 -68
- package/std/settlements/premium_forward.hsx +0 -257
- package/std/settlements/reconciled_payout.hsx +0 -77
- package/std/settlements/recurring_collection.hsx +0 -27
- package/std/settlements/rotating_pool.hsx +0 -252
- package/std/settlements/scheduled.hsx +0 -641
- package/std/settlements/security_deposit.hsx +0 -166
- package/std/settlements/settlement_batch.hsx +0 -122
- package/std/settlements/swap.hsx +0 -1114
- package/std/settlements/threshold_pool.hsx +0 -221
- package/std/settlements/weighted_distribution.hsx +0 -141
package/src/std-library.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { BUNDLED_STD_FILES } from "./std-bundle.ts";
|
|
2
|
-
|
|
3
2
|
export interface StandardLibrary {
|
|
4
|
-
|
|
3
|
+
source(header: string): string | undefined;
|
|
5
4
|
}
|
|
6
|
-
|
|
7
5
|
export const bundledStandardLibrary: StandardLibrary = {
|
|
8
|
-
source
|
|
9
|
-
if (specifier !== "std/settlements") return undefined;
|
|
10
|
-
return BUNDLED_STD_FILES.get(`settlements/${name}.hsx`);
|
|
11
|
-
},
|
|
6
|
+
source: (header) => BUNDLED_STD_FILES.get(`${header}.hsx`),
|
|
12
7
|
};
|
package/src/tunables.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { fail } from "./diagnostics.ts";
|
|
2
|
+
import type { Expr } from "./ast.ts";
|
|
3
|
+
|
|
4
|
+
/** Bounds use UDL units: minor units, basis points and milliseconds. */
|
|
5
|
+
export function tunableBounds(
|
|
6
|
+
type: Expr,
|
|
7
|
+
): { minimum: number | string; maximum: number | string } | undefined {
|
|
8
|
+
const name =
|
|
9
|
+
type.kind === "call" || type.kind === "type"
|
|
10
|
+
? type.name
|
|
11
|
+
: type.kind === "name"
|
|
12
|
+
? type.value
|
|
13
|
+
: "";
|
|
14
|
+
if (name === "money") return { minimum: "0", maximum: "999999999999999999" };
|
|
15
|
+
if (name === "percent") return { minimum: 0, maximum: 10000 };
|
|
16
|
+
if (name === "duration")
|
|
17
|
+
return { minimum: 1, maximum: Number.MAX_SAFE_INTEGER };
|
|
18
|
+
if (name !== "integer") return;
|
|
19
|
+
if (type.kind === "call") {
|
|
20
|
+
const numbers = type.args.map((arg) =>
|
|
21
|
+
arg.kind === "number" ? Number(arg.value) : NaN,
|
|
22
|
+
);
|
|
23
|
+
if (
|
|
24
|
+
numbers.length !== 2 ||
|
|
25
|
+
numbers.some((value) => !Number.isSafeInteger(value)) ||
|
|
26
|
+
numbers[0]! > numbers[1]!
|
|
27
|
+
)
|
|
28
|
+
fail(
|
|
29
|
+
type,
|
|
30
|
+
"integer tunable needs ordered safe integer bounds",
|
|
31
|
+
"write integer(1, 12)",
|
|
32
|
+
);
|
|
33
|
+
return { minimum: numbers[0]!, maximum: numbers[1]! };
|
|
34
|
+
}
|
|
35
|
+
return { minimum: 0, maximum: Number.MAX_SAFE_INTEGER };
|
|
36
|
+
}
|
package/std/cards.hsx
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
header cards
|
|
2
|
+
|
|
3
|
+
// Activate holder and card before creating and approving an authorization.
|
|
4
|
+
// Expose authorization.create, approve and capture; expose decline and reverse
|
|
5
|
+
// separately if callers need those paths. Issuer declines only from pending;
|
|
6
|
+
// merchant captures or reverses an approved reservation. Expiry voids approved
|
|
7
|
+
// reservations or abandons pending ones; it does not delay a captured refund.
|
|
8
|
+
// Create one transaction for the captured authorization and expose its refund.
|
|
9
|
+
// Refund is available immediately from posted, even before authorization.expiresAt,
|
|
10
|
+
// and moves the whole captured amount from merchant to holder under programOperator.
|
|
11
|
+
// No partial capture, repeated capture or partial transaction refund is provided.
|
|
12
|
+
// Disputes need a posted transaction, issuer review and win before their deadline;
|
|
13
|
+
// win invokes the same refund. The alias never changes the declared action actor.
|
|
14
|
+
|
|
15
|
+
// Cost: authorization approve, capture and reverse each declare one transfer
|
|
16
|
+
// instruction. Transaction refund declares one transfer; a successful dispute
|
|
17
|
+
// invokes that refund. Card state changes alone declare no transfers.
|
|
18
|
+
|
|
19
|
+
instrument cardholder(holder: party) {
|
|
20
|
+
summary: "An account holder whose card access can be activated, suspended or closed."
|
|
21
|
+
fields { holder: account of holder }
|
|
22
|
+
lifecycle { states: [pending, active, suspended, closed], initial: pending }
|
|
23
|
+
action create {}
|
|
24
|
+
action activate { from: pending, to: active }
|
|
25
|
+
action suspend { from: active, to: suspended }
|
|
26
|
+
action resume { from: suspended, to: active }
|
|
27
|
+
action close { from: [active, suspended], to: closed }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
instrument card(holder: ref<cards.cardholder>, spend_limit: money) {
|
|
31
|
+
summary: "A card with a per-spend ceiling that can be activated, frozen or cancelled."
|
|
32
|
+
fields { holder: ref<holder>, spendLimit: money = spend_limit }
|
|
33
|
+
lifecycle { states: [issued, active, frozen, cancelled], initial: issued }
|
|
34
|
+
action create { requires self.holder in [active] }
|
|
35
|
+
action activate { from: issued, to: active, requires self.holder in [active] }
|
|
36
|
+
action freeze { from: active, to: frozen }
|
|
37
|
+
action unfreeze { from: frozen, to: active, requires self.holder in [active] }
|
|
38
|
+
action cancel { from: [issued, active, frozen], to: cancelled }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
instrument authorization(card: ref<cards.card>, merchant: party, issuer: party = programOperator) {
|
|
42
|
+
summary: "Reserve a card payment for the merchant, then capture it or release the reservation."
|
|
43
|
+
fields { card: ref<card>, merchant: account of merchant, amount: money, expiresAt: date, reservation: text? }
|
|
44
|
+
lifecycle { states: [pending, approved, captured, declined, reversed, expired], initial: pending }
|
|
45
|
+
action create {
|
|
46
|
+
requires self.card in [active]
|
|
47
|
+
requires self.card.holder in [active]
|
|
48
|
+
requires self.amount <= self.card.spendLimit
|
|
49
|
+
}
|
|
50
|
+
action approve {
|
|
51
|
+
from: pending, to: approved, actor: { party: issuer }, deadline: { at: self.expiresAt }
|
|
52
|
+
requires self.card in [active]
|
|
53
|
+
requires self.card.holder in [active]
|
|
54
|
+
moves reserve self.amount from self.card.holder.holder to merchant capture "reservation"
|
|
55
|
+
}
|
|
56
|
+
action capture { from: approved, to: captured, actor: { party: merchant }, deadline: { at: self.expiresAt }, moves post self.reservation }
|
|
57
|
+
action reverse { from: approved, to: reversed, actor: { party: merchant }, moves void self.reservation }
|
|
58
|
+
action decline { from: pending, to: declined, actor: { party: issuer } }
|
|
59
|
+
action expire { from: approved, to: expired, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
|
|
60
|
+
action abandon { from: pending, to: expired, actor: clock, due: { at: self.expiresAt } }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
instrument transaction(authorization: ref<cards.authorization>) {
|
|
64
|
+
summary: "Record a captured card payment and return its amount when refunded."
|
|
65
|
+
fields { authorization: ref<authorization>, amount: money = self.authorization.amount }
|
|
66
|
+
lifecycle { states: [posted, refunded], initial: posted }
|
|
67
|
+
action create { requires self.authorization in [captured]; requires unique "captured_authorization" on [self.authorization] }
|
|
68
|
+
action refund {
|
|
69
|
+
from: posted, to: refunded, actor: { party: programOperator }, input { reason: text }
|
|
70
|
+
moves self.amount from self.authorization.merchant to self.authorization.card.holder.holder
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
instrument dispute(transaction: ref<cards.transaction>, issuer: party = programOperator, within: duration = 90d) {
|
|
75
|
+
summary: "Review a disputed card payment before its deadline and refund it when the dispute is won."
|
|
76
|
+
fields { transaction: ref<transaction>, evidence: text, expiresAt: date = after(self.createdAt, within) }
|
|
77
|
+
lifecycle { states: [filed, reviewing, won, lost], initial: filed }
|
|
78
|
+
action create { requires self.transaction in [posted]; requires unique "disputed_transaction" on [self.transaction] }
|
|
79
|
+
action review { from: filed, to: reviewing, actor: { party: issuer } }
|
|
80
|
+
action win {
|
|
81
|
+
from: reviewing, to: won, actor: { party: issuer }, deadline: { at: self.expiresAt }
|
|
82
|
+
invoke: [{ reference: self.transaction, action: refund, input: { reason: { field: self.evidence } } }]
|
|
83
|
+
}
|
|
84
|
+
action lose { from: [filed, reviewing], to: lost, actor: clock, due: { at: self.expiresAt } }
|
|
85
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
header collections
|
|
2
|
+
|
|
3
|
+
// Economics: Company capital pays agencies as participant payouts; outside capital passes through.
|
|
4
|
+
// A company agency earns outside capital fees; its own capital fee is internal.
|
|
5
|
+
|
|
6
|
+
// Expose case.create, assign and recover as separate steps. Creation needs overdue
|
|
7
|
+
// outstanding principal and a matching slice/plan. Assignment belongs to capital;
|
|
8
|
+
// recovery belongs to agency and pays the linked payment before the agency fee.
|
|
9
|
+
// Payment must belong to the same plan. Recovered means that payment was processed,
|
|
10
|
+
// not that the entire debt was repaid. Fee debits plan.capital, not the borrower.
|
|
11
|
+
// Expose recall for capital and return_uncollected for agency when needed.
|
|
12
|
+
// Contacts need an assigned case, matching agency, local hours and rolling count.
|
|
13
|
+
// Contact outcomes are separate records. Reminder dispatch is operator-owned;
|
|
14
|
+
// before_due/overdue are clock-owned and require active debt. Scheduled admission
|
|
15
|
+
// does not prove delivery or retry. No collector can infer authority from an alias.
|
|
16
|
+
|
|
17
|
+
// Cost: case recovery invokes the linked payment and then charges its agency
|
|
18
|
+
// fee. Its count includes those child effects. Contact and reminder delivery costs
|
|
19
|
+
// are not provider prices in hsx cost; the executor supplies commercial tariffs.
|
|
20
|
+
|
|
21
|
+
instrument case(on: ref<financing.installments>[], agency: party, capital: party = programOperator, overdue: duration = 3d, fee: percent = 20%) {
|
|
22
|
+
revisioned: true
|
|
23
|
+
summary: "Refer an overdue installment without creating another debt."
|
|
24
|
+
fields { assignedAgency: account of agency, plan: ref<on>, slice: ref<on.slice>, payment: ref<on.payment>, eligibleAt: date = after(self.slice.dueAt, overdue), agencyFee: money = rate(self.payment.amount, fee) }
|
|
25
|
+
lifecycle { states: [opened, assigned, recovered, recalled, returned_uncollected], initial: opened }
|
|
26
|
+
action create {
|
|
27
|
+
due: { at: self.eligibleAt }
|
|
28
|
+
requires self.plan == self.slice.plan
|
|
29
|
+
requires self.slice.principalReceivable.balance > "0"
|
|
30
|
+
requires unique "overdue_referral" on [self.slice]
|
|
31
|
+
}
|
|
32
|
+
action assign { from: opened, to: assigned, actor: { party: capital } }
|
|
33
|
+
action recover { expansionLimit: 8192, from: assigned, to: recovered, actor: { party: agency }, requires self.payment.plan == self.plan, invoke: [{ reference: self.payment, action: pay, input: {} }, { reference: self.id, action: pay_fee, input: {} }] }
|
|
34
|
+
action pay_fee { allowZero: true, from: recovered, to: recovered, actor: { parent: current() }, moves self.agencyFee from self.plan.capital to agency economics { recipient: agency, company: { recipient: capital, company: { purpose: internal, sourceParty: capital }, participant: { purpose: earning, sourceParty: capital } }, participant: { recipient: capital, company: { purpose: participant_payout, sourceParty: capital }, participant: { purpose: pass_through, sourceParty: capital } } } }
|
|
35
|
+
action recall { from: [opened, assigned], to: recalled, actor: { party: capital } }
|
|
36
|
+
action return_uncollected { from: assigned, to: returned_uncollected, actor: { party: agency } }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
instrument 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") {
|
|
40
|
+
constraints { contact_from: less_than(contact_until) }
|
|
41
|
+
summary: "An agency contact admitted against the locked rolling count and local hours."
|
|
42
|
+
fields { agency: account of agency, case: ref<case>, attemptedAt: date?, evidence: text, maximum: integer = max_contacts }
|
|
43
|
+
lifecycle { states: [recorded], initial: recorded }
|
|
44
|
+
invariants count of { instrument: current(), reference: "case", anchor: self.case, states: [recorded], limit: 366, window: { field: attemptedAt, milliseconds: window } } <= self.maximum
|
|
45
|
+
action create {
|
|
46
|
+
actor: { party: agency }
|
|
47
|
+
set: { attemptedAt: { field: self.now } }
|
|
48
|
+
requires self.agency == self.case.assignedAgency; requires self.case in [assigned]; requires hours self.now between contact_from and contact_until timezone timezone
|
|
49
|
+
}
|
|
50
|
+
records {
|
|
51
|
+
outcome: {
|
|
52
|
+
fields { contact: ref<parent>, agency: account of agency, outcome: enum(reached, no_answer, promise_to_pay, disputed, wrong_contact, refused), evidence: text, recordedAt: date? }
|
|
53
|
+
lifecycle { states: [recorded], initial: recorded }
|
|
54
|
+
action create {
|
|
55
|
+
actor: { party: agency }
|
|
56
|
+
requires self.agency == self.contact.case.assignedAgency
|
|
57
|
+
set: { recordedAt: { field: self.now } }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
instrument reminder(on: ref<financing.installments>[], operator: party = programOperator, before_days: integer(0, 366) = 3, overdue_days: integer(1, 366) = 1, max_per_day: integer(1, 10) = 1, send_from: integer(0, 23) = 8, send_until: integer(1, 24) = 20, timezone: text = "Asia/Riyadh") {
|
|
65
|
+
constraints { send_from: less_than(send_until) }
|
|
66
|
+
summary: "Send an email to the installment borrower's stored contact address."
|
|
67
|
+
fields { slice: ref<on.slice> }
|
|
68
|
+
lifecycle { states: [ready], initial: ready }
|
|
69
|
+
action create { requires unique "installment_reminder" on [self.slice] }
|
|
70
|
+
action dispatch {
|
|
71
|
+
from: ready, to: ready, actor: { party: operator }
|
|
72
|
+
invoke: [{ reference: self.id, action: send, input: {} }]
|
|
73
|
+
}
|
|
74
|
+
action before_due {
|
|
75
|
+
from: ready, to: ready, actor: clock
|
|
76
|
+
due: { at: self.slice.dueAt, localDay: { days: before_days, direction: "before", hour: send_from, timezone: timezone } }
|
|
77
|
+
invoke: [{ selection: { instrument: current(), reference: "slice", anchor: self.slice, states: [ready], limit: 1, where: { slice.plan.status: { literal: "active" } } }, action: send, guard: { kind: compare, left: { field: self.slice.principalReceivable.balance }, operator: ">", right: { literal: "0" } }, input: {} }]
|
|
78
|
+
}
|
|
79
|
+
action overdue {
|
|
80
|
+
from: ready, to: ready, actor: clock
|
|
81
|
+
due: { at: self.slice.dueAt, localDay: { days: overdue_days, direction: "after", hour: send_from, timezone: timezone } }
|
|
82
|
+
invoke: [{ selection: { instrument: current(), reference: "slice", anchor: self.slice, states: [ready], limit: 1, where: { slice.plan.status: { literal: "active" } } }, action: send, guard: { kind: compare, left: { field: self.slice.principalReceivable.balance }, operator: ">", right: { literal: "0" } }, input: {} }]
|
|
83
|
+
}
|
|
84
|
+
action send {
|
|
85
|
+
from: ready, to: ready, actor: { parent: current() }
|
|
86
|
+
requires self.slice.plan in [active]
|
|
87
|
+
requires self.slice.principalReceivable.balance > "0"
|
|
88
|
+
reminder: { installment: self.slice, recipient: self.slice.plan.borrower, dueAt: self.slice.dueAt, channel: "email", template: "payment_reminder", beforeDays: before_days, overdueDays: overdue_days, maxPerDay: max_per_day, startHour: send_from, endHour: send_until, timezone: timezone }
|
|
89
|
+
}
|
|
90
|
+
}
|
package/std/escrow.hsx
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
header escrow
|
|
2
|
+
|
|
3
|
+
// Economics: Funding and unspent returns are principal. Acceptance realizes earnings for
|
|
4
|
+
// the company or a payout to a bound outside seller; tax is pass-through.
|
|
5
|
+
|
|
6
|
+
// Expose create, fund, deliver and accept for the purchase path. Create captures
|
|
7
|
+
// subject.price; acceptance pays payee less the configured fee, not payer.
|
|
8
|
+
// Expose dispute, verify_return and refund separately for the return path.
|
|
9
|
+
// Payer disputes delivered goods; payee verifies return evidence from disputed;
|
|
10
|
+
// payee refunds the entire held price from return_verified by default.
|
|
11
|
+
// Acceptance expiry enters disputed, never released. Neither verify_return nor
|
|
12
|
+
// refund becomes public merely because dispute is exposed. Clock actions remain
|
|
13
|
+
// internal. Expose cancel for a pending hold, including a checkout-funded hold;
|
|
14
|
+
// it returns its credited balance to payer before cancelling. Confirm requires
|
|
15
|
+
// the whole captured price in held. This flow has no partial release or refund.
|
|
16
|
+
|
|
17
|
+
// Cost: create declares one owned account. Fund and refund each declare one
|
|
18
|
+
// transfer; accept expands the configured fee and tax into moves. Run hsx cost
|
|
19
|
+
// on the composed program for instruction counts. The executor supplies prices.
|
|
20
|
+
|
|
21
|
+
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 }) {
|
|
22
|
+
revisioned: true
|
|
23
|
+
summary: "Hold the subject price through delivery, acceptance and verified return."
|
|
24
|
+
fields {
|
|
25
|
+
payer: account of payer, payee: account of payee
|
|
26
|
+
held: account of self, deliveredAt: date?
|
|
27
|
+
price: money = subject.price
|
|
28
|
+
}
|
|
29
|
+
lifecycle { states: [pending, funded, delivered, disputed, return_verified, released, refunded, cancelled], initial: pending }
|
|
30
|
+
action create {
|
|
31
|
+
subject { price: money }
|
|
32
|
+
actor: { party: payer }
|
|
33
|
+
}
|
|
34
|
+
action fund {
|
|
35
|
+
from: pending, to: funded, actor: { party: payer }
|
|
36
|
+
subject { price: money }
|
|
37
|
+
moves self.price from payer to self.held economics { purpose: principal, sourceParty: payer }
|
|
38
|
+
}
|
|
39
|
+
action confirm {
|
|
40
|
+
from: pending, to: funded
|
|
41
|
+
requires self.held.balance == self.price
|
|
42
|
+
}
|
|
43
|
+
action deliver {
|
|
44
|
+
from: funded, to: delivered, actor: { party: payee }
|
|
45
|
+
set: { deliveredAt: { field: self.now } }
|
|
46
|
+
}
|
|
47
|
+
action accept { allowZero: true, requires self.price > "0";
|
|
48
|
+
from: delivered, to: released, actor: { party: payer }
|
|
49
|
+
input { acceptanceReference: text }
|
|
50
|
+
deadline: { at: self.deliveredAt, offset: accept_within }
|
|
51
|
+
moves self.price from self.held to payee fee fee economics { payee: { recipient: payee, company: { purpose: earning, sourceParty: payer }, participant: { purpose: participant_payout, sourceParty: programOperator } }, fee: { purpose: earning, sourceParty: payer }, tax: { purpose: pass_through, sourceParty: programOperator } }
|
|
52
|
+
}
|
|
53
|
+
action expire_acceptance {
|
|
54
|
+
from: delivered, to: disputed, actor: clock
|
|
55
|
+
due: { at: self.deliveredAt, offset: accept_within }
|
|
56
|
+
}
|
|
57
|
+
action dispute {
|
|
58
|
+
from: delivered, to: disputed, actor: { party: payer }
|
|
59
|
+
deadline: { at: self.deliveredAt, offset: accept_within }
|
|
60
|
+
}
|
|
61
|
+
action verify_return {
|
|
62
|
+
from: disputed, to: return_verified, actor: { party: payee }
|
|
63
|
+
input { returnEvidence: text }
|
|
64
|
+
}
|
|
65
|
+
action refund {
|
|
66
|
+
from: dispute.refund_after, to: refunded, actor: { party: payee }
|
|
67
|
+
moves self.price from self.held to payer economics { purpose: principal, sourceParty: programOperator }
|
|
68
|
+
}
|
|
69
|
+
action cancel { allowZero: true,
|
|
70
|
+
from: pending, to: cancelled, actor: { party: payer }
|
|
71
|
+
moves self.held.balance from self.held to payer economics { purpose: principal, sourceParty: programOperator }
|
|
72
|
+
}
|
|
73
|
+
}
|