@scriptdb/browser-client 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +48 -0
  2. package/dist/index.js +70 -458
  3. package/package.json +7 -25
package/README.md CHANGED
@@ -193,6 +193,54 @@ const client = new ScriptDBClient('scriptdb://localhost:1234', options);
193
193
  await client.connect();
194
194
  ```
195
195
 
196
+ ## CLI Tool
197
+
198
+ ScriptDB also provides a CLI tool for server management:
199
+
200
+ ```bash
201
+ # Install CLI globally
202
+ npm install -g @scriptdb/cli
203
+
204
+ # Start server
205
+ scriptdb start
206
+
207
+ # Start server in background (daemon mode with PM2)
208
+ scriptdb start -d
209
+
210
+ # Check server status
211
+ scriptdb status
212
+
213
+ # View real-time logs
214
+ scriptdb logs
215
+
216
+ # Monitor performance
217
+ scriptdb monit
218
+
219
+ # Stop server
220
+ scriptdb stop
221
+
222
+ # Restart server
223
+ scriptdb restart -d
224
+
225
+ # Start interactive shell
226
+ scriptdb shell
227
+ ```
228
+
229
+ ## Changelog
230
+
231
+ ### 1.1.0 (2025-01-16)
232
+
233
+ **Added**
234
+ - Native `scriptdb logs` command to view real-time logs
235
+ - Native `scriptdb monit` command to monitor performance
236
+ - Native `scriptdb restart` command to restart the server
237
+ - Native `scriptdb stop` command to stop the server
238
+ - ESLint configuration for TypeScript linting
239
+
240
+ **Fixed**
241
+ - Fixed TypeScript module resolution errors
242
+ - Improved error handling and Windows compatibility
243
+
196
244
  ## License
197
245
 
198
246
  MIT
package/dist/index.js CHANGED
@@ -5,417 +5,131 @@ var noopLogger = {
5
5
  warn: () => {},
6
6
  error: () => {}
7
7
  };
8
- async function createHmacSignature(message, secret, algorithm = "SHA-256") {
9
- const encoder = new TextEncoder;
10
- const keyData = encoder.encode(secret);
11
- const messageData = encoder.encode(message);
12
- const algoMap = {
13
- sha256: "SHA-256",
14
- sha384: "SHA-384",
15
- sha512: "SHA-512",
16
- "SHA-256": "SHA-256",
17
- "SHA-384": "SHA-384",
18
- "SHA-512": "SHA-512"
19
- };
20
- const cryptoAlgo = algoMap[algorithm] || "SHA-256";
21
- const key = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: cryptoAlgo }, false, ["sign"]);
22
- const signature = await crypto.subtle.sign("HMAC", key, messageData);
23
- return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
24
- }
25
8
 
26
9
  class BrowserClient {
27
- options;
28
10
  logger;
29
- secure;
11
+ host;
12
+ port;
13
+ username;
14
+ password;
30
15
  requestTimeout;
31
- retries;
32
- retryDelay;
33
- maxMessageSize;
34
- uri = "";
35
- protocolName = "";
36
- username = null;
37
- password = null;
38
- host = "";
39
- port = 0;
40
- database = null;
16
+ secure;
41
17
  ws = null;
18
+ _connected = false;
42
19
  _nextId = 1;
43
20
  _pending = new Map;
44
- _maxPending = 100;
45
- _maxQueue = 1000;
46
- _pendingQueue = [];
47
- _connected = false;
48
- _authenticating = false;
49
- token = null;
50
- _currentRetries = 0;
51
- tokenExpiry = null;
52
- _destroyed = false;
53
- _reconnectTimer = null;
54
- _authPendingId = null;
55
- signing = null;
56
- _stringify = JSON.stringify;
57
- ready = Promise.resolve();
58
- _resolveReadyFn = null;
59
- _rejectReadyFn = null;
60
- _connecting = null;
61
- constructor(uri, options = {}) {
62
- if (!uri || typeof uri !== "string")
63
- throw new Error("uri required");
64
- this.options = { ...options };
65
- this.maxMessageSize = Number.isFinite(this.options.maxMessageSize) ? this.options.maxMessageSize : 5 * 1024 * 1024;
66
- const rawLogger = this.options.logger || noopLogger;
67
- this.logger = {
68
- debug: (...args) => rawLogger.debug?.(...this._maskArgs(args)),
69
- info: (...args) => rawLogger.info?.(...this._maskArgs(args)),
70
- warn: (...args) => rawLogger.warn?.(...this._maskArgs(args)),
71
- error: (...args) => rawLogger.error?.(...this._maskArgs(args))
72
- };
73
- this.secure = typeof this.options.secure === "boolean" ? this.options.secure : true;
74
- if (!this.secure) {
75
- this.logger.warn?.("Warning: connecting in insecure mode (secure=false). This is not recommended.");
76
- }
77
- this.requestTimeout = Number.isFinite(this.options.requestTimeout) ? this.options.requestTimeout : 0;
78
- this.retries = Number.isFinite(this.options.retries) ? this.options.retries : 3;
79
- this.retryDelay = Number.isFinite(this.options.retryDelay) ? this.options.retryDelay : 1000;
80
- let parsed;
81
- try {
82
- parsed = new URL(uri);
83
- } catch (e) {
84
- throw new Error("Invalid uri");
85
- }
86
- if (parsed.protocol !== "scriptdb:") {
87
- throw new Error("URI must use scriptdb:// protocol");
88
- }
89
- if (parsed.protocol !== "scriptdb:") {
90
- throw new Error("URI must use scriptdb:// protocol");
91
- }
92
- this.uri = uri;
93
- this.protocolName = parsed.protocol ? parsed.protocol.replace(":", "") : "scriptdb";
94
- this.username = (typeof this.options.username === "string" ? this.options.username : decodeURIComponent(parsed.username)) || null;
95
- this.password = (typeof this.options.password === "string" ? this.options.password : decodeURIComponent(parsed.password)) || null;
96
- if (parsed.username && typeof this.options.username !== "string") {
97
- this.logger.warn?.("Credentials found in URI — consider passing credentials via options instead of embedding in URI");
98
- }
99
- try {
100
- parsed.username = "";
101
- parsed.password = "";
102
- this.uri = parsed.toString();
103
- } catch (e) {
104
- this.uri = uri;
105
- }
106
- this.host = parsed.hostname || "localhost";
107
- this.port = parsed.port ? parseInt(parsed.port, 10) : 1234;
108
- this.database = parsed.pathname && parsed.pathname.length > 1 ? parsed.pathname.slice(1) : null;
109
- this._maxPending = Number.isFinite(this.options.maxPending) ? this.options.maxPending : 100;
110
- this._maxQueue = Number.isFinite(this.options.maxQueue) ? this.options.maxQueue : 1000;
111
- this.signing = this.options.signing && this.options.signing.secret ? this.options.signing : null;
112
- this._stringify = typeof this.options.stringify === "function" ? this.options.stringify : JSON.stringify;
113
- this._createReady();
114
- }
115
- _mask(obj) {
116
- try {
117
- if (!obj || typeof obj !== "object")
118
- return obj;
119
- const copy = Array.isArray(obj) ? [...obj] : { ...obj };
120
- if (copy.token)
121
- copy.token = "****";
122
- if (copy.password)
123
- copy.password = "****";
124
- if (copy.data?.password)
125
- copy.data.password = "****";
126
- return copy;
127
- } catch (e) {
128
- return obj;
129
- }
130
- }
131
- _maskArgs(args) {
132
- return args.map((a) => {
133
- if (!a || typeof a === "string")
134
- return a;
135
- return this._mask(a);
136
- });
137
- }
138
- _createReady() {
139
- this.ready = new Promise((resolve, reject) => {
140
- this._resolveReadyFn = resolve;
141
- this._rejectReadyFn = reject;
142
- });
143
- }
144
- _resolveReady(value) {
145
- if (this._resolveReadyFn) {
146
- try {
147
- this._resolveReadyFn(value);
148
- } catch (e) {}
149
- this._resolveReadyFn = null;
150
- this._rejectReadyFn = null;
151
- }
152
- }
153
- _rejectReady(err) {
154
- if (this._rejectReadyFn) {
155
- const rejectFn = this._rejectReadyFn;
156
- this._resolveReadyFn = null;
157
- this._rejectReadyFn = null;
158
- setTimeout(() => {
159
- try {
160
- rejectFn(err);
161
- } catch (e) {}
162
- }, 0);
163
- }
21
+ constructor(options) {
22
+ this.logger = options.logger || noopLogger;
23
+ this.host = options.host || "localhost";
24
+ this.port = options.port || 1234;
25
+ this.username = options.username || null;
26
+ this.password = options.password || null;
27
+ this.requestTimeout = Number.isFinite(options.requestTimeout) ? options.requestTimeout : 120000;
28
+ this.secure = options.secure !== undefined ? options.secure : false;
164
29
  }
165
30
  get connected() {
166
31
  return this._connected;
167
32
  }
168
- connect() {
169
- if (this._connecting)
170
- return this._connecting;
171
- this._connecting = new Promise((resolve, reject) => {
33
+ async connect() {
34
+ return new Promise((resolve, reject) => {
172
35
  const wsPort = this.port + 1;
173
- const wsProtocol = this.secure ? "wss" : "ws";
174
- const wsUrl = `${wsProtocol}://${this.host}:${wsPort}`;
36
+ const wsUrl = `ws://${this.host}:${wsPort}`;
175
37
  this.logger.info?.("Connecting to", wsUrl);
