@scriptdb/browser-client 1.0.9 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -228,7 +228,7 @@ scriptdb shell
228
228
 
229
229
  ## Changelog
230
230
 
231
- ### 1.0.9 (2025-01-16)
231
+ ### 1.1.1 (2025-01-16)
232
232
 
233
233
  **Added**
234
234
  - Native `scriptdb logs` command to view real-time logs
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Browser-compatible ScriptDB Client
3
+ *
4
+ * This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.
5
+ * The proxy handles all communication with the ScriptDB server using @scriptdb/client.
6
+ */
7
+ interface Logger {
8
+ debug?: (...args: any[]) => void;
9
+ info?: (...args: any[]) => void;
10
+ warn?: (...args: any[]) => void;
11
+ error?: (...args: any[]) => void;
12
+ }
13
+ interface ClientOptions {
14
+ host?: string;
15
+ port?: number;
16
+ username?: string;
17
+ password?: string;
18
+ logger?: Logger;
19
+ requestTimeout?: number;
20
+ secure?: boolean;
21
+ }
22
+ declare class BrowserClient {
23
+ private logger;
24
+ private host;
25
+ private port;
26
+ private username;
27
+ private password;
28
+ private requestTimeout;
29
+ private secure;
30
+ private ws;
31
+ private _connected;
32
+ private _nextId;
33
+ private _pending;
34
+ constructor(options: ClientOptions);
35
+ /**
36
+ * Check if connected
37
+ */
38
+ get connected(): boolean;
39
+ /**
40
+ * Connect to WebSocket proxy and authenticate
41
+ */
42
+ connect(): Promise<BrowserClient>;
43
+ /**
44
+ * Setup message listeners
45
+ */
46
+ private _setupListeners;
47
+ /**
48
+ * Handle incoming message
49
+ */
50
+ private _handleMessage;
51
+ /**
52
+ * Send request to proxy
53
+ */
54
+ private sendRequest;
55
+ /**
56
+ * Cleanup on disconnect
57
+ */
58
+ private _cleanup;
59
+ /**
60
+ * Close connection
61
+ */
62
+ close(): void;
63
+ /**
64
+ * Disconnect (alias for close)
65
+ */
66
+ disconnect(): Promise<void>;
67
+ /**
68
+ * Login with username and password
69
+ */
70
+ login(username: string, password: string): Promise<any>;
71
+ /**
72
+ * Logout from server
73
+ */
74
+ logout(): Promise<any>;
75
+ /**
76
+ * List all databases
77
+ */
78
+ listDatabases(): Promise<any>;
79
+ /**
80
+ * Create a new database
81
+ */
82
+ createDatabase(name: string): Promise<any>;
83
+ /**
84
+ * Remove a database
85
+ */
86
+ removeDatabase(name: string): Promise<any>;
87
+ /**
88
+ * Rename a database
89
+ */
90
+ renameDatabase(oldName: string, newName: string): Promise<any>;
91
+ /**
92
+ * Execute code in a database
93
+ */
94
+ run(code: string, databaseName: string): Promise<any>;
95
+ /**
96
+ * Save a database to disk
97
+ */
98
+ saveDatabase(databaseName: string): Promise<any>;
99
+ /**
100
+ * Update database metadata
101
+ */
102
+ updateDatabase(databaseName: string, data: any): Promise<any>;
103
+ /**
104
+ * Get database content
105
+ */
106
+ getDatabase(name: string): Promise<{
107
+ success: boolean;
108
+ databaseName: string;
109
+ content: string;
110
+ path?: string;
111
+ }>;
112
+ /**
113
+ * Get server information
114
+ */
115
+ getInfo(): Promise<any>;
116
+ /**
117
+ * Execute shell command on server
118
+ */
119
+ executeShell(command: string): Promise<{
120
+ stdout: string;
121
+ stderr: string;
122
+ }>;
123
+ }
124
+
125
+ export { BrowserClient, BrowserClient as default };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Browser-compatible ScriptDB Client
3
+ *
4
+ * This is a lightweight WebSocket client that connects to the ScriptDB WebSocket proxy.
5
+ * The proxy handles all communication with the ScriptDB server using @scriptdb/client.
6
+ */
7
+ interface Logger {
8
+ debug?: (...args: any[]) => void;
9
+ info?: (...args: any[]) => void;
10
+ warn?: (...args: any[]) => void;
11
+ error?: (...args: any[]) => void;
12
+ }
13
+ interface ClientOptions {
14
+ host?: string;
15
+ port?: number;
16
+ username?: string;
17
+ password?: string;
18
+ logger?: Logger;
19
+ requestTimeout?: number;
20
+ secure?: boolean;
21
+ }
22
+ declare class BrowserClient {
23
+ private logger;
24
+ private host;
25
+ private port;
26
+ private username;
27
+ private password;
28
+ private requestTimeout;
29
+ private secure;
30
+ private ws;
31
+ private _connected;
32
+ private _nextId;
33
+ private _pending;
34
+ constructor(options: ClientOptions);
35
+ /**
36
+ * Check if connected
37
+ */
38
+ get connected(): boolean;
39
+ /**
40
+ * Connect to WebSocket proxy and authenticate
41
+ */
42
+ connect(): Promise<BrowserClient>;
43
+ /**
44
+ * Setup message listeners
45
+ */
46
+ private _setupListeners;
47
+ /**
48
+ * Handle incoming message
49
+ */
50
+ private _handleMessage;
51
+ /**
52
+ * Send request to proxy
53
+ */
54
+ private sendRequest;
55
+ /**
56
+ * Cleanup on disconnect
57
+ */
58
+ private _cleanup;
59
+ /**
60
+ * Close connection
61
+ */
62
+ close(): void;
63
+ /**
64
+ * Disconnect (alias for close)
65
+ */
66
+ disconnect(): Promise<void>;
67
+ /**
68
+ * Login with username and password
69
+ */
70
+ login(username: string, password: string): Promise<any>;
71
+ /**
72
+ * Logout from server
73
+ */
74
+ logout(): Promise<any>;
75
+ /**
76
+ * List all databases
77
+ */
78
+ listDatabases(): Promise<any>;
79
+ /**
80
+ * Create a new database
81
+ */
82
+ createDatabase(name: string): Promise<any>;
83
+ /**
84
+ * Remove a database
85
+ */
86
+ removeDatabase(name: string): Promise<any>;
87
+ /**
88
+ * Rename a database
89
+ */
90
+ renameDatabase(oldName: string, newName: string): Promise<any>;
91
+ /**
92
+ * Execute code in a database
93
+ */
94
+ run(code: string, databaseName: string): Promise<any>;
95
+ /**
96
+ * Save a database to disk
97
+ */
98
+ saveDatabase(databaseName: string): Promise<any>;
99
+ /**
100
+ * Update database metadata
101
+ */
102
+ updateDatabase(databaseName: string, data: any): Promise<any>;
103
+ /**
104
+ * Get database content
105
+ */
106
+ getDatabase(name: string): Promise<{
107
+ success: boolean;
108
+ databaseName: string;
109
+ content: string;
110
+ path?: string;
111
+ }>;
112
+ /**
113
+ * Get server information
114
+ */
115
+ getInfo(): Promise<any>;
116
+ /**
117
+ * Execute shell command on server
118
+ */
119
+ executeShell(command: string): Promise<{
120
+ stdout: string;
121
+ stderr: string;
122
+ }>;
123
+ }
124
+
125
+ export { BrowserClient, BrowserClient as default };
package/dist/index.js CHANGED
@@ -1,35 +1,62 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
1
20
  // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BrowserClient: () => BrowserClient,
