@circle-fin/app-kit 1.13.0 → 1.15.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 +122 -0
- package/README.md +3 -3
- package/bridge.cjs +8388 -515
- package/bridge.d.cts +640 -61
- package/bridge.d.mts +640 -61
- package/bridge.d.ts +640 -61
- package/bridge.mjs +8389 -516
- package/chains.cjs +275 -15
- package/chains.d.cts +117 -2
- package/chains.d.mts +117 -2
- package/chains.d.ts +117 -2
- package/chains.mjs +275 -16
- package/context.d.cts +577 -57
- package/context.d.mts +577 -57
- package/context.d.ts +577 -57
- package/earn.cjs +973 -271
- package/earn.d.cts +577 -57
- package/earn.d.mts +577 -57
- package/earn.d.ts +577 -57
- package/earn.mjs +919 -221
- package/estimateBridge.cjs +8388 -518
- package/estimateBridge.d.cts +704 -82
- package/estimateBridge.d.mts +704 -82
- package/estimateBridge.d.ts +704 -82
- package/estimateBridge.mjs +8390 -520
- package/estimateSwap.cjs +982 -237
- package/estimateSwap.d.cts +577 -57
- package/estimateSwap.d.mts +577 -57
- package/estimateSwap.d.ts +577 -57
- package/estimateSwap.mjs +981 -237
- package/index.cjs +21401 -8522
- package/index.d.cts +6045 -2149
- package/index.d.mts +6045 -2149
- package/index.d.ts +6045 -2149
- package/index.mjs +21401 -8524
- package/package.json +17 -6
- package/server.cjs +10040 -0
- package/server.cjs.map +1 -0
- package/server.d.cts +2467 -0
- package/server.d.mts +2467 -0
- package/server.d.ts +2467 -0
- package/server.mjs +10028 -0
- package/server.mjs.map +1 -0
- package/swap.cjs +982 -237
- package/swap.d.cts +577 -57
- package/swap.d.mts +577 -57
- package/swap.d.ts +577 -57
- package/swap.mjs +981 -237
- package/unifiedBalance.cjs +20510 -7599
- package/unifiedBalance.d.cts +807 -201
- package/unifiedBalance.d.mts +807 -201
- package/unifiedBalance.d.ts +807 -201
- package/unifiedBalance.mjs +20513 -7603
package/server.d.ts
ADDED
|
@@ -0,0 +1,2467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Circle Internet Group, Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { z } from '/home/runner/_work/stablecoin-kits-private/stablecoin-kits-private/node_modules/zod/dist/types/index.d.ts';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Module augmentation to register known token symbols.
|
|
23
|
+
*
|
|
24
|
+
* @remarks
|
|
25
|
+
* This file augments the `TokenSymbolRegistry` interface to provide
|
|
26
|
+
* type-safe autocomplete for built-in tokens.
|
|
27
|
+
*
|
|
28
|
+
* When imported, TypeScript will recognize 'USDC' as a valid
|
|
29
|
+
* `TokenSymbol` value with autocomplete support.
|
|
30
|
+
*
|
|
31
|
+
* Other packages or applications can create their own augmentations
|
|
32
|
+
* to add additional tokens.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import '@core/tokens' // Automatically includes this augmentation
|
|
37
|
+
*
|
|
38
|
+
* const symbol: TokenSymbol = 'USDC' // ✓ Autocomplete shows USDC
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare module './types' {
|
|
42
|
+
/**
|
|
43
|
+
* Module augmentation: Adds known token symbols as valid keys
|
|
44
|
+
* to the TokenSymbolRegistry interface.
|
|
45
|
+
*
|
|
46
|
+
* Keys are explicitly listed to ensure IDE autocomplete works properly.
|
|
47
|
+
*/
|
|
48
|
+
interface TokenSymbolRegistry {
|
|
49
|
+
USDC: true;
|
|
50
|
+
USDT: true;
|
|
51
|
+
EURC: true;
|
|
52
|
+
DAI: true;
|
|
53
|
+
USDE: true;
|
|
54
|
+
PYUSD: true;
|
|
55
|
+
WETH: true;
|
|
56
|
+
WBTC: true;
|
|
57
|
+
WSOL: true;
|
|
58
|
+
WAVAX: true;
|
|
59
|
+
WPOL: true;
|
|
60
|
+
ETH: true;
|
|
61
|
+
POL: true;
|
|
62
|
+
PLUME: true;
|
|
63
|
+
MON: true;
|
|
64
|
+
cirBTC: true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Enumeration of all blockchains known to this library.
|
|
70
|
+
*
|
|
71
|
+
* This enum contains every blockchain that has a chain definition, regardless
|
|
72
|
+
* of whether bridging is currently supported. For chains that support bridging
|
|
73
|
+
* via CCTPv2, see {@link BridgeChain}.
|
|
74
|
+
*
|
|
75
|
+
* @enum
|
|
76
|
+
* @category Enums
|
|
77
|
+
* @description Provides string identifiers for each blockchain with a definition.
|
|
78
|
+
* @see {@link BridgeChain} for the subset of chains that support CCTPv2 bridging.
|
|
79
|
+
*/
|
|
80
|
+
declare enum Blockchain {
|
|
81
|
+
Algorand = "Algorand",
|
|
82
|
+
Algorand_Testnet = "Algorand_Testnet",
|
|
83
|
+
Aptos = "Aptos",
|
|
84
|
+
Aptos_Testnet = "Aptos_Testnet",
|
|
85
|
+
Arbitrum = "Arbitrum",
|
|
86
|
+
Arbitrum_Sepolia = "Arbitrum_Sepolia",
|
|
87
|
+
Arc = "Arc",
|
|
88
|
+
Arc_Testnet = "Arc_Testnet",
|
|
89
|
+
Avalanche = "Avalanche",
|
|
90
|
+
Avalanche_Fuji = "Avalanche_Fuji",
|
|
91
|
+
Base = "Base",
|
|
92
|
+
Base_Sepolia = "Base_Sepolia",
|
|
93
|
+
Celo = "Celo",
|
|
94
|
+
Celo_Alfajores_Testnet = "Celo_Alfajores_Testnet",
|
|
95
|
+
Codex = "Codex",
|
|
96
|
+
Codex_Testnet = "Codex_Testnet",
|
|
97
|
+
Cronos = "Cronos",
|
|
98
|
+
Cronos_Testnet = "Cronos_Testnet",
|
|
99
|
+
Edge = "Edge",
|
|
100
|
+
Edge_Testnet = "Edge_Testnet",
|
|
101
|
+
Ethereum = "Ethereum",
|
|
102
|
+
Ethereum_Sepolia = "Ethereum_Sepolia",
|
|
103
|
+
Hedera = "Hedera",
|
|
104
|
+
Hedera_Testnet = "Hedera_Testnet",
|
|
105
|
+
HyperEVM = "HyperEVM",
|
|
106
|
+
HyperEVM_Testnet = "HyperEVM_Testnet",
|
|
107
|
+
Injective = "Injective",
|
|
108
|
+
Injective_Testnet = "Injective_Testnet",
|
|
109
|
+
Ink = "Ink",
|
|
110
|
+
Ink_Testnet = "Ink_Testnet",
|
|
111
|
+
Linea = "Linea",
|
|
112
|
+
Linea_Sepolia = "Linea_Sepolia",
|
|
113
|
+
Monad = "Monad",
|
|
114
|
+
Monad_Testnet = "Monad_Testnet",
|
|
115
|
+
Morph = "Morph",
|
|
116
|
+
Morph_Testnet = "Morph_Testnet",
|
|
117
|
+
NEAR = "NEAR",
|
|
118
|
+
NEAR_Testnet = "NEAR_Testnet",
|
|
119
|
+
Noble = "Noble",
|
|
120
|
+
Noble_Testnet = "Noble_Testnet",
|
|
121
|
+
Optimism = "Optimism",
|
|
122
|
+
Optimism_Sepolia = "Optimism_Sepolia",
|
|
123
|
+
Pharos = "Pharos",
|
|
124
|
+
Pharos_Testnet = "Pharos_Testnet",
|
|
125
|
+
Plasma = "Plasma",
|
|
126
|
+
Plasma_Testnet = "Plasma_Testnet",
|
|
127
|
+
Polkadot_Asset_Hub = "Polkadot_Asset_Hub",
|
|
128
|
+
Polkadot_Westmint = "Polkadot_Westmint",
|
|
129
|
+
Plume = "Plume",
|
|
130
|
+
Plume_Testnet = "Plume_Testnet",
|
|
131
|
+
Polygon = "Polygon",
|
|
132
|
+
Polygon_Amoy_Testnet = "Polygon_Amoy_Testnet",
|
|
133
|
+
Sei = "Sei",
|
|
134
|
+
Sei_Testnet = "Sei_Testnet",
|
|
135
|
+
Solana = "Solana",
|
|
136
|
+
Solana_Devnet = "Solana_Devnet",
|
|
137
|
+
Sonic = "Sonic",
|
|
138
|
+
Sonic_Testnet = "Sonic_Testnet",
|
|
139
|
+
Stellar = "Stellar",
|
|
140
|
+
Stellar_Testnet = "Stellar_Testnet",
|
|
141
|
+
Sui = "Sui",
|
|
142
|
+
Sui_Testnet = "Sui_Testnet",
|
|
143
|
+
Unichain = "Unichain",
|
|
144
|
+
Unichain_Sepolia = "Unichain_Sepolia",
|
|
145
|
+
World_Chain = "World_Chain",
|
|
146
|
+
World_Chain_Sepolia = "World_Chain_Sepolia",
|
|
147
|
+
XDC = "XDC",
|
|
148
|
+
XDC_Apothem = "XDC_Apothem",
|
|
149
|
+
X_Layer = "X_Layer",
|
|
150
|
+
X_Layer_Testnet = "X_Layer_Testnet",
|
|
151
|
+
ZKSync_Era = "ZKSync_Era",
|
|
152
|
+
ZKSync_Sepolia = "ZKSync_Sepolia"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Module augmentation to register Blockchain enum values as ChainIdentifiers.
|
|
157
|
+
*
|
|
158
|
+
* @remarks
|
|
159
|
+
* This file augments the `ChainRegistry` interface to provide type-safe
|
|
160
|
+
* autocomplete for all `Blockchain` enum values from `@core/chains`.
|
|
161
|
+
*
|
|
162
|
+
* When this augmentation is imported (via `@core/tokens`), TypeScript will
|
|
163
|
+
* recognize all blockchain identifiers as valid `ChainIdentifier` values
|
|
164
|
+
* with IDE autocomplete support.
|
|
165
|
+
*
|
|
166
|
+
* The `Blockchain` enum values are converted to their string representations,
|
|
167
|
+
* enabling both enum values and string literals to be accepted as chain identifiers.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* import { Blockchain } from '@core/chains'
|
|
172
|
+
* import type { ChainIdentifier } from '@core/tokens'
|
|
173
|
+
*
|
|
174
|
+
* // Using enum value
|
|
175
|
+
* const chain1: ChainIdentifier = Blockchain.Ethereum
|
|
176
|
+
*
|
|
177
|
+
* // Using string literal (with autocomplete!)
|
|
178
|
+
* const chain2: ChainIdentifier = 'Base'
|
|
179
|
+
*
|
|
180
|
+
* // Arbitrary strings also work (escape hatch for custom chains)
|
|
181
|
+
* const chain3: ChainIdentifier = 'my-custom-chain'
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
declare module './types' {
|
|
186
|
+
/**
|
|
187
|
+
* Module augmentation: Adds all Blockchain enum values as valid keys
|
|
188
|
+
* to the ChainRegistry interface for type-safe chain identifier support.
|
|
189
|
+
*
|
|
190
|
+
* This ensures both enum property access (e.g., Blockchain.Ethereum) and plain
|
|
191
|
+
* string literals (e.g., 'Ethereum') are accepted by TypeScript as chain keys,
|
|
192
|
+
* providing robust autocomplete and error checking.
|
|
193
|
+
*
|
|
194
|
+
* NOTE:
|
|
195
|
+
* - This interface intentionally has no body. It merges a mapped Record type
|
|
196
|
+
* into ChainRegistry solely for type augmentation.
|
|
197
|
+
* - This empty-body construct is a necessary TypeScript idiom for module
|
|
198
|
+
* augmentation with Record types—directly listing mapped keys is not
|
|
199
|
+
* feasible in interface extensions.
|
|
200
|
+
*
|
|
201
|
+
* eslint-disable-next-line directives below suppress linter complaints about
|
|
202
|
+
* the empty interface/mapping, which are benign and required for this pattern.
|
|
203
|
+
*/
|
|
204
|
+
interface ChainRegistry extends Record<`${Blockchain}`, true> {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Creates a union type that preserves IDE autocomplete for known literals
|
|
210
|
+
* while still accepting any string at runtime.
|
|
211
|
+
*
|
|
212
|
+
* @remarks
|
|
213
|
+
* This pattern uses `Record<never, never>` (an empty record type) to prevent
|
|
214
|
+
* TypeScript from widening string literals to just `string`. This gives us
|
|
215
|
+
* the best of both worlds: autocomplete for known values and flexibility
|
|
216
|
+
* for arbitrary strings.
|
|
217
|
+
*
|
|
218
|
+
* @typeParam T - The known string literal union to preserve.
|
|
219
|
+
*/
|
|
220
|
+
type LiteralUnion<T extends string> = T | (string & Record<never, never>);
|
|
221
|
+
/**
|
|
222
|
+
* Registry for known chain identifiers (augmentation target).
|
|
223
|
+
*
|
|
224
|
+
* @remarks
|
|
225
|
+
* This empty interface exists solely for module augmentation. Extend it to
|
|
226
|
+
* register chain identifiers for type-safe token definitions.
|
|
227
|
+
*
|
|
228
|
+
* **Why an interface?** TypeScript only allows module augmentation on
|
|
229
|
+
* interfaces, not type aliases.
|
|
230
|
+
*
|
|
231
|
+
* **Note:** This is NOT the EVM "chain ID" (numeric like 1 for Ethereum).
|
|
232
|
+
* It's a human-readable identifier like "Ethereum", "Solana", "Base".
|
|
233
|
+
*
|
|
234
|
+
* **Usage**
|
|
235
|
+
*
|
|
236
|
+
* Without augmentation, `ChainIdentifier` defaults to `string`.
|
|
237
|
+
* With `@core/chains` imported, you get autocomplete for all `Blockchain` values.
|
|
238
|
+
*
|
|
239
|
+
* **Custom Chains**
|
|
240
|
+
*
|
|
241
|
+
* ```typescript
|
|
242
|
+
* declare module '@core/tokens' {
|
|
243
|
+
* interface ChainRegistry {
|
|
244
|
+
* MyChain: true
|
|
245
|
+
* MyTestnet: true
|
|
246
|
+
* }
|
|
247
|
+
* }
|
|
248
|
+
* // Now ChainIdentifier includes 'MyChain' | 'MyTestnet' | ...
|
|
249
|
+
* ```
|
|
250
|
+
*
|
|
251
|
+
* The value (`true`) is a placeholder—only the keys matter.
|
|
252
|
+
*
|
|
253
|
+
* NOTE: The eslint-disable below suppresses warnings about empty interfaces.
|
|
254
|
+
* This is intentional—the interface exists solely as an augmentation target.
|
|
255
|
+
*/
|
|
256
|
+
interface ChainRegistry {
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Union of all registered chain identifiers.
|
|
260
|
+
*
|
|
261
|
+
* @remarks
|
|
262
|
+
* Derived from `ChainRegistry` keys:
|
|
263
|
+
* - Without augmentation: `string`
|
|
264
|
+
* - With `@core/chains`: `'Ethereum' | 'Solana' | ...` plus any string
|
|
265
|
+
*
|
|
266
|
+
* Uses `LiteralUnion` to preserve IDE autocomplete while allowing
|
|
267
|
+
* arbitrary strings at runtime.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const chain: ChainIdentifier = 'Ethereum' // Autocomplete works
|
|
272
|
+
* const custom: ChainIdentifier = 'my-chain' // Also valid
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
type ChainIdentifier = keyof ChainRegistry extends never ? string : LiteralUnion<Extract<keyof ChainRegistry, string>>;
|
|
276
|
+
/**
|
|
277
|
+
* Maps chain identifiers to their token locators.
|
|
278
|
+
*
|
|
279
|
+
* @remarks
|
|
280
|
+
* The key is a chain identifier (type-safe when `KnownChainIdentifiers` is
|
|
281
|
+
* augmented). This enables a single token definition to work across chains.
|
|
282
|
+
*
|
|
283
|
+
* When `@core/chains` is imported, you get autocomplete for known chains
|
|
284
|
+
* like `Ethereum`, `Base`, `Solana`, etc.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* import { Blockchain } from '@core/chains'
|
|
289
|
+
*
|
|
290
|
+
* const usdcLocators: ChainLocatorMap = {
|
|
291
|
+
* [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
292
|
+
* [Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
293
|
+
* [Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
294
|
+
* }
|
|
295
|
+
*
|
|
296
|
+
* // Or with string keys (always works)
|
|
297
|
+
* const locators: ChainLocatorMap = {
|
|
298
|
+
* 'ethereum': '0xa0b86991...',
|
|
299
|
+
* 'my-custom-chain': '0x1234...',
|
|
300
|
+
* }
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
type ChainLocatorMap = Record<ChainIdentifier, string>;
|
|
304
|
+
/**
|
|
305
|
+
* Complete definition of a token including metadata and chain locators.
|
|
306
|
+
*
|
|
307
|
+
* @remarks
|
|
308
|
+
* This is the canonical representation of a token in the registry.
|
|
309
|
+
* It includes the symbol, decimals, and chain-specific locators.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* const usdc: TokenDefinition = {
|
|
314
|
+
* symbol: 'USDC',
|
|
315
|
+
* decimals: 6,
|
|
316
|
+
* locators: {
|
|
317
|
+
* [Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
318
|
+
* [Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
319
|
+
* },
|
|
320
|
+
* }
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
interface TokenDefinition {
|
|
324
|
+
/**
|
|
325
|
+
* The token symbol (e.g., "USDC", "EURC").
|
|
326
|
+
*/
|
|
327
|
+
readonly symbol: string;
|
|
328
|
+
/**
|
|
329
|
+
* The default number of decimal places for the token.
|
|
330
|
+
* Used when no chain-specific override exists in {@link chainDecimals}.
|
|
331
|
+
* @example 6 for USDC, 18 for most ERC20 tokens
|
|
332
|
+
*/
|
|
333
|
+
readonly decimals: number;
|
|
334
|
+
/**
|
|
335
|
+
* Chain-specific locators for the token.
|
|
336
|
+
* Keys are chain identifiers, values are the token address/locator on that chain.
|
|
337
|
+
* Not all chains need to be present - tokens may only exist on a subset of chains.
|
|
338
|
+
*/
|
|
339
|
+
readonly locators: Partial<ChainLocatorMap>;
|
|
340
|
+
/**
|
|
341
|
+
* Optional per-chain decimal overrides.
|
|
342
|
+
*
|
|
343
|
+
* Some tokens have different decimal places on different chains
|
|
344
|
+
* (e.g., USDe is 18 decimals on EVM but 9 decimals on Solana).
|
|
345
|
+
* When present, the value for a specific chain takes precedence
|
|
346
|
+
* over the default {@link decimals}.
|
|
347
|
+
*/
|
|
348
|
+
readonly chainDecimals?: Partial<Record<ChainIdentifier, number>>;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* A raw token locator selector with explicit decimals.
|
|
352
|
+
*
|
|
353
|
+
* @remarks
|
|
354
|
+
* Use this form when working with arbitrary tokens not in the registry.
|
|
355
|
+
* The `locator` is the chain-specific address, and `decimals` is required
|
|
356
|
+
* unless using lenient mode.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```typescript
|
|
360
|
+
* // Selecting a custom token by address
|
|
361
|
+
* const selector: RawTokenSelector = {
|
|
362
|
+
* locator: '0x1234567890abcdef1234567890abcdef12345678',
|
|
363
|
+
* decimals: 18,
|
|
364
|
+
* }
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
interface RawTokenSelector {
|
|
368
|
+
/**
|
|
369
|
+
* The chain-specific token locator (address, program ID, etc.).
|
|
370
|
+
*/
|
|
371
|
+
readonly locator: string;
|
|
372
|
+
/**
|
|
373
|
+
* The number of decimal places.
|
|
374
|
+
* Required in strict mode, optional in lenient mode.
|
|
375
|
+
*/
|
|
376
|
+
readonly decimals?: number;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Registry for known token symbols (augmentation target).
|
|
380
|
+
*
|
|
381
|
+
* @remarks
|
|
382
|
+
* This empty interface exists solely for module augmentation. Extend it to
|
|
383
|
+
* register token symbols for type-safe selection.
|
|
384
|
+
*
|
|
385
|
+
* **Why an interface?** TypeScript only allows module augmentation on
|
|
386
|
+
* interfaces, not type aliases.
|
|
387
|
+
*
|
|
388
|
+
* **Usage**
|
|
389
|
+
*
|
|
390
|
+
* Without augmentation, `TokenSymbol` defaults to `string`.
|
|
391
|
+
*
|
|
392
|
+
* ```typescript
|
|
393
|
+
* declare module '@core/tokens' {
|
|
394
|
+
* interface TokenSymbolRegistry {
|
|
395
|
+
* USDC: true
|
|
396
|
+
* EURC: true
|
|
397
|
+
* }
|
|
398
|
+
* }
|
|
399
|
+
* // Now TokenSymbol includes 'USDC' | 'EURC' | ...
|
|
400
|
+
* ```
|
|
401
|
+
*
|
|
402
|
+
* NOTE: The eslint-disable below suppresses warnings about empty interfaces.
|
|
403
|
+
* This is intentional—the interface exists solely as an augmentation target.
|
|
404
|
+
*/
|
|
405
|
+
interface TokenSymbolRegistry {
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Union type of all registered token symbols.
|
|
409
|
+
*
|
|
410
|
+
* @remarks
|
|
411
|
+
* This type is derived from the keys of `TokenSymbolRegistry`:
|
|
412
|
+
* - **Without augmentation** — Simply `string` (any value)
|
|
413
|
+
* - **With augmentation** — `'USDC' | 'USDT' | ...` plus any string
|
|
414
|
+
*
|
|
415
|
+
* Uses `LiteralUnion` to preserve autocomplete for known values while
|
|
416
|
+
* still accepting any string at runtime.
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```typescript
|
|
420
|
+
* // With symbols.augment imported - autocomplete works
|
|
421
|
+
* const symbol: TokenSymbol = 'USDC'
|
|
422
|
+
*
|
|
423
|
+
* // Custom strings still accepted
|
|
424
|
+
* const symbol: TokenSymbol = 'MY_TOKEN'
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
type TokenSymbol = keyof TokenSymbolRegistry extends never ? string : LiteralUnion<Extract<keyof TokenSymbolRegistry, string>>;
|
|
428
|
+
/**
|
|
429
|
+
* Token selector accepted by adapters and the static
|
|
430
|
+
* {@link TokenRegistry.resolve} method.
|
|
431
|
+
*
|
|
432
|
+
* @remarks
|
|
433
|
+
* Keep this alias at adapter and registry boundaries so their accepted token
|
|
434
|
+
* forms remain explicit even when other product surfaces define narrower
|
|
435
|
+
* token inputs.
|
|
436
|
+
*/
|
|
437
|
+
type RegistryTokenSelector = TokenSymbol | RawTokenSelector;
|
|
438
|
+
/**
|
|
439
|
+
* The resolved token information after registry lookup.
|
|
440
|
+
*
|
|
441
|
+
* @remarks
|
|
442
|
+
* This is the result of resolving a `TokenSelector` against a chain.
|
|
443
|
+
* It always contains the locator and decimals, and optionally the symbol
|
|
444
|
+
* if the token was resolved from the registry.
|
|
445
|
+
*/
|
|
446
|
+
interface ResolvedToken {
|
|
447
|
+
/**
|
|
448
|
+
* The token symbol, if known.
|
|
449
|
+
* Present when resolved from registry, absent for raw locators.
|
|
450
|
+
*/
|
|
451
|
+
readonly symbol?: string;
|
|
452
|
+
/**
|
|
453
|
+
* The number of decimal places for the token.
|
|
454
|
+
*/
|
|
455
|
+
readonly decimals: number;
|
|
456
|
+
/**
|
|
457
|
+
* The chain-specific token locator (address, program ID, etc.).
|
|
458
|
+
*/
|
|
459
|
+
readonly locator: string;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Interface for the token registry.
|
|
463
|
+
*
|
|
464
|
+
* @remarks
|
|
465
|
+
* The registry is the sole source of truth for token information.
|
|
466
|
+
* It supports both symbol-based and raw locator-based token selection.
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```typescript
|
|
470
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
471
|
+
*
|
|
472
|
+
* // Create registry (includes built-in tokens like USDC)
|
|
473
|
+
* const registry = createTokenRegistry()
|
|
474
|
+
*
|
|
475
|
+
* // Resolve by symbol
|
|
476
|
+
* const usdc = registry.resolve('USDC', 'Ethereum')
|
|
477
|
+
* console.log(usdc.locator) // '0xa0b86991...'
|
|
478
|
+
*
|
|
479
|
+
* // Resolve raw locator
|
|
480
|
+
* const custom = registry.resolve({ locator: '0x...', decimals: 18 }, 'Ethereum')
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
interface TokenRegistry {
|
|
484
|
+
/**
|
|
485
|
+
* Resolve a token selector to concrete token information for a chain.
|
|
486
|
+
*
|
|
487
|
+
* @param selector - The token to resolve. Accepts a symbol or raw locator.
|
|
488
|
+
* @param chainId - The chain identifier to resolve for.
|
|
489
|
+
* @returns The resolved token information.
|
|
490
|
+
* @throws When the token cannot be resolved (unknown symbol, missing decimals, etc.).
|
|
491
|
+
*/
|
|
492
|
+
resolve(selector: RegistryTokenSelector, chainId: ChainIdentifier): ResolvedToken;
|
|
493
|
+
/**
|
|
494
|
+
* Resolve a token by chain-specific locator (address/program ID).
|
|
495
|
+
*
|
|
496
|
+
* @param address - The token locator to resolve.
|
|
497
|
+
* @param chainId - The chain identifier to resolve for.
|
|
498
|
+
* @returns The resolved token information.
|
|
499
|
+
* @throws When no registry token matches the locator on the chain.
|
|
500
|
+
*/
|
|
501
|
+
resolveByAddress(address: string, chainId: ChainIdentifier): ResolvedToken;
|
|
502
|
+
/**
|
|
503
|
+
* Get a token definition by symbol.
|
|
504
|
+
*
|
|
505
|
+
* @param symbol - The token symbol (e.g., "USDC").
|
|
506
|
+
* @returns The token definition, or undefined if not found.
|
|
507
|
+
*/
|
|
508
|
+
get(symbol: string): TokenDefinition | undefined;
|
|
509
|
+
/**
|
|
510
|
+
* Check if a symbol is registered.
|
|
511
|
+
*
|
|
512
|
+
* @param symbol - The token symbol to check.
|
|
513
|
+
* @returns True if the symbol is in the registry.
|
|
514
|
+
*/
|
|
515
|
+
has(symbol: string): boolean;
|
|
516
|
+
/**
|
|
517
|
+
* Get all registered token symbols.
|
|
518
|
+
*
|
|
519
|
+
* @returns An array of registered symbol strings.
|
|
520
|
+
*/
|
|
521
|
+
symbols(): string[];
|
|
522
|
+
/**
|
|
523
|
+
* Get all registered token definitions.
|
|
524
|
+
*
|
|
525
|
+
* @returns An array of all TokenDefinition objects in the registry.
|
|
526
|
+
*/
|
|
527
|
+
entries(): TokenDefinition[];
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Valid recoverability values for error handling strategies.
|
|
532
|
+
*
|
|
533
|
+
* - FATAL errors are thrown immediately (invalid inputs, insufficient funds)
|
|
534
|
+
* - RETRYABLE errors are returned when a flow fails to start but could work later
|
|
535
|
+
* - RESUMABLE errors are returned when a flow fails mid-execution but can be continued
|
|
536
|
+
*/
|
|
537
|
+
declare const RECOVERABILITY_VALUES: readonly ["RETRYABLE", "RESUMABLE", "FATAL"];
|
|
538
|
+
/**
|
|
539
|
+
* Error handling strategy for different types of failures.
|
|
540
|
+
*
|
|
541
|
+
* - FATAL errors are thrown immediately (invalid inputs, insufficient funds)
|
|
542
|
+
* - RETRYABLE errors are returned when a flow fails to start but could work later
|
|
543
|
+
* - RESUMABLE errors are returned when a flow fails mid-execution but can be continued
|
|
544
|
+
*/
|
|
545
|
+
type Recoverability = (typeof RECOVERABILITY_VALUES)[number];
|
|
546
|
+
/**
|
|
547
|
+
* Array of valid error type values for validation.
|
|
548
|
+
* Derived from ERROR_TYPES const object.
|
|
549
|
+
*/
|
|
550
|
+
declare const ERROR_TYPE_VALUES: ("INPUT" | "BALANCE" | "ONCHAIN" | "RPC" | "NETWORK" | "RATE_LIMIT" | "SERVICE" | "LIQUIDITY" | "UNKNOWN")[];
|
|
551
|
+
/**
|
|
552
|
+
* Error type indicating the category of the error.
|
|
553
|
+
*/
|
|
554
|
+
type ErrorType = (typeof ERROR_TYPE_VALUES)[number];
|
|
555
|
+
/**
|
|
556
|
+
* Structured error details with consistent properties for programmatic handling.
|
|
557
|
+
*
|
|
558
|
+
* This interface provides a standardized format for all errors in the
|
|
559
|
+
* App Kits system, enabling developers to handle different error
|
|
560
|
+
* types consistently and provide appropriate user feedback.
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```typescript
|
|
564
|
+
* const error: ErrorDetails = {
|
|
565
|
+
* code: 1001,
|
|
566
|
+
* name: "INPUT_NETWORK_MISMATCH",
|
|
567
|
+
* type: "INPUT",
|
|
568
|
+
* recoverability: "FATAL",
|
|
569
|
+
* message: "Source and destination networks must be different",
|
|
570
|
+
* cause: {
|
|
571
|
+
* trace: { sourceChain: "ethereum", destChain: "ethereum" }
|
|
572
|
+
* }
|
|
573
|
+
* }
|
|
574
|
+
* ```
|
|
575
|
+
*
|
|
576
|
+
* @example
|
|
577
|
+
* ```typescript
|
|
578
|
+
* const error: ErrorDetails = {
|
|
579
|
+
* code: 9001,
|
|
580
|
+
* name: "BALANCE_INSUFFICIENT_TOKEN",
|
|
581
|
+
* type: "BALANCE",
|
|
582
|
+
* recoverability: "FATAL",
|
|
583
|
+
* message: "Insufficient USDC balance on Ethereum",
|
|
584
|
+
* cause: {
|
|
585
|
+
* trace: { token: "USDC", chain: "Ethereum" }
|
|
586
|
+
* }
|
|
587
|
+
* }
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
interface ErrorDetails {
|
|
591
|
+
/** Numeric identifier following standardized ranges (see error code registry) */
|
|
592
|
+
code: number;
|
|
593
|
+
/** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
|
|
594
|
+
name: string;
|
|
595
|
+
/** Error category indicating where the error originated */
|
|
596
|
+
type: ErrorType;
|
|
597
|
+
/** Error handling strategy */
|
|
598
|
+
recoverability: Recoverability;
|
|
599
|
+
/** User-friendly explanation with context */
|
|
600
|
+
message: string;
|
|
601
|
+
/** Raw error details, context, or the original error that caused this one. */
|
|
602
|
+
cause?: {
|
|
603
|
+
/**
|
|
604
|
+
* Free-form error payload from the underlying system.
|
|
605
|
+
*
|
|
606
|
+
* The shape is **not uniform across error codes**: most codes set `trace`
|
|
607
|
+
* to the raw underlying error, while a few set a structured wrapper object
|
|
608
|
+
* `{ rawError, ...extras }` (e.g. `INPUT_AMOUNT_OUT_OF_RANGE` and
|
|
609
|
+
* `LIQUIDITY_INSUFFICIENT` add `minAmount` / `maxAmount` / `token`).
|
|
610
|
+
* Consumers must branch on `error.code` before reading structured fields off
|
|
611
|
+
* `trace`, and should treat the raw error as the fallback for all other codes.
|
|
612
|
+
*/
|
|
613
|
+
trace?: unknown;
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Simplified error information structure for logging and events.
|
|
618
|
+
*
|
|
619
|
+
* @remarks
|
|
620
|
+
* This lightweight type is used for error reporting in events, logs, and
|
|
621
|
+
* observability systems. It provides essential error context without the
|
|
622
|
+
* full ErrorDetails structure. Used across retry mechanisms, adapters,
|
|
623
|
+
* and other subsystems that need to record error information.
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```typescript
|
|
627
|
+
* import { ErrorInfo } from '@core/errors'
|
|
628
|
+
*
|
|
629
|
+
* const info: ErrorInfo = {
|
|
630
|
+
* name: 'NETWORK_TIMEOUT',
|
|
631
|
+
* message: 'Request timed out after 5000ms',
|
|
632
|
+
* code: 3002
|
|
633
|
+
* }
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
interface ErrorInfo {
|
|
637
|
+
/** Error name (e.g., 'TypeError', 'KitError', 'NETWORK_TIMEOUT'). */
|
|
638
|
+
name: string;
|
|
639
|
+
/** Error message describing what went wrong. */
|
|
640
|
+
message: string;
|
|
641
|
+
/** Optional error code if the error has one (e.g., KitError codes). */
|
|
642
|
+
code?: number;
|
|
643
|
+
/** Error category (e.g., INPUT, RPC, ONCHAIN, BALANCE, NETWORK, UNKNOWN). Only set for KitError instances. */
|
|
644
|
+
type?: string;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
declare class KitError extends Error implements ErrorDetails {
|
|
648
|
+
/** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
|
|
649
|
+
readonly code: number;
|
|
650
|
+
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
651
|
+
readonly name: string;
|
|
652
|
+
/** Error category indicating where the error originated */
|
|
653
|
+
readonly type: ErrorType;
|
|
654
|
+
/** Error handling strategy */
|
|
655
|
+
readonly recoverability: Recoverability;
|
|
656
|
+
/** Raw error details, context, or the original error that caused this one. */
|
|
657
|
+
readonly cause?: {
|
|
658
|
+
/** Free-form error payload from underlying system */
|
|
659
|
+
trace?: unknown;
|
|
660
|
+
};
|
|
661
|
+
/**
|
|
662
|
+
* Create a new KitError instance.
|
|
663
|
+
*
|
|
664
|
+
* @param details - The error details object containing all required properties.
|
|
665
|
+
* @throws \{TypeError\} When details parameter is missing or invalid.
|
|
666
|
+
*/
|
|
667
|
+
constructor(details: ErrorDetails);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Structured fields that can be attached to log entries.
|
|
672
|
+
*/
|
|
673
|
+
type LogFields = Record<string, unknown>;
|
|
674
|
+
/**
|
|
675
|
+
* Logger interface providing structured logging with child scoping.
|
|
676
|
+
*
|
|
677
|
+
* @remarks
|
|
678
|
+
* This interface defines a minimal, framework-agnostic logging contract.
|
|
679
|
+
* The underlying implementation uses pino for transport handling (console,
|
|
680
|
+
* file, remote, JSON, pretty, etc.) but consumers only interact with this
|
|
681
|
+
* stable interface.
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* ```typescript
|
|
685
|
+
* import { createLogger } from '@core/runtime'
|
|
686
|
+
*
|
|
687
|
+
* const logger = createLogger({ level: 'debug' })
|
|
688
|
+
*
|
|
689
|
+
* // Simple message
|
|
690
|
+
* logger.info('Server started')
|
|
691
|
+
*
|
|
692
|
+
* // Message with structured fields
|
|
693
|
+
* logger.info('Request received', { method: 'POST', path: '/api/transfer' })
|
|
694
|
+
*
|
|
695
|
+
* // Create child logger with context
|
|
696
|
+
* const requestLogger = logger.child({ requestId: 'abc-123' })
|
|
697
|
+
* requestLogger.debug('Processing transfer')
|
|
698
|
+
* // Output includes: requestId in all subsequent logs
|
|
699
|
+
* ```
|
|
700
|
+
*/
|
|
701
|
+
interface Logger {
|
|
702
|
+
/**
|
|
703
|
+
* Log a debug-level message.
|
|
704
|
+
* @param message - The log message.
|
|
705
|
+
* @param fields - Optional structured fields.
|
|
706
|
+
* @returns void
|
|
707
|
+
*/
|
|
708
|
+
debug(message: string, fields?: LogFields): void;
|
|
709
|
+
/**
|
|
710
|
+
* Log an info-level message.
|
|
711
|
+
* @param message - The log message.
|
|
712
|
+
* @param fields - Optional structured fields.
|
|
713
|
+
* @returns void
|
|
714
|
+
*/
|
|
715
|
+
info(message: string, fields?: LogFields): void;
|
|
716
|
+
/**
|
|
717
|
+
* Log a warning-level message.
|
|
718
|
+
* @param message - The log message.
|
|
719
|
+
* @param fields - Optional structured fields.
|
|
720
|
+
* @returns void
|
|
721
|
+
*/
|
|
722
|
+
warn(message: string, fields?: LogFields): void;
|
|
723
|
+
/**
|
|
724
|
+
* Log an error-level message.
|
|
725
|
+
* @param message - The log message.
|
|
726
|
+
* @param fields - Optional structured fields.
|
|
727
|
+
* @returns void
|
|
728
|
+
*/
|
|
729
|
+
error(message: string, fields?: LogFields): void;
|
|
730
|
+
/**
|
|
731
|
+
* Create a child logger with additional contextual bindings.
|
|
732
|
+
*
|
|
733
|
+
* @param tags - Key-value pairs to add to the child logger's context.
|
|
734
|
+
* Undefined values are filtered out automatically.
|
|
735
|
+
* @returns A new Logger instance with merged bindings.
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```typescript
|
|
739
|
+
* const requestLogger = logger.child({ requestId: 'req-123' })
|
|
740
|
+
* const userLogger = requestLogger.child({ userId: 'user-456' })
|
|
741
|
+
* // All logs from userLogger include both requestId and userId
|
|
742
|
+
* ```
|
|
743
|
+
*/
|
|
744
|
+
child(tags: LogFields): Logger;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Handler function for event subscriptions.
|
|
749
|
+
*/
|
|
750
|
+
type EventHandler = (event: Event) => void | Promise<void>;
|
|
751
|
+
/**
|
|
752
|
+
* Event bus for publishing and subscribing to events.
|
|
753
|
+
*
|
|
754
|
+
* @remarks
|
|
755
|
+
* Supports wildcard topic subscriptions:
|
|
756
|
+
* - `*` matches exactly one segment
|
|
757
|
+
* - `**` matches zero or more segments
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```typescript
|
|
761
|
+
* const bus = createEventBus()
|
|
762
|
+
*
|
|
763
|
+
* // Subscribe to all events
|
|
764
|
+
* bus.on((event) => console.log(event))
|
|
765
|
+
*
|
|
766
|
+
* // Subscribe to specific topic
|
|
767
|
+
* bus.on('tx.wait.started', (event) => console.log(event))
|
|
768
|
+
*
|
|
769
|
+
* // Subscribe with wildcard
|
|
770
|
+
* bus.on('tx.wait.*', (event) => console.log(event))
|
|
771
|
+
* bus.on('tx.**', (event) => console.log(event))
|
|
772
|
+
*
|
|
773
|
+
* // Emit events
|
|
774
|
+
* bus.emit({ name: 'tx.wait.started', data: { txId: '0x123' } })
|
|
775
|
+
* ```
|
|
776
|
+
*/
|
|
777
|
+
interface EventBus {
|
|
778
|
+
/**
|
|
779
|
+
* Emit an event to all matching subscribers.
|
|
780
|
+
*
|
|
781
|
+
* @param event - The event to emit.
|
|
782
|
+
* @remarks
|
|
783
|
+
* Synchronous and never throws. Handler errors are isolated.
|
|
784
|
+
*/
|
|
785
|
+
emit(event: Event): void;
|
|
786
|
+
/**
|
|
787
|
+
* Create a child event bus with scoped tags.
|
|
788
|
+
*
|
|
789
|
+
* @param tags - Tags to merge into all emitted events.
|
|
790
|
+
* @returns A new EventBus with merged tags.
|
|
791
|
+
*/
|
|
792
|
+
child(tags: Tags): EventBus;
|
|
793
|
+
/**
|
|
794
|
+
* Subscribe to all events.
|
|
795
|
+
*
|
|
796
|
+
* @param handler - Function called for every event.
|
|
797
|
+
* @returns Unsubscribe function.
|
|
798
|
+
*/
|
|
799
|
+
on(handler: EventHandler): () => void;
|
|
800
|
+
/**
|
|
801
|
+
* Subscribe to events matching a pattern.
|
|
802
|
+
*
|
|
803
|
+
* @param pattern - Topic pattern (supports `*` and `**` wildcards).
|
|
804
|
+
* @param handler - Function called for matching events.
|
|
805
|
+
* @returns Unsubscribe function.
|
|
806
|
+
*
|
|
807
|
+
* @remarks
|
|
808
|
+
* Wildcard semantics:
|
|
809
|
+
* - `*` matches exactly one segment (no dots)
|
|
810
|
+
* - `**` matches zero or more segments (only valid at end)
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* ```typescript
|
|
814
|
+
* bus.on('*', handler) // matches 'tx', 'user' (single segment only)
|
|
815
|
+
* bus.on('tx.wait.*', handler) // matches tx.wait.started, tx.wait.failed
|
|
816
|
+
* bus.on('tx.wait.**', handler) // matches tx.wait, tx.wait.started, tx.wait.foo.bar
|
|
817
|
+
* bus.on('**', handler) // matches all events
|
|
818
|
+
* ```
|
|
819
|
+
*/
|
|
820
|
+
on(pattern: string, handler: EventHandler): () => void;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Lifecycle event types and constants for pipeline phase tracking.
|
|
825
|
+
*
|
|
826
|
+
* @packageDocumentation
|
|
827
|
+
*/
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Terminal state of a lifecycle phase.
|
|
831
|
+
*
|
|
832
|
+
* @remarks
|
|
833
|
+
* Exported as a discriminated union so typed consumers of the event
|
|
834
|
+
* stream (agents, dashboards, test assertions) can exhaustively switch
|
|
835
|
+
* on phase outcomes instead of string-comparing event names.
|
|
836
|
+
*
|
|
837
|
+
* @remarks
|
|
838
|
+
* Carried on every lifecycle event's `data.status` (see
|
|
839
|
+
* {@link LifecycleEventData}) so consumers can switch on the outcome
|
|
840
|
+
* without re-deriving it from the event name.
|
|
841
|
+
*
|
|
842
|
+
* @example
|
|
843
|
+
* ```typescript
|
|
844
|
+
* import type { LifecycleEventData } from '@core/runtime'
|
|
845
|
+
*
|
|
846
|
+
* function handle(data: LifecycleEventData) {
|
|
847
|
+
* switch (data.status) {
|
|
848
|
+
* case 'started': return // ...
|
|
849
|
+
* case 'succeeded': return // ...
|
|
850
|
+
* case 'failed': return // ...
|
|
851
|
+
* }
|
|
852
|
+
* }
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
type OperationPhaseStatus = 'started' | 'succeeded' | 'failed';
|
|
856
|
+
/**
|
|
857
|
+
* Data payload for lifecycle events.
|
|
858
|
+
*
|
|
859
|
+
* @remarks
|
|
860
|
+
* These events are emitted by pipeline middleware to track phase execution.
|
|
861
|
+
* Field availability by event:
|
|
862
|
+
* - `op.phase.started`: `status`, `meta`, `inputSummary`
|
|
863
|
+
* - `op.phase.succeeded`: `status`, `meta`, `inputSummary`, `durationMs`
|
|
864
|
+
* - `op.phase.failed`: `status`, `meta`, `inputSummary`, `durationMs`, `error`
|
|
865
|
+
*/
|
|
866
|
+
interface LifecycleEventData {
|
|
867
|
+
/**
|
|
868
|
+
* Terminal status of the phase, mirroring the {@link OperationPhaseStatus}
|
|
869
|
+
* discriminant carried by the event name. Present on every lifecycle
|
|
870
|
+
* event so typed consumers can `switch (data.status)` without parsing
|
|
871
|
+
* the event name.
|
|
872
|
+
*/
|
|
873
|
+
status: OperationPhaseStatus;
|
|
874
|
+
/** Metadata from the pipeline context. */
|
|
875
|
+
meta?: Record<string, unknown> | undefined;
|
|
876
|
+
/** Shallow summary of input (type + keys) to avoid PII exposure. */
|
|
877
|
+
inputSummary?: {
|
|
878
|
+
type: string;
|
|
879
|
+
keys?: string[];
|
|
880
|
+
} | undefined;
|
|
881
|
+
/** Duration of the phase in milliseconds (only for succeeded/failed). */
|
|
882
|
+
durationMs?: number;
|
|
883
|
+
/** Error information (only for failed events). */
|
|
884
|
+
error?: ErrorInfo | undefined;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Augmentable event type registry for the App Kits ecosystem.
|
|
889
|
+
*
|
|
890
|
+
* @remarks
|
|
891
|
+
* This module provides a type-safe event system using TypeScript's
|
|
892
|
+
* module augmentation pattern. Packages can extend `KitEventMap`
|
|
893
|
+
* to register their own event types.
|
|
894
|
+
*
|
|
895
|
+
* **How to Augment:**
|
|
896
|
+
*
|
|
897
|
+
* Other packages can add their own event types by augmenting the
|
|
898
|
+
* `KitEventMap` interface:
|
|
899
|
+
*
|
|
900
|
+
* ```typescript
|
|
901
|
+
* // In your package's types file (e.g., my-package/src/types.ts)
|
|
902
|
+
* declare module '@core/runtime' {
|
|
903
|
+
* interface KitEventMap {
|
|
904
|
+
* 'tx.wait.started': { txId: string }
|
|
905
|
+
* 'tx.wait.completed': { txId: string; confirmations: number }
|
|
906
|
+
* 'tx.wait.failed': { txId: string; error: string }
|
|
907
|
+
* }
|
|
908
|
+
* }
|
|
909
|
+
* ```
|
|
910
|
+
*
|
|
911
|
+
* After augmentation, the event system will be fully typed:
|
|
912
|
+
*
|
|
913
|
+
* ```typescript
|
|
914
|
+
* // Type-safe event emission
|
|
915
|
+
* emit('tx.wait.started', { txId: '0x123' }) // ✓ OK
|
|
916
|
+
* emit('tx.wait.started', { wrong: 'field' }) // ✗ Type error
|
|
917
|
+
* emit('unknown.event', {}) // ✗ Type error (if strict)
|
|
918
|
+
* ```
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```typescript
|
|
922
|
+
* // bridge-kit/src/events.ts
|
|
923
|
+
* declare module '@core/runtime' {
|
|
924
|
+
* interface KitEventMap {
|
|
925
|
+
* 'bridge.transfer.initiated': {
|
|
926
|
+
* sourceChain: string
|
|
927
|
+
* destChain: string
|
|
928
|
+
* amount: string
|
|
929
|
+
* }
|
|
930
|
+
* 'bridge.transfer.completed': {
|
|
931
|
+
* txHash: string
|
|
932
|
+
* duration: number
|
|
933
|
+
* }
|
|
934
|
+
* }
|
|
935
|
+
* }
|
|
936
|
+
* ```
|
|
937
|
+
*/
|
|
938
|
+
/**
|
|
939
|
+
* The core event map interface for module augmentation.
|
|
940
|
+
*
|
|
941
|
+
* @remarks
|
|
942
|
+
* This interface pre-registers the lifecycle events that are always
|
|
943
|
+
* emitted by the pipeline middleware. Packages can extend it via
|
|
944
|
+
* TypeScript's declaration merging to register additional event types.
|
|
945
|
+
*
|
|
946
|
+
* **Pre-registered events:**
|
|
947
|
+
* - `op.phase.started` - Emitted when a pipeline phase begins
|
|
948
|
+
* - `op.phase.succeeded` - Emitted when a pipeline phase completes successfully
|
|
949
|
+
* - `op.phase.failed` - Emitted when a pipeline phase throws an error
|
|
950
|
+
*
|
|
951
|
+
* @example
|
|
952
|
+
* ```typescript
|
|
953
|
+
* // Add your own events via module augmentation
|
|
954
|
+
* declare module '@core/runtime' {
|
|
955
|
+
* interface KitEventMap {
|
|
956
|
+
* 'my.custom.event': { payload: string }
|
|
957
|
+
* }
|
|
958
|
+
* }
|
|
959
|
+
* ```
|
|
960
|
+
*/
|
|
961
|
+
interface KitEventMap {
|
|
962
|
+
'op.phase.started': LifecycleEventData;
|
|
963
|
+
'op.phase.succeeded': LifecycleEventData;
|
|
964
|
+
'op.phase.failed': LifecycleEventData;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Generic event map type for event-related constraints.
|
|
968
|
+
*
|
|
969
|
+
* @remarks
|
|
970
|
+
* Use this when you need to accept any event map without requiring
|
|
971
|
+
* the specific `KitEventMap` augmentations.
|
|
972
|
+
*
|
|
973
|
+
* Uses `any` to accept both interface and type declarations.
|
|
974
|
+
* `unknown` would reject interfaces without index signatures.
|
|
975
|
+
*/
|
|
976
|
+
type EventMap = Record<string, any>;
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Type-level event pattern matching utilities.
|
|
980
|
+
*
|
|
981
|
+
* @remarks
|
|
982
|
+
* These types provide compile-time pattern matching for event keys,
|
|
983
|
+
* mirroring the runtime matcher semantics:
|
|
984
|
+
* - Delimiter: `.`
|
|
985
|
+
* - `*` matches exactly one segment
|
|
986
|
+
* - `**` matches zero or more segments (only valid as last segment)
|
|
987
|
+
* - Exact matching otherwise
|
|
988
|
+
* - Invalid patterns (empty segments, `**` not last) yield `never`
|
|
989
|
+
*
|
|
990
|
+
* See `matching.test-d.ts` for type-level tests.
|
|
991
|
+
*/
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Extract string keys from an event map.
|
|
995
|
+
*
|
|
996
|
+
* @typeParam M - The event map type.
|
|
997
|
+
* @returns Union of string keys from the map.
|
|
998
|
+
*/
|
|
999
|
+
type Keys<M extends EventMap> = Extract<keyof M, string>;
|
|
1000
|
+
/**
|
|
1001
|
+
* Filter keys that match a pattern.
|
|
1002
|
+
*
|
|
1003
|
+
* @typeParam K - Union of string keys to filter.
|
|
1004
|
+
* @typeParam P - Pattern to match against.
|
|
1005
|
+
* @returns Union of keys that match the pattern, or `never` if pattern is invalid.
|
|
1006
|
+
*
|
|
1007
|
+
* @example
|
|
1008
|
+
* ```typescript
|
|
1009
|
+
* type Events = {
|
|
1010
|
+
* 'tx.wait.started': { txId: string }
|
|
1011
|
+
* 'tx.wait.failed': { error: string }
|
|
1012
|
+
* 'tx.send.completed': { hash: string }
|
|
1013
|
+
* }
|
|
1014
|
+
*
|
|
1015
|
+
* // Match single segment wildcard
|
|
1016
|
+
* type WaitEvents = MatchKeys<keyof Events, 'tx.wait.*'>
|
|
1017
|
+
* // = 'tx.wait.started' | 'tx.wait.failed'
|
|
1018
|
+
*
|
|
1019
|
+
* // Match multi-segment wildcard
|
|
1020
|
+
* type AllTxEvents = MatchKeys<keyof Events, 'tx.**'>
|
|
1021
|
+
* // = 'tx.wait.started' | 'tx.wait.failed' | 'tx.send.completed'
|
|
1022
|
+
*
|
|
1023
|
+
* // Match all events
|
|
1024
|
+
* type All = MatchKeys<keyof Events, '**'>
|
|
1025
|
+
* // = keyof Events
|
|
1026
|
+
* ```
|
|
1027
|
+
*/
|
|
1028
|
+
type MatchKeys<K extends string, P extends string> = ParsePattern<P> extends never ? never : ParsePattern<P> extends infer PSegs extends string[] ? K extends unknown ? MatchKey<K, PSegs> : never : never;
|
|
1029
|
+
/**
|
|
1030
|
+
* Split a string by '.' delimiter into a tuple of segments.
|
|
1031
|
+
* Returns `never` if any segment is empty (handles `.a`, `a.`, `a..b`).
|
|
1032
|
+
*/
|
|
1033
|
+
type SplitDot<S extends string> = S extends '' ? [] : S extends `${infer Head}.${infer Tail}` ? Head extends '' ? never : Tail extends '' ? never : SplitDot<Tail> extends infer Rest ? Rest extends never ? never : Rest extends string[] ? [Head, ...Rest] : never : never : [S];
|
|
1034
|
+
/**
|
|
1035
|
+
* Validate that `**` only appears as the last segment (or is the entire pattern).
|
|
1036
|
+
* Returns `true` if valid, `false` if invalid.
|
|
1037
|
+
*/
|
|
1038
|
+
type ValidateGlob<Segs extends string[]> = Segs extends [] ? true : Segs extends [infer Head extends string, ...infer Tail extends string[]] ? Head extends '**' ? Tail extends [] ? true : false : ValidateGlob<Tail> : true;
|
|
1039
|
+
/**
|
|
1040
|
+
* Parse and validate a pattern string.
|
|
1041
|
+
* Returns the segments tuple if valid, `never` if invalid.
|
|
1042
|
+
*/
|
|
1043
|
+
type ParsePattern<P extends string> = SplitDot<P> extends infer Segs ? Segs extends never ? never : Segs extends string[] ? ValidateGlob<Segs> extends true ? Segs : never : never : never;
|
|
1044
|
+
/**
|
|
1045
|
+
* Match a single key against parsed pattern segments.
|
|
1046
|
+
* Returns the key if it matches, `never` otherwise.
|
|
1047
|
+
*/
|
|
1048
|
+
type MatchKey<K extends string, PSegs extends string[]> = SplitDot<K> extends infer KSegs ? KSegs extends never ? never : KSegs extends string[] ? MatchSegments<KSegs, PSegs> extends true ? K : never : never : never;
|
|
1049
|
+
/**
|
|
1050
|
+
* Recursively match key segments against pattern segments.
|
|
1051
|
+
*
|
|
1052
|
+
* Rules:
|
|
1053
|
+
* - If pattern exhausted: key must also be exhausted
|
|
1054
|
+
* - If pattern head is `**`: match (since it's validated to be last)
|
|
1055
|
+
* - If pattern head is `*`: consume one key segment
|
|
1056
|
+
* - Otherwise: exact segment match required
|
|
1057
|
+
*/
|
|
1058
|
+
type MatchSegments<KSegs extends string[], PSegs extends string[]> = PSegs extends [] ? KSegs extends [] ? true : false : PSegs extends [infer PHead extends string, ...infer PTail extends string[]] ? PHead extends '**' ? true : KSegs extends [
|
|
1059
|
+
infer KHead extends string,
|
|
1060
|
+
...infer KTail extends string[]
|
|
1061
|
+
] ? PHead extends '*' ? MatchSegments<KTail, PTail> : PHead extends KHead ? MatchSegments<KTail, PTail> : false : false : false;
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Type-safe wrapper for the untyped EventBus.
|
|
1065
|
+
*
|
|
1066
|
+
* @remarks
|
|
1067
|
+
* This module provides compile-time type safety for event emission
|
|
1068
|
+
* and subscription while maintaining zero runtime overhead.
|
|
1069
|
+
*/
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* A strongly-typed event with known name and data types.
|
|
1073
|
+
*
|
|
1074
|
+
* @typeParam Name - The event name literal type.
|
|
1075
|
+
* @typeParam Data - The event payload type.
|
|
1076
|
+
*/
|
|
1077
|
+
type TypedEvent<Name extends string, Data> = Omit<Event, 'name' | 'data'> & {
|
|
1078
|
+
name: Name;
|
|
1079
|
+
data: Data;
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Create a discriminated union of typed events from an event map.
|
|
1083
|
+
*
|
|
1084
|
+
* @typeParam M - The event map.
|
|
1085
|
+
* @typeParam K - The keys to include (defaults to all keys).
|
|
1086
|
+
*
|
|
1087
|
+
* @remarks
|
|
1088
|
+
* This creates a proper discriminated union where TypeScript can narrow
|
|
1089
|
+
* the `data` type based on checking the `name` field.
|
|
1090
|
+
*/
|
|
1091
|
+
type EventUnion<M extends EventMap, K extends Keys<M> = Keys<M>> = {
|
|
1092
|
+
[N in K]: TypedEvent<N, M[N]>;
|
|
1093
|
+
}[K];
|
|
1094
|
+
/**
|
|
1095
|
+
* Handler for typed events.
|
|
1096
|
+
*/
|
|
1097
|
+
type TypedEventHandler<E> = (event: E) => void | Promise<void>;
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Metrics type definitions for the Runtime module.
|
|
1101
|
+
*
|
|
1102
|
+
* @remarks
|
|
1103
|
+
* Define a minimal, pluggable metrics interface that can be backed by any
|
|
1104
|
+
* metrics library (hot-shots, dd-trace, prom-client, etc.).
|
|
1105
|
+
*
|
|
1106
|
+
* The interface supports:
|
|
1107
|
+
* - Counters for monotonically increasing values
|
|
1108
|
+
* - Histograms for distributions (latencies, sizes)
|
|
1109
|
+
* - Timers as a convenience wrapper for timing operations
|
|
1110
|
+
* - Label scoping via `child()` for dimensional metrics
|
|
1111
|
+
*
|
|
1112
|
+
* @example
|
|
1113
|
+
* ```typescript
|
|
1114
|
+
* // Basic usage
|
|
1115
|
+
* metrics.counter('requests.total').inc({ method: 'POST' })
|
|
1116
|
+
* metrics.histogram('request.duration').observe({ status: 200 }, 42.5)
|
|
1117
|
+
*
|
|
1118
|
+
* // Timer convenience
|
|
1119
|
+
* const stop = metrics.timer('db.query').start({ table: 'users' })
|
|
1120
|
+
* await query()
|
|
1121
|
+
* stop() // Records duration automatically
|
|
1122
|
+
*
|
|
1123
|
+
* // Scoping adds base labels to all metrics
|
|
1124
|
+
* const scoped = metrics.child({ service: 'bridge', env: 'prod' })
|
|
1125
|
+
* scoped.counter('transfers').inc() // Includes service + env labels
|
|
1126
|
+
* ```
|
|
1127
|
+
*/
|
|
1128
|
+
/**
|
|
1129
|
+
* Labels for dimensional metrics.
|
|
1130
|
+
*
|
|
1131
|
+
* @remarks
|
|
1132
|
+
* Labels (also called tags in some systems) are key-value pairs that
|
|
1133
|
+
* provide dimensions for metric aggregation and filtering.
|
|
1134
|
+
* Values must be primitives for serialization compatibility.
|
|
1135
|
+
*
|
|
1136
|
+
* @example
|
|
1137
|
+
* ```typescript
|
|
1138
|
+
* const labels: MetricLabels = {
|
|
1139
|
+
* chain: 'Ethereum',
|
|
1140
|
+
* status: 'success',
|
|
1141
|
+
* retries: 3,
|
|
1142
|
+
* cached: true,
|
|
1143
|
+
* }
|
|
1144
|
+
* ```
|
|
1145
|
+
*/
|
|
1146
|
+
type MetricLabels = Record<string, string | number | boolean>;
|
|
1147
|
+
/**
|
|
1148
|
+
* A counter metric for monotonically increasing values.
|
|
1149
|
+
*
|
|
1150
|
+
* @remarks
|
|
1151
|
+
* Use counters for values that only go up: request counts, error counts,
|
|
1152
|
+
* bytes processed, etc. The value resets only on process restart.
|
|
1153
|
+
*
|
|
1154
|
+
* @see {@link Metrics.counter} to obtain a Counter instance.
|
|
1155
|
+
*
|
|
1156
|
+
* @example
|
|
1157
|
+
* ```typescript
|
|
1158
|
+
* const counter = metrics.counter('http.requests')
|
|
1159
|
+
*
|
|
1160
|
+
* // Increment by 1
|
|
1161
|
+
* counter.inc()
|
|
1162
|
+
* counter.inc({ method: 'GET' })
|
|
1163
|
+
*
|
|
1164
|
+
* // Increment by specific value
|
|
1165
|
+
* counter.inc(5)
|
|
1166
|
+
* counter.inc({ method: 'POST' }, 3)
|
|
1167
|
+
* ```
|
|
1168
|
+
*/
|
|
1169
|
+
interface Counter {
|
|
1170
|
+
/**
|
|
1171
|
+
* Increment the counter value.
|
|
1172
|
+
*
|
|
1173
|
+
* @param labelsOrValue - The labels object or increment value.
|
|
1174
|
+
* When a number, increments by that amount with no labels.
|
|
1175
|
+
* When an object, uses as labels with optional value in second param.
|
|
1176
|
+
* @param value - The increment value when first arg is labels. Default: 1.
|
|
1177
|
+
* @returns void
|
|
1178
|
+
*
|
|
1179
|
+
* @example
|
|
1180
|
+
* ```typescript
|
|
1181
|
+
* counter.inc() // +1, no labels
|
|
1182
|
+
* counter.inc(5) // +5, no labels
|
|
1183
|
+
* counter.inc({ method: 'GET' }) // +1, with labels
|
|
1184
|
+
* counter.inc({ method: 'POST' }, 3) // +3, with labels
|
|
1185
|
+
* ```
|
|
1186
|
+
*/
|
|
1187
|
+
inc(labelsOrValue?: MetricLabels | number, value?: number): void;
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* A histogram metric for recording value distributions.
|
|
1191
|
+
*
|
|
1192
|
+
* @remarks
|
|
1193
|
+
* Use histograms for values that vary and need percentile analysis:
|
|
1194
|
+
* request durations, response sizes, queue depths, etc.
|
|
1195
|
+
*
|
|
1196
|
+
* @see {@link Metrics.histogram} to obtain a Histogram instance.
|
|
1197
|
+
*
|
|
1198
|
+
* @example
|
|
1199
|
+
* ```typescript
|
|
1200
|
+
* const histogram = metrics.histogram('http.duration')
|
|
1201
|
+
*
|
|
1202
|
+
* // Record a value
|
|
1203
|
+
* histogram.observe(42.5)
|
|
1204
|
+
* histogram.observe({ status: 200 }, 42.5)
|
|
1205
|
+
* ```
|
|
1206
|
+
*/
|
|
1207
|
+
interface Histogram {
|
|
1208
|
+
/**
|
|
1209
|
+
* Record an observation value.
|
|
1210
|
+
*
|
|
1211
|
+
* @param labelsOrValue - The labels object or observed value.
|
|
1212
|
+
* When a number, records that value with no labels.
|
|
1213
|
+
* When an object, uses as labels with value in second param.
|
|
1214
|
+
* @param value - The observed value when first arg is labels. Default: 0.
|
|
1215
|
+
* @returns void
|
|
1216
|
+
*
|
|
1217
|
+
* @example
|
|
1218
|
+
* ```typescript
|
|
1219
|
+
* histogram.observe(42.5) // Value only
|
|
1220
|
+
* histogram.observe({ status: 200 }, 42.5) // With labels
|
|
1221
|
+
* ```
|
|
1222
|
+
*/
|
|
1223
|
+
observe(labelsOrValue?: MetricLabels | number, value?: number): void;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* A timer metric for measuring operation durations.
|
|
1227
|
+
*
|
|
1228
|
+
* @remarks
|
|
1229
|
+
* Timers provide a convenience wrapper that automatically records durations
|
|
1230
|
+
* to an underlying histogram. Call `start()` to begin timing and the
|
|
1231
|
+
* returned function to stop and record the elapsed time in milliseconds.
|
|
1232
|
+
*
|
|
1233
|
+
* @see {@link Metrics.timer} to obtain a Timer instance.
|
|
1234
|
+
*
|
|
1235
|
+
* @example
|
|
1236
|
+
* ```typescript
|
|
1237
|
+
* const timer = metrics.timer('db.query')
|
|
1238
|
+
*
|
|
1239
|
+
* // Start timing
|
|
1240
|
+
* const stop = timer.start({ table: 'users' })
|
|
1241
|
+
* await performQuery()
|
|
1242
|
+
* stop() // Records duration in milliseconds
|
|
1243
|
+
* ```
|
|
1244
|
+
*/
|
|
1245
|
+
interface Timer {
|
|
1246
|
+
/**
|
|
1247
|
+
* Start timing an operation.
|
|
1248
|
+
*
|
|
1249
|
+
* @param labels - The optional labels for the timing observation.
|
|
1250
|
+
* @returns A stop function that records the duration when called.
|
|
1251
|
+
*
|
|
1252
|
+
* @example
|
|
1253
|
+
* ```typescript
|
|
1254
|
+
* const stop = timer.start({ operation: 'fetch' })
|
|
1255
|
+
* await fetchData()
|
|
1256
|
+
* stop() // Records elapsed time
|
|
1257
|
+
* ```
|
|
1258
|
+
*/
|
|
1259
|
+
start(labels?: MetricLabels): () => void;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Main metrics interface for instrumentation.
|
|
1263
|
+
*
|
|
1264
|
+
* @remarks
|
|
1265
|
+
* This interface is designed to be thin and pluggable. Implementations
|
|
1266
|
+
* can delegate to any metrics library:
|
|
1267
|
+
*
|
|
1268
|
+
* - **hot-shots**: StatsD/DogStatsD client
|
|
1269
|
+
* - **dd-trace**: Datadog APM
|
|
1270
|
+
* - **prom-client**: Prometheus
|
|
1271
|
+
* - **opentelemetry-js**: OpenTelemetry
|
|
1272
|
+
*
|
|
1273
|
+
* The `child()` method creates a scoped metrics instance that automatically
|
|
1274
|
+
* includes base labels on all metric operations.
|
|
1275
|
+
*
|
|
1276
|
+
* @see {@link createMockMetrics} for testing.
|
|
1277
|
+
* @see {@link noopMetrics} for a no-op implementation.
|
|
1278
|
+
*
|
|
1279
|
+
* @example
|
|
1280
|
+
* ```typescript
|
|
1281
|
+
* // Create a scoped metrics instance
|
|
1282
|
+
* const appMetrics = metrics.child({
|
|
1283
|
+
* service: 'app-kit',
|
|
1284
|
+
* version: '1.0.0',
|
|
1285
|
+
* })
|
|
1286
|
+
*
|
|
1287
|
+
* // All metrics include service + version labels
|
|
1288
|
+
* appMetrics.counter('transfers.initiated').inc({ chain: 'ethereum' })
|
|
1289
|
+
* ```
|
|
1290
|
+
*/
|
|
1291
|
+
interface Metrics {
|
|
1292
|
+
/**
|
|
1293
|
+
* Get or create a counter metric by name.
|
|
1294
|
+
*
|
|
1295
|
+
* @param name - The metric name (e.g., 'http.requests.total').
|
|
1296
|
+
* @returns A Counter instance for the given name.
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```typescript
|
|
1300
|
+
* const counter = metrics.counter('requests.total')
|
|
1301
|
+
* counter.inc({ method: 'GET' })
|
|
1302
|
+
* ```
|
|
1303
|
+
*/
|
|
1304
|
+
counter(name: string): Counter;
|
|
1305
|
+
/**
|
|
1306
|
+
* Get or create a histogram metric by name.
|
|
1307
|
+
*
|
|
1308
|
+
* @param name - The metric name (e.g., 'http.request.duration').
|
|
1309
|
+
* @returns A Histogram instance for the given name.
|
|
1310
|
+
*
|
|
1311
|
+
* @example
|
|
1312
|
+
* ```typescript
|
|
1313
|
+
* const histogram = metrics.histogram('request.duration')
|
|
1314
|
+
* histogram.observe({ status: 200 }, 42.5)
|
|
1315
|
+
* ```
|
|
1316
|
+
*/
|
|
1317
|
+
histogram(name: string): Histogram;
|
|
1318
|
+
/**
|
|
1319
|
+
* Get or create a timer metric by name.
|
|
1320
|
+
*
|
|
1321
|
+
* @param name - The metric name (e.g., 'db.query.duration').
|
|
1322
|
+
* @returns A Timer instance for the given name.
|
|
1323
|
+
*
|
|
1324
|
+
* @example
|
|
1325
|
+
* ```typescript
|
|
1326
|
+
* const stop = metrics.timer('db.query').start()
|
|
1327
|
+
* await query()
|
|
1328
|
+
* stop()
|
|
1329
|
+
* ```
|
|
1330
|
+
*/
|
|
1331
|
+
timer(name: string): Timer;
|
|
1332
|
+
/**
|
|
1333
|
+
* Create a child metrics instance with scoped labels.
|
|
1334
|
+
*
|
|
1335
|
+
* @param labels - The base labels to include on all metric operations.
|
|
1336
|
+
* @returns A new Metrics instance with merged labels.
|
|
1337
|
+
*
|
|
1338
|
+
* @remarks
|
|
1339
|
+
* Labels from the child are merged with any labels passed to individual
|
|
1340
|
+
* metric operations. Call-site labels take precedence for the same key.
|
|
1341
|
+
*
|
|
1342
|
+
* @example
|
|
1343
|
+
* ```typescript
|
|
1344
|
+
* const scoped = metrics.child({ chain: 'Ethereum' })
|
|
1345
|
+
* scoped.counter('transfers').inc({ status: 'success' })
|
|
1346
|
+
* // Labels: { chain: 'Ethereum', status: 'success' }
|
|
1347
|
+
* ```
|
|
1348
|
+
*/
|
|
1349
|
+
child(labels: MetricLabels): Metrics;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
/**
|
|
1353
|
+
* Core type definitions for the runtime package.
|
|
1354
|
+
*
|
|
1355
|
+
* @remarks
|
|
1356
|
+
* This module defines the foundational types used throughout the SDK:
|
|
1357
|
+
*
|
|
1358
|
+
* - {@link Runtime} - Complete runtime with all services (clock, logger, metrics, events)
|
|
1359
|
+
* - {@link ExecutionContext} - Context for middleware with observability surface
|
|
1360
|
+
* - {@link Clock}, {@link Tags}, {@link Event} - Supporting types
|
|
1361
|
+
*
|
|
1362
|
+
* **Naming Convention**
|
|
1363
|
+
*
|
|
1364
|
+
* | Input Type | Resolved Type | Description |
|
|
1365
|
+
* |------------|---------------|-------------|
|
|
1366
|
+
* | `Partial<Runtime>` | `Runtime` | Runtime services |
|
|
1367
|
+
* | `OperationMeta` | `OperationContext` | WHAT - operation target |
|
|
1368
|
+
* | `InvocationMeta` | `InvocationContext` | WHO/HOW - call chain |
|
|
1369
|
+
*
|
|
1370
|
+
* @packageDocumentation
|
|
1371
|
+
*/
|
|
1372
|
+
|
|
1373
|
+
/**
|
|
1374
|
+
* Clock interface for time operations.
|
|
1375
|
+
*
|
|
1376
|
+
* @remarks
|
|
1377
|
+
* Abstracting time allows tests to control timing without real delays.
|
|
1378
|
+
* Production code uses {@link defaultClock}, tests use mock implementations.
|
|
1379
|
+
*
|
|
1380
|
+
* @example
|
|
1381
|
+
* ```typescript
|
|
1382
|
+
* import { defaultClock, type Clock } from '@core/runtime'
|
|
1383
|
+
*
|
|
1384
|
+
* const start = defaultClock.now()
|
|
1385
|
+
* // ... do work ...
|
|
1386
|
+
* const elapsed = defaultClock.since(start)
|
|
1387
|
+
* ```
|
|
1388
|
+
*/
|
|
1389
|
+
interface Clock {
|
|
1390
|
+
/**
|
|
1391
|
+
* Return the current timestamp in milliseconds since Unix epoch.
|
|
1392
|
+
*
|
|
1393
|
+
* @returns Current time in milliseconds.
|
|
1394
|
+
*/
|
|
1395
|
+
now(): number;
|
|
1396
|
+
/**
|
|
1397
|
+
* Calculate elapsed time since a given timestamp.
|
|
1398
|
+
*
|
|
1399
|
+
* @param start - The start timestamp in milliseconds.
|
|
1400
|
+
* @returns Elapsed time in milliseconds (`now() - start`).
|
|
1401
|
+
*/
|
|
1402
|
+
since: (start: number) => number;
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Contextual metadata tags for logging and metrics.
|
|
1406
|
+
*
|
|
1407
|
+
* @remarks
|
|
1408
|
+
* Tags are key-value pairs attached to log entries and metrics.
|
|
1409
|
+
* Values can be primitives or undefined (undefined values are filtered out).
|
|
1410
|
+
*
|
|
1411
|
+
* Common tags include:
|
|
1412
|
+
* - `opId` - Operation identifier for correlation
|
|
1413
|
+
* - `chain` - Blockchain network name
|
|
1414
|
+
* - `phase` - Current pipeline phase
|
|
1415
|
+
*
|
|
1416
|
+
* @example
|
|
1417
|
+
* ```typescript
|
|
1418
|
+
* import type { Tags } from '@core/runtime'
|
|
1419
|
+
*
|
|
1420
|
+
* const tags: Tags = {
|
|
1421
|
+
* opId: 'op-abc123',
|
|
1422
|
+
* chain: 'Ethereum',
|
|
1423
|
+
* phase: 'validate',
|
|
1424
|
+
* optional: undefined, // Will be filtered out
|
|
1425
|
+
* }
|
|
1426
|
+
* ```
|
|
1427
|
+
*/
|
|
1428
|
+
type Tags = Record<string, string | number | boolean | undefined>;
|
|
1429
|
+
/**
|
|
1430
|
+
* A structured event emitted by the runtime.
|
|
1431
|
+
*
|
|
1432
|
+
* @remarks
|
|
1433
|
+
* Events provide a standardized way to capture lifecycle moments,
|
|
1434
|
+
* actions, and state changes throughout the SDK.
|
|
1435
|
+
*/
|
|
1436
|
+
interface Event {
|
|
1437
|
+
/** The event name/identifier. */
|
|
1438
|
+
name: string;
|
|
1439
|
+
/** Log level for the event. */
|
|
1440
|
+
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
1441
|
+
/** Timestamp (epoch ms) when the event occurred. */
|
|
1442
|
+
at?: number;
|
|
1443
|
+
/** Contextual tags for filtering/categorization. */
|
|
1444
|
+
tags?: Tags;
|
|
1445
|
+
/** Arbitrary payload data associated with the event. */
|
|
1446
|
+
data?: unknown;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Complete runtime with all services guaranteed present.
|
|
1450
|
+
*
|
|
1451
|
+
* @remarks
|
|
1452
|
+
* The runtime is the container for all cross-cutting concerns: logging,
|
|
1453
|
+
* timing, events, and metrics. All services are guaranteed to be available.
|
|
1454
|
+
*
|
|
1455
|
+
* **Creating a Runtime**
|
|
1456
|
+
*
|
|
1457
|
+
* Use {@link createRuntime} to create a fully-configured runtime:
|
|
1458
|
+
* ```typescript
|
|
1459
|
+
* import { createRuntime } from '@core/runtime'
|
|
1460
|
+
*
|
|
1461
|
+
* const runtime = createRuntime()
|
|
1462
|
+
* runtime.logger.info('Hello')
|
|
1463
|
+
* runtime.metrics.counter('requests').inc()
|
|
1464
|
+
* ```
|
|
1465
|
+
*
|
|
1466
|
+
* **Partial Runtime Input**
|
|
1467
|
+
*
|
|
1468
|
+
* When accepting runtime configuration as input, use `Partial<Runtime>`:
|
|
1469
|
+
* ```typescript
|
|
1470
|
+
* function myFunction(options: { runtime?: Partial<Runtime> }) {
|
|
1471
|
+
* const runtime = createRuntime(options.runtime)
|
|
1472
|
+
* // ...
|
|
1473
|
+
* }
|
|
1474
|
+
* ```
|
|
1475
|
+
*
|
|
1476
|
+
* @example
|
|
1477
|
+
* ```typescript
|
|
1478
|
+
* import { createRuntime, type Runtime } from '@core/runtime'
|
|
1479
|
+
*
|
|
1480
|
+
* const runtime: Runtime = createRuntime()
|
|
1481
|
+
*
|
|
1482
|
+
* // All services are guaranteed present
|
|
1483
|
+
* runtime.logger.info('Processing request')
|
|
1484
|
+
* runtime.metrics.counter('requests').inc()
|
|
1485
|
+
* runtime.events.emit({ name: 'request.received' })
|
|
1486
|
+
* const now = runtime.clock.now()
|
|
1487
|
+
* ```
|
|
1488
|
+
*/
|
|
1489
|
+
interface Runtime {
|
|
1490
|
+
/**
|
|
1491
|
+
* Clock for time operations.
|
|
1492
|
+
*
|
|
1493
|
+
* @remarks
|
|
1494
|
+
* Provides the current timestamp. Use {@link defaultClock} for production
|
|
1495
|
+
* or custom clocks for deterministic testing.
|
|
1496
|
+
*/
|
|
1497
|
+
clock: Clock;
|
|
1498
|
+
/**
|
|
1499
|
+
* Logger for structured logging.
|
|
1500
|
+
*
|
|
1501
|
+
* @remarks
|
|
1502
|
+
* Provides debug, info, warn, error methods with structured data support.
|
|
1503
|
+
*/
|
|
1504
|
+
logger: Logger;
|
|
1505
|
+
/**
|
|
1506
|
+
* Metrics collector for observability.
|
|
1507
|
+
*
|
|
1508
|
+
* @remarks
|
|
1509
|
+
* Provides counters, histograms, and timers for application metrics.
|
|
1510
|
+
*/
|
|
1511
|
+
metrics: Metrics;
|
|
1512
|
+
/**
|
|
1513
|
+
* Event bus for pub/sub events.
|
|
1514
|
+
*
|
|
1515
|
+
* @remarks
|
|
1516
|
+
* Enables decoupled event emission and subscription across the SDK.
|
|
1517
|
+
*/
|
|
1518
|
+
events: EventBus;
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* A component in the call chain.
|
|
1522
|
+
*
|
|
1523
|
+
* @remarks
|
|
1524
|
+
* Each caller identifies itself with a type (app, kit, provider, adapter)
|
|
1525
|
+
* and a name/version. This enables proper attribution in logs, metrics, and traces.
|
|
1526
|
+
*
|
|
1527
|
+
* @example
|
|
1528
|
+
* ```typescript
|
|
1529
|
+
* import type { Caller } from '@core/runtime'
|
|
1530
|
+
*
|
|
1531
|
+
* const appCaller: Caller = { type: 'app', name: 'MyDApp', version: '1.0.0' }
|
|
1532
|
+
* const kitCaller: Caller = { type: 'kit', name: 'BridgeKit', version: '2.0.0' }
|
|
1533
|
+
* ```
|
|
1534
|
+
*/
|
|
1535
|
+
interface Caller {
|
|
1536
|
+
/**
|
|
1537
|
+
* Type of component in the call hierarchy.
|
|
1538
|
+
*
|
|
1539
|
+
* @remarks
|
|
1540
|
+
* Common types: `app`, `kit`, `provider`, `adapter`
|
|
1541
|
+
*/
|
|
1542
|
+
readonly type: string;
|
|
1543
|
+
/** Name of the component (e.g., 'BridgeKit', 'cctp-v2'). */
|
|
1544
|
+
readonly name: string;
|
|
1545
|
+
/** Version of the component (e.g., '1.0.0'). */
|
|
1546
|
+
readonly version?: string | undefined;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* User input for invocation metadata.
|
|
1550
|
+
*
|
|
1551
|
+
* @remarks
|
|
1552
|
+
* Defines **WHO** called and **HOW** to observe: trace correlation, runtime override,
|
|
1553
|
+
* and caller chain. This is the user-facing input type, resolved to `InvocationContext`.
|
|
1554
|
+
*
|
|
1555
|
+
* Passed as the optional invocation argument to actions and primitives.
|
|
1556
|
+
*
|
|
1557
|
+
* @example
|
|
1558
|
+
* ```typescript
|
|
1559
|
+
* import type { InvocationMeta } from '@core/runtime'
|
|
1560
|
+
*
|
|
1561
|
+
* // Minimal - just traceId
|
|
1562
|
+
* const meta: InvocationMeta = { traceId: 'abc-123' }
|
|
1563
|
+
*
|
|
1564
|
+
* // Full - with runtime override and caller chain
|
|
1565
|
+
* const meta: InvocationMeta = {
|
|
1566
|
+
* traceId: 'abc-123',
|
|
1567
|
+
* runtime: myRuntime,
|
|
1568
|
+
* callers: [
|
|
1569
|
+
* { type: 'app', name: 'MyDApp', version: '1.0.0' },
|
|
1570
|
+
* ],
|
|
1571
|
+
* }
|
|
1572
|
+
* ```
|
|
1573
|
+
*/
|
|
1574
|
+
interface InvocationMeta {
|
|
1575
|
+
/**
|
|
1576
|
+
* Trace ID for distributed tracing correlation.
|
|
1577
|
+
*
|
|
1578
|
+
* @remarks
|
|
1579
|
+
* If not provided, generated automatically.
|
|
1580
|
+
*/
|
|
1581
|
+
readonly traceId?: string | undefined;
|
|
1582
|
+
/**
|
|
1583
|
+
* Runtime override (complete replacement).
|
|
1584
|
+
*
|
|
1585
|
+
* @remarks
|
|
1586
|
+
* When provided, this runtime completely replaces the default runtime.
|
|
1587
|
+
* Must be a complete Runtime instance (e.g., from `createRuntime()`).
|
|
1588
|
+
* If not provided, the default runtime is used.
|
|
1589
|
+
*/
|
|
1590
|
+
readonly runtime?: Runtime | undefined;
|
|
1591
|
+
/**
|
|
1592
|
+
* Token registry override (complete replacement).
|
|
1593
|
+
*
|
|
1594
|
+
* @remarks
|
|
1595
|
+
* When provided, this registry completely replaces the default token registry.
|
|
1596
|
+
* Enables kits to pass their token registry to adapters.
|
|
1597
|
+
* If not provided, the default token registry is used.
|
|
1598
|
+
*/
|
|
1599
|
+
readonly tokens?: TokenRegistry | undefined;
|
|
1600
|
+
/**
|
|
1601
|
+
* Call chain - each caller appends itself.
|
|
1602
|
+
*
|
|
1603
|
+
* @remarks
|
|
1604
|
+
* Ordered from outermost (first) to innermost (last).
|
|
1605
|
+
* Example: [app, kit, provider]
|
|
1606
|
+
*/
|
|
1607
|
+
readonly callers?: readonly Caller[] | undefined;
|
|
1608
|
+
/**
|
|
1609
|
+
* Cooperative cancellation signal for the invocation.
|
|
1610
|
+
*
|
|
1611
|
+
* @remarks
|
|
1612
|
+
* When provided, the signal is threaded onto the resolved
|
|
1613
|
+
* {@link InvocationContext} so operations can forward it to
|
|
1614
|
+
* cancellable work (e.g. `fetch`, timers, adapter calls). Optional and
|
|
1615
|
+
* backwards-compatible: callers that do not support cancellation simply
|
|
1616
|
+
* omit it. Aborting the signal is the caller's responsibility; the
|
|
1617
|
+
* runtime only propagates it.
|
|
1618
|
+
*/
|
|
1619
|
+
readonly signal?: AbortSignal | undefined;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
/**
|
|
1623
|
+
* Operation primitives.
|
|
1624
|
+
*
|
|
1625
|
+
* @remarks
|
|
1626
|
+
* Operations are the unit of public surface a kit exposes. Each operation
|
|
1627
|
+
* has a name, a parameter shape, an optional Zod schema, and a `run`
|
|
1628
|
+
* function bound to a {@link KitContext}.
|
|
1629
|
+
*
|
|
1630
|
+
* The critical type-level concern here is preservation of per-operation
|
|
1631
|
+
* parameter generics so that `AdapterContext`-driven conditional typing
|
|
1632
|
+
* (notably {@link AddressField} narrowing on user-controlled vs.
|
|
1633
|
+
* developer-controlled adapters) survives end-to-end through
|
|
1634
|
+
* {@link createKit}'s method surface.
|
|
1635
|
+
*
|
|
1636
|
+
* @packageDocumentation
|
|
1637
|
+
*/
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* The bound, callable shape of a kit operation.
|
|
1641
|
+
*
|
|
1642
|
+
* @typeParam TParams - The operation's parameter shape.
|
|
1643
|
+
* @typeParam TResult - The operation's result shape.
|
|
1644
|
+
*
|
|
1645
|
+
* @remarks
|
|
1646
|
+
* Parameter generics are preserved on this type and on every container
|
|
1647
|
+
* that holds it. Containers must avoid `KitOperation<unknown, unknown>`
|
|
1648
|
+
* (generic erasure); use mapped types or `infer` instead.
|
|
1649
|
+
*/
|
|
1650
|
+
interface KitOperation<TParams = unknown, TResult = unknown> {
|
|
1651
|
+
readonly name: string;
|
|
1652
|
+
(params: TParams, invocation?: InvocationMeta): Promise<TResult>;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* Kit event subscription surface, backed by `runtime.events`.
|
|
1657
|
+
*
|
|
1658
|
+
* @remarks
|
|
1659
|
+
* kit-base no longer ships a bespoke event bus. Instead, the kit's
|
|
1660
|
+
* public `on` / `off` are a thin, strongly-typed wrapper over the
|
|
1661
|
+
* single `EventBus` owned by the kit's {@link Runtime}. This is the same
|
|
1662
|
+
* bus the operation middleware (lifecycle/retry) and any providers /
|
|
1663
|
+
* adapters in the call tree emit on — so one subscription observes the
|
|
1664
|
+
* whole tree. Wildcard (`*`) and namespace (`**`) patterns work via the
|
|
1665
|
+
* `EventBus` matcher.
|
|
1666
|
+
*
|
|
1667
|
+
* Mirrors `createAdapterEvents` from `@core/adapter-base` and augments
|
|
1668
|
+
* the same `@core/runtime` `KitEventMap`. Kits register their event keys
|
|
1669
|
+
* by augmenting that interface (see the package README).
|
|
1670
|
+
*
|
|
1671
|
+
* @packageDocumentation
|
|
1672
|
+
*/
|
|
1673
|
+
|
|
1674
|
+
/**
|
|
1675
|
+
* Strongly-typed event subscription surface attached to every
|
|
1676
|
+
* {@link Kit} and `CompositeKit` instance.
|
|
1677
|
+
*
|
|
1678
|
+
* @remarks
|
|
1679
|
+
* `on` returns an unsubscribe function (modern pattern). `off` accepts
|
|
1680
|
+
* the original handler reference and removes it for callers that prefer
|
|
1681
|
+
* explicit cleanup. Both are typed against the augmentable
|
|
1682
|
+
* {@link KitEventMap} from `@core/runtime`.
|
|
1683
|
+
*
|
|
1684
|
+
* @typeParam M - The event map (defaults to {@link KitEventMap}).
|
|
1685
|
+
*
|
|
1686
|
+
* @example
|
|
1687
|
+
* ```ts
|
|
1688
|
+
* // Subscribe to a specific event
|
|
1689
|
+
* kit.on('kit.bridge.completed', (event) => console.log(event.data))
|
|
1690
|
+
*
|
|
1691
|
+
* // Subscribe with a namespace pattern
|
|
1692
|
+
* kit.on('kit.bridge.*', (event) => console.log(event.name, event.data))
|
|
1693
|
+
*
|
|
1694
|
+
* // Subscribe to the runtime lifecycle events the middleware emits
|
|
1695
|
+
* kit.on('op.phase.*', (event) => console.log(event.name))
|
|
1696
|
+
*
|
|
1697
|
+
* // Unsubscribe via the returned function or via off()
|
|
1698
|
+
* const unsub = kit.on('kit.bridge.failed', handler)
|
|
1699
|
+
* unsub()
|
|
1700
|
+
* ```
|
|
1701
|
+
*/
|
|
1702
|
+
interface KitEventSubscription<M extends EventMap = KitEventMap> {
|
|
1703
|
+
/**
|
|
1704
|
+
* Subscribe to all events.
|
|
1705
|
+
*
|
|
1706
|
+
* @param handler - Handler receiving any event as a discriminated union.
|
|
1707
|
+
* @returns Unsubscribe function.
|
|
1708
|
+
*/
|
|
1709
|
+
on(handler: TypedEventHandler<EventUnion<M>>): () => void;
|
|
1710
|
+
/**
|
|
1711
|
+
* Subscribe to a specific event by exact name.
|
|
1712
|
+
*
|
|
1713
|
+
* @typeParam K - The event name literal.
|
|
1714
|
+
* @param name - The event name.
|
|
1715
|
+
* @param handler - Handler receiving the typed event.
|
|
1716
|
+
* @returns Unsubscribe function.
|
|
1717
|
+
*/
|
|
1718
|
+
on<K extends Keys<M>>(name: K, handler: TypedEventHandler<TypedEvent<K, M[K]>>): () => void;
|
|
1719
|
+
/**
|
|
1720
|
+
* Subscribe to events matching a glob pattern.
|
|
1721
|
+
*
|
|
1722
|
+
* @typeParam P - The pattern string (supports `*` and `**` wildcards).
|
|
1723
|
+
* @param pattern - The pattern to match event names against.
|
|
1724
|
+
* @param handler - Handler receiving matched events as a discriminated union.
|
|
1725
|
+
* @returns Unsubscribe function.
|
|
1726
|
+
*/
|
|
1727
|
+
on<P extends string>(pattern: P, handler: TypedEventHandler<EventUnion<M, MatchKeys<Keys<M>, P>>>): () => void;
|
|
1728
|
+
/**
|
|
1729
|
+
* Remove a previously registered handler.
|
|
1730
|
+
*
|
|
1731
|
+
* @param handler - The handler function reference to remove.
|
|
1732
|
+
*/
|
|
1733
|
+
off(handler: TypedEventHandler<EventUnion<M>>): void;
|
|
1734
|
+
/**
|
|
1735
|
+
* Remove subscriptions registered with this handler reference.
|
|
1736
|
+
*
|
|
1737
|
+
* @remarks
|
|
1738
|
+
* Removal is **by handler reference**, mirroring `createControllerEvents`
|
|
1739
|
+
* and the `@core/adapter-base` event surfaces. The `name` is accepted
|
|
1740
|
+
* for symmetry with {@link on}, but every subscription for `handler` is
|
|
1741
|
+
* removed regardless of the event name it was registered under. Unknown
|
|
1742
|
+
* handlers are a no-op.
|
|
1743
|
+
*
|
|
1744
|
+
* @typeParam K - The event name literal.
|
|
1745
|
+
* @param name - The event name (accepted for symmetry; not used to
|
|
1746
|
+
* scope removal).
|
|
1747
|
+
* @param handler - The handler function reference to remove.
|
|
1748
|
+
*/
|
|
1749
|
+
off<K extends Keys<M>>(name: K, handler: TypedEventHandler<TypedEvent<K, M[K]>>): void;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/**
|
|
1753
|
+
* Session wire format shared by the server kit (which mints sessions)
|
|
1754
|
+
* and the client kit (which consumes them to launch the widget).
|
|
1755
|
+
*
|
|
1756
|
+
* @remarks
|
|
1757
|
+
* The server kit speaks to `wallets-api` and unwraps its envelope into
|
|
1758
|
+
* the shape exported below. The same shape is what the client kit
|
|
1759
|
+
* consumes via `onrampKit.mountIframe({ session, container })` /
|
|
1760
|
+
* `onrampKit.openWindow({ session })`, so a host application can pass
|
|
1761
|
+
* the server result through verbatim — no manual mapping.
|
|
1762
|
+
*
|
|
1763
|
+
* @packageDocumentation
|
|
1764
|
+
*/
|
|
1765
|
+
/**
|
|
1766
|
+
* One session as returned by the Onramp session endpoint
|
|
1767
|
+
* (`POST /v1/stablecoinKits/sessions`), after the server kit unwraps
|
|
1768
|
+
* the `{ data }` envelope.
|
|
1769
|
+
*
|
|
1770
|
+
* @remarks
|
|
1771
|
+
* This is a **plain type**, not a runtime schema. The kit does not
|
|
1772
|
+
* validate the session response against a closed shape — an additive
|
|
1773
|
+
* change to the Onramp API (a new field, a new optional) must never
|
|
1774
|
+
* break minting. The index signature keeps the type tolerant of extra
|
|
1775
|
+
* fields on the wire. Note that `createSession` applies an outbound
|
|
1776
|
+
* allow-list before returning: only the modeled fields below cross the
|
|
1777
|
+
* server→browser trust boundary, so unmodeled upstream fields are
|
|
1778
|
+
* dropped rather than forwarded.
|
|
1779
|
+
*
|
|
1780
|
+
* Field-by-field rationale:
|
|
1781
|
+
*
|
|
1782
|
+
* - `sessionId` — opaque, server-issued identifier; surfaced on every
|
|
1783
|
+
* widget event for cross-side correlation.
|
|
1784
|
+
* - `sessionToken` — short-lived bearer credential the widget URL
|
|
1785
|
+
* embeds. Never sent back by the client; it is redeemed inside the
|
|
1786
|
+
* widget origin only.
|
|
1787
|
+
* - `widgetUrl` — fully composed launch URL (origin + path + token +
|
|
1788
|
+
* `destinationWallet`). The server kit prefers to compose this itself
|
|
1789
|
+
* so the client kit has no exposure to the widget origin. The client
|
|
1790
|
+
* still accepts a bare `sessionToken` and composes a URL locally as a
|
|
1791
|
+
* fallback. `getOnrampUrl` is the single place that enforces the
|
|
1792
|
+
* https-scheme / origin-pin safety checks before the URL reaches the
|
|
1793
|
+
* DOM — that is a security boundary, not response validation.
|
|
1794
|
+
* - `destinationWallet` — the wallet that receives funds. The widget
|
|
1795
|
+
* requires it as a launch query param, so the server kit echoes the
|
|
1796
|
+
* `destinationAddress` it was minted with here (and bakes it into
|
|
1797
|
+
* `widgetUrl`). Used by the client fallback path when it has to
|
|
1798
|
+
* compose the URL from a bare `sessionToken`.
|
|
1799
|
+
* - `expiresAt` — RFC 3339 timestamp. The client refuses to launch an
|
|
1800
|
+
* expired session and throws an `INPUT` `KitError` so the host can
|
|
1801
|
+
* re-mint.
|
|
1802
|
+
* - `traceId` — the trace id the server stamped when minting. Echoed
|
|
1803
|
+
* back so the client can tag widget events with the same id.
|
|
1804
|
+
*/
|
|
1805
|
+
interface OnrampSession {
|
|
1806
|
+
/**
|
|
1807
|
+
* Opaque server-issued session identifier.
|
|
1808
|
+
*
|
|
1809
|
+
* @remarks
|
|
1810
|
+
* Surfaced on every widget event for cross-side correlation.
|
|
1811
|
+
*/
|
|
1812
|
+
readonly sessionId: string;
|
|
1813
|
+
/**
|
|
1814
|
+
* Short-lived bearer credential the widget URL embeds.
|
|
1815
|
+
*
|
|
1816
|
+
* @remarks
|
|
1817
|
+
* Redeemed inside the widget origin only — never sent back by the
|
|
1818
|
+
* client kit.
|
|
1819
|
+
*/
|
|
1820
|
+
readonly sessionToken: string;
|
|
1821
|
+
/**
|
|
1822
|
+
* Fully composed launch URL (origin + path + token +
|
|
1823
|
+
* `destinationWallet`).
|
|
1824
|
+
*
|
|
1825
|
+
* @remarks
|
|
1826
|
+
* The server kit prefers to compose this itself; the client falls
|
|
1827
|
+
* back to composing locally from `sessionToken` when absent.
|
|
1828
|
+
*/
|
|
1829
|
+
readonly widgetUrl?: string;
|
|
1830
|
+
/**
|
|
1831
|
+
* Wallet address that will receive funds at the end of the onramp
|
|
1832
|
+
* flow.
|
|
1833
|
+
*
|
|
1834
|
+
* @remarks
|
|
1835
|
+
* Echoed verbatim from the `destinationAddress` the server kit
|
|
1836
|
+
* minted with. Used by the client kit's URL-composition fallback
|
|
1837
|
+
* when `widgetUrl` is absent.
|
|
1838
|
+
*/
|
|
1839
|
+
readonly destinationWallet?: string;
|
|
1840
|
+
/**
|
|
1841
|
+
* RFC 3339 expiry timestamp.
|
|
1842
|
+
*
|
|
1843
|
+
* @remarks
|
|
1844
|
+
* The client refuses to launch an expired session and throws an
|
|
1845
|
+
* `INPUT` `KitError` so the host can re-mint.
|
|
1846
|
+
*/
|
|
1847
|
+
readonly expiresAt: string;
|
|
1848
|
+
/**
|
|
1849
|
+
* Trace identifier stamped by the server kit at minting time.
|
|
1850
|
+
*
|
|
1851
|
+
* @remarks
|
|
1852
|
+
* Echoed back so the client can tag widget events with the same
|
|
1853
|
+
* id.
|
|
1854
|
+
*/
|
|
1855
|
+
readonly traceId?: string;
|
|
1856
|
+
/**
|
|
1857
|
+
* Index signature keeping the type tolerant of additive fields.
|
|
1858
|
+
*
|
|
1859
|
+
* @remarks
|
|
1860
|
+
* The kit does not assert a closed shape, so the type accepts extra
|
|
1861
|
+
* fields. At runtime, however, `createSession` rebuilds the result
|
|
1862
|
+
* from an allow-list of the modeled fields above and does **not**
|
|
1863
|
+
* forward unmodeled upstream fields across the server→browser
|
|
1864
|
+
* boundary.
|
|
1865
|
+
*/
|
|
1866
|
+
readonly [key: string]: unknown;
|
|
1867
|
+
}
|
|
1868
|
+
/**
|
|
1869
|
+
* Session shape always returned by `onrampServerKit.createSession()`.
|
|
1870
|
+
*
|
|
1871
|
+
* @remarks
|
|
1872
|
+
* The server kit normalises {@link OnrampSession} so that `widgetUrl`
|
|
1873
|
+
* and `traceId` are guaranteed to be populated by the time the host
|
|
1874
|
+
* passes the session through to the client. Use this alias when
|
|
1875
|
+
* declaring SSR / serializer contracts that round-trip the value
|
|
1876
|
+
* unchanged.
|
|
1877
|
+
*/
|
|
1878
|
+
interface MintedOnrampSession extends OnrampSession {
|
|
1879
|
+
/**
|
|
1880
|
+
* Fully composed launch URL — guaranteed present.
|
|
1881
|
+
*/
|
|
1882
|
+
readonly widgetUrl: string;
|
|
1883
|
+
/**
|
|
1884
|
+
* Trace identifier stamped by the server kit — guaranteed present.
|
|
1885
|
+
*/
|
|
1886
|
+
readonly traceId: string;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* Wire-format schema for the `createSession` request body.
|
|
1891
|
+
*
|
|
1892
|
+
* @remarks
|
|
1893
|
+
* Lives in the shared `protocol/` subtree so both the server kit (which
|
|
1894
|
+
* validates inbound parameters before calling wallets-api) and the
|
|
1895
|
+
* client kit (which POSTs the same shape to a host-owned session
|
|
1896
|
+
* endpoint via {@link fetchOnrampSession}) speak the exact same
|
|
1897
|
+
* contract.
|
|
1898
|
+
*
|
|
1899
|
+
* Validation is intentionally conservative: only the fields the kit
|
|
1900
|
+
* actually consumes (or rejects on shape grounds) are checked. Free-
|
|
1901
|
+
* form fields like `metadata` pass through so wallets-api can layer
|
|
1902
|
+
* its own validation on top without the SDK lagging behind.
|
|
1903
|
+
*
|
|
1904
|
+
* @packageDocumentation
|
|
1905
|
+
*/
|
|
1906
|
+
|
|
1907
|
+
/**
|
|
1908
|
+
* Zod schema for {@link OnrampSessionRequest}.
|
|
1909
|
+
*/
|
|
1910
|
+
declare const onrampSessionRequestSchema: z.ZodObject<{
|
|
1911
|
+
appUserId: z.ZodString;
|
|
1912
|
+
destinationAddress: z.ZodString;
|
|
1913
|
+
destinationChain: z.ZodOptional<z.ZodString>;
|
|
1914
|
+
amount: z.ZodOptional<z.ZodString>;
|
|
1915
|
+
currency: z.ZodOptional<z.ZodString>;
|
|
1916
|
+
email: z.ZodOptional<z.ZodString>;
|
|
1917
|
+
assets: z.ZodOptional<z.ZodObject<{
|
|
1918
|
+
tokens: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1919
|
+
chains: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1920
|
+
pairs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1921
|
+
token: z.ZodString;
|
|
1922
|
+
chain: z.ZodString;
|
|
1923
|
+
}, "strip", z.ZodTypeAny, {
|
|
1924
|
+
token: string;
|
|
1925
|
+
chain: string;
|
|
1926
|
+
}, {
|
|
1927
|
+
token: string;
|
|
1928
|
+
chain: string;
|
|
1929
|
+
}>, "many">>;
|
|
1930
|
+
}, "strip", z.ZodTypeAny, {
|
|
1931
|
+
tokens?: string[] | undefined;
|
|
1932
|
+
chains?: string[] | undefined;
|
|
1933
|
+
pairs?: {
|
|
1934
|
+
token: string;
|
|
1935
|
+
chain: string;
|
|
1936
|
+
}[] | undefined;
|
|
1937
|
+
}, {
|
|
1938
|
+
tokens?: string[] | undefined;
|
|
1939
|
+
chains?: string[] | undefined;
|
|
1940
|
+
pairs?: {
|
|
1941
|
+
token: string;
|
|
1942
|
+
chain: string;
|
|
1943
|
+
}[] | undefined;
|
|
1944
|
+
}>>;
|
|
1945
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
1946
|
+
}, "strip", z.ZodTypeAny, {
|
|
1947
|
+
appUserId: string;
|
|
1948
|
+
destinationAddress: string;
|
|
1949
|
+
destinationChain?: string | undefined;
|
|
1950
|
+
amount?: string | undefined;
|
|
1951
|
+
currency?: string | undefined;
|
|
1952
|
+
email?: string | undefined;
|
|
1953
|
+
assets?: {
|
|
1954
|
+
tokens?: string[] | undefined;
|
|
1955
|
+
chains?: string[] | undefined;
|
|
1956
|
+
pairs?: {
|
|
1957
|
+
token: string;
|
|
1958
|
+
chain: string;
|
|
1959
|
+
}[] | undefined;
|
|
1960
|
+
} | undefined;
|
|
1961
|
+
metadata?: Record<string, unknown> | undefined;
|
|
1962
|
+
}, {
|
|
1963
|
+
appUserId: string;
|
|
1964
|
+
destinationAddress: string;
|
|
1965
|
+
destinationChain?: string | undefined;
|
|
1966
|
+
amount?: string | undefined;
|
|
1967
|
+
currency?: string | undefined;
|
|
1968
|
+
email?: string | undefined;
|
|
1969
|
+
assets?: {
|
|
1970
|
+
tokens?: string[] | undefined;
|
|
1971
|
+
chains?: string[] | undefined;
|
|
1972
|
+
pairs?: {
|
|
1973
|
+
token: string;
|
|
1974
|
+
chain: string;
|
|
1975
|
+
}[] | undefined;
|
|
1976
|
+
} | undefined;
|
|
1977
|
+
metadata?: Record<string, unknown> | undefined;
|
|
1978
|
+
}>;
|
|
1979
|
+
/**
|
|
1980
|
+
* The wire-format request body accepted by the onramp `createSession`
|
|
1981
|
+
* operation.
|
|
1982
|
+
*
|
|
1983
|
+
* @remarks
|
|
1984
|
+
* Shared between the server kit (`onrampServerKit.createSession`) and
|
|
1985
|
+
* the client-side REST fetcher (`fetchOnrampSession({ url, body })`).
|
|
1986
|
+
*
|
|
1987
|
+
* - `appUserId`: opaque host-controlled identifier (most hosts use their
|
|
1988
|
+
* internal primary key). Required so wallets-api can correlate a
|
|
1989
|
+
* session with the host's user record.
|
|
1990
|
+
* - `destinationAddress`: wallet that receives funds at the end of the
|
|
1991
|
+
* onramp flow. The widget displays this as a confirmation step but
|
|
1992
|
+
* the SDK does not validate it on the client beyond
|
|
1993
|
+
* "non-empty string" — wallets-api owns chain-specific validation.
|
|
1994
|
+
* - `destinationChain`: optional. Defaults to "Ethereum" inside
|
|
1995
|
+
* wallets-api. Pass to pre-select a chain in the widget.
|
|
1996
|
+
* - `amount`, `currency`: optional pre-fills.
|
|
1997
|
+
* - `email`: optional KYC routing hint.
|
|
1998
|
+
* - `assets`: optional host-app scoping for the asset selector (which
|
|
1999
|
+
* token/chain pairs the widget offers). See {@link OnrampAssetSelection}.
|
|
2000
|
+
* Baked into the launch URL at mint time; never forwarded to wallets-api.
|
|
2001
|
+
* - `metadata`: free-form passthrough — useful for re-stitching the
|
|
2002
|
+
* session back to the host's order ledger.
|
|
2003
|
+
*
|
|
2004
|
+
* Note: the embedding-domain allowlist entry (`referrerDomain`) is **not**
|
|
2005
|
+
* part of this request. It is a server-only concern configured on
|
|
2006
|
+
* {@link OnrampServerKitOptions.referrerDomain} so it can never be set from
|
|
2007
|
+
* a client-submitted body.
|
|
2008
|
+
*/
|
|
2009
|
+
type OnrampSessionRequest = z.infer<typeof onrampSessionRequestSchema>;
|
|
2010
|
+
|
|
2011
|
+
/**
|
|
2012
|
+
* `createSession` type surface.
|
|
2013
|
+
*
|
|
2014
|
+
* @packageDocumentation
|
|
2015
|
+
*/
|
|
2016
|
+
|
|
2017
|
+
/**
|
|
2018
|
+
* Parameters accepted by `onrampServerKit.createSession()`.
|
|
2019
|
+
*
|
|
2020
|
+
* @remarks
|
|
2021
|
+
* - `appUserId`: opaque host-controlled identifier (most hosts use their
|
|
2022
|
+
* internal primary key). Required so wallets-api can correlate a
|
|
2023
|
+
* session with the host's user record.
|
|
2024
|
+
* - `destinationAddress`: wallet that receives funds at the end of the
|
|
2025
|
+
* onramp flow. The widget displays this as a confirmation step but
|
|
2026
|
+
* the SDK does not validate it on the client beyond
|
|
2027
|
+
* "non-empty string" — wallets-api owns chain-specific validation.
|
|
2028
|
+
* - `destinationChain`: optional. Defaults to "Ethereum" inside
|
|
2029
|
+
* wallets-api. Pass to pre-select a chain in the widget.
|
|
2030
|
+
* - `amount`, `currency`: optional pre-fills.
|
|
2031
|
+
* - `email`: optional KYC routing hint.
|
|
2032
|
+
* - `metadata`: free-form passthrough — useful for re-stitching the
|
|
2033
|
+
* session back to the host's order ledger.
|
|
2034
|
+
*/
|
|
2035
|
+
type CreateSessionParams = OnrampSessionRequest;
|
|
2036
|
+
/**
|
|
2037
|
+
* Strongly-typed handle to the `createSession` kit operation.
|
|
2038
|
+
*
|
|
2039
|
+
* @remarks
|
|
2040
|
+
* Exposed as a public type so host applications can pass the operation
|
|
2041
|
+
* around (e.g. dependency-injected into a route handler) without
|
|
2042
|
+
* losing param/result inference.
|
|
2043
|
+
*/
|
|
2044
|
+
type CreateSessionOperation = KitOperation<CreateSessionParams, MintedOnrampSession>;
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* Shared types for the onramp server kit factory and its operations.
|
|
2048
|
+
*
|
|
2049
|
+
* @packageDocumentation
|
|
2050
|
+
*/
|
|
2051
|
+
|
|
2052
|
+
/**
|
|
2053
|
+
* Options accepted by {@link createOnrampServerKit}.
|
|
2054
|
+
*
|
|
2055
|
+
* @remarks
|
|
2056
|
+
* Apart from the credential, every field carries a sensible production
|
|
2057
|
+
* default and is exposed strictly so test rigs and staging environments
|
|
2058
|
+
* can override individual primitives without forking the SDK.
|
|
2059
|
+
*/
|
|
2060
|
+
interface OnrampServerKitOptions {
|
|
2061
|
+
/**
|
|
2062
|
+
* Long-lived API credential issued by Circle to the host
|
|
2063
|
+
* application's server.
|
|
2064
|
+
*
|
|
2065
|
+
* @remarks
|
|
2066
|
+
* Use the value exactly as issued by the Circle console. Keys look like
|
|
2067
|
+
* `<ENV>_API_KEY:<keyId>:<keySecret>`. The kit sends it verbatim as a
|
|
2068
|
+
* bearer token (`Authorization: Bearer <value>`), so do not strip or
|
|
2069
|
+
* re-add the prefix.
|
|
2070
|
+
*
|
|
2071
|
+
* Must never be shipped to a browser. Use the resulting `sessionToken`
|
|
2072
|
+
* client-side instead.
|
|
2073
|
+
*/
|
|
2074
|
+
readonly apiKey: string;
|
|
2075
|
+
/**
|
|
2076
|
+
* Override the wallets-api base URL.
|
|
2077
|
+
*
|
|
2078
|
+
* @remarks
|
|
2079
|
+
* Defaults to {@link DEFAULT_API_BASE_URL}. Override for staging
|
|
2080
|
+
* environments or local emulators.
|
|
2081
|
+
*/
|
|
2082
|
+
readonly baseUrl?: string | undefined;
|
|
2083
|
+
/**
|
|
2084
|
+
* Override the hosted onramp widget origin.
|
|
2085
|
+
*
|
|
2086
|
+
* @remarks
|
|
2087
|
+
* Defaults to {@link DEFAULT_WIDGET_BASE_URL}. The server kit uses
|
|
2088
|
+
* this to compose `OnrampSession.widgetUrl` when wallets-api does
|
|
2089
|
+
* not return one. Override for staging environments.
|
|
2090
|
+
*/
|
|
2091
|
+
readonly widgetBaseUrl?: string | undefined;
|
|
2092
|
+
/**
|
|
2093
|
+
* Bare hostname of the page that embeds the widget in an iframe, e.g.
|
|
2094
|
+
* `'portal.arc.io'`. Forwarded to the proxy, which seals it into the
|
|
2095
|
+
* session JWT and passes it to Transak as the `referrerDomain` claim so
|
|
2096
|
+
* the host page is authorized as a frame ancestor of Transak's inner
|
|
2097
|
+
* iframe (its `frame-ancestors` CSP).
|
|
2098
|
+
*
|
|
2099
|
+
* @remarks
|
|
2100
|
+
* Set it whenever you embed the widget in an iframe on your own site;
|
|
2101
|
+
* omit it when the widget runs as a top-level page or popup. Value
|
|
2102
|
+
* rules:
|
|
2103
|
+
*
|
|
2104
|
+
* - Hostname only — no scheme, port, path, or wildcard. Correct:
|
|
2105
|
+
* `'portal.arc.io'`. Wrong: `'https://portal.arc.io'`,
|
|
2106
|
+
* `'portal.arc.io:443'`, `'portal.arc.io/buy'`, `'*.arc.io'`.
|
|
2107
|
+
* - For a subdomain, pass the exact host the page is served from
|
|
2108
|
+
* (`'buy.arc.io'`), not the apex domain.
|
|
2109
|
+
*
|
|
2110
|
+
* This is a **server-only construction option**, not a per-session
|
|
2111
|
+
* parameter, precisely so it is derived from your own trusted config
|
|
2112
|
+
* (e.g. a per-environment constant) and can never be taken from a
|
|
2113
|
+
* client-submitted session request or an `Origin`/`Referer` header — a
|
|
2114
|
+
* client-supplied value would let a caller widen the frame-ancestor
|
|
2115
|
+
* allowlist.
|
|
2116
|
+
*/
|
|
2117
|
+
readonly referrerDomain?: string | undefined;
|
|
2118
|
+
/**
|
|
2119
|
+
* Per-request timeout in milliseconds for outbound HTTP calls.
|
|
2120
|
+
*
|
|
2121
|
+
* @remarks
|
|
2122
|
+
* Defaults to {@link DEFAULT_REQUEST_TIMEOUT_MS} (15 s). Set to `0`
|
|
2123
|
+
* to disable timeouts (not recommended outside tests).
|
|
2124
|
+
*/
|
|
2125
|
+
readonly requestTimeoutMs?: number | undefined;
|
|
2126
|
+
/**
|
|
2127
|
+
* Override the `fetch` implementation used for outbound HTTP.
|
|
2128
|
+
*
|
|
2129
|
+
* @remarks
|
|
2130
|
+
* Defaults to `globalThis.fetch`. Provide a custom function in
|
|
2131
|
+
* environments without a global `fetch`, in tests, or when you want
|
|
2132
|
+
* to thread Node's `undici` configuration explicitly.
|
|
2133
|
+
*/
|
|
2134
|
+
readonly fetch?: typeof globalThis.fetch | undefined;
|
|
2135
|
+
/**
|
|
2136
|
+
* Optional extra HTTP headers merged onto every outbound request.
|
|
2137
|
+
*
|
|
2138
|
+
* @remarks
|
|
2139
|
+
* Useful for routing labels, internal proxy auth, or experiment
|
|
2140
|
+
* flags. The kit ignores reserved headers (`Authorization`,
|
|
2141
|
+
* `Content-Type`, `Accept`) to keep the wire format predictable.
|
|
2142
|
+
*/
|
|
2143
|
+
readonly extraHeaders?: Readonly<Record<string, string>> | undefined;
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* The fully-assembled server kit instance returned by
|
|
2147
|
+
* {@link createOnrampServerKit}.
|
|
2148
|
+
*
|
|
2149
|
+
* @remarks
|
|
2150
|
+
* The shape mirrors {@link Kit} from `@core/kit-base` — operation
|
|
2151
|
+
* methods plus the {@link KitEventSubscription} `on` / `off` pair backed
|
|
2152
|
+
* by the kit's runtime bus — but is hand-rolled so the public type stays
|
|
2153
|
+
* narrow (just `createSession`) without leaking the broader
|
|
2154
|
+
* operation-factory mapped-type machinery.
|
|
2155
|
+
*/
|
|
2156
|
+
interface OnrampServerKit extends KitEventSubscription {
|
|
2157
|
+
/**
|
|
2158
|
+
* Mint a short-lived session for the embedded onramp widget.
|
|
2159
|
+
*
|
|
2160
|
+
* @see {@link CreateSessionOperation}
|
|
2161
|
+
*/
|
|
2162
|
+
readonly createSession: CreateSessionOperation;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/**
|
|
2166
|
+
* Options accepted by {@link createSessionRouteHandler}.
|
|
2167
|
+
*/
|
|
2168
|
+
interface CreateSessionRouteHandlerOptions {
|
|
2169
|
+
/**
|
|
2170
|
+
* Authorize the inbound request before the session is minted.
|
|
2171
|
+
*
|
|
2172
|
+
* @remarks
|
|
2173
|
+
* Receives the raw {@link Request}. The handler is **fail-closed**:
|
|
2174
|
+
* only an explicit `true` or `undefined` (the "no opinion" return)
|
|
2175
|
+
* allows the request through. Any other value — `false`, `null`,
|
|
2176
|
+
* `0`, `''`, or an unexpected object from a plain-JS caller — short-
|
|
2177
|
+
* circuits with a `401`. Throw a {@link KitError} to surface a
|
|
2178
|
+
* custom status (see {@link createSessionRouteHandler} for the
|
|
2179
|
+
* type → status map).
|
|
2180
|
+
*
|
|
2181
|
+
* Common patterns:
|
|
2182
|
+
*
|
|
2183
|
+
* - Read your own host session cookie and assert the user is logged
|
|
2184
|
+
* in.
|
|
2185
|
+
* - Verify a CSRF token from the request headers.
|
|
2186
|
+
* - Apply rate limiting per-user before incurring the wallets-api
|
|
2187
|
+
* round-trip.
|
|
2188
|
+
*
|
|
2189
|
+
* @example
|
|
2190
|
+
* ```ts
|
|
2191
|
+
* createSessionRouteHandler(server, {
|
|
2192
|
+
* authorize: async (request) => {
|
|
2193
|
+
* const session = await getHostSession(request)
|
|
2194
|
+
* return session?.user != null
|
|
2195
|
+
* },
|
|
2196
|
+
* })
|
|
2197
|
+
* ```
|
|
2198
|
+
*/
|
|
2199
|
+
readonly authorize?: ((request: Request) => boolean | undefined | Promise<boolean | undefined>) | undefined;
|
|
2200
|
+
/**
|
|
2201
|
+
* Side-channel hook for logging / metrics on every failure path.
|
|
2202
|
+
*
|
|
2203
|
+
* @remarks
|
|
2204
|
+
* Invoked with the original error (typed `unknown`) and the inbound
|
|
2205
|
+
* {@link Request}. Errors thrown from this hook are swallowed to
|
|
2206
|
+
* keep the response path deterministic; emit metrics here, do not
|
|
2207
|
+
* mutate state.
|
|
2208
|
+
*
|
|
2209
|
+
* Return value is ignored. The handler still maps the error to an
|
|
2210
|
+
* HTTP response in the usual way.
|
|
2211
|
+
*/
|
|
2212
|
+
readonly onError?: ((error: unknown, request: Request) => void | Promise<void>) | undefined;
|
|
2213
|
+
}
|
|
2214
|
+
/**
|
|
2215
|
+
* Fetch-API request handler returned by
|
|
2216
|
+
* {@link createSessionRouteHandler}.
|
|
2217
|
+
*/
|
|
2218
|
+
type SessionRouteHandler = (request: Request) => Promise<Response>;
|
|
2219
|
+
/**
|
|
2220
|
+
* Build a drop-in route handler that wraps
|
|
2221
|
+
* {@link OnrampServerKit.createSession}.
|
|
2222
|
+
*
|
|
2223
|
+
* @remarks
|
|
2224
|
+
* **Security — read before deploying.** This handler is
|
|
2225
|
+
* **unauthenticated by default**: without an `authorize` hook it will
|
|
2226
|
+
* mint a session for any caller that POSTs a well-formed body, which
|
|
2227
|
+
* burns your API-key quota and lets anyone drive the onramp flow with
|
|
2228
|
+
* an attacker-chosen `destinationAddress`. Always pass an `authorize`
|
|
2229
|
+
* callback (host-session check, CSRF token, and/or per-user rate
|
|
2230
|
+
* limiting) in production. The kit cannot supply a safe default
|
|
2231
|
+
* because authentication is host-specific.
|
|
2232
|
+
*
|
|
2233
|
+
* Behaviour:
|
|
2234
|
+
*
|
|
2235
|
+
* - Accepts `POST` only (returns `405` otherwise).
|
|
2236
|
+
* - Validates the JSON body via {@link parseOnrampSessionRequest};
|
|
2237
|
+
* `400` on shape mismatch.
|
|
2238
|
+
* - Calls `serverKit.createSession(body)` and returns the resulting
|
|
2239
|
+
* session as JSON (`200`).
|
|
2240
|
+
* - Maps thrown {@link KitError}s to HTTP status:
|
|
2241
|
+
* `INPUT` → 400, `NETWORK` → 504, `RATE_LIMIT` → 429,
|
|
2242
|
+
* `SERVICE` / `RPC` → 502, `UNKNOWN` and everything else → 500.
|
|
2243
|
+
* - Sets `Cache-Control: no-store` on every response — session
|
|
2244
|
+
* tokens are short-lived and must never be cached by intermediaries.
|
|
2245
|
+
* - **Never** echoes the upstream wallets-api response body in the
|
|
2246
|
+
* error payload (the underlying `createSession` operation already
|
|
2247
|
+
* scrubs it); only `{ code, name, type, message }` from the
|
|
2248
|
+
* `KitError` is exposed to the client.
|
|
2249
|
+
*
|
|
2250
|
+
* Hosts that need bespoke error handling, response shaping, or non-
|
|
2251
|
+
* Fetch-API runtimes (Express, Fastify) should keep calling
|
|
2252
|
+
* `serverKit.createSession()` directly.
|
|
2253
|
+
*
|
|
2254
|
+
* @param serverKit - An {@link OnrampServerKit} returned by
|
|
2255
|
+
* {@link createOnrampServerKit}.
|
|
2256
|
+
* @param options - Optional hooks (auth, error logging).
|
|
2257
|
+
* @returns A `(request: Request) => Promise<Response>` handler.
|
|
2258
|
+
*
|
|
2259
|
+
* @example Next.js App Router (one-liner)
|
|
2260
|
+
* ```ts
|
|
2261
|
+
* // app/api/onramp/sessions/route.ts
|
|
2262
|
+
* import {
|
|
2263
|
+
* createOnrampServerKit,
|
|
2264
|
+
* createSessionRouteHandler,
|
|
2265
|
+
* } from '@circle-fin/onramp-kit/server'
|
|
2266
|
+
*
|
|
2267
|
+
* const server = createOnrampServerKit({ apiKey: process.env.ONRAMP_API_KEY! })
|
|
2268
|
+
* export const POST = createSessionRouteHandler(server)
|
|
2269
|
+
* ```
|
|
2270
|
+
*
|
|
2271
|
+
* @example With an authn check
|
|
2272
|
+
* ```ts
|
|
2273
|
+
* import { auth } from '@/lib/auth'
|
|
2274
|
+
*
|
|
2275
|
+
* export const POST = createSessionRouteHandler(server, {
|
|
2276
|
+
* authorize: async (request) => {
|
|
2277
|
+
* const session = await auth(request)
|
|
2278
|
+
* return session?.user != null
|
|
2279
|
+
* },
|
|
2280
|
+
* })
|
|
2281
|
+
* ```
|
|
2282
|
+
*/
|
|
2283
|
+
declare function createSessionRouteHandler(serverKit: OnrampServerKit, options?: CreateSessionRouteHandlerOptions): SessionRouteHandler;
|
|
2284
|
+
|
|
2285
|
+
/**
|
|
2286
|
+
* Identity for the server surface.
|
|
2287
|
+
*
|
|
2288
|
+
* @remarks
|
|
2289
|
+
* Shares the same `name` and `version` as {@link ONRAMP_KIT_IDENTITY}
|
|
2290
|
+
* but is exported under a distinct symbol so callers that import both
|
|
2291
|
+
* surfaces in the same module get clean autocompletion (`OnrampKit` vs
|
|
2292
|
+
* `OnrampServerKit`).
|
|
2293
|
+
*/
|
|
2294
|
+
declare const ONRAMP_SERVER_KIT_IDENTITY: {
|
|
2295
|
+
readonly name: "onramp-kit";
|
|
2296
|
+
readonly version: string;
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2299
|
+
/**
|
|
2300
|
+
* Coerce an unknown value into an {@link OnrampSession}.
|
|
2301
|
+
*
|
|
2302
|
+
* @param value - Value of unknown shape, typically the JSON-decoded
|
|
2303
|
+
* body of a host-app session endpoint.
|
|
2304
|
+
* @returns The session, with the `{ data }` envelope unwrapped when
|
|
2305
|
+
* present.
|
|
2306
|
+
*
|
|
2307
|
+
* @throws KitError - `INPUT_INVALID_SESSION` (code `1904`) when the
|
|
2308
|
+
* value (after envelope unwrapping) is not a non-null object — i.e.
|
|
2309
|
+
* not a usable session at all.
|
|
2310
|
+
*
|
|
2311
|
+
* @remarks
|
|
2312
|
+
* This helper does **not** assert a closed session schema — by design.
|
|
2313
|
+
* The kit never pins the Onramp API response to a fixed shape, so an
|
|
2314
|
+
* additive API change (a new field, a new optional) cannot break it.
|
|
2315
|
+
* The only checks are unwrapping the `{ data: session }` envelope (some
|
|
2316
|
+
* hosts proxy it straight through) and a minimal structural guard that
|
|
2317
|
+
* the result is a non-null object, so the returned value is at least
|
|
2318
|
+
* minimally backed rather than an unchecked cast. The security-critical
|
|
2319
|
+
* checks (https scheme, origin pin, expiry) still run later in
|
|
2320
|
+
* `getOnrampUrl`, right before the URL reaches the DOM.
|
|
2321
|
+
*
|
|
2322
|
+
* @example
|
|
2323
|
+
* ```ts
|
|
2324
|
+
* const session = parseOnrampSession(await response.json())
|
|
2325
|
+
* onramp.mountIframe({ session, container })
|
|
2326
|
+
* ```
|
|
2327
|
+
*/
|
|
2328
|
+
declare function parseOnrampSession(value: unknown): OnrampSession;
|
|
2329
|
+
/**
|
|
2330
|
+
* Parse an unknown value as an {@link OnrampSessionRequest}.
|
|
2331
|
+
*
|
|
2332
|
+
* @param value - Value of unknown shape, typically the JSON-decoded
|
|
2333
|
+
* request body received by a host-app session minting endpoint.
|
|
2334
|
+
* @returns The validated request body.
|
|
2335
|
+
*
|
|
2336
|
+
* @throws KitError - When the value does not satisfy the request
|
|
2337
|
+
* schema.
|
|
2338
|
+
*
|
|
2339
|
+
* @example
|
|
2340
|
+
* ```ts
|
|
2341
|
+
* app.post('/api/onramp/sessions', async (req, res) => {
|
|
2342
|
+
* const body = parseOnrampSessionRequest(await req.json())
|
|
2343
|
+
* const session = await onrampServerKit.createSession(body)
|
|
2344
|
+
* res.json(session)
|
|
2345
|
+
* })
|
|
2346
|
+
* ```
|
|
2347
|
+
*/
|
|
2348
|
+
declare function parseOnrampSessionRequest(value: unknown): OnrampSessionRequest;
|
|
2349
|
+
|
|
2350
|
+
/**
|
|
2351
|
+
* Factory for `@circle-fin/app-kit/server`.
|
|
2352
|
+
*
|
|
2353
|
+
* @remarks
|
|
2354
|
+
* Composes the server-only sub-kits that AppKit ships. Today there
|
|
2355
|
+
* is one: `onramp`, an {@link OnrampServerKit} that mints onramp
|
|
2356
|
+
* sessions using the host's long-lived `apiKey`. The factory is the
|
|
2357
|
+
* only place that touches the secret — by keeping it behind a
|
|
2358
|
+
* dedicated `/server` entry, bundlers reliably exclude it from
|
|
2359
|
+
* browser builds.
|
|
2360
|
+
*
|
|
2361
|
+
* @packageDocumentation
|
|
2362
|
+
*/
|
|
2363
|
+
|
|
2364
|
+
/**
|
|
2365
|
+
* Onramp slice of {@link AppServerKitConfig}.
|
|
2366
|
+
*
|
|
2367
|
+
* @remarks
|
|
2368
|
+
* Re-exported here as a named type so consumers do not need to
|
|
2369
|
+
* reach into `@circle-fin/onramp-kit/server` for the option shape.
|
|
2370
|
+
*/
|
|
2371
|
+
type AppServerKitOnrampConfig = OnrampServerKitOptions;
|
|
2372
|
+
/**
|
|
2373
|
+
* Options accepted by {@link createAppServerKit}.
|
|
2374
|
+
*
|
|
2375
|
+
* @example
|
|
2376
|
+
* ```typescript
|
|
2377
|
+
* import { createAppServerKit } from '@circle-fin/app-kit/server'
|
|
2378
|
+
*
|
|
2379
|
+
* const server = createAppServerKit({
|
|
2380
|
+
* onramp: {
|
|
2381
|
+
* apiKey: process.env.ONRAMP_API_KEY!,
|
|
2382
|
+
* // Required when embedding the widget in an iframe — the bare
|
|
2383
|
+
* // hostname of your embedding page.
|
|
2384
|
+
* referrerDomain: 'portal.arc.io',
|
|
2385
|
+
* },
|
|
2386
|
+
* })
|
|
2387
|
+
* ```
|
|
2388
|
+
*/
|
|
2389
|
+
interface AppServerKitConfig {
|
|
2390
|
+
/**
|
|
2391
|
+
* Configuration for the onramp server kit (session minting).
|
|
2392
|
+
*
|
|
2393
|
+
* @remarks
|
|
2394
|
+
* `apiKey` is required. If you embed the widget in an iframe, also set
|
|
2395
|
+
* `referrerDomain` (the bare hostname of your embedding page) — it has
|
|
2396
|
+
* no default and authorizes that page as a frame ancestor of the
|
|
2397
|
+
* provider's iframe. All remaining fields carry sensible production
|
|
2398
|
+
* defaults — see {@link OnrampServerKitOptions}.
|
|
2399
|
+
*/
|
|
2400
|
+
readonly onramp: AppServerKitOnrampConfig;
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* Fully-assembled server kit for `@circle-fin/app-kit/server`.
|
|
2404
|
+
*
|
|
2405
|
+
* @remarks
|
|
2406
|
+
* Hand-rolled namespace surface so additional server-only kits can
|
|
2407
|
+
* slot in over time without leaking implementation types into the
|
|
2408
|
+
* public API.
|
|
2409
|
+
*
|
|
2410
|
+
* @example
|
|
2411
|
+
* ```typescript
|
|
2412
|
+
* import {
|
|
2413
|
+
* createAppServerKit,
|
|
2414
|
+
* createSessionRouteHandler,
|
|
2415
|
+
* } from '@circle-fin/app-kit/server'
|
|
2416
|
+
*
|
|
2417
|
+
* const server = createAppServerKit({
|
|
2418
|
+
* onramp: { apiKey: process.env.ONRAMP_API_KEY! },
|
|
2419
|
+
* })
|
|
2420
|
+
*
|
|
2421
|
+
* export const POST = createSessionRouteHandler(server.onramp)
|
|
2422
|
+
* ```
|
|
2423
|
+
*/
|
|
2424
|
+
interface AppServerKit {
|
|
2425
|
+
/**
|
|
2426
|
+
* Server-side onramp surface — mints sessions for the embedded
|
|
2427
|
+
* onramp widget.
|
|
2428
|
+
*
|
|
2429
|
+
* @remarks
|
|
2430
|
+
* Pass `server.onramp` straight to `createSessionRouteHandler` to
|
|
2431
|
+
* mount the canonical session endpoint, or call
|
|
2432
|
+
* `server.onramp.createSession(params)` from a custom handler.
|
|
2433
|
+
*/
|
|
2434
|
+
readonly onramp: OnrampServerKit;
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Build the app server kit.
|
|
2438
|
+
*
|
|
2439
|
+
* @param config - {@link AppServerKitConfig}.
|
|
2440
|
+
* @returns A frozen {@link AppServerKit} ready to mint sessions.
|
|
2441
|
+
*
|
|
2442
|
+
* @throws KitError - When `config` is not an object, `config.onramp`
|
|
2443
|
+
* is missing / non-object, or the wrapped onramp factory rejects
|
|
2444
|
+
* its options (invalid `apiKey`, malformed URL overrides, etc.).
|
|
2445
|
+
*
|
|
2446
|
+
* @remarks
|
|
2447
|
+
* Stateless beyond closures. Cache one instance per app.
|
|
2448
|
+
*
|
|
2449
|
+
* @example
|
|
2450
|
+
* ```typescript
|
|
2451
|
+
* import {
|
|
2452
|
+
* createAppServerKit,
|
|
2453
|
+
* createSessionRouteHandler,
|
|
2454
|
+
* } from '@circle-fin/app-kit/server'
|
|
2455
|
+
*
|
|
2456
|
+
* const server = createAppServerKit({
|
|
2457
|
+
* onramp: { apiKey: process.env.ONRAMP_API_KEY! },
|
|
2458
|
+
* })
|
|
2459
|
+
*
|
|
2460
|
+
* // Wire the session route in one line:
|
|
2461
|
+
* export const POST = createSessionRouteHandler(server.onramp)
|
|
2462
|
+
* ```
|
|
2463
|
+
*/
|
|
2464
|
+
declare function createAppServerKit(config: AppServerKitConfig): AppServerKit;
|
|
2465
|
+
|
|
2466
|
+
export { KitError, ONRAMP_SERVER_KIT_IDENTITY, createAppServerKit, onrampSessionRequestSchema as createSessionParamsSchema, createSessionRouteHandler, parseOnrampSession, parseOnrampSessionRequest };
|
|
2467
|
+
export type { AppServerKit, AppServerKitConfig, AppServerKitOnrampConfig, CreateSessionOperation, CreateSessionParams, CreateSessionRouteHandlerOptions, MintedOnrampSession, OnrampServerKit, OnrampServerKitOptions, OnrampSession, OnrampSessionRequest, SessionRouteHandler };
|