@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.
- package/README.md +46 -19
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +14 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -22
- package/dist/index.js.map +1 -1
- package/dist/map.d.ts +29 -23
- package/dist/map.d.ts.map +1 -1
- package/dist/map.js +57 -36
- package/dist/map.js.map +1 -1
- package/dist/normalize.d.ts +97 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +75 -0
- package/dist/normalize.js.map +1 -0
- package/dist/raw.d.ts +246 -0
- package/dist/raw.d.ts.map +1 -0
- package/dist/raw.js +246 -0
- package/dist/raw.js.map +1 -0
- package/dist/types.d.ts +292 -144
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -16
- package/dist/types.js.map +1 -1
- package/dist/verify.d.ts +50 -41
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +380 -344
- package/dist/verify.js.map +1 -1
- package/package.json +5 -4
|
@@ -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
|