176
38
  try {
177
39
  this.ws = new WebSocket(wsUrl);
178
40
  } catch (e) {
179
41
  const error = e;
180
42
  this.logger.error?.("Connection failed", error.message);
181
- this._rejectReady(error);
182
- this._createReady();
183
- this._connecting = null;
184
43
  return reject(error);
185
44
  }
186
- this.ws.onopen = () => {
187
- this.logger.info?.("Connected to server");
45
+ this.ws.onopen = async () => {
46
+ console.log("WebSocket onopen - browserClient.ts");
47
+ this.logger.info?.("WebSocket connected");
188
48
  this._connected = true;
189
- this._currentRetries = 0;
190
49
  this._setupListeners();
191
- this.authenticate().then(() => {
192
- this._processQueue();
50
+ try {
51
+ console.log("Sending login request with credentials:", { username: this.username || "", hasPassword: !!this.password, secure: this.secure });
52
+ await this.sendRequest("login", {
53
+ username: this.username || "",
54
+ password: this.password || "",
55
+ secure: this.secure
56
+ });
57
+ console.log("Authenticated successfully");
58
+ this.logger.info?.("Authenticated successfully");
193
59
  resolve(this);
194
- }).catch((err) => {
60
+ } catch (err) {
61
+ console.error("Login failed:", err);
195
62
  reject(err);
196
- });
63
+ }
197
64
  };
198
- this.ws.onerror = (event) => {
199
- this.logger.error?.("WebSocket error:", event);
200
- this._handleDisconnect(new Error("WebSocket error"));
65
+ this.ws.onerror = (error) => {
66
+ console.error("WebSocket onerror:", error);
67
+ this.logger.error?.("WebSocket error:", error);
68
+ reject(new Error("WebSocket connection failed"));
201
69
  };
202
- this.ws.onclose = (event) => {
203
- this.logger.info?.("WebSocket closed", event.code, event.reason);
204
- this._handleDisconnect(null);
70
+ this.ws.onclose = () => {
71
+ console.log("WebSocket onclose");
72
+ this.logger.info?.("WebSocket disconnected");
73
+ this._connected = false;
74
+ this._cleanup();
205
75
  };
206
- }).catch((err) => {
207
- this._connecting = null;
208
- throw err;
209
76
  });
210
- return this._connecting;
211
77
  }
