@402flow/sdk 0.1.0-alpha.3 → 0.1.0-alpha.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.
@@ -1,123 +1,86 @@
1
- import { createParsedChallenge, getX402PaymentRequired, } from './challenge-types.js';
2
- import { parseX402V1BodyChallenge, parseX402V1HeaderChallenge, parseX402V1PaymentResponseHeader, } from './x402-v1.js';
3
- import { parseX402V2BodyChallenge, parseX402V2HeaderChallenge, parseX402V2PaymentResponseHeader, } from './x402-v2.js';
4
- import { monetaryAmountToMinorUnits, normalizedMoneySchema } from './contracts.js';
1
+ /**
2
+ * Inspect a merchant response for x402/l402 challenge evidence in either headers
3
+ * or a JSON body. The SDK prefers header evidence first because that is the most
4
+ * direct live protocol signal.
5
+ */
5
6
  export async function detectChallengeFromResponse(response) {
6
- const v2HeaderChallenge = parseX402V2HeaderChallenge(response);
7
- if (v2HeaderChallenge) {
8
- return v2HeaderChallenge;
9
- }
10
- const v1HeaderChallenge = parseX402V1HeaderChallenge(response);
11
- if (v1HeaderChallenge) {
12
- return v1HeaderChallenge;
13
- }
14
- const authenticateChallenge = parseAuthenticateHeader(response.headers.get('www-authenticate'));
15
- if (authenticateChallenge) {
16
- return authenticateChallenge;
17
- }
18
- const contentType = response.headers.get('content-type') ?? '';
19
- if (!contentType.includes('application/json')) {
20
- return undefined;
21
- }
22
- try {
23
- const payload = await response.clone().json();
24
- return parseBodyChallenge(payload);
25
- }
26
- catch {
27
- return undefined;
28
- }
29
- }
30
- export function parseX402PaymentResponseFromHeaders(headers) {
31
- const headerMap = headers instanceof Headers ? headers : new Headers(headers);
32
- const v2Header = headerMap.get('payment-response');
33
- if (v2Header) {
34
- return parseX402V2PaymentResponseHeader(v2Header);
35
- }
36
- const v1Header = headerMap.get('x-payment-response');
37
- if (v1Header) {
38
- return parseX402V1PaymentResponseHeader(v1Header);
7
+ const headers = captureHeaders(response.headers);
8
+ const protocol = sniffProtocolFromHeaders(headers);
9
+ if (protocol) {
10
+ const body = await tryReadJsonBody(response);
11
+ return body !== undefined
12
+ ? { protocol, headers, body }
13
+ : { protocol, headers };
14
+ }
15
+ const body = await tryReadJsonBody(response);
16
+ if (body !== undefined) {
17
+ const bodyProtocol = sniffProtocolFromBody(body);
18
+ if (bodyProtocol) {
19
+ return { protocol: bodyProtocol, headers, body };
20
+ }
39
21
  }
40
22
  return undefined;
41
23
  }
42
- export function parseX402PaymentResponseHeader(headerValue) {
43
- return (parseX402V2PaymentResponseHeader(headerValue) ??
44
- parseX402V1PaymentResponseHeader(headerValue));
24
+ function captureHeaders(source) {
25
+ const headers = {};
26
+ source.forEach((value, key) => {
27
+ headers[key] = value;
28
+ });
29
+ return headers;
45
30
  }
46
- function parseAuthenticateHeader(headerValue) {
47
- if (!headerValue) {
48
- return undefined;
49
- }
50
- const protocolMatch = headerValue.match(/\b(x402|l402)\b/i);
51
- if (!protocolMatch) {
52
- return undefined;
53
- }
54
- const protocol = protocolMatch[1]?.toLowerCase();
55
- const attributePattern = /(amount|asset|precision|payee)="([^"]+)"/gi;
56
- const attributes = {};
57
- let match = attributePattern.exec(headerValue);
58
- while (match) {
59
- const attributeName = match[1];
60
- const attributeValue = match[2];
61
- if (attributeName && attributeValue) {
62
- attributes[attributeName.toLowerCase()] = attributeValue;
31
+ // Header detection is the primary path because a real payable merchant usually
32
+ // signals the protocol directly in response headers.
33
+ function sniffProtocolFromHeaders(headers) {
34
+ if (headers['payment-required']) {
35
+ return 'x402';
36
+ }
37
+ const paymentProtocol = headers['x-payment-protocol']?.toLowerCase();
38
+ if (paymentProtocol === 'x402' || paymentProtocol === 'l402') {
39
+ return paymentProtocol;
40
+ }
41
+ const authenticate = headers['www-authenticate'];
42
+ if (authenticate) {
43
+ const match = authenticate.match(/\b(x402|l402)\b/i);
44
+ if (match) {
45
+ return match[1].toLowerCase();
63
46
  }
64
- match = attributePattern.exec(headerValue);
65
- }
66
- if (!attributes.amount || !attributes.asset) {
67
- return undefined;
68
47
  }
69
- return createParsedChallenge({
70
- protocol,
71
- money: normalizedMoneySchema.parse({
72
- asset: attributes.asset,
73
- amount: attributes.amount,
74
- amountMinor: monetaryAmountToMinorUnits(attributes.amount, Number(attributes.precision ?? '6')),
75
- precision: Number(attributes.precision ?? '6'),
76
- unit: 'minor',
77
- }),
78
- payee: attributes.payee,
79
- raw: {
80
- authenticate: headerValue,
81
- },
82
- });
48
+ return undefined;
83
49
  }
84
- function parseBodyChallenge(payload) {
50
+ // Some compatibility merchants embed the challenge in JSON instead of, or in
51
+ // addition to, headers. This fallback keeps the client tolerant of that shape.
52
+ function sniffProtocolFromBody(payload) {
85
53
  if (!payload || typeof payload !== 'object') {
86
54
  return undefined;
87
55
  }
88
- const candidate = 'challenge' in payload && payload.challenge && typeof payload.challenge === 'object'
56
+ const candidate = 'challenge' in payload &&
57
+ payload.challenge &&
58
+ typeof payload.challenge === 'object'
89
59
  ? payload.challenge
90
60
  : payload;
91
- const v2Challenge = parseX402V2BodyChallenge(candidate);
92
- if (v2Challenge) {
93
- return v2Challenge;
61
+ if ('x402Version' in candidate) {
62
+ return 'x402';
94
63
  }
95
- const v1Challenge = parseX402V1BodyChallenge(candidate);
96
- if (v1Challenge) {
97
- return v1Challenge;
64
+ if ('protocol' in candidate && typeof candidate.protocol === 'string') {
65
+ const protocol = candidate.protocol.toLowerCase();
66
+ if (protocol === 'x402' || protocol === 'l402') {
67
+ return protocol;
68
+ }
98
69
  }
99
- if (!candidate || typeof candidate !== 'object') {
70
+ return undefined;
71
+ }
72
+ // The detector only parses JSON bodies when content-type says JSON, and it never
73
+ // consumes the original response stream thanks to clone().
74
+ async function tryReadJsonBody(response) {
75
+ const contentType = response.headers.get('content-type') ?? '';
76
+ if (!contentType.includes('application/json')) {
100
77
  return undefined;
101
78
  }
102
- const protocol = 'protocol' in candidate && typeof candidate.protocol === 'string'
103
- ? candidate.protocol.toLowerCase()
104
- : undefined;
105
- const money = 'money' in candidate ? candidate.money : undefined;
106
- const payee = 'payee' in candidate && typeof candidate.payee === 'string'
107
- ? candidate.payee
108
- : undefined;
109
- const raw = 'raw' in candidate && candidate.raw && typeof candidate.raw === 'object'
110
- ? candidate.raw
111
- : undefined;
112
- if ((protocol !== 'x402' && protocol !== 'l402') || !money) {
79
+ try {
80
+ return await response.clone().json();
81
+ }
82
+ catch {
113
83
  return undefined;
114
84
  }
115
- return createParsedChallenge({
116
- protocol,
117
- money: normalizedMoneySchema.parse(money),
118
- payee,
119
- raw: raw ?? candidate,
120
- });
121
85
  }
122
- export { getX402PaymentRequired };
123
86
  //# sourceMappingURL=challenge-detection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"challenge-detection.js","sourceRoot":"","sources":["../src/challenge-detection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"challenge-detection.js","sourceRoot":"","sources":["../src/challenge-detection.ts"],"names":[],"mappings":"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"}