@armory-sh/base 0.2.18 → 0.2.20
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/dist/index.d.ts +3 -0
- package/dist/index.js +183 -6
- package/dist/types/api.d.ts +56 -0
- package/dist/types/hooks.d.ts +35 -0
- package/dist/utils/routes.d.ts +28 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ export { ERC20_ABI } from "./abi/erc20";
|
|
|
14
14
|
export type { TypedDataDomain, EIP712Domain, TransferWithAuthorization, TypedDataField, EIP712Types, } from "./eip712";
|
|
15
15
|
export { EIP712_TYPES, USDC_DOMAIN, createEIP712Domain, createTransferWithAuthorization, validateTransferWithAuthorization, } from "./eip712";
|
|
16
16
|
export { resolveNetwork, resolveToken, resolveFacilitator, checkFacilitatorSupport, validatePaymentConfig, validateAcceptConfig, getAvailableNetworks, getAvailableTokens, isValidationError, isResolvedNetwork, isResolvedToken, createError, normalizeNetworkName, } from "./validation";
|
|
17
|
+
export type { RoutePattern, RouteMatcher, RouteConfig, ParsedPattern, RouteInputConfig, RouteValidationError, } from "./utils/routes";
|
|
18
|
+
export { parseRoutePattern, matchRoute, findMatchingRoute, validateRouteConfig, } from "./utils/routes";
|
|
17
19
|
export { encodePaymentV2, decodePaymentV2, encodeSettlementV2, decodeSettlementV2, isPaymentV2, isSettlementV2, } from "./encoding";
|
|
18
20
|
export type { NetworkId, TokenId, FacilitatorConfig, FacilitatorVerifyResult, FacilitatorSettleResult, SettlementMode, PayToAddress, AcceptPaymentOptions, PricingConfig, PaymentResult, PaymentError, PaymentErrorCode, ArmoryPaymentResult, ResolvedNetwork, ResolvedToken, ResolvedFacilitator, ResolvedPaymentConfig, ValidationError, } from "./types/api";
|
|
21
|
+
export type { PaymentRequiredContext, PaymentPayloadContext, HookResult, OnPaymentRequiredHook, BeforePaymentHook, ExtensionHook, HookConfig, HookRegistry, } from "./types/hooks";
|
|
19
22
|
export { createX402V2Payload, createLegacyV2Payload, INVALID_PAYLOADS, TEST_PAYER_ADDRESS, TEST_PAY_TO_ADDRESS, TEST_CONTRACT_ADDRESS, TEST_PRIVATE_KEY, } from "./fixtures/payloads";
|
|
20
23
|
export { DEFAULT_PAYMENT_CONFIG, type TestPaymentConfig } from "./fixtures/config";
|
package/dist/index.js
CHANGED
|
@@ -1109,13 +1109,186 @@ var isResolvedToken = (value) => {
|
|
|
1109
1109
|
return typeof value === "object" && value !== null && "config" in value && "caipAsset" in value;
|
|
1110
1110
|
};
|
|
1111
1111
|
|
|
1112
|
+
// src/utils/routes.ts
|
|
1113
|
+
var PRIORITY_EXACT = 3;
|
|
1114
|
+
var PRIORITY_PARAMETRIZED = 2;
|
|
1115
|
+
var PRIORITY_WILDCARD = 1;
|
|
1116
|
+
function parseRoutePattern(pattern) {
|
|
1117
|
+
const normalizedPattern = pattern.startsWith("/") ? pattern : `/${pattern}`;
|
|
1118
|
+
const segments = normalizedPattern.split("/").filter(Boolean);
|
|
1119
|
+
let isWildcard = false;
|
|
1120
|
+
let isParametrized = false;
|
|
1121
|
+
const paramNames = [];
|
|
1122
|
+
const seenParamNames = /* @__PURE__ */ new Set();
|
|
1123
|
+
const recordParamName = (name) => {
|
|
1124
|
+
if (!name) {
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
if (seenParamNames.has(name)) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
seenParamNames.add(name);
|
|
1131
|
+
paramNames.push(name);
|
|
1132
|
+
};
|
|
1133
|
+
for (const segment of segments) {
|
|
1134
|
+
if (segment === "*") {
|
|
1135
|
+
isWildcard = true;
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
const hasWildcardToken = segment.includes("*");
|
|
1139
|
+
if (segment.startsWith(":")) {
|
|
1140
|
+
isParametrized = true;
|
|
1141
|
+
const paramName = segment.replace(/\*+$/, "").slice(1);
|
|
1142
|
+
recordParamName(paramName);
|
|
1143
|
+
if (hasWildcardToken) {
|
|
1144
|
+
isWildcard = true;
|
|
1145
|
+
}
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (hasWildcardToken) {
|
|
1149
|
+
const parts = segment.split("*");
|
|
1150
|
+
for (const part of parts) {
|
|
1151
|
+
if (part.startsWith(":")) {
|
|
1152
|
+
isParametrized = true;
|
|
1153
|
+
recordParamName(part.slice(1));
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
isWildcard = true;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
let priority = PRIORITY_WILDCARD;
|
|
1160
|
+
if (!isWildcard && !isParametrized) {
|
|
1161
|
+
priority = PRIORITY_EXACT;
|
|
1162
|
+
} else if (isParametrized && !isWildcard) {
|
|
1163
|
+
priority = PRIORITY_PARAMETRIZED;
|
|
1164
|
+
} else if (isParametrized && isWildcard) {
|
|
1165
|
+
priority = PRIORITY_PARAMETRIZED;
|
|
1166
|
+
}
|
|
1167
|
+
return { segments, isWildcard, isParametrized, paramNames, priority };
|
|
1168
|
+
}
|
|
1169
|
+
function matchSegment(patternSegment, pathSegment) {
|
|
1170
|
+
if (patternSegment === "*") {
|
|
1171
|
+
return true;
|
|
1172
|
+
}
|
|
1173
|
+
if (patternSegment.startsWith(":")) {
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
if (patternSegment.includes("*")) {
|
|
1177
|
+
const regex = new RegExp(
|
|
1178
|
+
"^" + patternSegment.replace(/\*/g, ".*").replace(/:/g, "") + "$"
|
|
1179
|
+
);
|
|
1180
|
+
return regex.test(pathSegment);
|
|
1181
|
+
}
|
|
1182
|
+
return patternSegment === pathSegment;
|
|
1183
|
+
}
|
|
1184
|
+
function matchWildcardPattern(patternSegments, pathSegments) {
|
|
1185
|
+
const requiredSegments = patternSegments.filter((s) => s !== "*");
|
|
1186
|
+
if (requiredSegments.length > pathSegments.length) {
|
|
1187
|
+
return false;
|
|
1188
|
+
}
|
|
1189
|
+
for (let i = 0; i < requiredSegments.length; i++) {
|
|
1190
|
+
const patternIndex = patternSegments.indexOf(requiredSegments[i]);
|
|
1191
|
+
if (pathSegments[patternIndex] !== requiredSegments[i].replace(/^\:/, "")) {
|
|
1192
|
+
if (!requiredSegments[i].startsWith(":") && requiredSegments[i] !== "*") {
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return true;
|
|
1198
|
+
}
|
|
1199
|
+
function matchRoute(pattern, path) {
|
|
1200
|
+
const normalizedPattern = pattern.startsWith("/") ? pattern : `/${pattern}`;
|
|
1201
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1202
|
+
if (normalizedPattern === normalizedPath) {
|
|
1203
|
+
return true;
|
|
1204
|
+
}
|
|
1205
|
+
const parsed = parseRoutePattern(normalizedPattern);
|
|
1206
|
+
const patternSegments = parsed.segments;
|
|
1207
|
+
const pathSegments = normalizedPath.split("/").filter(Boolean);
|
|
1208
|
+
if (!parsed.isWildcard && patternSegments.length !== pathSegments.length) {
|
|
1209
|
+
return false;
|
|
1210
|
+
}
|
|
1211
|
+
if (parsed.isWildcard && patternSegments.length > pathSegments.length + 1) {
|
|
1212
|
+
return false;
|
|
1213
|
+
}
|
|
1214
|
+
if (parsed.isWildcard) {
|
|
1215
|
+
return matchWildcardPattern(patternSegments, pathSegments);
|
|
1216
|
+
}
|
|
1217
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
1218
|
+
if (!matchSegment(patternSegments[i], pathSegments[i])) {
|
|
1219
|
+
return false;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
function findMatchingRoute(routes, path) {
|
|
1225
|
+
const matchingRoutes = [];
|
|
1226
|
+
for (const route of routes) {
|
|
1227
|
+
if (matchRoute(route.pattern, path)) {
|
|
1228
|
+
const parsed = parseRoutePattern(route.pattern);
|
|
1229
|
+
matchingRoutes.push({ route, parsed });
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
if (matchingRoutes.length === 0) {
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
matchingRoutes.sort((a, b) => {
|
|
1236
|
+
if (b.parsed.priority !== a.parsed.priority) {
|
|
1237
|
+
return b.parsed.priority - a.parsed.priority;
|
|
1238
|
+
}
|
|
1239
|
+
if (b.parsed.segments.length !== a.parsed.segments.length) {
|
|
1240
|
+
return b.parsed.segments.length - a.parsed.segments.length;
|
|
1241
|
+
}
|
|
1242
|
+
return b.route.pattern.length - a.route.pattern.length;
|
|
1243
|
+
});
|
|
1244
|
+
return matchingRoutes[0].route;
|
|
1245
|
+
}
|
|
1246
|
+
function containsWildcard(pattern) {
|
|
1247
|
+
return pattern.includes("*");
|
|
1248
|
+
}
|
|
1249
|
+
function validateRouteConfig(config) {
|
|
1250
|
+
const { route, routes } = config;
|
|
1251
|
+
if (!route && !routes) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
1254
|
+
if (route && routes) {
|
|
1255
|
+
return {
|
|
1256
|
+
code: "INVALID_ROUTE_CONFIG",
|
|
1257
|
+
message: "Cannot specify both 'route' and 'routes'. Use 'route' for a single exact path or 'routes' for multiple paths.",
|
|
1258
|
+
path: "route",
|
|
1259
|
+
value: { route, routes }
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
if (route && containsWildcard(route)) {
|
|
1263
|
+
return {
|
|
1264
|
+
code: "INVALID_ROUTE_PATTERN",
|
|
1265
|
+
message: `Wildcard routes must use the routes array, not 'route'. Use 'routes: ["/api/*"]' instead of 'route: "/api/*"'.`,
|
|
1266
|
+
path: "route",
|
|
1267
|
+
value: route,
|
|
1268
|
+
validOptions: ['routes: ["/api/*"]', 'routes: ["/api/users", "/api/posts"]']
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1112
1274
|
// src/encoding.ts
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1275
|
+
function safeBase64Decode2(str) {
|
|
1276
|
+
const padding = 4 - str.length % 4;
|
|
1277
|
+
if (padding !== 4) {
|
|
1278
|
+
str += "=".repeat(padding);
|
|
1279
|
+
}
|
|
1280
|
+
str = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
1281
|
+
return Buffer.from(str, "base64").toString("utf-8");
|
|
1282
|
+
}
|
|
1283
|
+
function safeBase64Encode2(str) {
|
|
1284
|
+
return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1285
|
+
}
|
|
1286
|
+
var base64JsonEncode = (data) => safeBase64Encode2(JSON.stringify(data));
|
|
1287
|
+
var base64JsonDecode = (encoded) => JSON.parse(safeBase64Decode2(encoded));
|
|
1288
|
+
var encodePaymentV2 = (payload) => base64JsonEncode(payload);
|
|
1289
|
+
var decodePaymentV2 = (encoded) => base64JsonDecode(encoded);
|
|
1290
|
+
var encodeSettlementV2 = (response) => base64JsonEncode(response);
|
|
1291
|
+
var decodeSettlementV2 = (encoded) => base64JsonDecode(encoded);
|
|
1119
1292
|
var isPaymentV2 = (payload) => "signature" in payload && typeof payload.signature === "object";
|
|
1120
1293
|
var isSettlementV2 = (response) => "status" in response;
|
|
1121
1294
|
|
|
@@ -1264,6 +1437,7 @@ export {
|
|
|
1264
1437
|
encodeSettlementV2,
|
|
1265
1438
|
encodeX402Response,
|
|
1266
1439
|
extractPaymentFromHeaders,
|
|
1440
|
+
findMatchingRoute,
|
|
1267
1441
|
fromAtomicUnits,
|
|
1268
1442
|
getAllCustomTokens,
|
|
1269
1443
|
getAllTokens,
|
|
@@ -1307,9 +1481,11 @@ export {
|
|
|
1307
1481
|
isX402V2Requirements,
|
|
1308
1482
|
isX402V2Settlement,
|
|
1309
1483
|
legacyToPaymentPayload,
|
|
1484
|
+
matchRoute,
|
|
1310
1485
|
networkToCaip2,
|
|
1311
1486
|
normalizeAddress,
|
|
1312
1487
|
normalizeNetworkName,
|
|
1488
|
+
parseRoutePattern,
|
|
1313
1489
|
parseSignature as parseSignatureV2,
|
|
1314
1490
|
registerToken,
|
|
1315
1491
|
resolveFacilitator,
|
|
@@ -1321,5 +1497,6 @@ export {
|
|
|
1321
1497
|
unregisterToken,
|
|
1322
1498
|
validateAcceptConfig,
|
|
1323
1499
|
validatePaymentConfig,
|
|
1500
|
+
validateRouteConfig,
|
|
1324
1501
|
validateTransferWithAuthorization
|
|
1325
1502
|
};
|
package/dist/types/api.d.ts
CHANGED
|
@@ -194,3 +194,59 @@ export interface ValidationError {
|
|
|
194
194
|
/** Valid options */
|
|
195
195
|
validOptions?: string[];
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Route pattern type for matching paths
|
|
199
|
+
*/
|
|
200
|
+
export type RoutePattern = string;
|
|
201
|
+
/**
|
|
202
|
+
* Route matcher function type
|
|
203
|
+
*/
|
|
204
|
+
export type RouteMatcher = (path: string) => boolean;
|
|
205
|
+
/**
|
|
206
|
+
* Route configuration with associated config data
|
|
207
|
+
*/
|
|
208
|
+
export interface RouteConfig<T = unknown> {
|
|
209
|
+
/** Route pattern (e.g., "/api/users", "/api/*", "/api/users/:id") */
|
|
210
|
+
pattern: RoutePattern;
|
|
211
|
+
/** Configuration associated with this route */
|
|
212
|
+
config: T;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Parsed route pattern information
|
|
216
|
+
*/
|
|
217
|
+
export interface ParsedPattern {
|
|
218
|
+
/** Route segments split by "/" */
|
|
219
|
+
segments: string[];
|
|
220
|
+
/** Whether this pattern contains a wildcard */
|
|
221
|
+
isWildcard: boolean;
|
|
222
|
+
/** Whether this pattern contains parameters (e.g., :id) */
|
|
223
|
+
isParametrized: boolean;
|
|
224
|
+
/** Parameter names extracted from the pattern */
|
|
225
|
+
paramNames: string[];
|
|
226
|
+
/** Match priority (higher = more specific) */
|
|
227
|
+
priority: number;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Route input configuration for middleware
|
|
231
|
+
*/
|
|
232
|
+
export interface RouteInputConfig {
|
|
233
|
+
/** Single exact route (no wildcards allowed) */
|
|
234
|
+
route?: string;
|
|
235
|
+
/** Multiple routes (allows wildcards) */
|
|
236
|
+
routes?: string[];
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Route-specific validation error details
|
|
240
|
+
*/
|
|
241
|
+
export interface RouteValidationError {
|
|
242
|
+
/** Error code (custom string, not PaymentErrorCode) */
|
|
243
|
+
code: string;
|
|
244
|
+
/** Error message */
|
|
245
|
+
message: string;
|
|
246
|
+
/** Path to the invalid value */
|
|
247
|
+
path?: string;
|
|
248
|
+
/** Invalid value */
|
|
249
|
+
value?: unknown;
|
|
250
|
+
/** Valid options */
|
|
251
|
+
validOptions?: string[];
|
|
252
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension Hook System Types
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic hook system for x402 clients to register
|
|
5
|
+
* extension handlers that can modify payment payloads or respond
|
|
6
|
+
* to payment requirements.
|
|
7
|
+
*
|
|
8
|
+
* Hooks allow extensibility without custom code in each client package.
|
|
9
|
+
*/
|
|
10
|
+
import type { PaymentPayloadV2, PaymentRequirementsV2, Extensions, Address } from "./v2";
|
|
11
|
+
export interface PaymentRequiredContext {
|
|
12
|
+
url: RequestInfo | URL;
|
|
13
|
+
requestInit: RequestInit | undefined;
|
|
14
|
+
requirements: PaymentRequirementsV2;
|
|
15
|
+
serverExtensions: Extensions | undefined;
|
|
16
|
+
fromAddress: Address;
|
|
17
|
+
nonce: `0x${string}`;
|
|
18
|
+
validBefore: number;
|
|
19
|
+
}
|
|
20
|
+
export interface PaymentPayloadContext<TWallet = unknown> {
|
|
21
|
+
payload: PaymentPayloadV2;
|
|
22
|
+
requirements: PaymentRequirementsV2;
|
|
23
|
+
wallet: TWallet;
|
|
24
|
+
paymentContext: PaymentRequiredContext;
|
|
25
|
+
}
|
|
26
|
+
export type HookResult = void | Promise<void>;
|
|
27
|
+
export type OnPaymentRequiredHook<TWallet = unknown> = (context: PaymentRequiredContext) => HookResult;
|
|
28
|
+
export type BeforePaymentHook<TWallet = unknown> = (context: PaymentPayloadContext<TWallet>) => HookResult;
|
|
29
|
+
export type ExtensionHook<TWallet = unknown> = OnPaymentRequiredHook<TWallet> | BeforePaymentHook<TWallet>;
|
|
30
|
+
export interface HookConfig<TWallet = unknown> {
|
|
31
|
+
hook: ExtensionHook<TWallet>;
|
|
32
|
+
priority?: number;
|
|
33
|
+
name?: string;
|
|
34
|
+
}
|
|
35
|
+
export type HookRegistry<TWallet = unknown> = Record<string, HookConfig<TWallet>>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type RoutePattern = string;
|
|
2
|
+
export type RouteMatcher = (path: string) => boolean;
|
|
3
|
+
export interface RouteConfig<T = unknown> {
|
|
4
|
+
pattern: RoutePattern;
|
|
5
|
+
config: T;
|
|
6
|
+
}
|
|
7
|
+
export interface ParsedPattern {
|
|
8
|
+
segments: string[];
|
|
9
|
+
isWildcard: boolean;
|
|
10
|
+
isParametrized: boolean;
|
|
11
|
+
paramNames: string[];
|
|
12
|
+
priority: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseRoutePattern(pattern: string): ParsedPattern;
|
|
15
|
+
export declare function matchRoute(pattern: string, path: string): boolean;
|
|
16
|
+
export declare function findMatchingRoute<T>(routes: RouteConfig<T>[], path: string): RouteConfig<T> | null;
|
|
17
|
+
export interface RouteInputConfig {
|
|
18
|
+
route?: string;
|
|
19
|
+
routes?: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface RouteValidationError {
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
path?: string;
|
|
25
|
+
value?: unknown;
|
|
26
|
+
validOptions?: string[];
|
|
27
|
+
}
|
|
28
|
+
export declare function validateRouteConfig(config: RouteInputConfig): RouteValidationError | null;
|