@scriptdb/browser-client 1.1.1 → 1.1.2
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 +1 -1
- package/dist/index.d.mts +19 -3
- package/dist/index.d.ts +19 -3
- package/dist/index.js +45 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.
|
|
5
5
|
* The proxy handles all communication with the ScriptDB server using @scriptdb/client.
|
|
6
6
|
*/
|
|
7
|
+
type Action = 'script-code' | 'save-db' | 'remove-db' | 'rename-db' | 'login' | 'logout' | 'get-info' | 'list-dbs' | 'create-db' | 'update-db' | 'get-db' | 'shell-command';
|
|
7
8
|
interface Logger {
|
|
8
9
|
debug?: (...args: any[]) => void;
|
|
9
10
|
info?: (...args: any[]) => void;
|
|
@@ -19,6 +20,12 @@ interface ClientOptions {
|
|
|
19
20
|
requestTimeout?: number;
|
|
20
21
|
secure?: boolean;
|
|
21
22
|
}
|
|
23
|
+
interface Message {
|
|
24
|
+
id?: number;
|
|
25
|
+
action?: Action;
|
|
26
|
+
message?: string;
|
|
27
|
+
data?: any;
|
|
28
|
+
}
|
|
22
29
|
declare class BrowserClient {
|
|
23
30
|
private logger;
|
|
24
31
|
private host;
|
|
@@ -49,9 +56,9 @@ declare class BrowserClient {
|
|
|
49
56
|
*/
|
|
50
57
|
private _handleMessage;
|
|
51
58
|
/**
|
|
52
|
-
* Send request to proxy
|
|
59
|
+
* Send request to proxy (internal helper)
|
|
53
60
|
*/
|
|
54
|
-
private
|
|
61
|
+
private _sendRequest;
|
|
55
62
|
/**
|
|
56
63
|
* Cleanup on disconnect
|
|
57
64
|
*/
|
|
@@ -64,6 +71,15 @@ declare class BrowserClient {
|
|
|
64
71
|
* Disconnect (alias for close)
|
|
65
72
|
*/
|
|
66
73
|
disconnect(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Send request helper (for public API methods)
|
|
76
|
+
*/
|
|
77
|
+
sendRequest(action: Action, data?: any): Promise<any>;
|
|
78
|
+
/**
|
|
79
|
+
* Execute a command. Supports concurrent requests via request id mapping.
|
|
80
|
+
* Returns a Promise resolved with response.data or rejected on ERROR/timeout.
|
|
81
|
+
*/
|
|
82
|
+
execute(message: Message): Promise<any>;
|
|
67
83
|
/**
|
|
68
84
|
* Login with username and password
|
|
69
85
|
*/
|
|
@@ -91,7 +107,7 @@ declare class BrowserClient {
|
|
|
91
107
|
/**
|
|
92
108
|
* Execute code in a database
|
|
93
109
|
*/
|
|
94
|
-
run(code: string, databaseName: string): Promise<any>;
|
|
110
|
+
run(code: string | Function, databaseName: string): Promise<any>;
|
|
95
111
|
/**
|
|
96
112
|
* Save a database to disk
|
|
97
113
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.
|
|
5
5
|
* The proxy handles all communication with the ScriptDB server using @scriptdb/client.
|
|
6
6
|
*/
|
|
7
|
+
type Action = 'script-code' | 'save-db' | 'remove-db' | 'rename-db' | 'login' | 'logout' | 'get-info' | 'list-dbs' | 'create-db' | 'update-db' | 'get-db' | 'shell-command';
|
|
7
8
|
interface Logger {
|
|
8
9
|
debug?: (...args: any[]) => void;
|
|
9
10
|
info?: (...args: any[]) => void;
|
|
@@ -19,6 +20,12 @@ interface ClientOptions {
|
|
|
19
20
|
requestTimeout?: number;
|
|
20
21
|
secure?: boolean;
|
|
21
22
|
}
|
|
23
|
+
interface Message {
|
|
24
|
+
id?: number;
|
|
25
|
+
action?: Action;
|
|
26
|
+
message?: string;
|
|
27
|
+
data?: any;
|
|
28
|
+
}
|
|
22
29
|
declare class BrowserClient {
|
|
23
30
|
private logger;
|
|
24
31
|
private host;
|
|
@@ -49,9 +56,9 @@ declare class BrowserClient {
|
|
|
49
56
|
*/
|
|
50
57
|
private _handleMessage;
|
|
51
58
|
/**
|
|
52
|
-
* Send request to proxy
|
|
59
|
+
* Send request to proxy (internal helper)
|
|
53
60
|
*/
|
|
54
|
-
private
|
|
61
|
+
private _sendRequest;
|
|
55
62
|
/**
|
|
56
63
|
* Cleanup on disconnect
|
|
57
64
|
*/
|
|
@@ -64,6 +71,15 @@ declare class BrowserClient {
|
|
|
64
71
|
* Disconnect (alias for close)
|
|
65
72
|
*/
|
|
66
73
|
disconnect(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Send request helper (for public API methods)
|
|
76
|
+
*/
|
|
77
|
+
sendRequest(action: Action, data?: any): Promise<any>;
|
|
78
|
+
/**
|
|
79
|
+
* Execute a command. Supports concurrent requests via request id mapping.
|
|
80
|
+
* Returns a Promise resolved with response.data or rejected on ERROR/timeout.
|
|
81
|
+
*/
|
|
82
|
+
execute(message: Message): Promise<any>;
|
|
67
83
|
/**
|
|
68
84
|
* Login with username and password
|
|
69
85
|
*/
|
|
@@ -91,7 +107,7 @@ declare class BrowserClient {
|
|
|
91
107
|
/**
|
|
92
108
|
* Execute code in a database
|
|
93
109
|
*/
|
|
94
|
-
run(code: string, databaseName: string): Promise<any>;
|
|
110
|
+
run(code: string | Function, databaseName: string): Promise<any>;
|
|
95
111
|
/**
|
|
96
112
|
* Save a database to disk
|
|
97
113
|
*/
|
package/dist/index.js
CHANGED
|
@@ -155,9 +155,9 @@ var BrowserClient = class {
|
|
|
155
155
|
console.log("Message has no id, ignoring");
|
|
156
156
|
}
|
|
157
157
|
/**
|
|
158
|
-
* Send request to proxy
|
|
158
|
+
* Send request to proxy (internal helper)
|
|
159
159
|
*/
|
|
160
|
-
async
|
|
160
|
+
async _sendRequest(action, data = {}) {
|
|
161
161
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
162
162
|
throw new Error("Not connected");
|
|
163
163
|
}
|
|
@@ -215,6 +215,22 @@ var BrowserClient = class {
|
|
|
215
215
|
async disconnect() {
|
|
216
216
|
this.close();
|
|
217
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Send request helper (for public API methods)
|
|
220
|
+
*/
|
|
221
|
+
async sendRequest(action, data = {}) {
|
|
222
|
+
return this.execute({ action, data });
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Execute a command. Supports concurrent requests via request id mapping.
|
|
226
|
+
* Returns a Promise resolved with response.data or rejected on ERROR/timeout.
|
|
227
|
+
*/
|
|
228
|
+
async execute(message) {
|
|
229
|
+
if (!message.action) {
|
|
230
|
+
throw new Error("Action is required");
|
|
231
|
+
}
|
|
232
|
+
return this._sendRequest(message.action, message.data);
|
|
233
|
+
}
|
|
218
234
|
// ========== Public API Methods ==========
|
|
219
235
|
/**
|
|
220
236
|
* Login with username and password
|
|
@@ -256,7 +272,33 @@ var BrowserClient = class {
|
|
|
256
272
|
* Execute code in a database
|
|
257
273
|
*/
|
|
258
274
|
async run(code, databaseName) {
|
|
259
|
-
|
|
275
|
+
let stringCode;
|
|
276
|
+
if (typeof code === "function") {
|
|
277
|
+
const funcStr = code.toString();
|
|
278
|
+
const arrowMatch = funcStr.match(/^[\s]*\(?\s*\)?\s*=>\s*{?/);
|
|
279
|
+
const functionMatch = funcStr.match(/^[\s]*function\s*\(?[\w\s]*\)?\s*{/);
|
|
280
|
+
const match = arrowMatch || functionMatch;
|
|
281
|
+
const start = match ? match[0].length : 0;
|
|
282
|
+
const end = funcStr.lastIndexOf("}");
|
|
283
|
+
stringCode = funcStr.substring(start, end);
|
|
284
|
+
stringCode = stringCode.replace(/^[\s\r\n]+/, "").replace(/[\s\r\n]+$/, "");
|
|
285
|
+
stringCode = stringCode.replace(
|
|
286
|
+
/import\s*\(\s*([^)]+?)\s*\)\s*\.from\s*\(\s*(['"])([^'"]+)\2\s*\)/g,
|
|
287
|
+
(_, importArg, quote, modulePath) => {
|
|
288
|
+
const trimmed = importArg.trim();
|
|
289
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
290
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
291
|
+
return `import { ${inner} } from ${quote}${modulePath}${quote}`;
|
|
292
|
+
} else {
|
|
293
|
+
return `import ${trimmed} from ${quote}${modulePath}${quote}`;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
stringCode = stringCode.split("\n").map((line) => line.trim()).join("\n").trim();
|
|
298
|
+
} else {
|
|
299
|
+
stringCode = code;
|
|
300
|
+
}
|
|
301
|
+
return this.sendRequest("script-code", { code: stringCode, databaseName });
|
|
260
302
|
}
|
|
261
303
|
/**
|
|
262
304
|
* Save a database to disk
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Browser-compatible ScriptDB Client\n * \n * This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.\n * The proxy handles all communication with the ScriptDB server using @scriptdb/client.\n */\n\ntype Action = 'script-code' | 'save-db' | 'remove-db' | 'rename-db' | 'login' | 'logout' | 'get-info' | 'list-dbs' | 'create-db' | 'update-db' | 'get-db' | 'shell-command';\n\ninterface Logger {\n debug?: (...args: any[]) => void;\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n}\n\ninterface ClientOptions {\n host?: string;\n port?: number;\n username?: string;\n password?: string;\n logger?: Logger;\n requestTimeout?: number;\n secure?: boolean;\n}\n\ninterface PendingRequest {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n timer: number | null;\n}\n\ninterface Message {\n id?: number;\n action?: Action;\n message?: string;\n data?: any;\n}\n\nconst noopLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport class BrowserClient {\n private logger: Logger;\n private host: string;\n private port: number;\n private username: string | null;\n private password: string | null;\n private requestTimeout: number;\n private secure: boolean;\n\n private ws: WebSocket | null = null;\n private _connected: boolean = false;\n private _nextId: number = 1;\n private _pending: Map<number, PendingRequest> = new Map();\n\n constructor(options: ClientOptions) {\n this.logger = options.logger || noopLogger;\n this.host = options.host || 'localhost';\n this.port = options.port || 1234;\n this.username = options.username || null;\n this.password = options.password || null;\n this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout! : 120000; // 2 minutes default\n this.secure = options.secure !== undefined ? options.secure : false;\n }\n\n /**\n * Check if connected\n */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Connect to WebSocket proxy and authenticate\n */\n async connect(): Promise<BrowserClient> {\n return new Promise((resolve, reject) => {\n const wsPort = this.port + 1;\n const wsUrl = `ws://${this.host}:${wsPort}`;\n this.logger.info?.('Connecting to', wsUrl);\n\n try {\n this.ws = new WebSocket(wsUrl);\n } catch (e) {\n const error = e as Error;\n this.logger.error?.(\"Connection failed\", error.message);\n return reject(error);\n }\n\n this.ws.onopen = async () => {\n console.log('WebSocket onopen - browserClient.ts');\n this.logger.info?.('WebSocket connected');\n this._connected = true;\n this._setupListeners();\n\n // Always send login request (even with empty credentials for anonymous access)\n try {\n console.log('Sending login request with credentials:', { username: this.username || '', hasPassword: !!this.password, secure: this.secure });\n await this.sendRequest('login', { \n username: this.username || '', \n password: this.password || '',\n secure: this.secure\n });\n console.log('Authenticated successfully');\n this.logger.info?.('Authenticated successfully');\n resolve(this);\n } catch (err) {\n console.error('Login failed:', err);\n reject(err);\n }\n };\n\n this.ws.onerror = (error) => {\n console.error('WebSocket onerror:', error);\n this.logger.error?.('WebSocket error:', error);\n reject(new Error('WebSocket connection failed'));\n };\n\n this.ws.onclose = () => {\n console.log('WebSocket onclose');\n this.logger.info?.('WebSocket disconnected');\n this._connected = false;\n this._cleanup();\n };\n });\n }\n\n /**\n * Setup message listeners\n */\n private _setupListeners() {\n if (!this.ws) return;\n\n this.ws.onmessage = (event) => {\n console.log('WebSocket onmessage fired - browserClient.ts', event.data);\n try {\n const msg = JSON.parse(event.data) as Message;\n console.log('Parsed message:', msg);\n this._handleMessage(msg);\n } catch (error) {\n console.error('Failed to parse message:', error, event.data);\n this.logger.error?.('Failed to parse message:', error);\n }\n };\n }\n\n /**\n * Handle incoming message\n */\n private _handleMessage(msg: Message) {\n console.log('_handleMessage called with:', msg);\n if (!msg || typeof msg !== \"object\") {\n console.log('Invalid message - not an object');\n return;\n }\n\n this.logger.debug?.('Received message:', msg);\n\n // Handle response with id\n if (typeof msg.id !== \"undefined\") {\n console.log('Message has id:', msg.id);\n const pending = this._pending.get(msg.id);\n console.log('Pending request found:', !!pending, 'total pending:', this._pending.size);\n if (!pending) {\n this.logger.debug?.(\"No pending request for id\", msg.id);\n console.log(\"No pending request for id\", msg.id);\n return;\n }\n\n const { resolve, reject, timer } = pending;\n if (timer) clearTimeout(timer);\n this._pending.delete(msg.id);\n\n console.log('Resolving request with message:', msg.message, 'data:', msg.data);\n\n // Check for errors\n if (msg.message === \"ERROR\" || msg.message === \"AUTH FAILED\" || msg.message === \"AUTH FAIL\") {\n return reject(new Error(typeof msg.data === 'string' ? msg.data : \"Request failed\"));\n }\n\n // Success - AUTH OK, OK, or SUCCESS\n if (msg.message === \"AUTH OK\" || msg.message === \"OK\" || msg.message === \"SUCCESS\") {\n console.log('Calling resolve with data:', msg.data);\n return resolve(msg.data);\n }\n\n // Default to resolve with data\n console.log('Default resolve with data:', msg.data);\n return resolve(msg.data);\n }\n console.log('Message has no id, ignoring');\n }\n\n /**\n * Send request to proxy\n */\n private async sendRequest(action: Action, data: any = {}): Promise<any> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n\n const id = this._nextId++;\n const request = { id, action, data };\n\n return new Promise((resolve, reject) => {\n let timer: number | null = null;\n if (this.requestTimeout > 0) {\n timer = window.setTimeout(() => {\n if (this._pending.has(id)) {\n this._pending.delete(id);\n reject(new Error(\"Request timeout\"));\n }\n }, this.requestTimeout);\n }\n\n this._pending.set(id, { resolve, reject, timer });\n\n try {\n this.ws?.send(JSON.stringify(request));\n } catch (e) {\n if (timer) clearTimeout(timer);\n this._pending.delete(id);\n reject(e);\n }\n });\n }\n\n /**\n * Cleanup on disconnect\n */\n private _cleanup() {\n this._pending.forEach((pending) => {\n if (pending.timer) clearTimeout(pending.timer);\n try {\n pending.reject(new Error(\"Disconnected\"));\n } catch (e) {}\n });\n this._pending.clear();\n }\n\n /**\n * Close connection\n */\n close() {\n if (this.ws) {\n try {\n this.ws.close();\n } catch (e) {}\n this.ws = null;\n }\n this._cleanup();\n }\n\n /**\n * Disconnect (alias for close)\n */\n async disconnect(): Promise<void> {\n this.close();\n }\n\n // ========== Public API Methods ==========\n\n /**\n * Login with username and password\n */\n async login(username: string, password: string): Promise<any> {\n return this.sendRequest('login', { username, password });\n }\n\n /**\n * Logout from server\n */\n async logout(): Promise<any> {\n return this.sendRequest('logout', {});\n }\n\n /**\n * List all databases\n */\n async listDatabases(): Promise<any> {\n return this.sendRequest('list-dbs', {});\n }\n\n /**\n * Create a new database\n */\n async createDatabase(name: string): Promise<any> {\n return this.sendRequest('create-db', { databaseName: name });\n }\n\n /**\n * Remove a database\n */\n async removeDatabase(name: string): Promise<any> {\n return this.sendRequest('remove-db', { databaseName: name });\n }\n\n /**\n * Rename a database\n */\n async renameDatabase(oldName: string, newName: string): Promise<any> {\n return this.sendRequest('rename-db', { databaseName: oldName, newName });\n }\n\n /**\n * Execute code in a database\n */\n async run(code: string, databaseName: string): Promise<any> {\n return this.sendRequest('script-code', { code, databaseName });\n }\n\n /**\n * Save a database to disk\n */\n async saveDatabase(databaseName: string): Promise<any> {\n return this.sendRequest('save-db', { databaseName });\n }\n\n /**\n * Update database metadata\n */\n async updateDatabase(databaseName: string, data: any): Promise<any> {\n return this.sendRequest('update-db', { databaseName, ...data });\n }\n\n /**\n * Get database content\n */\n async getDatabase(name: string): Promise<{ success: boolean; databaseName: string; content: string; path?: string }> {\n return this.sendRequest('get-db', { databaseName: name });\n }\n\n /**\n * Get server information\n */\n async getInfo(): Promise<any> {\n return this.sendRequest('get-info', {});\n }\n\n /**\n * Execute shell command on server\n */\n async executeShell(command: string): Promise<{ stdout: string; stderr: string }> {\n return this.sendRequest('shell-command', { command });\n }\n}\n\nexport default BrowserClient;\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Browser-compatible ScriptDB Client\n * \n * This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.\n * The proxy handles all communication with the ScriptDB server using @scriptdb/client.\n */\n\ntype Action = 'script-code' | 'save-db' | 'remove-db' | 'rename-db' | 'login' | 'logout' | 'get-info' | 'list-dbs' | 'create-db' | 'update-db' | 'get-db' | 'shell-command';\n\ninterface Logger {\n debug?: (...args: any[]) => void;\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n}\n\ninterface ClientOptions {\n host?: string;\n port?: number;\n username?: string;\n password?: string;\n logger?: Logger;\n requestTimeout?: number;\n secure?: boolean;\n}\n\ninterface PendingRequest {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n timer: number | null;\n}\n\ninterface Message {\n id?: number;\n action?: Action;\n message?: string;\n data?: any;\n}\n\nconst noopLogger: Logger = {\n debug: () => { },\n info: () => { },\n warn: () => { },\n error: () => { },\n};\n\nexport class BrowserClient {\n private logger: Logger;\n private host: string;\n private port: number;\n private username: string | null;\n private password: string | null;\n private requestTimeout: number;\n private secure: boolean;\n\n private ws: WebSocket | null = null;\n private _connected: boolean = false;\n private _nextId: number = 1;\n private _pending: Map<number, PendingRequest> = new Map();\n\n constructor(options: ClientOptions) {\n this.logger = options.logger || noopLogger;\n this.host = options.host || 'localhost';\n this.port = options.port || 1234;\n this.username = options.username || null;\n this.password = options.password || null;\n this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout! : 120000; // 2 minutes default\n this.secure = options.secure !== undefined ? options.secure : false;\n }\n\n /**\n * Check if connected\n */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Connect to WebSocket proxy and authenticate\n */\n async connect(): Promise<BrowserClient> {\n return new Promise((resolve, reject) => {\n const wsPort = this.port + 1;\n const wsUrl = `ws://${this.host}:${wsPort}`;\n this.logger.info?.('Connecting to', wsUrl);\n\n try {\n this.ws = new WebSocket(wsUrl);\n } catch (e) {\n const error = e as Error;\n this.logger.error?.(\"Connection failed\", error.message);\n return reject(error);\n }\n\n this.ws.onopen = async () => {\n console.log('WebSocket onopen - browserClient.ts');\n this.logger.info?.('WebSocket connected');\n this._connected = true;\n this._setupListeners();\n\n // Always send login request (even with empty credentials for anonymous access)\n try {\n console.log('Sending login request with credentials:', { username: this.username || '', hasPassword: !!this.password, secure: this.secure });\n await this.sendRequest('login', {\n username: this.username || '',\n password: this.password || '',\n secure: this.secure\n });\n console.log('Authenticated successfully');\n this.logger.info?.('Authenticated successfully');\n resolve(this);\n } catch (err) {\n console.error('Login failed:', err);\n reject(err);\n }\n };\n\n this.ws.onerror = (error) => {\n console.error('WebSocket onerror:', error);\n this.logger.error?.('WebSocket error:', error);\n reject(new Error('WebSocket connection failed'));\n };\n\n this.ws.onclose = () => {\n console.log('WebSocket onclose');\n this.logger.info?.('WebSocket disconnected');\n this._connected = false;\n this._cleanup();\n };\n });\n }\n\n /**\n * Setup message listeners\n */\n private _setupListeners() {\n if (!this.ws) return;\n\n this.ws.onmessage = (event) => {\n console.log('WebSocket onmessage fired - browserClient.ts', event.data);\n try {\n const msg = JSON.parse(event.data) as Message;\n console.log('Parsed message:', msg);\n this._handleMessage(msg);\n } catch (error) {\n console.error('Failed to parse message:', error, event.data);\n this.logger.error?.('Failed to parse message:', error);\n }\n };\n }\n\n /**\n * Handle incoming message\n */\n private _handleMessage(msg: Message) {\n console.log('_handleMessage called with:', msg);\n if (!msg || typeof msg !== \"object\") {\n console.log('Invalid message - not an object');\n return;\n }\n\n this.logger.debug?.('Received message:', msg);\n\n // Handle response with id\n if (typeof msg.id !== \"undefined\") {\n console.log('Message has id:', msg.id);\n const pending = this._pending.get(msg.id);\n console.log('Pending request found:', !!pending, 'total pending:', this._pending.size);\n if (!pending) {\n this.logger.debug?.(\"No pending request for id\", msg.id);\n console.log(\"No pending request for id\", msg.id);\n return;\n }\n\n const { resolve, reject, timer } = pending;\n if (timer) clearTimeout(timer);\n this._pending.delete(msg.id);\n\n console.log('Resolving request with message:', msg.message, 'data:', msg.data);\n\n // Check for errors\n if (msg.message === \"ERROR\" || msg.message === \"AUTH FAILED\" || msg.message === \"AUTH FAIL\") {\n return reject(new Error(typeof msg.data === 'string' ? msg.data : \"Request failed\"));\n }\n\n // Success - AUTH OK, OK, or SUCCESS\n if (msg.message === \"AUTH OK\" || msg.message === \"OK\" || msg.message === \"SUCCESS\") {\n console.log('Calling resolve with data:', msg.data);\n return resolve(msg.data);\n }\n\n // Default to resolve with data\n console.log('Default resolve with data:', msg.data);\n return resolve(msg.data);\n }\n console.log('Message has no id, ignoring');\n }\n\n /**\n * Send request to proxy (internal helper)\n */\n private async _sendRequest(action: Action, data: any = {}): Promise<any> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n\n const id = this._nextId++;\n const request = { id, action, data };\n\n return new Promise((resolve, reject) => {\n let timer: number | null = null;\n if (this.requestTimeout > 0) {\n timer = window.setTimeout(() => {\n if (this._pending.has(id)) {\n this._pending.delete(id);\n reject(new Error(\"Request timeout\"));\n }\n }, this.requestTimeout);\n }\n\n this._pending.set(id, { resolve, reject, timer });\n\n try {\n this.ws?.send(JSON.stringify(request));\n } catch (e) {\n if (timer) clearTimeout(timer);\n this._pending.delete(id);\n reject(e);\n }\n });\n }\n\n /**\n * Cleanup on disconnect\n */\n private _cleanup() {\n this._pending.forEach((pending) => {\n if (pending.timer) clearTimeout(pending.timer);\n try {\n pending.reject(new Error(\"Disconnected\"));\n } catch (e) { }\n });\n this._pending.clear();\n }\n\n /**\n * Close connection\n */\n close() {\n if (this.ws) {\n try {\n this.ws.close();\n } catch (e) { }\n this.ws = null;\n }\n this._cleanup();\n }\n\n /**\n * Disconnect (alias for close)\n */\n async disconnect(): Promise<void> {\n this.close();\n }\n\n /**\n * Send request helper (for public API methods)\n */\n async sendRequest(action: Action, data: any = {}): Promise<any> {\n return this.execute({ action, data });\n }\n\n /**\n * Execute a command. Supports concurrent requests via request id mapping.\n * Returns a Promise resolved with response.data or rejected on ERROR/timeout.\n */\n async execute(message: Message): Promise<any> {\n if (!message.action) {\n throw new Error('Action is required');\n }\n return this._sendRequest(message.action, message.data);\n }\n\n // ========== Public API Methods ==========\n\n /**\n * Login with username and password\n */\n async login(username: string, password: string): Promise<any> {\n return this.sendRequest('login', { username, password });\n }\n\n /**\n * Logout from server\n */\n async logout(): Promise<any> {\n return this.sendRequest('logout', {});\n }\n\n /**\n * List all databases\n */\n async listDatabases(): Promise<any> {\n return this.sendRequest('list-dbs', {});\n }\n\n /**\n * Create a new database\n */\n async createDatabase(name: string): Promise<any> {\n return this.sendRequest('create-db', { databaseName: name });\n }\n\n /**\n * Remove a database\n */\n async removeDatabase(name: string): Promise<any> {\n return this.sendRequest('remove-db', { databaseName: name });\n }\n\n /**\n * Rename a database\n */\n async renameDatabase(oldName: string, newName: string): Promise<any> {\n return this.sendRequest('rename-db', { databaseName: oldName, newName });\n }\n\n /**\n * Execute code in a database\n */\n async run(code: string | Function, databaseName: string): Promise<any> {\n let stringCode: string;\n if (typeof code === 'function') {\n const funcStr = code.toString();\n // ตัด arrow function หรือ function keyword และ opening brace ออก\n const arrowMatch = funcStr.match(/^[\\s]*\\(?\\s*\\)?\\s*=>\\s*{?/);\n const functionMatch = funcStr.match(/^[\\s]*function\\s*\\(?[\\w\\s]*\\)?\\s*{/);\n const match = arrowMatch || functionMatch;\n const start = match ? match[0].length : 0;\n const end = funcStr.lastIndexOf('}');\n stringCode = funcStr.substring(start, end);\n // Trim leading newline, spaces, and trailing\n stringCode = stringCode.replace(/^[\\s\\r\\n]+/, '').replace(/[\\s\\r\\n]+$/, '');\n\n // Transform import(aa).from(\"module\") to import aa from \"module\"\n stringCode = stringCode.replace(\n /import\\s*\\(\\s*([^)]+?)\\s*\\)\\s*\\.from\\s*\\(\\s*(['\"])([^'\"]+)\\2\\s*\\)/g,\n (_, importArg, quote, modulePath) => {\n // Check if importArg is wrapped in braces (destructuring)\n const trimmed = importArg.trim();\n if (trimmed.startsWith('{') && trimmed.endsWith('}')) {\n // Destructuring: import({bb}) -> import { bb }\n const inner = trimmed.slice(1, -1).trim();\n return `import { ${inner} } from ${quote}${modulePath}${quote}`;\n } else {\n // Default: import(aa) -> import aa\n return `import ${trimmed} from ${quote}${modulePath}${quote}`;\n }\n }\n );\n\n // Trim leading whitespace from each line\n stringCode = stringCode.split('\\n').map(line => line.trim()).join('\\n').trim();\n } else {\n stringCode = code;\n }\n\n return this.sendRequest('script-code', { code: stringCode, databaseName });\n }\n\n /**\n * Save a database to disk\n */\n async saveDatabase(databaseName: string): Promise<any> {\n return this.sendRequest('save-db', { databaseName });\n }\n\n /**\n * Update database metadata\n */\n async updateDatabase(databaseName: string, data: any): Promise<any> {\n return this.sendRequest('update-db', { databaseName, ...data });\n }\n\n /**\n * Get database content\n */\n async getDatabase(name: string): Promise<{ success: boolean; databaseName: string; content: string; path?: string }> {\n return this.sendRequest('get-db', { databaseName: name });\n }\n\n /**\n * Get server information\n */\n async getInfo(): Promise<any> {\n return this.sendRequest('get-info', {});\n }\n\n /**\n * Execute shell command on server\n */\n async executeShell(command: string): Promise<{ stdout: string; stderr: string }> {\n return this.sendRequest('shell-command', { command });\n }\n}\n\nexport default BrowserClient;\n\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -130,9 +130,9 @@ var BrowserClient = class {
|
|
|
130
130
|
console.log("Message has no id, ignoring");
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
|
-
* Send request to proxy
|
|
133
|
+
* Send request to proxy (internal helper)
|
|
134
134
|
*/
|
|
135
|
-
async
|
|
135
|
+
async _sendRequest(action, data = {}) {
|
|
136
136
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
137
137
|
throw new Error("Not connected");
|
|
138
138
|
}
|
|
@@ -190,6 +190,22 @@ var BrowserClient = class {
|
|
|
190
190
|
async disconnect() {
|
|
191
191
|
this.close();
|
|
192
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Send request helper (for public API methods)
|
|
195
|
+
*/
|
|
196
|
+
async sendRequest(action, data = {}) {
|
|
197
|
+
return this.execute({ action, data });
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Execute a command. Supports concurrent requests via request id mapping.
|
|
201
|
+
* Returns a Promise resolved with response.data or rejected on ERROR/timeout.
|
|
202
|
+
*/
|
|
203
|
+
async execute(message) {
|
|
204
|
+
if (!message.action) {
|
|
205
|
+
throw new Error("Action is required");
|
|
206
|
+
}
|
|
207
|
+
return this._sendRequest(message.action, message.data);
|
|
208
|
+
}
|
|
193
209
|
// ========== Public API Methods ==========
|
|
194
210
|
/**
|
|
195
211
|
* Login with username and password
|
|
@@ -231,7 +247,33 @@ var BrowserClient = class {
|
|
|
231
247
|
* Execute code in a database
|
|
232
248
|
*/
|
|
233
249
|
async run(code, databaseName) {
|
|
234
|
-
|
|
250
|
+
let stringCode;
|
|
251
|
+
if (typeof code === "function") {
|
|
252
|
+
const funcStr = code.toString();
|
|
253
|
+
const arrowMatch = funcStr.match(/^[\s]*\(?\s*\)?\s*=>\s*{?/);
|
|
254
|
+
const functionMatch = funcStr.match(/^[\s]*function\s*\(?[\w\s]*\)?\s*{/);
|
|
255
|
+
const match = arrowMatch || functionMatch;
|
|
256
|
+
const start = match ? match[0].length : 0;
|
|
257
|
+
const end = funcStr.lastIndexOf("}");
|
|
258
|
+
stringCode = funcStr.substring(start, end);
|
|
259
|
+
stringCode = stringCode.replace(/^[\s\r\n]+/, "").replace(/[\s\r\n]+$/, "");
|
|
260
|
+
stringCode = stringCode.replace(
|
|
261
|
+
/import\s*\(\s*([^)]+?)\s*\)\s*\.from\s*\(\s*(['"])([^'"]+)\2\s*\)/g,
|
|
262
|
+
(_, importArg, quote, modulePath) => {
|
|
263
|
+
const trimmed = importArg.trim();
|
|
264
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
265
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
266
|
+
return `import { ${inner} } from ${quote}${modulePath}${quote}`;
|
|
267
|
+
} else {
|
|
268
|
+
return `import ${trimmed} from ${quote}${modulePath}${quote}`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
stringCode = stringCode.split("\n").map((line) => line.trim()).join("\n").trim();
|
|
273
|
+
} else {
|
|
274
|
+
stringCode = code;
|
|
275
|
+
}
|
|
276
|
+
return this.sendRequest("script-code", { code: stringCode, databaseName });
|
|
235
277
|
}
|
|
236
278
|
/**
|
|
237
279
|
* Save a database to disk
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Browser-compatible ScriptDB Client\n * \n * This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.\n * The proxy handles all communication with the ScriptDB server using @scriptdb/client.\n */\n\ntype Action = 'script-code' | 'save-db' | 'remove-db' | 'rename-db' | 'login' | 'logout' | 'get-info' | 'list-dbs' | 'create-db' | 'update-db' | 'get-db' | 'shell-command';\n\ninterface Logger {\n debug?: (...args: any[]) => void;\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n}\n\ninterface ClientOptions {\n host?: string;\n port?: number;\n username?: string;\n password?: string;\n logger?: Logger;\n requestTimeout?: number;\n secure?: boolean;\n}\n\ninterface PendingRequest {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n timer: number | null;\n}\n\ninterface Message {\n id?: number;\n action?: Action;\n message?: string;\n data?: any;\n}\n\nconst noopLogger: Logger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport class BrowserClient {\n private logger: Logger;\n private host: string;\n private port: number;\n private username: string | null;\n private password: string | null;\n private requestTimeout: number;\n private secure: boolean;\n\n private ws: WebSocket | null = null;\n private _connected: boolean = false;\n private _nextId: number = 1;\n private _pending: Map<number, PendingRequest> = new Map();\n\n constructor(options: ClientOptions) {\n this.logger = options.logger || noopLogger;\n this.host = options.host || 'localhost';\n this.port = options.port || 1234;\n this.username = options.username || null;\n this.password = options.password || null;\n this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout! : 120000; // 2 minutes default\n this.secure = options.secure !== undefined ? options.secure : false;\n }\n\n /**\n * Check if connected\n */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Connect to WebSocket proxy and authenticate\n */\n async connect(): Promise<BrowserClient> {\n return new Promise((resolve, reject) => {\n const wsPort = this.port + 1;\n const wsUrl = `ws://${this.host}:${wsPort}`;\n this.logger.info?.('Connecting to', wsUrl);\n\n try {\n this.ws = new WebSocket(wsUrl);\n } catch (e) {\n const error = e as Error;\n this.logger.error?.(\"Connection failed\", error.message);\n return reject(error);\n }\n\n this.ws.onopen = async () => {\n console.log('WebSocket onopen - browserClient.ts');\n this.logger.info?.('WebSocket connected');\n this._connected = true;\n this._setupListeners();\n\n // Always send login request (even with empty credentials for anonymous access)\n try {\n console.log('Sending login request with credentials:', { username: this.username || '', hasPassword: !!this.password, secure: this.secure });\n await this.sendRequest('login', { \n username: this.username || '', \n password: this.password || '',\n secure: this.secure\n });\n console.log('Authenticated successfully');\n this.logger.info?.('Authenticated successfully');\n resolve(this);\n } catch (err) {\n console.error('Login failed:', err);\n reject(err);\n }\n };\n\n this.ws.onerror = (error) => {\n console.error('WebSocket onerror:', error);\n this.logger.error?.('WebSocket error:', error);\n reject(new Error('WebSocket connection failed'));\n };\n\n this.ws.onclose = () => {\n console.log('WebSocket onclose');\n this.logger.info?.('WebSocket disconnected');\n this._connected = false;\n this._cleanup();\n };\n });\n }\n\n /**\n * Setup message listeners\n */\n private _setupListeners() {\n if (!this.ws) return;\n\n this.ws.onmessage = (event) => {\n console.log('WebSocket onmessage fired - browserClient.ts', event.data);\n try {\n const msg = JSON.parse(event.data) as Message;\n console.log('Parsed message:', msg);\n this._handleMessage(msg);\n } catch (error) {\n console.error('Failed to parse message:', error, event.data);\n this.logger.error?.('Failed to parse message:', error);\n }\n };\n }\n\n /**\n * Handle incoming message\n */\n private _handleMessage(msg: Message) {\n console.log('_handleMessage called with:', msg);\n if (!msg || typeof msg !== \"object\") {\n console.log('Invalid message - not an object');\n return;\n }\n\n this.logger.debug?.('Received message:', msg);\n\n // Handle response with id\n if (typeof msg.id !== \"undefined\") {\n console.log('Message has id:', msg.id);\n const pending = this._pending.get(msg.id);\n console.log('Pending request found:', !!pending, 'total pending:', this._pending.size);\n if (!pending) {\n this.logger.debug?.(\"No pending request for id\", msg.id);\n console.log(\"No pending request for id\", msg.id);\n return;\n }\n\n const { resolve, reject, timer } = pending;\n if (timer) clearTimeout(timer);\n this._pending.delete(msg.id);\n\n console.log('Resolving request with message:', msg.message, 'data:', msg.data);\n\n // Check for errors\n if (msg.message === \"ERROR\" || msg.message === \"AUTH FAILED\" || msg.message === \"AUTH FAIL\") {\n return reject(new Error(typeof msg.data === 'string' ? msg.data : \"Request failed\"));\n }\n\n // Success - AUTH OK, OK, or SUCCESS\n if (msg.message === \"AUTH OK\" || msg.message === \"OK\" || msg.message === \"SUCCESS\") {\n console.log('Calling resolve with data:', msg.data);\n return resolve(msg.data);\n }\n\n // Default to resolve with data\n console.log('Default resolve with data:', msg.data);\n return resolve(msg.data);\n }\n console.log('Message has no id, ignoring');\n }\n\n /**\n * Send request to proxy\n */\n private async sendRequest(action: Action, data: any = {}): Promise<any> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n\n const id = this._nextId++;\n const request = { id, action, data };\n\n return new Promise((resolve, reject) => {\n let timer: number | null = null;\n if (this.requestTimeout > 0) {\n timer = window.setTimeout(() => {\n if (this._pending.has(id)) {\n this._pending.delete(id);\n reject(new Error(\"Request timeout\"));\n }\n }, this.requestTimeout);\n }\n\n this._pending.set(id, { resolve, reject, timer });\n\n try {\n this.ws?.send(JSON.stringify(request));\n } catch (e) {\n if (timer) clearTimeout(timer);\n this._pending.delete(id);\n reject(e);\n }\n });\n }\n\n /**\n * Cleanup on disconnect\n */\n private _cleanup() {\n this._pending.forEach((pending) => {\n if (pending.timer) clearTimeout(pending.timer);\n try {\n pending.reject(new Error(\"Disconnected\"));\n } catch (e) {}\n });\n this._pending.clear();\n }\n\n /**\n * Close connection\n */\n close() {\n if (this.ws) {\n try {\n this.ws.close();\n } catch (e) {}\n this.ws = null;\n }\n this._cleanup();\n }\n\n /**\n * Disconnect (alias for close)\n */\n async disconnect(): Promise<void> {\n this.close();\n }\n\n // ========== Public API Methods ==========\n\n /**\n * Login with username and password\n */\n async login(username: string, password: string): Promise<any> {\n return this.sendRequest('login', { username, password });\n }\n\n /**\n * Logout from server\n */\n async logout(): Promise<any> {\n return this.sendRequest('logout', {});\n }\n\n /**\n * List all databases\n */\n async listDatabases(): Promise<any> {\n return this.sendRequest('list-dbs', {});\n }\n\n /**\n * Create a new database\n */\n async createDatabase(name: string): Promise<any> {\n return this.sendRequest('create-db', { databaseName: name });\n }\n\n /**\n * Remove a database\n */\n async removeDatabase(name: string): Promise<any> {\n return this.sendRequest('remove-db', { databaseName: name });\n }\n\n /**\n * Rename a database\n */\n async renameDatabase(oldName: string, newName: string): Promise<any> {\n return this.sendRequest('rename-db', { databaseName: oldName, newName });\n }\n\n /**\n * Execute code in a database\n */\n async run(code: string, databaseName: string): Promise<any> {\n return this.sendRequest('script-code', { code, databaseName });\n }\n\n /**\n * Save a database to disk\n */\n async saveDatabase(databaseName: string): Promise<any> {\n return this.sendRequest('save-db', { databaseName });\n }\n\n /**\n * Update database metadata\n */\n async updateDatabase(databaseName: string, data: any): Promise<any> {\n return this.sendRequest('update-db', { databaseName, ...data });\n }\n\n /**\n * Get database content\n */\n async getDatabase(name: string): Promise<{ success: boolean; databaseName: string; content: string; path?: string }> {\n return this.sendRequest('get-db', { databaseName: name });\n }\n\n /**\n * Get server information\n */\n async getInfo(): Promise<any> {\n return this.sendRequest('get-info', {});\n }\n\n /**\n * Execute shell command on server\n */\n async executeShell(command: string): Promise<{ stdout: string; stderr: string }> {\n return this.sendRequest('shell-command', { command });\n }\n}\n\nexport default BrowserClient;\n\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * Browser-compatible ScriptDB Client\n * \n * This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.\n * The proxy handles all communication with the ScriptDB server using @scriptdb/client.\n */\n\ntype Action = 'script-code' | 'save-db' | 'remove-db' | 'rename-db' | 'login' | 'logout' | 'get-info' | 'list-dbs' | 'create-db' | 'update-db' | 'get-db' | 'shell-command';\n\ninterface Logger {\n debug?: (...args: any[]) => void;\n info?: (...args: any[]) => void;\n warn?: (...args: any[]) => void;\n error?: (...args: any[]) => void;\n}\n\ninterface ClientOptions {\n host?: string;\n port?: number;\n username?: string;\n password?: string;\n logger?: Logger;\n requestTimeout?: number;\n secure?: boolean;\n}\n\ninterface PendingRequest {\n resolve: (value: any) => void;\n reject: (error: Error) => void;\n timer: number | null;\n}\n\ninterface Message {\n id?: number;\n action?: Action;\n message?: string;\n data?: any;\n}\n\nconst noopLogger: Logger = {\n debug: () => { },\n info: () => { },\n warn: () => { },\n error: () => { },\n};\n\nexport class BrowserClient {\n private logger: Logger;\n private host: string;\n private port: number;\n private username: string | null;\n private password: string | null;\n private requestTimeout: number;\n private secure: boolean;\n\n private ws: WebSocket | null = null;\n private _connected: boolean = false;\n private _nextId: number = 1;\n private _pending: Map<number, PendingRequest> = new Map();\n\n constructor(options: ClientOptions) {\n this.logger = options.logger || noopLogger;\n this.host = options.host || 'localhost';\n this.port = options.port || 1234;\n this.username = options.username || null;\n this.password = options.password || null;\n this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout! : 120000; // 2 minutes default\n this.secure = options.secure !== undefined ? options.secure : false;\n }\n\n /**\n * Check if connected\n */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Connect to WebSocket proxy and authenticate\n */\n async connect(): Promise<BrowserClient> {\n return new Promise((resolve, reject) => {\n const wsPort = this.port + 1;\n const wsUrl = `ws://${this.host}:${wsPort}`;\n this.logger.info?.('Connecting to', wsUrl);\n\n try {\n this.ws = new WebSocket(wsUrl);\n } catch (e) {\n const error = e as Error;\n this.logger.error?.(\"Connection failed\", error.message);\n return reject(error);\n }\n\n this.ws.onopen = async () => {\n console.log('WebSocket onopen - browserClient.ts');\n this.logger.info?.('WebSocket connected');\n this._connected = true;\n this._setupListeners();\n\n // Always send login request (even with empty credentials for anonymous access)\n try {\n console.log('Sending login request with credentials:', { username: this.username || '', hasPassword: !!this.password, secure: this.secure });\n await this.sendRequest('login', {\n username: this.username || '',\n password: this.password || '',\n secure: this.secure\n });\n console.log('Authenticated successfully');\n this.logger.info?.('Authenticated successfully');\n resolve(this);\n } catch (err) {\n console.error('Login failed:', err);\n reject(err);\n }\n };\n\n this.ws.onerror = (error) => {\n console.error('WebSocket onerror:', error);\n this.logger.error?.('WebSocket error:', error);\n reject(new Error('WebSocket connection failed'));\n };\n\n this.ws.onclose = () => {\n console.log('WebSocket onclose');\n this.logger.info?.('WebSocket disconnected');\n this._connected = false;\n this._cleanup();\n };\n });\n }\n\n /**\n * Setup message listeners\n */\n private _setupListeners() {\n if (!this.ws) return;\n\n this.ws.onmessage = (event) => {\n console.log('WebSocket onmessage fired - browserClient.ts', event.data);\n try {\n const msg = JSON.parse(event.data) as Message;\n console.log('Parsed message:', msg);\n this._handleMessage(msg);\n } catch (error) {\n console.error('Failed to parse message:', error, event.data);\n this.logger.error?.('Failed to parse message:', error);\n }\n };\n }\n\n /**\n * Handle incoming message\n */\n private _handleMessage(msg: Message) {\n console.log('_handleMessage called with:', msg);\n if (!msg || typeof msg !== \"object\") {\n console.log('Invalid message - not an object');\n return;\n }\n\n this.logger.debug?.('Received message:', msg);\n\n // Handle response with id\n if (typeof msg.id !== \"undefined\") {\n console.log('Message has id:', msg.id);\n const pending = this._pending.get(msg.id);\n console.log('Pending request found:', !!pending, 'total pending:', this._pending.size);\n if (!pending) {\n this.logger.debug?.(\"No pending request for id\", msg.id);\n console.log(\"No pending request for id\", msg.id);\n return;\n }\n\n const { resolve, reject, timer } = pending;\n if (timer) clearTimeout(timer);\n this._pending.delete(msg.id);\n\n console.log('Resolving request with message:', msg.message, 'data:', msg.data);\n\n // Check for errors\n if (msg.message === \"ERROR\" || msg.message === \"AUTH FAILED\" || msg.message === \"AUTH FAIL\") {\n return reject(new Error(typeof msg.data === 'string' ? msg.data : \"Request failed\"));\n }\n\n // Success - AUTH OK, OK, or SUCCESS\n if (msg.message === \"AUTH OK\" || msg.message === \"OK\" || msg.message === \"SUCCESS\") {\n console.log('Calling resolve with data:', msg.data);\n return resolve(msg.data);\n }\n\n // Default to resolve with data\n console.log('Default resolve with data:', msg.data);\n return resolve(msg.data);\n }\n console.log('Message has no id, ignoring');\n }\n\n /**\n * Send request to proxy (internal helper)\n */\n private async _sendRequest(action: Action, data: any = {}): Promise<any> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected');\n }\n\n const id = this._nextId++;\n const request = { id, action, data };\n\n return new Promise((resolve, reject) => {\n let timer: number | null = null;\n if (this.requestTimeout > 0) {\n timer = window.setTimeout(() => {\n if (this._pending.has(id)) {\n this._pending.delete(id);\n reject(new Error(\"Request timeout\"));\n }\n }, this.requestTimeout);\n }\n\n this._pending.set(id, { resolve, reject, timer });\n\n try {\n this.ws?.send(JSON.stringify(request));\n } catch (e) {\n if (timer) clearTimeout(timer);\n this._pending.delete(id);\n reject(e);\n }\n });\n }\n\n /**\n * Cleanup on disconnect\n */\n private _cleanup() {\n this._pending.forEach((pending) => {\n if (pending.timer) clearTimeout(pending.timer);\n try {\n pending.reject(new Error(\"Disconnected\"));\n } catch (e) { }\n });\n this._pending.clear();\n }\n\n /**\n * Close connection\n */\n close() {\n if (this.ws) {\n try {\n this.ws.close();\n } catch (e) { }\n this.ws = null;\n }\n this._cleanup();\n }\n\n /**\n * Disconnect (alias for close)\n */\n async disconnect(): Promise<void> {\n this.close();\n }\n\n /**\n * Send request helper (for public API methods)\n */\n async sendRequest(action: Action, data: any = {}): Promise<any> {\n return this.execute({ action, data });\n }\n\n /**\n * Execute a command. Supports concurrent requests via request id mapping.\n * Returns a Promise resolved with response.data or rejected on ERROR/timeout.\n */\n async execute(message: Message): Promise<any> {\n if (!message.action) {\n throw new Error('Action is required');\n }\n return this._sendRequest(message.action, message.data);\n }\n\n // ========== Public API Methods ==========\n\n /**\n * Login with username and password\n */\n async login(username: string, password: string): Promise<any> {\n return this.sendRequest('login', { username, password });\n }\n\n /**\n * Logout from server\n */\n async logout(): Promise<any> {\n return this.sendRequest('logout', {});\n }\n\n /**\n * List all databases\n */\n async listDatabases(): Promise<any> {\n return this.sendRequest('list-dbs', {});\n }\n\n /**\n * Create a new database\n */\n async createDatabase(name: string): Promise<any> {\n return this.sendRequest('create-db', { databaseName: name });\n }\n\n /**\n * Remove a database\n */\n async removeDatabase(name: string): Promise<any> {\n return this.sendRequest('remove-db', { databaseName: name });\n }\n\n /**\n * Rename a database\n */\n async renameDatabase(oldName: string, newName: string): Promise<any> {\n return this.sendRequest('rename-db', { databaseName: oldName, newName });\n }\n\n /**\n * Execute code in a database\n */\n async run(code: string | Function, databaseName: string): Promise<any> {\n let stringCode: string;\n if (typeof code === 'function') {\n const funcStr = code.toString();\n // ตัด arrow function หรือ function keyword และ opening brace ออก\n const arrowMatch = funcStr.match(/^[\\s]*\\(?\\s*\\)?\\s*=>\\s*{?/);\n const functionMatch = funcStr.match(/^[\\s]*function\\s*\\(?[\\w\\s]*\\)?\\s*{/);\n const match = arrowMatch || functionMatch;\n const start = match ? match[0].length : 0;\n const end = funcStr.lastIndexOf('}');\n stringCode = funcStr.substring(start, end);\n // Trim leading newline, spaces, and trailing\n stringCode = stringCode.replace(/^[\\s\\r\\n]+/, '').replace(/[\\s\\r\\n]+$/, '');\n\n // Transform import(aa).from(\"module\") to import aa from \"module\"\n stringCode = stringCode.replace(\n /import\\s*\\(\\s*([^)]+?)\\s*\\)\\s*\\.from\\s*\\(\\s*(['\"])([^'\"]+)\\2\\s*\\)/g,\n (_, importArg, quote, modulePath) => {\n // Check if importArg is wrapped in braces (destructuring)\n const trimmed = importArg.trim();\n if (trimmed.startsWith('{') && trimmed.endsWith('}')) {\n // Destructuring: import({bb}) -> import { bb }\n const inner = trimmed.slice(1, -1).trim();\n return `import { ${inner} } from ${quote}${modulePath}${quote}`;\n } else {\n // Default: import(aa) -> import aa\n return `import ${trimmed} from ${quote}${modulePath}${quote}`;\n }\n }\n );\n\n // Trim leading whitespace from each line\n stringCode = stringCode.split('\\n').map(line => line.trim()).join('\\n').trim();\n } else {\n stringCode = code;\n }\n\n return this.sendRequest('script-code', { code: stringCode, databaseName });\n }\n\n /**\n * Save a database to disk\n */\n async saveDatabase(databaseName: string): Promise<any> {\n return this.sendRequest('save-db', { databaseName });\n }\n\n /**\n * Update database metadata\n */\n async updateDatabase(databaseName: string, data: any): Promise<any> {\n return this.sendRequest('update-db', { databaseName, ...data });\n }\n\n /**\n * Get database content\n */\n async getDatabase(name: string): Promise<{ success: boolean; databaseName: string; content: string; path?: string }> {\n return this.sendRequest('get-db', { databaseName: name });\n }\n\n /**\n * Get server information\n */\n async getInfo(): Promise<any> {\n return this.sendRequest('get-info', {});\n }\n\n /**\n * Execute shell command on server\n */\n async executeShell(command: string): Promise<{ stdout: string; stderr: string }> {\n return this.sendRequest('shell-command', { command });\n }\n}\n\nexport default BrowserClient;\n\n"],"mappings":"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","names":[]}
|