@hatk/hatk 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/backfill.d.ts +11 -0
  2. package/dist/backfill.d.ts.map +1 -0
  3. package/dist/backfill.js +328 -0
  4. package/dist/car.d.ts +5 -0
  5. package/dist/car.d.ts.map +1 -0
  6. package/dist/car.js +52 -0
  7. package/dist/cbor.d.ts +7 -0
  8. package/dist/cbor.d.ts.map +1 -0
  9. package/dist/cbor.js +89 -0
  10. package/dist/cid.d.ts +4 -0
  11. package/dist/cid.d.ts.map +1 -0
  12. package/dist/cid.js +39 -0
  13. package/dist/cli.d.ts +3 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +1663 -0
  16. package/dist/config.d.ts +47 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +43 -0
  19. package/dist/db.d.ts +134 -0
  20. package/dist/db.d.ts.map +1 -0
  21. package/dist/db.js +1361 -0
  22. package/dist/feeds.d.ts +95 -0
  23. package/dist/feeds.d.ts.map +1 -0
  24. package/dist/feeds.js +144 -0
  25. package/dist/fts.d.ts +20 -0
  26. package/dist/fts.d.ts.map +1 -0
  27. package/dist/fts.js +762 -0
  28. package/dist/hydrate.d.ts +23 -0
  29. package/dist/hydrate.d.ts.map +1 -0
  30. package/dist/hydrate.js +75 -0
  31. package/dist/indexer.d.ts +14 -0
  32. package/dist/indexer.d.ts.map +1 -0
  33. package/dist/indexer.js +316 -0
  34. package/dist/labels.d.ts +29 -0
  35. package/dist/labels.d.ts.map +1 -0
  36. package/dist/labels.js +111 -0
  37. package/dist/lex-types.d.ts +401 -0
  38. package/dist/lex-types.d.ts.map +1 -0
  39. package/dist/lex-types.js +4 -0
  40. package/dist/lexicon-resolve.d.ts +14 -0
  41. package/dist/lexicon-resolve.d.ts.map +1 -0
  42. package/dist/lexicon-resolve.js +280 -0
  43. package/dist/logger.d.ts +4 -0
  44. package/dist/logger.d.ts.map +1 -0
  45. package/dist/logger.js +23 -0
  46. package/dist/main.d.ts +3 -0
  47. package/dist/main.d.ts.map +1 -0
  48. package/dist/main.js +148 -0
  49. package/dist/mst.d.ts +6 -0
  50. package/dist/mst.d.ts.map +1 -0
  51. package/dist/mst.js +30 -0
  52. package/dist/oauth/client.d.ts +16 -0
  53. package/dist/oauth/client.d.ts.map +1 -0
  54. package/dist/oauth/client.js +54 -0
  55. package/dist/oauth/crypto.d.ts +28 -0
  56. package/dist/oauth/crypto.d.ts.map +1 -0
  57. package/dist/oauth/crypto.js +101 -0
  58. package/dist/oauth/db.d.ts +47 -0
  59. package/dist/oauth/db.d.ts.map +1 -0
  60. package/dist/oauth/db.js +139 -0
  61. package/dist/oauth/discovery.d.ts +22 -0
  62. package/dist/oauth/discovery.d.ts.map +1 -0
  63. package/dist/oauth/discovery.js +50 -0
  64. package/dist/oauth/dpop.d.ts +11 -0
  65. package/dist/oauth/dpop.d.ts.map +1 -0
  66. package/dist/oauth/dpop.js +56 -0
  67. package/dist/oauth/hooks.d.ts +10 -0
  68. package/dist/oauth/hooks.d.ts.map +1 -0
  69. package/dist/oauth/hooks.js +40 -0
  70. package/dist/oauth/server.d.ts +86 -0
  71. package/dist/oauth/server.d.ts.map +1 -0
  72. package/dist/oauth/server.js +572 -0
  73. package/dist/opengraph.d.ts +34 -0
  74. package/dist/opengraph.d.ts.map +1 -0
  75. package/dist/opengraph.js +198 -0
  76. package/dist/schema.d.ts +51 -0
  77. package/dist/schema.d.ts.map +1 -0
  78. package/dist/schema.js +358 -0
  79. package/dist/seed.d.ts +29 -0
  80. package/dist/seed.d.ts.map +1 -0
  81. package/dist/seed.js +86 -0
  82. package/dist/server.d.ts +6 -0
  83. package/dist/server.d.ts.map +1 -0
  84. package/dist/server.js +1024 -0
  85. package/dist/setup.d.ts +8 -0
  86. package/dist/setup.d.ts.map +1 -0
  87. package/dist/setup.js +48 -0
  88. package/dist/test-browser.d.ts +14 -0
  89. package/dist/test-browser.d.ts.map +1 -0
  90. package/dist/test-browser.js +26 -0
  91. package/dist/test.d.ts +47 -0
  92. package/dist/test.d.ts.map +1 -0
  93. package/dist/test.js +256 -0
  94. package/dist/views.d.ts +40 -0
  95. package/dist/views.d.ts.map +1 -0
  96. package/dist/views.js +178 -0
  97. package/dist/vite-plugin.d.ts +5 -0
  98. package/dist/vite-plugin.d.ts.map +1 -0
  99. package/dist/vite-plugin.js +86 -0
  100. package/dist/xrpc-client.d.ts +18 -0
  101. package/dist/xrpc-client.d.ts.map +1 -0
  102. package/dist/xrpc-client.js +54 -0
  103. package/dist/xrpc.d.ts +53 -0
  104. package/dist/xrpc.d.ts.map +1 -0
  105. package/dist/xrpc.js +139 -0
  106. package/fonts/Inter-Regular.woff +0 -0
  107. package/package.json +41 -0
  108. package/public/admin-auth.js +320 -0
  109. package/public/admin.html +2166 -0
