@dvmkit/sdk 0.1.0-rc.2 → 0.1.0-rc.3
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/README.md +10 -2
- package/dist/{revenue-reporter-GB4WKLDC.js → chunk-2ABMGUDS.js} +1 -0
- package/dist/chunk-5SO7ZVOH.js +375 -0
- package/dist/chunk-66HGCPBU.js +25 -0
- package/dist/{chunk-KQAJVVZT.js → chunk-AZBXSXQT.js} +27 -287
- package/dist/chunk-C2DC4FKR.js +21424 -0
- package/dist/{chunk-H25M54MI.js → chunk-C3MTFLC6.js} +16 -0
- package/dist/{chunk-OJ5WFIB2.js → chunk-EXHBXA4U.js} +1 -1
- package/dist/chunk-FT7IM66W.js +1557 -0
- package/dist/{chunk-KXWROQGK.js → chunk-FUJ36YDV.js} +1 -24
- package/dist/{tempo-charge-store-6GJEMNUU.js → chunk-JZWELPFH.js} +1 -0
- package/dist/{chunk-365P52XQ.js → chunk-LWUR4CGG.js} +2 -1
- package/dist/chunk-RHP3BRTH.js +1090 -0
- package/dist/{tempo-session-store-FTEEGZXA.js → chunk-RU7SXHLO.js} +2 -1
- package/dist/{chunk-7IH5SG2A.js → chunk-TKA6ZP4M.js} +62 -41
- package/dist/chunk-TVI4V7GF.js +283 -0
- package/dist/{payout-reporter-4TNWRS5F.js → chunk-X3IKFWJA.js} +3 -2
- package/dist/{chunk-DCNT4PJS.js → chunk-XY5Y5REG.js} +6 -258
- package/dist/chunk-XYTSDAPH.js +232 -0
- package/dist/chunk-YD3TZNXV.js +1042 -0
- package/dist/{credit-ledger-RO4FGSHG.js → credit-ledger-ED6JXKVD.js} +2 -2
- package/dist/credit-menu-DONAtGVf.d.ts +5076 -0
- package/dist/{ssrf-DZi-xJyn.d.ts → fx-Bq4cvn16.d.ts} +37 -119
- package/dist/index.d.ts +8 -66
- package/dist/index.js +11 -219
- package/dist/internal/index.d.ts +5745 -0
- package/dist/internal/index.js +6379 -0
- package/dist/{job-store-6gR4pZRP.d.ts → job-store-m2pYmvbr.d.ts} +1493 -12
- package/dist/{memory-credit-ledger-I2G64DDK.js → memory-credit-ledger-XJ5VQEVP.js} +3 -3
- package/dist/payout-reporter-3UB5WRCV.js +13 -0
- package/dist/revenue-reporter-JIKUPXOK.js +7 -0
- package/dist/server/index.d.ts +14 -3590
- package/dist/server/index.js +309 -20796
- package/dist/ssrf-DbFkpDv0.d.ts +118 -0
- package/dist/tempo-charge-store-RIFTALZK.js +8 -0
- package/dist/tempo-session-store-DALMRIWN.js +11 -0
- package/dist/testing/index.d.ts +3 -2
- package/dist/testing/index.js +3 -2
- package/dist/usd-DjVAPMlf.d.ts +97 -0
- package/dist/x402-5EVIUSEP.js +81 -0
- package/package.json +6 -2
- package/dist/x402-35VLYFKZ.js +0 -1272
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Currency } from './job-store-
|
|
1
|
+
import { C as Currency } from './job-store-m2pYmvbr.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Quote-time fx snapshot. Embedded in scribe's `lockedQuote` for within-job
|
|
@@ -60,6 +60,19 @@ interface PlatformFxSource {
|
|
|
60
60
|
url: string;
|
|
61
61
|
token: string;
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* In-process cache window for the fx snapshot. 5 minutes amortises rate-limit
|
|
65
|
+
* budget across concurrent quotes and gives a within-window quote → job pair a
|
|
66
|
+
* consistent rate without an explicit lock. Bumped from 60s in internal-review; the
|
|
67
|
+
* per-call drift remains sub-cent at typical DVM amounts (BTC's intra-5-min
|
|
68
|
+
* volatility is well under 1%). Within-job consistency is unaffected — scribe's
|
|
69
|
+
* `lockedQuote.fx` is captured once at quote-time and reused at settlement
|
|
70
|
+
* regardless of the TTL. Hosted-DVM upstream fan-out is handled by internal-review
|
|
71
|
+
* (platform-served fx endpoint) rather than this TTL.
|
|
72
|
+
*/
|
|
73
|
+
declare const FX_CACHE_TTL_MS = 300000;
|
|
74
|
+
/** Default retry budget per `fetch()` call before raising `fx_rate_unavailable`. */
|
|
75
|
+
declare const FX_RETRY_COUNT = 2;
|
|
63
76
|
/** Agent-relayable copy for an {@link FxRateUnavailableError}. */
|
|
64
77
|
interface FxRateUnavailableCopy {
|
|
65
78
|
display?: string;
|
|
@@ -163,6 +176,28 @@ interface CreateFxFetcherOpts {
|
|
|
163
176
|
}
|
|
164
177
|
/** Build the default fx fetcher backed by `DVMKIT_FX_SOURCE` (or CoinGecko). */
|
|
165
178
|
declare function createFxFetcher(opts?: CreateFxFetcherOpts): FxFetcher;
|
|
179
|
+
/**
|
|
180
|
+
* Prime `fetcher`'s snapshot cache so the first priced surface to need a rate
|
|
181
|
+
* isn't the process's first fetch (internal-review).
|
|
182
|
+
*
|
|
183
|
+
* internal-review gave every fx hop in the payment layer a ladder down onto
|
|
184
|
+
* {@link FxFetcher.lastKnown}, which closes the failure for any process that
|
|
185
|
+
* has ever held a rate. A free-to-submit DVM never has: the job is free, so no
|
|
186
|
+
* quote fetches one, and the ask-time pin in `ctx.requestPayment` *is* the
|
|
187
|
+
* first fetch — a blip at the rate provider in that window has nothing to
|
|
188
|
+
* degrade onto and still costs the receipt its whole `credit` block.
|
|
189
|
+
*
|
|
190
|
+
* Fire-and-forget by construction: never awaited, so it can't slow a boot;
|
|
191
|
+
* never rethrown, so it can't fail one. A failure is deliberately **silent** —
|
|
192
|
+
* a warn on every offline `dvmctl dev` boot is worse than the gap this closes,
|
|
193
|
+
* and the first real ask walks the internal-review ladder and names its own failure
|
|
194
|
+
* there. Racing that ask costs one upstream fetch rather than two, because
|
|
195
|
+
* {@link createFxFetcher} dedupes in flight.
|
|
196
|
+
*
|
|
197
|
+
* Already-warm fetchers are left alone, so calling this twice — or against an
|
|
198
|
+
* injected fetcher a caller primed itself — is free.
|
|
199
|
+
*/
|
|
200
|
+
declare function warmFxSnapshot(fetcher: FxFetcher): void;
|
|
166
201
|
/**
|
|
167
202
|
* Look up `currency`'s rate in `snapshot`.
|
|
168
203
|
*
|
|
@@ -205,121 +240,4 @@ declare function fxRateFor(snapshot: FxRateSnapshot, currency: Currency): number
|
|
|
205
240
|
*/
|
|
206
241
|
declare function resolveFxSourceFromEnv(env: Record<string, string | undefined>): string | PlatformFxSource | undefined;
|
|
207
242
|
|
|
208
|
-
|
|
209
|
-
* Why `assertSafeUrl` refused this URL. Carried on `SSRFError` so callers can
|
|
210
|
-
* branch without string-matching `err.message` — DNS failures (NXDOMAIN, empty
|
|
211
|
-
* resolution), unparseable URLs, and a non-https scheme each have distinct
|
|
212
|
-
* recovery paths and shouldn't collapse together.
|
|
213
|
-
*/
|
|
214
|
-
type SSRFReason = "invalid_url" | "insecure_scheme" | "private_address" | "dns_failed";
|
|
215
|
-
/**
|
|
216
|
-
* Thrown when an input URL points at internal infrastructure we refuse to
|
|
217
|
-
* fetch from inside the cluster. Carries a `reason` discriminator so the
|
|
218
|
-
* caller can map cleanly onto its public taxonomy instead of inspecting the
|
|
219
|
-
* message string.
|
|
220
|
-
*/
|
|
221
|
-
declare class SSRFError extends Error {
|
|
222
|
-
readonly code: "ssrf_blocked";
|
|
223
|
-
readonly reason: SSRFReason;
|
|
224
|
-
constructor(reason: SSRFReason, message: string);
|
|
225
|
-
}
|
|
226
|
-
/** Pluggable DNS resolver. Defaults to `node:dns/promises` `lookup(host, { all: true })`. */
|
|
227
|
-
type SSRFResolver = (host: string) => Promise<{
|
|
228
|
-
address: string;
|
|
229
|
-
family: number;
|
|
230
|
-
}[]>;
|
|
231
|
-
/**
|
|
232
|
-
* Configurable hooks for `assertSafeUrl`. Defaults preserve the dvmkit
|
|
233
|
-
* baseline denylist; extra entries compose with (never replace) the defaults.
|
|
234
|
-
*/
|
|
235
|
-
interface SSRFGuardOpts {
|
|
236
|
-
/** Custom DNS resolver — typically a test stub. */
|
|
237
|
-
resolver?: SSRFResolver;
|
|
238
|
-
/** Additional exact hostnames to deny on top of the defaults (lowercased on input). */
|
|
239
|
-
extraHostnamesExact?: Iterable<string>;
|
|
240
|
-
/**
|
|
241
|
-
* Additional hostname suffixes to deny on top of the defaults. Each entry
|
|
242
|
-
* should include the leading dot (e.g. `.example.invalid`) — the same shape
|
|
243
|
-
* the defaults use.
|
|
244
|
-
*/
|
|
245
|
-
extraHostnameSuffixes?: Iterable<string>;
|
|
246
|
-
/** Additional IPv4 predicate. Returns true for an unsafe address. */
|
|
247
|
-
extraUnsafeIPv4?: (ipv4: string) => boolean;
|
|
248
|
-
/** Additional IPv6 predicate. Returns true for an unsafe address. */
|
|
249
|
-
extraUnsafeIPv6?: (ipv6: string) => boolean;
|
|
250
|
-
/**
|
|
251
|
-
* Underlying fetch implementation `createPinnedFetch` wraps. Defaults to
|
|
252
|
-
* `globalThis.fetch`. Pass an instrumented wrapper (e.g. scribe's
|
|
253
|
-
* `ctx.fetch` from `createInstrumentedFetch`) to keep `dvm.fetch` spans
|
|
254
|
-
* around outbound HEAD / GET — the dispatcher pin for every safe redirect
|
|
255
|
-
* hop is preserved because `init.dispatcher` flows through any thin fetch
|
|
256
|
-
* wrapper unchanged.
|
|
257
|
-
*/
|
|
258
|
-
baseFetch?: typeof fetch;
|
|
259
|
-
/**
|
|
260
|
-
* Called after a redirect response is accepted and before the target is
|
|
261
|
-
* resolved or connected. Lets an outer transport refuse to replay an
|
|
262
|
-
* original request once its source has demonstrably responded.
|
|
263
|
-
*/
|
|
264
|
-
onRedirectFollowed?: () => void;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Reject URLs whose host points at internal infrastructure — private /
|
|
268
|
-
* link-local IP ranges, the AWS / GCP metadata IP, Fly's `*.internal`
|
|
269
|
-
* service-discovery names, and a small denylist of obviously-internal
|
|
270
|
-
* hostnames. Defence-in-depth — full mitigation belongs at the egress firewall
|
|
271
|
-
* and at fetch-time IP pinning (`createPinnedFetch`, internal-review).
|
|
272
|
-
*
|
|
273
|
-
* Throws `SSRFError` with a `reason` discriminator so callers can branch onto
|
|
274
|
-
* their own typed error taxonomy.
|
|
275
|
-
*/
|
|
276
|
-
declare function assertSafeUrl(url: string, opts?: SSRFGuardOpts): Promise<void>;
|
|
277
|
-
/**
|
|
278
|
-
* A `fetch`-compatible function pinned to a single pre-validated IP address.
|
|
279
|
-
*
|
|
280
|
-
* Returned by {@link createPinnedFetch}. Closes the DNS-rebinding window in
|
|
281
|
-
* which `assertSafeUrl` validates an address but the subsequent `fetch()`
|
|
282
|
-
* re-resolves DNS and lands on a different (private) address.
|
|
283
|
-
*
|
|
284
|
-
* Callers MUST `await close()` when done with the pin so undici's connection
|
|
285
|
-
* pool is released.
|
|
286
|
-
*/
|
|
287
|
-
interface PinnedFetch {
|
|
288
|
-
/** Fetch whose initial request and every followed redirect are pinned to their pre-validated address sets. */
|
|
289
|
-
fetch: typeof fetch;
|
|
290
|
-
/** Primary (first) validated address of the initial URL. */
|
|
291
|
-
address: string;
|
|
292
|
-
/** IP family (4 or 6) of {@link address} (the primary). */
|
|
293
|
-
family: number;
|
|
294
|
-
/** Release the undici connection pool backing this pinned fetch. */
|
|
295
|
-
close(): Promise<void>;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Resolve the URL's host once, validate every answer against the same
|
|
299
|
-
* denylist `assertSafeUrl` applies, then return a `fetch` whose `connect.lookup`
|
|
300
|
-
* replays that frozen, pre-validated address set (see {@link buildPinnedLookup}).
|
|
301
|
-
* Redirects are followed manually: each target URL is independently required
|
|
302
|
-
* to be HTTPS, resolved once, denylist-checked, and pinned before it can be
|
|
303
|
-
* requested. That prevents an automatic redirect from reusing the source
|
|
304
|
-
* host's dispatcher for a different hostname, while keeping a redirect from
|
|
305
|
-
* opening a DNS-rebinding window.
|
|
306
|
-
*
|
|
307
|
-
* Throws `SSRFError` with the same `reason` discriminator as `assertSafeUrl`,
|
|
308
|
-
* so error-mapping paths (`mapSsrfError` in cast/scribe) work unchanged.
|
|
309
|
-
*
|
|
310
|
-
* For IP-literal URLs no dispatcher is needed, but redirects still pass
|
|
311
|
-
* through the same validation loop.
|
|
312
|
-
*
|
|
313
|
-
* Replaying the full validated set (not just `addresses[0]`) preserves Node's
|
|
314
|
-
* happy-eyeballs fallback across the answers — a host whose first address is an
|
|
315
|
-
* unroutable family still connects via the others — while keeping the rebind
|
|
316
|
-
* window closed, since every replayed address was already denylist-checked.
|
|
317
|
-
*
|
|
318
|
-
* Pass `opts.baseFetch` to wrap an instrumented fetch (e.g. scribe's
|
|
319
|
-
* `ctx.fetch` from `createInstrumentedFetch`) — `dvm.fetch` spans flow
|
|
320
|
-
* through because `init.dispatcher` is forwarded to the underlying
|
|
321
|
-
* `globalThis.fetch` unchanged.
|
|
322
|
-
*/
|
|
323
|
-
declare function createPinnedFetch(url: string, opts?: SSRFGuardOpts): Promise<PinnedFetch>;
|
|
324
|
-
|
|
325
|
-
export { type CreateFxFetcherOpts as C, DEFAULT_FX_CURRENCIES as D, type FxFetcher as F, type PinnedFetch as P, SSRFError as S, DEFAULT_FX_RATE_SOURCE as a, type FxRateSnapshot as b, FxRateUnavailableError as c, type PlatformFxSource as d, type SSRFGuardOpts as e, type SSRFReason as f, type SSRFResolver as g, assertSafeUrl as h, createFxFetcher as i, createPinnedFetch as j, fxRateFor as k, resolveFxSourceFromEnv as r };
|
|
243
|
+
export { type CreateFxFetcherOpts as C, DEFAULT_FX_CURRENCIES as D, type FxFetcher as F, type PlatformFxSource as P, DEFAULT_FX_RATE_SOURCE as a, type FxRateSnapshot as b, FxRateUnavailableError as c, createFxFetcher as d, FX_CACHE_TTL_MS as e, fxRateFor as f, FX_RETRY_COUNT as g, resolveFxSourceFromEnv as r, warmFxSnapshot as w };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { C as Currency, Z as ZodLike, D as DVMConfig, a as DVMDescriptor } from './job-store-
|
|
2
|
-
export { A as ApprovalContent, b as ArtifactContent, c as CancelContent, d as CanonicalEnvelope, e as CreateSignedRequestVerifierOpts, f as CreditConfig, g as CreditView, h as DEFAULT_CREDIT_MAX, i as DEFAULT_CREDIT_MIN, j as DEFAULT_CREDIT_TTL_SECONDS, k as DVMRouteContext, I as IncomingMessage, l as InputType, m as InvalidCurrencyError, J as JobRecord, n as JobStatus, o as JobStore, K as KVStore, L as Logger, P as PaymentContent, p as PaymentMethod, q as PriceValue, r as ProgressContent, s as PromptOpts, Q as QuoteConfig, t as QuoteContext, u as QuoteResult, R as ResolvedCreditConfig, v as ResponseContent, S as SDKJobContext, w as SDKPaymentRequestOpts, x as SIGNED_REQUEST_AUTH_ID, y as SIGNED_REQUEST_STATEMENT_VERSION, z as SignedRequestAudience, B as SignedRequestDomain, E as SignedRequestError, F as SignedRequestFailure, G as SignedRequestReplayStore, H as SignedRequestSignOpts, M as SignedRequestStatementHeader, N as SignedRequestVerifier, U as UnsupportedCurrencyError, O as createSignedRequestVerifier, T as isZodSchema, V as signedRequestStatementHeader, W as validateCurrency } from './job-store-
|
|
3
|
-
export { C as CreateFxFetcherOpts, D as DEFAULT_FX_CURRENCIES, a as DEFAULT_FX_RATE_SOURCE, F as FxFetcher, b as FxRateSnapshot, c as FxRateUnavailableError, P as
|
|
1
|
+
import { C as Currency, Z as ZodLike, D as DVMConfig, a as DVMDescriptor } from './job-store-m2pYmvbr.js';
|
|
2
|
+
export { A as ApprovalContent, b as ArtifactContent, c as CancelContent, d as CanonicalEnvelope, e as CreateSignedRequestVerifierOpts, f as CreditConfig, g as CreditView, h as DEFAULT_CREDIT_MAX, i as DEFAULT_CREDIT_MIN, j as DEFAULT_CREDIT_TTL_SECONDS, k as DVMRouteContext, I as IncomingMessage, l as InputType, m as InvalidCurrencyError, J as JobRecord, n as JobStatus, o as JobStore, K as KVStore, L as Logger, P as PaymentContent, p as PaymentMethod, q as PriceValue, r as ProgressContent, s as PromptOpts, Q as QuoteConfig, t as QuoteContext, u as QuoteResult, R as ResolvedCreditConfig, v as ResponseContent, S as SDKJobContext, w as SDKPaymentRequestOpts, x as SIGNED_REQUEST_AUTH_ID, y as SIGNED_REQUEST_STATEMENT_VERSION, z as SignedRequestAudience, B as SignedRequestDomain, E as SignedRequestError, F as SignedRequestFailure, G as SignedRequestReplayStore, H as SignedRequestSignOpts, M as SignedRequestStatementHeader, N as SignedRequestVerifier, U as UnsupportedCurrencyError, O as createSignedRequestVerifier, T as isZodSchema, V as signedRequestStatementHeader, W as validateCurrency } from './job-store-m2pYmvbr.js';
|
|
3
|
+
export { C as CreateFxFetcherOpts, D as DEFAULT_FX_CURRENCIES, a as DEFAULT_FX_RATE_SOURCE, F as FxFetcher, b as FxRateSnapshot, c as FxRateUnavailableError, P as PlatformFxSource, d as createFxFetcher, f as fxRateFor, r as resolveFxSourceFromEnv } from './fx-Bq4cvn16.js';
|
|
4
|
+
export { I as InvalidFxRateError, f as fiatToSatsCeil, a as formatFiat, b as formatUsd, r as roundUsd, s as satsToFiat } from './usd-DjVAPMlf.js';
|
|
5
|
+
export { P as PinnedFetch, S as SSRFError, a as SSRFGuardOpts, b as SSRFReason, c as SSRFResolver, d as assertSafeUrl, e as createPinnedFetch } from './ssrf-DbFkpDv0.js';
|
|
4
6
|
export { z } from 'zod';
|
|
5
7
|
import '@cashu/cashu-ts';
|
|
6
8
|
import 'mppx';
|
|
7
|
-
import 'hono';
|
|
8
9
|
import '@x402/core/server';
|
|
9
10
|
import '@x402/evm/batch-settlement/server';
|
|
11
|
+
import 'hono';
|
|
12
|
+
import 'viem';
|
|
10
13
|
import 'pg';
|
|
11
14
|
import '@x402/core/types';
|
|
12
15
|
|
|
@@ -32,67 +35,6 @@ declare function buildUpfront(input: {
|
|
|
32
35
|
currency: string;
|
|
33
36
|
}): Upfront;
|
|
34
37
|
|
|
35
|
-
/**
|
|
36
|
-
* Thrown by `fiatToSatsCeil` / `satsToFiat` when the supplied `ratePerBtc` is
|
|
37
|
-
* non-finite or non-positive. Surfaces a misconfigured fx fetcher loudly
|
|
38
|
-
* instead of silently producing a 0-sats charge or a 0-fiat display string —
|
|
39
|
-
* a silent 0 lost real money in internal-review slice-04. Callers obtain a valid rate
|
|
40
|
-
* from the SDK's fx fetcher (which raises `FxRateUnavailableError` on
|
|
41
|
-
* upstream failure); this error is the defence-in-depth backstop for any
|
|
42
|
-
* caller that constructs a rate by other means.
|
|
43
|
-
*/
|
|
44
|
-
declare class InvalidFxRateError extends Error {
|
|
45
|
-
readonly code = "invalid_fx_rate";
|
|
46
|
-
readonly ratePerBtc: unknown;
|
|
47
|
-
constructor(ratePerBtc: unknown);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Convert a fiat amount to integer sats at the supplied fiat-per-BTC rate.
|
|
51
|
-
* Rounds **up** so the operator never overcharges the caller (caller-friendly
|
|
52
|
-
* rounding). Negative or non-finite amounts collapse to 0 (a $0 charge is
|
|
53
|
-
* well-defined). A non-finite or non-positive `ratePerBtc` raises
|
|
54
|
-
* `InvalidFxRateError` — a silent 0 would mask a misconfigured fetcher into
|
|
55
|
-
* shipping a free charge. The math is currency-agnostic — `ratePerBtc` is the
|
|
56
|
-
* number of units of *some* fiat per 1 BTC; the caller looks the right rate
|
|
57
|
-
* out of an `FxRateSnapshot`.
|
|
58
|
-
*/
|
|
59
|
-
declare function fiatToSatsCeil(amount: number, ratePerBtc: number): number;
|
|
60
|
-
/**
|
|
61
|
-
* Convert integer sats back to a fiat amount at the supplied fiat-per-BTC
|
|
62
|
-
* rate. A non-finite or non-positive `ratePerBtc` raises
|
|
63
|
-
* `InvalidFxRateError` — the silent `0` would land in display strings and
|
|
64
|
-
* mislead callers about the value at hand.
|
|
65
|
-
*/
|
|
66
|
-
declare function satsToFiat(sats: number, ratePerBtc: number): number;
|
|
67
|
-
/**
|
|
68
|
-
* Round a USD value to 4 decimal places (matches scribe's stored display
|
|
69
|
-
* precision). This is the **storage / accounting** precision — preserves
|
|
70
|
-
* sub-cent amounts so a `$0.0150` per-minute rate doesn't collapse to
|
|
71
|
-
* `$0.02` before it ever reaches a multiplier. See {@link formatUsd} for the
|
|
72
|
-
* human-render precision, which deliberately truncates at the cent boundary
|
|
73
|
-
* for amounts ≥ $0.01.
|
|
74
|
-
*/
|
|
75
|
-
declare function roundUsd(usd: number): number;
|
|
76
|
-
/**
|
|
77
|
-
* Format a USD amount with adaptive precision so sub-cent amounts don't
|
|
78
|
-
* render as `$0.00`. The 2dp/4dp boundary at $0.01 is **human-render**
|
|
79
|
-
* precision and is intentionally coarser than {@link roundUsd}'s storage
|
|
80
|
-
* precision: a value like `0.015` keeps four decimals through `roundUsd`
|
|
81
|
-
* (storage / multiplier inputs) but renders as `$0.02` here (human display).
|
|
82
|
-
* Don't try to unify the two — they serve different surfaces.
|
|
83
|
-
*/
|
|
84
|
-
declare function formatUsd(usd: number): string;
|
|
85
|
-
/**
|
|
86
|
-
* Format a fiat amount with currency-aware symbol, precision, and ISO code.
|
|
87
|
-
* Used by the CLI to render payment-request `fiat_amount` envelopes ("$0.05
|
|
88
|
-
* USD", "€0.05 EUR", "¥5 JPY"). Precision is adaptive: JPY has no sub-yen
|
|
89
|
-
* denomination so renders as 0 decimals, every other currency uses 2 decimals
|
|
90
|
-
* at or above 0.01 and 4 below so sub-cent figures don't collapse to 0.00.
|
|
91
|
-
* Unknown currencies (anything not in {@link FIAT_SYMBOLS}) render with the
|
|
92
|
-
* uppercased ISO code and no leading symbol.
|
|
93
|
-
*/
|
|
94
|
-
declare function formatFiat(amount: number, currency: Currency): string;
|
|
95
|
-
|
|
96
38
|
/** Create a typed DVM descriptor from a configuration object. */
|
|
97
39
|
declare function configureDVM<State = Record<string, unknown>, InputSchema extends ZodLike | undefined = undefined>(config: DVMConfig<State, InputSchema>): DVMDescriptor<State, InputSchema>;
|
|
98
40
|
/** Check whether this SDK module instance created a value through `configureDVM`. */
|
|
@@ -141,4 +83,4 @@ interface ProgressHeartbeatOpts {
|
|
|
141
83
|
*/
|
|
142
84
|
declare function withProgressHeartbeat<T>(opts: ProgressHeartbeatOpts, fn: () => Promise<T>): Promise<T>;
|
|
143
85
|
|
|
144
|
-
export { Currency, DVMConfig, DVMDescriptor,
|
|
86
|
+
export { Currency, DVMConfig, DVMDescriptor, type ProgressHeartbeatOpts, type Upfront, ZodLike, buildUpfront, configureDVM, envFlag, isConfiguredDVMDescriptor, withProgressHeartbeat };
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SSRFError,
|
|
3
|
+
assertSafeUrl,
|
|
4
|
+
createPinnedFetch,
|
|
5
|
+
envFlag
|
|
6
|
+
} from "./chunk-TVI4V7GF.js";
|
|
7
|
+
import {
|
|
8
|
+
configureDVM,
|
|
9
|
+
isConfiguredDVMDescriptor
|
|
10
|
+
} from "./chunk-XYTSDAPH.js";
|
|
1
11
|
import {
|
|
2
12
|
DEFAULT_CREDIT_MAX,
|
|
3
13
|
DEFAULT_CREDIT_MIN,
|
|
4
14
|
DEFAULT_CREDIT_TTL_SECONDS,
|
|
5
15
|
SIGNED_REQUEST_AUTH_ID,
|
|
6
16
|
SIGNED_REQUEST_STATEMENT_VERSION,
|
|
7
|
-
SSRFError,
|
|
8
17
|
SignedRequestError,
|
|
9
|
-
assertSafeUrl,
|
|
10
|
-
createPinnedFetch,
|
|
11
18
|
createSignedRequestVerifier,
|
|
12
|
-
envFlag,
|
|
13
19
|
isZodSchema,
|
|
14
20
|
signedRequestStatementHeader
|
|
15
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-AZBXSXQT.js";
|
|
16
22
|
import {
|
|
17
23
|
DEFAULT_FX_CURRENCIES,
|
|
18
24
|
DEFAULT_FX_RATE_SOURCE,
|
|
@@ -30,224 +36,10 @@ import {
|
|
|
30
36
|
fiatToSatsCeil,
|
|
31
37
|
formatFiat,
|
|
32
38
|
formatUsd,
|
|
33
|
-
parseUsdPrice,
|
|
34
39
|
roundUsd,
|
|
35
40
|
satsToFiat
|
|
36
41
|
} from "./chunk-AT6V3SY7.js";
|
|
37
42
|
|
|
38
|
-
// src/lib/wire-schema.ts
|
|
39
|
-
var CAPABILITY_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
40
|
-
|
|
41
|
-
// src/sdk/configure.ts
|
|
42
|
-
function configureDVM(config) {
|
|
43
|
-
const tags = collectTags(config);
|
|
44
|
-
const currency = resolvePricingCurrency(config.currency);
|
|
45
|
-
const capabilities = buildCapabilityMap(config, currency);
|
|
46
|
-
const onlyCapability = singleCapabilityEntry(capabilities);
|
|
47
|
-
const descriptor = {
|
|
48
|
-
name: config.name,
|
|
49
|
-
description: config.description ?? "",
|
|
50
|
-
tags,
|
|
51
|
-
currency,
|
|
52
|
-
price: onlyCapability?.price,
|
|
53
|
-
inputSchema: onlyCapability?.input,
|
|
54
|
-
state: onlyCapability?.state ?? {},
|
|
55
|
-
idleTimeout: config.idleTimeout ?? 3600,
|
|
56
|
-
processingWatchdog: config.processingWatchdog,
|
|
57
|
-
config,
|
|
58
|
-
capabilities,
|
|
59
|
-
paymentMethods: config.paymentMethods,
|
|
60
|
-
x402: config.x402,
|
|
61
|
-
mpp: config.mpp,
|
|
62
|
-
credit: resolveCreditConfig(config.credit),
|
|
63
|
-
auth: config.auth
|
|
64
|
-
};
|
|
65
|
-
CONFIGURED_DVM_DESCRIPTORS.add(descriptor);
|
|
66
|
-
return Object.freeze(descriptor);
|
|
67
|
-
}
|
|
68
|
-
function isConfiguredDVMDescriptor(value) {
|
|
69
|
-
return typeof value === "object" && value !== null && CONFIGURED_DVM_DESCRIPTORS.has(value);
|
|
70
|
-
}
|
|
71
|
-
var CONFIGURED_DVM_DESCRIPTORS = /* @__PURE__ */ new WeakSet();
|
|
72
|
-
function resolvePricingCurrency(raw) {
|
|
73
|
-
if (raw === void 0) return "usd";
|
|
74
|
-
try {
|
|
75
|
-
return validateCurrency(raw);
|
|
76
|
-
} catch (err) {
|
|
77
|
-
if (err instanceof InvalidCurrencyError) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`configureDVM: currency ${JSON.stringify(raw)} is not an ISO 4217 lowercase code like "usd" or "eur". Omit the field to price in USD.`,
|
|
80
|
-
{ cause: err }
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
throw err;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function resolveCreditConfig(credit) {
|
|
87
|
-
if (!credit) return void 0;
|
|
88
|
-
const min = credit.min ?? DEFAULT_CREDIT_MIN;
|
|
89
|
-
const max = credit.max ?? DEFAULT_CREDIT_MAX;
|
|
90
|
-
const ttlSeconds = credit.ttl ?? DEFAULT_CREDIT_TTL_SECONDS;
|
|
91
|
-
const minAmount = validatePriceLiteral(min, "credit.min");
|
|
92
|
-
const maxAmount = validatePriceLiteral(max, "credit.max");
|
|
93
|
-
if (minAmount > maxAmount) {
|
|
94
|
-
throw new Error(
|
|
95
|
-
`credit.min (${JSON.stringify(min)}) exceeds credit.max (${JSON.stringify(max)}) \u2014 no funding amount could satisfy both.`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
if (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0) {
|
|
99
|
-
throw new Error(`credit.ttl must be a positive number of seconds (got ${ttlSeconds}).`);
|
|
100
|
-
}
|
|
101
|
-
const allowOneShotStablecoin = credit.allowOneShotStablecoin === true;
|
|
102
|
-
return { min, max, ttlSeconds, allowOneShotStablecoin };
|
|
103
|
-
}
|
|
104
|
-
function validatePriceLiteral(value, field, key = field) {
|
|
105
|
-
if (typeof value === "number") {
|
|
106
|
-
throw new Error(
|
|
107
|
-
`${field} is a number (${value}). Prices are USD literals \u2014 write ${key}: "$0.01" for one cent. Callers still pay in sats; the SDK converts at request time. Omit the field for a free capability, or use \`onQuote\` for dynamic pricing.`
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
if (typeof value !== "string") {
|
|
111
|
-
throw new Error(`${field} must be a USD literal like "$0.05" (got ${JSON.stringify(value)}).`);
|
|
112
|
-
}
|
|
113
|
-
return parseUsdPrice(value, field);
|
|
114
|
-
}
|
|
115
|
-
function validateCapabilityPrice(price, capabilityName, currency) {
|
|
116
|
-
if (price === void 0) return;
|
|
117
|
-
if (currency !== "usd") {
|
|
118
|
-
throw new Error(
|
|
119
|
-
`configureDVM: capability "${capabilityName}" declares a static price (${JSON.stringify(price)}) but this DVM prices in ${currency}. Static prices are USD literals, so they cannot express ${currency} \u2014 price this capability with \`onQuote\` returning { amount, currency: ${JSON.stringify(currency)} }, or drop the DVM-level \`currency\` to price in USD.`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
validatePriceLiteral(price, `configureDVM: capability "${capabilityName}" price`, "price");
|
|
123
|
-
}
|
|
124
|
-
function collectTags(config) {
|
|
125
|
-
const tagSet = /* @__PURE__ */ new Set();
|
|
126
|
-
if (config.tag) tagSet.add(config.tag);
|
|
127
|
-
if (config.tags) {
|
|
128
|
-
for (const t of config.tags) tagSet.add(t);
|
|
129
|
-
}
|
|
130
|
-
return [...tagSet];
|
|
131
|
-
}
|
|
132
|
-
function buildCapabilityMap(config, currency) {
|
|
133
|
-
const hasExplicitBlock = config.capabilities !== void 0;
|
|
134
|
-
const hasFlatCapabilityName = typeof config.capability === "string";
|
|
135
|
-
const hasFlatHandler = typeof config.onJob === "function";
|
|
136
|
-
if (hasExplicitBlock && (hasFlatCapabilityName || hasFlatHandler || flatPerCapFieldsSet(config))) {
|
|
137
|
-
throw new Error(
|
|
138
|
-
"configureDVM: pass either the flat single-capability shape (capability + onJob + ...) or the explicit `capabilities` block, not both."
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
if (hasExplicitBlock) {
|
|
142
|
-
return buildExplicitCapabilities(config.capabilities ?? {}, currency);
|
|
143
|
-
}
|
|
144
|
-
if (!hasFlatHandler) {
|
|
145
|
-
throw new Error("configureDVM: onJob handler is required");
|
|
146
|
-
}
|
|
147
|
-
const name = typeof config.capability === "string" ? config.capability : slugifyCapabilityName(config.name);
|
|
148
|
-
validateCapabilityName(name);
|
|
149
|
-
validateCapabilityPrice(config.price, name, currency);
|
|
150
|
-
validateExampleAgainstInput(name, config.input, config.example);
|
|
151
|
-
const entry = {
|
|
152
|
-
name,
|
|
153
|
-
description: config.description ?? "",
|
|
154
|
-
input: config.input,
|
|
155
|
-
example: config.example,
|
|
156
|
-
state: config.state ?? {},
|
|
157
|
-
price: config.price,
|
|
158
|
-
onQuote: config.onQuote,
|
|
159
|
-
onJob: config.onJob,
|
|
160
|
-
onResponse: config.onResponse,
|
|
161
|
-
onPayment: config.onPayment,
|
|
162
|
-
onApproval: config.onApproval,
|
|
163
|
-
onCancel: config.onCancel,
|
|
164
|
-
onMessage: config.onMessage
|
|
165
|
-
};
|
|
166
|
-
return { [name]: entry };
|
|
167
|
-
}
|
|
168
|
-
function buildExplicitCapabilities(capabilities, currency) {
|
|
169
|
-
const keys = Object.keys(capabilities);
|
|
170
|
-
if (keys.length === 0) {
|
|
171
|
-
throw new Error("configureDVM: `capabilities` block must declare at least one capability");
|
|
172
|
-
}
|
|
173
|
-
const out = {};
|
|
174
|
-
for (const name of keys) {
|
|
175
|
-
validateCapabilityName(name);
|
|
176
|
-
const cap = capabilities[name];
|
|
177
|
-
if (typeof cap.onJob !== "function") {
|
|
178
|
-
throw new Error(`configureDVM: capability "${name}" is missing required onJob handler`);
|
|
179
|
-
}
|
|
180
|
-
validateCapabilityPrice(cap.price, name, currency);
|
|
181
|
-
validateExampleAgainstInput(name, cap.input, cap.example);
|
|
182
|
-
const entry = {
|
|
183
|
-
name,
|
|
184
|
-
description: cap.description ?? "",
|
|
185
|
-
// Per-capability `input` is the builder's Zod schema; the `any` widening
|
|
186
|
-
// through `Record<string, CapabilityConfig<any, any>>` is unavoidable
|
|
187
|
-
// here.
|
|
188
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
189
|
-
input: cap.input,
|
|
190
|
-
example: cap.example,
|
|
191
|
-
state: cap.state !== void 0 ? structuredClone(cap.state) : {},
|
|
192
|
-
price: cap.price,
|
|
193
|
-
onQuote: cap.onQuote,
|
|
194
|
-
onJob: cap.onJob,
|
|
195
|
-
onResponse: cap.onResponse,
|
|
196
|
-
onPayment: cap.onPayment,
|
|
197
|
-
onApproval: cap.onApproval,
|
|
198
|
-
onCancel: cap.onCancel,
|
|
199
|
-
onMessage: cap.onMessage
|
|
200
|
-
};
|
|
201
|
-
out[name] = entry;
|
|
202
|
-
}
|
|
203
|
-
return out;
|
|
204
|
-
}
|
|
205
|
-
function flatPerCapFieldsSet(config) {
|
|
206
|
-
return config.input !== void 0 || config.example !== void 0 || config.state !== void 0 || config.price !== void 0 || config.onQuote !== void 0 || typeof config.onResponse === "function" || typeof config.onPayment === "function" || typeof config.onApproval === "function" || typeof config.onCancel === "function" || typeof config.onMessage === "function";
|
|
207
|
-
}
|
|
208
|
-
function validateExampleAgainstInput(capName, input, example) {
|
|
209
|
-
if (example === void 0) return;
|
|
210
|
-
if (!isZodSchema(input)) return;
|
|
211
|
-
const result = input.safeParse(example);
|
|
212
|
-
if (result.success) return;
|
|
213
|
-
throw new Error(
|
|
214
|
-
`configureDVM: capability "${capName}" example does not satisfy its input schema. Update the example to match the schema (including any \`.refine()\` checks). Issues: ${formatZodIssues(result.error)}`
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
function formatZodIssues(err) {
|
|
218
|
-
if (err && typeof err === "object" && "issues" in err && Array.isArray(err.issues)) {
|
|
219
|
-
const issues = err.issues;
|
|
220
|
-
if (issues.length === 0) return "(no issues reported)";
|
|
221
|
-
return issues.map((issue) => {
|
|
222
|
-
const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
|
|
223
|
-
return `${path}: ${issue.message}`;
|
|
224
|
-
}).join("; ");
|
|
225
|
-
}
|
|
226
|
-
if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
227
|
-
return err.message.replace(/\s+/g, " ").trim();
|
|
228
|
-
}
|
|
229
|
-
try {
|
|
230
|
-
return JSON.stringify(err);
|
|
231
|
-
} catch {
|
|
232
|
-
return String(err);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function validateCapabilityName(name) {
|
|
236
|
-
if (!CAPABILITY_NAME_RE.test(name)) {
|
|
237
|
-
throw new Error(
|
|
238
|
-
`configureDVM: invalid capability name "${name}" \u2014 must be lowercase letters/digits/hyphens, must start with a letter or digit, must not contain slashes.`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
function slugifyCapabilityName(name) {
|
|
243
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
244
|
-
}
|
|
245
|
-
function singleCapabilityEntry(capabilities) {
|
|
246
|
-
const keys = Object.keys(capabilities);
|
|
247
|
-
if (keys.length !== 1) return void 0;
|
|
248
|
-
return capabilities[keys[0]];
|
|
249
|
-
}
|
|
250
|
-
|
|
251
43
|
// src/sdk/heartbeat.ts
|
|
252
44
|
async function withProgressHeartbeat(opts, fn) {
|
|
253
45
|
const onProgress = opts.onProgress;
|