@gibs/quotes 1.13.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/LICENSE +15 -0
- package/README.md +45 -0
- package/dist/cache.d.ts +73 -0
- package/dist/cache.js +137 -0
- package/dist/carriers/calibration.d.ts +115 -0
- package/dist/carriers/calibration.js +83 -0
- package/dist/carriers/lifi.d.ts +63 -0
- package/dist/carriers/lifi.js +217 -0
- package/dist/carriers/near-intents.d.ts +91 -0
- package/dist/carriers/near-intents.js +249 -0
- package/dist/carriers/price-catalogue.d.ts +186 -0
- package/dist/carriers/price-catalogue.js +191 -0
- package/dist/carriers/relay.d.ts +97 -0
- package/dist/carriers/relay.js +242 -0
- package/dist/chain-lists.d.ts +144 -0
- package/dist/chain-lists.js +169 -0
- package/dist/client.d.ts +102 -0
- package/dist/client.js +402 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +18 -0
- package/dist/quote-guards.d.ts +128 -0
- package/dist/quote-guards.js +240 -0
- package/dist/quote-refusals.d.ts +180 -0
- package/dist/quote-refusals.js +284 -0
- package/dist/quote-service-contract.d.ts +2240 -0
- package/dist/quote-service-contract.js +247 -0
- package/dist/search/capabilities.d.ts +68 -0
- package/dist/search/capabilities.js +28 -0
- package/dist/search/enumerate.d.ts +162 -0
- package/dist/search/enumerate.js +313 -0
- package/dist/search/index.d.ts +17 -0
- package/dist/search/index.js +17 -0
- package/dist/search/serve.d.ts +178 -0
- package/dist/search/serve.js +142 -0
- package/dist/search/waves.d.ts +481 -0
- package/dist/search/waves.js +939 -0
- package/dist/single-flight.d.ts +90 -0
- package/dist/single-flight.js +142 -0
- package/dist/types.d.ts +502 -0
- package/dist/types.js +1 -0
- package/dist/upstream.d.ts +126 -0
- package/dist/upstream.js +343 -0
- package/llms.txt +217 -0
- package/package.json +142 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
import type { Ecosystem } from '@gibs/bridge-sdk/ecosystems';
|
|
2
|
+
import type { ProviderId } from '@gibs/bridge-sdk/providers';
|
|
3
|
+
import type { AssetNode, HopEdge, HopEdgeOrigin, HopFloor } from '@gibs/bridge-sdk/routing';
|
|
4
|
+
import type { AskEdge, AskEdgeAnswer, AskEdgeRequest, EdgeRefusal, EdgeRefusalKind } from './search/waves.js';
|
|
5
|
+
import type { DisplayProviderSlot } from './quote-service-contract.js';
|
|
6
|
+
/**
|
|
7
|
+
* The internal interfaces later steps in this package build against: the
|
|
8
|
+
* route-search {@link Carrier} plug-in vocabulary, the funnel every carrier's
|
|
9
|
+
* network calls pass through, the display-quote answer cache, and the
|
|
10
|
+
* configuration object that makes the whole library agnostic to any one
|
|
11
|
+
* host. See `docs/quote-service.md`'s "Library" and "Carriers" sections for
|
|
12
|
+
* the narrative this file's shapes implement.
|
|
13
|
+
*
|
|
14
|
+
* NOTHING HERE NAMES A HOST. No `gibs.finance`, no chain 369, no PulseChain,
|
|
15
|
+
* no omnibridge, no PulseX, no API key. Every fact a host supplies travels
|
|
16
|
+
* through {@link QuoteClientConfig} or a registered {@link Carrier} — never a
|
|
17
|
+
* constant in this file.
|
|
18
|
+
*/
|
|
19
|
+
/** Identifies one registered {@link Carrier}. A plain string, not a closed union — see this section's head note for why. */
|
|
20
|
+
export type CarrierId = string;
|
|
21
|
+
/** One asset a route search can hold: a token, or a chain's native currency, on one chain. Alias of `@gibs/bridge-sdk/routing`'s `AssetNode`. */
|
|
22
|
+
export type CarrierAssetNode = AssetNode;
|
|
23
|
+
/** How a hop's crossing is initiated. Alias of `@gibs/bridge-sdk/routing`'s `HopEdgeOrigin`. */
|
|
24
|
+
export type CarrierHopOrigin = HopEdgeOrigin;
|
|
25
|
+
/** A known minimum this edge has already been refused below. Alias of `@gibs/bridge-sdk/routing`'s `HopFloor`. */
|
|
26
|
+
export type CarrierHopFloor = HopFloor;
|
|
27
|
+
/**
|
|
28
|
+
* One directed edge a {@link Carrier} can move funds across. Alias of
|
|
29
|
+
* `@gibs/bridge-sdk/routing`'s `HopEdge` — that type's own `carrier` field is
|
|
30
|
+
* already {@link CarrierId} (a plain string), not a closed union, so no
|
|
31
|
+
* narrowing is needed here.
|
|
32
|
+
*/
|
|
33
|
+
export type CarrierHopEdge = HopEdge;
|
|
34
|
+
/** One request a {@link Carrier} is asked: "what does this edge deliver for this input?" Alias of `@gibs/bridge-sdk/routing`'s `AskEdgeRequest`. */
|
|
35
|
+
export type CarrierAskRequest = AskEdgeRequest;
|
|
36
|
+
/**
|
|
37
|
+
* Why an edge answered with a refusal rather than a deliverable amount.
|
|
38
|
+
* Alias of `@gibs/bridge-sdk/routing`'s `EdgeRefusalKind` — a broader
|
|
39
|
+
* vocabulary than {@link QuoteRefusalKind} in `quote-refusals.ts`, because a
|
|
40
|
+
* carrier can also refuse for a bound its own local read enforces
|
|
41
|
+
* (`'below-minimum'`, `'above-available'`), not only an aggregator's "no" or
|
|
42
|
+
* "could not ask."
|
|
43
|
+
*/
|
|
44
|
+
export type CarrierRefusalKind = EdgeRefusalKind;
|
|
45
|
+
/** Why a carrier refused, with its reason and any minimum learned from it. Alias of `@gibs/bridge-sdk/routing`'s `EdgeRefusal`. */
|
|
46
|
+
export type CarrierRefusal = EdgeRefusal;
|
|
47
|
+
/** What answering one {@link CarrierAskRequest} produces. Alias of `@gibs/bridge-sdk/routing`'s `AskEdgeAnswer`. */
|
|
48
|
+
export type CarrierAskAnswer = AskEdgeAnswer;
|
|
49
|
+
/** Asks one carrier's edge, at one amount, for what it delivers. Alias of `@gibs/bridge-sdk/routing`'s `AskEdge`. */
|
|
50
|
+
export type CarrierAsk = AskEdge;
|
|
51
|
+
/** What {@link Carrier.fusesWithNext} is asked about the hop immediately following the one it performs. */
|
|
52
|
+
export type CarrierFusesWithNextOptions = {
|
|
53
|
+
/**
|
|
54
|
+
* What the acting hop itself spends — its `from` node. A carrier whose
|
|
55
|
+
* destination call is available only from some origins (LI.FI's
|
|
56
|
+
* contract-calls endpoint answers for Ethereum alone) reads its chain here.
|
|
57
|
+
*/
|
|
58
|
+
readonly hopFrom: CarrierAssetNode;
|
|
59
|
+
/** The carrier of the hop immediately after this one in the same plan. */
|
|
60
|
+
readonly nextHopCarrier: CarrierId;
|
|
61
|
+
/**
|
|
62
|
+
* What the next hop spends — its `from` node, which is where the acting hop
|
|
63
|
+
* lands. A native coin here means the next hop's call is payable (the
|
|
64
|
+
* omnibridge's native entry), and a carrier that cannot attach value to its
|
|
65
|
+
* destination call must not fuse into it.
|
|
66
|
+
*/
|
|
67
|
+
readonly nextHopFrom: CarrierAssetNode;
|
|
68
|
+
/** The chain id the next hop DELIVERS to — its `to.chainId`. */
|
|
69
|
+
readonly nextHopDestinationChainId: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* How a reader may fund the first hop of a route section that starts with
|
|
73
|
+
* this carrier.
|
|
74
|
+
*
|
|
75
|
+
* REPLACES A BOOLEAN `acceptsByHandDeposit` FIELD ON PURPOSE — see this
|
|
76
|
+
* file's Code Style rule against boolean fields. `'wallet-signed'` and
|
|
77
|
+
* `'by-hand-deposit'` are not two poles of one axis a reader flips; a third,
|
|
78
|
+
* genuinely different carrier exists that offers BOTH (NEAR Intents, which
|
|
79
|
+
* carries a pay-by-hand origin's first hop today and also runs as an
|
|
80
|
+
* ordinary wallet-signed aggregator mid-route), and a boolean has no way to
|
|
81
|
+
* say "either" without a second field beside it — which is exactly the
|
|
82
|
+
* "two names for one fact" trap the boolean would have created.
|
|
83
|
+
*
|
|
84
|
+
* - `'wallet-signed'` — the reader's connected wallet signs a transaction.
|
|
85
|
+
* Every aggregator and on-chain carrier that has no deposit-address flow
|
|
86
|
+
* (LI.FI, Relay, the omnibridge crossing, a PulseX swap) is this.
|
|
87
|
+
* - `'by-hand-deposit'` — the reader sends funds to a carrier-supplied
|
|
88
|
+
* deposit address; no wallet signature. `@gibs/bridge-sdk/chain-names`'s
|
|
89
|
+
* `isDepositAddressChain` is the origin-side test this pairs with:
|
|
90
|
+
* `enumerate.ts` prunes every carrier whose `depositMode` is
|
|
91
|
+
* `'wallet-signed'` from a pay-by-hand origin's first hop.
|
|
92
|
+
* - `'either'` — the carrier can be funded either way, chosen elsewhere by
|
|
93
|
+
* which hop it fills (NEAR Intents today).
|
|
94
|
+
*/
|
|
95
|
+
export type CarrierDepositMode = 'wallet-signed' | 'by-hand-deposit' | 'either';
|
|
96
|
+
/**
|
|
97
|
+
* Which signer a route section STARTING with this carrier uses — what a
|
|
98
|
+
* display layer names next to its "sign" prompt.
|
|
99
|
+
*
|
|
100
|
+
* AN OPEN STRING-LITERAL UNION, LIKE {@link CarrierId}'s OWN NOTE EXPLAINS:
|
|
101
|
+
* this package's own built-in carriers (LI.FI, Relay, NEAR Intents) are all
|
|
102
|
+
* `'aggregator'`; a host's own carriers name the other two known values here
|
|
103
|
+
* (gibs registers the omnibridge crossing as `'omnibridge'` and its PulseX
|
|
104
|
+
* conversion leg as `'swap'`) or coins its own term entirely — the
|
|
105
|
+
* `(string & {})` member keeps editor autocomplete for the known set without
|
|
106
|
+
* closing the union to a host's own vocabulary, exactly as `CarrierId`
|
|
107
|
+
* itself stays a plain string rather than a closed union.
|
|
108
|
+
*/
|
|
109
|
+
export type CarrierSender = 'omnibridge' | 'aggregator' | 'swap' | 'deposit-address' | (string & {});
|
|
110
|
+
/**
|
|
111
|
+
* What {@link Carrier.exact} answers for one edge at one input amount, once
|
|
112
|
+
* {@link Carrier.prepare} has read whatever local data (a fee rate, a pool's
|
|
113
|
+
* reserves, a known limit) the answer needs.
|
|
114
|
+
*
|
|
115
|
+
* TWO CASES, BOTH DISTINCT FROM THE BARE `null` {@link Carrier.exact} MAY ALSO
|
|
116
|
+
* RETURN. `'delivered'` is the exact answer. `'refused'` is ALSO a real
|
|
117
|
+
* answer — the carrier read its own data and that data says this amount
|
|
118
|
+
* cannot cross (below a minimum, above an available reserve) — so it carries
|
|
119
|
+
* a {@link CarrierRefusal}, reusing the SAME reason vocabulary a live
|
|
120
|
+
* provider's own `ask()` refusal already uses, rather than inventing a
|
|
121
|
+
* second one for a local read. Bare `null` is reserved for a carrier that
|
|
122
|
+
* has NOTHING to say about this edge at all (its `prepare` was never called,
|
|
123
|
+
* or this edge fell outside what it read) — a structural non-answer, not a
|
|
124
|
+
* reasoned refusal; `enumerate.ts` treats both the same way for pruning
|
|
125
|
+
* (this candidate hop is dropped), but only `'refused'` carries a reason
|
|
126
|
+
* worth keeping.
|
|
127
|
+
*/
|
|
128
|
+
export type ExactHopResult = {
|
|
129
|
+
readonly outcome: 'delivered';
|
|
130
|
+
/** What this edge delivers for exactly the requested input amount, in `to`'s base units. */
|
|
131
|
+
readonly deliveredAmount: bigint;
|
|
132
|
+
} | {
|
|
133
|
+
readonly outcome: 'refused';
|
|
134
|
+
/** Why this carrier's own data says the amount cannot cross. */
|
|
135
|
+
readonly refusal: CarrierRefusal;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* What {@link Carrier.estimate} answers for one edge at one input amount — a
|
|
139
|
+
* PROVIDER MODEL, not a live quote and not a local exact read: a price ratio
|
|
140
|
+
* times (one minus the carrier's typical fee) minus a fixed cost, calibrated
|
|
141
|
+
* against observed history. `enumerate.ts` always grounds a figure this
|
|
142
|
+
* method produces on the `'provider-model'` {@link @gibs/bridge-sdk/routing!EstimateBasis} — RANKABLE
|
|
143
|
+
* (a search may compare and prune on it) but NEVER CONFIRMED (a search may
|
|
144
|
+
* never report it as the final winner without a live quote first) — see
|
|
145
|
+
* `@gibs/bridge-sdk/routing`'s `RANKABLE_ESTIMATE_BASES` and
|
|
146
|
+
* `isConfirmedBasis` for where that split is enforced.
|
|
147
|
+
*/
|
|
148
|
+
export type EstimatedHopResult = {
|
|
149
|
+
/** What this carrier's provider model expects this edge to deliver for the requested input amount, in `to`'s base units. */
|
|
150
|
+
readonly deliveredAmount: bigint;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* One plug-in the route-search maximization engine can ask: a built-in
|
|
154
|
+
* aggregator this package ships (LI.FI, Relay, NEAR Intents) or a host's own
|
|
155
|
+
* local read (an omnibridge crossing, a PulseX swap, or anything else a
|
|
156
|
+
* host's own code registers).
|
|
157
|
+
*
|
|
158
|
+
* `ProviderAsker` (the built-in aggregator's own vocabulary) IS a `Carrier` —
|
|
159
|
+
* see {@link ProviderAsker}'s doc for why the two are one vocabulary, not two.
|
|
160
|
+
*/
|
|
161
|
+
export type Carrier = {
|
|
162
|
+
/** This carrier's own id — what {@link CarrierHopEdge.carrier} names when this carrier performs a hop. */
|
|
163
|
+
readonly id: CarrierId;
|
|
164
|
+
/**
|
|
165
|
+
* Every edge this carrier can move funds across right now, given its own
|
|
166
|
+
* capability data (a chain list, a token list, bridgeable pairs — whatever
|
|
167
|
+
* it was built from). Mirrors `routing/search/capabilities.ts`'s
|
|
168
|
+
* `CapabilityIndex.carriersFrom`, scoped to one carrier rather than every
|
|
169
|
+
* registered one.
|
|
170
|
+
*/
|
|
171
|
+
readonly edgesFrom: (from: CarrierAssetNode, to: CarrierAssetNode) => readonly CarrierHopEdge[];
|
|
172
|
+
/** Asks one of this carrier's own edges. */
|
|
173
|
+
readonly ask: CarrierAsk;
|
|
174
|
+
/**
|
|
175
|
+
* Runs ONE BATCHED set of reads this carrier needs before {@link exact} can
|
|
176
|
+
* answer synchronously — a local carrier's own fee rate, pool reserves, or
|
|
177
|
+
* a known limit, read once per search rather than once per candidate hop.
|
|
178
|
+
* Called at most once per {@link enumerateRoutes} call, before any `exact`
|
|
179
|
+
* call, over every node the search might branch through; omitted entirely
|
|
180
|
+
* by a carrier with no local read to batch (every built-in aggregator this
|
|
181
|
+
* package ships — LI.FI, Relay, NEAR Intents — has none; they answer
|
|
182
|
+
* through {@link estimate} or a live `ask` instead).
|
|
183
|
+
*
|
|
184
|
+
* @param nodes - every node the search might branch through
|
|
185
|
+
*/
|
|
186
|
+
readonly prepare?: (nodes: readonly CarrierAssetNode[]) => Promise<void>;
|
|
187
|
+
/**
|
|
188
|
+
* A SYNCHRONOUS, EXACT answer for one edge at one input amount, read from
|
|
189
|
+
* whatever {@link prepare} already loaded — never a network call. The
|
|
190
|
+
* owner's own routing model ("each individual step and its associated fees
|
|
191
|
+
* can be known ahead of time") is what this method exists to express: a
|
|
192
|
+
* local carrier whose crossing or swap formula is closed-form given its own
|
|
193
|
+
* read (the omnibridge crossing, a PulseX swap, a native wrap/unwrap)
|
|
194
|
+
* answers here instead of estimating. Omitted by every carrier that has no
|
|
195
|
+
* closed-form answer — an aggregator supplies {@link estimate} instead, or
|
|
196
|
+
* neither, falling back to a caller-supplied price-feed estimate.
|
|
197
|
+
*
|
|
198
|
+
* WHEN THIS IS DEFINED, `enumerate.ts` NEVER FALLS THROUGH TO `estimate` OR
|
|
199
|
+
* THE PRICE-FEED DEFAULT FOR THIS CARRIER'S EDGES — a hop this method
|
|
200
|
+
* answers always carries a `'quote'` {@link @gibs/bridge-sdk/routing!HopOutcome},
|
|
201
|
+
* never an `'estimate'` one, because a carrier that KNOWS its own exact
|
|
202
|
+
* math has nothing left to estimate.
|
|
203
|
+
*
|
|
204
|
+
* @param edge - the candidate hop
|
|
205
|
+
* @param amountIn - the amount flowing into `edge`, in `edge.from`'s base units
|
|
206
|
+
* @returns the exact result, or null — see {@link ExactHopResult}'s own doc
|
|
207
|
+
* for the difference between its `'refused'` case and a bare `null`
|
|
208
|
+
*/
|
|
209
|
+
readonly exact?: (edge: CarrierHopEdge, amountIn: bigint) => ExactHopResult | null;
|
|
210
|
+
/**
|
|
211
|
+
* A PROVIDER-MODEL estimate for one edge at one input amount — a price
|
|
212
|
+
* ratio times (one minus this carrier's typical fee) minus a fixed cost,
|
|
213
|
+
* calibrated against observed history, never a live quote. The aggregator
|
|
214
|
+
* counterpart to {@link exact}: a carrier with no closed-form crossing
|
|
215
|
+
* formula (LI.FI, Relay, NEAR Intents) supplies this instead, so
|
|
216
|
+
* `enumerate.ts` can rank its edges on more than a bare price-feed
|
|
217
|
+
* conversion without asking the network during enumeration. See
|
|
218
|
+
* {@link EstimatedHopResult}'s own doc for the `'provider-model'`
|
|
219
|
+
* {@link @gibs/bridge-sdk/routing!EstimateBasis} every answer here is grounded on.
|
|
220
|
+
*
|
|
221
|
+
* RETURNS NULL WHEN THIS CARRIER HAS NOTHING TO SAY, MOST COMMONLY A
|
|
222
|
+
* MISSING PRICE — a provider-model figure needs both ends of an edge
|
|
223
|
+
* priced (`@gibs/quotes/carriers/price-catalogue!PriceCatalogue`); a token
|
|
224
|
+
* neither this carrier's own catalogue nor the search has learned a price
|
|
225
|
+
* for cannot be estimated, and guessing would rank it on a fabricated
|
|
226
|
+
* number. `enumerate.ts` reads `null` here exactly as it reads this method
|
|
227
|
+
* being absent altogether: it falls through to the caller-supplied
|
|
228
|
+
* price-feed `estimateHopOutput` instead of stopping the search.
|
|
229
|
+
*
|
|
230
|
+
* @param edge - the candidate hop
|
|
231
|
+
* @param amountIn - the amount flowing into `edge`, in `edge.from`'s base units
|
|
232
|
+
* @returns the modeled result, or null when this carrier cannot estimate this edge right now
|
|
233
|
+
*/
|
|
234
|
+
readonly estimate?: (edge: CarrierHopEdge, amountIn: bigint) => EstimatedHopResult | null;
|
|
235
|
+
/**
|
|
236
|
+
* Whether this carrier competes for a shared, rate-limited network budget,
|
|
237
|
+
* as opposed to a free local read (an on-chain fee-rate read, a
|
|
238
|
+
* `getAmountsOut` call). Mirrors `routing/search/graph.ts`'s
|
|
239
|
+
* `isRateLimitedCarrier`, expressed per-carrier rather than as a lookup
|
|
240
|
+
* over a fixed set.
|
|
241
|
+
*/
|
|
242
|
+
readonly isRateLimited: boolean;
|
|
243
|
+
/**
|
|
244
|
+
* How many of this carrier's requests may run at once, across every asker
|
|
245
|
+
* sharing it. Required when {@link isRateLimited} is true; meaningless (and
|
|
246
|
+
* ignored) for a free local read, which is never funnelled through a
|
|
247
|
+
* concurrency cap at all.
|
|
248
|
+
*/
|
|
249
|
+
readonly concurrencyLimit?: number;
|
|
250
|
+
/** How a reader may fund a section starting with this carrier — see {@link CarrierDepositMode}'s own doc. */
|
|
251
|
+
readonly depositMode: CarrierDepositMode;
|
|
252
|
+
/** Which signer a section starting with this carrier uses — see {@link CarrierSender}'s own doc. */
|
|
253
|
+
readonly sender: CarrierSender;
|
|
254
|
+
/**
|
|
255
|
+
* A typical wait, in seconds, for this carrier's own crossing to settle —
|
|
256
|
+
* used to score a plan before any live quote names a real figure (a live
|
|
257
|
+
* `ask()` answer's own `claimedDurationSeconds`, when one comes back,
|
|
258
|
+
* always takes precedence; see `routing/carriers/ask-answer.ts`'s
|
|
259
|
+
* `CarrierAskAnswer`). Static per carrier, not per edge, because this
|
|
260
|
+
* package's own route-search maximization runs this figure through scoring
|
|
261
|
+
* BEFORE any edge is asked — see the owner's own model, "each individual
|
|
262
|
+
* step and its associated fees can be known ahead of time."
|
|
263
|
+
*/
|
|
264
|
+
readonly settlementSeconds: number;
|
|
265
|
+
/**
|
|
266
|
+
* Whether THIS carrier's own execution, on arrival, also performs the
|
|
267
|
+
* NEXT hop in the plan — so the two hops together cost the reader only ONE
|
|
268
|
+
* signature. Mirrors `routing/search/graph.ts`'s `HopEdge.fusesNext`
|
|
269
|
+
* (which records the OUTCOME of this check for one specific pair of hops)
|
|
270
|
+
* and the OLD `hopFusesWithBridgeEntry`'s role (which HARD-CODED "the next
|
|
271
|
+
* hop is the omnibridge entry call" — a fact only gibs's own carriers may
|
|
272
|
+
* know).
|
|
273
|
+
*
|
|
274
|
+
* OMITTED MEANS "NEVER FUSES" — the correct default for every carrier that
|
|
275
|
+
* does not execute a destination call on arrival (every built-in
|
|
276
|
+
* aggregator here that merely delivers to the reader's own wallet, and any
|
|
277
|
+
* free local read with no executor of its own). A carrier that DOES
|
|
278
|
+
* execute destination calls, and that recognizes a following hop as its
|
|
279
|
+
* own kind of "entry call," supplies this to decide the two cases: gibs's
|
|
280
|
+
* own LI.FI/Relay-wrapping registration (`packages/ui/src/lib/state/useRouteSearch.ts`)
|
|
281
|
+
* is where `'pulsechain'`/`'tokensex'` ever appear in this check — never in
|
|
282
|
+
* this package.
|
|
283
|
+
*
|
|
284
|
+
* @param nextHop - the carrier and destination chain of the hop immediately following this one
|
|
285
|
+
* @returns whether this hop's signature also covers the next one
|
|
286
|
+
*/
|
|
287
|
+
readonly fusesWithNext?: (nextHop: CarrierFusesWithNextOptions) => boolean;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* A built-in carrier this library ships: LI.FI, Relay, or NEAR Intents.
|
|
291
|
+
*
|
|
292
|
+
* THE SAME THING AS A {@link Carrier}, NOT A SECOND VOCABULARY. Before this
|
|
293
|
+
* type existed, `bridge-sdk`'s `providers.ts` used `ProviderQuoteContext` to
|
|
294
|
+
* describe what a provider needs beyond the route request, and the
|
|
295
|
+
* route-search engine used `AskEdge`/`HopCarrier` to describe the same
|
|
296
|
+
* question in different words — two names for "something that answers what
|
|
297
|
+
* an edge delivers." `ProviderAsker` is a plain alias for `Carrier` whose
|
|
298
|
+
* `id` happens to be a {@link ProviderId}, so a `Carrier & { id: ProviderId }`
|
|
299
|
+
* and a `ProviderAsker` are interchangeable: a later step's `UpstreamFunnel`
|
|
300
|
+
* wraps either one identically, and a caller never has to know which kind it
|
|
301
|
+
* holds.
|
|
302
|
+
*/
|
|
303
|
+
export type ProviderAsker = Carrier & {
|
|
304
|
+
readonly id: ProviderId;
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Wraps a {@link Carrier}'s own `ask` so every network call it makes passes
|
|
308
|
+
* through this library's shared guards before it ever reaches `fetch`: the
|
|
309
|
+
* keyed concurrency limiter and Retry-After cooldown (`quote-guards.ts`), and
|
|
310
|
+
* the host's `neverSendChainIds` tripwire (`QuoteClientConfig`). One funnel
|
|
311
|
+
* wraps every carrier a client holds, so the guards govern the WHOLE client
|
|
312
|
+
* rather than being left to whichever carrier happens to remember to call
|
|
313
|
+
* them — the same reasoning `routing/search/waves.ts`'s own doc gives for
|
|
314
|
+
* keeping its concurrency limiter internal rather than trusting each caller
|
|
315
|
+
* to apply one.
|
|
316
|
+
*
|
|
317
|
+
* A FREE CARRIER (`isRateLimited: false`) IS RETURNED UNCHANGED, except for
|
|
318
|
+
* the chain-guard check, which applies to every carrier — a local read that
|
|
319
|
+
* happened to be pointed at a guarded chain is exactly as dangerous as a
|
|
320
|
+
* network call to one.
|
|
321
|
+
*/
|
|
322
|
+
export type UpstreamFunnel = {
|
|
323
|
+
/**
|
|
324
|
+
* Wraps one carrier, returning a carrier whose `ask` goes through every
|
|
325
|
+
* shared guard. The returned carrier's `id`, `edgesFrom` and
|
|
326
|
+
* `isRateLimited` are unchanged; only `ask` is wrapped.
|
|
327
|
+
*
|
|
328
|
+
* @param carrier - the carrier to wrap
|
|
329
|
+
* @returns a carrier with the same identity, guarded `ask`
|
|
330
|
+
*/
|
|
331
|
+
readonly wrap: (carrier: Carrier) => Carrier;
|
|
332
|
+
};
|
|
333
|
+
/** One cached display-quote answer, with the bookkeeping needed to compute `ageMs` and `cache` on a later read. */
|
|
334
|
+
export type AnswerCacheEntry = {
|
|
335
|
+
readonly answer: DisplayProviderSlot;
|
|
336
|
+
/** Epoch milliseconds this entry was recorded — the basis for `ageMs` and time-to-live expiry. */
|
|
337
|
+
readonly recordedAtMs: number;
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
* The display-quote endpoint's shared cache: keyed by
|
|
341
|
+
* `quote-guards.ts`'s `displayQuoteCacheKey`, single-flight over identical
|
|
342
|
+
* concurrent requests, least-recently-used eviction around 10,000 entries —
|
|
343
|
+
* see `docs/quote-service.md`'s "Cache" section for the exact policy a
|
|
344
|
+
* concrete implementation must follow.
|
|
345
|
+
*/
|
|
346
|
+
export type AnswerCache = {
|
|
347
|
+
/**
|
|
348
|
+
* Reads a cached entry, or null when there is none or it has aged out
|
|
349
|
+
* past the caller-supplied time-to-live for its {@link DisplayProviderSlot}
|
|
350
|
+
* status (30 s for `quoted`/`no-quote`, 5 s for `could-not-ask`, until the
|
|
351
|
+
* next chain-list refresh for `cannot-carry`).
|
|
352
|
+
*
|
|
353
|
+
* @param key - a {@link displayQuoteCacheKey} result
|
|
354
|
+
* @returns the entry, or null
|
|
355
|
+
*/
|
|
356
|
+
readonly get: (key: string) => AnswerCacheEntry | null;
|
|
357
|
+
/**
|
|
358
|
+
* Records an answer under `key`, evicting the least-recently-used entry
|
|
359
|
+
* first when the cache is already at capacity.
|
|
360
|
+
*
|
|
361
|
+
* @param key - a {@link displayQuoteCacheKey} result
|
|
362
|
+
* @param answer - the answer to cache
|
|
363
|
+
* @param recordedAtMs - when it was recorded
|
|
364
|
+
*/
|
|
365
|
+
readonly set: (key: string, answer: DisplayProviderSlot, recordedAtMs: number) => void;
|
|
366
|
+
/**
|
|
367
|
+
* Runs `task` at most once per `key` among concurrent callers: the first
|
|
368
|
+
* caller under a key runs `task`; every concurrent caller under the SAME
|
|
369
|
+
* key joins that one call rather than starting a second. Implements
|
|
370
|
+
* `docs/quote-service.md`'s "Cache" section: "identical concurrent
|
|
371
|
+
* requests wait on one upstream call," and "a client disconnect never
|
|
372
|
+
* cancels the shared call."
|
|
373
|
+
*
|
|
374
|
+
* @param key - a {@link displayQuoteCacheKey} result
|
|
375
|
+
* @param task - the upstream call to make, if nobody else is already making it
|
|
376
|
+
* @returns the answer, and whether this caller joined another's in-flight
|
|
377
|
+
* call rather than starting its own
|
|
378
|
+
*/
|
|
379
|
+
readonly joinOrRun: (key: string, task: () => Promise<DisplayProviderSlot>) => Promise<{
|
|
380
|
+
readonly answer: DisplayProviderSlot;
|
|
381
|
+
readonly joined: boolean;
|
|
382
|
+
}>;
|
|
383
|
+
};
|
|
384
|
+
/** What a LI.FI carrier needs beyond the route request — every field a host's own, never a default this library supplies. */
|
|
385
|
+
export type LifiProviderConfig = {
|
|
386
|
+
/** Forwarded as the `x-lifi-api-key` header, when set. */
|
|
387
|
+
readonly apiKey?: string;
|
|
388
|
+
/** Forwarded as the `integrator` query parameter LI.FI attributes traffic and referral fees to. */
|
|
389
|
+
readonly integrator?: string;
|
|
390
|
+
/** Overrides LI.FI's own API root, for a host proxying it under its own domain. */
|
|
391
|
+
readonly baseUrl?: string;
|
|
392
|
+
/** Referral fee as a decimal fraction (e.g. `0.001` = 0.1%), forwarded as `fee` when positive. */
|
|
393
|
+
readonly fee?: number;
|
|
394
|
+
};
|
|
395
|
+
/** What a Relay carrier needs beyond the route request. */
|
|
396
|
+
export type RelayProviderConfig = {
|
|
397
|
+
/** Forwarded as the `x-api-key` header, when set. */
|
|
398
|
+
readonly apiKey?: string;
|
|
399
|
+
/** Forwarded as `referrer` on every request. */
|
|
400
|
+
readonly referrer?: string;
|
|
401
|
+
/** Overrides Relay's own API root. */
|
|
402
|
+
readonly baseUrl?: string;
|
|
403
|
+
};
|
|
404
|
+
/** What a NEAR Intents carrier needs beyond the route request. */
|
|
405
|
+
export type NearIntentsProviderConfig = {
|
|
406
|
+
/** Forwarded as `X-API-Key`, when set — NEAR Intents' own partner key. */
|
|
407
|
+
readonly apiKey?: string;
|
|
408
|
+
/** Forwarded as `referral` on every quote request. */
|
|
409
|
+
readonly referral?: string;
|
|
410
|
+
/** Overrides NEAR Intents' own API root. */
|
|
411
|
+
readonly baseUrl?: string;
|
|
412
|
+
};
|
|
413
|
+
/** Every built-in carrier's own configuration, all optional — a host wires only the providers it uses. */
|
|
414
|
+
export type QuoteClientProvidersConfig = {
|
|
415
|
+
readonly lifi?: LifiProviderConfig;
|
|
416
|
+
readonly relay?: RelayProviderConfig;
|
|
417
|
+
readonly nearIntents?: NearIntentsProviderConfig;
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* How a {@link QuoteClientConfig} behaves when its own `serviceUrl` cannot be
|
|
421
|
+
* reached: `'direct'` runs the same built-in carriers against the providers
|
|
422
|
+
* directly (the browser fallback `docs/quote-service.md`'s "Decisions"
|
|
423
|
+
* section describes); `'none'` surfaces the failure instead of ever calling
|
|
424
|
+
* a provider straight from this runtime — the right choice for a server that
|
|
425
|
+
* IS the service, which has no "direct" to fall back to beyond itself.
|
|
426
|
+
*/
|
|
427
|
+
export type QuoteClientFallbackMode = 'direct' | 'none';
|
|
428
|
+
/** A host's own limits, layered over this library's neutral defaults. */
|
|
429
|
+
export type QuoteClientLimitsConfig = {
|
|
430
|
+
/** Per-carrier in-flight request cap, keyed by {@link CarrierId}. Falls back to the carrier's own `concurrencyLimit` when a carrier is not named here. */
|
|
431
|
+
readonly concurrencyByCarrier?: Readonly<Record<CarrierId, number>>;
|
|
432
|
+
/** Overrides `quote-guards.ts`'s `DEFAULT_RETRY_AFTER_COOLDOWN_MS` (30 s). */
|
|
433
|
+
readonly defaultRetryAfterMs?: number;
|
|
434
|
+
};
|
|
435
|
+
/** A host's own cache sizing, layered over this library's neutral defaults. */
|
|
436
|
+
export type QuoteClientCacheConfig = {
|
|
437
|
+
/** Overrides the answer time-to-live, in milliseconds, for a `quoted`/`no-quote` entry. */
|
|
438
|
+
readonly ttlMs?: number;
|
|
439
|
+
/** Overrides the least-recently-used eviction size (about 10,000 entries by default). */
|
|
440
|
+
readonly maxEntries?: number;
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* The one configuration object a host builds this library's client from.
|
|
444
|
+
* Nothing about a host — its name, its allowed origins, its chain policy,
|
|
445
|
+
* its keys — lives anywhere else in this package; it all arrives here.
|
|
446
|
+
*
|
|
447
|
+
* THREE MODES, ONE SHAPE. See `docs/quote-service.md`'s "Library" section
|
|
448
|
+
* for the full description of each:
|
|
449
|
+
*
|
|
450
|
+
* - **Backend** — `providers` carries real secrets (`apiKey`s); this config
|
|
451
|
+
* builds the same client the display-quote service's server process runs.
|
|
452
|
+
* - **Frontend, direct** — `providers` carries no secret (or only a public,
|
|
453
|
+
* rate-limit-budget identifier, exactly as today's browser bundle already
|
|
454
|
+
* does for Relay); `fallback: 'none'` or `serviceUrl` absent, so the
|
|
455
|
+
* client calls providers straight from the browser.
|
|
456
|
+
* - **Frontend, via a service with a fallback** — `serviceUrl` names a host's
|
|
457
|
+
* own display-quote service; `fallback: 'direct'` runs the same built-in
|
|
458
|
+
* carriers straight from the browser when that service cannot be reached,
|
|
459
|
+
* under `providers`' own (public) values.
|
|
460
|
+
*
|
|
461
|
+
* @see LifiProviderConfig
|
|
462
|
+
* @see RelayProviderConfig
|
|
463
|
+
* @see NearIntentsProviderConfig
|
|
464
|
+
*/
|
|
465
|
+
export type QuoteClientConfig = {
|
|
466
|
+
/** Every built-in carrier's own configuration. */
|
|
467
|
+
readonly providers?: QuoteClientProvidersConfig;
|
|
468
|
+
/** A host's own display-quote service, when this client should prefer it over asking providers directly. */
|
|
469
|
+
readonly serviceUrl?: string;
|
|
470
|
+
/** What to do when `serviceUrl` is set but unreachable. Required, so a caller always states its intent rather than inheriting a silent default. */
|
|
471
|
+
readonly fallback: QuoteClientFallbackMode;
|
|
472
|
+
/**
|
|
473
|
+
* A server-held address this host controls, per ecosystem, that a
|
|
474
|
+
* pre-quote can name as its own recipient without exposing a real user
|
|
475
|
+
* address — see `docs/quote-service.md`'s "Endpoint" section, "a
|
|
476
|
+
* server-held probe address." A host supplies one per ecosystem it wants
|
|
477
|
+
* to quote non-Ethereum origins for; an ecosystem with none configured is
|
|
478
|
+
* simply not quoted for that origin.
|
|
479
|
+
*/
|
|
480
|
+
readonly probeAddresses?: Readonly<Partial<Record<Ecosystem, string>>>;
|
|
481
|
+
/**
|
|
482
|
+
* Chain ids no request this client builds may ever name, in an address or
|
|
483
|
+
* a request body, including a forwarding path — the tripwire
|
|
484
|
+
* `docs/quote-service.md`'s "Rules that never bend" section requires. A
|
|
485
|
+
* host with no such restriction passes an empty array or omits the field;
|
|
486
|
+
* this library enforces nothing on its own, because it does not know any
|
|
487
|
+
* host's chain ids to forbid.
|
|
488
|
+
*/
|
|
489
|
+
readonly neverSendChainIds?: readonly number[];
|
|
490
|
+
readonly cache?: QuoteClientCacheConfig;
|
|
491
|
+
readonly limits?: QuoteClientLimitsConfig;
|
|
492
|
+
/**
|
|
493
|
+
* The `fetch` implementation every carrier calls through. Defaults to the
|
|
494
|
+
* runtime global when omitted; a host — or a test — that needs a
|
|
495
|
+
* different one (logging, a fixture replay, a Node runtime with no global
|
|
496
|
+
* fetch) supplies its own. Never imported from `node:*`, so this stays
|
|
497
|
+
* isomorphic.
|
|
498
|
+
*/
|
|
499
|
+
readonly fetch?: typeof fetch;
|
|
500
|
+
/** The clock every time-sensitive guard reads. Defaults to `Date.now` when omitted; a test supplies its own. */
|
|
501
|
+
readonly now?: () => number;
|
|
502
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { CarrierId, UpstreamFunnel } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The single choke point every outbound network call a {@link Carrier} makes
|
|
4
|
+
* passes through — see `docs/quote-service.md`'s "Carriers" section for the
|
|
5
|
+
* narrative and `types.ts`'s {@link UpstreamFunnel} for the contract this
|
|
6
|
+
* module implements.
|
|
7
|
+
*
|
|
8
|
+
* TWO LAYERS, BECAUSE `Carrier.ask` HIDES THE WIRE. `UpstreamFunnel.wrap`
|
|
9
|
+
* only sees a {@link CarrierAskRequest} (an edge and an amount) and a
|
|
10
|
+
* {@link CarrierAskAnswer} (delivered/refusal) — never a URL, a request body,
|
|
11
|
+
* or a response header, because that is exactly what makes `Carrier` usable
|
|
12
|
+
* for a free local read as well as a network call. The guards this module
|
|
13
|
+
* must enforce — the Retry-After cooldown, the 401-code-1010 breaker, and
|
|
14
|
+
* the chain tripwire's URL/body scan — all need that wire-level view. So
|
|
15
|
+
* this module builds it in two pieces:
|
|
16
|
+
*
|
|
17
|
+
* - {@link createUpstreamFunnel}'s `wrap` applies the STRUCTURAL guards every
|
|
18
|
+
* `Carrier.ask` call can be judged on without touching the network: the
|
|
19
|
+
* chain-id tripwire read off the edge itself, the per-carrier concurrency
|
|
20
|
+
* cap, and refusing locally while a carrier's cooldown is active.
|
|
21
|
+
* - Its `guardedFetch` is the WIRE-level guard: every built-in carrier
|
|
22
|
+
* (`carriers/lifi.ts`, `carriers/relay.ts`, `carriers/near-intents.ts`)
|
|
23
|
+
* must call it — bound to its own {@link CarrierId} — instead of calling
|
|
24
|
+
* `fetch` directly. It re-runs the chain tripwire against the actual
|
|
25
|
+
* outgoing URL and body (the backstop `docs/quote-service.md` calls for:
|
|
26
|
+
* "rejects any 369/943 in the address or body"), records a `429`'s
|
|
27
|
+
* `Retry-After` into the SAME cooldown `wrap` reads, and — for `'lifi'`
|
|
28
|
+
* only — arms the five-minute breaker on a `401` carrying LI.FI's
|
|
29
|
+
* `code: 1010`.
|
|
30
|
+
*
|
|
31
|
+
* A caller wires the two together: build the funnel first, construct each
|
|
32
|
+
* carrier with `(input, init) => funnel.guardedFetch(carrierId, input, init)`
|
|
33
|
+
* as its `fetchImpl`, THEN call `funnel.wrap(carrier)` on the result. Skipping
|
|
34
|
+
* either half leaves a real gap — `wrap` alone never sees a 429 to cool down
|
|
35
|
+
* from, and `guardedFetch` alone never caps concurrency.
|
|
36
|
+
*/
|
|
37
|
+
/** Every built-in carrier's own concurrency cap, unless `concurrencyByCarrier` overrides it. */
|
|
38
|
+
export declare const DEFAULT_CARRIER_CONCURRENCY = 4;
|
|
39
|
+
/** NEAR Intents' own default concurrency cap — see `docs/quote-service.md`'s "Guards" section: "4, NEAR 3." */
|
|
40
|
+
export declare const NEAR_INTENTS_CONCURRENCY = 3;
|
|
41
|
+
/** Per-provider defaults, read when `concurrencyByCarrier` names no override for a carrier. */
|
|
42
|
+
export declare const DEFAULT_CONCURRENCY_BY_CARRIER: Readonly<Record<string, number>>;
|
|
43
|
+
/** How long the LI.FI carrier is refused after a `401` carrying `code: 1010` — five minutes, per `docs/quote-service.md`'s "Cache" section. */
|
|
44
|
+
export declare const LIFI_INVALID_API_KEY_BREAKER_MS: number;
|
|
45
|
+
/**
|
|
46
|
+
* Thrown by {@link GuardedFetch} BEFORE any network call, when the outgoing
|
|
47
|
+
* request names a chain id the host listed in `neverSendChainIds` — either in
|
|
48
|
+
* a query parameter or inside the JSON body, under one of the structured
|
|
49
|
+
* chain-id field names this module reads (never a raw string search over the
|
|
50
|
+
* whole request, which would also match an amount or an address that merely
|
|
51
|
+
* CONTAINS the digits).
|
|
52
|
+
*/
|
|
53
|
+
export declare class NeverSendChainIdError extends Error {
|
|
54
|
+
readonly chainId: number;
|
|
55
|
+
readonly carrierId: CarrierId;
|
|
56
|
+
constructor(chainId: number, carrierId: CarrierId);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Thrown by {@link GuardedFetch} when a configured provider key would
|
|
60
|
+
* otherwise appear in the outgoing URL or body — `docs/quote-service.md`'s
|
|
61
|
+
* rule, "keys go only into headers, never URLs or bodies, never logs,"
|
|
62
|
+
* enforced rather than merely documented.
|
|
63
|
+
*/
|
|
64
|
+
export declare class KeyLeakError extends Error {
|
|
65
|
+
readonly carrierId: CarrierId;
|
|
66
|
+
constructor(carrierId: CarrierId);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Thrown by {@link GuardedFetch} for `'lifi'` while its five-minute breaker
|
|
70
|
+
* is active — see {@link LIFI_INVALID_API_KEY_BREAKER_MS}. Never thrown for
|
|
71
|
+
* any other carrier; only LI.FI answers a rejected key with a 401 this
|
|
72
|
+
* module can classify (`isLifiInvalidApiKeyResponse`).
|
|
73
|
+
*/
|
|
74
|
+
export declare class LifiApiKeyBreakerActiveError extends Error {
|
|
75
|
+
constructor();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A `fetch` every built-in carrier's own network call must go through,
|
|
79
|
+
* keyed by which carrier is calling — see this module's head note for why a
|
|
80
|
+
* carrier id, rather than a plain `typeof fetch`, is required here.
|
|
81
|
+
*/
|
|
82
|
+
export type GuardedFetch = (carrierId: CarrierId, input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
83
|
+
export type CreateUpstreamFunnelOptions = {
|
|
84
|
+
/** Chain ids no request this funnel guards may ever name — see `QuoteClientConfig.neverSendChainIds`. */
|
|
85
|
+
readonly neverSendChainIds?: readonly number[];
|
|
86
|
+
/** Every configured provider key, checked against the outgoing URL and body so a key never travels anywhere but a header. */
|
|
87
|
+
readonly keys?: readonly string[];
|
|
88
|
+
/** Per-carrier in-flight cap, overriding {@link DEFAULT_CONCURRENCY_BY_CARRIER}. */
|
|
89
|
+
readonly concurrencyByCarrier?: Readonly<Record<CarrierId, number>>;
|
|
90
|
+
/** Overrides `quote-guards.ts`'s `DEFAULT_RETRY_AFTER_COOLDOWN_MS` for a carrier's own Retry-After cooldown. */
|
|
91
|
+
readonly defaultRetryAfterMs?: number;
|
|
92
|
+
/**
|
|
93
|
+
* How long, from the moment an `ask` call is queued, it may wait for a
|
|
94
|
+
* concurrency slot before {@link createUpstreamFunnel}'s `wrap` answers
|
|
95
|
+
* `could-not-ask: busy` instead. Omitted waits indefinitely, matching
|
|
96
|
+
* `KeyedConcurrencyLimiter.run`'s own default.
|
|
97
|
+
*/
|
|
98
|
+
readonly askDeadlineMs?: number;
|
|
99
|
+
readonly fetch?: typeof fetch;
|
|
100
|
+
readonly now?: () => number;
|
|
101
|
+
};
|
|
102
|
+
/** {@link UpstreamFunnel}, widened with the wire-level {@link GuardedFetch} every built-in carrier is constructed with. */
|
|
103
|
+
export type QuoteUpstreamFunnel = UpstreamFunnel & {
|
|
104
|
+
readonly guardedFetch: GuardedFetch;
|
|
105
|
+
/**
|
|
106
|
+
* Total `429` responses `guardedFetch` has seen across every carrier since
|
|
107
|
+
* this funnel was built — the figure `/stats`'s `rateLimited429s` reports,
|
|
108
|
+
* per `docs/quote-service.md`'s "Endpoint" section. Counts the response,
|
|
109
|
+
* not the cooldown it starts, so a burst of calls that all land during one
|
|
110
|
+
* already-active cooldown (refused locally by `wrap`, never reaching
|
|
111
|
+
* `guardedFetch`) does not inflate it.
|
|
112
|
+
*/
|
|
113
|
+
readonly rateLimited429Count: () => number;
|
|
114
|
+
/** Every carrier's own queued-task count, summed — see `quote-guards.ts`'s `KeyedConcurrencyLimiter.queueDepth`. */
|
|
115
|
+
readonly queueDepth: () => number;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Builds the funnel every carrier's `ask` — built-in or host-registered —
|
|
119
|
+
* should be wrapped through, and the wire-level {@link GuardedFetch} every
|
|
120
|
+
* built-in carrier's own HTTP call should go through. See this module's
|
|
121
|
+
* head note for how the two fit together.
|
|
122
|
+
*
|
|
123
|
+
* @param options - see {@link CreateUpstreamFunnelOptions}
|
|
124
|
+
* @returns the funnel
|
|
125
|
+
*/
|
|
126
|
+
export declare const createUpstreamFunnel: (options?: CreateUpstreamFunnelOptions) => QuoteUpstreamFunnel;
|