@weftlabs/sdk 0.25.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.
Files changed (124) hide show
  1. package/README.md +499 -0
  2. package/dist/client-C9_zh3Qg.d.mts +11 -0
  3. package/dist/client-C9_zh3Qg.d.ts +11 -0
  4. package/dist/facilitator/index.d.mts +18 -0
  5. package/dist/facilitator/index.d.ts +18 -0
  6. package/dist/facilitator/index.js +180 -0
  7. package/dist/facilitator/index.js.map +1 -0
  8. package/dist/facilitator/index.mjs +149 -0
  9. package/dist/facilitator/index.mjs.map +1 -0
  10. package/dist/facilitator/middleware/index.d.mts +539 -0
  11. package/dist/facilitator/middleware/index.d.ts +539 -0
  12. package/dist/facilitator/middleware/index.js +1522 -0
  13. package/dist/facilitator/middleware/index.js.map +1 -0
  14. package/dist/facilitator/middleware/index.mjs +1493 -0
  15. package/dist/facilitator/middleware/index.mjs.map +1 -0
  16. package/dist/index.d.mts +5667 -0
  17. package/dist/index.d.ts +5667 -0
  18. package/dist/index.js +5555 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/index.mjs +5553 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/dist/server.d.mts +8 -0
  23. package/dist/server.d.ts +8 -0
  24. package/dist/server.js +1587 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/server.mjs +1574 -0
  27. package/dist/server.mjs.map +1 -0
  28. package/docs/APIKeysApi.md +219 -0
  29. package/docs/AccountApi.md +72 -0
  30. package/docs/AccountBootstrapApproval.md +36 -0
  31. package/docs/AccountBootstrapCreated.md +44 -0
  32. package/docs/AccountBootstrapCreatedApproval.md +38 -0
  33. package/docs/AccountBootstrapCreatedResponse.md +32 -0
  34. package/docs/AccountBootstrapRequest.md +42 -0
  35. package/docs/AccountBootstrapStatus.md +40 -0
  36. package/docs/AccountBootstrapStatusResponse.md +32 -0
  37. package/docs/AccountDetails.md +43 -0
  38. package/docs/AgentBootstrapApi.md +223 -0
  39. package/docs/ApiKey.md +42 -0
  40. package/docs/ApiKeyCreated.md +40 -0
  41. package/docs/ApiKeyCreatedResponse.md +32 -0
  42. package/docs/ApiKeyListResponse.md +34 -0
  43. package/docs/AuthApi.md +408 -0
  44. package/docs/AuthResponse.md +32 -0
  45. package/docs/AuthResponseData.md +36 -0
  46. package/docs/BalanceApi.md +73 -0
  47. package/docs/BalanceResponse.md +45 -0
  48. package/docs/ConfirmRequest.md +32 -0
  49. package/docs/CreateApiKeyRequest.md +32 -0
  50. package/docs/DocumentationApi.md +65 -0
  51. package/docs/EnrolledResource.md +59 -0
  52. package/docs/ErrorResponse.md +33 -0
  53. package/docs/Fetch403Response.md +44 -0
  54. package/docs/FetchApi.md +91 -0
  55. package/docs/FetchBalanceSnapshot.md +41 -0
  56. package/docs/FetchErrorResponse.md +41 -0
  57. package/docs/FetchRequest.md +46 -0
  58. package/docs/FetchRequestBody.md +31 -0
  59. package/docs/FetchResponse.md +49 -0
  60. package/docs/InsufficientScopeResponse.md +37 -0
  61. package/docs/MeApiKey.md +40 -0
  62. package/docs/MeApiKeyCreator.md +37 -0
  63. package/docs/MeResponse.md +32 -0
  64. package/docs/MeResponseData.md +56 -0
  65. package/docs/MessageResponse.md +32 -0
  66. package/docs/MessageResponseData.md +32 -0
  67. package/docs/ModelError.md +39 -0
  68. package/docs/Pagination.md +38 -0
  69. package/docs/PasswordResetRequest.md +32 -0
  70. package/docs/PasswordUpdateRequest.md +36 -0
  71. package/docs/Payment.md +56 -0
  72. package/docs/PaymentListResponse.md +34 -0
  73. package/docs/PaymentResponse.md +32 -0
  74. package/docs/PaymentsApi.md +154 -0
  75. package/docs/PrincipalWallet.md +34 -0
  76. package/docs/PromoBalance.md +37 -0
  77. package/docs/Purchase.md +58 -0
  78. package/docs/PurchaseArtifact.md +38 -0
  79. package/docs/PurchaseListResponse.md +34 -0
  80. package/docs/PurchaseResponse.md +32 -0
  81. package/docs/PurchasesApi.md +157 -0
  82. package/docs/RateLimitResponse.md +34 -0
  83. package/docs/ResendConfirmationRequest.md +32 -0
  84. package/docs/ResourceEnrollmentRequest.md +39 -0
  85. package/docs/ResourceEnrollmentResponse.md +32 -0
  86. package/docs/ResourceStats.md +45 -0
  87. package/docs/ResourcesApi.md +77 -0
  88. package/docs/SearchAccessMethod.md +52 -0
  89. package/docs/SearchApi.md +86 -0
  90. package/docs/SearchCapabilityRef.md +34 -0
  91. package/docs/SearchCuratedCallability.md +34 -0
  92. package/docs/SearchCuratedExecution.md +32 -0
  93. package/docs/SearchCuratedOperation.md +36 -0
  94. package/docs/SearchCuratedService.md +34 -0
  95. package/docs/SearchCuratedSource.md +36 -0
  96. package/docs/SearchEndpointCall.md +39 -0
  97. package/docs/SearchEndpointHit.md +74 -0
  98. package/docs/SearchEndpointPrice.md +41 -0
  99. package/docs/SearchErrorResponse.md +35 -0
  100. package/docs/SearchExecutionModeFilter.md +34 -0
  101. package/docs/SearchFilterSpec.md +49 -0
  102. package/docs/SearchMethodFilter.md +34 -0
  103. package/docs/SearchPaymentOffer.md +47 -0
  104. package/docs/SearchPriceAtomicFilter.md +41 -0
  105. package/docs/SearchPriceUsdFilter.md +41 -0
  106. package/docs/SearchProtocolFilter.md +35 -0
  107. package/docs/SearchProviderRef.md +36 -0
  108. package/docs/SearchRequest.md +36 -0
  109. package/docs/SearchResourceTypeFilter.md +35 -0
  110. package/docs/SearchResponse.md +67 -0
  111. package/docs/SearchResponseWarningsInner.md +38 -0
  112. package/docs/SearchResult.md +38 -0
  113. package/docs/SearchStringSetFilter.md +34 -0
  114. package/docs/SearchWeftFetchCompatibility.md +36 -0
  115. package/docs/SignInRequest.md +34 -0
  116. package/docs/SignUpRequest.md +36 -0
  117. package/docs/SpendingPolicy.md +36 -0
  118. package/docs/User.md +36 -0
  119. package/docs/UserPrincipal.md +49 -0
  120. package/docs/Wallet.md +42 -0
  121. package/examples/charge-api-hono.mjs +40 -0
  122. package/examples/charge-api.mjs +43 -0
  123. package/examples/quickstart.mjs +16 -0
  124. package/package.json +105 -0
