@hyperscale0/hsx 1.0.0-beta.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -9
- package/LICENSING.md +3 -3
- package/README.md +39 -149
- package/TRADEMARKS.md +4 -4
- package/bin/hsx.ts +2 -0
- package/dist/bin/hsx.js +2 -0
- package/dist/bin/hsx.js.map +1 -1
- package/dist/src/ast.d.ts +135 -10
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +24 -0
- package/dist/src/ast.js.map +1 -1
- package/dist/src/cli.d.ts +4 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +172 -16
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +42 -7
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +82 -16
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +78 -0
- package/dist/src/cost.d.ts.map +1 -0
- package/dist/src/cost.js +369 -0
- package/dist/src/cost.js.map +1 -0
- package/dist/src/diagnostics.d.ts +11 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +420 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/emit.d.ts +34 -0
- package/dist/src/emit.d.ts.map +1 -0
- package/dist/src/emit.js +137 -0
- package/dist/src/emit.js.map +1 -0
- package/dist/src/entry-overrides.d.ts +17 -3
- package/dist/src/entry-overrides.d.ts.map +1 -1
- package/dist/src/entry-overrides.js +180 -35
- package/dist/src/entry-overrides.js.map +1 -1
- package/dist/src/format.d.ts +10 -0
- package/dist/src/format.d.ts.map +1 -0
- package/dist/src/format.js +183 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/index.d.ts +11 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +10 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/ir.d.ts +66 -0
- package/dist/src/ir.d.ts.map +1 -0
- package/dist/src/ir.js +15 -0
- package/dist/src/ir.js.map +1 -0
- package/dist/src/lex.d.ts +9 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +50 -4
- package/dist/src/lex.js.map +1 -1
- package/dist/src/limits.d.ts +2 -3
- package/dist/src/limits.d.ts.map +1 -1
- package/dist/src/limits.js +2 -3
- package/dist/src/limits.js.map +1 -1
- package/dist/src/lsp/server.d.ts +32 -0
- package/dist/src/lsp/server.d.ts.map +1 -0
- package/dist/src/lsp/server.js +391 -0
- package/dist/src/lsp/server.js.map +1 -0
- package/dist/src/modules.d.ts +38 -0
- package/dist/src/modules.d.ts.map +1 -0
- package/dist/src/modules.js +368 -0
- package/dist/src/modules.js.map +1 -0
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +590 -17
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts +2 -0
- package/dist/src/std-bundle.d.ts.map +1 -0
- package/dist/src/std-bundle.js +88 -0
- package/dist/src/std-bundle.js.map +1 -0
- package/dist/src/std-library.d.ts +5 -0
- package/dist/src/std-library.d.ts.map +1 -0
- package/dist/src/std-library.js +9 -0
- package/dist/src/std-library.js.map +1 -0
- package/dist/src/typecheck.d.ts +8 -0
- package/dist/src/typecheck.d.ts.map +1 -0
- package/dist/src/typecheck.js +2958 -0
- package/dist/src/typecheck.js.map +1 -0
- package/dist/src/version.d.ts +4 -15
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +4 -15
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +26 -0
- package/docs/guide/01-first-program.md +24 -0
- package/docs/guide/02-money.md +25 -0
- package/docs/guide/03-instruments.md +22 -0
- package/docs/guide/04-lifecycles.md +25 -0
- package/docs/guide/05-fees-and-splits.md +45 -0
- package/docs/guide/06-schedules.md +22 -0
- package/docs/guide/07-composition.md +46 -0
- package/docs/guide/08-writing-a-module.md +26 -0
- package/docs/guide/09-cost.md +24 -0
- package/docs/guide/10-diagnostics.md +27 -0
- package/docs/llms-full.txt +1739 -0
- package/docs/llms.txt +45 -0
- package/docs/playground.md +54 -0
- package/docs/reference/cli.md +35 -0
- package/docs/reference/diagnostics.md +619 -0
- package/docs/reference/grammar.md +39 -0
- package/docs/reference/std/advance.md +35 -0
- package/docs/reference/std/cancellable_booking.md +35 -0
- package/docs/reference/std/captured_payment.md +43 -0
- package/docs/reference/std/conditional_disbursement.md +33 -0
- package/docs/reference/std/credit_facility.md +35 -0
- package/docs/reference/std/held_payment.md +64 -0
- package/docs/reference/std/instant_transfer.md +30 -0
- package/docs/reference/std/metered.md +27 -0
- package/docs/reference/std/pooled_split.md +26 -0
- package/docs/reference/std/premium_forward.md +39 -0
- package/docs/reference/std/reconciled_payout.md +33 -0
- package/docs/reference/std/recurring_collection.md +25 -0
- package/docs/reference/std/rotating_pool.md +42 -0
- package/docs/reference/std/scheduled.md +51 -0
- package/docs/reference/std/security_deposit.md +46 -0
- package/docs/reference/std/settlement_batch.md +42 -0
- package/docs/reference/std/swap.md +46 -0
- package/docs/reference/std/threshold_pool.md +47 -0
- package/docs/reference/std/weighted_distribution.md +39 -0
- package/docs/reference/types.md +20 -0
- package/docs/reference/udl-output.md +13 -0
- package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
- package/docs/sessions/2026-09-02-two-instruments.md +41 -0
- package/examples/01-first-program/README.md +45 -0
- package/examples/01-first-program/tip-jar.hsx +17 -0
- package/examples/02-imports-and-archetypes/README.md +96 -0
- package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
- package/examples/03-diagnostics/README.md +68 -0
- package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
- package/examples/03-diagnostics/corner-shop.hsx +19 -0
- package/examples/04-complete-product/README.md +84 -0
- package/examples/04-complete-product/study-hall.hsx +67 -0
- package/examples/05-watch-club/README.md +9 -0
- package/examples/05-watch-club/watch-club.hsx +124 -0
- package/examples/README.md +18 -0
- package/examples/advance/README.md +3 -0
- package/examples/advance/advance.hsx +13 -0
- package/examples/advance/advance.udl +339 -0
- package/examples/cancellable_booking/README.md +3 -0
- package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
- package/examples/cancellable_booking/cancellable_booking.udl +391 -0
- package/examples/captured_payment/README.md +3 -0
- package/examples/captured_payment/captured_payment.hsx +21 -0
- package/examples/captured_payment/captured_payment.udl +677 -0
- package/examples/conditional_disbursement/README.md +3 -0
- package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
- package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
- package/examples/cost-table.json +386 -0
- package/examples/credit_facility/README.md +3 -0
- package/examples/credit_facility/credit_facility.hsx +27 -0
- package/examples/credit_facility/credit_facility.udl +1373 -0
- package/examples/held_payment/README.md +3 -0
- package/examples/held_payment/held_payment.hsx +13 -0
- package/examples/held_payment/held_payment.udl +507 -0
- package/examples/instant_transfer/README.md +3 -0
- package/examples/instant_transfer/instant_transfer.hsx +16 -0
- package/examples/instant_transfer/instant_transfer.udl +364 -0
- package/examples/metered/README.md +3 -0
- package/examples/metered/metered.hsx +13 -0
- package/examples/metered/metered.udl +247 -0
- package/examples/pooled_split/README.md +3 -0
- package/examples/pooled_split/pooled_split.hsx +15 -0
- package/examples/pooled_split/pooled_split.udl +425 -0
- package/examples/premium_forward/README.md +3 -0
- package/examples/premium_forward/premium_forward.hsx +19 -0
- package/examples/premium_forward/premium_forward.udl +594 -0
- package/examples/reconciled_payout/README.md +3 -0
- package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
- package/examples/reconciled_payout/reconciled_payout.udl +303 -0
- package/examples/recurring_collection/README.md +3 -0
- package/examples/recurring_collection/recurring_collection.hsx +21 -0
- package/examples/recurring_collection/recurring_collection.udl +1130 -0
- package/examples/rotating_pool/README.md +3 -0
- package/examples/rotating_pool/rotating_pool.hsx +18 -0
- package/examples/rotating_pool/rotating_pool.udl +4352 -0
- package/examples/scheduled/README.md +3 -0
- package/examples/scheduled/scheduled.hsx +12 -0
- package/examples/scheduled/scheduled.udl +335 -0
- package/examples/security_deposit/README.md +3 -0
- package/examples/security_deposit/security_deposit.hsx +21 -0
- package/examples/security_deposit/security_deposit.udl +293 -0
- package/examples/settlement_batch/README.md +3 -0
- package/examples/settlement_batch/settlement_batch.hsx +19 -0
- package/examples/settlement_batch/settlement_batch.udl +753 -0
- package/examples/swap/README.md +3 -0
- package/examples/swap/swap.hsx +19 -0
- package/examples/swap/swap.udl +876 -0
- package/examples/threshold_pool/README.md +3 -0
- package/examples/threshold_pool/threshold_pool.hsx +16 -0
- package/examples/threshold_pool/threshold_pool.udl +667 -0
- package/examples/weighted_distribution/README.md +3 -0
- package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
- package/examples/weighted_distribution/weighted_distribution.udl +337 -0
- package/package.json +27 -6
- package/skills/hsx/SKILL.md +483 -0
- package/src/ast.ts +193 -9
- package/src/cli.ts +191 -20
- package/src/compile.ts +160 -23
- package/src/cost.ts +579 -0
- package/src/diagnostics.ts +442 -0
- package/src/emit.ts +201 -0
- package/src/entry-overrides.ts +263 -48
- package/src/format.ts +261 -0
- package/src/index.ts +34 -4
- package/src/ir.ts +99 -0
- package/src/lex.ts +59 -4
- package/src/limits.ts +2 -3
- package/src/lsp/server.ts +460 -0
- package/src/modules.ts +446 -0
- package/src/parse.ts +655 -21
- package/src/std-bundle.ts +91 -0
- package/src/std-library.ts +12 -0
- package/src/typecheck.ts +3876 -0
- package/src/version.ts +4 -16
- package/std/SEMANTICS.md +130 -0
- package/std/settlements/advance.hsx +177 -0
- package/std/settlements/cancellable_booking.hsx +87 -0
- package/std/settlements/captured_payment.hsx +144 -0
- package/std/settlements/conditional_disbursement.hsx +185 -0
- package/std/settlements/credit_facility.hsx +167 -0
- package/std/settlements/held_payment.hsx +578 -0
- package/std/settlements/index.hsx +3 -0
- package/std/settlements/instant_transfer.hsx +190 -0
- package/std/settlements/metered.hsx +67 -0
- package/std/settlements/pooled_split.hsx +68 -0
- package/std/settlements/premium_forward.hsx +257 -0
- package/std/settlements/reconciled_payout.hsx +77 -0
- package/std/settlements/recurring_collection.hsx +27 -0
- package/std/settlements/rotating_pool.hsx +252 -0
- package/std/settlements/scheduled.hsx +641 -0
- package/std/settlements/security_deposit.hsx +166 -0
- package/std/settlements/settlement_batch.hsx +122 -0
- package/std/settlements/swap.hsx +1114 -0
- package/std/settlements/threshold_pool.hsx +221 -0
- package/std/settlements/weighted_distribution.hsx +141 -0
- package/dist/src/archetypes.d.ts +0 -17
- package/dist/src/archetypes.d.ts.map +0 -1
- package/dist/src/archetypes.js +0 -327
- package/dist/src/archetypes.js.map +0 -1
- package/dist/src/check.d.ts +0 -12
- package/dist/src/check.d.ts.map +0 -1
- package/dist/src/check.js +0 -1910
- package/dist/src/check.js.map +0 -1
- package/dist/src/lower.d.ts +0 -146
- package/dist/src/lower.d.ts.map +0 -1
- package/dist/src/lower.js +0 -4570
- package/dist/src/lower.js.map +0 -1
- package/dist/src/model.d.ts +0 -467
- package/dist/src/model.d.ts.map +0 -1
- package/dist/src/model.js +0 -15
- package/dist/src/model.js.map +0 -1
- package/spec/hsx-ir.schema.json +0 -920
- package/src/archetypes.ts +0 -340
- package/src/check.ts +0 -2938
- package/src/lower.ts +0 -5416
- package/src/model.ts +0 -519
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
module std.settlements.threshold_pool
|
|
2
|
+
|
|
3
|
+
export instrument threshold_pool<C>(
|
|
4
|
+
contributor: optional<party>,
|
|
5
|
+
beneficiary: optional<party>,
|
|
6
|
+
target: money<C>,
|
|
7
|
+
commitment: money<C>,
|
|
8
|
+
max_contributors: optional<integer>,
|
|
9
|
+
close_by: date,
|
|
10
|
+
close_policy: optional<text>,
|
|
11
|
+
overfund_policy: optional<text>,
|
|
12
|
+
cancel_policy: optional<text>,
|
|
13
|
+
fail_policy: optional<text>,
|
|
14
|
+
beneficiary_account: optional<account<C>>,
|
|
15
|
+
memo: optional<text>,
|
|
16
|
+
contribution_instrument: optional<block>,
|
|
17
|
+
wording: optional<block>,
|
|
18
|
+
) {
|
|
19
|
+
when(wording) {
|
|
20
|
+
let(wording_unknown_keys): keys_except(wording, "pool_summary", "pool_description", "create_summary", "commitment_create_summary", "commitment_cancel_summary", "commitment_refund_summary");
|
|
21
|
+
when(wording_unknown_keys) {
|
|
22
|
+
unsupported {
|
|
23
|
+
code: HSX1407;
|
|
24
|
+
message: "threshold_pool wording contains an unknown key";
|
|
25
|
+
fix: "use only the six declared threshold_pool wording keys";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
agent_description: "Reach for threshold pool when many contributors commit toward one target and nothing is collected unless the pool reaches it by the close date. Commitments sit in escrow until the pool activates or fails. Pick weighted distribution to pay a pool out and instant transfer for a single payment.";
|
|
30
|
+
when_not(contribution_instrument) {
|
|
31
|
+
fields {
|
|
32
|
+
[beneficiary]AccountId { type: account<C>; description: concat("The ", words(beneficiary), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
33
|
+
close_by { type: date; description: "Stored close anchor"; }
|
|
34
|
+
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
35
|
+
maxContributors { type: integer; description: concat("Exactly ", max_contributors, " admitted contributors"); const: max_contributors; }
|
|
36
|
+
target { type: money<C>; description: "Funding target in {C} minor units"; }
|
|
37
|
+
}
|
|
38
|
+
lifecycle {
|
|
39
|
+
states open active failed settled;
|
|
40
|
+
initial open;
|
|
41
|
+
on activate: open -> active;
|
|
42
|
+
on fail: open -> failed;
|
|
43
|
+
on close: active -> settled;
|
|
44
|
+
}
|
|
45
|
+
parties { beneficiary: concat(camel(beneficiary), "AccountId"); }
|
|
46
|
+
required: [concat(camel(beneficiary), "AccountId"), currency, target, close_by, maxContributors];
|
|
47
|
+
title: sentence(instrument);
|
|
48
|
+
id_prefix: prefix(instrument);
|
|
49
|
+
let(pool_summary): get(wording, "pool_summary");
|
|
50
|
+
when_not(pool_summary) { summary: concat("Threshold contribution pool for ", words(beneficiary)); }
|
|
51
|
+
when(pool_summary) { summary: pool_summary; }
|
|
52
|
+
let(pool_description): get(wording, "pool_description");
|
|
53
|
+
when_not(pool_description) { description: "All-or-nothing aggregate contribution threshold"; }
|
|
54
|
+
when(pool_description) { description: pool_description; }
|
|
55
|
+
aggregate_invariants { childField: amount; childInstrumentId: concat(instrument, "_commitment"); childRefField: concat(camel(instrument), "Id"); childStatuses: [committed]; parentField: target; }
|
|
56
|
+
aggregate_invariants { childInstrumentId: concat(instrument, "_commitment"); childRefField: concat(camel(instrument), "Id"); childStatuses: [committed]; count: true; parentField: maxContributors; }
|
|
57
|
+
action create {
|
|
58
|
+
agent_description: "Open the pool before any commitment exists. The caller supplies the target, the currency, the beneficiary account, the close anchor, and the pinned contributor count. No money moves and the pool opens for commitments.";
|
|
59
|
+
let(create_summary): get(wording, "create_summary");
|
|
60
|
+
when_not(create_summary) { summary: "Open the contribution pool"; }
|
|
61
|
+
when(create_summary) { summary: create_summary; }
|
|
62
|
+
moves: [];
|
|
63
|
+
steps: [];
|
|
64
|
+
}
|
|
65
|
+
action activate {
|
|
66
|
+
summary: "Activate when commitments meet the target";
|
|
67
|
+
due { field: close_by; }
|
|
68
|
+
requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [committed]; check: { kind: sum_at_least; amountField: amount; targetField: target; }; }
|
|
69
|
+
moves: [];
|
|
70
|
+
steps: [];
|
|
71
|
+
}
|
|
72
|
+
action fail {
|
|
73
|
+
summary: "Fail when commitments remain below target";
|
|
74
|
+
due { field: close_by; }
|
|
75
|
+
requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [committed]; check: { kind: sum_below; amountField: amount; targetField: target; }; }
|
|
76
|
+
moves: [];
|
|
77
|
+
steps: [];
|
|
78
|
+
}
|
|
79
|
+
action close {
|
|
80
|
+
agent_description: "Settle the pool once every admitted commitment is collected or was cancelled before activation. The pool must be active. No money moves here, collect already paid the beneficiary.";
|
|
81
|
+
summary: "Settle after every admitted row is collected or was cancelled before activation";
|
|
82
|
+
requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [cancelled, collected]; check: { kind: all_in; }; }
|
|
83
|
+
moves: [];
|
|
84
|
+
steps: [];
|
|
85
|
+
}
|
|
86
|
+
instruments {
|
|
87
|
+
instrument {
|
|
88
|
+
let(parent_name): instrument;
|
|
89
|
+
id: concat(parent_name, "_commitment");
|
|
90
|
+
generatedPrefix: true;
|
|
91
|
+
fields {
|
|
92
|
+
amount { type: money<C>; description: "One whole commitment amount"; }
|
|
93
|
+
[beneficiary]AccountId { type: account<C>; description: concat("The ", words(beneficiary), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
94
|
+
[parent_name]Id { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
|
|
95
|
+
[contributor]AccountId { type: account<C>; description: concat("The ", words(contributor), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
96
|
+
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
97
|
+
}
|
|
98
|
+
lifecycle {
|
|
99
|
+
states committed cancelled collected refunded;
|
|
100
|
+
initial committed;
|
|
101
|
+
on cancel: committed -> cancelled;
|
|
102
|
+
on collect: committed -> collected;
|
|
103
|
+
on refund: committed -> refunded;
|
|
104
|
+
}
|
|
105
|
+
parties { beneficiary: concat(camel(beneficiary), "AccountId"); payer: concat(camel(contributor), "AccountId"); }
|
|
106
|
+
required: [concat(camel(beneficiary), "AccountId"), concat(camel(contributor), "AccountId"), amount, currency, concat(camel(parent_name), "Id")];
|
|
107
|
+
title: concat(sentence(parent_name), " Commitment");
|
|
108
|
+
agent_description: concat("Reach for this child row to record one whole commitment into a ", words(parent_name), ". Each contributor commits once, the money sits in the product escrow, and whether it is collected or refunded depends on the pool reaching its target.");
|
|
109
|
+
summary: concat("Whole commitment to ", parent_name);
|
|
110
|
+
description: concat("One whole commitment linked to ", parent_name);
|
|
111
|
+
action create {
|
|
112
|
+
agent_description: "Commit one whole amount into the pool escrow. The pool must still be open, the currency and beneficiary account must match it, and the committed total plus this amount must stay within the target. The money leaves the contributor now.";
|
|
113
|
+
let(commitment_create_summary): get(wording, "commitment_create_summary");
|
|
114
|
+
when_not(commitment_create_summary) { summary: "Store one whole commitment without exceeding the pool target"; }
|
|
115
|
+
when(commitment_create_summary) { summary: commitment_create_summary; }
|
|
116
|
+
requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; bind: { [beneficiary]AccountId: concat("fields.", camel(beneficiary), "AccountId"); currency: "fields.currency"; }; }
|
|
117
|
+
requires exposure { amountField: amount; anchorField: concat(camel(parent_name), "Id"); capField: target; capOnAnchor: true; childInstrumentId: concat(parent_name, "_commitment"); statuses: [committed]; }
|
|
118
|
+
moves: [{ amount: amount; from: payer; to: refs.escrowAccountId; key: commit; }];
|
|
119
|
+
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: "fields.currency"; }; "owner.id": { from: instance; path: productId; }; "owner.type": { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { escrowAccountId: accountId; }; }];
|
|
120
|
+
}
|
|
121
|
+
action cancel {
|
|
122
|
+
agent_description: "Pull one commitment back out of escrow to the contributor. The pool must still be open, so this is unavailable once the pool activates or fails. The money returns and the commitment is closed.";
|
|
123
|
+
let(commitment_cancel_summary): get(wording, "commitment_cancel_summary");
|
|
124
|
+
when_not(commitment_cancel_summary) { summary: "Cancel one commitment while the pool is open"; }
|
|
125
|
+
when(commitment_cancel_summary) { summary: commitment_cancel_summary; }
|
|
126
|
+
requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
|
|
127
|
+
moves: [{ amount: amount; from: refs.escrowAccountId; to: payer; key: cancel; }];
|
|
128
|
+
steps: [];
|
|
129
|
+
}
|
|
130
|
+
action collect {
|
|
131
|
+
agent_description: "Release one commitment out of escrow to the beneficiary. The pool must be active, which happens only once commitments meet the target. The money moves for good.";
|
|
132
|
+
summary: "Collect one successful commitment whole";
|
|
133
|
+
requires refs { field: concat(camel(parent_name), "Id"); statuses: [active]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
|
|
134
|
+
moves: [{ amount: amount; from: refs.escrowAccountId; to: beneficiary; key: collect; }];
|
|
135
|
+
steps: [];
|
|
136
|
+
}
|
|
137
|
+
action refund {
|
|
138
|
+
agent_description: "Return one commitment out of escrow to the contributor after the pool failed to reach its target. The pool must be in failed. The money returns and the commitment is closed.";
|
|
139
|
+
let(commitment_refund_summary): get(wording, "commitment_refund_summary");
|
|
140
|
+
when_not(commitment_refund_summary) { summary: "Refund one failed-pool commitment whole"; }
|
|
141
|
+
when(commitment_refund_summary) { summary: commitment_refund_summary; }
|
|
142
|
+
requires refs { field: concat(camel(parent_name), "Id"); statuses: [failed]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
|
|
143
|
+
moves: [{ amount: amount; from: refs.escrowAccountId; to: payer; key: refund; }];
|
|
144
|
+
steps: [];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
when(contribution_instrument) {
|
|
151
|
+
let(contribution_id): get(contribution_instrument, "instrument");
|
|
152
|
+
let(contribution_ref_field): get(contribution_instrument, "ref_field");
|
|
153
|
+
let(contribution_amount_field): get(contribution_instrument, "amount_field");
|
|
154
|
+
let(committed_status): get(contribution_instrument, "committed_status");
|
|
155
|
+
let(collected_status): get(contribution_instrument, "collected_status");
|
|
156
|
+
let(instrument_id_prefix): get(contribution_instrument, "id_prefix");
|
|
157
|
+
let(active_parked): get(contribution_instrument, "active_parked");
|
|
158
|
+
let(custody_ref): get(contribution_instrument, "custody_ref");
|
|
159
|
+
let(beneficiary_account_description): get(contribution_instrument, "beneficiary_account_description");
|
|
160
|
+
let(target_description): get(contribution_instrument, "target_description");
|
|
161
|
+
let(currency_description): get(contribution_instrument, "currency_description");
|
|
162
|
+
let(create_examples): get(contribution_instrument, "create_examples");
|
|
163
|
+
let(activate_examples): get(contribution_instrument, "activate_examples");
|
|
164
|
+
let(fail_examples): get(contribution_instrument, "fail_examples");
|
|
165
|
+
let(close_examples): get(contribution_instrument, "close_examples");
|
|
166
|
+
let(beneficiary_account_field): camel(beneficiary_account);
|
|
167
|
+
let(target_field): camel(target);
|
|
168
|
+
let(close_field): camel(close_by);
|
|
169
|
+
|
|
170
|
+
fields {
|
|
171
|
+
[beneficiary_account_field] { type: account<C>; description: beneficiary_account_description; pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
172
|
+
[close_field] { type: date; }
|
|
173
|
+
currency { type: text; description: currency_description; maxLength: 3; minLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
174
|
+
when(memo) { [memo] { type: text; optional: true; maxLength: 180; minLength: 1; } }
|
|
175
|
+
[target_field] { type: money<C>; description: target_description; }
|
|
176
|
+
}
|
|
177
|
+
lifecycle {
|
|
178
|
+
states open active failed settled;
|
|
179
|
+
initial open;
|
|
180
|
+
on activate: open -> active;
|
|
181
|
+
on close: active -> settled;
|
|
182
|
+
on fail: open -> failed;
|
|
183
|
+
}
|
|
184
|
+
parties { beneficiary: beneficiary_account_field; }
|
|
185
|
+
required: [beneficiary_account_field, target_field, currency, close_field];
|
|
186
|
+
aggregate_invariants { childField: contribution_amount_field; childInstrumentId: contribution_id; childRefField: contribution_ref_field; childStatuses: [committed_status, collected_status]; parentField: target_field; }
|
|
187
|
+
caller_parked_states { active: active_parked; }
|
|
188
|
+
id_prefix: instrument_id_prefix;
|
|
189
|
+
action create {
|
|
190
|
+
public: none;
|
|
191
|
+
agent_description: "Open the pool before any contribution exists. The caller supplies the target, the currency, the beneficiary account, and the close anchor, and the pool provisions its escrow account. No money moves.";
|
|
192
|
+
examples: create_examples;
|
|
193
|
+
moves: [];
|
|
194
|
+
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: "fields.currency"; }; "owner.id": { from: instance; path: productId; }; "owner.type": { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { [custody_ref]: accountId; }; }];
|
|
195
|
+
}
|
|
196
|
+
action activate {
|
|
197
|
+
public: none;
|
|
198
|
+
due { field: close_field; }
|
|
199
|
+
examples: activate_examples;
|
|
200
|
+
requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [committed_status]; check: { kind: sum_at_least; amountField: contribution_amount_field; targetField: target_field; }; }
|
|
201
|
+
moves: [];
|
|
202
|
+
steps: [];
|
|
203
|
+
}
|
|
204
|
+
action fail {
|
|
205
|
+
public: none;
|
|
206
|
+
due { field: close_field; }
|
|
207
|
+
examples: fail_examples;
|
|
208
|
+
requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [committed_status]; check: { kind: sum_below; amountField: contribution_amount_field; targetField: target_field; }; }
|
|
209
|
+
moves: [];
|
|
210
|
+
steps: [];
|
|
211
|
+
}
|
|
212
|
+
action close {
|
|
213
|
+
public: none;
|
|
214
|
+
agent_description: "Settle the pool once every contribution has been collected into the beneficiary account. No money moves. Refused while any contribution is still uncollected, so wait for the per-contribution collections to run rather than retrying.";
|
|
215
|
+
examples: close_examples;
|
|
216
|
+
requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [collected_status]; check: { kind: all_in; }; }
|
|
217
|
+
moves: [];
|
|
218
|
+
steps: [];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
module std.settlements.weighted_distribution
|
|
2
|
+
|
|
3
|
+
export instrument weighted_distribution<C>(
|
|
4
|
+
source: party,
|
|
5
|
+
recipient: party,
|
|
6
|
+
amount: money<C>,
|
|
7
|
+
record_at: date,
|
|
8
|
+
weight: optional<money<C>>,
|
|
9
|
+
max_recipients: optional<integer>,
|
|
10
|
+
snapshot: optional<condition>,
|
|
11
|
+
rounding_policy: optional<text>,
|
|
12
|
+
withholding_policy: optional<text>,
|
|
13
|
+
correction_policy: optional<text>,
|
|
14
|
+
flat: optional<block>,
|
|
15
|
+
id_prefix_override: optional<text>,
|
|
16
|
+
) {
|
|
17
|
+
when_not(flat) {
|
|
18
|
+
fields {
|
|
19
|
+
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
20
|
+
amount { type: money<C>; description: "Distribution pool in {C} minor units"; }
|
|
21
|
+
[source]AccountId { type: account<C>; description: concat("The ", words(source), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
22
|
+
maxRecipients { type: integer; description: concat("Exactly ", max_recipients, " frozen entitlement rows"); const: max_recipients; }
|
|
23
|
+
record_at { type: date; description: "Stored record date"; }
|
|
24
|
+
}
|
|
25
|
+
lifecycle { states open snapshotted; initial open; on [snapshot]: open -> snapshotted; }
|
|
26
|
+
parties { payer: concat(camel(source), "AccountId"); }
|
|
27
|
+
required: [concat(camel(source), "AccountId"), currency, amount, record_at, maxRecipients];
|
|
28
|
+
title: sentence(instrument);
|
|
29
|
+
id_prefix: prefix(instrument);
|
|
30
|
+
agent_description: "Reach for weighted distribution when one pool splits across many recipients by declared weights. Record one entitlement per recipient, freeze the set with the snapshot port, then each entitlement pays its own largest-remainder share, so the shares add back to the pool exactly. Settlement batch goes the other way, many charges into one payout.";
|
|
31
|
+
summary: "Frozen largest-remainder distribution";
|
|
32
|
+
description: "Evidence-frozen weighted distribution";
|
|
33
|
+
aggregate_invariants { childInstrumentId: concat(instrument, "_entitlement"); childRefField: concat(camel(instrument), "Id"); childStatuses: [recorded, paid]; count: true; parentField: maxRecipients; }
|
|
34
|
+
action create { agent_description: "Open a distribution and start recording entitlements. No money moves. The pool amount, the record date, and the exact recipient count are fixed here."; summary: "Open entitlement recording"; moves: []; steps: []; }
|
|
35
|
+
action [snapshot] {
|
|
36
|
+
agent_description: "Freeze the entitlement set against a stated evidence reference. The distribution must still be open and its recorded entitlements must match the declared recipient count. Nothing can be recorded afterwards and there is no unfreeze. No money moves.";
|
|
37
|
+
summary: "Freeze the entitlement set from stored evidence";
|
|
38
|
+
port { allowedParties: [payer]; }
|
|
39
|
+
input { type: object; properties: { evidenceReference: { type: string; description: "Required decision reference retained in the operation receipt"; minLength: 1; maxLength: 180; }; }; required: [evidenceReference]; additionalProperties: false; }
|
|
40
|
+
capture_input { snapshotEvidenceReference: evidenceReference; }
|
|
41
|
+
moves: [];
|
|
42
|
+
steps: [];
|
|
43
|
+
}
|
|
44
|
+
instruments {
|
|
45
|
+
instrument {
|
|
46
|
+
let(parent_name): instrument;
|
|
47
|
+
id: concat(parent_name, "_entitlement");
|
|
48
|
+
generatedPrefix: true;
|
|
49
|
+
fields {
|
|
50
|
+
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
51
|
+
[source]AccountId { type: account<C>; description: concat("The ", words(source), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
52
|
+
[weight] { type: money<C>; description: "Stored non-negative entitlement weight"; }
|
|
53
|
+
[parent_name]Id { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
|
|
54
|
+
[recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
55
|
+
}
|
|
56
|
+
lifecycle { states recorded paid; initial recorded; on payout: recorded -> paid; }
|
|
57
|
+
parties { beneficiary: concat(camel(recipient), "AccountId"); payer: concat(camel(source), "AccountId"); }
|
|
58
|
+
required: [concat(camel(recipient), "AccountId"), concat(camel(source), "AccountId"), currency, concat(camel(parent_name), "Id"), weight];
|
|
59
|
+
title: concat(sentence(parent_name), " Entitlement");
|
|
60
|
+
agent_description: concat("One recipient weighted claim on ", parent_name, ". Record one per recipient before the snapshot. Its share is derived from its weight against the frozen set, never supplied by the caller.");
|
|
61
|
+
summary: concat("Frozen entitlement in ", parent_name);
|
|
62
|
+
description: concat("One frozen weighted entitlement in ", parent_name);
|
|
63
|
+
action create {
|
|
64
|
+
agent_description: "Record one recipient weight against an open distribution. The parent distribution must still be open, and the currency and source account are copied from it. No money moves. Do not supply a share. It is derived later from the frozen set.";
|
|
65
|
+
summary: "Record one entitlement before snapshot";
|
|
66
|
+
requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; bind: { currency: "fields.currency"; [source]AccountId: concat("fields.", camel(source), "AccountId"); }; }
|
|
67
|
+
moves: [];
|
|
68
|
+
steps: [];
|
|
69
|
+
}
|
|
70
|
+
action payout {
|
|
71
|
+
agent_description: "Pay this recipient their largest-remainder share of the pool. The parent distribution must already be snapshotted. The amount is computed from the recorded weights, not supplied by the caller, and a zero share moves nothing. This moves money and does not reverse.";
|
|
72
|
+
summary: "Pay the deterministic largest-remainder share once";
|
|
73
|
+
requires refs { field: concat(camel(parent_name), "Id"); statuses: [snapshotted]; match: { "fields.currency": "fields.currency"; "fields.[source]AccountId": concat("fields.", camel(source), "AccountId"); }; }
|
|
74
|
+
computes distribute { amountRef: payoutShare; onZero: skip_steps; pool: { from: parent; path: concat("fields.", amount); }; refField: concat(camel(parent_name), "Id"); statuses: [recorded, paid]; weightField: weight; }
|
|
75
|
+
moves: [{ amount: refs.payoutShare; from: payer; to: beneficiary; key: payout; }];
|
|
76
|
+
steps: [];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
when(flat) {
|
|
82
|
+
let(pool): get(flat, "pool");
|
|
83
|
+
let(pool_ref): get(pool, "field");
|
|
84
|
+
let(pool_ref_field): camel(pool_ref);
|
|
85
|
+
let(pool_instrument): get(pool, "instrument");
|
|
86
|
+
let(pool_create_statuses): get(pool, "create_statuses");
|
|
87
|
+
let(pool_payout_statuses): get(pool, "payout_statuses");
|
|
88
|
+
let(pool_bind): get(pool, "bind");
|
|
89
|
+
let(pool_match): get(pool, "match");
|
|
90
|
+
let(pool_amount_path): get(pool, "amount_path");
|
|
91
|
+
let(weight_source): get(flat, "weight_source");
|
|
92
|
+
let(weight_ref): get(weight_source, "field");
|
|
93
|
+
let(weight_ref_field): camel(weight_ref);
|
|
94
|
+
let(weight_statuses): get(weight_source, "statuses");
|
|
95
|
+
let(weight_bind): get(weight_source, "bind");
|
|
96
|
+
let(weight_match): get(weight_source, "match");
|
|
97
|
+
let(unique_weight_source): get(weight_source, "unique");
|
|
98
|
+
let(group): get(flat, "group");
|
|
99
|
+
let(group_ref): get(group, "field");
|
|
100
|
+
let(source_account_field): camel(concat(source, "_account_id"));
|
|
101
|
+
let(recipient_account_field): camel(concat(recipient, "_account_id"));
|
|
102
|
+
fields {
|
|
103
|
+
[amount] { type: money<C>; description: get(flat, "amount_description"); }
|
|
104
|
+
[record_at] { type: date; }
|
|
105
|
+
[pool_ref] { type: pool_ref; description: get(pool, "description"); pattern: get(pool, "pattern"); }
|
|
106
|
+
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
107
|
+
[weight_ref] { type: weight_ref; description: get(weight_source, "description"); pattern: get(weight_source, "pattern"); }
|
|
108
|
+
[group_ref] { type: group_ref; description: get(group, "description"); pattern: get(group, "pattern"); }
|
|
109
|
+
[recipient]AccountId { type: account<C>; description: get(flat, "recipient_account_description"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
110
|
+
[source]AccountId { type: account<C>; description: get(flat, "source_account_description"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
111
|
+
}
|
|
112
|
+
lifecycle { states claimed paid; initial claimed; on payout: claimed -> paid; }
|
|
113
|
+
parties { beneficiary: recipient_account_field; }
|
|
114
|
+
required: get(flat, "required");
|
|
115
|
+
title: sentence(instrument);
|
|
116
|
+
id_prefix: if_eq(id_prefix_override, false, prefix(instrument), id_prefix_override);
|
|
117
|
+
agent_description: concat("One weighted claim on a distribution pool that another instrument owns. Record one claim per recipient before the pool settles. Its share is derived from its weight against the settled pool, never supplied by the caller, and the shares add back to the pool exactly.");
|
|
118
|
+
summary: "Flat largest-remainder distribution claim";
|
|
119
|
+
description: "One weighted claim on a settled distribution pool";
|
|
120
|
+
action create {
|
|
121
|
+
public: none;
|
|
122
|
+
agent_description: concat("Record one recipient weight against a pool that is still open. Both the pool and the weight source must be in an accepting state, and the same weight source cannot be claimed twice. No money moves. Do not supply a share. It is derived at payout from the settled pool.");
|
|
123
|
+
summary: "Open one weighted distribution claim";
|
|
124
|
+
requires_refs: [
|
|
125
|
+
{ field: pool_ref_field; statuses: pool_create_statuses; bind: pool_bind; match: pool_match; },
|
|
126
|
+
{ field: weight_ref_field; statuses: weight_statuses; bind: weight_bind; match: weight_match; unique: unique_weight_source; }
|
|
127
|
+
];
|
|
128
|
+
moves: [];
|
|
129
|
+
steps: [];
|
|
130
|
+
}
|
|
131
|
+
action payout {
|
|
132
|
+
public: none;
|
|
133
|
+
summary: "Pay one largest-remainder share from the settled pool";
|
|
134
|
+
due { field: record_at; whenParentStatus: { instrumentId: pool_instrument; refField: pool_ref_field; statuses: pool_payout_statuses; }; }
|
|
135
|
+
requires refs { field: pool_ref_field; statuses: pool_payout_statuses; }
|
|
136
|
+
computes distribute { amountRef: payoutShare; onZero: skip_steps; pool: { from: parent; path: pool_amount_path; }; refField: pool_ref_field; statuses: [claimed, paid]; weightField: amount; }
|
|
137
|
+
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.payoutShare; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", recipient_account_field); }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", source_account_field); }; }; capture: { payoutTransferId: transferId; }; }];
|
|
138
|
+
steps: [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/dist/src/archetypes.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settlement declarations shared by checking and lowering. A local event cap
|
|
3
|
-
* limits one settlement before the whole-program budget applies.
|
|
4
|
-
*/
|
|
5
|
-
export declare const SETTLEMENT_ARCHETYPES: readonly ["advance", "captured_payment", "conditional_disbursement", "credit_facility", "deposit", "funding_round", "held_payment", "instant_transfer", "metered", "pooled_split", "premium_forward", "recurring_collection", "rotating_pool", "scheduled", "settlement_batch", "swap", "weighted_distribution"];
|
|
6
|
-
export type ArchetypeName = (typeof SETTLEMENT_ARCHETYPES)[number];
|
|
7
|
-
interface ArchetypeDefinition {
|
|
8
|
-
readonly eventCap: number;
|
|
9
|
-
readonly keys: readonly string[];
|
|
10
|
-
readonly maxRates?: number;
|
|
11
|
-
readonly maxRecipients?: number;
|
|
12
|
-
readonly referenceableExits: readonly string[];
|
|
13
|
-
readonly required: readonly string[];
|
|
14
|
-
}
|
|
15
|
-
export declare const ARCHETYPE_DEFINITIONS: Record<ArchetypeName, ArchetypeDefinition>;
|
|
16
|
-
export {};
|
|
17
|
-
//# sourceMappingURL=archetypes.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"archetypes.d.ts","sourceRoot":"","sources":["../../src/archetypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,qBAAqB,kTAkBxB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,aAAa,EAAE,mBAAmB,CA+S1E,CAAC"}
|