212
78
  _setupListeners() {
213
79
  if (!this.ws)
214
80
  return;
215
81
  this.ws.onmessage = (event) => {
216
- if (typeof event.data === "string" && event.data.length > this.maxMessageSize) {
217
- this.logger.error?.("Incoming message exceeds maxMessageSize — ignoring");
218
- return;
219
- }
82
+ console.log("WebSocket onmessage fired - browserClient.ts", event.data);
220
83
  try {
221
84
  const msg = JSON.parse(event.data);
222
- if (!msg || typeof msg !== "object")
223
- return;
224
- const validSchema = (typeof msg.id === "undefined" || typeof msg.id === "number") && (typeof msg.action === "string" || typeof msg.action === "undefined") && (typeof msg.command === "string" || typeof msg.command === "undefined") && (typeof msg.message === "string" || typeof msg.message === "undefined") && (typeof msg.data === "object" || typeof msg.data === "undefined" || msg.data === null);
225
- if (!validSchema) {
226
- this.logger.warn?.("Message failed schema validation — ignoring");
227
- return;
228
- }
85
+ console.log("Parsed message:", msg);
229
86
  this._handleMessage(msg);
230
87
  } catch (error) {
88
+ console.error("Failed to parse message:", error, event.data);
231
89
  this.logger.error?.("Failed to parse message:", error);
232
90
  }
233
91
  };
234
92
  }
