@aztec/wallet-sdk 0.0.1-commit.7d4e6cd → 0.0.1-commit.8afd444
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 +218 -355
- package/dest/base-wallet/base_wallet.d.ts +33 -10
- package/dest/base-wallet/base_wallet.d.ts.map +1 -1
- package/dest/base-wallet/base_wallet.js +53 -16
- package/dest/crypto.d.ts +73 -27
- package/dest/crypto.d.ts.map +1 -1
- package/dest/crypto.js +219 -41
- package/dest/emoji_alphabet.d.ts +35 -0
- package/dest/emoji_alphabet.d.ts.map +1 -0
- package/dest/emoji_alphabet.js +299 -0
- package/dest/extension/handlers/background_connection_handler.d.ts +158 -0
- package/dest/extension/handlers/background_connection_handler.d.ts.map +1 -0
- package/dest/extension/handlers/background_connection_handler.js +258 -0
- package/dest/extension/handlers/content_script_connection_handler.d.ts +56 -0
- package/dest/extension/handlers/content_script_connection_handler.d.ts.map +1 -0
- package/dest/extension/handlers/content_script_connection_handler.js +174 -0
- package/dest/extension/handlers/index.d.ts +12 -0
- package/dest/extension/handlers/index.d.ts.map +1 -0
- package/dest/extension/handlers/index.js +10 -0
- package/dest/extension/handlers/internal_message_types.d.ts +63 -0
- package/dest/extension/handlers/internal_message_types.d.ts.map +1 -0
- package/dest/extension/handlers/internal_message_types.js +22 -0
- package/dest/extension/provider/extension_provider.d.ts +107 -0
- package/dest/extension/provider/extension_provider.d.ts.map +1 -0
- package/dest/extension/provider/extension_provider.js +160 -0
- package/dest/extension/provider/extension_wallet.d.ts +131 -0
- package/dest/extension/provider/extension_wallet.d.ts.map +1 -0
- package/dest/extension/provider/extension_wallet.js +271 -0
- package/dest/extension/provider/index.d.ts +3 -0
- package/dest/extension/provider/index.d.ts.map +1 -0
- package/dest/{providers/extension → extension/provider}/index.js +0 -1
- package/dest/manager/index.d.ts +2 -7
- package/dest/manager/index.d.ts.map +1 -1
- package/dest/manager/index.js +0 -4
- package/dest/manager/types.d.ts +108 -5
- package/dest/manager/types.d.ts.map +1 -1
- package/dest/manager/types.js +17 -1
- package/dest/manager/wallet_manager.d.ts +50 -7
- package/dest/manager/wallet_manager.d.ts.map +1 -1
- package/dest/manager/wallet_manager.js +178 -29
- package/dest/types.d.ts +55 -15
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +10 -2
- package/package.json +11 -10
- package/src/base-wallet/base_wallet.ts +74 -28
- package/src/crypto.ts +263 -47
- package/src/emoji_alphabet.ts +317 -0
- package/src/extension/handlers/background_connection_handler.ts +423 -0
- package/src/extension/handlers/content_script_connection_handler.ts +246 -0
- package/src/extension/handlers/index.ts +25 -0
- package/src/extension/handlers/internal_message_types.ts +69 -0
- package/src/extension/provider/extension_provider.ts +233 -0
- package/src/extension/provider/extension_wallet.ts +321 -0
- package/src/extension/provider/index.ts +7 -0
- package/src/manager/index.ts +3 -9
- package/src/manager/types.ts +112 -4
- package/src/manager/wallet_manager.ts +204 -31
- package/src/types.ts +57 -14
- package/dest/providers/extension/extension_provider.d.ts +0 -17
- package/dest/providers/extension/extension_provider.d.ts.map +0 -1
- package/dest/providers/extension/extension_provider.js +0 -56
- package/dest/providers/extension/extension_wallet.d.ts +0 -95
- package/dest/providers/extension/extension_wallet.d.ts.map +0 -1
- package/dest/providers/extension/extension_wallet.js +0 -225
- package/dest/providers/extension/index.d.ts +0 -5
- package/dest/providers/extension/index.d.ts.map +0 -1
- package/src/providers/extension/extension_provider.ts +0 -72
- package/src/providers/extension/extension_wallet.ts +0 -275
- package/src/providers/extension/index.ts +0 -11
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { ChainInfo } from '@aztec/aztec.js/account';
|
|
2
|
+
import { type EncryptedPayload } from '../../crypto.js';
|
|
3
|
+
import { type DiscoveryRequest, type KeyExchangeRequest, type WalletInfo, type WalletMessage, type WalletResponse } from '../../types.js';
|
|
4
|
+
import { type BackgroundMessage, type MessageSender } from './internal_message_types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Status of a pending discovery request.
|
|
7
|
+
*/
|
|
8
|
+
export type DiscoveryStatus = 'pending' | 'approved' | 'rejected';
|
|
9
|
+
/**
|
|
10
|
+
* A pending discovery request from a dApp.
|
|
11
|
+
*/
|
|
12
|
+
export interface PendingDiscovery {
|
|
13
|
+
/** Unique request identifier. */
|
|
14
|
+
requestId: string;
|
|
15
|
+
/** Application identifier. */
|
|
16
|
+
appId: string;
|
|
17
|
+
/** Optional application name. */
|
|
18
|
+
appName?: string;
|
|
19
|
+
/** Origin URL of the requesting page. */
|
|
20
|
+
origin: string;
|
|
21
|
+
/** Network information. */
|
|
22
|
+
chainInfo: ChainInfo;
|
|
23
|
+
/** Browser tab ID. */
|
|
24
|
+
tabId: number;
|
|
25
|
+
/** Request timestamp. */
|
|
26
|
+
timestamp: number;
|
|
27
|
+
/** Current status. */
|
|
28
|
+
status: DiscoveryStatus;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* An active session with a connected dApp.
|
|
32
|
+
* Created after key exchange completes.
|
|
33
|
+
*/
|
|
34
|
+
export interface ActiveSession {
|
|
35
|
+
/** Unique session identifier. */
|
|
36
|
+
sessionId: string;
|
|
37
|
+
/** Derived AES-GCM encryption key. */
|
|
38
|
+
sharedKey: CryptoKey;
|
|
39
|
+
/** Hex-encoded verification hash for visual comparison. */
|
|
40
|
+
verificationHash: string;
|
|
41
|
+
/** Browser tab ID. */
|
|
42
|
+
tabId: number;
|
|
43
|
+
/** Origin URL of the connected app. */
|
|
44
|
+
origin: string;
|
|
45
|
+
/** Application identifier. */
|
|
46
|
+
appId: string;
|
|
47
|
+
/** Connection timestamp. */
|
|
48
|
+
connectedAt: number;
|
|
49
|
+
/** Network information. */
|
|
50
|
+
chainInfo: ChainInfo;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Transport interface for background script communication.
|
|
54
|
+
*/
|
|
55
|
+
export interface BackgroundTransport {
|
|
56
|
+
/**
|
|
57
|
+
* Send a message to a specific tab.
|
|
58
|
+
* Typically `(tabId, message) => browser.tabs.sendMessage(tabId, message)`.
|
|
59
|
+
*/
|
|
60
|
+
sendToTab: (tabId: number, message: BackgroundMessage) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Register a listener for messages from content scripts.
|
|
63
|
+
* Typically `browser.runtime.onMessage.addListener`.
|
|
64
|
+
*/
|
|
65
|
+
addContentListener: (handler: (message: unknown, sender: MessageSender) => void) => void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Event callbacks for the background connection handler.
|
|
69
|
+
* All callbacks are optional.
|
|
70
|
+
*/
|
|
71
|
+
export interface BackgroundConnectionCallbacks {
|
|
72
|
+
/**
|
|
73
|
+
* Called when a new discovery request is received and stored as pending.
|
|
74
|
+
*/
|
|
75
|
+
onPendingDiscovery?: (discovery: PendingDiscovery) => void;
|
|
76
|
+
/**
|
|
77
|
+
* Called when a session is established (key exchange complete).
|
|
78
|
+
*/
|
|
79
|
+
onSessionEstablished?: (session: ActiveSession) => void;
|
|
80
|
+
/**
|
|
81
|
+
* Called when a session is terminated.
|
|
82
|
+
*/
|
|
83
|
+
onSessionTerminated?: (sessionId: string) => void;
|
|
84
|
+
/**
|
|
85
|
+
* Called when a decrypted wallet message is received.
|
|
86
|
+
*/
|
|
87
|
+
onWalletMessage?: (session: ActiveSession, message: WalletMessage) => void;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Configuration for the background connection handler.
|
|
91
|
+
*/
|
|
92
|
+
export interface BackgroundConnectionConfig {
|
|
93
|
+
/** Unique wallet identifier. */
|
|
94
|
+
walletId: string;
|
|
95
|
+
/** Display name for the wallet. */
|
|
96
|
+
walletName: string;
|
|
97
|
+
/** Wallet version string. */
|
|
98
|
+
walletVersion: string;
|
|
99
|
+
/** Optional wallet icon URL. */
|
|
100
|
+
walletIcon?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Handles wallet session flow in the extension background script.
|
|
104
|
+
*
|
|
105
|
+
* This class manages:
|
|
106
|
+
* - Pending discovery requests (before user approval)
|
|
107
|
+
* - Active sessions (after key exchange)
|
|
108
|
+
* - Per-session ECDH key exchange
|
|
109
|
+
* - Message encryption/decryption
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const handler = new BackgroundConnectionHandler(
|
|
114
|
+
* {
|
|
115
|
+
* walletId: 'my-wallet',
|
|
116
|
+
* walletName: 'My Wallet',
|
|
117
|
+
* walletVersion: '1.0.0',
|
|
118
|
+
* },
|
|
119
|
+
* {
|
|
120
|
+
* sendToTab: (tabId, message) => browser.tabs.sendMessage(tabId, message),
|
|
121
|
+
* addContentListener: (handler) => browser.runtime.onMessage.addListener(handler),
|
|
122
|
+
* },
|
|
123
|
+
* {
|
|
124
|
+
* onPendingDiscovery: (discovery) => updateBadge(),
|
|
125
|
+
* onSessionEstablished: (session) => console.log('Connected:', session.sessionId),
|
|
126
|
+
* onWalletMessage: (session, message) => nativePort.postMessage(message),
|
|
127
|
+
* }
|
|
128
|
+
* );
|
|
129
|
+
*
|
|
130
|
+
* handler.initialize();
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export declare class BackgroundConnectionHandler {
|
|
134
|
+
private config;
|
|
135
|
+
private transport;
|
|
136
|
+
private callbacks;
|
|
137
|
+
private pendingDiscoveries;
|
|
138
|
+
private activeSessions;
|
|
139
|
+
constructor(config: BackgroundConnectionConfig, transport: BackgroundTransport, callbacks?: BackgroundConnectionCallbacks);
|
|
140
|
+
initialize(): void;
|
|
141
|
+
private handleMessage;
|
|
142
|
+
getWalletInfo(): WalletInfo;
|
|
143
|
+
handleDiscoveryRequest(request: DiscoveryRequest, tabId: number, origin: string): void;
|
|
144
|
+
approveDiscovery(requestId: string): boolean;
|
|
145
|
+
rejectDiscovery(requestId: string): boolean;
|
|
146
|
+
handleKeyExchangeRequest(sessionId: string, request: KeyExchangeRequest): Promise<void>;
|
|
147
|
+
handleEncryptedMessage(sessionId: string, encrypted: EncryptedPayload): Promise<void>;
|
|
148
|
+
sendResponse(sessionId: string, response: WalletResponse): Promise<void>;
|
|
149
|
+
terminateSession(sessionId: string): void;
|
|
150
|
+
terminateForTab(tabId: number): void;
|
|
151
|
+
clearAll(): void;
|
|
152
|
+
getPendingDiscoveries(): PendingDiscovery[];
|
|
153
|
+
getPendingDiscoveryCount(): number;
|
|
154
|
+
getActiveSessions(): ActiveSession[];
|
|
155
|
+
getSession(sessionId: string): ActiveSession | undefined;
|
|
156
|
+
getPendingDiscovery(requestId: string): PendingDiscovery | undefined;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZF9jb25uZWN0aW9uX2hhbmRsZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHRlbnNpb24vaGFuZGxlcnMvYmFja2dyb3VuZF9jb25uZWN0aW9uX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFekQsT0FBTyxFQUNMLEtBQUssZ0JBQWdCLEVBT3RCLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUNMLEtBQUssZ0JBQWdCLEVBQ3JCLEtBQUssa0JBQWtCLEVBRXZCLEtBQUssVUFBVSxFQUNmLEtBQUssYUFBYSxFQUVsQixLQUFLLGNBQWMsRUFDcEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQ0wsS0FBSyxpQkFBaUIsRUFJdEIsS0FBSyxhQUFhLEVBQ25CLE1BQU0sNkJBQTZCLENBQUM7QUFFckM7O0dBRUc7QUFDSCxNQUFNLE1BQU0sZUFBZSxHQUFHLFNBQVMsR0FBRyxVQUFVLEdBQUcsVUFBVSxDQUFDO0FBRWxFOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGdCQUFnQjtJQUMvQixpQ0FBaUM7SUFDakMsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQiw4QkFBOEI7SUFDOUIsS0FBSyxFQUFFLE1BQU0sQ0FBQztJQUNkLGlDQUFpQztJQUNqQyxPQUFPLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDakIseUNBQXlDO0lBQ3pDLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFDZiwyQkFBMkI7SUFDM0IsU0FBUyxFQUFFLFNBQVMsQ0FBQztJQUNyQixzQkFBc0I7SUFDdEIsS0FBSyxFQUFFLE1BQU0sQ0FBQztJQUNkLHlCQUF5QjtJQUN6QixTQUFTLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLHNCQUFzQjtJQUN0QixNQUFNLEVBQUUsZUFBZSxDQUFDO0NBQ3pCO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxX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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"background_connection_handler.d.ts","sourceRoot":"","sources":["../../../src/extension/handlers/background_connection_handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { decrypt, deriveSessionKeys, encrypt, exportPublicKey, generateKeyPair, importPublicKey } from '../../crypto.js';
|
|
2
|
+
import { WalletMessageType } from '../../types.js';
|
|
3
|
+
import { InternalMessageType, MessageOrigin } from './internal_message_types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handles wallet session flow in the extension background script.
|
|
6
|
+
*
|
|
7
|
+
* This class manages:
|
|
8
|
+
* - Pending discovery requests (before user approval)
|
|
9
|
+
* - Active sessions (after key exchange)
|
|
10
|
+
* - Per-session ECDH key exchange
|
|
11
|
+
* - Message encryption/decryption
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const handler = new BackgroundConnectionHandler(
|
|
16
|
+
* {
|
|
17
|
+
* walletId: 'my-wallet',
|
|
18
|
+
* walletName: 'My Wallet',
|
|
19
|
+
* walletVersion: '1.0.0',
|
|
20
|
+
* },
|
|
21
|
+
* {
|
|
22
|
+
* sendToTab: (tabId, message) => browser.tabs.sendMessage(tabId, message),
|
|
23
|
+
* addContentListener: (handler) => browser.runtime.onMessage.addListener(handler),
|
|
24
|
+
* },
|
|
25
|
+
* {
|
|
26
|
+
* onPendingDiscovery: (discovery) => updateBadge(),
|
|
27
|
+
* onSessionEstablished: (session) => console.log('Connected:', session.sessionId),
|
|
28
|
+
* onWalletMessage: (session, message) => nativePort.postMessage(message),
|
|
29
|
+
* }
|
|
30
|
+
* );
|
|
31
|
+
*
|
|
32
|
+
* handler.initialize();
|
|
33
|
+
* ```
|
|
34
|
+
*/ export class BackgroundConnectionHandler {
|
|
35
|
+
config;
|
|
36
|
+
transport;
|
|
37
|
+
callbacks;
|
|
38
|
+
pendingDiscoveries;
|
|
39
|
+
activeSessions;
|
|
40
|
+
constructor(config, transport, callbacks = {}){
|
|
41
|
+
this.config = config;
|
|
42
|
+
this.transport = transport;
|
|
43
|
+
this.callbacks = callbacks;
|
|
44
|
+
this.pendingDiscoveries = new Map();
|
|
45
|
+
this.activeSessions = new Map();
|
|
46
|
+
this.handleMessage = (message, sender)=>{
|
|
47
|
+
const msg = message;
|
|
48
|
+
if (msg.origin !== MessageOrigin.CONTENT_SCRIPT) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const tabId = sender.tab?.id;
|
|
52
|
+
const tabOrigin = sender.tab?.url ? new URL(sender.tab.url).origin : 'unknown';
|
|
53
|
+
if (!tabId) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const { type, sessionId, content } = msg;
|
|
57
|
+
switch(type){
|
|
58
|
+
case InternalMessageType.DISCOVERY_REQUEST:
|
|
59
|
+
this.handleDiscoveryRequest(content, tabId, tabOrigin);
|
|
60
|
+
break;
|
|
61
|
+
case InternalMessageType.KEY_EXCHANGE_REQUEST:
|
|
62
|
+
if (sessionId) {
|
|
63
|
+
this.handleKeyExchangeRequest(sessionId, content).catch(()=>{
|
|
64
|
+
// Key exchange failed - session won't be established
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
case InternalMessageType.DISCONNECT_REQUEST:
|
|
69
|
+
if (sessionId) {
|
|
70
|
+
this.terminateSession(sessionId);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
case InternalMessageType.SECURE_MESSAGE:
|
|
74
|
+
if (sessionId) {
|
|
75
|
+
void this.handleEncryptedMessage(sessionId, content);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
initialize() {
|
|
82
|
+
this.transport.addContentListener(this.handleMessage);
|
|
83
|
+
}
|
|
84
|
+
handleMessage;
|
|
85
|
+
getWalletInfo() {
|
|
86
|
+
return {
|
|
87
|
+
id: this.config.walletId,
|
|
88
|
+
name: this.config.walletName,
|
|
89
|
+
version: this.config.walletVersion,
|
|
90
|
+
icon: this.config.walletIcon
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
handleDiscoveryRequest(request, tabId, origin) {
|
|
94
|
+
const discovery = {
|
|
95
|
+
requestId: request.requestId,
|
|
96
|
+
appId: request.appId,
|
|
97
|
+
origin,
|
|
98
|
+
chainInfo: request.chainInfo,
|
|
99
|
+
tabId,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
status: 'pending'
|
|
102
|
+
};
|
|
103
|
+
this.pendingDiscoveries.set(request.requestId, discovery);
|
|
104
|
+
this.callbacks.onPendingDiscovery?.(discovery);
|
|
105
|
+
}
|
|
106
|
+
approveDiscovery(requestId) {
|
|
107
|
+
const discovery = this.pendingDiscoveries.get(requestId);
|
|
108
|
+
if (!discovery || discovery.status !== 'pending') {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
// The discovery requestId becomes our sessionId
|
|
112
|
+
// This is what will be used internally to correlate
|
|
113
|
+
// content<->background messages
|
|
114
|
+
const sessionId = requestId;
|
|
115
|
+
discovery.status = 'approved';
|
|
116
|
+
this.transport.sendToTab(discovery.tabId, {
|
|
117
|
+
origin: MessageOrigin.BACKGROUND,
|
|
118
|
+
type: InternalMessageType.DISCOVERY_APPROVED,
|
|
119
|
+
sessionId,
|
|
120
|
+
content: this.getWalletInfo()
|
|
121
|
+
});
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
rejectDiscovery(requestId) {
|
|
125
|
+
const discovery = this.pendingDiscoveries.get(requestId);
|
|
126
|
+
if (!discovery || discovery.status !== 'pending') {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
discovery.status = 'rejected';
|
|
130
|
+
this.pendingDiscoveries.delete(requestId);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
async handleKeyExchangeRequest(sessionId, request) {
|
|
134
|
+
const discovery = this.pendingDiscoveries.get(sessionId);
|
|
135
|
+
if (!discovery || discovery.status !== 'approved') {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const keyPair = await generateKeyPair();
|
|
140
|
+
const publicKey = await exportPublicKey(keyPair.publicKey);
|
|
141
|
+
const appPublicKey = await importPublicKey(request.publicKey);
|
|
142
|
+
const sessionKeys = await deriveSessionKeys(keyPair, appPublicKey, false);
|
|
143
|
+
const session = {
|
|
144
|
+
sessionId,
|
|
145
|
+
sharedKey: sessionKeys.encryptionKey,
|
|
146
|
+
verificationHash: sessionKeys.verificationHash,
|
|
147
|
+
tabId: discovery.tabId,
|
|
148
|
+
origin: discovery.origin,
|
|
149
|
+
appId: discovery.appId,
|
|
150
|
+
connectedAt: Date.now(),
|
|
151
|
+
chainInfo: discovery.chainInfo
|
|
152
|
+
};
|
|
153
|
+
this.activeSessions.set(sessionId, session);
|
|
154
|
+
this.pendingDiscoveries.delete(sessionId);
|
|
155
|
+
const response = {
|
|
156
|
+
type: WalletMessageType.KEY_EXCHANGE_RESPONSE,
|
|
157
|
+
requestId: sessionId,
|
|
158
|
+
publicKey
|
|
159
|
+
};
|
|
160
|
+
this.transport.sendToTab(discovery.tabId, {
|
|
161
|
+
origin: MessageOrigin.BACKGROUND,
|
|
162
|
+
type: InternalMessageType.KEY_EXCHANGE_RESPONSE,
|
|
163
|
+
sessionId,
|
|
164
|
+
content: response
|
|
165
|
+
});
|
|
166
|
+
this.callbacks.onSessionEstablished?.(session);
|
|
167
|
+
} catch {
|
|
168
|
+
// Key exchange failed silently - session won't be established
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async handleEncryptedMessage(sessionId, encrypted) {
|
|
172
|
+
const session = this.activeSessions.get(sessionId);
|
|
173
|
+
if (!session) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const message = await decrypt(session.sharedKey, encrypted);
|
|
178
|
+
this.callbacks.onWalletMessage?.(session, message);
|
|
179
|
+
} catch {
|
|
180
|
+
// Decryption failed - ignore malformed message
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async sendResponse(sessionId, response) {
|
|
184
|
+
const session = this.activeSessions.get(sessionId);
|
|
185
|
+
if (!session) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const encrypted = await encrypt(session.sharedKey, JSON.stringify(response));
|
|
190
|
+
this.transport.sendToTab(session.tabId, {
|
|
191
|
+
origin: MessageOrigin.BACKGROUND,
|
|
192
|
+
type: InternalMessageType.SECURE_RESPONSE,
|
|
193
|
+
sessionId,
|
|
194
|
+
content: encrypted
|
|
195
|
+
});
|
|
196
|
+
} catch {
|
|
197
|
+
// Encryption failed - response won't be sent
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
terminateSession(sessionId) {
|
|
201
|
+
const session = this.activeSessions.get(sessionId);
|
|
202
|
+
if (session) {
|
|
203
|
+
// Notify the content script (and ultimately the dApp) that the session is disconnected
|
|
204
|
+
this.transport.sendToTab(session.tabId, {
|
|
205
|
+
origin: MessageOrigin.BACKGROUND,
|
|
206
|
+
type: InternalMessageType.SESSION_DISCONNECTED,
|
|
207
|
+
sessionId
|
|
208
|
+
});
|
|
209
|
+
this.activeSessions.delete(sessionId);
|
|
210
|
+
this.callbacks.onSessionTerminated?.(sessionId);
|
|
211
|
+
// Restore discovery to approved state so user can retry key exchange
|
|
212
|
+
const discovery = {
|
|
213
|
+
requestId: sessionId,
|
|
214
|
+
appId: session.appId,
|
|
215
|
+
origin: session.origin,
|
|
216
|
+
chainInfo: session.chainInfo,
|
|
217
|
+
tabId: session.tabId,
|
|
218
|
+
timestamp: Date.now(),
|
|
219
|
+
status: 'approved'
|
|
220
|
+
};
|
|
221
|
+
this.pendingDiscoveries.set(sessionId, discovery);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
terminateForTab(tabId) {
|
|
225
|
+
for (const [sessionId, session] of this.activeSessions){
|
|
226
|
+
if (session.tabId === tabId) {
|
|
227
|
+
this.terminateSession(sessionId);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
for (const [requestId, discovery] of this.pendingDiscoveries){
|
|
231
|
+
if (discovery.tabId === tabId) {
|
|
232
|
+
this.pendingDiscoveries.delete(requestId);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
clearAll() {
|
|
237
|
+
for (const sessionId of this.activeSessions.keys()){
|
|
238
|
+
this.callbacks.onSessionTerminated?.(sessionId);
|
|
239
|
+
}
|
|
240
|
+
this.activeSessions.clear();
|
|
241
|
+
this.pendingDiscoveries.clear();
|
|
242
|
+
}
|
|
243
|
+
getPendingDiscoveries() {
|
|
244
|
+
return Array.from(this.pendingDiscoveries.values()).filter((d)=>d.status === 'pending');
|
|
245
|
+
}
|
|
246
|
+
getPendingDiscoveryCount() {
|
|
247
|
+
return this.getPendingDiscoveries().length;
|
|
248
|
+
}
|
|
249
|
+
getActiveSessions() {
|
|
250
|
+
return Array.from(this.activeSessions.values());
|
|
251
|
+
}
|
|
252
|
+
getSession(sessionId) {
|
|
253
|
+
return this.activeSessions.get(sessionId);
|
|
254
|
+
}
|
|
255
|
+
getPendingDiscovery(requestId) {
|
|
256
|
+
return this.pendingDiscoveries.get(requestId);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type BackgroundMessage, type ContentScriptMessage } from './internal_message_types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Transport interface for content script communication.
|
|
4
|
+
*/
|
|
5
|
+
export interface ContentScriptTransport {
|
|
6
|
+
/**
|
|
7
|
+
* Send a message to the background script.
|
|
8
|
+
* Typically `browser.runtime.sendMessage`.
|
|
9
|
+
*/
|
|
10
|
+
sendToBackground: (message: ContentScriptMessage) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Register a listener for messages from the background script.
|
|
13
|
+
* Typically `browser.runtime.onMessage.addListener`.
|
|
14
|
+
*/
|
|
15
|
+
addBackgroundListener: (handler: (message: BackgroundMessage) => void) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Handles wallet connection flow in the extension content script.
|
|
19
|
+
*
|
|
20
|
+
* This class manages:
|
|
21
|
+
* - Listening for discovery requests from the page
|
|
22
|
+
* - Creating MessageChannels after discovery approval
|
|
23
|
+
* - Relaying key exchange messages between page and background
|
|
24
|
+
* - Relaying encrypted messages between page and background
|
|
25
|
+
*
|
|
26
|
+
* The content script acts as a pure relay - it never has access to
|
|
27
|
+
* private keys or shared secrets.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const handler = new ContentScriptConnectionHandler({
|
|
32
|
+
* sendToBackground: (message) => browser.runtime.sendMessage(message),
|
|
33
|
+
* addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* handler.start();
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class ContentScriptConnectionHandler {
|
|
40
|
+
private transport;
|
|
41
|
+
private ports;
|
|
42
|
+
private listening;
|
|
43
|
+
private pageMessageHandler;
|
|
44
|
+
constructor(transport: ContentScriptTransport);
|
|
45
|
+
start(): void;
|
|
46
|
+
private handleBackgroundMessage;
|
|
47
|
+
private handleDiscoveryRequest;
|
|
48
|
+
private handleDiscoveryApproved;
|
|
49
|
+
private handleKeyExchangeResponse;
|
|
50
|
+
private handleSecureResponse;
|
|
51
|
+
private handleSessionDisconnected;
|
|
52
|
+
closeConnection(sessionId: string): void;
|
|
53
|
+
closeAllConnections(): void;
|
|
54
|
+
getConnectionCount(): number;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGVudF9zY3JpcHRfY29ubmVjdGlvbl9oYW5kbGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZXh0ZW5zaW9uL2hhbmRsZXJzL2NvbnRlbnRfc2NyaXB0X2Nvbm5lY3Rpb25faGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQ0wsS0FBSyxpQkFBaUIsRUFDdEIsS0FBSyxvQkFBb0IsRUFHMUIsTUFBTSw2QkFBNkIsQ0FBQztBQW9DckM7O0dBRUc7QUFDSCxNQUFNLFdBQVcsc0JBQXNCO0lBQ3JDOzs7T0FHRztJQUNILGdCQUFnQixFQUFFLENBQUMsT0FBTyxFQUFFLG9CQUFvQixLQUFLLElBQUksQ0FBQztJQUUxRDs7O09BR0c7SUFDSCxxQkFBcUIsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLE9BQU8sRUFBRSxpQkFBaUIsS0FBSyxJQUFJLEtBQUssSUFBSSxDQUFDO0NBQ2hGO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXFCRztBQUNILHFCQUFhLDhCQUE4QjtJQUs3QixPQUFPLENBQUMsU0FBUztJQUo3QixPQUFPLENBQUMsS0FBSyxDQUFxQztJQUNsRCxPQUFPLENBQUMsU0FBUyxDQUFTO0lBQzFCLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBZ0Q7SUFFMUUsWUFBb0IsU0FBUyxFQUFFLHNCQUFzQixFQUFJO0lBRXpELEtBQUssSUFBSSxJQUFJLENBOEJaO0lBRUQsT0FBTyxDQUFDLHVCQUF1QixDQXFCN0I7SUFFRixPQUFPLENBQUMsc0JBQXNCO0lBUTlCLE9BQU8sQ0FBQyx1QkFBdUI7SUFpRC9CLE9BQU8sQ0FBQyx5QkFBeUI7SUFRakMsT0FBTyxDQUFDLG9CQUFvQjtJQVE1QixPQUFPLENBQUMseUJBQXlCO0lBVWpDLGVBQWUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxHQUFHLElBQUksQ0FNdkM7SUFFRCxtQkFBbUIsSUFBSSxJQUFJLENBSzFCO0lBRUQsa0JBQWtCLElBQUksTUFBTSxDQUUzQjtDQUNGIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content_script_connection_handler.d.ts","sourceRoot":"","sources":["../../../src/extension/handlers/content_script_connection_handler.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAG1B,MAAM,6BAA6B,CAAC;AAoCrC;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,gBAAgB,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAE1D;;;OAGG;IACH,qBAAqB,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,KAAK,IAAI,CAAC;CAChF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,8BAA8B;IAK7B,OAAO,CAAC,SAAS;IAJ7B,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAgD;IAE1E,YAAoB,SAAS,EAAE,sBAAsB,EAAI;IAEzD,KAAK,IAAI,IAAI,CA8BZ;IAED,OAAO,CAAC,uBAAuB,CAqB7B;IAEF,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,uBAAuB;IAiD/B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,yBAAyB;IAUjC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAMvC;IAED,mBAAmB,IAAI,IAAI,CAK1B;IAED,kBAAkB,IAAI,MAAM,CAE3B;CACF"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { WalletMessageType } from '../../types.js';
|
|
2
|
+
import { InternalMessageType, MessageOrigin } from './internal_message_types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles wallet connection flow in the extension content script.
|
|
5
|
+
*
|
|
6
|
+
* This class manages:
|
|
7
|
+
* - Listening for discovery requests from the page
|
|
8
|
+
* - Creating MessageChannels after discovery approval
|
|
9
|
+
* - Relaying key exchange messages between page and background
|
|
10
|
+
* - Relaying encrypted messages between page and background
|
|
11
|
+
*
|
|
12
|
+
* The content script acts as a pure relay - it never has access to
|
|
13
|
+
* private keys or shared secrets.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const handler = new ContentScriptConnectionHandler({
|
|
18
|
+
* sendToBackground: (message) => browser.runtime.sendMessage(message),
|
|
19
|
+
* addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* handler.start();
|
|
23
|
+
* ```
|
|
24
|
+
*/ export class ContentScriptConnectionHandler {
|
|
25
|
+
transport;
|
|
26
|
+
ports;
|
|
27
|
+
listening;
|
|
28
|
+
pageMessageHandler;
|
|
29
|
+
constructor(transport){
|
|
30
|
+
this.transport = transport;
|
|
31
|
+
this.ports = new Map();
|
|
32
|
+
this.listening = false;
|
|
33
|
+
this.pageMessageHandler = null;
|
|
34
|
+
this.handleBackgroundMessage = (message)=>{
|
|
35
|
+
if (message.origin !== MessageOrigin.BACKGROUND) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const { type, sessionId, content } = message;
|
|
39
|
+
switch(type){
|
|
40
|
+
case InternalMessageType.DISCOVERY_APPROVED:
|
|
41
|
+
this.handleDiscoveryApproved(sessionId, content);
|
|
42
|
+
break;
|
|
43
|
+
case InternalMessageType.KEY_EXCHANGE_RESPONSE:
|
|
44
|
+
this.handleKeyExchangeResponse(sessionId, content);
|
|
45
|
+
break;
|
|
46
|
+
case InternalMessageType.SECURE_RESPONSE:
|
|
47
|
+
this.handleSecureResponse(sessionId, content);
|
|
48
|
+
break;
|
|
49
|
+
case InternalMessageType.SESSION_DISCONNECTED:
|
|
50
|
+
this.handleSessionDisconnected(sessionId);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
start() {
|
|
56
|
+
if (this.listening) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.transport.addBackgroundListener(this.handleBackgroundMessage);
|
|
60
|
+
this.pageMessageHandler = (event)=>{
|
|
61
|
+
if (event.source !== window) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
let data;
|
|
65
|
+
try {
|
|
66
|
+
data = JSON.parse(event.data);
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (!data?.type) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (data.type === WalletMessageType.DISCOVERY) {
|
|
74
|
+
this.handleDiscoveryRequest(data);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
window.addEventListener('message', this.pageMessageHandler);
|
|
78
|
+
this.listening = true;
|
|
79
|
+
}
|
|
80
|
+
handleBackgroundMessage;
|
|
81
|
+
handleDiscoveryRequest(request) {
|
|
82
|
+
this.transport.sendToBackground({
|
|
83
|
+
origin: MessageOrigin.CONTENT_SCRIPT,
|
|
84
|
+
type: InternalMessageType.DISCOVERY_REQUEST,
|
|
85
|
+
content: request
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
handleDiscoveryApproved(sessionId, walletInfo) {
|
|
89
|
+
const channel = new MessageChannel();
|
|
90
|
+
this.ports.set(sessionId, {
|
|
91
|
+
port: channel.port1,
|
|
92
|
+
sessionId
|
|
93
|
+
});
|
|
94
|
+
channel.port1.onmessage = (event)=>{
|
|
95
|
+
const data = event.data;
|
|
96
|
+
switch(data?.type){
|
|
97
|
+
case WalletMessageType.KEY_EXCHANGE_REQUEST:
|
|
98
|
+
this.transport.sendToBackground({
|
|
99
|
+
origin: MessageOrigin.CONTENT_SCRIPT,
|
|
100
|
+
type: InternalMessageType.KEY_EXCHANGE_REQUEST,
|
|
101
|
+
sessionId,
|
|
102
|
+
content: data
|
|
103
|
+
});
|
|
104
|
+
break;
|
|
105
|
+
case WalletMessageType.DISCONNECT:
|
|
106
|
+
this.transport.sendToBackground({
|
|
107
|
+
origin: MessageOrigin.CONTENT_SCRIPT,
|
|
108
|
+
type: InternalMessageType.DISCONNECT_REQUEST,
|
|
109
|
+
sessionId,
|
|
110
|
+
content: data
|
|
111
|
+
});
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
this.transport.sendToBackground({
|
|
115
|
+
origin: MessageOrigin.CONTENT_SCRIPT,
|
|
116
|
+
type: InternalMessageType.SECURE_MESSAGE,
|
|
117
|
+
sessionId,
|
|
118
|
+
content: data
|
|
119
|
+
});
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
channel.port1.start();
|
|
124
|
+
const response = {
|
|
125
|
+
type: WalletMessageType.DISCOVERY_RESPONSE,
|
|
126
|
+
requestId: sessionId,
|
|
127
|
+
walletInfo
|
|
128
|
+
};
|
|
129
|
+
window.postMessage(JSON.stringify(response), '*', [
|
|
130
|
+
channel.port2
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
handleKeyExchangeResponse(sessionId, response) {
|
|
134
|
+
const connection = this.ports.get(sessionId);
|
|
135
|
+
if (!connection) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
connection.port.postMessage(response);
|
|
139
|
+
}
|
|
140
|
+
handleSecureResponse(sessionId, content) {
|
|
141
|
+
const connection = this.ports.get(sessionId);
|
|
142
|
+
if (!connection) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
connection.port.postMessage(content);
|
|
146
|
+
}
|
|
147
|
+
handleSessionDisconnected(sessionId) {
|
|
148
|
+
const connection = this.ports.get(sessionId);
|
|
149
|
+
if (!connection) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
connection.port.postMessage({
|
|
153
|
+
type: WalletMessageType.DISCONNECT
|
|
154
|
+
});
|
|
155
|
+
connection.port.close();
|
|
156
|
+
this.ports.delete(sessionId);
|
|
157
|
+
}
|
|
158
|
+
closeConnection(sessionId) {
|
|
159
|
+
const connection = this.ports.get(sessionId);
|
|
160
|
+
if (connection) {
|
|
161
|
+
connection.port.close();
|
|
162
|
+
this.ports.delete(sessionId);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
closeAllConnections() {
|
|
166
|
+
for (const [sessionId, connection] of this.ports){
|
|
167
|
+
connection.port.close();
|
|
168
|
+
this.ports.delete(sessionId);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
getConnectionCount() {
|
|
172
|
+
return this.ports.size;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe exports for extension background and content scripts.
|
|
3
|
+
*
|
|
4
|
+
* This module contains ONLY handlers that work in service worker/content script
|
|
5
|
+
* environments without Node.js polyfills.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { BackgroundConnectionHandler, type PendingDiscovery, type ActiveSession, type DiscoveryStatus, type BackgroundConnectionCallbacks, type BackgroundConnectionConfig, type BackgroundTransport, } from './background_connection_handler.js';
|
|
10
|
+
export { ContentScriptConnectionHandler, type ContentScriptTransport } from './content_script_connection_handler.js';
|
|
11
|
+
export { type ContentScriptMessage, type BackgroundMessage, type MessageSender, type MessageOriginType, MessageOrigin, } from './internal_message_types.js';
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHRlbnNpb24vaGFuZGxlcnMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7R0FPRztBQUNILE9BQU8sRUFDTCwyQkFBMkIsRUFDM0IsS0FBSyxnQkFBZ0IsRUFDckIsS0FBSyxhQUFhLEVBQ2xCLEtBQUssZUFBZSxFQUNwQixLQUFLLDZCQUE2QixFQUNsQyxLQUFLLDBCQUEwQixFQUMvQixLQUFLLG1CQUFtQixHQUN6QixNQUFNLG9DQUFvQyxDQUFDO0FBQzVDLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxLQUFLLHNCQUFzQixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDckgsT0FBTyxFQUNMLEtBQUssb0JBQW9CLEVBQ3pCLEtBQUssaUJBQWlCLEVBQ3RCLEtBQUssYUFBYSxFQUNsQixLQUFLLGlCQUFpQixFQUN0QixhQUFhLEdBQ2QsTUFBTSw2QkFBNkIsQ0FBQyJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extension/handlers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,2BAA2B,EAC3B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,8BAA8B,EAAE,KAAK,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,aAAa,GACd,MAAM,6BAA6B,CAAC"}
|