@aldus-runtime/gate-engine 0.2.0-next.19 → 0.2.0-next.20
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/dist/spend.d.ts +37 -1
- package/dist/spend.d.ts.map +1 -1
- package/dist/spend.js +30 -0
- package/dist/spend.js.map +1 -1
- package/package.json +3 -3
- package/src/spend.ts +65 -1
package/dist/spend.d.ts
CHANGED
|
@@ -82,7 +82,34 @@ export interface SpendLedger {
|
|
|
82
82
|
counted: CostRecord[];
|
|
83
83
|
/** Cost records excluded because they were voided. */
|
|
84
84
|
excluded: CostRecord[];
|
|
85
|
+
/**
|
|
86
|
+
* Charges whose amount nobody knows yet (§19.3; #150).
|
|
87
|
+
*
|
|
88
|
+
* A provider may charge a request and withhold or delay the figure. While one of these stands
|
|
89
|
+
* against a grant, **Aldus cannot prove how much authorization remains** — the charge is real
|
|
90
|
+
* and its size is not yet a fact.
|
|
91
|
+
*
|
|
92
|
+
* A record here is never treated as free, voided, or a zero draw. Zero is a numerical assertion;
|
|
93
|
+
* this is an uncertainty state, and the two are not interchangeable.
|
|
94
|
+
*/
|
|
95
|
+
unresolvedUnknown: CostRecord[];
|
|
96
|
+
/**
|
|
97
|
+
* Whether {@link SpendLedger.remaining} is a number anyone may spend against.
|
|
98
|
+
*
|
|
99
|
+
* `false` while any unresolved unknown charge stands. `remaining` still reports the arithmetic
|
|
100
|
+
* over what is known, because an operator wants the figure — but presenting it as headroom
|
|
101
|
+
* would state a safe amount that nothing establishes.
|
|
102
|
+
*/
|
|
103
|
+
remainingIsDeterminate: boolean;
|
|
85
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Whether a record is a charge of unknown size (§19.3; #150).
|
|
107
|
+
*
|
|
108
|
+
* An estimate does not resolve it. An estimate is evidence about what a request was expected to
|
|
109
|
+
* cost, and the ruling on #150 is explicit that it does not confirm the final charge — so a record
|
|
110
|
+
* carrying both an estimate and `billingStatus: "unknown"` is still unresolved.
|
|
111
|
+
*/
|
|
112
|
+
export declare function isUnresolvedUnknownCharge(record: CostRecord): boolean;
|
|
86
113
|
/**
|
|
87
114
|
* Total what has been drawn against a grant.
|
|
88
115
|
*
|
|
@@ -99,7 +126,16 @@ export interface SpendRequest {
|
|
|
99
126
|
operation?: string;
|
|
100
127
|
}
|
|
101
128
|
/** Why a spend was refused. */
|
|
102
|
-
export type SpendRefusalReason = "per-request-limit" | "total-limit" | "already-overspent" | "negative-amount"
|
|
129
|
+
export type SpendRefusalReason = "per-request-limit" | "total-limit" | "already-overspent" | "negative-amount"
|
|
130
|
+
/**
|
|
131
|
+
* A charge of unknown size stands against this grant (§19.3; #150).
|
|
132
|
+
*
|
|
133
|
+
* Refused rather than allowed-with-a-warning: while the size of a real charge is unknown, the
|
|
134
|
+
* remaining headroom is not a fact, and spending against a figure nobody can establish is how a
|
|
135
|
+
* ceiling is exceeded without any single decision being wrong. Resolution is a reconciled amount
|
|
136
|
+
* or a new authorization under an explicit policy — both human acts.
|
|
137
|
+
*/
|
|
138
|
+
| "billing-unconfirmed";
|
|
103
139
|
/** The outcome of a stop-on-budget check. */
|
|
104
140
|
export type SpendCheck = {
|
|
105
141
|
allowed: true;
|
package/dist/spend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spend.d.ts","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAY7D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,8FAA8F;AAC9F,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,QAAQ,EAAE,KAAK,CAAC;IAChB,wFAAwF;IACxF,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAK3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAG1E;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,QAAQ,EAAE,KAAK,CAAC;IAChB,8EAA8E;IAC9E,SAAS,EAAE,KAAK,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"spend.d.ts","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAY7D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,8FAA8F;AAC9F,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,QAAQ,EAAE,KAAK,CAAC;IAChB,wFAAwF;IACxF,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAK3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAG1E;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,QAAQ,EAAE,KAAK,CAAC;IAChB,8EAA8E;IAC9E,SAAS,EAAE,KAAK,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB;;;;;;;;;OASG;IACH,iBAAiB,EAAE,UAAU,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,sBAAsB,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAErE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,WAAW,CA4B1F;AAED,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,MAAM,EAAE,KAAK,CAAC;IACd,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+BAA+B;AAC/B,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,aAAa,GACb,mBAAmB,GACnB,iBAAiB;AACnB;;;;;;;GAOG;GACD,qBAAqB,CAAC;AAE1B,6CAA6C;AAC7C,MAAM,MAAM,UAAU,GAClB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,cAAc,EAAE,KAAK,CAAA;CAAE,GAC7D;IACE,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEN;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,OAAO,EAAE,YAAY,GACpB,UAAU,CAyEZ"}
|
package/dist/spend.js
CHANGED
|
@@ -62,6 +62,16 @@ export function costRecordDraw(record, currency) {
|
|
|
62
62
|
return zeroMoney(currency);
|
|
63
63
|
return record.actual ?? record.estimated ?? zeroMoney(currency);
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Whether a record is a charge of unknown size (§19.3; #150).
|
|
67
|
+
*
|
|
68
|
+
* An estimate does not resolve it. An estimate is evidence about what a request was expected to
|
|
69
|
+
* cost, and the ruling on #150 is explicit that it does not confirm the final charge — so a record
|
|
70
|
+
* carrying both an estimate and `billingStatus: "unknown"` is still unresolved.
|
|
71
|
+
*/
|
|
72
|
+
export function isUnresolvedUnknownCharge(record) {
|
|
73
|
+
return record.billingStatus === "unknown";
|
|
74
|
+
}
|
|
65
75
|
/**
|
|
66
76
|
* Total what has been drawn against a grant.
|
|
67
77
|
*
|
|
@@ -85,12 +95,15 @@ export function computeLedger(grant, costs) {
|
|
|
85
95
|
}
|
|
86
96
|
const headroom = subtractMoney(grant.maxTotal, consumed);
|
|
87
97
|
const overspent = isNegativeMoney(headroom);
|
|
98
|
+
const unresolvedUnknown = counted.filter(isUnresolvedUnknownCharge);
|
|
88
99
|
return {
|
|
89
100
|
consumed,
|
|
90
101
|
remaining: overspent ? zeroMoney(currency) : headroom,
|
|
91
102
|
overspent,
|
|
92
103
|
counted,
|
|
93
104
|
excluded,
|
|
105
|
+
unresolvedUnknown,
|
|
106
|
+
remainingIsDeterminate: unresolvedUnknown.length === 0,
|
|
94
107
|
};
|
|
95
108
|
}
|
|
96
109
|
/**
|
|
@@ -116,6 +129,23 @@ export function checkSpend(grant, costs, request) {
|
|
|
116
129
|
"cost record, not as a negative draw, so that the audit trail keeps the original charge.",
|
|
117
130
|
};
|
|
118
131
|
}
|
|
132
|
+
// Before the arithmetic, because the arithmetic is what cannot be trusted. While a charge of
|
|
133
|
+
// unknown size stands against this grant, `remaining` is the total over what is *known* — and
|
|
134
|
+
// spending against it would treat an unresolved charge as a zero draw, which is the one thing
|
|
135
|
+
// the ruling on #150 forbids (§19.3).
|
|
136
|
+
if (!ledger.remainingIsDeterminate) {
|
|
137
|
+
return {
|
|
138
|
+
allowed: false,
|
|
139
|
+
reason: "billing-unconfirmed",
|
|
140
|
+
ledger,
|
|
141
|
+
explanation: `${ledger.unresolvedUnknown.length} charge(s) against this authorization have an ` +
|
|
142
|
+
"unconfirmed amount, so the remaining budget is indeterminate rather than " +
|
|
143
|
+
`${formatMoney(ledger.remaining)}. Automatic spend is refused until the amount is ` +
|
|
144
|
+
"reconciled or an operator issues a new authorization: an unknown charge is neither free " +
|
|
145
|
+
"nor zero, and drawing against a figure nobody can establish is how a ceiling is exceeded " +
|
|
146
|
+
"without any single decision being wrong (§19.3).",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
119
149
|
if (ledger.overspent) {
|
|
120
150
|
return {
|
|
121
151
|
allowed: false,
|
package/dist/spend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spend.js","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAe,EACf,aAAa,EACb,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,YAAY,CAAC;AAuBpD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAiB;IACjD,OAAO,kBAAkB,CAAC;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;KAC3C,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB;IAC/C,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB,EAAE,QAAgB;IACjE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC,QAAQ,CAAC,CAAC;IACxD,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC;AAClE,CAAC;
|
|
1
|
+
{"version":3,"file":"spend.js","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aldus-runtime/gate-engine",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Aldus Gate Engine \u2014 hash-bound human gates, cascading invalidation, and spend authorization.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"typecheck:test": "tsc -p tsconfig.test.json"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@aldus-runtime/core": "0.2.0-next.
|
|
36
|
+
"@aldus-runtime/core": "0.2.0-next.20"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@aldus-runtime/testkit": "0.2.0-next.
|
|
39
|
+
"@aldus-runtime/testkit": "0.2.0-next.20",
|
|
40
40
|
"@types/node": "^26.2.0",
|
|
41
41
|
"typescript": "^7.0.2",
|
|
42
42
|
"vitest": "^4.1.10"
|
package/src/spend.ts
CHANGED
|
@@ -109,6 +109,36 @@ export interface SpendLedger {
|
|
|
109
109
|
counted: CostRecord[];
|
|
110
110
|
/** Cost records excluded because they were voided. */
|
|
111
111
|
excluded: CostRecord[];
|
|
112
|
+
/**
|
|
113
|
+
* Charges whose amount nobody knows yet (§19.3; #150).
|
|
114
|
+
*
|
|
115
|
+
* A provider may charge a request and withhold or delay the figure. While one of these stands
|
|
116
|
+
* against a grant, **Aldus cannot prove how much authorization remains** — the charge is real
|
|
117
|
+
* and its size is not yet a fact.
|
|
118
|
+
*
|
|
119
|
+
* A record here is never treated as free, voided, or a zero draw. Zero is a numerical assertion;
|
|
120
|
+
* this is an uncertainty state, and the two are not interchangeable.
|
|
121
|
+
*/
|
|
122
|
+
unresolvedUnknown: CostRecord[];
|
|
123
|
+
/**
|
|
124
|
+
* Whether {@link SpendLedger.remaining} is a number anyone may spend against.
|
|
125
|
+
*
|
|
126
|
+
* `false` while any unresolved unknown charge stands. `remaining` still reports the arithmetic
|
|
127
|
+
* over what is known, because an operator wants the figure — but presenting it as headroom
|
|
128
|
+
* would state a safe amount that nothing establishes.
|
|
129
|
+
*/
|
|
130
|
+
remainingIsDeterminate: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Whether a record is a charge of unknown size (§19.3; #150).
|
|
135
|
+
*
|
|
136
|
+
* An estimate does not resolve it. An estimate is evidence about what a request was expected to
|
|
137
|
+
* cost, and the ruling on #150 is explicit that it does not confirm the final charge — so a record
|
|
138
|
+
* carrying both an estimate and `billingStatus: "unknown"` is still unresolved.
|
|
139
|
+
*/
|
|
140
|
+
export function isUnresolvedUnknownCharge(record: CostRecord): boolean {
|
|
141
|
+
return record.billingStatus === "unknown";
|
|
112
142
|
}
|
|
113
143
|
|
|
114
144
|
/**
|
|
@@ -136,12 +166,15 @@ export function computeLedger(grant: SpendGrant, costs: readonly CostRecord[]):
|
|
|
136
166
|
|
|
137
167
|
const headroom = subtractMoney(grant.maxTotal, consumed);
|
|
138
168
|
const overspent = isNegativeMoney(headroom);
|
|
169
|
+
const unresolvedUnknown = counted.filter(isUnresolvedUnknownCharge);
|
|
139
170
|
return {
|
|
140
171
|
consumed,
|
|
141
172
|
remaining: overspent ? zeroMoney(currency) : headroom,
|
|
142
173
|
overspent,
|
|
143
174
|
counted,
|
|
144
175
|
excluded,
|
|
176
|
+
unresolvedUnknown,
|
|
177
|
+
remainingIsDeterminate: unresolvedUnknown.length === 0,
|
|
145
178
|
};
|
|
146
179
|
}
|
|
147
180
|
|
|
@@ -155,7 +188,19 @@ export interface SpendRequest {
|
|
|
155
188
|
|
|
156
189
|
/** Why a spend was refused. */
|
|
157
190
|
export type SpendRefusalReason =
|
|
158
|
-
"per-request-limit"
|
|
191
|
+
| "per-request-limit"
|
|
192
|
+
| "total-limit"
|
|
193
|
+
| "already-overspent"
|
|
194
|
+
| "negative-amount"
|
|
195
|
+
/**
|
|
196
|
+
* A charge of unknown size stands against this grant (§19.3; #150).
|
|
197
|
+
*
|
|
198
|
+
* Refused rather than allowed-with-a-warning: while the size of a real charge is unknown, the
|
|
199
|
+
* remaining headroom is not a fact, and spending against a figure nobody can establish is how a
|
|
200
|
+
* ceiling is exceeded without any single decision being wrong. Resolution is a reconciled amount
|
|
201
|
+
* or a new authorization under an explicit policy — both human acts.
|
|
202
|
+
*/
|
|
203
|
+
| "billing-unconfirmed";
|
|
159
204
|
|
|
160
205
|
/** The outcome of a stop-on-budget check. */
|
|
161
206
|
export type SpendCheck =
|
|
@@ -197,6 +242,25 @@ export function checkSpend(
|
|
|
197
242
|
};
|
|
198
243
|
}
|
|
199
244
|
|
|
245
|
+
// Before the arithmetic, because the arithmetic is what cannot be trusted. While a charge of
|
|
246
|
+
// unknown size stands against this grant, `remaining` is the total over what is *known* — and
|
|
247
|
+
// spending against it would treat an unresolved charge as a zero draw, which is the one thing
|
|
248
|
+
// the ruling on #150 forbids (§19.3).
|
|
249
|
+
if (!ledger.remainingIsDeterminate) {
|
|
250
|
+
return {
|
|
251
|
+
allowed: false,
|
|
252
|
+
reason: "billing-unconfirmed",
|
|
253
|
+
ledger,
|
|
254
|
+
explanation:
|
|
255
|
+
`${ledger.unresolvedUnknown.length} charge(s) against this authorization have an ` +
|
|
256
|
+
"unconfirmed amount, so the remaining budget is indeterminate rather than " +
|
|
257
|
+
`${formatMoney(ledger.remaining)}. Automatic spend is refused until the amount is ` +
|
|
258
|
+
"reconciled or an operator issues a new authorization: an unknown charge is neither free " +
|
|
259
|
+
"nor zero, and drawing against a figure nobody can establish is how a ceiling is exceeded " +
|
|
260
|
+
"without any single decision being wrong (§19.3).",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
200
264
|
if (ledger.overspent) {
|
|
201
265
|
return {
|
|
202
266
|
allowed: false,
|