@armory-sh/base 0.2.28 → 0.2.30

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.
Files changed (43) hide show
  1. package/README.md +304 -28
  2. package/dist/client-hooks-runtime.d.ts +9 -0
  3. package/dist/encoding/x402.d.ts +1 -1
  4. package/dist/errors.d.ts +10 -0
  5. package/dist/facilitator-capabilities.d.ts +9 -0
  6. package/dist/index.d.ts +30 -22
  7. package/dist/index.js +1198 -650
  8. package/dist/payment-client.d.ts +1 -1
  9. package/dist/payment-requirements.d.ts +35 -0
  10. package/dist/protocol.d.ts +33 -0
  11. package/dist/types/api.d.ts +1 -1
  12. package/dist/types/hooks.d.ts +17 -1
  13. package/dist/types/protocol.d.ts +1 -1
  14. package/dist/types/wallet.d.ts +24 -0
  15. package/dist/utils/base64.d.ts +4 -0
  16. package/dist/utils/x402.d.ts +1 -1
  17. package/dist/validation.d.ts +1 -1
  18. package/package.json +15 -2
  19. package/src/abi/erc20.ts +84 -0
  20. package/src/client-hooks-runtime.ts +153 -0
  21. package/src/data/tokens.ts +199 -0
  22. package/src/eip712.ts +108 -0
  23. package/src/encoding/x402.ts +205 -0
  24. package/src/encoding.ts +98 -0
  25. package/src/errors.ts +23 -0
  26. package/src/facilitator-capabilities.ts +125 -0
  27. package/src/index.ts +330 -0
  28. package/src/payment-client.ts +201 -0
  29. package/src/payment-requirements.ts +354 -0
  30. package/src/protocol.ts +57 -0
  31. package/src/types/api.ts +304 -0
  32. package/src/types/hooks.ts +85 -0
  33. package/src/types/networks.ts +175 -0
  34. package/src/types/protocol.ts +182 -0
  35. package/src/types/v2.ts +282 -0
  36. package/src/types/wallet.ts +30 -0
  37. package/src/types/x402.ts +151 -0
  38. package/src/utils/base64.ts +48 -0
  39. package/src/utils/routes.ts +240 -0
  40. package/src/utils/utils/index.ts +7 -0
  41. package/src/utils/utils/mock-facilitator.ts +184 -0
  42. package/src/utils/x402.ts +147 -0
  43. package/src/validation.ts +654 -0
