chit402-verify 0.3.5 → 0.4.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/LICENSE +1 -1
- package/README.md +205 -17
- package/dist/anchor-trust.d.ts +144 -0
- package/dist/anchor-trust.d.ts.map +1 -0
- package/dist/anchor-trust.js +441 -0
- package/dist/anchor-trust.js.map +1 -0
- package/dist/anchor-witness.d.ts +265 -0
- package/dist/anchor-witness.d.ts.map +1 -0
- package/dist/anchor-witness.js +833 -0
- package/dist/anchor-witness.js.map +1 -0
- package/dist/base-payer.d.ts +65 -0
- package/dist/base-payer.d.ts.map +1 -0
- package/dist/base-payer.js +222 -0
- package/dist/base-payer.js.map +1 -0
- package/dist/binding.d.ts +52 -0
- package/dist/binding.d.ts.map +1 -0
- package/dist/binding.js +80 -0
- package/dist/binding.js.map +1 -0
- package/dist/canonical-preimage.d.ts +9 -0
- package/dist/canonical-preimage.d.ts.map +1 -0
- package/dist/canonical-preimage.js +25 -0
- package/dist/canonical-preimage.js.map +1 -0
- package/dist/carry-forward.d.ts +79 -0
- package/dist/carry-forward.d.ts.map +1 -0
- package/dist/carry-forward.js +184 -0
- package/dist/carry-forward.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2086 -0
- package/dist/cli.js.map +1 -0
- package/dist/epoch.d.ts +143 -0
- package/dist/epoch.d.ts.map +1 -0
- package/dist/epoch.js +352 -0
- package/dist/epoch.js.map +1 -0
- package/dist/head-binding.d.ts +24 -0
- package/dist/head-binding.d.ts.map +1 -0
- package/dist/head-binding.js +61 -0
- package/dist/head-binding.js.map +1 -0
- package/dist/index.d.ts +585 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1648 -0
- package/dist/index.js.map +1 -0
- package/dist/issuer-history.d.ts +151 -0
- package/dist/issuer-history.d.ts.map +1 -0
- package/dist/issuer-history.js +595 -0
- package/dist/issuer-history.js.map +1 -0
- package/dist/issuer-pin.d.ts +126 -0
- package/dist/issuer-pin.d.ts.map +1 -0
- package/dist/issuer-pin.js +567 -0
- package/dist/issuer-pin.js.map +1 -0
- package/dist/jcs.d.ts +8 -0
- package/dist/jcs.d.ts.map +1 -0
- package/dist/jcs.js +64 -0
- package/dist/jcs.js.map +1 -0
- package/dist/jws.d.ts +46 -0
- package/dist/jws.d.ts.map +1 -0
- package/dist/jws.js +129 -0
- package/dist/jws.js.map +1 -0
- package/dist/payer.d.ts +65 -0
- package/dist/payer.d.ts.map +1 -0
- package/dist/payer.js +148 -0
- package/dist/payer.js.map +1 -0
- package/dist/preimage.d.ts +78 -0
- package/dist/preimage.d.ts.map +1 -0
- package/dist/preimage.js +363 -0
- package/dist/preimage.js.map +1 -0
- package/dist/receipt-lane.d.ts +235 -0
- package/dist/receipt-lane.d.ts.map +1 -0
- package/dist/receipt-lane.js +446 -0
- package/dist/receipt-lane.js.map +1 -0
- package/dist/receipt-version.d.ts +98 -0
- package/dist/receipt-version.d.ts.map +1 -0
- package/dist/receipt-version.js +364 -0
- package/dist/receipt-version.js.map +1 -0
- package/dist/refusal.d.ts +93 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +249 -0
- package/dist/refusal.js.map +1 -0
- package/dist/request-binding.d.ts +7 -0
- package/dist/request-binding.d.ts.map +1 -0
- package/dist/request-binding.js +26 -0
- package/dist/request-binding.js.map +1 -0
- package/dist/row-hash.d.ts +22 -0
- package/dist/row-hash.d.ts.map +1 -0
- package/dist/row-hash.js +26 -0
- package/dist/row-hash.js.map +1 -0
- package/dist/shell.d.ts +65 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +268 -0
- package/dist/shell.js.map +1 -0
- package/dist/solana-payer.d.ts +99 -0
- package/dist/solana-payer.d.ts.map +1 -0
- package/dist/solana-payer.js +258 -0
- package/dist/solana-payer.js.map +1 -0
- package/dist/v11-receipt.d.ts +23 -0
- package/dist/v11-receipt.d.ts.map +1 -0
- package/dist/v11-receipt.js +368 -0
- package/dist/v11-receipt.js.map +1 -0
- package/dist/verdict.d.ts +98 -0
- package/dist/verdict.d.ts.map +1 -0
- package/dist/verdict.js +390 -0
- package/dist/verdict.js.map +1 -0
- package/issuer-history.snapshot.json +1 -0
- package/package.json +32 -22
- package/cli.js +0 -42
- package/index.cjs +0 -8
- package/index.d.ts +0 -5
- package/index.js +0 -7
package/dist/jcs.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RFC 8785 JSON Canonicalization.
|
|
4
|
+
* Same rules as `jcsCanonicalize` in services/gateway/src/offer-receipt.js.
|
|
5
|
+
* Issuer-history entry hashes and the receipt canonical object use this form.
|
|
6
|
+
* Per-field receipt hashes keep the encoding they were signed with.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.jcsCanonicalize = jcsCanonicalize;
|
|
10
|
+
function jcsCanonicalize(value) {
|
|
11
|
+
return jcsValue(value);
|
|
12
|
+
}
|
|
13
|
+
function jcsValue(value) {
|
|
14
|
+
if (value === null || value === undefined)
|
|
15
|
+
return 'null';
|
|
16
|
+
const type = typeof value;
|
|
17
|
+
if (type === 'boolean')
|
|
18
|
+
return value ? 'true' : 'false';
|
|
19
|
+
if (type === 'number') {
|
|
20
|
+
if (!Number.isFinite(value))
|
|
21
|
+
throw new Error('Cannot canonicalize Infinity or NaN');
|
|
22
|
+
if (Object.is(value, -0))
|
|
23
|
+
return '0';
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
if (type === 'string')
|
|
27
|
+
return jcsString(value);
|
|
28
|
+
if (Array.isArray(value))
|
|
29
|
+
return `[${value.map(jcsValue).join(',')}]`;
|
|
30
|
+
if (type === 'object')
|
|
31
|
+
return jcsObject(value);
|
|
32
|
+
throw new Error(`Cannot canonicalize value of type ${type}`);
|
|
33
|
+
}
|
|
34
|
+
function jcsString(str) {
|
|
35
|
+
let result = '"';
|
|
36
|
+
for (let i = 0; i < str.length; i += 1) {
|
|
37
|
+
const char = str[i];
|
|
38
|
+
const code = str.charCodeAt(i);
|
|
39
|
+
if (code < 32) {
|
|
40
|
+
result += `\\u${code.toString(16).padStart(4, '0')}`;
|
|
41
|
+
}
|
|
42
|
+
else if (char === '"') {
|
|
43
|
+
result += '\\"';
|
|
44
|
+
}
|
|
45
|
+
else if (char === '\\') {
|
|
46
|
+
result += '\\\\';
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
result += char;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return `${result}"`;
|
|
53
|
+
}
|
|
54
|
+
function jcsObject(obj) {
|
|
55
|
+
const keys = Object.keys(obj).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
56
|
+
const pairs = [];
|
|
57
|
+
for (const key of keys) {
|
|
58
|
+
const value = obj[key];
|
|
59
|
+
if (value !== undefined)
|
|
60
|
+
pairs.push(`${jcsString(key)}:${jcsValue(value)}`);
|
|
61
|
+
}
|
|
62
|
+
return `{${pairs.join(',')}}`;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=jcs.js.map
|
package/dist/jcs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jcs.js","sourceRoot":"","sources":["../src/jcs.ts"],"names":[],"mappings":"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"}
|
package/dist/jws.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface Es256Jwk {
|
|
2
|
+
kty: 'EC';
|
|
3
|
+
crv: 'P-256';
|
|
4
|
+
x: string;
|
|
5
|
+
y: string;
|
|
6
|
+
kid?: string;
|
|
7
|
+
alg?: string;
|
|
8
|
+
use?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ReceiptWithIssuerJwk {
|
|
11
|
+
issuer_signature?: {
|
|
12
|
+
kid?: string;
|
|
13
|
+
jws?: string;
|
|
14
|
+
issuer_jwk?: Es256Jwk;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Production issuer kid (RFC 7638 thumbprint of the api.chit402.com ES256 key).
|
|
19
|
+
* Offline default pin. Override with `trustedKids` / `--trusted-kid`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_TRUSTED_ISSUER_KIDS: readonly ["IvFpmC-vPhkY_v0vidsrWVT9uzlE5XWKZgAEOeJTq1Q"];
|
|
22
|
+
/** Hosts whose published JWKS may be fetched and treated as a trust root. */
|
|
23
|
+
export declare const DEFAULT_TRUSTED_JWKS_HOSTS: readonly ["api.chit402.com"];
|
|
24
|
+
export declare const KEY_UNTRUSTED = "key untrusted";
|
|
25
|
+
export declare function resolvePinnedIssuerJwk(receipt: ReceiptWithIssuerJwk): Es256Jwk | null;
|
|
26
|
+
/**
|
|
27
|
+
* RFC 7638 JWK thumbprint (SHA-256, base64url).
|
|
28
|
+
* EC required members in lexicographic order: crv, kty, x, y.
|
|
29
|
+
*/
|
|
30
|
+
export declare function jwkThumbprint(jwk: Pick<Es256Jwk, 'kty' | 'crv' | 'x' | 'y'>): string;
|
|
31
|
+
export declare function isEs256PublicJwk(jwk: Es256Jwk | null | undefined): jwk is Es256Jwk;
|
|
32
|
+
/** True when the key's RFC 7638 thumbprint is one of the pinned trusted kids. */
|
|
33
|
+
export declare function isPinnedTrustedJwk(jwk: Es256Jwk | null | undefined, trustedKids: readonly string[]): boolean;
|
|
34
|
+
export declare function sameEs256Key(a: Es256Jwk, b: Es256Jwk): boolean;
|
|
35
|
+
export declare function readJwsHeader(jws: string): {
|
|
36
|
+
alg?: string;
|
|
37
|
+
kid?: string;
|
|
38
|
+
jku?: string;
|
|
39
|
+
} | null;
|
|
40
|
+
export declare function jwksHostAllowed(uri: string, hosts: readonly string[]): boolean;
|
|
41
|
+
export declare function verifyIssuerJws(jws: string, jwk: Es256Jwk): {
|
|
42
|
+
valid: boolean;
|
|
43
|
+
payload?: Record<string, unknown>;
|
|
44
|
+
reason?: string;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=jws.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jws.d.ts","sourceRoot":"","sources":["../src/jws.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,EAAE,OAAO,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,gBAAgB,CAAC,EAAE;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,QAAQ,CAAC;KACvB,CAAC;CACH;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,0DAE9B,CAAC;AAEX,6EAA6E;AAC7E,eAAO,MAAM,0BAA0B,8BAA+B,CAAC;AAEvE,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,QAAQ,GAAG,IAAI,CAMrF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,MAAM,CAQpF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GAAG,GAAG,IAAI,QAAQ,CAElF;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EAChC,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAGT;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAE9D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQ9F;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAQ9E;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,QAAQ,GACZ;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAyCxE"}
|
package/dist/jws.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KEY_UNTRUSTED = exports.DEFAULT_TRUSTED_JWKS_HOSTS = exports.DEFAULT_TRUSTED_ISSUER_KIDS = void 0;
|
|
4
|
+
exports.resolvePinnedIssuerJwk = resolvePinnedIssuerJwk;
|
|
5
|
+
exports.jwkThumbprint = jwkThumbprint;
|
|
6
|
+
exports.isEs256PublicJwk = isEs256PublicJwk;
|
|
7
|
+
exports.isPinnedTrustedJwk = isPinnedTrustedJwk;
|
|
8
|
+
exports.sameEs256Key = sameEs256Key;
|
|
9
|
+
exports.readJwsHeader = readJwsHeader;
|
|
10
|
+
exports.jwksHostAllowed = jwksHostAllowed;
|
|
11
|
+
exports.verifyIssuerJws = verifyIssuerJws;
|
|
12
|
+
/**
|
|
13
|
+
* Compact JWS (ES256) verification for Chit402 receipts.
|
|
14
|
+
*
|
|
15
|
+
* A signature is only valid when the verifying key is trusted:
|
|
16
|
+
* - it is the JWKS entry for the JWS `kid` (caller-supplied file, or a JWKS
|
|
17
|
+
* fetched from an allowlisted issuer `jwks_uri`), or
|
|
18
|
+
* - its RFC 7638 thumbprint equals a pinned trusted kid.
|
|
19
|
+
* The receipt's embedded `issuer_jwk` is not a trust root by itself.
|
|
20
|
+
*/
|
|
21
|
+
const node_crypto_1 = require("node:crypto");
|
|
22
|
+
/**
|
|
23
|
+
* Production issuer kid (RFC 7638 thumbprint of the api.chit402.com ES256 key).
|
|
24
|
+
* Offline default pin. Override with `trustedKids` / `--trusted-kid`.
|
|
25
|
+
*/
|
|
26
|
+
exports.DEFAULT_TRUSTED_ISSUER_KIDS = [
|
|
27
|
+
'IvFpmC-vPhkY_v0vidsrWVT9uzlE5XWKZgAEOeJTq1Q',
|
|
28
|
+
];
|
|
29
|
+
/** Hosts whose published JWKS may be fetched and treated as a trust root. */
|
|
30
|
+
exports.DEFAULT_TRUSTED_JWKS_HOSTS = ['api.chit402.com'];
|
|
31
|
+
exports.KEY_UNTRUSTED = 'key untrusted';
|
|
32
|
+
function resolvePinnedIssuerJwk(receipt) {
|
|
33
|
+
const jwk = receipt.issuer_signature?.issuer_jwk;
|
|
34
|
+
if (!jwk || jwk.kty !== 'EC' || jwk.crv !== 'P-256' || !jwk.x || !jwk.y)
|
|
35
|
+
return null;
|
|
36
|
+
const kid = receipt.issuer_signature?.kid;
|
|
37
|
+
if (kid && jwk.kid && kid !== jwk.kid)
|
|
38
|
+
return null;
|
|
39
|
+
return jwk;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* RFC 7638 JWK thumbprint (SHA-256, base64url).
|
|
43
|
+
* EC required members in lexicographic order: crv, kty, x, y.
|
|
44
|
+
*/
|
|
45
|
+
function jwkThumbprint(jwk) {
|
|
46
|
+
const canonical = JSON.stringify({
|
|
47
|
+
crv: jwk.crv,
|
|
48
|
+
kty: jwk.kty,
|
|
49
|
+
x: jwk.x,
|
|
50
|
+
y: jwk.y,
|
|
51
|
+
});
|
|
52
|
+
return (0, node_crypto_1.createHash)('sha256').update(canonical).digest('base64url');
|
|
53
|
+
}
|
|
54
|
+
function isEs256PublicJwk(jwk) {
|
|
55
|
+
return !!jwk && jwk.kty === 'EC' && jwk.crv === 'P-256' && !!jwk.x && !!jwk.y;
|
|
56
|
+
}
|
|
57
|
+
/** True when the key's RFC 7638 thumbprint is one of the pinned trusted kids. */
|
|
58
|
+
function isPinnedTrustedJwk(jwk, trustedKids) {
|
|
59
|
+
if (!isEs256PublicJwk(jwk) || trustedKids.length === 0)
|
|
60
|
+
return false;
|
|
61
|
+
return trustedKids.includes(jwkThumbprint(jwk));
|
|
62
|
+
}
|
|
63
|
+
function sameEs256Key(a, b) {
|
|
64
|
+
return a.x === b.x && a.y === b.y && a.crv === b.crv && a.kty === b.kty;
|
|
65
|
+
}
|
|
66
|
+
function readJwsHeader(jws) {
|
|
67
|
+
const headerB64 = jws.split('.')[0];
|
|
68
|
+
if (!headerB64)
|
|
69
|
+
return null;
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(Buffer.from(headerB64, 'base64url').toString('utf8'));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function jwksHostAllowed(uri, hosts) {
|
|
78
|
+
try {
|
|
79
|
+
const url = new URL(uri);
|
|
80
|
+
if (url.protocol !== 'https:')
|
|
81
|
+
return false;
|
|
82
|
+
return hosts.some((host) => host.toLowerCase() === url.hostname.toLowerCase());
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function verifyIssuerJws(jws, jwk) {
|
|
89
|
+
if (!jws || typeof jws !== 'string') {
|
|
90
|
+
return { valid: false, reason: 'invalid_jws' };
|
|
91
|
+
}
|
|
92
|
+
const parts = jws.split('.');
|
|
93
|
+
if (parts.length !== 3) {
|
|
94
|
+
return { valid: false, reason: 'malformed_jws' };
|
|
95
|
+
}
|
|
96
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
97
|
+
let header;
|
|
98
|
+
let payload;
|
|
99
|
+
try {
|
|
100
|
+
header = JSON.parse(Buffer.from(headerB64, 'base64url').toString('utf8'));
|
|
101
|
+
payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf8'));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return { valid: false, reason: 'json_parse_error' };
|
|
105
|
+
}
|
|
106
|
+
if (header.alg !== 'ES256') {
|
|
107
|
+
return { valid: false, reason: `unsupported_alg: ${header.alg}` };
|
|
108
|
+
}
|
|
109
|
+
if (header.kid && jwk.kid && header.kid !== jwk.kid) {
|
|
110
|
+
return { valid: false, reason: 'kid_mismatch' };
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const publicKey = (0, node_crypto_1.createPublicKey)({
|
|
114
|
+
key: { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y },
|
|
115
|
+
format: 'jwk',
|
|
116
|
+
});
|
|
117
|
+
const signature = Buffer.from(signatureB64, 'base64url');
|
|
118
|
+
const signingInput = `${headerB64}.${payloadB64}`;
|
|
119
|
+
const valid = (0, node_crypto_1.verify)('sha256', Buffer.from(signingInput, 'utf8'), {
|
|
120
|
+
key: publicKey,
|
|
121
|
+
dsaEncoding: 'ieee-p1363',
|
|
122
|
+
}, signature);
|
|
123
|
+
return valid ? { valid: true, payload } : { valid: false, reason: 'signature_invalid' };
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return { valid: false, reason: `verification_error: ${err.message}` };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=jws.js.map
|
package/dist/jws.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jws.js","sourceRoot":"","sources":["../src/jws.ts"],"names":[],"mappings":"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"}
|
package/dist/payer.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified offline payer binding verification (Base + Solana).
|
|
3
|
+
*/
|
|
4
|
+
import { type BasePayerVerification, type BaseReceiptFetcher } from './base-payer.js';
|
|
5
|
+
import { type SolanaPayerVerification, type SolanaRpcFetcher } from './solana-payer.js';
|
|
6
|
+
export type PayerRail = 'base' | 'solana' | 'unknown';
|
|
7
|
+
export interface PayerBindingVerification {
|
|
8
|
+
checked: boolean;
|
|
9
|
+
valid: boolean;
|
|
10
|
+
rail: PayerRail;
|
|
11
|
+
payerWallet?: string | null;
|
|
12
|
+
paymentRef?: string | null;
|
|
13
|
+
expectedAmount?: string | null;
|
|
14
|
+
base?: BasePayerVerification;
|
|
15
|
+
solana?: SolanaPayerVerification;
|
|
16
|
+
reason?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ReceiptPayerClaims {
|
|
19
|
+
payment?: {
|
|
20
|
+
ref?: string | null;
|
|
21
|
+
gross_amount?: string | null;
|
|
22
|
+
payee?: string | null;
|
|
23
|
+
asset?: string | null;
|
|
24
|
+
rail?: string | null;
|
|
25
|
+
} | null;
|
|
26
|
+
caller_binding?: {
|
|
27
|
+
payer_wallet?: string | null;
|
|
28
|
+
} | null;
|
|
29
|
+
}
|
|
30
|
+
export interface VerifyPayerBindingOptions {
|
|
31
|
+
rpcUrl?: string;
|
|
32
|
+
solanaRpcUrl?: string;
|
|
33
|
+
fetchSolanaTransaction?: SolanaRpcFetcher;
|
|
34
|
+
fetchBaseReceipt?: BaseReceiptFetcher;
|
|
35
|
+
}
|
|
36
|
+
/** Decode the payload segment of a compact JWS (no signature check). */
|
|
37
|
+
export declare function decodeJwsPayload(jws: string): Record<string, unknown> | null;
|
|
38
|
+
interface PaymentClaim {
|
|
39
|
+
ref?: string | null;
|
|
40
|
+
gross_amount?: string | null;
|
|
41
|
+
payee?: string | null;
|
|
42
|
+
asset?: string | null;
|
|
43
|
+
rail?: string | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Payer facts from the JWS payload when one is present.
|
|
47
|
+
* The outer `payment` / `caller_binding` copies are unsigned and are used only
|
|
48
|
+
* when the receipt has no JWS. Callers that need verified facts must check the
|
|
49
|
+
* issuer signature first; this decode does not itself prove the signature.
|
|
50
|
+
*/
|
|
51
|
+
export declare function receiptPayerClaimsFromEnvelope(receipt: {
|
|
52
|
+
payment?: PaymentClaim | null;
|
|
53
|
+
caller_binding?: {
|
|
54
|
+
payer_wallet?: string | null;
|
|
55
|
+
} | null;
|
|
56
|
+
issuer_signature?: {
|
|
57
|
+
jws?: string;
|
|
58
|
+
} | null;
|
|
59
|
+
}): ReceiptPayerClaims;
|
|
60
|
+
/**
|
|
61
|
+
* Verify caller_binding.payer_wallet against on-chain settlement in payment.ref.
|
|
62
|
+
*/
|
|
63
|
+
export declare function verifyPayerBinding(claims: ReceiptPayerClaims, options?: VerifyPayerBindingOptions): Promise<PayerBindingVerification>;
|
|
64
|
+
export {};
|
|
65
|
+
//# sourceMappingURL=payer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payer.d.ts","sourceRoot":"","sources":["../src/payer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAmB,KAAK,qBAAqB,EAAE,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACvG,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtD,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,qBAAqB,CAAC;IAC7B,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,GAAG,IAAI,CAAC;IACT,cAAc,CAAC,EAAE;QACf,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAC1C,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;CACvC;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQ5E;AAED,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAYD;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE;IACtD,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,gBAAgB,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC5C,GAAG,kBAAkB,CAyBrB;AAYD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA2EnC"}
|
package/dist/payer.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unified offline payer binding verification (Base + Solana).
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.decodeJwsPayload = decodeJwsPayload;
|
|
7
|
+
exports.receiptPayerClaimsFromEnvelope = receiptPayerClaimsFromEnvelope;
|
|
8
|
+
exports.verifyPayerBinding = verifyPayerBinding;
|
|
9
|
+
const base_payer_js_1 = require("./base-payer.js");
|
|
10
|
+
const solana_payer_js_1 = require("./solana-payer.js");
|
|
11
|
+
/** Decode the payload segment of a compact JWS (no signature check). */
|
|
12
|
+
function decodeJwsPayload(jws) {
|
|
13
|
+
const parts = jws.split('.');
|
|
14
|
+
if (parts.length < 2)
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function paymentClaims(payment) {
|
|
24
|
+
return {
|
|
25
|
+
ref: payment?.ref ?? null,
|
|
26
|
+
gross_amount: payment?.gross_amount != null ? String(payment.gross_amount) : null,
|
|
27
|
+
payee: payment?.payee ?? null,
|
|
28
|
+
asset: payment?.asset ?? null,
|
|
29
|
+
rail: payment?.rail ?? null,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Payer facts from the JWS payload when one is present.
|
|
34
|
+
* The outer `payment` / `caller_binding` copies are unsigned and are used only
|
|
35
|
+
* when the receipt has no JWS. Callers that need verified facts must check the
|
|
36
|
+
* issuer signature first; this decode does not itself prove the signature.
|
|
37
|
+
*/
|
|
38
|
+
function receiptPayerClaimsFromEnvelope(receipt) {
|
|
39
|
+
const jws = receipt.issuer_signature?.jws;
|
|
40
|
+
if (jws) {
|
|
41
|
+
const claims = decodeJwsPayload(jws);
|
|
42
|
+
if (claims) {
|
|
43
|
+
const payment = claims.payment;
|
|
44
|
+
const callerBinding = claims.caller_binding;
|
|
45
|
+
return {
|
|
46
|
+
payment: paymentClaims(payment),
|
|
47
|
+
caller_binding: callerBinding ?? null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (receipt.payment?.ref || receipt.caller_binding?.payer_wallet) {
|
|
52
|
+
return {
|
|
53
|
+
payment: paymentClaims(receipt.payment),
|
|
54
|
+
caller_binding: receipt.caller_binding ?? null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
payment: { ref: null, gross_amount: null, payee: null, asset: null, rail: null },
|
|
59
|
+
caller_binding: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function detectRail(paymentRef) {
|
|
63
|
+
if (!paymentRef)
|
|
64
|
+
return 'unknown';
|
|
65
|
+
const lower = paymentRef.toLowerCase();
|
|
66
|
+
if (lower.startsWith('solana:'))
|
|
67
|
+
return 'solana';
|
|
68
|
+
if (lower.startsWith('base:') || lower.startsWith('base-sepolia:') || lower.startsWith('eip155:')) {
|
|
69
|
+
return 'base';
|
|
70
|
+
}
|
|
71
|
+
return 'unknown';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Verify caller_binding.payer_wallet against on-chain settlement in payment.ref.
|
|
75
|
+
*/
|
|
76
|
+
async function verifyPayerBinding(claims, options = {}) {
|
|
77
|
+
const paymentRef = claims.payment?.ref ?? null;
|
|
78
|
+
const payerWallet = claims.caller_binding?.payer_wallet ?? null;
|
|
79
|
+
const grossAmount = claims.payment?.gross_amount ?? null;
|
|
80
|
+
const payee = claims.payment?.payee ?? null;
|
|
81
|
+
const asset = claims.payment?.asset ?? null;
|
|
82
|
+
const rail = detectRail(paymentRef);
|
|
83
|
+
if (!paymentRef) {
|
|
84
|
+
return { checked: false, valid: false, rail, reason: 'no_payment_ref' };
|
|
85
|
+
}
|
|
86
|
+
if (!payerWallet) {
|
|
87
|
+
return { checked: false, valid: false, rail, paymentRef, reason: 'no_payer_wallet' };
|
|
88
|
+
}
|
|
89
|
+
if (grossAmount == null || grossAmount === '') {
|
|
90
|
+
return { checked: false, valid: false, rail, paymentRef, payerWallet, reason: 'no_gross_amount' };
|
|
91
|
+
}
|
|
92
|
+
if (rail === 'solana') {
|
|
93
|
+
const solana = await (0, solana_payer_js_1.verifySolanaPayer)({
|
|
94
|
+
paymentRef,
|
|
95
|
+
payerWallet,
|
|
96
|
+
grossAmount,
|
|
97
|
+
rpcUrl: options.solanaRpcUrl || options.rpcUrl,
|
|
98
|
+
fetchTransaction: options.fetchSolanaTransaction,
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
checked: solana.checked,
|
|
102
|
+
valid: solana.valid,
|
|
103
|
+
rail: 'solana',
|
|
104
|
+
payerWallet,
|
|
105
|
+
paymentRef,
|
|
106
|
+
expectedAmount: grossAmount,
|
|
107
|
+
solana,
|
|
108
|
+
reason: solana.reason,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (rail === 'base') {
|
|
112
|
+
if (!payee) {
|
|
113
|
+
return { checked: false, valid: false, rail, paymentRef, payerWallet, expectedAmount: grossAmount, reason: 'no_payee' };
|
|
114
|
+
}
|
|
115
|
+
if (!asset) {
|
|
116
|
+
return { checked: false, valid: false, rail, paymentRef, payerWallet, expectedAmount: grossAmount, reason: 'no_asset' };
|
|
117
|
+
}
|
|
118
|
+
const base = await (0, base_payer_js_1.verifyBasePayer)({
|
|
119
|
+
paymentRef,
|
|
120
|
+
payerWallet,
|
|
121
|
+
grossAmount,
|
|
122
|
+
payee,
|
|
123
|
+
asset,
|
|
124
|
+
rpcUrl: options.rpcUrl,
|
|
125
|
+
fetchReceipt: options.fetchBaseReceipt,
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
checked: base.checked,
|
|
129
|
+
valid: base.valid,
|
|
130
|
+
rail: 'base',
|
|
131
|
+
payerWallet,
|
|
132
|
+
paymentRef,
|
|
133
|
+
expectedAmount: grossAmount,
|
|
134
|
+
base,
|
|
135
|
+
reason: base.reason,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
checked: false,
|
|
140
|
+
valid: false,
|
|
141
|
+
rail: 'unknown',
|
|
142
|
+
payerWallet,
|
|
143
|
+
paymentRef,
|
|
144
|
+
expectedAmount: grossAmount,
|
|
145
|
+
reason: `unsupported_payment_ref: ${paymentRef}`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=payer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payer.js","sourceRoot":"","sources":["../src/payer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export interface PreimageLeaf {
|
|
2
|
+
preimage_utf8?: string;
|
|
3
|
+
preimage_hex?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface AuditSibling {
|
|
6
|
+
hash?: string;
|
|
7
|
+
/** Ignored. Index and tree size choose the side. */
|
|
8
|
+
position?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AuditPath {
|
|
11
|
+
index?: number;
|
|
12
|
+
tree_size?: number;
|
|
13
|
+
siblings?: AuditSibling[];
|
|
14
|
+
root?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PreimageEntry {
|
|
17
|
+
field?: string;
|
|
18
|
+
alg?: string;
|
|
19
|
+
recomputable?: boolean;
|
|
20
|
+
preimage_utf8?: string;
|
|
21
|
+
preimage_hex?: string;
|
|
22
|
+
hash?: string;
|
|
23
|
+
merkle?: string;
|
|
24
|
+
/** Old public shape: every leaf body in the prefix. Still accepted. */
|
|
25
|
+
leaves?: PreimageLeaf[];
|
|
26
|
+
/** This receipt's leaf body. Other leaves are not on this object. */
|
|
27
|
+
leaf?: PreimageLeaf & {
|
|
28
|
+
index?: number;
|
|
29
|
+
task_id?: string;
|
|
30
|
+
kind?: string;
|
|
31
|
+
};
|
|
32
|
+
audit_path?: AuditPath;
|
|
33
|
+
url?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface PreimageBlock {
|
|
36
|
+
schema?: string;
|
|
37
|
+
fields?: Record<string, PreimageEntry>;
|
|
38
|
+
not_recomputable?: {
|
|
39
|
+
field?: string;
|
|
40
|
+
hash?: string | null;
|
|
41
|
+
reason?: string;
|
|
42
|
+
}[];
|
|
43
|
+
links?: {
|
|
44
|
+
preimage?: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface PreimageCheck {
|
|
48
|
+
checked: boolean;
|
|
49
|
+
ok: boolean;
|
|
50
|
+
errors: string[];
|
|
51
|
+
fields: {
|
|
52
|
+
field: string;
|
|
53
|
+
ok: boolean;
|
|
54
|
+
reason?: string;
|
|
55
|
+
}[];
|
|
56
|
+
}
|
|
57
|
+
/** The audit path folds to this receipt's leaf. A different leaf in the same prefix is not this receipt. */
|
|
58
|
+
export declare const LEAF_NOT_BOUND = "leaf_not_bound";
|
|
59
|
+
/**
|
|
60
|
+
* Bind an audit path to this receipt. Returns `leaf_not_bound` when any
|
|
61
|
+
* check fails, including when the agreed row hash is null, empty, or
|
|
62
|
+
* disagreed, or the leaf encoding does not canonicalize to `task_id|row_hash`.
|
|
63
|
+
* Inclusion leaf hash and leaf index are checked only when that field is present.
|
|
64
|
+
* Inclusion tree size is not compared: the witness can cover a longer log.
|
|
65
|
+
*/
|
|
66
|
+
export declare function auditLeafBinding(receipt: Record<string, unknown>, entry: PreimageEntry): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Fields a stranger is expected to recompute when they are present.
|
|
69
|
+
* Coverage and tree_head_hash count only when the issuer published a preimage.
|
|
70
|
+
* Output, HMAC, and book-row universe hashes do not.
|
|
71
|
+
*/
|
|
72
|
+
export declare function requiredPreimageFields(receipt: Record<string, unknown>): string[];
|
|
73
|
+
export declare function verifyPublishedPreimages(receipt: Record<string, unknown>, { requirePreimages, fetchImpl, trustedHosts, }?: {
|
|
74
|
+
requirePreimages?: boolean;
|
|
75
|
+
fetchImpl?: typeof fetch;
|
|
76
|
+
trustedHosts?: readonly string[];
|
|
77
|
+
}): Promise<PreimageCheck>;
|
|
78
|
+
//# sourceMappingURL=preimage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preimage.d.ts","sourceRoot":"","sources":["../src/preimage.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,qEAAqE;IACrE,IAAI,CAAC,EAAE,YAAY,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,gBAAgB,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/E,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3D;AA6DD,4GAA4G;AAC5G,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAqE/C;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CA6BtG;AAyED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAUjF;AAmBD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,EACE,gBAAwB,EACxB,SAA4B,EAC5B,YAAkC,GACnC,GAAE;IACD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,GACL,OAAO,CAAC,aAAa,CAAC,CA4DxB"}
|