24
+ default: () => index_default
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
2
27
  var noopLogger = {
3
- debug: () => {},
4
- info: () => {},
5
- warn: () => {},
6
- error: () => {}
28
+ debug: () => {
29
+ },
30
+ info: () => {
31
+ },
32
+ warn: () => {
33
+ },
34
+ error: () => {
35
+ }
7
36
  };
8
-
9
- class BrowserClient {
10
- logger;
11
- host;
12
- port;
13
- username;
14
- password;
15
- requestTimeout;
16
- secure;
17
- ws = null;
18
- _connected = false;
19
- _nextId = 1;
20
- _pending = new Map;
37
+ var BrowserClient = class {
21
38
  constructor(options) {
39
+ this.ws = null;
40
+ this._connected = false;
41
+ this._nextId = 1;
42
+ this._pending = /* @__PURE__ */ new Map();
22
43
  this.logger = options.logger || noopLogger;
23
44
  this.host = options.host || "localhost";
24
45
  this.port = options.port || 1234;
25
46
  this.username = options.username || null;
26
47
  this.password = options.password || null;
27
- this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout : 120000;
28
- this.secure = options.secure !== undefined ? options.secure : false;
48
+ this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout : 12e4;
49
+ this.secure = options.secure !== void 0 ? options.secure : false;
29
50
  }
51
+ /**
52
+ * Check if connected
53
+ */
30
54
  get connected() {
31
55
  return this._connected;
32
56
  }
57
+ /**
58
+ * Connect to WebSocket proxy and authenticate
59
+ */
33
60
  async connect() {
34
61
  return new Promise((resolve, reject) => {
35
62
  const wsPort = this.port + 1;
@@ -75,9 +102,11 @@ class BrowserClient {
75
102
  };
76
103
  });
77
104
  }
105
+ /**
106
+ * Setup message listeners
107
+ */
78
108
  _setupListeners() {
79
- if (!this.ws)
80
- return;
109
+ if (!this.ws) return;
81
110
  this.ws.onmessage = (event) => {
82
111
  console.log("WebSocket onmessage fired - browserClient.ts", event.data);
83
112
  try {
@@ -90,6 +119,9 @@ class BrowserClient {
90
119
  }
91
120
  };
92
121
  }
122
+ /**
123
+ * Handle incoming message
124
+ */
93
125
  _handleMessage(msg) {
94
126
  console.log("_handleMessage called with:", msg);
95
127
  if (!msg || typeof msg !== "object") {
@@ -107,8 +139,7 @@ class BrowserClient {
107
139
  return;
108
140
  }
109
141
  const { resolve, reject, timer } = pending;
110
- if (timer)
111
- clearTimeout(timer);
142
+ if (timer) clearTimeout(timer);
112
143
  this._pending.delete(msg.id);
113
144
  console.log("Resolving request with message:", msg.message, "data:", msg.data);
114
145
  if (msg.message === "ERROR" || msg.message === "AUTH FAILED" || msg.message === "AUTH FAIL") {
@@ -123,6 +154,9 @@ class BrowserClient {
123
154
  }
124
155
  console.log("Message has no id, ignoring");
125
156
  }
157
+ /**
158
+ * Send request to proxy
159
+ */
126
160
  async sendRequest(action, data = {}) {
127
161
  if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
128
162
  throw new Error("Not connected");
@@ -143,71 +177,121 @@ class BrowserClient {
143
177
  try {
144
178
  this.ws?.send(JSON.stringify(request));
145
179
  } catch (e) {
146
- if (timer)
147
- clearTimeout(timer);
180
+ if (timer) clearTimeout(timer);
148
181
  this._pending.delete(id);
149
182
  reject(e);
150
183
  }
151
184
  });
152
185
  }
186
+ /**
187
+ * Cleanup on disconnect
188
+ */
153
189
  _cleanup() {
154
190
  this._pending.forEach((pending) => {
155
- if (pending.timer)
156
- clearTimeout(pending.timer);
191
+ if (pending.timer) clearTimeout(pending.timer);
157
192
  try {
158
193
  pending.reject(new Error("Disconnected"));
159
- } catch (e) {}
194
+ } catch (e) {
195
+ }
160
196
  });
161
197
  this._pending.clear();
162
198
  }
199
+ /**
200
+ * Close connection
201
+ */
163
202
  close() {
164
203
  if (this.ws) {
165
204
  try {
166
205
  this.ws.close();
167
- } catch (e) {}
206
+ } catch (e) {
207
+ }
168
208
  this.ws = null;
169
209
  }
170
210
  this._cleanup();
171
211
  }
212
+ /**
213
+ * Disconnect (alias for close)
214
+ */
172
215
  async disconnect() {
173
216
  this.close();
174
217
  }
218
+ // ========== Public API Methods ==========
219
+ /**
220
+ * Login with username and password
221
+ */
175
222
  async login(username, password) {
176
223
  return this.sendRequest("login", { username, password });
177
224
  }
225
+ /**
226
+ * Logout from server
227
+ */
178
228
  async logout() {
179
229
  return this.sendRequest("logout", {});
180
230
  }
231
+ /**
232
+ * List all databases
233
+ */
181
234
  async listDatabases() {
182
235
  return this.sendRequest("list-dbs", {});
183
236
  }
237
+ /**
238
+ * Create a new database
239
+ */
184
240
  async createDatabase(name) {
185
241
  return this.sendRequest("create-db", { databaseName: name });
186
242
  }
243
+ /**
244
+ * Remove a database
245
+ */
187
246
  async removeDatabase(name) {
188
247
  return this.sendRequest("remove-db", { databaseName: name });
189
248
  }
249
+ /**
250
+ * Rename a database
251
+ */
190
252
  async renameDatabase(oldName, newName) {
191
253
  return this.sendRequest("rename-db", { databaseName: oldName, newName });
192
254
  }
255
+ /**
256
+ * Execute code in a database
257
+ */
193
258
  async run(code, databaseName) {
194
259
  return this.sendRequest("script-code", { code, databaseName });
195
260
  }
261
+ /**
262
+ * Save a database to disk
263
+ */
196
264
  async saveDatabase(databaseName) {
197
265
  return this.sendRequest("save-db", { databaseName });
198
266
  }
267
+ /**
268
+ * Update database metadata
269
+ */
199
270
  async updateDatabase(databaseName, data) {
200
271
  return this.sendRequest("update-db", { databaseName, ...data });
201
272
  }
273
+ /**
274
+ * Get database content
275
+ */
276
+ async getDatabase(name) {
277
+ return this.sendRequest("get-db", { databaseName: name });
278
+ }
279
+ /**
280
+ * Get server information
281
+ */
202
282
  async getInfo() {
203
283
  return this.sendRequest("get-info", {});
204
284
  }
285
+ /**
286
+ * Execute shell command on server
287
+ */
205
288
  async executeShell(command) {
206
289
  return this.sendRequest("shell-command", { command });
207
290
  }
208
- }
209
- var src_default = BrowserClient;
210
- export {
211
- src_default as default,
212
- BrowserClient
213
291
  };
292
+ var index_default = BrowserClient;
293
+ // Annotate the CommonJS export names for ESM import in node:
294
+ 0 && (module.exports = {
295
+ BrowserClient
296
+ });
297
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,272 @@
1
+ // src/index.ts
2
+ var noopLogger = {
3
+ debug: () => {
4
+ },
5
+ info: () => {
6
+ },
7
+ warn: () => {
8
+ },
9
+ error: () => {
10
+ }
11
+ };
12
+ var BrowserClient = class {
13
+ constructor(options) {
14
+ this.ws = null;
15
+ this._connected = false;
16
+ this._nextId = 1;
17
+ this._pending = /* @__PURE__ */ new Map();
18
+ this.logger = options.logger || noopLogger;
19
+ this.host = options.host || "localhost";
20
+ this.port = options.port || 1234;
21
+ this.username = options.username || null;
22
+ this.password = options.password || null;
23
+ this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout : 12e4;
24
+ this.secure = options.secure !== void 0 ? options.secure : false;
25
+ }
26
+ /**
27
+ * Check if connected
28
+ */
29
+ get connected() {
30
+ return this._connected;
31
+ }
32
+ /**
33
+ * Connect to WebSocket proxy and authenticate
34
+ */
35
+ async connect() {
36
+ return new Promise((resolve, reject) => {
37
+ const wsPort = this.port + 1;
38
+ const wsUrl = `ws://${this.host}:${wsPort}`;
39
+ this.logger.info?.("Connecting to", wsUrl);
40
+ try {
41
+ this.ws = new WebSocket(wsUrl);
42
+ } catch (e) {
43
+ const error = e;
44
+ this.logger.error?.("Connection failed", error.message);
45
+ return reject(error);
46
+ }
47
+ this.ws.onopen = async () => {
48
+ console.log("WebSocket onopen - browserClient.ts");
49
+ this.logger.info?.("WebSocket connected");
50
+ this._connected = true;
51
+ this._setupListeners();
52
+ try {
53
+ console.log("Sending login request with credentials:", { username: this.username || "", hasPassword: !!this.password, secure: this.secure });
54
+ await this.sendRequest("login", {
55
+ username: this.username || "",
56
+ password: this.password || "",
57
+ secure: this.secure
58
+ });
59
+ console.log("Authenticated successfully");
60
+ this.logger.info?.("Authenticated successfully");
61
+ resolve(this);
62
+ } catch (err) {
63
+ console.error("Login failed:", err);
64
+ reject(err);
65
+ }
66
+ };
67
+ this.ws.onerror = (error) => {
68
+ console.error("WebSocket onerror:", error);
69
+ this.logger.error?.("WebSocket error:", error);
70
+ reject(new Error("WebSocket connection failed"));
71
+ };
72
+ this.ws.onclose = () => {
73
+ console.log("WebSocket onclose");
74
+ this.logger.info?.("WebSocket disconnected");
75
+ this._connected = false;
76
+ this._cleanup();
77
+ };
78
+ });
79
+ }
80
+ /**
81
+ * Setup message listeners
82
+ */
83
+ _setupListeners() {
84
+ if (!this.ws) return;
85
+ this.ws.onmessage = (event) => {
86
+ console.log("WebSocket onmessage fired - browserClient.ts", event.data);
87
+ try {
88
+ const msg = JSON.parse(event.data);
89
+ console.log("Parsed message:", msg);
90
+ this._handleMessage(msg);
91
+ } catch (error) {
92
+ console.error("Failed to parse message:", error, event.data);
93
+ this.logger.error?.("Failed to parse message:", error);
94
+ }
95
+ };
96
+ }
97
+ /**
98
+ * Handle incoming message
99
+ */
100
+ _handleMessage(msg) {
101
+ console.log("_handleMessage called with:", msg);
102
+ if (!msg || typeof msg !== "object") {
103
+ console.log("Invalid message - not an object");
104
+ return;
105
+ }
106
+ this.logger.debug?.("Received message:", msg);
107
+ if (typeof msg.id !== "undefined") {
108
+ console.log("Message has id:", msg.id);
109
+ const pending = this._pending.get(msg.id);
110
+ console.log("Pending request found:", !!pending, "total pending:", this._pending.size);
111
+ if (!pending) {
112
+ this.logger.debug?.("No pending request for id", msg.id);
113
+ console.log("No pending request for id", msg.id);
114
+ return;
115
+ }
116
+ const { resolve, reject, timer } = pending;
117
+ if (timer) clearTimeout(timer);
118
+ this._pending.delete(msg.id);
119
+ console.log("Resolving request with message:", msg.message, "data:", msg.data);
120
+ if (msg.message === "ERROR" || msg.message === "AUTH FAILED" || msg.message === "AUTH FAIL") {
121
+ return reject(new Error(typeof msg.data === "string" ? msg.data : "Request failed"));
122
+ }
123
+ if (msg.message === "AUTH OK" || msg.message === "OK" || msg.message === "SUCCESS") {
124
+ console.log("Calling resolve with data:", msg.data);
125
+ return resolve(msg.data);
126
+ }
127
+ console.log("Default resolve with data:", msg.data);
128
+ return resolve(msg.data);
129
+ }
130
+ console.log("Message has no id, ignoring");
131
+ }
132
+ /**
133
+ * Send request to proxy
134
+ */
135
+ async sendRequest(action, data = {}) {
136
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
137
+ throw new Error("Not connected");
138
+ }
139
+ const id = this._nextId++;
140
+ const request = { id, action, data };
141
+ return new Promise((resolve, reject) => {
142
+ let timer = null;
143
+ if (this.requestTimeout > 0) {
144
+ timer = window.setTimeout(() => {
145
+ if (this._pending.has(id)) {
146
+ this._pending.delete(id);
147
+ reject(new Error("Request timeout"));
148
+ }
149
+ }, this.requestTimeout);
150
+ }
151
+ this._pending.set(id, { resolve, reject, timer });
152
+ try {
153
+ this.ws?.send(JSON.stringify(request));
154
+ } catch (e) {
155
+ if (timer) clearTimeout(timer);
156
+ this._pending.delete(id);
157
+ reject(e);
158
+ }
159
+ });
160
+ }
161
+ /**
162
+ * Cleanup on disconnect
163
+ */
164
+ _cleanup() {
165
+ this._pending.forEach((pending) => {
166
+ if (pending.timer) clearTimeout(pending.timer);
167
+ try {
168
+ pending.reject(new Error("Disconnected"));
169
+ } catch (e) {
170
+ }
171
+ });
172
+ this._pending.clear();
173
+ }
174
+ /**
175
+ * Close connection
176
+ */
177
+ close() {
178
+ if (this.ws) {
179
+ try {
180
+ this.ws.close();
181
+ } catch (e) {
182
+ }
183
+ this.ws = null;
184
+ }
185
+ this._cleanup();
186
+ }
187
+ /**
188
+ * Disconnect (alias for close)
189
+ */
190
+ async disconnect() {
191
+ this.close();
192
+ }
193
+ // ========== Public API Methods ==========
194
+ /**
195
+ * Login with username and password
196
+ */
197
+ async login(username, password) {
198
+ return this.sendRequest("login", { username, password });
199
+ }
200
+ /**
201
+ * Logout from server
202
+ */
203
+ async logout() {
204
+ return this.sendRequest("logout", {});
205
+ }
206
+ /**
207
+ * List all databases
208
+ */
209
+ async listDatabases() {
210
+ return this.sendRequest("list-dbs", {});
211
+ }
212
+ /**
213
+ * Create a new database
214
+ */
215
+ async createDatabase(name) {
216
+ return this.sendRequest("create-db", { databaseName: name });
217
+ }
218
+ /**
219
+ * Remove a database
220
+ */
221
+ async removeDatabase(name) {
222
+ return this.sendRequest("remove-db", { databaseName: name });
223
+ }
224
+ /**
225
+ * Rename a database
226
+ */
227
+ async renameDatabase(oldName, newName) {
228
+ return this.sendRequest("rename-db", { databaseName: oldName, newName });
229
+ }
230
+ /**
231
+ * Execute code in a database
232
+ */
233
+ async run(code, databaseName) {
234
+ return this.sendRequest("script-code", { code, databaseName });
235
+ }
236
+ /**
237
+ * Save a database to disk
238
+ */
239
+ async saveDatabase(databaseName) {
240
+ return this.sendRequest("save-db", { databaseName });
241
+ }
242
+ /**
243
+ * Update database metadata
244
+ */
245
+ async updateDatabase(databaseName, data) {
246
+ return this.sendRequest("update-db", { databaseName, ...data });
247
+ }
248
+ /**
249
+ * Get database content
250
+ */
251
+ async getDatabase(name) {
252
+ return this.sendRequest("get-db", { databaseName: name });
253
+ }
254
+ /**
255
+ * Get server information
256
+ */
257
+ async getInfo() {
258
+ return this.sendRequest("get-info", {});
259
+ }
260
+ /**
261
+ * Execute shell command on server
262
+ */
263
+ async executeShell(command) {
264
+ return this.sendRequest("shell-command", { command });
265
+ }
266
+ };
267
+ var index_default = BrowserClient;
268
+ export {
269
+ BrowserClient,
270
+ index_default as default
271
+ };
272
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scriptdb/browser-client",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "Browser WebSocket client for ScriptDB",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -17,10 +17,7 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "dev": "bun --watch src/index.ts",
20
- "build": "bun build src/index.ts --outdir dist --target browser --format esm --splitting",
21
- "build:cjs": "bun build src/index.ts --outdir dist --target browser --format cjs --outfile dist/index.js",
22
- "build:types": "tsc --emitDeclarationOnly",
23
- "build:all": "bun run build && bun run build:cjs && bun run build:types",
20
+ "build": "tsup",
24
21
  "typecheck": "tsc --noEmit",
25
22
  "clean": "rm -rf dist"
26
23
  },