@peac/adapter-x402 0.12.0-preview.2 → 0.12.1

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.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * x402 semantic normalization (Layer B)
3
+ *
4
+ * Converts EIP-712 placeholder values into semantic absence.
5
+ * This layer exists because EIP-712 requires fixed schemas, so unused
6
+ * values are represented as `0` (for numbers) and `""` (for strings).
7
+ *
8
+ * Normalization rules:
9
+ * - `validUntil === 0` -> `undefined` (EIP-712 "no expiry" placeholder)
10
+ * - `transaction === ""` -> `undefined` (EIP-712 "no transaction" placeholder)
11
+ * - All other fields pass through unchanged
12
+ *
13
+ * IMPORTANT: Raw artifacts are NEVER mutated. Normalization produces
14
+ * new objects; the originals are preserved in proofs.
15
+ */
16
+ import type { RawOfferPayload, RawReceiptPayload } from './raw.js';
17
+ /**
18
+ * Normalized offer payload (semantically clean)
19
+ *
20
+ * Same fields as RawOfferPayload, but with EIP-712 placeholders
21
+ * converted to semantic absence (`undefined`).
22
+ */
23
+ export interface NormalizedOfferPayload {
24
+ /** Schema version */
25
+ version: number;
26
+ /** Resource URL the offer is for */
27
+ resourceUrl: string;
28
+ /** Settlement scheme */
29
+ scheme: string;
30
+ /** CAIP-2 network identifier */
31
+ network: string;
32
+ /** Payment asset identifier */
33
+ asset: string;
34
+ /** Payment recipient address */
35
+ payTo: string;
36
+ /** Payment amount in minor units */
37
+ amount: string;
38
+ /**
39
+ * Offer expiry as epoch seconds
40
+ *
41
+ * `undefined` means "no expiry" (was `0` in EIP-712 encoding, or absent).
42
+ * Verification layer enforces expiry policy separately.
43
+ */
44
+ validUntil?: number;
45
+ }
46
+ /**
47
+ * Normalized receipt payload (semantically clean)
48
+ *
49
+ * Same fields as RawReceiptPayload, but with EIP-712 placeholders
50
+ * converted to semantic absence (`undefined`).
51
+ */
52
+ export interface NormalizedReceiptPayload {
53
+ /** Schema version */
54
+ version: number;
55
+ /** CAIP-2 network identifier */
56
+ network: string;
57
+ /** Resource URL the receipt is for */
58
+ resourceUrl: string;
59
+ /** Payer address */
60
+ payer: string;
61
+ /** Receipt issuance timestamp (epoch seconds) */
62
+ issuedAt: number;
63
+ /**
64
+ * On-chain transaction reference
65
+ *
66
+ * `undefined` means "no transaction reference" (was `""` in EIP-712, or absent).
67
+ * Privacy-minimal: transaction is optional enrichable evidence, not receipt identity.
68
+ */
69
+ transaction?: string;
70
+ }
71
+ /**
72
+ * Normalize an offer payload from raw wire to semantic representation
73
+ *
74
+ * Applies EIP-712 placeholder rules:
75
+ * - `validUntil === 0` -> `undefined` (no expiry)
76
+ *
77
+ * `validUntil` is always present in the raw payload (required per upstream TS).
78
+ * All other fields pass through unchanged.
79
+ *
80
+ * @param raw - The raw offer payload (Layer A2)
81
+ * @returns Normalized offer payload (Layer B)
82
+ */
83
+ export declare function normalizeOfferPayload(raw: RawOfferPayload): NormalizedOfferPayload;
84
+ /**
85
+ * Normalize a receipt payload from raw wire to semantic representation
86
+ *
87
+ * Applies EIP-712 placeholder rules:
88
+ * - `transaction === ""` -> `undefined` (no transaction)
89
+ *
90
+ * `transaction` is always present in the raw payload (required per upstream TS).
91
+ * All other fields pass through unchanged.
92
+ *
93
+ * @param raw - The raw receipt payload (Layer A2)
94
+ * @returns Normalized receipt payload (Layer B)
95
+ */
96
+ export declare function normalizeReceiptPayload(raw: RawReceiptPayload): NormalizedReceiptPayload;
97
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAMnE;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,sBAAsB,CAclF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,iBAAiB,GAAG,wBAAwB,CAYxF"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * x402 semantic normalization (Layer B)
4
+ *
5
+ * Converts EIP-712 placeholder values into semantic absence.
6
+ * This layer exists because EIP-712 requires fixed schemas, so unused
7
+ * values are represented as `0` (for numbers) and `""` (for strings).
8
+ *
9
+ * Normalization rules:
10
+ * - `validUntil === 0` -> `undefined` (EIP-712 "no expiry" placeholder)
11
+ * - `transaction === ""` -> `undefined` (EIP-712 "no transaction" placeholder)
12
+ * - All other fields pass through unchanged
13
+ *
14
+ * IMPORTANT: Raw artifacts are NEVER mutated. Normalization produces
15
+ * new objects; the originals are preserved in proofs.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.normalizeOfferPayload = normalizeOfferPayload;
19
+ exports.normalizeReceiptPayload = normalizeReceiptPayload;
20
+ // ---------------------------------------------------------------------------
21
+ // Normalization Functions
22
+ // ---------------------------------------------------------------------------
23
+ /**
24
+ * Normalize an offer payload from raw wire to semantic representation
25
+ *
26
+ * Applies EIP-712 placeholder rules:
27
+ * - `validUntil === 0` -> `undefined` (no expiry)
28
+ *
29
+ * `validUntil` is always present in the raw payload (required per upstream TS).
30
+ * All other fields pass through unchanged.
31
+ *
32
+ * @param raw - The raw offer payload (Layer A2)
33
+ * @returns Normalized offer payload (Layer B)
34
+ */
35
+ function normalizeOfferPayload(raw) {
36
+ return {
37
+ version: raw.version,
38
+ resourceUrl: raw.resourceUrl,
39
+ scheme: raw.scheme,
40
+ network: raw.network,
41
+ asset: raw.asset,
42
+ payTo: raw.payTo,
43
+ amount: raw.amount,
44
+ // EIP-712 placeholder: 0 means "no expiry"
45
+ ...(raw.validUntil !== 0 && {
46
+ validUntil: raw.validUntil,
47
+ }),
48
+ };
49
+ }
50
+ /**
51
+ * Normalize a receipt payload from raw wire to semantic representation
52
+ *
53
+ * Applies EIP-712 placeholder rules:
54
+ * - `transaction === ""` -> `undefined` (no transaction)
55
+ *
56
+ * `transaction` is always present in the raw payload (required per upstream TS).
57
+ * All other fields pass through unchanged.
58
+ *
59
+ * @param raw - The raw receipt payload (Layer A2)
60
+ * @returns Normalized receipt payload (Layer B)
61
+ */
62
+ function normalizeReceiptPayload(raw) {
63
+ return {
64
+ version: raw.version,
65
+ network: raw.network,
66
+ resourceUrl: raw.resourceUrl,
67
+ payer: raw.payer,
68
+ issuedAt: raw.issuedAt,
69
+ // EIP-712 placeholder: "" means "no transaction"
70
+ ...(raw.transaction !== '' && {
71
+ transaction: raw.transaction,
72
+ }),
73
+ };
74
+ }
75
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAgFH,sDAcC;AAcD,0DAYC;AAxDD,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,SAAgB,qBAAqB,CAAC,GAAoB;IACxD,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,2CAA2C;QAC3C,GAAG,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,IAAI;YAC1B,UAAU,EAAE,GAAG,CAAC,UAAU;SAC3B,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,uBAAuB,CAAC,GAAsB;IAC5D,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,iDAAiD;QACjD,GAAG,CAAC,GAAG,CAAC,WAAW,KAAK,EAAE,IAAI;YAC5B,WAAW,EAAE,GAAG,CAAC,WAAW;SAC7B,CAAC;KACH,CAAC;AACJ,CAAC"}
package/dist/raw.d.ts ADDED
@@ -0,0 +1,246 @@
1
+ /**
2
+ * x402 raw wire types and extraction (Layer A)
3
+ *
4
+ * Exact mirror of upstream coinbase/x402 TypeScript types and field shapes
5
+ * after PR #935 (Offer/Receipt Extension) merged at commit f2bbb5c.
6
+ *
7
+ * This layer exists to:
8
+ * 1. Match upstream encoded wire shapes exactly (no renames, no approximations)
9
+ * 2. Provide extraction helpers for both JWS and EIP-712 formats
10
+ * 3. Serve as the single source of truth for wire-level types
11
+ *
12
+ * Upstream TypeScript keeps `validUntil` and `transaction` as always-present
13
+ * fields because EIP-712 requires fixed schemas. Unused values are encoded as
14
+ * `0` (validUntil) and `""` (transaction). This layer preserves that encoding;
15
+ * semantic normalization to `undefined` happens in Layer B (normalize.ts).
16
+ *
17
+ * IMPORTANT: Types in this file MUST match upstream exactly.
18
+ * Do NOT rename fields, add PEAC-specific fields, or change types.
19
+ */
20
+ /**
21
+ * Upstream extension key for offer/receipt extension
22
+ *
23
+ * Matches upstream `OFFER_RECEIPT = "offer-receipt"`.
24
+ * Used in `extensions["offer-receipt"].info`.
25
+ */
26
+ export declare const OFFER_RECEIPT: "offer-receipt";
27
+ /**
28
+ * Exact upstream x402 offer payload (encoded, Layer A2)
29
+ *
30
+ * Field names and types match coinbase/x402 PR #935 TS interface exactly.
31
+ * All fields are always present in the encoded payload.
32
+ * EIP-712 uses `0` for unused `validUntil` (encoding placeholder, not semantic absence).
33
+ * Conversion to semantic optionality happens exclusively in Layer B (normalize.ts).
34
+ */
35
+ export interface RawOfferPayload {
36
+ /** Schema version (upstream: number, not string) */
37
+ version: number;
38
+ /** Resource URL the offer is for (required per upstream) */
39
+ resourceUrl: string;
40
+ /** Settlement scheme (required per upstream) */
41
+ scheme: string;
42
+ /** CAIP-2 network identifier (e.g., "eip155:8453") */
43
+ network: string;
44
+ /** Payment asset identifier (e.g., "USDC") */
45
+ asset: string;
46
+ /** Payment recipient address */
47
+ payTo: string;
48
+ /** Payment amount in minor units */
49
+ amount: string;
50
+ /**
51
+ * Offer expiry as epoch seconds
52
+ *
53
+ * Always present in the encoded payload per upstream TS interface.
54
+ * EIP-712 encodes unused values as `0` (fixed schema requirement).
55
+ * Layer B (normalize.ts) converts `0` to `undefined` (semantic absence).
56
+ */
57
+ validUntil: number;
58
+ }
59
+ /**
60
+ * Exact upstream x402 receipt payload (encoded, Layer A2)
61
+ *
62
+ * Field names and types match coinbase/x402 PR #935 TS interface exactly.
63
+ * All fields are always present in the encoded payload.
64
+ * EIP-712 uses `""` for unused `transaction` (encoding placeholder, not semantic absence).
65
+ * Conversion to semantic optionality happens exclusively in Layer B (normalize.ts).
66
+ *
67
+ * Privacy-minimal by design: receipts carry service-delivery proof, not payment details.
68
+ */
69
+ export interface RawReceiptPayload {
70
+ /** Schema version (upstream: number) */
71
+ version: number;
72
+ /** CAIP-2 network identifier */
73
+ network: string;
74
+ /** Resource URL the receipt is for */
75
+ resourceUrl: string;
76
+ /** Payer address */
77
+ payer: string;
78
+ /** Receipt issuance timestamp (epoch seconds) */
79
+ issuedAt: number;
80
+ /**
81
+ * On-chain transaction reference
82
+ *
83
+ * Always present in the encoded payload per upstream TS interface.
84
+ * EIP-712 encodes unused values as `""` (fixed schema requirement).
85
+ * Layer B (normalize.ts) converts `""` to `undefined` (semantic absence).
86
+ */
87
+ transaction: string;
88
+ }
89
+ /**
90
+ * JWS-signed offer (compact serialization)
91
+ *
92
+ * For JWS format, the payload is encoded inside the compact JWS string.
93
+ * There is no separate `payload` field. The `signature` field contains
94
+ * the full compact JWS (header.payload.signature).
95
+ */
96
+ export interface RawJWSSignedOffer {
97
+ format: 'jws';
98
+ /** Compact JWS containing the offer payload (header.payload.signature) */
99
+ signature: string;
100
+ /** Index into accepts[] that this offer targets (per-offer, not top-level) */
101
+ acceptIndex?: number;
102
+ }
103
+ /**
104
+ * EIP-712 signed offer
105
+ *
106
+ * For EIP-712 format, the payload is a separate field alongside
107
+ * the hex-encoded ECDSA signature.
108
+ */
109
+ export interface RawEIP712SignedOffer {
110
+ format: 'eip712';
111
+ /** The signed offer payload */
112
+ payload: RawOfferPayload;
113
+ /** Hex-encoded ECDSA signature */
114
+ signature: string;
115
+ /** Index into accepts[] that this offer targets (per-offer, not top-level) */
116
+ acceptIndex?: number;
117
+ }
118
+ /** Discriminated union of signed offer formats */
119
+ export type RawSignedOffer = RawJWSSignedOffer | RawEIP712SignedOffer;
120
+ /**
121
+ * JWS-signed receipt (compact serialization)
122
+ *
123
+ * Same pattern as JWS offers: payload is inside the compact JWS string.
124
+ */
125
+ export interface RawJWSSignedReceipt {
126
+ format: 'jws';
127
+ /** Compact JWS containing the receipt payload */
128
+ signature: string;
129
+ }
130
+ /**
131
+ * EIP-712 signed receipt
132
+ */
133
+ export interface RawEIP712SignedReceipt {
134
+ format: 'eip712';
135
+ /** The signed receipt payload */
136
+ payload: RawReceiptPayload;
137
+ /** Hex-encoded ECDSA signature */
138
+ signature: string;
139
+ }
140
+ /** Discriminated union of signed receipt formats */
141
+ export type RawSignedReceipt = RawJWSSignedReceipt | RawEIP712SignedReceipt;
142
+ /**
143
+ * Upstream x402 extension info structure
144
+ *
145
+ * In upstream, the offer/receipt data lives under:
146
+ * `extensions["offer-receipt"].info`
147
+ */
148
+ export interface RawX402ExtensionInfo {
149
+ /** Array of signed offers */
150
+ offers: RawSignedOffer[];
151
+ /** Optional signed receipt */
152
+ receipt?: RawSignedReceipt;
153
+ }
154
+ /**
155
+ * Default maximum compact JWS byte length (64 KB)
156
+ *
157
+ * Enforced BEFORE base64url decoding to prevent memory amplification.
158
+ */
159
+ export declare const MAX_COMPACT_JWS_BYTES: number;
160
+ /**
161
+ * Parsed compact JWS components
162
+ *
163
+ * The raw `compact` string is the signed artifact and MUST be preserved as-is.
164
+ * These parsed components are for extraction only; never reconstruct from parts.
165
+ */
166
+ export interface ParsedCompactJWS {
167
+ /** Decoded JOSE header */
168
+ header: Record<string, unknown>;
169
+ /** Decoded payload (guaranteed to be a plain object) */
170
+ payload: Record<string, unknown>;
171
+ /** Raw signature segment (base64url-encoded) */
172
+ signatureSegment: string;
173
+ }
174
+ /**
175
+ * Parse a compact JWS string with hardened validation
176
+ *
177
+ * Contract:
178
+ * 1. Exactly 3 segments (split on `.`)
179
+ * 2. No padding characters (`=`) in any segment
180
+ * 3. Size limit enforced BEFORE decoding
181
+ * 4. Payload MUST be a plain JSON object (not array, primitive, or null)
182
+ * 5. Never reconstruct: the compact JWS is the signed artifact
183
+ * 6. Fail-closed on any anomaly
184
+ *
185
+ * @param compact - The compact JWS string
186
+ * @param maxBytes - Maximum byte length (default: MAX_COMPACT_JWS_BYTES)
187
+ * @returns Parsed components
188
+ * @throws X402Error with specific error code on any parsing failure
189
+ */
190
+ export declare function parseCompactJWS(compact: string, maxBytes?: number): ParsedCompactJWS;
191
+ /**
192
+ * Extract offer payload from a signed offer
193
+ *
194
+ * For EIP-712: returns the `payload` field directly.
195
+ * For JWS: decodes the compact JWS and returns the decoded payload.
196
+ *
197
+ * @param offer - The signed offer (discriminated union)
198
+ * @param maxJwsBytes - Maximum JWS byte length for JWS format
199
+ * @returns The raw offer payload
200
+ * @throws X402Error if extraction fails
201
+ */
202
+ export declare function extractOfferPayload(offer: RawSignedOffer, maxJwsBytes?: number): RawOfferPayload;
203
+ /**
204
+ * Extract receipt payload from a signed receipt
205
+ *
206
+ * For EIP-712: returns the `payload` field directly.
207
+ * For JWS: decodes the compact JWS and returns the decoded payload.
208
+ *
209
+ * @param receipt - The signed receipt (discriminated union)
210
+ * @param maxJwsBytes - Maximum JWS byte length for JWS format
211
+ * @returns The raw receipt payload
212
+ * @throws X402Error if extraction fails
213
+ */
214
+ export declare function extractReceiptPayload(receipt: RawSignedReceipt, maxJwsBytes?: number): RawReceiptPayload;
215
+ /**
216
+ * Extract x402 extension info from an upstream response body
217
+ *
218
+ * Equivalent to upstream's `extractOffersFromPaymentRequired()` but operates
219
+ * on the raw response body rather than the upstream `PaymentRequired` type.
220
+ * Looks for `extensions["offer-receipt"].info` in the body.
221
+ *
222
+ * This is the entry point for converting raw upstream wire data into
223
+ * PEAC's `X402OfferReceiptChallenge` convenience type.
224
+ *
225
+ * @param body - The upstream response body (unknown shape)
226
+ * @returns The extension info, or null if absent/malformed
227
+ */
228
+ export declare function extractExtensionInfo(body: unknown): RawX402ExtensionInfo | null;
229
+ /**
230
+ * Standard x402 response header names for payment receipts
231
+ *
232
+ * Upstream uses `X-PAYMENT-RESPONSE` (primary) and `PAYMENT-RESPONSE` (alias).
233
+ */
234
+ export declare const X402_RECEIPT_HEADERS: readonly ["x-payment-response", "payment-response"];
235
+ /**
236
+ * Extract signed receipt from x402 settlement response headers
237
+ *
238
+ * Equivalent to upstream's `extractReceiptFromResponse()` but operates
239
+ * on raw headers rather than a `Response` object. Upstream sends the
240
+ * signed receipt as JSON in `X-PAYMENT-RESPONSE` or `PAYMENT-RESPONSE`.
241
+ *
242
+ * @param headers - Response headers (case-insensitive lookup)
243
+ * @returns The signed receipt, or null if absent/malformed
244
+ */
245
+ export declare function extractReceiptFromHeaders(headers: Record<string, string | string[] | undefined>): RawSignedReceipt | null;
246
+ //# sourceMappingURL=raw.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../src/raw.ts"],"names":[],"mappings":"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"}
package/dist/raw.js ADDED
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ /**
3
+ * x402 raw wire types and extraction (Layer A)
4
+ *
5
+ * Exact mirror of upstream coinbase/x402 TypeScript types and field shapes
6
+ * after PR #935 (Offer/Receipt Extension) merged at commit f2bbb5c.
7
+ *
8
+ * This layer exists to:
9
+ * 1. Match upstream encoded wire shapes exactly (no renames, no approximations)
10
+ * 2. Provide extraction helpers for both JWS and EIP-712 formats
11
+ * 3. Serve as the single source of truth for wire-level types
12
+ *
13
+ * Upstream TypeScript keeps `validUntil` and `transaction` as always-present
14
+ * fields because EIP-712 requires fixed schemas. Unused values are encoded as
15
+ * `0` (validUntil) and `""` (transaction). This layer preserves that encoding;
16
+ * semantic normalization to `undefined` happens in Layer B (normalize.ts).
17
+ *
18
+ * IMPORTANT: Types in this file MUST match upstream exactly.
19
+ * Do NOT rename fields, add PEAC-specific fields, or change types.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.X402_RECEIPT_HEADERS = exports.MAX_COMPACT_JWS_BYTES = exports.OFFER_RECEIPT = void 0;
23
+ exports.parseCompactJWS = parseCompactJWS;
24
+ exports.extractOfferPayload = extractOfferPayload;
25
+ exports.extractReceiptPayload = extractReceiptPayload;
26
+ exports.extractExtensionInfo = extractExtensionInfo;
27
+ exports.extractReceiptFromHeaders = extractReceiptFromHeaders;
28
+ const crypto_1 = require("@peac/crypto");
29
+ const errors_js_1 = require("./errors.js");
30
+ // ---------------------------------------------------------------------------
31
+ // Upstream Extension Constant
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * Upstream extension key for offer/receipt extension
35
+ *
36
+ * Matches upstream `OFFER_RECEIPT = "offer-receipt"`.
37
+ * Used in `extensions["offer-receipt"].info`.
38
+ */
39
+ exports.OFFER_RECEIPT = 'offer-receipt';
40
+ // ---------------------------------------------------------------------------
41
+ // JWS Parsing Contract (Hardened)
42
+ // ---------------------------------------------------------------------------
43
+ /**
44
+ * Default maximum compact JWS byte length (64 KB)
45
+ *
46
+ * Enforced BEFORE base64url decoding to prevent memory amplification.
47
+ */
48
+ exports.MAX_COMPACT_JWS_BYTES = 64 * 1024;
49
+ /**
50
+ * Parse a compact JWS string with hardened validation
51
+ *
52
+ * Contract:
53
+ * 1. Exactly 3 segments (split on `.`)
54
+ * 2. No padding characters (`=`) in any segment
55
+ * 3. Size limit enforced BEFORE decoding
56
+ * 4. Payload MUST be a plain JSON object (not array, primitive, or null)
57
+ * 5. Never reconstruct: the compact JWS is the signed artifact
58
+ * 6. Fail-closed on any anomaly
59
+ *
60
+ * @param compact - The compact JWS string
61
+ * @param maxBytes - Maximum byte length (default: MAX_COMPACT_JWS_BYTES)
62
+ * @returns Parsed components
63
+ * @throws X402Error with specific error code on any parsing failure
64
+ */
65
+ function parseCompactJWS(compact, maxBytes = exports.MAX_COMPACT_JWS_BYTES) {
66
+ // 1. Size limit before any processing
67
+ if (compact.length > maxBytes) {
68
+ throw new errors_js_1.X402Error('jws_too_large', `Compact JWS exceeds ${maxBytes} byte limit`);
69
+ }
70
+ // 2. Split into exactly 3 segments
71
+ const segments = compact.split('.');
72
+ if (segments.length !== 3) {
73
+ throw new errors_js_1.X402Error('jws_malformed', `Expected 3 segments in compact JWS, got ${segments.length}`);
74
+ }
75
+ // 3. Reject padded base64url (per RFC 7515, base64url has no padding)
76
+ for (const seg of segments) {
77
+ if (seg.includes('=')) {
78
+ throw new errors_js_1.X402Error('jws_padded_base64url', 'Padded base64url is not valid in compact JWS (RFC 7515)');
79
+ }
80
+ }
81
+ // 4. Decode header
82
+ let header;
83
+ try {
84
+ const headerJson = (0, crypto_1.base64urlDecodeString)(segments[0]);
85
+ header = JSON.parse(headerJson);
86
+ }
87
+ catch (e) {
88
+ throw new errors_js_1.X402Error('jws_malformed', `Failed to decode JWS header: ${e instanceof Error ? e.message : String(e)}`);
89
+ }
90
+ if (typeof header !== 'object' || header === null || Array.isArray(header)) {
91
+ throw new errors_js_1.X402Error('jws_malformed', 'JWS header must be a JSON object');
92
+ }
93
+ // 5. Decode payload
94
+ let payload;
95
+ try {
96
+ const payloadJson = (0, crypto_1.base64urlDecodeString)(segments[1]);
97
+ payload = JSON.parse(payloadJson);
98
+ }
99
+ catch (e) {
100
+ throw new errors_js_1.X402Error('jws_malformed', `Failed to decode JWS payload: ${e instanceof Error ? e.message : String(e)}`);
101
+ }
102
+ // 6. Payload must be a plain object (not array, primitive, or null)
103
+ if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
104
+ throw new errors_js_1.X402Error('jws_payload_not_object', 'JWS payload must be a JSON object');
105
+ }
106
+ return {
107
+ header: header,
108
+ payload: payload,
109
+ signatureSegment: segments[2],
110
+ };
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Payload Extraction Helpers
114
+ // ---------------------------------------------------------------------------
115
+ /**
116
+ * Extract offer payload from a signed offer
117
+ *
118
+ * For EIP-712: returns the `payload` field directly.
119
+ * For JWS: decodes the compact JWS and returns the decoded payload.
120
+ *
121
+ * @param offer - The signed offer (discriminated union)
122
+ * @param maxJwsBytes - Maximum JWS byte length for JWS format
123
+ * @returns The raw offer payload
124
+ * @throws X402Error if extraction fails
125
+ */
126
+ function extractOfferPayload(offer, maxJwsBytes) {
127
+ if (offer.format === 'eip712') {
128
+ return offer.payload;
129
+ }
130
+ // JWS format: decode from compact serialization
131
+ const parsed = parseCompactJWS(offer.signature, maxJwsBytes);
132
+ return parsed.payload;
133
+ }
134
+ /**
135
+ * Extract receipt payload from a signed receipt
136
+ *
137
+ * For EIP-712: returns the `payload` field directly.
138
+ * For JWS: decodes the compact JWS and returns the decoded payload.
139
+ *
140
+ * @param receipt - The signed receipt (discriminated union)
141
+ * @param maxJwsBytes - Maximum JWS byte length for JWS format
142
+ * @returns The raw receipt payload
143
+ * @throws X402Error if extraction fails
144
+ */
145
+ function extractReceiptPayload(receipt, maxJwsBytes) {
146
+ if (receipt.format === 'eip712') {
147
+ return receipt.payload;
148
+ }
149
+ // JWS format: decode from compact serialization
150
+ const parsed = parseCompactJWS(receipt.signature, maxJwsBytes);
151
+ return parsed.payload;
152
+ }
153
+ /**
154
+ * Extract x402 extension info from an upstream response body
155
+ *
156
+ * Equivalent to upstream's `extractOffersFromPaymentRequired()` but operates
157
+ * on the raw response body rather than the upstream `PaymentRequired` type.
158
+ * Looks for `extensions["offer-receipt"].info` in the body.
159
+ *
160
+ * This is the entry point for converting raw upstream wire data into
161
+ * PEAC's `X402OfferReceiptChallenge` convenience type.
162
+ *
163
+ * @param body - The upstream response body (unknown shape)
164
+ * @returns The extension info, or null if absent/malformed
165
+ */
166
+ function extractExtensionInfo(body) {
167
+ if (typeof body !== 'object' || body === null || Array.isArray(body)) {
168
+ return null;
169
+ }
170
+ const typed = body;
171
+ const extensions = typed.extensions;
172
+ if (typeof extensions !== 'object' || extensions === null || Array.isArray(extensions)) {
173
+ return null;
174
+ }
175
+ const ext = extensions;
176
+ const offerReceipt = ext['offer-receipt'];
177
+ if (typeof offerReceipt !== 'object' || offerReceipt === null || Array.isArray(offerReceipt)) {
178
+ return null;
179
+ }
180
+ const orTyped = offerReceipt;
181
+ const info = orTyped.info;
182
+ if (typeof info !== 'object' || info === null || Array.isArray(info)) {
183
+ return null;
184
+ }
185
+ const infoTyped = info;
186
+ // Validate offers is an array
187
+ if (!Array.isArray(infoTyped.offers)) {
188
+ return null;
189
+ }
190
+ return {
191
+ offers: infoTyped.offers,
192
+ ...(infoTyped.receipt !== undefined && {
193
+ receipt: infoTyped.receipt,
194
+ }),
195
+ };
196
+ }
197
+ // ---------------------------------------------------------------------------
198
+ // Upstream Message Shape Extractors
199
+ // ---------------------------------------------------------------------------
200
+ /**
201
+ * Standard x402 response header names for payment receipts
202
+ *
203
+ * Upstream uses `X-PAYMENT-RESPONSE` (primary) and `PAYMENT-RESPONSE` (alias).
204
+ */
205
+ exports.X402_RECEIPT_HEADERS = ['x-payment-response', 'payment-response'];
206
+ /**
207
+ * Extract signed receipt from x402 settlement response headers
208
+ *
209
+ * Equivalent to upstream's `extractReceiptFromResponse()` but operates
210
+ * on raw headers rather than a `Response` object. Upstream sends the
211
+ * signed receipt as JSON in `X-PAYMENT-RESPONSE` or `PAYMENT-RESPONSE`.
212
+ *
213
+ * @param headers - Response headers (case-insensitive lookup)
214
+ * @returns The signed receipt, or null if absent/malformed
215
+ */
216
+ function extractReceiptFromHeaders(headers) {
217
+ // Case-insensitive header lookup
218
+ const lowerHeaders = {};
219
+ for (const [key, value] of Object.entries(headers)) {
220
+ if (value !== undefined) {
221
+ lowerHeaders[key.toLowerCase()] = Array.isArray(value) ? value[0] : value;
222
+ }
223
+ }
224
+ let raw;
225
+ for (const name of exports.X402_RECEIPT_HEADERS) {
226
+ raw = lowerHeaders[name];
227
+ if (raw)
228
+ break;
229
+ }
230
+ if (!raw)
231
+ return null;
232
+ try {
233
+ const parsed = JSON.parse(raw);
234
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
235
+ return null;
236
+ if (parsed.format !== 'eip712' && parsed.format !== 'jws')
237
+ return null;
238
+ if (typeof parsed.signature !== 'string')
239
+ return null;
240
+ return parsed;
241
+ }
242
+ catch {
243
+ return null;
244
+ }
245
+ }
246
+ //# sourceMappingURL=raw.js.map