@@ -0,0 +1,101 @@
1
+ // packages/hatk/src/oauth/crypto.ts
2
+ const P256_N = BigInt('0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551');
3
+ const P256_N_DIV_2 = P256_N / 2n;
4
+ export function base64UrlEncode(bytes) {
5
+ let binary = '';
6
+ for (const byte of bytes)
7
+ binary += String.fromCharCode(byte);
8
+ const base64 = btoa(binary);
9
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
10
+ }
11
+ export function base64UrlDecode(str) {
12
+ const base64 = str.replace(/-/g, '+').replace(/_/g, '/');
13
+ const pad = base64.length % 4;
14
+ const padded = pad ? base64 + '='.repeat(4 - pad) : base64;
15
+ const binary = atob(padded);
16
+ const bytes = new Uint8Array(binary.length);
17
+ for (let i = 0; i < binary.length; i++)
18
+ bytes[i] = binary.charCodeAt(i);
19
+ return bytes;
20
+ }
21
+ function bytesToBigInt(bytes) {
22
+ let hex = '';
23
+ for (const b of bytes)
24
+ hex += b.toString(16).padStart(2, '0');
25
+ return BigInt('0x' + hex);
26
+ }
27
+ function bigIntToBytes(n, length) {
28
+ const hex = n.toString(16).padStart(length * 2, '0');
29
+ const bytes = new Uint8Array(length);
30
+ for (let i = 0; i < length; i++)
31
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
32
+ return bytes;
33
+ }
34
+ export async function generateKeyPair() {
35
+ const keyPair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']);
36
+ const privateJwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey);
37
+ const publicJwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey);
38
+ return { privateJwk, publicJwk };
39
+ }
40
+ export async function importPrivateKey(jwk) {
41
+ return crypto.subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
42
+ }
43
+ export async function importPublicKey(jwk) {
44
+ return crypto.subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);
45
+ }
46
+ export async function signEs256(privateKey, data) {
47
+ const signature = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, privateKey, data);
48
+ const sig = new Uint8Array(signature);
49
+ const r = sig.slice(0, 32);
50
+ const s = sig.slice(32, 64);
51
+ const sBigInt = bytesToBigInt(s);
52
+ // Low-S normalization (AT Protocol requirement)
53
+ if (sBigInt > P256_N_DIV_2) {
54
+ const newS = P256_N - sBigInt;
55
+ const normalized = new Uint8Array(64);
56
+ normalized.set(r, 0);
57
+ normalized.set(bigIntToBytes(newS, 32), 32);
58
+ return normalized;
59
+ }
60
+ return sig;
61
+ }
62
+ export async function verifyEs256(publicKey, signature, data) {
63
+ return crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, publicKey, signature, data);
64
+ }
65
+ export async function computeJwkThumbprint(jwk) {
66
+ const input = JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y });
67
+ const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input));
68
+ return base64UrlEncode(new Uint8Array(hash));
69
+ }
70
+ export async function sha256(data) {
71
+ return new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(data)));
72
+ }
73
+ export function createJwt(header, payload, signature) {
74
+ const h = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)));
75
+ const p = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)));
76
+ const s = base64UrlEncode(signature);
77
+ return `${h}.${p}.${s}`;
78
+ }
79
+ export function parseJwt(token) {
80
+ const parts = token.split('.');
81
+ if (parts.length !== 3)
82
+ throw new Error('Invalid JWT format');
83
+ const header = JSON.parse(new TextDecoder().decode(base64UrlDecode(parts[0])));
84
+ const payload = JSON.parse(new TextDecoder().decode(base64UrlDecode(parts[1])));
85
+ const signatureInput = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
86
+ const signature = base64UrlDecode(parts[2]);
87
+ return { header, payload, signatureInput, signature };
88
+ }
89
+ export async function signJwt(header, payload, privateKey) {
90
+ const h = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)));
91
+ const p = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)));
92
+ const input = new TextEncoder().encode(`${h}.${p}`);
93
+ const sig = await signEs256(privateKey, input);
94
+ return `${h}.${p}.${base64UrlEncode(sig)}`;
95
+ }
96
+ export function randomBytes(length) {
97
+ return crypto.getRandomValues(new Uint8Array(length));
98
+ }
99
+ export function randomToken() {
100
+ return base64UrlEncode(randomBytes(32));
101
+ }
@@ -0,0 +1,47 @@
1
+ export declare const OAUTH_DDL = "\nCREATE TABLE IF NOT EXISTS _oauth_keys (\n kid TEXT PRIMARY KEY,\n private_key TEXT NOT NULL,\n public_key TEXT NOT NULL,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS _oauth_sessions (\n did TEXT PRIMARY KEY,\n pds_endpoint TEXT NOT NULL,\n access_token TEXT NOT NULL,\n refresh_token TEXT,\n dpop_jkt TEXT NOT NULL,\n token_expires_at INTEGER,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE IF NOT EXISTS _oauth_requests (\n request_uri TEXT PRIMARY KEY,\n client_id TEXT NOT NULL,\n redirect_uri TEXT NOT NULL,\n scope TEXT,\n state TEXT,\n code_challenge TEXT NOT NULL,\n code_challenge_method TEXT NOT NULL DEFAULT 'S256',\n dpop_jkt TEXT NOT NULL,\n pds_request_uri TEXT,\n pds_auth_server TEXT,\n pds_code_verifier TEXT,\n pds_state TEXT,\n did TEXT,\n login_hint TEXT,\n expires_at INTEGER NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS _oauth_codes (\n code TEXT PRIMARY KEY,\n request_uri TEXT NOT NULL,\n created_at INTEGER NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS _oauth_refresh_tokens (\n token TEXT PRIMARY KEY,\n client_id TEXT NOT NULL,\n did TEXT NOT NULL,\n dpop_jkt TEXT NOT NULL,\n scope TEXT,\n created_at INTEGER NOT NULL,\n expires_at INTEGER,\n revoked INTEGER NOT NULL DEFAULT 0\n);\n\nCREATE TABLE IF NOT EXISTS _oauth_dpop_jtis (\n jti TEXT PRIMARY KEY,\n expires_at INTEGER NOT NULL\n);\n";
2
+ export declare function getServerKey(kid: string): Promise<{
3
+ privateKey: string;
4
+ publicKey: string;
5
+ } | null>;
6
+ export declare function storeServerKey(kid: string, privateKey: string, publicKey: string): Promise<void>;
7
+ export declare function storeOAuthRequest(requestUri: string, data: {
8
+ clientId: string;
9
+ redirectUri: string;
10
+ scope?: string;
11
+ state?: string;
12
+ codeChallenge: string;
13
+ codeChallengeMethod?: string;
14
+ dpopJkt: string;
15
+ pdsRequestUri?: string;
16
+ pdsAuthServer?: string;
17
+ pdsCodeVerifier?: string;
18
+ pdsState?: string;
19
+ did?: string;
20
+ loginHint?: string;
21
+ expiresAt: number;
22
+ }): Promise<void>;
23
+ export declare function getOAuthRequest(requestUri: string): Promise<any | null>;
24
+ export declare function deleteOAuthRequest(requestUri: string): Promise<void>;
25
+ export declare function storeAuthCode(code: string, requestUri: string): Promise<void>;
26
+ export declare function consumeAuthCode(code: string): Promise<string | null>;
27
+ export declare function storeSession(did: string, data: {
28
+ pdsEndpoint: string;
29
+ accessToken: string;
30
+ refreshToken?: string;
31
+ dpopJkt: string;
32
+ tokenExpiresAt?: number;
33
+ }): Promise<void>;
34
+ export declare function getSession(did: string): Promise<any | null>;
35
+ export declare function deleteSession(did: string): Promise<void>;
36
+ export declare function storeRefreshToken(token: string, data: {
37
+ clientId: string;
38
+ did: string;
39
+ dpopJkt: string;
40
+ scope?: string;
41
+ expiresAt?: number;
42
+ }): Promise<void>;
43
+ export declare function getRefreshToken(token: string): Promise<any | null>;
44
+ export declare function revokeRefreshToken(token: string): Promise<void>;
45
+ export declare function checkAndStoreDpopJti(jti: string, expiresAt: number): Promise<boolean>;
46
+ export declare function cleanupExpiredOAuth(): Promise<void>;
47
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/oauth/db.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,SAAS,87CA0DrB,CAAA;AAID,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAIzG;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOtG;AAID,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB,GACA,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAM7E;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAID,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOnF;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK1E;AAID,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IACJ,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,GACA,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGjE;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAID,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IACJ,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAGxE;AAED,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErE;AAID,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK3F;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CASzD"}
@@ -0,0 +1,139 @@
1
+ // packages/hatk/src/oauth/db.ts
2
+ import { querySQL, runSQL } from "../db.js";
3
+ // --- DDL ---
4
+ export const OAUTH_DDL = `
5
+ CREATE TABLE IF NOT EXISTS _oauth_keys (
6
+ kid TEXT PRIMARY KEY,
7
+ private_key TEXT NOT NULL,
8
+ public_key TEXT NOT NULL,
9
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
10
+ );
11
+
12
+ CREATE TABLE IF NOT EXISTS _oauth_sessions (
13
+ did TEXT PRIMARY KEY,
14
+ pds_endpoint TEXT NOT NULL,
15
+ access_token TEXT NOT NULL,
16
+ refresh_token TEXT,
17
+ dpop_jkt TEXT NOT NULL,
18
+ token_expires_at INTEGER,
19
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
20
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS _oauth_requests (
24
+ request_uri TEXT PRIMARY KEY,
25
+ client_id TEXT NOT NULL,
26
+ redirect_uri TEXT NOT NULL,
27
+ scope TEXT,
28
+ state TEXT,
29
+ code_challenge TEXT NOT NULL,
30
+ code_challenge_method TEXT NOT NULL DEFAULT 'S256',
31
+ dpop_jkt TEXT NOT NULL,
32
+ pds_request_uri TEXT,
33
+ pds_auth_server TEXT,
34
+ pds_code_verifier TEXT,
35
+ pds_state TEXT,
36
+ did TEXT,
37
+ login_hint TEXT,
38
+ expires_at INTEGER NOT NULL
39
+ );
40
+
41
+ CREATE TABLE IF NOT EXISTS _oauth_codes (
42
+ code TEXT PRIMARY KEY,
43
+ request_uri TEXT NOT NULL,
44
+ created_at INTEGER NOT NULL
45
+ );
46
+
47
+ CREATE TABLE IF NOT EXISTS _oauth_refresh_tokens (
48
+ token TEXT PRIMARY KEY,
49
+ client_id TEXT NOT NULL,
50
+ did TEXT NOT NULL,
51
+ dpop_jkt TEXT NOT NULL,
52
+ scope TEXT,
53
+ created_at INTEGER NOT NULL,
54
+ expires_at INTEGER,
55
+ revoked INTEGER NOT NULL DEFAULT 0
56
+ );
57
+
58
+ CREATE TABLE IF NOT EXISTS _oauth_dpop_jtis (
59
+ jti TEXT PRIMARY KEY,
60
+ expires_at INTEGER NOT NULL
61
+ );
62
+ `;
63
+ // --- Key Management ---
64
+ export async function getServerKey(kid) {
65
+ const rows = await querySQL('SELECT private_key, public_key FROM _oauth_keys WHERE kid = $1', [kid]);
66
+ if (rows.length === 0)
67
+ return null;
68
+ return { privateKey: rows[0].private_key, publicKey: rows[0].public_key };
69
+ }
70
+ export async function storeServerKey(kid, privateKey, publicKey) {
71
+ await runSQL('INSERT OR REPLACE INTO _oauth_keys (kid, private_key, public_key) VALUES ($1, $2, $3)', kid, privateKey, publicKey);
72
+ }
73
+ // --- OAuth Request Storage ---
74
+ export async function storeOAuthRequest(requestUri, data) {
75
+ await runSQL(`INSERT INTO _oauth_requests (request_uri, client_id, redirect_uri, scope, state, code_challenge, code_challenge_method, dpop_jkt, pds_request_uri, pds_auth_server, pds_code_verifier, pds_state, did, login_hint, expires_at)
76
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)`, requestUri, data.clientId, data.redirectUri, data.scope || null, data.state || null, data.codeChallenge, data.codeChallengeMethod || 'S256', data.dpopJkt, data.pdsRequestUri || null, data.pdsAuthServer || null, data.pdsCodeVerifier || null, data.pdsState || null, data.did || null, data.loginHint || null, data.expiresAt);
77
+ }
78
+ export async function getOAuthRequest(requestUri) {
79
+ const rows = await querySQL('SELECT * FROM _oauth_requests WHERE request_uri = $1 AND expires_at > $2', [
80
+ requestUri,
81
+ Math.floor(Date.now() / 1000),
82
+ ]);
83
+ return rows.length > 0 ? rows[0] : null;
84
+ }
85
+ export async function deleteOAuthRequest(requestUri) {
86
+ await runSQL('DELETE FROM _oauth_requests WHERE request_uri = $1', requestUri);
87
+ }
88
+ // --- Authorization Codes ---
89
+ export async function storeAuthCode(code, requestUri) {
90
+ await runSQL('INSERT INTO _oauth_codes (code, request_uri, created_at) VALUES ($1, $2, $3)', code, requestUri, Math.floor(Date.now() / 1000));
91
+ }
92
+ export async function consumeAuthCode(code) {
93
+ const rows = await querySQL('SELECT request_uri FROM _oauth_codes WHERE code = $1', [code]);
94
+ if (rows.length === 0)
95
+ return null;
96
+ await runSQL('DELETE FROM _oauth_codes WHERE code = $1', code);
97
+ return rows[0].request_uri;
98
+ }
99
+ // --- Sessions ---
100
+ export async function storeSession(did, data) {
101
+ await runSQL(`INSERT OR REPLACE INTO _oauth_sessions (did, pds_endpoint, access_token, refresh_token, dpop_jkt, token_expires_at, updated_at)
102
+ VALUES ($1,$2,$3,$4,$5,$6,CURRENT_TIMESTAMP)`, did, data.pdsEndpoint, data.accessToken, data.refreshToken || null, data.dpopJkt, data.tokenExpiresAt || null);
103
+ }
104
+ export async function getSession(did) {
105
+ const rows = await querySQL('SELECT * FROM _oauth_sessions WHERE did = $1', [did]);
106
+ return rows.length > 0 ? rows[0] : null;
107
+ }
108
+ export async function deleteSession(did) {
109
+ await runSQL('DELETE FROM _oauth_sessions WHERE did = $1', did);
110
+ }
111
+ // --- Refresh Tokens ---
112
+ export async function storeRefreshToken(token, data) {
113
+ const now = Math.floor(Date.now() / 1000);
114
+ const expiresAt = data.expiresAt ?? now + 14 * 86400; // 14 days default
115
+ await runSQL(`INSERT INTO _oauth_refresh_tokens (token, client_id, did, dpop_jkt, scope, created_at, expires_at)
116
+ VALUES ($1,$2,$3,$4,$5,$6,$7)`, token, data.clientId, data.did, data.dpopJkt, data.scope || null, now, expiresAt);
117
+ }
118
+ export async function getRefreshToken(token) {
119
+ const rows = await querySQL('SELECT * FROM _oauth_refresh_tokens WHERE token = $1', [token]);
120
+ return rows.length > 0 ? rows[0] : null;
121
+ }
122
+ export async function revokeRefreshToken(token) {
123
+ await runSQL('UPDATE _oauth_refresh_tokens SET revoked = 1 WHERE token = $1', token);
124
+ }
125
+ // --- DPoP JTI Replay Protection ---
126
+ export async function checkAndStoreDpopJti(jti, expiresAt) {
127
+ const rows = await querySQL('SELECT 1 FROM _oauth_dpop_jtis WHERE jti = $1', [jti]);
128
+ if (rows.length > 0)
129
+ return false;
130
+ await runSQL('INSERT INTO _oauth_dpop_jtis (jti, expires_at) VALUES ($1, $2)', jti, expiresAt);
131
+ return true;
132
+ }
133
+ export async function cleanupExpiredOAuth() {
134
+ const now = Math.floor(Date.now() / 1000);
135
+ await runSQL('DELETE FROM _oauth_dpop_jtis WHERE expires_at < $1', now);
136
+ await runSQL('DELETE FROM _oauth_requests WHERE expires_at < $1', now);
137
+ await runSQL('DELETE FROM _oauth_codes WHERE created_at < $1', now - 600);
138
+ await runSQL('DELETE FROM _oauth_refresh_tokens WHERE revoked = 1 OR (expires_at IS NOT NULL AND expires_at < $1)', now);
139
+ }
@@ -0,0 +1,22 @@
1
+ export interface AuthServerMetadata {
2
+ issuer: string;
3
+ authorization_endpoint: string;
4
+ token_endpoint: string;
5
+ pushed_authorization_request_endpoint?: string;
6
+ jwks_uri: string;
7
+ dpop_signing_alg_values_supported?: string[];
8
+ [key: string]: unknown;
9
+ }
10
+ export declare function resolveDid(did: string, plcUrl: string): Promise<any>;
11
+ export declare function getPdsEndpoint(didDoc: any): string | null;
12
+ export declare function fetchProtectedResourceMetadata(pdsEndpoint: string): Promise<{
13
+ authorization_servers: string[];
14
+ }>;
15
+ export declare function fetchAuthServerMetadata(authServerEndpoint: string): Promise<AuthServerMetadata>;
16
+ export declare function discoverAuthServer(did: string, plcUrl: string): Promise<{
17
+ pdsEndpoint: string;
18
+ authServerEndpoint: string;
19
+ authServerMetadata: AuthServerMetadata;
20
+ }>;
21
+ export declare function resolveHandle(handle: string, relayUrl?: string): Promise<string>;
22
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/oauth/discovery.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,sBAAsB,EAAE,MAAM,CAAA;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,qCAAqC,CAAC,EAAE,MAAM,CAAA;IAC9C,QAAQ,EAAE,MAAM,CAAA;IAChB,iCAAiC,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAU1E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAGzD;AAED,wBAAsB,8BAA8B,CAClD,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,qBAAqB,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAI9C;AAED,wBAAsB,uBAAuB,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAIrG;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IACT,WAAW,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,kBAAkB,CAAA;CACvC,CAAC,CAWD;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMtF"}
@@ -0,0 +1,50 @@
1
+ // packages/hatk/src/oauth/discovery.ts
2
+ export async function resolveDid(did, plcUrl) {
3
+ if (did.startsWith('did:web:')) {
4
+ const domain = did.slice('did:web:'.length);
5
+ const res = await fetch(`https://${domain}/.well-known/did.json`);
6
+ if (!res.ok)
7
+ throw new Error(`did:web resolution failed: ${res.status}`);
8
+ return res.json();
9
+ }
10
+ const res = await fetch(`${plcUrl}/${did}`);
11
+ if (!res.ok)
12
+ throw new Error(`PLC resolution failed: ${res.status}`);
13
+ return res.json();
14
+ }
15
+ export function getPdsEndpoint(didDoc) {
16
+ const service = didDoc.service?.find((s) => s.id === '#atproto_pds' || s.type === 'AtprotoPersonalDataServer');
17
+ return service?.serviceEndpoint || null;
18
+ }
19
+ export async function fetchProtectedResourceMetadata(pdsEndpoint) {
20
+ const res = await fetch(`${pdsEndpoint}/.well-known/oauth-protected-resource`);
21
+ if (!res.ok)
22
+ throw new Error(`Protected resource metadata failed: ${res.status}`);
23
+ return res.json();
24
+ }
25
+ export async function fetchAuthServerMetadata(authServerEndpoint) {
26
+ const res = await fetch(`${authServerEndpoint}/.well-known/oauth-authorization-server`);
27
+ if (!res.ok)
28
+ throw new Error(`Auth server metadata failed: ${res.status}`);
29
+ return res.json();
30
+ }
31
+ export async function discoverAuthServer(did, plcUrl) {
32
+ const didDoc = await resolveDid(did, plcUrl);
33
+ const pdsEndpoint = getPdsEndpoint(didDoc);
34
+ if (!pdsEndpoint)
35
+ throw new Error(`No PDS endpoint in DID document for ${did}`);
36
+ const protectedResource = await fetchProtectedResourceMetadata(pdsEndpoint);
37
+ const authServerEndpoint = protectedResource.authorization_servers[0];
38
+ if (!authServerEndpoint)
39
+ throw new Error(`No auth server for PDS ${pdsEndpoint}`);
40
+ const authServerMetadata = await fetchAuthServerMetadata(authServerEndpoint);
41
+ return { pdsEndpoint, authServerEndpoint, authServerMetadata };
42
+ }
43
+ export async function resolveHandle(handle, relayUrl) {
44
+ const baseUrl = relayUrl?.includes('localhost:2583') ? 'http://localhost:2583' : 'https://bsky.social';
45
+ const res = await fetch(`${baseUrl}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`);
46
+ if (!res.ok)
47
+ throw new Error(`resolveHandle failed: ${res.status}`);
48
+ const data = await res.json();
49
+ return data.did;
50
+ }
@@ -0,0 +1,11 @@
1
+ export interface DpopResult {
2
+ jkt: string;
3
+ jti: string;
4
+ iat: number;
5
+ jwk: JsonWebKey;
6
+ }
7
+ /** Validate a DPoP proof JWT from a client request. */
8
+ export declare function parseDpopProof(proof: string, method: string, url: string, expectedJkt?: string, accessToken?: string): Promise<DpopResult>;
9
+ /** Create a DPoP proof JWT for making requests to a PDS (server-as-client). */
10
+ export declare function createDpopProof(privateJwk: JsonWebKey, publicJwk: JsonWebKey, method: string, url: string, accessToken?: string, nonce?: string): Promise<string>;
11
+ //# sourceMappingURL=dpop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dpop.d.ts","sourceRoot":"","sources":["../../src/oauth/dpop.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,UAAU,CAAA;CAChB;AAED,uDAAuD;AACvD,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,CAAC,CAgCrB;AAED,+EAA+E;AAC/E,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAkBjB"}
@@ -0,0 +1,56 @@
1
+ // packages/hatk/src/oauth/dpop.ts
2
+ import { parseJwt, importPublicKey, verifyEs256, computeJwkThumbprint, sha256, base64UrlEncode, signJwt, importPrivateKey, } from "./crypto.js";
3
+ /** Validate a DPoP proof JWT from a client request. */
4
+ export async function parseDpopProof(proof, method, url, expectedJkt, accessToken) {
5
+ const { header, payload, signatureInput, signature } = parseJwt(proof);
6
+ if (header.typ !== 'dpop+jwt')
7
+ throw new Error('DPoP proof must have typ dpop+jwt');
8
+ if (header.alg !== 'ES256')
9
+ throw new Error('DPoP proof must use ES256');
10
+ if (!header.jwk || header.jwk.kty !== 'EC')
11
+ throw new Error('DPoP proof must contain EC key');
12
+ const publicKey = await importPublicKey(header.jwk);
13
+ const valid = await verifyEs256(publicKey, signature, signatureInput);
14
+ if (!valid)
15
+ throw new Error('DPoP proof signature invalid');
16
+ if (payload.htm !== method)
17
+ throw new Error('DPoP htm mismatch');
18
+ const normalizeUrl = (u) => u.replace(/\/$/, '').split('?')[0].toLowerCase();
19
+ if (normalizeUrl(payload.htu) !== normalizeUrl(url))
20
+ throw new Error('DPoP htu mismatch');
21
+ const now = Math.floor(Date.now() / 1000);
22
+ if (!payload.iat || payload.iat > now + 60 || payload.iat < now - 300) {
23
+ throw new Error('DPoP proof expired or invalid iat');
24
+ }
25
+ if (!payload.jti)
26
+ throw new Error('DPoP proof missing jti');
27
+ const jkt = await computeJwkThumbprint(header.jwk);
28
+ if (expectedJkt && jkt !== expectedJkt)
29
+ throw new Error('DPoP key mismatch');
30
+ if (accessToken) {
31
+ const tokenHash = await sha256(accessToken);
32
+ const expectedAth = base64UrlEncode(tokenHash);
33
+ if (payload.ath !== expectedAth)
34
+ throw new Error('DPoP ath mismatch');
35
+ }
36
+ return { jkt, jti: payload.jti, iat: payload.iat, jwk: header.jwk };
37
+ }
38
+ /** Create a DPoP proof JWT for making requests to a PDS (server-as-client). */
39
+ export async function createDpopProof(privateJwk, publicJwk, method, url, accessToken, nonce) {
40
+ const privateKey = await importPrivateKey(privateJwk);
41
+ const minimalPublicJwk = { kty: publicJwk.kty, crv: publicJwk.crv, x: publicJwk.x, y: publicJwk.y };
42
+ const header = { typ: 'dpop+jwt', alg: 'ES256', jwk: minimalPublicJwk };
43
+ const payload = {
44
+ jti: crypto.randomUUID(),
45
+ htm: method,
46
+ htu: url.split('?')[0],
47
+ iat: Math.floor(Date.now() / 1000),
48
+ };
49
+ if (accessToken) {
50
+ const hash = await sha256(accessToken);
51
+ payload.ath = base64UrlEncode(hash);
52
+ }
53
+ if (nonce)
54
+ payload.nonce = nonce;
55
+ return signJwt(header, payload, privateKey);
56
+ }
@@ -0,0 +1,10 @@
1
+ /** onLogin hook: called after each successful OAuth login. */
2
+ export type OnLoginCtx = {
3
+ did: string;
4
+ ensureRepo: (did: string) => Promise<void>;
5
+ };
6
+ /** Load on-login hook from the exercise's hooks/ directory. */
7
+ export declare function loadOnLoginHook(hooksDir: string): Promise<void>;
8
+ /** Fire the onLogin hook if loaded. Errors are logged but don't block login. */
9
+ export declare function fireOnLoginHook(did: string): Promise<void>;
10
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/oauth/hooks.ts"],"names":[],"mappings":"AAMA,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3C,CAAA;AAMD,+DAA+D;AAC/D,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrE;AAOD,gFAAgF;AAChF,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhE"}
@@ -0,0 +1,40 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync } from 'node:fs';
10
+ import { resolve } from 'node:path';
11
+ import { log } from "../logger.js";
12
+ import { setRepoStatus } from "../db.js";
13
+ import { triggerAutoBackfill } from "../indexer.js";
14
+ let onLoginHook = null;
15
+ /** Load on-login hook from the exercise's hooks/ directory. */
16
+ export async function loadOnLoginHook(hooksDir) {
17
+ const tsPath = resolve(hooksDir, 'on-login.ts');
18
+ const jsPath = resolve(hooksDir, 'on-login.js');
19
+ const path = existsSync(tsPath) ? tsPath : existsSync(jsPath) ? jsPath : null;
20
+ if (!path)
21
+ return;
22
+ const mod = await import(__rewriteRelativeImportExtension(path));
23
+ onLoginHook = mod.default;
24
+ log('[oauth] on-login hook loaded');
25
+ }
26
+ async function ensureRepo(did) {
27
+ await setRepoStatus(did, 'pending');
28
+ triggerAutoBackfill(did);
29
+ }
30
+ /** Fire the onLogin hook if loaded. Errors are logged but don't block login. */
31
+ export async function fireOnLoginHook(did) {
32
+ if (!onLoginHook)
33
+ return;
34
+ try {
35
+ await onLoginHook({ did, ensureRepo });
36
+ }
37
+ catch (err) {
38
+ console.error('[oauth] onLogin hook error:', err.message);
39
+ }
40
+ }
@@ -0,0 +1,86 @@
1
+ import type { OAuthConfig } from '../config.ts';
2
+ export declare function initOAuth(_config: OAuthConfig, plcUrl: string, relayUrl: string): Promise<void>;
3
+ export declare function getAuthServerMetadata(issuer: string, config: OAuthConfig): {
4
+ issuer: string;
5
+ authorization_endpoint: string;
6
+ token_endpoint: string;
7
+ revocation_endpoint: string;
8
+ pushed_authorization_request_endpoint: string;
9
+ jwks_uri: string;
10
+ scopes_supported: string[];
11
+ subject_types_supported: string[];
12
+ response_types_supported: string[];
13
+ response_modes_supported: string[];
14
+ grant_types_supported: string[];
15
+ code_challenge_methods_supported: string[];
16
+ token_endpoint_auth_methods_supported: string[];
17
+ dpop_signing_alg_values_supported: string[];
18
+ require_pushed_authorization_requests: boolean;
19
+ authorization_response_iss_parameter_supported: boolean;
20
+ client_id_metadata_document_supported: boolean;
21
+ protected_resources: string[];
22
+ };
23
+ export declare function getProtectedResourceMetadata(issuer: string, config: OAuthConfig): {
24
+ resource: string;
25
+ authorization_servers: string[];
26
+ bearer_methods_supported: string[];
27
+ scopes_supported: string[];
28
+ };
29
+ export declare function getJwks(): {
30
+ keys: {
31
+ kid: string;
32
+ use: string;
33
+ alg: string;
34
+ crv?: string;
35
+ d?: string;
36
+ dp?: string;
37
+ dq?: string;
38
+ e?: string;
39
+ ext?: boolean;
40
+ k?: string;
41
+ key_ops?: string[];
42
+ kty?: string;
43
+ n?: string;
44
+ oth?: RsaOtherPrimesInfo[];
45
+ p?: string;
46
+ q?: string;
47
+ qi?: string;
48
+ x?: string;
49
+ y?: string;
50
+ }[];
51
+ };
52
+ export declare function getClientMetadata(issuer: string, config: OAuthConfig): {
53
+ client_id: string;
54
+ client_name: string;
55
+ redirect_uris: string[];
56
+ grant_types: string[];
57
+ response_types: string[];
58
+ token_endpoint_auth_method: string;
59
+ dpop_bound_access_tokens: boolean;
60
+ scope: string;
61
+ };
62
+ export declare function handlePar(config: OAuthConfig, body: Record<string, string>, dpopHeader: string, requestUrl: string): Promise<{
63
+ request_uri: string;
64
+ expires_in: number;
65
+ }>;
66
+ export declare function buildAuthorizeRedirect(config: OAuthConfig, request: any): string;
67
+ export declare function handleCallback(config: OAuthConfig, code: string, state: string | null, iss: string | null): Promise<{
68
+ requestUri: string;
69
+ clientRedirectUri: string;
70
+ clientState: string | null;
71
+ }>;
72
+ export declare function handleToken(config: OAuthConfig, body: Record<string, string>, dpopHeader: string, requestUrl: string): Promise<any>;
73
+ export declare function refreshPdsSession(config: OAuthConfig, session: {
74
+ did: string;
75
+ pds_endpoint: string;
76
+ refresh_token: string;
77
+ dpop_jkt: string;
78
+ }): Promise<{
79
+ accessToken: string;
80
+ refreshToken?: string;
81
+ expiresAt?: number;
82
+ } | null>;
83
+ export declare function authenticate(authHeader: string | null, dpopHeader: string | null, method: string, url: string): Promise<{
84
+ did: string;
85
+ } | null>;
86
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/oauth/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AA0E/C,wBAAsB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBrG;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;;;;;;;;;;;;;;;;;;;EAqBxE;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;;;;;EAO/E;AAED,wBAAgB,OAAO;;;;;;;;;;;;;;;;;;;;;;EAWtB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;;;;;;;;;EAcpE;AAID,wBAAsB,SAAS,CAC7B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAuItD;AAID,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,GAAG,MAAM,CAShF;AAID,wBAAsB,cAAc,CAClC,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GACjB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAyHxF;AAID,wBAAsB,WAAW,CAC/B,MAAM,EAAE,WAAW,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,GAAG,CAAC,CAUd;AA0JD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtF,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAmEpF;AAID,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA0BjC"}