taximeter 0.1.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 +9 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +141 -0
- package/DECISIONS.md +54 -0
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/SECURITY.md +69 -0
- package/SPEC-NOTES.md +369 -0
- package/SPEC.md +474 -0
- package/VERIFICATION.md +943 -0
- package/dist/chunk-I7YXGTRZ.js +1167 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +494 -0
- package/dist/index.d.ts +404 -0
- package/dist/index.js +182 -0
- package/dist/ui/assets/index-DOE5bD9P.js +101 -0
- package/dist/ui/assets/index-De4zKRDL.css +1 -0
- package/dist/ui/index.html +15 -0
- package/docs/RECORDING.md +75 -0
- package/docs/SDK.md +192 -0
- package/docs/demo.gif +0 -0
- package/docs/demo.mjs +145 -0
- package/docs/demo.tape +15 -0
- package/package.json +79 -0
- package/taximeter.config.example.json +16 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Server } from 'node:http';
|
|
3
|
+
|
|
4
|
+
declare const configSchema: z.ZodObject<{
|
|
5
|
+
budgets: z.ZodPrefault<z.ZodObject<{
|
|
6
|
+
perTask: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
7
|
+
amount: z.ZodString;
|
|
8
|
+
asset: z.ZodString;
|
|
9
|
+
network: z.ZodOptional<z.ZodString>;
|
|
10
|
+
window: z.ZodOptional<z.ZodEnum<{
|
|
11
|
+
"1h": "1h";
|
|
12
|
+
"24h": "24h";
|
|
13
|
+
"7d": "7d";
|
|
14
|
+
"30d": "30d";
|
|
15
|
+
}>>;
|
|
16
|
+
}, z.core.$strict>>>;
|
|
17
|
+
perAgent: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
18
|
+
amount: z.ZodString;
|
|
19
|
+
asset: z.ZodString;
|
|
20
|
+
network: z.ZodOptional<z.ZodString>;
|
|
21
|
+
window: z.ZodOptional<z.ZodEnum<{
|
|
22
|
+
"1h": "1h";
|
|
23
|
+
"24h": "24h";
|
|
24
|
+
"7d": "7d";
|
|
25
|
+
"30d": "30d";
|
|
26
|
+
}>>;
|
|
27
|
+
}, z.core.$strict>>>;
|
|
28
|
+
global: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
29
|
+
amount: z.ZodString;
|
|
30
|
+
asset: z.ZodString;
|
|
31
|
+
network: z.ZodOptional<z.ZodString>;
|
|
32
|
+
window: z.ZodOptional<z.ZodEnum<{
|
|
33
|
+
"1h": "1h";
|
|
34
|
+
"24h": "24h";
|
|
35
|
+
"7d": "7d";
|
|
36
|
+
"30d": "30d";
|
|
37
|
+
}>>;
|
|
38
|
+
}, z.core.$strict>>>;
|
|
39
|
+
}, z.core.$strict>>;
|
|
40
|
+
policy: z.ZodPrefault<z.ZodObject<{
|
|
41
|
+
allowHosts: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
42
|
+
denyHosts: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
43
|
+
allowPayTo: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
44
|
+
maxSinglePayment: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
45
|
+
maxSingleAsset: z.ZodDefault<z.ZodString>;
|
|
46
|
+
}, z.core.$strict>>;
|
|
47
|
+
ports: z.ZodPrefault<z.ZodObject<{
|
|
48
|
+
proxy: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
dashboard: z.ZodDefault<z.ZodNumber>;
|
|
50
|
+
}, z.core.$strict>>;
|
|
51
|
+
db: z.ZodDefault<z.ZodString>;
|
|
52
|
+
upstream: z.ZodOptional<z.ZodURL>;
|
|
53
|
+
}, z.core.$strict>;
|
|
54
|
+
type TaximeterConfig = z.infer<typeof configSchema>;
|
|
55
|
+
/** Merge validated layers from lowest to highest priority without mutating inputs. */
|
|
56
|
+
declare function parseConfig(...layers: unknown[]): TaximeterConfig;
|
|
57
|
+
|
|
58
|
+
declare function loadConfig(flags?: unknown, options?: {
|
|
59
|
+
cwd?: string;
|
|
60
|
+
home?: string;
|
|
61
|
+
env?: unknown;
|
|
62
|
+
configFile?: string;
|
|
63
|
+
}): {
|
|
64
|
+
db: string;
|
|
65
|
+
budgets: {
|
|
66
|
+
perTask: {
|
|
67
|
+
amount: string;
|
|
68
|
+
asset: string;
|
|
69
|
+
network?: string | undefined;
|
|
70
|
+
window?: "1h" | "24h" | "7d" | "30d" | undefined;
|
|
71
|
+
} | null;
|
|
72
|
+
perAgent: {
|
|
73
|
+
amount: string;
|
|
74
|
+
asset: string;
|
|
75
|
+
network?: string | undefined;
|
|
76
|
+
window?: "1h" | "24h" | "7d" | "30d" | undefined;
|
|
77
|
+
} | null;
|
|
78
|
+
global: {
|
|
79
|
+
amount: string;
|
|
80
|
+
asset: string;
|
|
81
|
+
network?: string | undefined;
|
|
82
|
+
window?: "1h" | "24h" | "7d" | "30d" | undefined;
|
|
83
|
+
} | null;
|
|
84
|
+
};
|
|
85
|
+
policy: {
|
|
86
|
+
allowHosts: string[];
|
|
87
|
+
denyHosts: string[];
|
|
88
|
+
allowPayTo: string[];
|
|
89
|
+
maxSinglePayment: string | null;
|
|
90
|
+
maxSingleAsset: string;
|
|
91
|
+
};
|
|
92
|
+
ports: {
|
|
93
|
+
proxy: number;
|
|
94
|
+
dashboard: number;
|
|
95
|
+
};
|
|
96
|
+
upstream?: string | undefined;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
declare const requestSchema: z.ZodObject<{
|
|
100
|
+
url: z.ZodURL;
|
|
101
|
+
method: z.ZodString;
|
|
102
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
103
|
+
}, z.core.$strip>;
|
|
104
|
+
declare const responseSchema: z.ZodObject<{
|
|
105
|
+
status: z.ZodNumber;
|
|
106
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
107
|
+
body: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
|
|
108
|
+
}, z.core.$strip>;
|
|
109
|
+
declare const paymentEventSchema: z.ZodObject<{
|
|
110
|
+
id: z.ZodUUID;
|
|
111
|
+
ts: z.ZodISODateTime;
|
|
112
|
+
rail: z.ZodLiteral<"x402">;
|
|
113
|
+
attemptedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
114
|
+
status: z.ZodEnum<{
|
|
115
|
+
observed: "observed";
|
|
116
|
+
blocked: "blocked";
|
|
117
|
+
}>;
|
|
118
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
119
|
+
amount: z.ZodString;
|
|
120
|
+
decimals: z.ZodNumber;
|
|
121
|
+
decimalsKnown: z.ZodBoolean;
|
|
122
|
+
asset: z.ZodString;
|
|
123
|
+
assetSymbol: z.ZodOptional<z.ZodString>;
|
|
124
|
+
network: z.ZodString;
|
|
125
|
+
payTo: z.ZodString;
|
|
126
|
+
payer: z.ZodOptional<z.ZodString>;
|
|
127
|
+
resource: z.ZodURL;
|
|
128
|
+
host: z.ZodString;
|
|
129
|
+
txHash: z.ZodOptional<z.ZodString>;
|
|
130
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
131
|
+
agentId: z.ZodOptional<z.ZodString>;
|
|
132
|
+
raw: z.ZodString;
|
|
133
|
+
paymentKey: z.ZodString;
|
|
134
|
+
settlementStatus: z.ZodDefault<z.ZodEnum<{
|
|
135
|
+
unknown: "unknown";
|
|
136
|
+
confirmed: "confirmed";
|
|
137
|
+
failed: "failed";
|
|
138
|
+
}>>;
|
|
139
|
+
settlement_unknown: z.ZodDefault<z.ZodBoolean>;
|
|
140
|
+
}, z.core.$strip>;
|
|
141
|
+
type PaymentEvent = z.infer<typeof paymentEventSchema>;
|
|
142
|
+
declare const outcomeSchema: z.ZodObject<{
|
|
143
|
+
id: z.ZodUUID;
|
|
144
|
+
ts: z.ZodISODateTime;
|
|
145
|
+
paymentId: z.ZodUUID;
|
|
146
|
+
attemptId: z.ZodOptional<z.ZodUUID>;
|
|
147
|
+
attemptedAt: z.ZodOptional<z.ZodISODateTime>;
|
|
148
|
+
status: z.ZodEnum<{
|
|
149
|
+
unknown: "unknown";
|
|
150
|
+
confirmed: "confirmed";
|
|
151
|
+
failed: "failed";
|
|
152
|
+
}>;
|
|
153
|
+
txHash: z.ZodOptional<z.ZodString>;
|
|
154
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
155
|
+
}, z.core.$strip>;
|
|
156
|
+
type Outcome = z.infer<typeof outcomeSchema>;
|
|
157
|
+
declare const diagnosticSchema: z.ZodObject<{
|
|
158
|
+
id: z.ZodUUID;
|
|
159
|
+
ts: z.ZodISODateTime;
|
|
160
|
+
code: z.ZodEnum<{
|
|
161
|
+
settlement_unknown: "settlement_unknown";
|
|
162
|
+
parse_failed: "parse_failed";
|
|
163
|
+
tls_unmetered: "tls_unmetered";
|
|
164
|
+
storage_failed: "storage_failed";
|
|
165
|
+
}>;
|
|
166
|
+
resource: z.ZodString;
|
|
167
|
+
message: z.ZodString;
|
|
168
|
+
}, z.core.$strip>;
|
|
169
|
+
type Diagnostic = z.infer<typeof diagnosticSchema>;
|
|
170
|
+
declare const blockedBodySchema: z.ZodObject<{
|
|
171
|
+
error: z.ZodLiteral<"blocked_by_taximeter">;
|
|
172
|
+
reason: z.ZodString;
|
|
173
|
+
budget: z.ZodNullable<z.ZodString>;
|
|
174
|
+
spent: z.ZodString;
|
|
175
|
+
remaining: z.ZodNullable<z.ZodString>;
|
|
176
|
+
}, z.core.$strip>;
|
|
177
|
+
type BlockedBody = z.infer<typeof blockedBodySchema>;
|
|
178
|
+
|
|
179
|
+
/** Synchronous storage edge; money derivations remain pure in derive.ts. */
|
|
180
|
+
declare class Ledger {
|
|
181
|
+
private readonly database;
|
|
182
|
+
constructor(input: string);
|
|
183
|
+
transaction<T>(operation: () => T): T;
|
|
184
|
+
append(input: unknown): boolean;
|
|
185
|
+
appendOutcome(input: unknown): boolean;
|
|
186
|
+
events(): {
|
|
187
|
+
id: string;
|
|
188
|
+
ts: string;
|
|
189
|
+
rail: "x402";
|
|
190
|
+
status: "observed" | "blocked";
|
|
191
|
+
amount: string;
|
|
192
|
+
decimals: number;
|
|
193
|
+
decimalsKnown: boolean;
|
|
194
|
+
asset: string;
|
|
195
|
+
network: string;
|
|
196
|
+
payTo: string;
|
|
197
|
+
resource: string;
|
|
198
|
+
host: string;
|
|
199
|
+
raw: string;
|
|
200
|
+
paymentKey: string;
|
|
201
|
+
settlementStatus: "unknown" | "confirmed" | "failed";
|
|
202
|
+
settlement_unknown: boolean;
|
|
203
|
+
attemptedAt?: string | undefined;
|
|
204
|
+
reason?: string | undefined;
|
|
205
|
+
assetSymbol?: string | undefined;
|
|
206
|
+
payer?: string | undefined;
|
|
207
|
+
txHash?: string | undefined;
|
|
208
|
+
taskId?: string | undefined;
|
|
209
|
+
agentId?: string | undefined;
|
|
210
|
+
}[];
|
|
211
|
+
outcomes(): {
|
|
212
|
+
id: string;
|
|
213
|
+
ts: string;
|
|
214
|
+
paymentId: string;
|
|
215
|
+
status: "unknown" | "confirmed" | "failed";
|
|
216
|
+
attemptId?: string | undefined;
|
|
217
|
+
attemptedAt?: string | undefined;
|
|
218
|
+
txHash?: string | undefined;
|
|
219
|
+
reason?: string | undefined;
|
|
220
|
+
}[];
|
|
221
|
+
view(): {
|
|
222
|
+
id: string;
|
|
223
|
+
ts: string;
|
|
224
|
+
rail: "x402";
|
|
225
|
+
status: "observed" | "blocked";
|
|
226
|
+
amount: string;
|
|
227
|
+
decimals: number;
|
|
228
|
+
decimalsKnown: boolean;
|
|
229
|
+
asset: string;
|
|
230
|
+
network: string;
|
|
231
|
+
payTo: string;
|
|
232
|
+
resource: string;
|
|
233
|
+
host: string;
|
|
234
|
+
raw: string;
|
|
235
|
+
paymentKey: string;
|
|
236
|
+
settlementStatus: "unknown" | "confirmed" | "failed";
|
|
237
|
+
settlement_unknown: boolean;
|
|
238
|
+
attemptedAt?: string | undefined;
|
|
239
|
+
reason?: string | undefined;
|
|
240
|
+
assetSymbol?: string | undefined;
|
|
241
|
+
payer?: string | undefined;
|
|
242
|
+
txHash?: string | undefined;
|
|
243
|
+
taskId?: string | undefined;
|
|
244
|
+
agentId?: string | undefined;
|
|
245
|
+
}[];
|
|
246
|
+
diagnose(code: Diagnostic["code"], resource: string, message: string): void;
|
|
247
|
+
diagnostics(): {
|
|
248
|
+
id: string;
|
|
249
|
+
ts: string;
|
|
250
|
+
code: "settlement_unknown" | "parse_failed" | "tls_unmetered" | "storage_failed";
|
|
251
|
+
resource: string;
|
|
252
|
+
message: string;
|
|
253
|
+
}[];
|
|
254
|
+
close(): void;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
type WireRequest = z.infer<typeof requestSchema>;
|
|
258
|
+
type WireResponse = z.infer<typeof responseSchema>;
|
|
259
|
+
interface Rail {
|
|
260
|
+
readonly name: string;
|
|
261
|
+
detect(request: WireRequest, response?: WireResponse): boolean;
|
|
262
|
+
parse(request: WireRequest, response?: WireResponse): PaymentEvent | null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
type Report = (code: Diagnostic["code"], resource: string, message: string) => void;
|
|
266
|
+
type Settlement = Pick<Outcome, "status" | "txHash" | "reason">;
|
|
267
|
+
/** Stateful challenge cache at the intake edge. Unknown data always returns null. */
|
|
268
|
+
declare class X402Rail implements Rail {
|
|
269
|
+
readonly name = "x402";
|
|
270
|
+
private readonly cache;
|
|
271
|
+
private readonly now;
|
|
272
|
+
private readonly diagnostic;
|
|
273
|
+
constructor(options?: {
|
|
274
|
+
diagnostic?: Report;
|
|
275
|
+
now?: () => number;
|
|
276
|
+
});
|
|
277
|
+
private report;
|
|
278
|
+
detect(request: WireRequest, response?: WireResponse): boolean;
|
|
279
|
+
private attribution;
|
|
280
|
+
parse(input: WireRequest, reply?: WireResponse): PaymentEvent | null;
|
|
281
|
+
settlement(event: PaymentEvent, input: WireResponse): Settlement;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
type Intake = {
|
|
285
|
+
body?: BlockedBody;
|
|
286
|
+
payment?: PaymentEvent;
|
|
287
|
+
attempt?: Outcome;
|
|
288
|
+
};
|
|
289
|
+
/** Shared proxy/SDK intake. Every reservation is committed before returning to I/O. */
|
|
290
|
+
declare class Meter {
|
|
291
|
+
readonly ledger: Ledger;
|
|
292
|
+
readonly rail: X402Rail;
|
|
293
|
+
readonly config: TaximeterConfig;
|
|
294
|
+
constructor(ledger: Ledger, config: TaximeterConfig);
|
|
295
|
+
diagnose(code: Diagnostic["code"], resource: string, message: string): void;
|
|
296
|
+
begin(request: WireRequest): Intake;
|
|
297
|
+
observe(request: WireRequest, response: WireResponse): void;
|
|
298
|
+
complete(intake: Intake, response?: WireResponse): void;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** `amount` is the row's contribution to totals; the original proposal is separate. */
|
|
302
|
+
declare function toCsv(input: PaymentEvent[]): string;
|
|
303
|
+
declare function toJson(input: PaymentEvent[]): string;
|
|
304
|
+
declare function toInvoice(input: PaymentEvent[]): string;
|
|
305
|
+
|
|
306
|
+
declare const totalSchema: z.ZodObject<{
|
|
307
|
+
network: z.ZodString;
|
|
308
|
+
asset: z.ZodString;
|
|
309
|
+
assetSymbol: z.ZodOptional<z.ZodString>;
|
|
310
|
+
decimals: z.ZodNumber;
|
|
311
|
+
decimalsKnown: z.ZodBoolean;
|
|
312
|
+
amount: z.ZodString;
|
|
313
|
+
confirmedAmount: z.ZodString;
|
|
314
|
+
unknownAmount: z.ZodString;
|
|
315
|
+
}, z.core.$strip>;
|
|
316
|
+
type Total = z.infer<typeof totalSchema>;
|
|
317
|
+
/** Stable replay: confirmed evidence wins; otherwise the latest outcome applies. */
|
|
318
|
+
declare function deriveEvents(events: PaymentEvent[], outcomes?: Outcome[]): PaymentEvent[];
|
|
319
|
+
/** Never use a SQLite SUM or floating-point conversion for money. */
|
|
320
|
+
declare function totals(events: PaymentEvent[]): Total[];
|
|
321
|
+
declare function formatAmount(amount: string, decimals: number): string;
|
|
322
|
+
|
|
323
|
+
type Decision = {
|
|
324
|
+
allowed: true;
|
|
325
|
+
} | {
|
|
326
|
+
allowed: false;
|
|
327
|
+
body: BlockedBody;
|
|
328
|
+
};
|
|
329
|
+
/** Pure, synchronous budget decision. The caller must reserve in the same transaction. */
|
|
330
|
+
declare function evaluate(proposed: PaymentEvent, events: PaymentEvent[], config: TaximeterConfig, now: number): Decision;
|
|
331
|
+
|
|
332
|
+
declare function createProxy(options: {
|
|
333
|
+
ledger: Ledger;
|
|
334
|
+
config: TaximeterConfig;
|
|
335
|
+
}): Server;
|
|
336
|
+
declare function closeProxy(server: Server): Promise<void>;
|
|
337
|
+
|
|
338
|
+
declare const optionsSchema: z.ZodObject<{
|
|
339
|
+
ledger: z.ZodOptional<z.ZodCustom<Ledger, Ledger>>;
|
|
340
|
+
config: z.ZodOptional<z.ZodObject<{
|
|
341
|
+
budgets: z.ZodOptional<z.ZodObject<{
|
|
342
|
+
perTask: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
343
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
344
|
+
asset: z.ZodOptional<z.ZodString>;
|
|
345
|
+
network: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
346
|
+
window: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
347
|
+
"1h": "1h";
|
|
348
|
+
"24h": "24h";
|
|
349
|
+
"7d": "7d";
|
|
350
|
+
"30d": "30d";
|
|
351
|
+
}>>>;
|
|
352
|
+
}, z.core.$strict>>>;
|
|
353
|
+
perAgent: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
354
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
355
|
+
asset: z.ZodOptional<z.ZodString>;
|
|
356
|
+
network: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
357
|
+
window: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
358
|
+
"1h": "1h";
|
|
359
|
+
"24h": "24h";
|
|
360
|
+
"7d": "7d";
|
|
361
|
+
"30d": "30d";
|
|
362
|
+
}>>>;
|
|
363
|
+
}, z.core.$strict>>>;
|
|
364
|
+
global: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
365
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
366
|
+
asset: z.ZodOptional<z.ZodString>;
|
|
367
|
+
network: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
368
|
+
window: z.ZodOptional<z.ZodOptional<z.ZodEnum<{
|
|
369
|
+
"1h": "1h";
|
|
370
|
+
"24h": "24h";
|
|
371
|
+
"7d": "7d";
|
|
372
|
+
"30d": "30d";
|
|
373
|
+
}>>>;
|
|
374
|
+
}, z.core.$strict>>>;
|
|
375
|
+
}, z.core.$strict>>;
|
|
376
|
+
policy: z.ZodOptional<z.ZodObject<{
|
|
377
|
+
allowHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
378
|
+
denyHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
379
|
+
allowPayTo: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
380
|
+
maxSinglePayment: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
381
|
+
maxSingleAsset: z.ZodOptional<z.ZodString>;
|
|
382
|
+
}, z.core.$strict>>;
|
|
383
|
+
ports: z.ZodOptional<z.ZodObject<{
|
|
384
|
+
proxy: z.ZodOptional<z.ZodNumber>;
|
|
385
|
+
dashboard: z.ZodOptional<z.ZodNumber>;
|
|
386
|
+
}, z.core.$strict>>;
|
|
387
|
+
db: z.ZodOptional<z.ZodString>;
|
|
388
|
+
upstream: z.ZodOptional<z.ZodURL>;
|
|
389
|
+
}, z.core.$strict>>;
|
|
390
|
+
db: z.ZodOptional<z.ZodString>;
|
|
391
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
392
|
+
agentId: z.ZodOptional<z.ZodString>;
|
|
393
|
+
}, z.core.$strict>;
|
|
394
|
+
type MeterOptions = z.input<typeof optionsSchema>;
|
|
395
|
+
type MeteredFetch = typeof globalThis.fetch & {
|
|
396
|
+
close(): void;
|
|
397
|
+
readonly ledger: Ledger;
|
|
398
|
+
};
|
|
399
|
+
/** Place inside the payment wrapper: wrapFetchWithPayment(withMeter(fetch, opts), client). */
|
|
400
|
+
declare function withMeter(fetchImpl: typeof globalThis.fetch, input?: MeterOptions): MeteredFetch;
|
|
401
|
+
|
|
402
|
+
declare const version = "0.1.0";
|
|
403
|
+
|
|
404
|
+
export { Ledger, Meter, type MeterOptions, type MeteredFetch, type PaymentEvent, type Rail, type TaximeterConfig, X402Rail, closeProxy, createProxy, deriveEvents, evaluate, formatAmount, loadConfig, parseConfig, paymentEventSchema, toCsv, toInvoice, toJson, totals, version, withMeter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Ledger,
|
|
3
|
+
Meter,
|
|
4
|
+
X402Rail,
|
|
5
|
+
closeProxy,
|
|
6
|
+
configPatchSchema,
|
|
7
|
+
createProxy,
|
|
8
|
+
deriveEvents,
|
|
9
|
+
evaluate,
|
|
10
|
+
formatAmount,
|
|
11
|
+
httpUrlSchema,
|
|
12
|
+
labelSchema,
|
|
13
|
+
loadConfig,
|
|
14
|
+
parseConfig,
|
|
15
|
+
paymentEventSchema,
|
|
16
|
+
toCsv,
|
|
17
|
+
toInvoice,
|
|
18
|
+
toJson,
|
|
19
|
+
totals
|
|
20
|
+
} from "./chunk-I7YXGTRZ.js";
|
|
21
|
+
|
|
22
|
+
// src/sdk/index.ts
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
var headerInputSchema = z.union([
|
|
25
|
+
z.instanceof(Headers),
|
|
26
|
+
z.record(z.string(), z.string()),
|
|
27
|
+
z.array(z.tuple([z.string(), z.string()]))
|
|
28
|
+
]);
|
|
29
|
+
var initSchema = z.looseObject({
|
|
30
|
+
method: z.string().optional(),
|
|
31
|
+
headers: headerInputSchema.optional(),
|
|
32
|
+
signal: z.instanceof(AbortSignal).nullable().optional()
|
|
33
|
+
});
|
|
34
|
+
var sdkLabel = labelSchema.refine(
|
|
35
|
+
(value) => !/[\r\n\0]/.test(value),
|
|
36
|
+
"Attribution must fit an HTTP header"
|
|
37
|
+
);
|
|
38
|
+
var optionsSchema = z.strictObject({
|
|
39
|
+
ledger: z.instanceof(Ledger).optional(),
|
|
40
|
+
config: configPatchSchema.optional(),
|
|
41
|
+
db: z.string().min(1).optional(),
|
|
42
|
+
taskId: sdkLabel.optional(),
|
|
43
|
+
agentId: sdkLabel.optional()
|
|
44
|
+
});
|
|
45
|
+
async function challengeBody(response) {
|
|
46
|
+
const reader = response.clone().body?.getReader();
|
|
47
|
+
if (!reader) return new Uint8Array();
|
|
48
|
+
let done = false;
|
|
49
|
+
let timer;
|
|
50
|
+
const read = async () => {
|
|
51
|
+
const parts = [];
|
|
52
|
+
let length = 0;
|
|
53
|
+
while (true) {
|
|
54
|
+
const chunk = await reader.read();
|
|
55
|
+
if (chunk.done) {
|
|
56
|
+
done = true;
|
|
57
|
+
return Buffer.concat(parts);
|
|
58
|
+
}
|
|
59
|
+
const bytes = z.instanceof(Uint8Array).parse(chunk.value);
|
|
60
|
+
length += bytes.byteLength;
|
|
61
|
+
if (length > 65536) return void 0;
|
|
62
|
+
parts.push(bytes);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
return await Promise.race([
|
|
67
|
+
read(),
|
|
68
|
+
new Promise((resolve) => {
|
|
69
|
+
timer = setTimeout(() => resolve(void 0), 100);
|
|
70
|
+
})
|
|
71
|
+
]);
|
|
72
|
+
} finally {
|
|
73
|
+
if (timer) clearTimeout(timer);
|
|
74
|
+
if (!done) void reader.cancel().catch(() => {
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function withMeter(fetchImpl, input = {}) {
|
|
79
|
+
const transport = z.custom((value) => typeof value === "function").parse(fetchImpl);
|
|
80
|
+
const options = optionsSchema.parse(input);
|
|
81
|
+
const overrides = { ...options.config, ...options.db ? { db: options.db } : {} };
|
|
82
|
+
const config = options.ledger ? parseConfig(overrides) : loadConfig(overrides);
|
|
83
|
+
const ledger = options.ledger ?? new Ledger(config.db);
|
|
84
|
+
const meter = new Meter(ledger, config);
|
|
85
|
+
let closed = false;
|
|
86
|
+
const wrapped = async (input2, init) => {
|
|
87
|
+
if (closed) return transport(input2, init);
|
|
88
|
+
let url;
|
|
89
|
+
let method;
|
|
90
|
+
let headers;
|
|
91
|
+
let forwarded = init;
|
|
92
|
+
try {
|
|
93
|
+
const target = z.union([z.string(), z.instanceof(URL), z.instanceof(Request)]).parse(input2);
|
|
94
|
+
const options2 = initSchema.optional().parse(init);
|
|
95
|
+
url = httpUrlSchema.parse(target instanceof Request ? target.url : target.toString());
|
|
96
|
+
method = options2?.method ?? (target instanceof Request ? target.method : "GET");
|
|
97
|
+
headers = new Headers(
|
|
98
|
+
options2?.headers ?? (target instanceof Request ? target.headers : void 0)
|
|
99
|
+
);
|
|
100
|
+
const signal = options2?.signal ?? (target instanceof Request ? target.signal : void 0);
|
|
101
|
+
if (signal?.aborted) return transport(input2, init);
|
|
102
|
+
} catch {
|
|
103
|
+
meter.diagnose(
|
|
104
|
+
"parse_failed",
|
|
105
|
+
"sdk",
|
|
106
|
+
"Unsupported fetch input; passed directly to the supplied transport."
|
|
107
|
+
);
|
|
108
|
+
return transport(input2, init);
|
|
109
|
+
}
|
|
110
|
+
if (options.taskId) headers.set("Taximeter-Task", options.taskId);
|
|
111
|
+
if (options.agentId) headers.set("Taximeter-Agent", options.agentId);
|
|
112
|
+
if (options.taskId || options.agentId) forwarded = { ...init, headers };
|
|
113
|
+
const request = { url, method, headers: Object.fromEntries(headers) };
|
|
114
|
+
const intake = meter.begin(request);
|
|
115
|
+
if (intake.body)
|
|
116
|
+
return new Response(JSON.stringify(intake.body), {
|
|
117
|
+
status: 402,
|
|
118
|
+
headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
|
|
119
|
+
});
|
|
120
|
+
let response;
|
|
121
|
+
try {
|
|
122
|
+
response = await transport(input2, forwarded);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
meter.complete(intake);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
const responseHeaders = Object.fromEntries(response.headers);
|
|
128
|
+
const reply = { status: response.status, headers: responseHeaders };
|
|
129
|
+
meter.complete(intake, reply);
|
|
130
|
+
if (response.redirected)
|
|
131
|
+
meter.diagnose(
|
|
132
|
+
"parse_failed",
|
|
133
|
+
url,
|
|
134
|
+
"The supplied transport followed a redirect internally; intermediate requests were not visible to Taximeter policy. Use redirect: 'error' or expose each hop when host policies must apply to every destination."
|
|
135
|
+
);
|
|
136
|
+
if (response.status === 402 && !response.redirected) {
|
|
137
|
+
try {
|
|
138
|
+
const body = response.headers.has("payment-required") ? void 0 : await challengeBody(response);
|
|
139
|
+
const inspectionHeaders = { ...responseHeaders };
|
|
140
|
+
delete inspectionHeaders["content-encoding"];
|
|
141
|
+
meter.observe(request, { ...reply, headers: inspectionHeaders, body });
|
|
142
|
+
} catch {
|
|
143
|
+
meter.diagnose(
|
|
144
|
+
"parse_failed",
|
|
145
|
+
url,
|
|
146
|
+
"Could not inspect response clone; original response preserved."
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return response;
|
|
151
|
+
};
|
|
152
|
+
return Object.assign(wrapped, {
|
|
153
|
+
ledger,
|
|
154
|
+
close() {
|
|
155
|
+
if (closed) return;
|
|
156
|
+
closed = true;
|
|
157
|
+
if (!options.ledger) ledger.close();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/index.ts
|
|
163
|
+
var version = "0.1.0";
|
|
164
|
+
export {
|
|
165
|
+
Ledger,
|
|
166
|
+
Meter,
|
|
167
|
+
X402Rail,
|
|
168
|
+
closeProxy,
|
|
169
|
+
createProxy,
|
|
170
|
+
deriveEvents,
|
|
171
|
+
evaluate,
|
|
172
|
+
formatAmount,
|
|
173
|
+
loadConfig,
|
|
174
|
+
parseConfig,
|
|
175
|
+
paymentEventSchema,
|
|
176
|
+
toCsv,
|
|
177
|
+
toInvoice,
|
|
178
|
+
toJson,
|
|
179
|
+
totals,
|
|
180
|
+
version,
|
|
181
|
+
withMeter
|
|
182
|
+
};
|