@@ -0,0 +1,354 @@
1
+ import type {
2
+ NetworkId,
3
+ ResolvedNetwork,
4
+ ResolvedToken,
5
+ TokenId,
6
+ ValidationError,
7
+ } from "./types/api";
8
+ import { getNetworkConfig } from "./types/networks";
9
+ import type { Address, PaymentRequirementsV2 } from "./types/v2";
10
+ import { toAtomicUnits } from "./utils/x402";
11
+ import {
12
+ normalizeNetworkName,
13
+ resolveNetwork,
14
+ resolveToken,
15
+ } from "./validation";
16
+
17
+ export interface PaymentConfig {
18
+ payTo: Address | string;
19
+ chains?: NetworkId[];
20
+ chain?: NetworkId;
21
+ tokens?: TokenId[];
22
+ token?: TokenId;
23
+ amount?: string;
24
+ maxTimeoutSeconds?: number;
25
+
26
+ payToByChain?: Record<string, Address | string>;
27
+ payToByToken?: Record<string, Record<string, Address | string>>;
28
+
29
+ facilitatorUrl?: string;
30
+ facilitatorUrlByChain?: Record<string, string>;
31
+ facilitatorUrlByToken?: Record<string, Record<string, string>>;
32
+ }
33
+
34
+ export interface ResolvedRequirementsConfig {
35
+ requirements: PaymentRequirementsV2[];
36
+ error?: ValidationError;
37
+ }
38
+
39
+ export interface FacilitatorRoutingConfig {
40
+ facilitatorUrl?: string;
41
+ facilitatorUrlByChain?: Record<string, string>;
42
+ facilitatorUrlByToken?: Record<string, Record<string, string>>;
43
+ }
44
+
45
+ const DEFAULT_NETWORKS: NetworkId[] = [
46
+ "ethereum",
47
+ "base",
48
+ "base-sepolia",
49
+ "skale-base",
50
+ "skale-base-sepolia",
51
+ "ethereum-sepolia",
52
+ ];
53
+
54
+ const DEFAULT_TOKENS: TokenId[] = ["usdc"];
55
+
56
+ const isValidationError = (value: unknown): value is ValidationError => {
57
+ return typeof value === "object" && value !== null && "code" in value;
58
+ };
59
+
60
+ function resolvePayTo(
61
+ config: PaymentConfig,
62
+ network: ResolvedNetwork,
63
+ token: ResolvedToken,
64
+ ): Address | string {
65
+ const chainId = network.config.chainId;
66
+
67
+ if (config.payToByToken) {
68
+ for (const [chainKey, tokenMap] of Object.entries(config.payToByToken)) {
69
+ const resolvedChain = resolveNetwork(chainKey);
70
+ if (
71
+ !isValidationError(resolvedChain) &&
72
+ resolvedChain.config.chainId === chainId
73
+ ) {
74
+ for (const [tokenKey, address] of Object.entries(tokenMap)) {
75
+ const resolvedToken = resolveToken(tokenKey, network);
76
+ if (
77
+ !isValidationError(resolvedToken) &&
78
+ resolvedToken.config.contractAddress.toLowerCase() ===
79
+ token.config.contractAddress.toLowerCase()
80
+ ) {
81
+ return address;
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ if (config.payToByChain) {
89
+ for (const [chainKey, address] of Object.entries(config.payToByChain)) {
90
+ const resolvedChain = resolveNetwork(chainKey);
91
+ if (
92
+ !isValidationError(resolvedChain) &&
93
+ resolvedChain.config.chainId === chainId
94
+ ) {
95
+ return address;
96
+ }
97
+ }
98
+ }
99
+
100
+ return config.payTo;
101
+ }
102
+
103
+ function resolveFacilitatorUrl(
104
+ config: PaymentConfig,
105
+ network: ResolvedNetwork,
106
+ token: ResolvedToken,
107
+ ): string | undefined {
108
+ const chainId = network.config.chainId;
109
+
110
+ if (config.facilitatorUrlByToken) {
111
+ for (const [chainKey, tokenMap] of Object.entries(
112
+ config.facilitatorUrlByToken,
113
+ )) {
114
+ const resolvedChain = resolveNetwork(chainKey);
115
+ if (
116
+ !isValidationError(resolvedChain) &&
117
+ resolvedChain.config.chainId === chainId
118
+ ) {
119
+ for (const [tokenKey, url] of Object.entries(tokenMap)) {
120
+ const resolvedToken = resolveToken(tokenKey, network);
121
+ if (
122
+ !isValidationError(resolvedToken) &&
123
+ resolvedToken.config.contractAddress.toLowerCase() ===
124
+ token.config.contractAddress.toLowerCase()
125
+ ) {
126
+ return url;
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ if (config.facilitatorUrlByChain) {
134
+ for (const [chainKey, url] of Object.entries(
135
+ config.facilitatorUrlByChain,
136
+ )) {
137
+ const resolvedChain = resolveNetwork(chainKey);
138
+ if (
139
+ !isValidationError(resolvedChain) &&
140
+ resolvedChain.config.chainId === chainId
141
+ ) {
142
+ return url;
143
+ }
144
+ }
145
+ }
146
+
147
+ return config.facilitatorUrl;
148
+ }
149
+
150
+ export function resolveFacilitatorUrlFromRequirement(
151
+ config: FacilitatorRoutingConfig,
152
+ requirement: Pick<PaymentRequirementsV2, "network" | "asset">,
153
+ ): string | undefined {
154
+ const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
155
+ const assetAddress = requirement.asset.toLowerCase();
156
+
157
+ if (config.facilitatorUrlByToken) {
158
+ for (const [chainKey, tokenMap] of Object.entries(
159
+ config.facilitatorUrlByToken,
160
+ )) {
161
+ const resolvedChain = resolveNetwork(chainKey);
162
+ if (
163
+ isValidationError(resolvedChain) ||
164
+ resolvedChain.config.chainId !== chainId
165
+ ) {
166
+ continue;
167
+ }
168
+
169
+ for (const [tokenKey, url] of Object.entries(tokenMap)) {
170
+ const resolvedToken = resolveToken(tokenKey, resolvedChain);
171
+ if (
172
+ !isValidationError(resolvedToken) &&
173
+ resolvedToken.config.contractAddress.toLowerCase() === assetAddress
174
+ ) {
175
+ return url;
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ if (config.facilitatorUrlByChain) {
182
+ for (const [chainKey, url] of Object.entries(
183
+ config.facilitatorUrlByChain,
184
+ )) {
185
+ const resolvedChain = resolveNetwork(chainKey);
186
+ if (
187
+ !isValidationError(resolvedChain) &&
188
+ resolvedChain.config.chainId === chainId
189
+ ) {
190
+ return url;
191
+ }
192
+ }
193
+ }
194
+
195
+ return config.facilitatorUrl;
196
+ }
197
+
198
+ function resolveNetworks(chainInputs: NetworkId[] | undefined): {
199
+ networks: ResolvedNetwork[];
200
+ error?: ValidationError;
201
+ } {
202
+ const resolvedNetworks: ResolvedNetwork[] = [];
203
+ const errors: string[] = [];
204
+
205
+ const networks = chainInputs?.length ? chainInputs : DEFAULT_NETWORKS;
206
+
207
+ for (const networkId of networks) {
208
+ const resolved = resolveNetwork(networkId);
209
+ if (isValidationError(resolved)) {
210
+ errors.push(`Network "${networkId}": ${resolved.message}`);
211
+ } else {
212
+ resolvedNetworks.push(resolved);
213
+ }
214
+ }
215
+
216
+ if (errors.length > 0) {
217
+ return {
218
+ networks: resolvedNetworks,
219
+ error: {
220
+ code: "VALIDATION_FAILED",
221
+ message: errors.join("; "),
222
+ },
223
+ };
224
+ }
225
+
226
+ return { networks: resolvedNetworks };
227
+ }
228
+
229
+ export function createPaymentRequirements(
230
+ config: PaymentConfig,
231
+ ): ResolvedRequirementsConfig {
232
+ const {
233
+ payTo,
234
+ chains,
235
+ chain,
236
+ tokens,
237
+ token,
238
+ amount = "1.0",
239
+ maxTimeoutSeconds = 300,
240
+ } = config;
241
+
242
+ const chainInputs = chain ? [chain] : chains;
243
+ const tokenInputs = token ? [token] : tokens;
244
+
245
+ const { networks, error: networksError } = resolveNetworks(chainInputs);
246
+ if (networksError) {
247
+ return { requirements: [], error: networksError };
248
+ }
249
+
250
+ const requirements: PaymentRequirementsV2[] = [];
251
+
252
+ for (const network of networks) {
253
+ const tokensToResolve = tokenInputs?.length ? tokenInputs : DEFAULT_TOKENS;
254
+
255
+ for (const tokenId of tokensToResolve) {
256
+ const resolvedToken = resolveToken(tokenId, network);
257
+ if (isValidationError(resolvedToken)) {
258
+ continue;
259
+ }
260
+
261
+ const atomicAmount = toAtomicUnits(amount);
262
+ const tokenConfig = resolvedToken.config;
263
+
264
+ const resolvedPayTo = resolvePayTo(config, network, resolvedToken);
265
+ const resolvedFacilitatorUrl = resolveFacilitatorUrl(
266
+ config,
267
+ network,
268
+ resolvedToken,
269
+ );
270
+
271
+ const requirement: PaymentRequirementsV2 = {
272
+ scheme: "exact",
273
+ network: network.caip2,
274
+ amount: atomicAmount,
275
+ asset: tokenConfig.contractAddress,
276
+ payTo: resolvedPayTo as `0x${string}`,
277
+ maxTimeoutSeconds,
278
+ extra: {
279
+ name: tokenConfig.name,
280
+ version: tokenConfig.version,
281
+ },
282
+ };
283
+
284
+ requirements.push(requirement);
285
+ }
286
+ }
287
+
288
+ if (requirements.length === 0) {
289
+ return {
290
+ requirements: [],
291
+ error: {
292
+ code: "VALIDATION_FAILED",
293
+ message: "No valid network/token combinations found",
294
+ },
295
+ };
296
+ }
297
+
298
+ return { requirements };
299
+ }
300
+
301
+ export function findRequirementByNetwork(
302
+ requirements: PaymentRequirementsV2[],
303
+ network: string,
304
+ ): PaymentRequirementsV2 | undefined {
305
+ const normalized = normalizeNetworkName(network);
306
+ const netConfig = getNetworkConfig(normalized);
307
+ if (!netConfig) return undefined;
308
+
309
+ return requirements.find((r) => r.network === netConfig.caip2Id);
310
+ }
311
+
312
+ export function findRequirementByAccepted(
313
+ requirements: PaymentRequirementsV2[],
314
+ accepted: {
315
+ scheme: string;
316
+ network: string;
317
+ amount: string;
318
+ asset: string;
319
+ payTo: string;
320
+ },
321
+ ): PaymentRequirementsV2 | undefined {
322
+ const acceptedPayTo =
323
+ typeof accepted.payTo === "string" ? accepted.payTo.toLowerCase() : "";
324
+ const exactMatch = requirements.find(
325
+ (requirement) =>
326
+ requirement.scheme === accepted.scheme &&
327
+ requirement.network === accepted.network &&
328
+ requirement.amount === accepted.amount &&
329
+ requirement.asset.toLowerCase() === accepted.asset.toLowerCase() &&
330
+ typeof requirement.payTo === "string" &&
331
+ requirement.payTo.toLowerCase() === acceptedPayTo,
332
+ );
333
+ if (exactMatch) {
334
+ return exactMatch;
335
+ }
336
+
337
+ const payToAwareMatch = requirements.find(
338
+ (requirement) =>
339
+ requirement.scheme === accepted.scheme &&
340
+ requirement.network === accepted.network &&
341
+ acceptedPayTo.length > 0 &&
342
+ typeof requirement.payTo === "string" &&
343
+ requirement.payTo.toLowerCase() === acceptedPayTo,
344
+ );
345
+ if (payToAwareMatch) {
346
+ return payToAwareMatch;
347
+ }
348
+
349
+ return requirements.find(
350
+ (requirement) =>
351
+ requirement.scheme === accepted.scheme &&
352
+ requirement.network === accepted.network,
353
+ );
354
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * X402 Protocol Utilities (V2 Only)
3
+ *
4
+ * Shared protocol utilities for x402 client implementations.
5
+ * These functions are protocol-level and don't depend on wallet libraries.
6
+ */
7
+
8
+ import { V2_HEADERS } from "./types/v2";
9
+ import { decodeBase64ToUtf8, normalizeBase64Url } from "./utils/base64";
10
+
11
+ /**
12
+ * Parse JSON or Base64-encoded JSON
13
+ * Handles both raw JSON and Base64URL-encoded JSON
14
+ */
15
+ export function parseJsonOrBase64(value: string): unknown {
16
+ try {
17
+ return JSON.parse(value);
18
+ } catch {
19
+ const normalized = normalizeBase64Url(value);
20
+ return JSON.parse(decodeBase64ToUtf8(normalized));
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Detect x402 protocol version from response
26
+ * V2-only: Always returns 2
27
+ */
28
+ export function detectX402Version(_response: Response): 2 {
29
+ return 2;
30
+ }
31
+
32
+ /**
33
+ * Get payment header name for protocol version
34
+ * V2-only: Returns PAYMENT-SIGNATURE header name
35
+ */
36
+ export function getPaymentHeaderName(_version: 2): string {
37
+ return V2_HEADERS.PAYMENT_SIGNATURE;
38
+ }
39
+
40
+ /**
41
+ * Generate a timestamp-based nonce as hex string
42
+ * Matches Coinbase SDK format: "0x" + 64 hex characters
43
+ * Uses timestamp for reproducibility (vs random)
44
+ */
45
+ export function generateNonce(): `0x${string}` {
46
+ const now = Math.floor(Date.now() / 1000);
47
+ return `0x${(now * 1000).toString(16).padStart(64, "0")}` as `0x${string}`;
48
+ }
49
+
50
+ /**
51
+ * Calculate expiry timestamp
52
+ * @param expirySeconds - Seconds from now (default: 3600 = 1 hour)
53
+ * @returns Unix timestamp
54
+ */
55
+ export function calculateValidBefore(expirySeconds: number = 3600): number {
56
+ return Math.floor(Date.now() / 1000) + expirySeconds;
57
+ }
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Simple, developer-friendly API types for Armory
3
+ * Focuses on DX/UX - "everything just magically works"
4
+ */
5
+
6
+ import type { CustomToken, NetworkConfig } from "./networks";
7
+ import type { Address, CAIPAssetId } from "./v2";
8
+
9
+ // ═══════════════════════════════════════════════════════════════
10
+ // Simple API Types
11
+ // ═══════════════════════════════════════════════════════════════
12
+
13
+ /**
14
+ * Network identifier - flexible input that resolves to a chain
15
+ * - Network name: "base", "ethereum", "skale-base"
16
+ * - CAIP-2: "eip155:8453"
17
+ * - Chain ID: 8453
18
+ */
19
+ export type NetworkId = string | number;
20
+
21
+ /**
22
+ * Token identifier - flexible input that resolves to a token
23
+ * - Symbol: "usdc", "eurc", "usdt" (case-insensitive)
24
+ * - CAIP Asset ID: "eip155:8453/erc20:0x..."
25
+ * - Full token config object
26
+ */
27
+ export type TokenId = string | CustomToken;
28
+
29
+ /**
30
+ * Facilitator configuration
31
+ */
32
+ export interface FacilitatorConfig {
33
+ /** Facilitator URL for verification/settlement */
34
+ url: string;
35
+ /** Optional authentication headers */
36
+ headers?: () => Record<string, string>;
37
+ /** Networks this facilitator supports (auto-detected if not provided) */
38
+ networks?: NetworkId[];
39
+ /** Tokens this facilitator supports (optional) */
40
+ tokens?: TokenId[];
41
+ }
42
+
43
+ /**
44
+ * Result from facilitator verification
45
+ */
46
+ export interface FacilitatorVerifyResult {
47
+ success: boolean;
48
+ payerAddress?: string;
49
+ balance?: string;
50
+ requiredAmount?: string;
51
+ error?: string;
52
+ }
53
+
54
+ /**
55
+ * Result from facilitator settlement
56
+ */
57
+ export interface FacilitatorSettleResult {
58
+ success: boolean;
59
+ txHash?: string;
60
+ error?: string;
61
+ }
62
+
63
+ /**
64
+ * Settlement mode - verify only, settle only, or both
65
+ */
66
+ export type SettlementMode = "verify" | "settle" | "async";
67
+
68
+ /**
69
+ * CAIP-2 chain ID type (e.g., eip155:8453)
70
+ */
71
+ export type CAIP2ChainId = `eip155:${string}`;
72
+
73
+ /**
74
+ * CAIP-2 asset ID type (e.g., eip155:8453/erc20:0xa0b8691...)
75
+ */
76
+ export type CAIP2AssetId = `eip155:${string}/erc20:${string}`;
77
+
78
+ /**
79
+ * Payment destination - address, CAIP-2 chain ID, or CAIP asset ID
80
+ */
81
+ export type PayToAddress = `0x${string}` | CAIP2ChainId | CAIP2AssetId;
82
+
83
+ /**
84
+ * Pricing configuration for a specific network/token/facilitator combination
85
+ */
86
+ export interface PricingConfig {
87
+ /** Network for this pricing */
88
+ network?: NetworkId;
89
+ /** Token for this pricing */
90
+ token?: TokenId;
91
+ /** Facilitator URL for this pricing (optional - applies to all facilitators if not specified) */
92
+ facilitator?: string;
93
+ /** Amount to charge */
94
+ amount: string;
95
+ }
96
+
97
+ /**
98
+ * Payment accept options for merchants (v2 multi-support)
99
+ */
100
+ export interface AcceptPaymentOptions {
101
+ /** Networks to accept payments on */
102
+ networks?: NetworkId[];
103
+ /** Tokens to accept (defaults to ["usdc"]) */
104
+ tokens?: TokenId[];
105
+ /** Facilitator(s) for payment verification/settlement */
106
+ facilitators?: FacilitatorConfig | FacilitatorConfig[];
107
+ /** Protocol version (V2 only) */
108
+ version?: 2;
109
+ /** Pricing configurations - if not provided, uses amount from top-level config */
110
+ pricing?: PricingConfig[];
111
+ }
112
+
113
+ /**
114
+ * Result of a payment attempt
115
+ */
116
+ export interface PaymentResult<T = unknown> {
117
+ /** Payment successful */
118
+ success: true;
119
+ /** Response data */
120
+ data: T;
121
+ /** Transaction hash (if settled) */
122
+ txHash?: string;
123
+ }
124
+
125
+ /**
126
+ * Payment error result
127
+ */
128
+ export interface PaymentError {
129
+ /** Payment failed */
130
+ success: false;
131
+ /** Error code */
132
+ code: PaymentErrorCode;
133
+ /** Human-readable error message */
134
+ message: string;
135
+ /** Underlying error details */
136
+ details?: unknown;
137
+ }
138
+
139
+ /**
140
+ * Payment error codes
141
+ */
142
+ export type PaymentErrorCode =
143
+ | "UNKNOWN_NETWORK"
144
+ | "UNKNOWN_TOKEN"
145
+ | "TOKEN_NOT_ON_NETWORK"
146
+ | "FACILITATOR_UNSUPPORTED"
147
+ | "FACILITATOR_NO_NETWORK_SUPPORT"
148
+ | "FACILITATOR_NO_TOKEN_SUPPORT"
149
+ | "INVALID_CAIP_FORMAT"
150
+ | "CHAIN_MISMATCH"
151
+ | "PAYMENT_REQUIRED"
152
+ | "PAYMENT_DECLINED"
153
+ | "SIGNING_FAILED"
154
+ | "NETWORK_ERROR"
155
+ | "VALIDATION_FAILED";
156
+
157
+ /**
158
+ * Combined payment result type
159
+ */
160
+ export type ArmoryPaymentResult<T = unknown> = PaymentResult<T> | PaymentError;
161
+
162
+ // ═══════════════════════════════════════════════════════════════
163
+ // Resolved Configuration (internal use)
164
+ // ═══════════════════════════════════════════════════════════════
165
+
166
+ /**
167
+ * Fully resolved network configuration
168
+ */
169
+ export interface ResolvedNetwork {
170
+ /** Original input */
171
+ input: NetworkId;
172
+ /** Network config */
173
+ config: NetworkConfig;
174
+ /** CAIP-2 ID */
175
+ caip2: `eip155:${string}`;
176
+ }
177
+
178
+ /**
179
+ * Fully resolved token configuration
180
+ */
181
+ export interface ResolvedToken {
182
+ /** Original input */
183
+ input: TokenId;
184
+ /** Token config */
185
+ config: CustomToken;
186
+ /** CAIP Asset ID */
187
+ caipAsset: CAIPAssetId;
188
+ /** Network this token is on */
189
+ network: ResolvedNetwork;
190
+ }
191
+
192
+ /**
193
+ * Fully resolved facilitator configuration
194
+ */
195
+ export interface ResolvedFacilitator {
196
+ /** Original input */
197
+ input: FacilitatorConfig;
198
+ /** URL */
199
+ url: string;
200
+ /** Supported networks (resolved) */
201
+ networks: ResolvedNetwork[];
202
+ /** Supported tokens (resolved) */
203
+ tokens: ResolvedToken[];
204
+ }
205
+
206
+ /**
207
+ * Fully resolved payment configuration
208
+ */
209
+ export interface ResolvedPaymentConfig {
210
+ /** Network to use */
211
+ network: ResolvedNetwork;
212
+ /** Token to use */
213
+ token: ResolvedToken;
214
+ /** Facilitator(s) to use */
215
+ facilitators: ResolvedFacilitator[];
216
+ /** Protocol version (V2 only) */
217
+ version: 2;
218
+ /** Recipient address */
219
+ payTo: Address;
220
+ /** Amount to charge */
221
+ amount: string;
222
+ }
223
+
224
+ /**
225
+ * Validation error details
226
+ */
227
+ export interface ValidationError {
228
+ /** Error code */
229
+ code: PaymentErrorCode;
230
+ /** Error message */
231
+ message: string;
232
+ /** Path to the invalid value */
233
+ path?: string;
234
+ /** Invalid value */
235
+ value?: unknown;
236
+ /** Valid options */
237
+ validOptions?: string[];
238
+ }
239
+
240
+ // ═══════════════════════════════════════════════════════════════
241
+ // Route Configuration Types
242
+ // ═══════════════════════════════════════════════════════════════
243
+
244
+ /**
245
+ * Route pattern type for matching paths
246
+ */
247
+ export type RoutePattern = string;
248
+
249
+ /**
250
+ * Route matcher function type
251
+ */
252
+ export type RouteMatcher = (path: string) => boolean;
253
+
254
+ /**
255
+ * Route configuration with associated config data
256
+ */
257
+ export interface RouteConfig<T = unknown> {
258
+ /** Route pattern (e.g., "/api/users", "/api/*", "/api/users/:id") */
259
+ pattern: RoutePattern;
260
+ /** Configuration associated with this route */
261
+ config: T;
262
+ }
263
+
264
+ /**
265
+ * Parsed route pattern information
266
+ */
267
+ export interface ParsedPattern {
268
+ /** Route segments split by "/" */
269
+ segments: string[];
270
+ /** Whether this pattern contains a wildcard */
271
+ isWildcard: boolean;
272
+ /** Whether this pattern contains parameters (e.g., :id) */
273
+ isParametrized: boolean;
274
+ /** Parameter names extracted from the pattern */
275
+ paramNames: string[];
276
+ /** Match priority (higher = more specific) */
277
+ priority: number;
278
+ }
279
+
280
+ /**
281
+ * Route input configuration for middleware
282
+ */
283
+ export interface RouteInputConfig {
284
+ /** Single exact route (no wildcards allowed) */
285
+ route?: string;
286
+ /** Multiple routes (allows wildcards) */
287
+ routes?: string[];
288
+ }
289
+
290
+ /**
291
+ * Route-specific validation error details
292
+ */
293
+ export interface RouteValidationError {
294
+ /** Error code (custom string, not PaymentErrorCode) */
295
+ code: string;
296
+ /** Error message */
297
+ message: string;
298
+ /** Path to the invalid value */
299
+ path?: string;
300
+ /** Invalid value */
301
+ value?: unknown;
302
+ /** Valid options */
303
+ validOptions?: string[];
304
+ }