235
93
  _handleMessage(msg) {
236
- if (!msg || typeof msg !== "object")
94
+ console.log("_handleMessage called with:", msg);
95
+ if (!msg || typeof msg !== "object") {
96
+ console.log("Invalid message - not an object");
237
97
  return;
98
+ }
238
99
  this.logger.debug?.("Received message:", msg);
239
100
  if (typeof msg.id !== "undefined") {
101
+ console.log("Message has id:", msg.id);
240
102
  const pending = this._pending.get(msg.id);
103
+ console.log("Pending request found:", !!pending, "total pending:", this._pending.size);
241
104
  if (!pending) {
242
105
  this.logger.debug?.("No pending request for id", msg.id);
106
+ console.log("No pending request for id", msg.id);
243
107
  return;
244
108
  }
245
109
  const { resolve, reject, timer } = pending;
246
110
  if (timer)
247
111
  clearTimeout(timer);
248
112
  this._pending.delete(msg.id);
249
- this._processQueue();
250
- if (msg.action === "login" || msg.command === "login") {
251
- if (msg.message === "AUTH OK") {
252
- this.token = msg.data?.token || null;
253
- this._resolveReady(null);
254
- return resolve(msg.data);
255
- } else {
256
- this._rejectReady(new Error("Authentication failed"));
257
- const errorMsg = msg.data || "Authentication failed";
258
- try {
259
- this.ws?.close();
260
- } catch (e) {}
261
- return reject(new Error(typeof errorMsg === "string" ? errorMsg : "Authentication failed"));
262
- }
263
- }
264
- if ((msg.action === "script-code" || msg.command === "script-code") && msg.message === "OK") {
265
- return resolve(msg.data);
266
- }
267
- if ((msg.action === "script-code" || msg.command === "script-code") && msg.message === "ERROR") {
268
- return reject(new Error(typeof msg.data === "string" ? msg.data : "Server returned ERROR"));
269
- }
270
- if (msg.action === "create-db" && msg.message === "SUCCESS") {
271
- return resolve(msg.data);
272
- }
273
- if (msg.action === "create-db" && msg.message === "ERROR") {
274
- return reject(new Error(typeof msg.data === "string" ? msg.data : "Failed to create database"));
275
- }
276
- if (msg.message === "OK" || msg.message === "SUCCESS") {
277
- return resolve(msg.data);
278
- } else if (msg.message === "ERROR" || msg.message === "AUTH FAIL" || msg.message === "AUTH FAILED") {
113
+ console.log("Resolving request with message:", msg.message, "data:", msg.data);
114
+ if (msg.message === "ERROR" || msg.message === "AUTH FAILED" || msg.message === "AUTH FAIL") {
279
115
  return reject(new Error(typeof msg.data === "string" ? msg.data : "Request failed"));
280
116
  }
281
- return reject(new Error("Invalid response from server"));
282
- }
283
- this.logger.debug?.("Unhandled message from server", this._mask(msg));
284
- }
285
- async _buildFinalMessage(payloadBase, id) {
286
- const payloadObj = {
287
- id,
288
- action: payloadBase.action,
289
- ...payloadBase.data !== undefined ? { data: payloadBase.data } : {}
290
- };
291
- if (this.token)
292
- payloadObj.token = this.token;
293
- const payloadStr = this._stringify(payloadObj);
294
- if (this.signing && this.signing.secret) {
295
- const sig = await createHmacSignature(payloadStr, this.signing.secret, this.signing.algorithm || "sha256");
296
- const envelope = { id, signature: sig, payload: payloadObj };
297
- return this._stringify(envelope);
298
- }
299
- return payloadStr;
300
- }
301
- authenticate() {
302
- if (this._authenticating) {
303
- return Promise.reject(new Error("Already authenticating"));
304
- }
305
- this._authenticating = true;
306
- return new Promise((resolve, reject) => {
307
- const id = this._nextId++;
308
- this._authPendingId = id;
309
- const payload = {
310
- id,
311
- action: "login",
312
- data: { username: this.username, password: this.password }
313
- };
314
- let timer = null;
315
- if (this.requestTimeout > 0) {
316
- timer = window.setTimeout(() => {
317
- this._pending.delete(id);
318
- this._authenticating = false;
319
- this._authPendingId = null;
320
- reject(new Error("Auth timeout"));
321
- this.close();
322
- }, this.requestTimeout);
323
- }
324
- this._pending.set(id, {
325
- resolve: (data) => {
326
- if (timer)
327
- clearTimeout(timer);
328
- this._authenticating = false;
329
- this._authPendingId = null;
330
- if (data?.token)
331
- this.token = data.token;
332
- resolve(data);
333
- },
334
- reject: (err) => {
335
- if (timer)
336
- clearTimeout(timer);
337
- this._authenticating = false;
338
- this._authPendingId = null;
339
- reject(err);
340
- },
341
- timer
342
- });
343
- try {
344
- this.ws?.send(JSON.stringify(payload));
345
- } catch (e) {
346
- if (timer)
347
- clearTimeout(timer);
348
- this._pending.delete(id);
349
- this._authenticating = false;
350
- this._authPendingId = null;
351
- reject(e);
352
- }
353
- }).then((data) => {
354
- if (data?.token) {
355
- this.token = data.token;
356
- this._resolveReady(null);
357
- }
358
- return data;
359
- }).catch((err) => {
360
- this._rejectReady(err);
361
- throw err;
362
- });
363
- }
364
- async _processQueue() {
365
- while (this._pending.size < this._maxPending && this._pendingQueue.length > 0) {
366
- const item = this._pendingQueue.shift();
367
- const { payloadBase, id, resolve, reject, timer } = item;
368
- if (this.tokenExpiry && Date.now() >= this.tokenExpiry) {
369
- const refreshed = await this._maybeRefreshToken();
370
- if (!refreshed) {
371
- if (timer)
372
- clearTimeout(timer);
373
- try {
374
- reject(new Error("Token expired and refresh failed"));
375
- } catch (e) {}
376
- continue;
377
- }
378
- }
379
- let messageStr;
380
- try {
381
- messageStr = await this._buildFinalMessage(payloadBase, id);
382
- } catch (e) {
383
- if (timer)
384
- clearTimeout(timer);
385
- try {
386
- reject(e);
387
- } catch (er) {}
388
- continue;
389
- }
390
- this._pending.set(id, { resolve, reject, timer });
391
- try {
392
- this.ws?.send(messageStr);
393
- } catch (e) {
394
- if (timer)
395
- clearTimeout(timer);
396
- this._pending.delete(id);
397
- try {
398
- reject(e);
399
- } catch (er) {}
117
+ if (msg.message === "AUTH OK" || msg.message === "OK" || msg.message === "SUCCESS") {
118
+ console.log("Calling resolve with data:", msg.data);
119
+ return resolve(msg.data);
400
120
  }
121
+ console.log("Default resolve with data:", msg.data);
122
+ return resolve(msg.data);
401
123
  }
124
+ console.log("Message has no id, ignoring");
402
125
  }
403
- async execute(payload) {
404
- try {
405
- await this.ready;
406
- } catch (err) {
407
- throw new Error("Not authenticated: " + (err?.message || err));
126
+ async sendRequest(action, data = {}) {
127
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
128
+ throw new Error("Not connected");
408
129
  }
409
- if (!this.token)
410
- throw new Error("Not authenticated");
411
130
  const id = this._nextId++;
412
- const payloadBase = { action: payload.action, data: payload.data };
413
- if (this.tokenExpiry && Date.now() >= this.tokenExpiry) {
414
- const refreshed = await this._maybeRefreshToken();
415
- if (!refreshed)
416
- throw new Error("Token expired");
417
- }
418
- return new Promise(async (resolve, reject) => {
131
+ const request = { id, action, data };
132
+ return new Promise((resolve, reject) => {
419
133
  let timer = null;
420
134
  if (this.requestTimeout > 0) {
421
135
  timer = window.setTimeout(() => {
@@ -425,27 +139,9 @@ class BrowserClient {
425
139
  }
426
140
  }, this.requestTimeout);
427
141
  }
428
- if (this._pending.size >= this._maxPending) {
429
- if (this._pendingQueue.length >= this._maxQueue) {
430
- if (timer)
431
- clearTimeout(timer);
432
- return reject(new Error("Pending queue full"));
433
- }
434
- this._pendingQueue.push({ payloadBase, id, resolve, reject, timer });
435
- this._processQueue().catch(() => {});
436
- return;
437
- }
438
- let messageStr;
439
- try {
440
- messageStr = await this._buildFinalMessage(payloadBase, id);
441
- } catch (e) {
442
- if (timer)
443
- clearTimeout(timer);
444
- return reject(e);
445
- }
446
142
  this._pending.set(id, { resolve, reject, timer });
447
143
  try {
448
- this.ws?.send(messageStr);
144
+ this.ws?.send(JSON.stringify(request));
449
145
  } catch (e) {
450
146
  if (timer)
451
147
  clearTimeout(timer);
@@ -454,69 +150,15 @@ class BrowserClient {
454
150
  }
455
151
  });
456
152
  }
457
- async sendRequest(action, data = {}) {
458
- return this.execute({ action, data });
459
- }
460
- async _maybeRefreshToken() {
461
- if (!this.options.tokenRefresh || typeof this.options.tokenRefresh !== "function") {
462
- return false;
463
- }
464
- try {
465
- const res = await this.options.tokenRefresh();
466
- if (res?.token) {
467
- this.token = res.token;
468
- if (res.expiresAt)
469
- this.tokenExpiry = res.expiresAt;
470
- return true;
471
- }
472
- } catch (e) {
473
- this.logger.error?.("Token refresh failed", e?.message || String(e));
474
- }
475
- return false;
476
- }
477
- _handleDisconnect(err) {
478
- if (!this._connected && !this._authenticating)
479
- return;
480
- const wasAuthenticating = this._authenticating;
481
- this._pending.forEach((pending, id) => {
153
+ _cleanup() {
154
+ this._pending.forEach((pending) => {
482
155
  if (pending.timer)
483
156
  clearTimeout(pending.timer);
484
- const errorMsg = wasAuthenticating ? "Authentication failed - credentials may be required" : err?.message || "Disconnected";
485
- setTimeout(() => {
486
- try {
487
- pending.reject(new Error(errorMsg));
488
- } catch (e) {}
489
- }, 0);
490
- this._pending.delete(id);
491
- });
492
- this._connected = false;
493
- this._connecting = null;
494
- this._authenticating = false;
495
- if (wasAuthenticating && this.retries === 0) {
496
- const rejectErr = err || new Error("Authentication failed and no retries configured");
497
- try {
498
- this._rejectReady(rejectErr);
499
- } catch (e) {}
500
- return;
501
- }
502
- if (this._currentRetries < this.retries && !this._destroyed) {
503
- this._createReady();
504
- const base = Math.min(this.retryDelay * Math.pow(2, this._currentRetries), 30000);
505
- const jitter = Math.floor(Math.random() * Math.min(1000, base));
506
- const delay = base + jitter;
507
- this._currentRetries += 1;
508
- this.logger.info?.(`Attempting reconnect in ${delay}ms (attempt ${this._currentRetries})`);
509
- this.token = null;
510
- this._reconnectTimer = window.setTimeout(() => {
511
- this.connect().catch((e) => {
512
- this.logger.error?.("Reconnect failed", e?.message || e);
513
- });
514
- }, delay);
515
- } else {
516
157
  try {
517
- this._rejectReady(err || new Error("Disconnected and no retries left"));
158
+ pending.reject(new Error("Disconnected"));
518
159
  } catch (e) {}
519
- }
160
+ });
161
+ this._pending.clear();
520
162
  }
521
163
  close() {
522
164
  if (this.ws) {
@@ -525,37 +167,8 @@ class BrowserClient {
525
167
  } catch (e) {}
526
168
  this.ws = null;
527
169
  }
528
- this._connected = false;
529
170
  this._cleanup();
530
171
  }
531
- destroy() {
532
- this._destroyed = true;
533
- if (this._reconnectTimer) {
534
- clearTimeout(this._reconnectTimer);
535
- this._reconnectTimer = null;
536
- }
537
- this._rejectReady(new Error("Client destroyed"));
538
- this._pending.forEach((pending, id) => {
539
- if (pending.timer)
540
- clearTimeout(pending.timer);
541
- try {
542
- pending.reject(new Error("Client destroyed"));
543
- } catch (e) {}
544
- this._pending.delete(id);
545
- });
546
- this._pendingQueue = [];
547
- this.close();
548
- }
549
- _cleanup() {
550
- this._pending.forEach((pending) => {
551
- if (pending.timer)
552
- clearTimeout(pending.timer);
553
- try {
554
- pending.reject(new Error("Disconnected"));
555
- } catch (e) {}
556
- });
557
- this._pending.clear();
558
- }
559
172
  async disconnect() {
560
173
  this.close();
561
174
  }
@@ -596,6 +209,5 @@ class BrowserClient {
596
209
  var src_default = BrowserClient;
597
210
  export {
598
211
  src_default as default,
599
- BrowserClient as ScriptDBClient,
600
212
  BrowserClient
601
213
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@scriptdb/browser-client",
3
- "version": "1.0.8",
4
- "description": "Client module resolver for script database",
3
+ "version": "1.1.0",
4
+ "description": "Browser WebSocket client for ScriptDB",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -17,33 +17,15 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "dev": "bun --watch src/index.ts",
20
- "build": "bun build src/index.ts --outdir dist --target node --format esm --splitting --external bcryptjs --external bottleneck --external jsonwebtoken --external lru-cache",
21
- "build:cjs": "bun build src/index.ts --outdir dist --target node --format cjs --outfile dist/index.js --external bcryptjs --external bottleneck --external jsonwebtoken --external lru-cache",
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
22
  "build:types": "tsc --emitDeclarationOnly",
23
23
  "build:all": "bun run build && bun run build:cjs && bun run build:types",
24
- "test": "bun test",
25
- "lint": "bun run lint:src",
26
- "lint:src": "eslint src --ext .ts,.tsx",
27
- "lint:fix": "eslint src --ext .ts,.tsx --fix",
28
24
  "typecheck": "tsc --noEmit",
29
25
  "clean": "rm -rf dist"
30
26
  },
31
27
  "devDependencies": {
32
- "@types/bcryptjs": "^3.0.0",
33
- "@types/bun": "^1.3.2",
34
- "@types/jsonwebtoken": "^9.0.10",
35
- "@types/lru-cache": "^7.10.10",
36
- "@types/node": "^24.10.1",
37
- "@typescript-eslint/eslint-plugin": "^6.0.0",
38
- "@typescript-eslint/parser": "^6.0.0",
39
- "bun-types": "latest",
40
- "eslint": "^8.0.0",
41
- "typescript": "^5.0.0"
42
- },
43
- "dependencies": {
44
- "bcryptjs": "^3.0.3",
45
- "bottleneck": "^2.19.5",
46
- "jsonwebtoken": "^9.0.2",
47
- "lru-cache": "^11.2.2"
28
+ "@types/node": "^20.0.0",
29
+ "typescript": "^5.3.3"
48
30
  }
49
- }
31
+ }