@@ -0,0 +1,1493 @@
1
+ // src/facilitator/middleware/express.ts
2
+ import {
3
+ SETTLEMENT_OVERRIDES_HEADER,
4
+ withPrivateCacheControl,
5
+ x402HTTPResourceServer,
6
+ x402ResourceServer
7
+ } from "@x402/core/server";
8
+
9
+ // src/facilitator/client.ts
10
+ import { HTTPFacilitatorClient } from "@x402/core/server";
11
+ var FACILITATOR_UNAVAILABLE_ERROR = "weft:facilitator-settle-unavailable";
12
+ var X402_FACILITATOR_URL = "https://x402.weft.network";
13
+ var X402_FACILITATOR_URL_ENV = "X402_FACILITATOR_URL";
14
+ var FacilitatorUnavailableError = class extends Error {
15
+ constructor() {
16
+ super(FACILITATOR_UNAVAILABLE_ERROR);
17
+ this.statusCode = 503;
18
+ this.name = "FacilitatorUnavailableError";
19
+ }
20
+ };
21
+ function isUnavailableSettleError(error) {
22
+ if (!(error instanceof Error)) return false;
23
+ const candidate = error;
24
+ return candidate.name === "SettleError" && candidate.statusCode === 503 && "errorReason" in candidate && "errorMessage" in candidate && "payer" in candidate && "transaction" in candidate && "network" in candidate && !(candidate.errorReason === "settlement_pending" && candidate.transaction);
25
+ }
26
+ var WeftHTTPFacilitatorClient = class extends HTTPFacilitatorClient {
27
+ async settle(paymentPayload, paymentRequirements) {
28
+ try {
29
+ return await super.settle(paymentPayload, paymentRequirements);
30
+ } catch (error) {
31
+ if (isUnavailableSettleError(error)) {
32
+ throw new FacilitatorUnavailableError();
33
+ }
34
+ throw error;
35
+ }
36
+ }
37
+ };
38
+ function validateUrl(url) {
39
+ if (!url || url.trim() === "") {
40
+ throw new Error("Invalid URL: URL cannot be empty");
41
+ }
42
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
43
+ throw new Error(
44
+ `Invalid URL format: URL must start with http:// or https://, got: ${url}`
45
+ );
46
+ }
47
+ }
48
+ function resolveUrl(config) {
49
+ if (config?.url) {
50
+ return config.url;
51
+ }
52
+ if (typeof process !== "undefined" && process.env?.[X402_FACILITATOR_URL_ENV]) {
53
+ return process.env[X402_FACILITATOR_URL_ENV];
54
+ }
55
+ return X402_FACILITATOR_URL;
56
+ }
57
+ function assertPathKeyedAuthHeaders(headers) {
58
+ const isHeaderObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
59
+ const hasPathKey = ["verify", "settle", "supported", "bazaar"].some(
60
+ (key) => isHeaderObject(headers[key])
61
+ );
62
+ const looksFlat = !hasPathKey && Object.values(headers).some((value) => !isHeaderObject(value));
63
+ if (looksFlat) {
64
+ throw new Error(
65
+ 'createAuthHeaders must return an object keyed by facilitator path, e.g. { verify: { Authorization: "..." }, settle: { ... }, supported: { ... } }, but received a flat headers object.'
66
+ );
67
+ }
68
+ }
69
+ function mergeSellerWins(derived, seller) {
70
+ const merged = { ...derived };
71
+ for (const [name, value] of Object.entries(seller ?? {})) {
72
+ for (const existing of Object.keys(merged)) {
73
+ if (existing.toLowerCase() === name.toLowerCase()) {
74
+ delete merged[existing];
75
+ }
76
+ }
77
+ merged[name] = value;
78
+ }
79
+ return merged;
80
+ }
81
+ function createFacilitatorClient(config, weftAuthHeaders) {
82
+ const url = resolveUrl(config);
83
+ validateUrl(url);
84
+ const sellerCreateAuthHeaders = config?.createAuthHeaders;
85
+ const derivedByPath = Object.entries(weftAuthHeaders ?? {}).filter(
86
+ ([, headers]) => headers !== void 0 && Object.keys(headers).length > 0
87
+ );
88
+ const createAuthHeaders = derivedByPath.length > 0 ? async () => {
89
+ const sellerHeaders = sellerCreateAuthHeaders ? await sellerCreateAuthHeaders() : void 0;
90
+ if (sellerHeaders) {
91
+ assertPathKeyedAuthHeaders(sellerHeaders);
92
+ }
93
+ const merged = { ...sellerHeaders };
94
+ for (const [path, derived] of derivedByPath) {
95
+ merged[path] = mergeSellerWins(derived, sellerHeaders?.[path]);
96
+ }
97
+ return merged;
98
+ } : sellerCreateAuthHeaders;
99
+ return new WeftHTTPFacilitatorClient({
100
+ url,
101
+ createAuthHeaders
102
+ });
103
+ }
104
+
105
+ // package.json
106
+ var version = "0.25.0";
107
+
108
+ // src/facilitator/middleware/product.ts
109
+ var PRODUCT_TYPES = ["api", "agent", "mcp"];
110
+ var WEFT_TYPE_TAG_PREFIX = "weft:type:";
111
+ var MAX_TAGS = 5;
112
+ var MAX_TAG_CHARS = 32;
113
+ var MAX_DIMENSIONS = 8;
114
+ var MAX_DIMENSION_CHARS = 64;
115
+ var DIMENSION_NAME = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
116
+ var MAX_SERVICE_NAME_CHARS = 32;
117
+ var MAX_ICON_URL_CHARS = 2048;
118
+ var PRINTABLE_ASCII = /^[\x20-\x7e]+$/;
119
+ var WEFT_PRODUCT_EXTENSION_KEY = "weft.product";
120
+ var WEFT_PRODUCT_INFO_SCHEMA = {
121
+ $schema: "https://json-schema.org/draft/2020-12/schema",
122
+ type: "object",
123
+ properties: {
124
+ kind: { type: "string", enum: [...PRODUCT_TYPES] },
125
+ product_id: { type: "string", minLength: 1 },
126
+ manifest_hash: { type: "string", minLength: 1 }
127
+ },
128
+ additionalProperties: false
129
+ };
130
+ function productTypeTag(type) {
131
+ return `${WEFT_TYPE_TAG_PREFIX}${type}`;
132
+ }
133
+ function isDeclared(value) {
134
+ return value !== void 0 && value !== null;
135
+ }
136
+ function typeName(value) {
137
+ if (value === null) {
138
+ return "null";
139
+ }
140
+ if (Array.isArray(value)) {
141
+ return "an array";
142
+ }
143
+ return `a ${typeof value}`;
144
+ }
145
+ function show(value) {
146
+ try {
147
+ return JSON.stringify(value) ?? String(value);
148
+ } catch {
149
+ return typeName(value);
150
+ }
151
+ }
152
+ function isSingleRoute(routes) {
153
+ return "accepts" in routes;
154
+ }
155
+ function createWarn() {
156
+ const seen = /* @__PURE__ */ new Set();
157
+ return (message, dedupeKey) => {
158
+ const key = dedupeKey ?? message;
159
+ if (seen.has(key)) {
160
+ return;
161
+ }
162
+ seen.add(key);
163
+ console.warn(`[weft] ${message}`);
164
+ };
165
+ }
166
+ function asString(field, value, warn) {
167
+ if (!isDeclared(value)) {
168
+ return void 0;
169
+ }
170
+ if (typeof value !== "string") {
171
+ warn(
172
+ `ignoring ${field} ${show(value)}: expected a string, got ${typeName(value)}`
173
+ );
174
+ return void 0;
175
+ }
176
+ return value;
177
+ }
178
+ function isReservedTypeTag(tag) {
179
+ return tag.trim().toLowerCase().startsWith(WEFT_TYPE_TAG_PREFIX);
180
+ }
181
+ function resolveType(value, field, warn) {
182
+ if (!isDeclared(value)) {
183
+ return void 0;
184
+ }
185
+ if (typeof value !== "string" || !PRODUCT_TYPES.includes(value)) {
186
+ warn(
187
+ `ignoring ${field} ${show(value)}: expected one of ${PRODUCT_TYPES.join(", ")}`
188
+ );
189
+ return void 0;
190
+ }
191
+ return value;
192
+ }
193
+ function asTagList(value, field, warn) {
194
+ if (!isDeclared(value)) {
195
+ return void 0;
196
+ }
197
+ if (!Array.isArray(value)) {
198
+ warn(
199
+ `ignoring ${field} ${show(value)}: expected an array of strings, got ${typeName(value)}`
200
+ );
201
+ return void 0;
202
+ }
203
+ return value.filter((entry) => {
204
+ if (typeof entry === "string") {
205
+ return true;
206
+ }
207
+ warn(
208
+ `dropping tag ${show(entry)}: expected a string, got ${typeName(entry)}`
209
+ );
210
+ return false;
211
+ });
212
+ }
213
+ function resolveTags(routeTags, identityTags, type, warn) {
214
+ const declared = asTagList(routeTags, "route tags", warn) ?? asTagList(identityTags, "tags", warn);
215
+ if (declared === void 0 && type === void 0) {
216
+ return void 0;
217
+ }
218
+ const sellerTags = (declared ?? []).filter((tag) => {
219
+ if (!isReservedTypeTag(tag)) {
220
+ return true;
221
+ }
222
+ warn(
223
+ `dropping reserved tag ${show(tag)}: declare the product kind with \`type\` (${PRODUCT_TYPES.join(", ")}) on the middleware config or on the route`
224
+ );
225
+ return false;
226
+ });
227
+ const malformed = [];
228
+ const carried = [
229
+ ...type !== void 0 ? [productTypeTag(type)] : [],
230
+ ...sellerTags
231
+ ].filter((tag) => {
232
+ if (tag.length <= MAX_TAG_CHARS && PRINTABLE_ASCII.test(tag)) {
233
+ return true;
234
+ }
235
+ malformed.push(tag);
236
+ return false;
237
+ });
238
+ if (malformed.length > 0) {
239
+ warn(
240
+ `dropping ${malformed.length} tag(s) the x402 protocol cannot carry (max ${MAX_TAG_CHARS} printable-ASCII characters each): ${malformed.join(", ")}`
241
+ );
242
+ }
243
+ const deduped = [...new Set(carried)];
244
+ if (deduped.length > MAX_TAGS) {
245
+ const dropped = deduped.slice(MAX_TAGS);
246
+ warn(
247
+ `dropping ${dropped.length} tag(s): the x402 protocol carries ${MAX_TAGS}` + (type !== void 0 ? " and the declared type uses one of them" : "") + `. Dropped: ${dropped.join(", ")}`
248
+ );
249
+ }
250
+ const bounded = deduped.slice(0, MAX_TAGS);
251
+ return bounded.length > 0 ? bounded : void 0;
252
+ }
253
+ function resolveServiceName(routeServiceName, identityName, warn) {
254
+ const declared = asString("route serviceName", routeServiceName, warn) ?? asString("name", identityName, warn);
255
+ if (declared === void 0) {
256
+ return void 0;
257
+ }
258
+ if (!PRINTABLE_ASCII.test(declared)) {
259
+ warn(
260
+ `dropping product name ${show(declared)}: the x402 protocol carries 1-${MAX_SERVICE_NAME_CHARS} printable-ASCII characters (U+0020-U+007E)`
261
+ );
262
+ return void 0;
263
+ }
264
+ if (declared.length > MAX_SERVICE_NAME_CHARS) {
265
+ const truncated = declared.slice(0, MAX_SERVICE_NAME_CHARS);
266
+ warn(
267
+ `product name ${show(declared)} is ${declared.length} characters; the x402 protocol carries ${MAX_SERVICE_NAME_CHARS}, so it travels as ${show(truncated)}`
268
+ );
269
+ return truncated;
270
+ }
271
+ return declared;
272
+ }
273
+ function resolveIconUrl(routeIconUrl, identityIconUrl, warn) {
274
+ const declared = asString("route iconUrl", routeIconUrl, warn) ?? asString("iconUrl", identityIconUrl, warn);
275
+ if (declared === void 0) {
276
+ return void 0;
277
+ }
278
+ if (declared.length > MAX_ICON_URL_CHARS) {
279
+ warn(
280
+ `dropping iconUrl: ${declared.length} characters exceeds the ${MAX_ICON_URL_CHARS} the x402 protocol carries`
281
+ );
282
+ return void 0;
283
+ }
284
+ let scheme;
285
+ try {
286
+ scheme = new URL(declared).protocol;
287
+ } catch {
288
+ warn(
289
+ `dropping iconUrl ${show(declared)}: expected an absolute http or https URL`
290
+ );
291
+ return void 0;
292
+ }
293
+ if (scheme !== "http:" && scheme !== "https:") {
294
+ warn(
295
+ `dropping iconUrl ${show(declared)}: only http and https are carried (a dashboard renders this URL)`
296
+ );
297
+ return void 0;
298
+ }
299
+ return declared;
300
+ }
301
+ function resolveDimensions(value, warn) {
302
+ const declared = asTagList(value, "dimensions", warn);
303
+ if (declared === void 0) {
304
+ return void 0;
305
+ }
306
+ const malformed = [];
307
+ const named = declared.filter((name) => {
308
+ if (name.length <= MAX_DIMENSION_CHARS && DIMENSION_NAME.test(name)) {
309
+ return true;
310
+ }
311
+ malformed.push(name);
312
+ return false;
313
+ });
314
+ if (malformed.length > 0) {
315
+ warn(
316
+ `dropping ${malformed.length} dimension(s) that do not name a field (max ${MAX_DIMENSION_CHARS} characters, starting with a letter or underscore): ${malformed.join(", ")}`
317
+ );
318
+ }
319
+ const deduped = [...new Set(named)];
320
+ if (deduped.length > MAX_DIMENSIONS) {
321
+ warn(
322
+ `dropping ${deduped.length - MAX_DIMENSIONS} dimension(s): at most ${MAX_DIMENSIONS} travel. Dropped: ${deduped.slice(MAX_DIMENSIONS).join(", ")}`
323
+ );
324
+ }
325
+ const bounded = deduped.slice(0, MAX_DIMENSIONS);
326
+ return bounded.length > 0 ? bounded : void 0;
327
+ }
328
+ function resolveOpaqueString(field, value, warn) {
329
+ const declared = asString(field, value, warn);
330
+ if (declared === void 0) {
331
+ return void 0;
332
+ }
333
+ const trimmed = declared.trim();
334
+ if (trimmed === "") {
335
+ warn(`ignoring empty ${field}`);
336
+ return void 0;
337
+ }
338
+ return trimmed;
339
+ }
340
+ function resolveProductExtensions(routeExtensions, type, declaration, warn) {
341
+ const productId = resolveOpaqueString(
342
+ "productId",
343
+ declaration.productId,
344
+ warn
345
+ );
346
+ const manifestHash = resolveOpaqueString(
347
+ "manifestHash",
348
+ declaration.manifestHash,
349
+ warn
350
+ );
351
+ const info = {
352
+ ...type !== void 0 && { kind: type },
353
+ ...productId !== void 0 && { product_id: productId },
354
+ ...manifestHash !== void 0 && { manifest_hash: manifestHash }
355
+ };
356
+ if (Object.keys(info).length === 0) {
357
+ return void 0;
358
+ }
359
+ if (routeExtensions !== void 0) {
360
+ if (typeof routeExtensions !== "object" || routeExtensions === null || Array.isArray(routeExtensions)) {
361
+ warn(
362
+ `route extensions ${show(routeExtensions)} are ${typeName(routeExtensions)}, not an object; leaving them untouched and skipping the ${WEFT_PRODUCT_EXTENSION_KEY} declaration for this route`
363
+ );
364
+ return void 0;
365
+ }
366
+ if (WEFT_PRODUCT_EXTENSION_KEY in routeExtensions) {
367
+ return void 0;
368
+ }
369
+ }
370
+ return {
371
+ ...routeExtensions,
372
+ [WEFT_PRODUCT_EXTENSION_KEY]: { info, schema: WEFT_PRODUCT_INFO_SCHEMA }
373
+ };
374
+ }
375
+ function applyToRoute(route, identity, warn) {
376
+ if (typeof route !== "object" || route === null) {
377
+ warn(
378
+ `ignoring route ${show(route)}: expected a route config object, got ${typeName(route)}`
379
+ );
380
+ return route;
381
+ }
382
+ const {
383
+ type: declaredType,
384
+ serviceName: declaredServiceName,
385
+ tags: declaredTags,
386
+ iconUrl: declaredIconUrl,
387
+ ...rest
388
+ } = route;
389
+ const type = resolveType(declaredType, "route type", warn) ?? resolveType(identity.type, "type", warn);
390
+ const serviceName = resolveServiceName(
391
+ declaredServiceName,
392
+ identity.name,
393
+ warn
394
+ );
395
+ const iconUrl = resolveIconUrl(declaredIconUrl, identity.iconUrl, warn);
396
+ const tags = resolveTags(declaredTags, identity.tags, type, warn);
397
+ const extensions = resolveProductExtensions(
398
+ rest.extensions,
399
+ type,
400
+ identity,
401
+ warn
402
+ );
403
+ return {
404
+ ...rest,
405
+ ...serviceName !== void 0 && { serviceName },
406
+ ...tags !== void 0 && { tags },
407
+ ...iconUrl !== void 0 && { iconUrl },
408
+ ...extensions !== void 0 && { extensions }
409
+ };
410
+ }
411
+ function hasProductIdentity(identity) {
412
+ return isDeclared(identity.name) || isDeclared(identity.type) || isDeclared(identity.tags) || isDeclared(identity.iconUrl) || isDeclared(identity.productId) || isDeclared(identity.manifestHash);
413
+ }
414
+ function hasRouteIdentity(routes) {
415
+ if (isSingleRoute(routes)) {
416
+ return isDeclared(routes.type);
417
+ }
418
+ return Object.values(routes).some((route) => isDeclared(route?.type));
419
+ }
420
+ function sanitizeProductIdentity(identity) {
421
+ const silent = () => void 0;
422
+ const type = resolveType(identity.type, "type", silent);
423
+ const name = resolveServiceName(void 0, identity.name, silent);
424
+ const iconUrl = resolveIconUrl(void 0, identity.iconUrl, silent);
425
+ const tags = resolveTags(void 0, identity.tags, type, silent)?.filter(
426
+ (tag) => !isReservedTypeTag(tag)
427
+ );
428
+ return {
429
+ ...name !== void 0 && { name },
430
+ ...type !== void 0 && { type },
431
+ ...tags !== void 0 && tags.length > 0 && { tags },
432
+ ...iconUrl !== void 0 && { iconUrl }
433
+ };
434
+ }
435
+ function applyProductIdentity(routes, identity) {
436
+ if (typeof routes !== "object" || routes === null) {
437
+ return routes;
438
+ }
439
+ if (!hasProductIdentity(identity) && !hasRouteIdentity(routes)) {
440
+ return routes;
441
+ }
442
+ const warn = createWarn();
443
+ if (isSingleRoute(routes)) {
444
+ return applyToRoute(routes, identity, warn);
445
+ }
446
+ return Object.fromEntries(
447
+ Object.entries(routes).map(([pattern, route]) => [
448
+ pattern,
449
+ applyToRoute(route, identity, warn)
450
+ ])
451
+ );
452
+ }
453
+
454
+ // src/facilitator/middleware/handshake.ts
455
+ var WEFT_DECLARED_HEADER = "X-Weft-Declared";
456
+ function base64UrlEncode(value) {
457
+ const bytes = new TextEncoder().encode(value);
458
+ let binary = "";
459
+ for (const byte of bytes) {
460
+ binary += String.fromCharCode(byte);
461
+ }
462
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
463
+ }
464
+ function declaredIdentityValue(declaration) {
465
+ const declared = sanitizeProductIdentity(declaration);
466
+ const dimensions = resolveDimensions(declaration.dimensions, createWarn());
467
+ const payload = {
468
+ ...declared.name !== void 0 && { name: declared.name },
469
+ ...declared.type !== void 0 && { type: declared.type },
470
+ ...declared.tags !== void 0 && { tags: declared.tags },
471
+ ...declared.iconUrl !== void 0 && { icon_url: declared.iconUrl },
472
+ ...dimensions !== void 0 && { dimensions }
473
+ };
474
+ if (Object.keys(payload).length === 0) {
475
+ return void 0;
476
+ }
477
+ return base64UrlEncode(JSON.stringify(payload));
478
+ }
479
+ var HEADER_SAFE = /^[\x21-\x7e]+$/;
480
+ function resolveApiKey(apiKey) {
481
+ if (apiKey === void 0 || apiKey === null) {
482
+ return void 0;
483
+ }
484
+ if (typeof apiKey !== "string") {
485
+ console.warn(
486
+ `[weft] ignoring apiKey: expected a string, got ${typeof apiKey}`
487
+ );
488
+ return void 0;
489
+ }
490
+ const trimmed = apiKey.trim();
491
+ if (trimmed === "") {
492
+ console.warn("[weft] ignoring empty apiKey");
493
+ return void 0;
494
+ }
495
+ if (!HEADER_SAFE.test(trimmed)) {
496
+ console.warn(
497
+ "[weft] ignoring apiKey: it contains whitespace or non-printable characters that cannot travel in an HTTP header"
498
+ );
499
+ return void 0;
500
+ }
501
+ return trimmed;
502
+ }
503
+ var WEFT_API_KEY_HEADER = "X-API-Key";
504
+ function buildFacilitatorAuthHeaders(adapter, apiKey, declaration) {
505
+ const key = resolveApiKey(apiKey);
506
+ const supported = {
507
+ "User-Agent": `weft-sdk-${adapter}/${version}`
508
+ };
509
+ if (key !== void 0) {
510
+ supported["Authorization"] = `Bearer ${key}`;
511
+ }
512
+ const declared = declaredIdentityValue(declaration);
513
+ if (declared !== void 0) {
514
+ supported[WEFT_DECLARED_HEADER] = declared;
515
+ }
516
+ const settle = key !== void 0 ? { [WEFT_API_KEY_HEADER]: key } : void 0;
517
+ const verify = key !== void 0 ? { [WEFT_API_KEY_HEADER]: key } : void 0;
518
+ return {
519
+ supported,
520
+ ...settle !== void 0 && { settle },
521
+ ...verify !== void 0 && { verify }
522
+ };
523
+ }
524
+
525
+ // src/facilitator/middleware/extensions.ts
526
+ var WEFT_REQUEST_EXTENSION_KEY = "weft.request";
527
+ var WEFT_REQUEST_INFO_SCHEMA = {
528
+ $schema: "https://json-schema.org/draft/2020-12/schema",
529
+ title: "Weft per-request context",
530
+ description: "Seller-authored context for one paid request, for display only. Unauthenticated buyer input by the time it is read; key nothing on it.",
531
+ type: "object",
532
+ additionalProperties: true
533
+ };
534
+ var MAX_EXTENSION_BYTES = 16 * 1024;
535
+ function requestOf(context) {
536
+ const transport = context.transportContext;
537
+ return transport?.request;
538
+ }
539
+ function dropKey(context, key) {
540
+ delete context.paymentRequiredResponse.extensions?.[key];
541
+ return void 0;
542
+ }
543
+ function toJson(value) {
544
+ try {
545
+ return JSON.stringify(value);
546
+ } catch {
547
+ return void 0;
548
+ }
549
+ }
550
+ function byteLength(text) {
551
+ return new TextEncoder().encode(text).length;
552
+ }
553
+ function projectedBytes(context, key, value) {
554
+ const projected = {
555
+ ...context.paymentRequiredResponse.extensions,
556
+ [key]: value
557
+ };
558
+ const json = toJson(projected);
559
+ return json === void 0 ? void 0 : byteLength(json);
560
+ }
561
+ function dynamicExtension(key, warn) {
562
+ return {
563
+ key,
564
+ async enrichPaymentRequiredResponse(declaration, context) {
565
+ if (typeof declaration !== "function") {
566
+ return void 0;
567
+ }
568
+ const request = requestOf(context);
569
+ if (!request) {
570
+ warn(
571
+ `extensions[${key}] is a callback but no HTTP request context reached it; dropping the key from the challenge`,
572
+ `${key}:no-request`
573
+ );
574
+ return dropKey(context, key);
575
+ }
576
+ let resolved;
577
+ try {
578
+ resolved = await declaration(request);
579
+ } catch (error) {
580
+ warn(
581
+ `extensions[${key}] callback failed; dropping the key from the challenge: ` + (error instanceof Error ? error.message : String(error)),
582
+ `${key}:threw`
583
+ );
584
+ return dropKey(context, key);
585
+ }
586
+ if (resolved === void 0 || resolved === null) {
587
+ return dropKey(context, key);
588
+ }
589
+ const json = toJson(resolved);
590
+ if (json === void 0) {
591
+ warn(
592
+ `extensions[${key}] callback returned a value JSON cannot carry (a function, a circular reference, a BigInt or similar); dropping the key from the challenge`,
593
+ `${key}:unserializable`
594
+ );
595
+ return dropKey(context, key);
596
+ }
597
+ const wire = JSON.parse(json);
598
+ if (typeof wire !== "object" || wire === null || Array.isArray(wire)) {
599
+ warn(
600
+ `extensions[${key}] callback returned ${Array.isArray(wire) ? "an array" : `a ${typeof wire}`}; the x402 extensions channel carries objects, so the key is dropped from the challenge`,
601
+ `${key}:not-an-object`
602
+ );
603
+ return dropKey(context, key);
604
+ }
605
+ const value = key === WEFT_REQUEST_EXTENSION_KEY ? { info: wire, schema: WEFT_REQUEST_INFO_SCHEMA } : wire;
606
+ const bytes = projectedBytes(context, key, value) ?? byteLength(json);
607
+ if (bytes > MAX_EXTENSION_BYTES) {
608
+ warn(
609
+ `extensions[${key}] takes the challenge's extensions to ${bytes} bytes, over the ${MAX_EXTENSION_BYTES}-byte facilitator relay cap; dropping the key so the rest of the declaration still reaches settlement`,
610
+ `${key}:over-cap`
611
+ );
612
+ return dropKey(context, key);
613
+ }
614
+ return value;
615
+ }
616
+ };
617
+ }
618
+ function routeConfigs(routes) {
619
+ if (typeof routes !== "object" || routes === null) {
620
+ return [];
621
+ }
622
+ const configs = "accepts" in routes ? [routes] : Object.values(routes);
623
+ return configs.filter(
624
+ (config) => typeof config === "object" && config !== null
625
+ );
626
+ }
627
+ function dynamicExtensionKeys(routes) {
628
+ const keys = /* @__PURE__ */ new Set();
629
+ for (const config of routeConfigs(routes)) {
630
+ const extensions = config.extensions;
631
+ if (typeof extensions !== "object" || extensions === null) {
632
+ continue;
633
+ }
634
+ for (const [key, value] of Object.entries(extensions)) {
635
+ if (typeof value === "function") {
636
+ keys.add(key);
637
+ }
638
+ }
639
+ }
640
+ return [...keys];
641
+ }
642
+ function registerDynamicExtensions(server, routes) {
643
+ const keys = dynamicExtensionKeys(routes);
644
+ if (keys.length === 0) {
645
+ return;
646
+ }
647
+ const warn = createWarn();
648
+ if (typeof server.registerExtension !== "function") {
649
+ warn(
650
+ `this @x402/core build has no registerExtension, so per-request route extensions (${keys.join(", ")}) cannot be resolved and will not ship`
651
+ );
652
+ return;
653
+ }
654
+ for (const key of keys) {
655
+ server.registerExtension(dynamicExtension(key, warn));
656
+ }
657
+ }
658
+
659
+ // src/facilitator/middleware/replay.ts
660
+ import { decodePaymentSignatureHeader } from "@x402/core/http";
661
+ import { validatePaymentPayload } from "@x402/core/schemas";
662
+ function paymentResumeCandidate(context) {
663
+ if (!context.paymentHeader) return void 0;
664
+ try {
665
+ const paymentPayload = decodePaymentSignatureHeader(context.paymentHeader);
666
+ validatePaymentPayload(paymentPayload);
667
+ if (paymentPayload.x402Version !== 2) return void 0;
668
+ return {
669
+ paymentPayload,
670
+ paymentRequirements: paymentPayload.accepted
671
+ };
672
+ } catch {
673
+ return void 0;
674
+ }
675
+ }
676
+ function resumePaymentResult(resourceServer, resumed, context) {
677
+ return {
678
+ type: "payment-verified",
679
+ paymentPayload: resumed.paymentPayload,
680
+ paymentRequirements: resumed.paymentRequirements,
681
+ declaredExtensions: resumed.declaredExtensions,
682
+ beforeHandlerSettlement: resumed.beforeHandlerSettlement,
683
+ cancellationDispatcher: resumed.cancellationDispatcher ?? resourceServer.createPaymentCancellationDispatcher(
684
+ resumed.paymentPayload,
685
+ resumed.paymentRequirements,
686
+ resumed.declaredExtensions,
687
+ { request: context },
688
+ resumed.beforeHandlerSettlement ? ["before-handler"] : []
689
+ )
690
+ };
691
+ }
692
+
693
+ // src/facilitator/middleware/settlement.ts
694
+ import { decodePaymentResponseHeader } from "@x402/core/http";
695
+ var CORE_FACILITATOR_UNAVAILABLE = /^Facilitator settle failed \(503\):(?: |$)/;
696
+ function isFacilitatorUnavailable(errorReason) {
697
+ return errorReason === FACILITATOR_UNAVAILABLE_ERROR || CORE_FACILITATOR_UNAVAILABLE.test(errorReason ?? "");
698
+ }
699
+ function responseHeader(headers, name) {
700
+ const value = Object.entries(headers).find(
701
+ ([key]) => key.toLowerCase() === name
702
+ )?.[1];
703
+ return Array.isArray(value) ? value[0] : value;
704
+ }
705
+ function isFacilitatorUnavailableResponse(response) {
706
+ if (typeof response.body === "object" && response.body !== null) {
707
+ for (const field of ["errorReason", "error"]) {
708
+ if (field in response.body && typeof response.body[field] === "string" && isFacilitatorUnavailable(
709
+ response.body[field]
710
+ )) {
711
+ return true;
712
+ }
713
+ }
714
+ }
715
+ const paymentResponse = responseHeader(response.headers, "payment-response");
716
+ if (!paymentResponse) return false;
717
+ try {
718
+ return isFacilitatorUnavailable(
719
+ decodePaymentResponseHeader(paymentResponse).errorReason
720
+ );
721
+ } catch {
722
+ return false;
723
+ }
724
+ }
725
+ function isJsonResponse(response) {
726
+ const contentType = responseHeader(response.headers, "content-type");
727
+ return /^(application\/json|[^;]+\+json)(?:;|$)/i.test(contentType ?? "");
728
+ }
729
+ function serializeResponseBody(response) {
730
+ if (isJsonResponse(response)) return JSON.stringify(response.body ?? {});
731
+ if (response.body instanceof Blob || response.body instanceof ArrayBuffer || ArrayBuffer.isView(response.body)) {
732
+ return response.body;
733
+ }
734
+ return typeof response.body === "string" ? response.body : String(response.body ?? "");
735
+ }
736
+
737
+ // src/facilitator/middleware/express.ts
738
+ var UNSAFE_FAILURE_HEADERS = /* @__PURE__ */ new Set([
739
+ SETTLEMENT_OVERRIDES_HEADER.toLowerCase(),
740
+ "location",
741
+ "set-cookie"
742
+ ]);
743
+ function isUnsafeFailureHeader(name, value) {
744
+ const normalized = name.toLowerCase();
745
+ return UNSAFE_FAILURE_HEADERS.has(normalized) || normalized === "cache-control" && value !== void 0 && /\bpublic\b/i.test(
746
+ Array.isArray(value) ? value.join(",") : String(value)
747
+ );
748
+ }
749
+ var FACILITATOR_SYNC_RETRY_FLOOR_MS = 3e4;
750
+ var ExpressAdapter = class {
751
+ constructor(req) {
752
+ this.req = req;
753
+ }
754
+ getHeader(name) {
755
+ const value = this.req.header(name);
756
+ return Array.isArray(value) ? value[0] : value;
757
+ }
758
+ getMethod() {
759
+ return this.req.method;
760
+ }
761
+ getPath() {
762
+ return this.req.path;
763
+ }
764
+ getUrl() {
765
+ const host = this.req.headers.host || "localhost";
766
+ return `${this.req.protocol}://${host}${this.req.path}`;
767
+ }
768
+ getAcceptHeader() {
769
+ return this.getHeader("Accept") || "";
770
+ }
771
+ getUserAgent() {
772
+ return this.getHeader("User-Agent") || "";
773
+ }
774
+ getQueryParams() {
775
+ return this.req.query;
776
+ }
777
+ getQueryParam(name) {
778
+ return this.req.query[name];
779
+ }
780
+ getBody() {
781
+ return this.req.body;
782
+ }
783
+ };
784
+ function toHeaderRecord(headers) {
785
+ const record = {};
786
+ for (const [key, value] of Object.entries(headers)) {
787
+ if (value === void 0) continue;
788
+ record[key] = Array.isArray(value) ? value.join(",") : String(value);
789
+ }
790
+ return record;
791
+ }
792
+ function responseBodyFromBufferedCalls(calls) {
793
+ const chunks = [];
794
+ for (const [method, args] of calls) {
795
+ if (method !== "write" && method !== "end") continue;
796
+ const chunk = args[0];
797
+ if (chunk === void 0) continue;
798
+ if (typeof chunk === "string") {
799
+ const encoding = typeof args[1] === "string" ? args[1] : "utf8";
800
+ if (!Buffer.isEncoding(encoding)) return void 0;
801
+ chunks.push(Buffer.from(chunk, encoding));
802
+ } else if (Buffer.isBuffer(chunk) || chunk instanceof Uint8Array) {
803
+ chunks.push(Buffer.from(chunk));
804
+ } else {
805
+ return void 0;
806
+ }
807
+ }
808
+ return Buffer.concat(chunks);
809
+ }
810
+ function restoreHeaders(res, headers) {
811
+ for (const name of Object.keys(res.getHeaders())) res.removeHeader(name);
812
+ for (const [name, value] of Object.entries(headers)) {
813
+ if (value !== void 0) res.setHeader(name, value);
814
+ }
815
+ }
816
+ async function sendCoreResponse(res, response) {
817
+ res.status(response.status);
818
+ Object.entries(response.headers).forEach(([key, value]) => {
819
+ res.setHeader(key, value);
820
+ });
821
+ if (isJsonResponse(response)) {
822
+ res.json(response.body ?? {});
823
+ } else if (response.body instanceof Blob) {
824
+ res.send(Buffer.from(await response.body.arrayBuffer()));
825
+ } else if (response.body instanceof ArrayBuffer) {
826
+ res.send(Buffer.from(response.body));
827
+ } else if (ArrayBuffer.isView(response.body)) {
828
+ res.send(
829
+ Buffer.from(
830
+ response.body.buffer,
831
+ response.body.byteOffset,
832
+ response.body.byteLength
833
+ )
834
+ );
835
+ } else {
836
+ res.send(
837
+ typeof response.body === "string" ? response.body : String(response.body ?? "")
838
+ );
839
+ }
840
+ }
841
+ function sendFacilitatorUnavailable(res) {
842
+ res.removeHeader("payment-response");
843
+ res.removeHeader(SETTLEMENT_OVERRIDES_HEADER);
844
+ res.setHeader("retry-after", "1");
845
+ res.setHeader("cache-control", withPrivateCacheControl(null));
846
+ res.status(503).json({ error: "facilitator_unavailable" });
847
+ }
848
+ function weftPaymentMiddleware(routes, config) {
849
+ const facilitatorClient = createFacilitatorClient(
850
+ config?.facilitator,
851
+ buildFacilitatorAuthHeaders("express", config?.apiKey, config ?? {})
852
+ );
853
+ const resourceServer = new x402ResourceServer(facilitatorClient);
854
+ registerDynamicExtensions(resourceServer, routes);
855
+ if (config?.schemes) {
856
+ config.schemes.forEach(({ network, server: schemeServer }) => {
857
+ resourceServer.register(network, schemeServer);
858
+ });
859
+ }
860
+ const httpServer = new x402HTTPResourceServer(
861
+ resourceServer,
862
+ applyProductIdentity(routes, config ?? {})
863
+ );
864
+ if (config?.paywall) {
865
+ httpServer.registerPaywallProvider(config.paywall);
866
+ }
867
+ const syncOnStart = config?.syncFacilitatorOnStart ?? true;
868
+ let facilitatorSynced = false;
869
+ let syncInFlight = null;
870
+ let lastFailedSyncAt = 0;
871
+ const syncFacilitator = () => {
872
+ syncInFlight ?? (syncInFlight = httpServer.initialize().then(
873
+ () => {
874
+ facilitatorSynced = true;
875
+ },
876
+ (error) => {
877
+ lastFailedSyncAt = Date.now();
878
+ console.warn(
879
+ "[weft] facilitator sync failed; payment-protected routes degrade until a later attempt succeeds: " + (error instanceof Error ? error.message : String(error))
880
+ );
881
+ }
882
+ ).finally(() => {
883
+ syncInFlight = null;
884
+ }));
885
+ return syncInFlight;
886
+ };
887
+ let bootSync = syncOnStart ? syncFacilitator() : null;
888
+ return async (req, res, next) => {
889
+ const adapter = new ExpressAdapter(req);
890
+ const context = {
891
+ adapter,
892
+ path: req.path,
893
+ method: req.method,
894
+ paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
895
+ };
896
+ if (!httpServer.requiresPayment(context)) {
897
+ return next();
898
+ }
899
+ if (bootSync) {
900
+ await bootSync;
901
+ bootSync = null;
902
+ }
903
+ if (syncOnStart && !facilitatorSynced && Date.now() - lastFailedSyncAt >= FACILITATOR_SYNC_RETRY_FLOOR_MS) {
904
+ void syncFacilitator();
905
+ }
906
+ let result;
907
+ try {
908
+ const resumeCandidate = paymentResumeCandidate(context);
909
+ const resumedPayment = resumeCandidate ? await config?.resumeVerifiedPayment?.(context, resumeCandidate) : void 0;
910
+ result = resumedPayment ? resumePaymentResult(resourceServer, resumedPayment, context) : await httpServer.processHTTPRequest(context, config?.paywallConfig);
911
+ } catch (error) {
912
+ return next(error);
913
+ }
914
+ switch (result.type) {
915
+ case "no-payment-required":
916
+ return next();
917
+ case "payment-error": {
918
+ const { response } = result;
919
+ if (isFacilitatorUnavailableResponse(response)) {
920
+ sendFacilitatorUnavailable(res);
921
+ return;
922
+ }
923
+ await sendCoreResponse(res, response);
924
+ return;
925
+ }
926
+ case "payment-verified": {
927
+ const {
928
+ paymentPayload,
929
+ paymentRequirements,
930
+ declaredExtensions,
931
+ cancellationDispatcher,
932
+ beforeHandlerSettlement
933
+ } = result;
934
+ const originalWriteHead = res.writeHead.bind(res);
935
+ const originalWrite = res.write.bind(res);
936
+ const originalEnd = res.end.bind(res);
937
+ const originalFlushHeaders = res.flushHeaders.bind(res);
938
+ const originalSetHeader = res.setHeader.bind(res);
939
+ const originalRemoveHeader = res.removeHeader.bind(res);
940
+ const headersBeforeHandler = { ...res.getHeaders() };
941
+ let bufferedCalls = [];
942
+ let settled = false;
943
+ const failureHeaders = /* @__PURE__ */ new Map();
944
+ const restoreResponseMethods = () => {
945
+ res.setHeader = originalSetHeader;
946
+ res.removeHeader = originalRemoveHeader;
947
+ res.writeHead = originalWriteHead;
948
+ res.write = originalWrite;
949
+ res.end = originalEnd;
950
+ res.flushHeaders = originalFlushHeaders;
951
+ };
952
+ const replayBufferedCall = ([method, args]) => {
953
+ if (method === "writeHead") {
954
+ const [statusCode, statusMessage] = args;
955
+ return typeof statusMessage === "string" ? originalWriteHead(statusCode, statusMessage) : originalWriteHead(statusCode);
956
+ }
957
+ if (method === "write") return originalWrite(...args);
958
+ if (method === "end") return originalEnd(...args);
959
+ return originalFlushHeaders();
960
+ };
961
+ let endCalled;
962
+ const endPromise = new Promise((resolve) => {
963
+ endCalled = resolve;
964
+ });
965
+ res.writeHead = function(...args) {
966
+ if (!settled) {
967
+ const [statusCode, statusMessageOrHeaders, maybeHeaders] = args;
968
+ if (typeof statusCode === "number") {
969
+ res.statusCode = statusCode;
970
+ }
971
+ const headers = typeof statusMessageOrHeaders === "string" ? maybeHeaders : statusMessageOrHeaders;
972
+ if (Array.isArray(headers)) {
973
+ const grouped = /* @__PURE__ */ new Map();
974
+ for (let index = 0; index < headers.length; index += 2) {
975
+ const name = String(headers[index]);
976
+ const key = name.toLowerCase();
977
+ const entry = grouped.get(key) ?? { name, values: [] };
978
+ entry.values.push(String(headers[index + 1]));
979
+ grouped.set(key, entry);
980
+ }
981
+ for (const { name, values } of grouped.values()) {
982
+ res.setHeader(name, values.length === 1 ? values[0] : values);
983
+ }
984
+ } else if (headers && typeof headers === "object") {
985
+ for (const [key, value] of Object.entries(headers)) {
986
+ if (value === void 0) continue;
987
+ res.setHeader(
988
+ key,
989
+ Array.isArray(value) ? value : String(value)
990
+ );
991
+ }
992
+ }
993
+ bufferedCalls.push(["writeHead", args]);
994
+ return res;
995
+ }
996
+ return originalWriteHead(...args);
997
+ };
998
+ res.setHeader = function(name, value) {
999
+ const result2 = originalSetHeader(name, value);
1000
+ if (!settled && res.statusCode >= 400) {
1001
+ failureHeaders.set(name.toLowerCase(), {
1002
+ name,
1003
+ value: Array.isArray(value) ? [...value] : value
1004
+ });
1005
+ }
1006
+ return result2;
1007
+ };
1008
+ res.removeHeader = function(name) {
1009
+ originalRemoveHeader(name);
1010
+ if (!settled && res.statusCode >= 400) {
1011
+ failureHeaders.set(name.toLowerCase(), { name });
1012
+ }
1013
+ };
1014
+ res.write = function(...args) {
1015
+ if (!settled) {
1016
+ bufferedCalls.push(["write", args]);
1017
+ return true;
1018
+ }
1019
+ return originalWrite(...args);
1020
+ };
1021
+ res.end = function(...args) {
1022
+ if (!settled) {
1023
+ bufferedCalls.push(["end", args]);
1024
+ endCalled();
1025
+ return res;
1026
+ }
1027
+ return originalEnd(...args);
1028
+ };
1029
+ res.flushHeaders = function() {
1030
+ if (!settled) {
1031
+ bufferedCalls.push(["flushHeaders", []]);
1032
+ return;
1033
+ }
1034
+ return originalFlushHeaders();
1035
+ };
1036
+ const finalizeFailure = async (cancellation) => {
1037
+ const preservedFailureHeaders = /* @__PURE__ */ new Map();
1038
+ for (const [name, value] of Object.entries(res.getHeaders())) {
1039
+ if (value === void 0 || isUnsafeFailureHeader(name, value))
1040
+ continue;
1041
+ preservedFailureHeaders.set(
1042
+ name,
1043
+ Array.isArray(value) ? [...value] : value
1044
+ );
1045
+ }
1046
+ const cancelSettlement = await cancellationDispatcher.cancel(cancellation);
1047
+ settled = true;
1048
+ restoreHeaders(res, headersBeforeHandler);
1049
+ for (const [name, value] of Object.entries(res.getHeaders())) {
1050
+ if (isUnsafeFailureHeader(name, value)) res.removeHeader(name);
1051
+ }
1052
+ for (const [name, value] of preservedFailureHeaders) {
1053
+ res.setHeader(name, value);
1054
+ }
1055
+ for (const { name, value } of failureHeaders.values()) {
1056
+ if (value === void 0 || isUnsafeFailureHeader(name, value))
1057
+ res.removeHeader(name);
1058
+ else res.setHeader(name, value);
1059
+ }
1060
+ const existingCacheControl = Object.entries(res.getHeaders()).find(
1061
+ ([key]) => key.toLowerCase() === "cache-control"
1062
+ )?.[1];
1063
+ const headers = httpServer.createFailurePathSettlementHeaders(
1064
+ cancelSettlement,
1065
+ beforeHandlerSettlement,
1066
+ paymentPayload,
1067
+ Array.isArray(existingCacheControl) ? existingCacheControl.join(",") : existingCacheControl === void 0 ? void 0 : String(existingCacheControl)
1068
+ );
1069
+ Object.entries(headers ?? {}).forEach(([key, value]) => {
1070
+ res.setHeader(key, value);
1071
+ });
1072
+ restoreResponseMethods();
1073
+ for (const call of bufferedCalls) replayBufferedCall(call);
1074
+ bufferedCalls = [];
1075
+ };
1076
+ try {
1077
+ next();
1078
+ } catch (error) {
1079
+ bufferedCalls = [];
1080
+ restoreHeaders(res, headersBeforeHandler);
1081
+ failureHeaders.clear();
1082
+ try {
1083
+ next(error);
1084
+ } catch (errorMiddlewareError) {
1085
+ restoreResponseMethods();
1086
+ throw errorMiddlewareError;
1087
+ }
1088
+ await endPromise;
1089
+ await finalizeFailure({ reason: "handler_threw", error });
1090
+ return;
1091
+ }
1092
+ await endPromise;
1093
+ if (res.statusCode >= 400) {
1094
+ await finalizeFailure({
1095
+ reason: "handler_failed",
1096
+ responseStatus: res.statusCode
1097
+ });
1098
+ return;
1099
+ }
1100
+ try {
1101
+ const settleResult = await httpServer.processSettlement(
1102
+ paymentPayload,
1103
+ paymentRequirements,
1104
+ declaredExtensions,
1105
+ // The route handler signals a settlement override (a metered amount
1106
+ // below the signed maximum) through a response header.
1107
+ // `processSettlement` only reads that off `transportContext`, so
1108
+ // omitting this settles the full authorised amount and makes
1109
+ // `upto` behave exactly like `exact`.
1110
+ {
1111
+ request: context,
1112
+ responseBody: responseBodyFromBufferedCalls(bufferedCalls),
1113
+ responseHeaders: toHeaderRecord(res.getHeaders())
1114
+ },
1115
+ void 0,
1116
+ beforeHandlerSettlement
1117
+ );
1118
+ if (!settleResult.success) {
1119
+ bufferedCalls = [];
1120
+ restoreHeaders(res, headersBeforeHandler);
1121
+ if (isFacilitatorUnavailable(settleResult.errorReason)) {
1122
+ sendFacilitatorUnavailable(res);
1123
+ return;
1124
+ }
1125
+ const { response } = settleResult;
1126
+ await sendCoreResponse(res, response);
1127
+ return;
1128
+ }
1129
+ Object.entries(settleResult.headers).forEach(([key, value]) => {
1130
+ const headerValue = Array.isArray(value) ? value.join(",") : String(value);
1131
+ res.setHeader(key, headerValue);
1132
+ });
1133
+ const cacheControl = res.getHeaders()["cache-control"];
1134
+ res.setHeader(
1135
+ "cache-control",
1136
+ withPrivateCacheControl(
1137
+ Array.isArray(cacheControl) ? cacheControl.join(",") : cacheControl === void 0 ? null : String(cacheControl)
1138
+ )
1139
+ );
1140
+ res.removeHeader(SETTLEMENT_OVERRIDES_HEADER);
1141
+ } catch (error) {
1142
+ bufferedCalls = [];
1143
+ restoreHeaders(res, headersBeforeHandler);
1144
+ settled = true;
1145
+ restoreResponseMethods();
1146
+ next(error);
1147
+ return;
1148
+ } finally {
1149
+ settled = true;
1150
+ restoreResponseMethods();
1151
+ for (const call of bufferedCalls) replayBufferedCall(call);
1152
+ bufferedCalls = [];
1153
+ }
1154
+ return;
1155
+ }
1156
+ }
1157
+ };
1158
+ }
1159
+
1160
+ // src/facilitator/middleware/hono.ts
1161
+ import {
1162
+ SETTLEMENT_OVERRIDES_HEADER as SETTLEMENT_OVERRIDES_HEADER2,
1163
+ withPrivateCacheControl as withPrivateCacheControl2,
1164
+ x402HTTPResourceServer as x402HTTPResourceServer2,
1165
+ x402ResourceServer as x402ResourceServer2
1166
+ } from "@x402/core/server";
1167
+ var FACILITATOR_SYNC_RETRY_FLOOR_MS2 = 3e4;
1168
+ var HonoAdapter = class {
1169
+ constructor(c) {
1170
+ this.c = c;
1171
+ }
1172
+ getHeader(name) {
1173
+ return this.c.req.header(name);
1174
+ }
1175
+ getMethod() {
1176
+ return this.c.req.method;
1177
+ }
1178
+ getPath() {
1179
+ return this.c.req.path;
1180
+ }
1181
+ getUrl() {
1182
+ return this.c.req.url;
1183
+ }
1184
+ getAcceptHeader() {
1185
+ return this.c.req.header("Accept") || "";
1186
+ }
1187
+ getUserAgent() {
1188
+ return this.c.req.header("User-Agent") || "";
1189
+ }
1190
+ getQueryParams() {
1191
+ const query = this.c.req.query();
1192
+ const result = {};
1193
+ for (const [key, value] of Object.entries(query)) {
1194
+ result[key] = value;
1195
+ }
1196
+ return result;
1197
+ }
1198
+ getQueryParam(name) {
1199
+ return this.c.req.query(name);
1200
+ }
1201
+ async getBody() {
1202
+ try {
1203
+ return await this.c.req.json();
1204
+ } catch {
1205
+ return void 0;
1206
+ }
1207
+ }
1208
+ };
1209
+ function toHeaderRecord2(headers) {
1210
+ if (!headers) return void 0;
1211
+ const record = {};
1212
+ headers.forEach((value, key) => {
1213
+ record[key] = value;
1214
+ });
1215
+ return record;
1216
+ }
1217
+ function withResponseHeaders(response, additions) {
1218
+ if ((!additions || Object.keys(additions).length === 0) && !response.headers.has(SETTLEMENT_OVERRIDES_HEADER2))
1219
+ return response;
1220
+ const headers = new Headers(response.headers);
1221
+ Object.entries(additions ?? {}).forEach(([key, value]) => {
1222
+ headers.set(key, value);
1223
+ });
1224
+ headers.delete(SETTLEMENT_OVERRIDES_HEADER2);
1225
+ return new Response(response.body, {
1226
+ status: response.status,
1227
+ statusText: response.statusText,
1228
+ headers
1229
+ });
1230
+ }
1231
+ function withSuccessfulSettlementHeaders(response, additions) {
1232
+ const headers = new Headers(response.headers);
1233
+ Object.entries(additions).forEach(([key, value]) => {
1234
+ headers.set(key, value);
1235
+ });
1236
+ headers.delete(SETTLEMENT_OVERRIDES_HEADER2);
1237
+ headers.set(
1238
+ "cache-control",
1239
+ withPrivateCacheControl2(headers.get("cache-control"))
1240
+ );
1241
+ return new Response(response.body, {
1242
+ status: response.status,
1243
+ statusText: response.statusText,
1244
+ headers
1245
+ });
1246
+ }
1247
+ function failureResponse(body, status, headers) {
1248
+ const responseHeaders = new Headers();
1249
+ for (const [name, value] of Object.entries(headers)) {
1250
+ const values = Array.isArray(value) ? value : [value];
1251
+ for (const item of values) responseHeaders.append(name, item);
1252
+ }
1253
+ return new Response(serializeResponseBody({ body, headers }), {
1254
+ status,
1255
+ headers: responseHeaders
1256
+ });
1257
+ }
1258
+ function facilitatorUnavailableResponse() {
1259
+ return failureResponse({ error: "facilitator_unavailable" }, 503, {
1260
+ "retry-after": "1",
1261
+ "cache-control": withPrivateCacheControl2(null),
1262
+ "content-type": "application/json; charset=UTF-8"
1263
+ });
1264
+ }
1265
+ function replaceContextResponse(c, response) {
1266
+ for (const name of [...c.res?.headers.keys() ?? []]) {
1267
+ c.header(name, void 0);
1268
+ }
1269
+ c.res = response;
1270
+ }
1271
+ function weftPaymentMiddlewareHono(routes, config) {
1272
+ const facilitatorClient = createFacilitatorClient(
1273
+ config?.facilitator,
1274
+ buildFacilitatorAuthHeaders("hono", config?.apiKey, config ?? {})
1275
+ );
1276
+ const resourceServer = new x402ResourceServer2(facilitatorClient);
1277
+ registerDynamicExtensions(resourceServer, routes);
1278
+ if (config?.schemes) {
1279
+ config.schemes.forEach(({ network, server: schemeServer }) => {
1280
+ resourceServer.register(network, schemeServer);
1281
+ });
1282
+ }
1283
+ const httpServer = new x402HTTPResourceServer2(
1284
+ resourceServer,
1285
+ applyProductIdentity(routes, config ?? {})
1286
+ );
1287
+ if (config?.paywall) {
1288
+ httpServer.registerPaywallProvider(config.paywall);
1289
+ }
1290
+ const syncOnStart = config?.syncFacilitatorOnStart ?? true;
1291
+ let facilitatorSynced = false;
1292
+ let syncInFlight = null;
1293
+ let lastFailedSyncAt = 0;
1294
+ const syncFacilitator = () => {
1295
+ syncInFlight ?? (syncInFlight = httpServer.initialize().then(
1296
+ () => {
1297
+ facilitatorSynced = true;
1298
+ },
1299
+ (error) => {
1300
+ lastFailedSyncAt = Date.now();
1301
+ console.warn(
1302
+ "[weft] facilitator sync failed; payment-protected routes degrade until a later attempt succeeds: " + (error instanceof Error ? error.message : String(error))
1303
+ );
1304
+ }
1305
+ ).finally(() => {
1306
+ syncInFlight = null;
1307
+ }));
1308
+ return syncInFlight;
1309
+ };
1310
+ let bootSync = syncOnStart ? syncFacilitator() : null;
1311
+ return async (c, next) => {
1312
+ const adapter = new HonoAdapter(c);
1313
+ const context = {
1314
+ adapter,
1315
+ path: c.req.path,
1316
+ method: c.req.method,
1317
+ paymentHeader: adapter.getHeader("payment-signature") || adapter.getHeader("x-payment")
1318
+ };
1319
+ if (!httpServer.requiresPayment(context)) {
1320
+ return next();
1321
+ }
1322
+ if (bootSync) {
1323
+ await bootSync;
1324
+ bootSync = null;
1325
+ }
1326
+ if (syncOnStart && !facilitatorSynced && Date.now() - lastFailedSyncAt >= FACILITATOR_SYNC_RETRY_FLOOR_MS2) {
1327
+ void syncFacilitator();
1328
+ }
1329
+ const resumeCandidate = paymentResumeCandidate(context);
1330
+ const resumedPayment = resumeCandidate ? await config?.resumeVerifiedPayment?.(context, resumeCandidate) : void 0;
1331
+ const result = resumedPayment ? resumePaymentResult(resourceServer, resumedPayment, context) : await httpServer.processHTTPRequest(context, config?.paywallConfig);
1332
+ switch (result.type) {
1333
+ case "no-payment-required":
1334
+ return next();
1335
+ case "payment-error": {
1336
+ const { response } = result;
1337
+ if (isFacilitatorUnavailableResponse(response)) {
1338
+ return facilitatorUnavailableResponse();
1339
+ }
1340
+ if (!response.isHtml && !isJsonResponse(response)) {
1341
+ return failureResponse(
1342
+ response.body,
1343
+ response.status,
1344
+ response.headers
1345
+ );
1346
+ }
1347
+ Object.entries(response.headers).forEach(([key, value]) => {
1348
+ c.header(key, value);
1349
+ });
1350
+ if (response.isHtml) {
1351
+ return c.html(response.body, response.status);
1352
+ }
1353
+ return c.json(response.body || {}, response.status);
1354
+ }
1355
+ case "payment-verified": {
1356
+ const {
1357
+ paymentPayload,
1358
+ paymentRequirements,
1359
+ declaredExtensions,
1360
+ cancellationDispatcher,
1361
+ beforeHandlerSettlement
1362
+ } = result;
1363
+ const propagateHandlerError = async (error) => {
1364
+ replaceContextResponse(c, new Response());
1365
+ const cancelSettlement = await cancellationDispatcher.cancel({
1366
+ reason: "handler_threw",
1367
+ error
1368
+ });
1369
+ const headers = httpServer.createFailurePathSettlementHeaders(
1370
+ cancelSettlement,
1371
+ beforeHandlerSettlement,
1372
+ paymentPayload
1373
+ );
1374
+ Object.entries(headers ?? {}).forEach(([key, value]) => {
1375
+ c.header(key, value);
1376
+ });
1377
+ throw error;
1378
+ };
1379
+ const originalHeader = c.header;
1380
+ const handlerHeaders = /* @__PURE__ */ new Set();
1381
+ const errorHeaders = /* @__PURE__ */ new Set();
1382
+ c.header = (name, value, options) => {
1383
+ (c.error ? errorHeaders : handlerHeaders).add(name.toLowerCase());
1384
+ originalHeader.call(c, name, value, options);
1385
+ };
1386
+ try {
1387
+ await next();
1388
+ } catch (error) {
1389
+ c.header = originalHeader;
1390
+ await propagateHandlerError(error);
1391
+ }
1392
+ c.header = originalHeader;
1393
+ if (c.error) {
1394
+ const res2 = c.res ?? new Response();
1395
+ const responseHeaders = new Headers(res2.headers);
1396
+ for (const name of handlerHeaders) {
1397
+ if (!errorHeaders.has(name)) responseHeaders.delete(name);
1398
+ }
1399
+ const cancelSettlement = await cancellationDispatcher.cancel({
1400
+ reason: "handler_threw",
1401
+ error: c.error
1402
+ });
1403
+ const headers = httpServer.createFailurePathSettlementHeaders(
1404
+ cancelSettlement,
1405
+ beforeHandlerSettlement,
1406
+ paymentPayload,
1407
+ responseHeaders.get("cache-control")
1408
+ );
1409
+ const sanitizedResponse = new Response(res2.body, {
1410
+ status: res2.status,
1411
+ statusText: res2.statusText,
1412
+ headers: responseHeaders
1413
+ });
1414
+ replaceContextResponse(
1415
+ c,
1416
+ withResponseHeaders(sanitizedResponse, headers)
1417
+ );
1418
+ return;
1419
+ }
1420
+ let res = c.res;
1421
+ if (res && res.status >= 400) {
1422
+ const cancelSettlement = await cancellationDispatcher.cancel({
1423
+ reason: "handler_failed",
1424
+ responseStatus: res.status
1425
+ });
1426
+ const headers = httpServer.createFailurePathSettlementHeaders(
1427
+ cancelSettlement,
1428
+ beforeHandlerSettlement,
1429
+ paymentPayload,
1430
+ res.headers.get("cache-control")
1431
+ );
1432
+ replaceContextResponse(c, withResponseHeaders(res, headers));
1433
+ return;
1434
+ }
1435
+ let settleResult;
1436
+ try {
1437
+ settleResult = await httpServer.processSettlement(
1438
+ paymentPayload,
1439
+ paymentRequirements,
1440
+ declaredExtensions,
1441
+ // The route handler signals a settlement override (a metered amount
1442
+ // below the signed maximum) through a response header.
1443
+ // `processSettlement` only reads that off `transportContext`, so
1444
+ // omitting this settles the full authorised amount and makes
1445
+ // `upto` behave exactly like `exact`.
1446
+ {
1447
+ request: context,
1448
+ responseBody: res ? Buffer.from(await res.clone().arrayBuffer()) : void 0,
1449
+ responseHeaders: toHeaderRecord2(res?.headers)
1450
+ },
1451
+ void 0,
1452
+ beforeHandlerSettlement
1453
+ );
1454
+ } catch (error) {
1455
+ replaceContextResponse(c, new Response());
1456
+ throw error;
1457
+ }
1458
+ if (!settleResult.success) {
1459
+ if (isFacilitatorUnavailable(settleResult.errorReason)) {
1460
+ replaceContextResponse(c, facilitatorUnavailableResponse());
1461
+ return;
1462
+ }
1463
+ const { response } = settleResult;
1464
+ replaceContextResponse(
1465
+ c,
1466
+ failureResponse(response.body, response.status, response.headers)
1467
+ );
1468
+ return;
1469
+ } else if (res) {
1470
+ res = withSuccessfulSettlementHeaders(res, settleResult.headers);
1471
+ }
1472
+ if (res) replaceContextResponse(c, res);
1473
+ return;
1474
+ }
1475
+ }
1476
+ };
1477
+ }
1478
+ export {
1479
+ MAX_EXTENSION_BYTES,
1480
+ WEFT_API_KEY_HEADER,
1481
+ WEFT_DECLARED_HEADER,
1482
+ WEFT_PRODUCT_EXTENSION_KEY,
1483
+ WEFT_PRODUCT_INFO_SCHEMA,
1484
+ WEFT_REQUEST_EXTENSION_KEY,
1485
+ WEFT_REQUEST_INFO_SCHEMA,
1486
+ WEFT_TYPE_TAG_PREFIX,
1487
+ applyProductIdentity,
1488
+ productTypeTag,
1489
+ registerDynamicExtensions,
1490
+ weftPaymentMiddleware,
1491
+ weftPaymentMiddlewareHono
1492
+ };
1493
+ //# sourceMappingURL=index.mjs.map