@cnstra/devtools-transport-ws 1.2.0 → 2.0.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/dist/index.cjs CHANGED
@@ -20,12 +20,6 @@ var CNSDevToolsTransportWs = class {
20
20
  this.pendingBatches.push(message);
21
21
  await this.flush();
22
22
  }
23
- onReplayStart(handler) {
24
- this.onReplayHandler = handler;
25
- return () => {
26
- this.onReplayHandler = void 0;
27
- };
28
- }
29
23
  onStimulationCommand(handler) {
30
24
  this.onCommandHandler = handler;
31
25
  return () => {
@@ -38,7 +32,7 @@ var CNSDevToolsTransportWs = class {
38
32
  if (!impl) throw new Error("No WebSocket implementation. Pass webSocketImpl option.");
39
33
  return impl;
40
34
  }
41
- ensureSocket() {
35
+ openSocket() {
42
36
  if (this.ws?.readyState === 1) return Promise.resolve();
43
37
  if (this.connecting) {
44
38
  return new Promise((resolve) => {
@@ -67,7 +61,7 @@ var CNSDevToolsTransportWs = class {
67
61
  if (this.opts.consoleLogEnabled) {
68
62
  console.log(`[DevTools] Disconnected, reconnecting (attempt ${this.reconnectAttempts})`);
69
63
  }
70
- setTimeout(() => this.ensureSocket().catch(() => {
64
+ setTimeout(() => this.openSocket().catch(() => {
71
65
  }), this.opts.reconnectDelayMs ?? 1e3);
72
66
  } else if (this.reconnectAttempts >= (this.opts.maxReconnectAttempts ?? Infinity)) {
73
67
  if (this.opts.consoleLogEnabled) console.log("[DevTools] Max reconnect attempts reached");
@@ -82,15 +76,8 @@ var CNSDevToolsTransportWs = class {
82
76
  ws.onmessage = (ev) => {
83
77
  try {
84
78
  const msg = JSON.parse(typeof ev.data === "string" ? ev.data : String(ev.data));
85
- if (msg?.type === "replay.start") {
86
- const result = devtoolsDto.CNSDTOReplayStartMessageSchema.safeParse(msg);
87
- if (result.success) {
88
- this.onReplayHandler?.(result.data);
89
- } else if (this.opts.consoleLogEnabled) {
90
- console.warn("[DevTools] Invalid replay.start message:", result.error.message);
91
- }
92
- } else if (msg?.type === "cns.stimulation.resume" || msg?.type === "cns.stimulation.launch") {
93
- const result = devtoolsDto.CNSDTOAppCommandSchema.safeParse(msg);
79
+ if (msg?.type === "cns.stimulation.resume" || msg?.type === "cns.stimulation.launch") {
80
+ const result = devtoolsDto.CNSAppCommandDTOSchema.safeParse(msg);
94
81
  if (result.success) {
95
82
  this.onCommandHandler?.(result.data);
96
83
  } else if (this.opts.consoleLogEnabled) {
@@ -104,7 +91,7 @@ var CNSDevToolsTransportWs = class {
104
91
  }
105
92
  async flush() {
106
93
  if (this.pendingBatches.length === 0) return;
107
- await this.ensureSocket();
94
+ await this.openSocket();
108
95
  if (this.ws?.readyState !== 1) return;
109
96
  const batches = this.pendingBatches.splice(0, this.pendingBatches.length);
110
97
  if (batches.length === 0) return;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["CNSDTOReplayStartMessageSchema","CNSDTOAppCommandSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ICNSDevToolsTransport } from '@cnstra/devtools';\nimport {\n CNSDTOReplayStartMessageSchema,\n CNSDTOAppCommandSchema,\n type CNSDTOAppBatchMessage,\n} from '@cnstra/devtools-dto';\n\nexport type CNSDevToolsTransportWsOptions = {\n /** WebSocket URL, e.g. ws://localhost:3141 */\n url: string;\n protocols?: string | string[];\n /** Reconnect delay in ms (default: 1000) */\n reconnectDelayMs?: number;\n /** Max buffered batch items before force flush (default: 100) */\n batchMaxSize?: number;\n /** Provide a WebSocket implementation for Node.js (e.g. `ws`) */\n webSocketImpl?: typeof WebSocket;\n /** Max reconnect attempts (default: Infinity) */\n maxReconnectAttempts?: number;\n consoleLogEnabled?: boolean;\n};\n\nexport class CNSDevToolsTransportWs implements ICNSDevToolsTransport {\n private ws?: WebSocket;\n private connecting = false;\n private closed = false;\n private reconnectAttempts = 0;\n private hasConnectedOnce = false;\n\n private readonly pendingBatches: CNSDTOAppBatchMessage[] = [];\n private lastTopologyBatch?: CNSDTOAppBatchMessage;\n private onReplayHandler?: (cmd: any) => void;\n private onCommandHandler?: (cmd: any) => void;\n\n constructor(private readonly opts: CNSDevToolsTransportWsOptions) {}\n\n // ─── ICNSDevToolsTransport ────────────────────────────────────────────────────\n\n async sendBatch(message: CNSDTOAppBatchMessage): Promise<void> {\n if (message.items.some(i => i.type === 'topology')) {\n this.lastTopologyBatch = message;\n }\n this.pendingBatches.push(message);\n await this.flush();\n }\n\n onReplayStart(handler: (cmd: any) => void): () => void {\n this.onReplayHandler = handler;\n return () => { this.onReplayHandler = undefined; };\n }\n\n onStimulationCommand(handler: (cmd: any) => void): () => void {\n this.onCommandHandler = handler;\n return () => { this.onCommandHandler = undefined; };\n }\n\n // ─── Connection management ────────────────────────────────────────────────────\n\n private get WS(): typeof WebSocket {\n const impl = this.opts.webSocketImpl\n ?? (typeof WebSocket !== 'undefined' ? WebSocket : undefined);\n if (!impl) throw new Error('No WebSocket implementation. Pass webSocketImpl option.');\n return impl;\n }\n\n private ensureSocket(): Promise<void> {\n if (this.ws?.readyState === 1) return Promise.resolve();\n if (this.connecting) {\n return new Promise(resolve => {\n const poll = () => this.ws?.readyState === 1 ? resolve() : setTimeout(poll, 50);\n poll();\n });\n }\n this.connecting = true;\n return new Promise(resolve => {\n const ws = new this.WS(this.opts.url, this.opts.protocols) as WebSocket;\n this.ws = ws;\n\n ws.onopen = () => {\n this.connecting = false;\n this.reconnectAttempts = 0;\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Connected');\n\n if (this.hasConnectedOnce && this.lastTopologyBatch) {\n this.pendingBatches.unshift(this.lastTopologyBatch);\n }\n this.hasConnectedOnce = true;\n this.flush().then(resolve);\n };\n\n ws.onclose = () => {\n this.connecting = false;\n if (!this.closed && this.reconnectAttempts < (this.opts.maxReconnectAttempts ?? Infinity)) {\n this.reconnectAttempts++;\n if (this.opts.consoleLogEnabled) {\n console.log(`[DevTools] Disconnected, reconnecting (attempt ${this.reconnectAttempts})`);\n }\n setTimeout(() => this.ensureSocket().catch(() => {}), this.opts.reconnectDelayMs ?? 1000);\n } else if (this.reconnectAttempts >= (this.opts.maxReconnectAttempts ?? Infinity)) {\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Max reconnect attempts reached');\n }\n };\n\n ws.onerror = () => { try { ws.close(); } catch {} };\n\n ws.onmessage = (ev: MessageEvent) => {\n try {\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : String(ev.data));\n if (msg?.type === 'replay.start') {\n const result = CNSDTOReplayStartMessageSchema.safeParse(msg);\n if (result.success) {\n this.onReplayHandler?.(result.data);\n } else if (this.opts.consoleLogEnabled) {\n console.warn('[DevTools] Invalid replay.start message:', result.error.message);\n }\n } else if (\n msg?.type === 'cns.stimulation.resume' ||\n msg?.type === 'cns.stimulation.launch'\n ) {\n const result = CNSDTOAppCommandSchema.safeParse(msg);\n if (result.success) {\n this.onCommandHandler?.(result.data);\n } else if (this.opts.consoleLogEnabled) {\n console.warn('[DevTools] Invalid stimulation command:', result.error.message);\n }\n }\n } catch {}\n };\n });\n }\n\n private async flush(): Promise<void> {\n if (this.pendingBatches.length === 0) return;\n await this.ensureSocket();\n if (this.ws?.readyState !== 1) return;\n\n const batches = this.pendingBatches.splice(0, this.pendingBatches.length);\n if (batches.length === 0) return;\n try {\n const items = batches.flatMap(b => b.items);\n if (items.length === 0) return;\n this.ws.send(JSON.stringify({ type: 'batch', items }));\n } catch {\n this.pendingBatches.unshift(...batches);\n }\n }\n\n close(): void {\n this.closed = true;\n try { this.ws?.close(); } catch {}\n this.ws = undefined;\n }\n\n get isConnected(): boolean {\n return this.ws?.readyState === 1;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["CNSAppCommandDTOSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ICNSDevToolsTransport } from '@cnstra/devtools';\nimport {\n CNSAppCommandDTOSchema,\n type TCNSAppBatchMessageDTO,\n} from '@cnstra/devtools-dto';\n\nexport type TCNSDevToolsTransportWsOptions = {\n /** WebSocket URL, e.g. ws://localhost:3141 */\n url: string;\n protocols?: string | string[];\n /** Reconnect delay in ms (default: 1000) */\n reconnectDelayMs?: number;\n /** Max buffered batch items before force flush (default: 100) */\n batchMaxSize?: number;\n /** Provide a WebSocket implementation for Node.js (e.g. `ws`) */\n webSocketImpl?: typeof WebSocket;\n /** Max reconnect attempts (default: Infinity) */\n maxReconnectAttempts?: number;\n consoleLogEnabled?: boolean;\n};\n\nexport class CNSDevToolsTransportWs implements ICNSDevToolsTransport {\n private ws?: WebSocket;\n private connecting = false;\n private closed = false;\n private reconnectAttempts = 0;\n private hasConnectedOnce = false;\n\n private readonly pendingBatches: TCNSAppBatchMessageDTO[] = [];\n private lastTopologyBatch?: TCNSAppBatchMessageDTO;\n private onCommandHandler?: (cmd: any) => void;\n\n constructor(private readonly opts: TCNSDevToolsTransportWsOptions) {}\n\n // ─── ICNSDevToolsTransport ────────────────────────────────────────────────────\n\n async sendBatch(message: TCNSAppBatchMessageDTO): Promise<void> {\n if (message.items.some(i => i.type === 'topology')) {\n this.lastTopologyBatch = message;\n }\n this.pendingBatches.push(message);\n await this.flush();\n }\n\n onStimulationCommand(handler: (cmd: any) => void): () => void {\n this.onCommandHandler = handler;\n return () => { this.onCommandHandler = undefined; };\n }\n\n // ─── Connection management ────────────────────────────────────────────────────\n\n private get WS(): typeof WebSocket {\n const impl = this.opts.webSocketImpl\n ?? (typeof WebSocket !== 'undefined' ? WebSocket : undefined);\n if (!impl) throw new Error('No WebSocket implementation. Pass webSocketImpl option.');\n return impl;\n }\n\n private openSocket(): Promise<void> {\n if (this.ws?.readyState === 1) return Promise.resolve();\n if (this.connecting) {\n return new Promise(resolve => {\n const poll = () => this.ws?.readyState === 1 ? resolve() : setTimeout(poll, 50);\n poll();\n });\n }\n this.connecting = true;\n return new Promise(resolve => {\n const ws = new this.WS(this.opts.url, this.opts.protocols) as WebSocket;\n this.ws = ws;\n\n ws.onopen = () => {\n this.connecting = false;\n this.reconnectAttempts = 0;\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Connected');\n\n if (this.hasConnectedOnce && this.lastTopologyBatch) {\n this.pendingBatches.unshift(this.lastTopologyBatch);\n }\n this.hasConnectedOnce = true;\n this.flush().then(resolve);\n };\n\n ws.onclose = () => {\n this.connecting = false;\n if (!this.closed && this.reconnectAttempts < (this.opts.maxReconnectAttempts ?? Infinity)) {\n this.reconnectAttempts++;\n if (this.opts.consoleLogEnabled) {\n console.log(`[DevTools] Disconnected, reconnecting (attempt ${this.reconnectAttempts})`);\n }\n setTimeout(() => this.openSocket().catch(() => {}), this.opts.reconnectDelayMs ?? 1000);\n } else if (this.reconnectAttempts >= (this.opts.maxReconnectAttempts ?? Infinity)) {\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Max reconnect attempts reached');\n }\n };\n\n ws.onerror = () => { try { ws.close(); } catch {} };\n\n ws.onmessage = (ev: MessageEvent) => {\n try {\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : String(ev.data));\n if (\n msg?.type === 'cns.stimulation.resume' ||\n msg?.type === 'cns.stimulation.launch'\n ) {\n const result = CNSAppCommandDTOSchema.safeParse(msg);\n if (result.success) {\n this.onCommandHandler?.(result.data);\n } else if (this.opts.consoleLogEnabled) {\n console.warn('[DevTools] Invalid stimulation command:', result.error.message);\n }\n }\n } catch {}\n };\n });\n }\n\n private async flush(): Promise<void> {\n if (this.pendingBatches.length === 0) return;\n await this.openSocket();\n if (this.ws?.readyState !== 1) return;\n\n const batches = this.pendingBatches.splice(0, this.pendingBatches.length);\n if (batches.length === 0) return;\n try {\n const items = batches.flatMap(b => b.items);\n if (items.length === 0) return;\n this.ws.send(JSON.stringify({ type: 'batch', items }));\n } catch {\n this.pendingBatches.unshift(...batches);\n }\n }\n\n close(): void {\n this.closed = true;\n try { this.ws?.close(); } catch {}\n this.ws = undefined;\n }\n\n get isConnected(): boolean {\n return this.ws?.readyState === 1;\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ICNSDevToolsTransport } from '@cnstra/devtools';
2
- import { type CNSDTOAppBatchMessage } from '@cnstra/devtools-dto';
3
- export type CNSDevToolsTransportWsOptions = {
2
+ import { type TCNSAppBatchMessageDTO } from '@cnstra/devtools-dto';
3
+ export type TCNSDevToolsTransportWsOptions = {
4
4
  /** WebSocket URL, e.g. ws://localhost:3141 */
5
5
  url: string;
6
6
  protocols?: string | string[];
@@ -23,14 +23,12 @@ export declare class CNSDevToolsTransportWs implements ICNSDevToolsTransport {
23
23
  private hasConnectedOnce;
24
24
  private readonly pendingBatches;
25
25
  private lastTopologyBatch?;
26
- private onReplayHandler?;
27
26
  private onCommandHandler?;
28
- constructor(opts: CNSDevToolsTransportWsOptions);
29
- sendBatch(message: CNSDTOAppBatchMessage): Promise<void>;
30
- onReplayStart(handler: (cmd: any) => void): () => void;
27
+ constructor(opts: TCNSDevToolsTransportWsOptions);
28
+ sendBatch(message: TCNSAppBatchMessageDTO): Promise<void>;
31
29
  onStimulationCommand(handler: (cmd: any) => void): () => void;
32
30
  private get WS();
33
- private ensureSocket;
31
+ private openSocket;
34
32
  private flush;
35
33
  close(): void;
36
34
  get isConnected(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAGH,KAAK,qBAAqB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,6BAA6B,GAAG;IACxC,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;IACjC,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,qBAAa,sBAAuB,YAAW,qBAAqB;IAYpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXjC,OAAO,CAAC,EAAE,CAAC,CAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+B;IAC9D,OAAO,CAAC,iBAAiB,CAAC,CAAwB;IAClD,OAAO,CAAC,eAAe,CAAC,CAAqB;IAC7C,OAAO,CAAC,gBAAgB,CAAC,CAAqB;gBAEjB,IAAI,EAAE,6BAA6B;IAI1D,SAAS,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D,aAAa,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAKtD,oBAAoB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAO7D,OAAO,KAAK,EAAE,GAKb;IAED,OAAO,CAAC,YAAY;YAkEN,KAAK;IAgBnB,KAAK,IAAI,IAAI;IAMb,IAAI,WAAW,IAAI,OAAO,CAEzB;CACJ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAEH,KAAK,sBAAsB,EAC9B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,8BAA8B,GAAG;IACzC,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;IACjC,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,qBAAa,sBAAuB,YAAW,qBAAqB;IAWpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAVjC,OAAO,CAAC,EAAE,CAAC,CAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgC;IAC/D,OAAO,CAAC,iBAAiB,CAAC,CAAyB;IACnD,OAAO,CAAC,gBAAgB,CAAC,CAAqB;gBAEjB,IAAI,EAAE,8BAA8B;IAI3D,SAAS,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/D,oBAAoB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,MAAM,IAAI;IAO7D,OAAO,KAAK,EAAE,GAKb;IAED,OAAO,CAAC,UAAU;YA2DJ,KAAK;IAgBnB,KAAK,IAAI,IAAI;IAMb,IAAI,WAAW,IAAI,OAAO,CAEzB;CACJ"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CNSDTOReplayStartMessageSchema, CNSDTOAppCommandSchema } from '@cnstra/devtools-dto';
1
+ import { CNSAppCommandDTOSchema } from '@cnstra/devtools-dto';
2
2
 
3
3
  // src/index.ts
4
4
  var CNSDevToolsTransportWs = class {
@@ -18,12 +18,6 @@ var CNSDevToolsTransportWs = class {
18
18
  this.pendingBatches.push(message);
19
19
  await this.flush();
20
20
  }
21
- onReplayStart(handler) {
22
- this.onReplayHandler = handler;
23
- return () => {
24
- this.onReplayHandler = void 0;
25
- };
26
- }
27
21
  onStimulationCommand(handler) {
28
22
  this.onCommandHandler = handler;
29
23
  return () => {
@@ -36,7 +30,7 @@ var CNSDevToolsTransportWs = class {
36
30
  if (!impl) throw new Error("No WebSocket implementation. Pass webSocketImpl option.");
37
31
  return impl;
38
32
  }
39
- ensureSocket() {
33
+ openSocket() {
40
34
  if (this.ws?.readyState === 1) return Promise.resolve();
41
35
  if (this.connecting) {
42
36
  return new Promise((resolve) => {
@@ -65,7 +59,7 @@ var CNSDevToolsTransportWs = class {
65
59
  if (this.opts.consoleLogEnabled) {
66
60
  console.log(`[DevTools] Disconnected, reconnecting (attempt ${this.reconnectAttempts})`);
67
61
  }
68
- setTimeout(() => this.ensureSocket().catch(() => {
62
+ setTimeout(() => this.openSocket().catch(() => {
69
63
  }), this.opts.reconnectDelayMs ?? 1e3);
70
64
  } else if (this.reconnectAttempts >= (this.opts.maxReconnectAttempts ?? Infinity)) {
71
65
  if (this.opts.consoleLogEnabled) console.log("[DevTools] Max reconnect attempts reached");
@@ -80,15 +74,8 @@ var CNSDevToolsTransportWs = class {
80
74
  ws.onmessage = (ev) => {
81
75
  try {
82
76
  const msg = JSON.parse(typeof ev.data === "string" ? ev.data : String(ev.data));
83
- if (msg?.type === "replay.start") {
84
- const result = CNSDTOReplayStartMessageSchema.safeParse(msg);
85
- if (result.success) {
86
- this.onReplayHandler?.(result.data);
87
- } else if (this.opts.consoleLogEnabled) {
88
- console.warn("[DevTools] Invalid replay.start message:", result.error.message);
89
- }
90
- } else if (msg?.type === "cns.stimulation.resume" || msg?.type === "cns.stimulation.launch") {
91
- const result = CNSDTOAppCommandSchema.safeParse(msg);
77
+ if (msg?.type === "cns.stimulation.resume" || msg?.type === "cns.stimulation.launch") {
78
+ const result = CNSAppCommandDTOSchema.safeParse(msg);
92
79
  if (result.success) {
93
80
  this.onCommandHandler?.(result.data);
94
81
  } else if (this.opts.consoleLogEnabled) {
@@ -102,7 +89,7 @@ var CNSDevToolsTransportWs = class {
102
89
  }
103
90
  async flush() {
104
91
  if (this.pendingBatches.length === 0) return;
105
- await this.ensureSocket();
92
+ await this.openSocket();
106
93
  if (this.ws?.readyState !== 1) return;
107
94
  const batches = this.pendingBatches.splice(0, this.pendingBatches.length);
108
95
  if (batches.length === 0) return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ICNSDevToolsTransport } from '@cnstra/devtools';\nimport {\n CNSDTOReplayStartMessageSchema,\n CNSDTOAppCommandSchema,\n type CNSDTOAppBatchMessage,\n} from '@cnstra/devtools-dto';\n\nexport type CNSDevToolsTransportWsOptions = {\n /** WebSocket URL, e.g. ws://localhost:3141 */\n url: string;\n protocols?: string | string[];\n /** Reconnect delay in ms (default: 1000) */\n reconnectDelayMs?: number;\n /** Max buffered batch items before force flush (default: 100) */\n batchMaxSize?: number;\n /** Provide a WebSocket implementation for Node.js (e.g. `ws`) */\n webSocketImpl?: typeof WebSocket;\n /** Max reconnect attempts (default: Infinity) */\n maxReconnectAttempts?: number;\n consoleLogEnabled?: boolean;\n};\n\nexport class CNSDevToolsTransportWs implements ICNSDevToolsTransport {\n private ws?: WebSocket;\n private connecting = false;\n private closed = false;\n private reconnectAttempts = 0;\n private hasConnectedOnce = false;\n\n private readonly pendingBatches: CNSDTOAppBatchMessage[] = [];\n private lastTopologyBatch?: CNSDTOAppBatchMessage;\n private onReplayHandler?: (cmd: any) => void;\n private onCommandHandler?: (cmd: any) => void;\n\n constructor(private readonly opts: CNSDevToolsTransportWsOptions) {}\n\n // ─── ICNSDevToolsTransport ────────────────────────────────────────────────────\n\n async sendBatch(message: CNSDTOAppBatchMessage): Promise<void> {\n if (message.items.some(i => i.type === 'topology')) {\n this.lastTopologyBatch = message;\n }\n this.pendingBatches.push(message);\n await this.flush();\n }\n\n onReplayStart(handler: (cmd: any) => void): () => void {\n this.onReplayHandler = handler;\n return () => { this.onReplayHandler = undefined; };\n }\n\n onStimulationCommand(handler: (cmd: any) => void): () => void {\n this.onCommandHandler = handler;\n return () => { this.onCommandHandler = undefined; };\n }\n\n // ─── Connection management ────────────────────────────────────────────────────\n\n private get WS(): typeof WebSocket {\n const impl = this.opts.webSocketImpl\n ?? (typeof WebSocket !== 'undefined' ? WebSocket : undefined);\n if (!impl) throw new Error('No WebSocket implementation. Pass webSocketImpl option.');\n return impl;\n }\n\n private ensureSocket(): Promise<void> {\n if (this.ws?.readyState === 1) return Promise.resolve();\n if (this.connecting) {\n return new Promise(resolve => {\n const poll = () => this.ws?.readyState === 1 ? resolve() : setTimeout(poll, 50);\n poll();\n });\n }\n this.connecting = true;\n return new Promise(resolve => {\n const ws = new this.WS(this.opts.url, this.opts.protocols) as WebSocket;\n this.ws = ws;\n\n ws.onopen = () => {\n this.connecting = false;\n this.reconnectAttempts = 0;\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Connected');\n\n if (this.hasConnectedOnce && this.lastTopologyBatch) {\n this.pendingBatches.unshift(this.lastTopologyBatch);\n }\n this.hasConnectedOnce = true;\n this.flush().then(resolve);\n };\n\n ws.onclose = () => {\n this.connecting = false;\n if (!this.closed && this.reconnectAttempts < (this.opts.maxReconnectAttempts ?? Infinity)) {\n this.reconnectAttempts++;\n if (this.opts.consoleLogEnabled) {\n console.log(`[DevTools] Disconnected, reconnecting (attempt ${this.reconnectAttempts})`);\n }\n setTimeout(() => this.ensureSocket().catch(() => {}), this.opts.reconnectDelayMs ?? 1000);\n } else if (this.reconnectAttempts >= (this.opts.maxReconnectAttempts ?? Infinity)) {\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Max reconnect attempts reached');\n }\n };\n\n ws.onerror = () => { try { ws.close(); } catch {} };\n\n ws.onmessage = (ev: MessageEvent) => {\n try {\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : String(ev.data));\n if (msg?.type === 'replay.start') {\n const result = CNSDTOReplayStartMessageSchema.safeParse(msg);\n if (result.success) {\n this.onReplayHandler?.(result.data);\n } else if (this.opts.consoleLogEnabled) {\n console.warn('[DevTools] Invalid replay.start message:', result.error.message);\n }\n } else if (\n msg?.type === 'cns.stimulation.resume' ||\n msg?.type === 'cns.stimulation.launch'\n ) {\n const result = CNSDTOAppCommandSchema.safeParse(msg);\n if (result.success) {\n this.onCommandHandler?.(result.data);\n } else if (this.opts.consoleLogEnabled) {\n console.warn('[DevTools] Invalid stimulation command:', result.error.message);\n }\n }\n } catch {}\n };\n });\n }\n\n private async flush(): Promise<void> {\n if (this.pendingBatches.length === 0) return;\n await this.ensureSocket();\n if (this.ws?.readyState !== 1) return;\n\n const batches = this.pendingBatches.splice(0, this.pendingBatches.length);\n if (batches.length === 0) return;\n try {\n const items = batches.flatMap(b => b.items);\n if (items.length === 0) return;\n this.ws.send(JSON.stringify({ type: 'batch', items }));\n } catch {\n this.pendingBatches.unshift(...batches);\n }\n }\n\n close(): void {\n this.closed = true;\n try { this.ws?.close(); } catch {}\n this.ws = undefined;\n }\n\n get isConnected(): boolean {\n return this.ws?.readyState === 1;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ICNSDevToolsTransport } from '@cnstra/devtools';\nimport {\n CNSAppCommandDTOSchema,\n type TCNSAppBatchMessageDTO,\n} from '@cnstra/devtools-dto';\n\nexport type TCNSDevToolsTransportWsOptions = {\n /** WebSocket URL, e.g. ws://localhost:3141 */\n url: string;\n protocols?: string | string[];\n /** Reconnect delay in ms (default: 1000) */\n reconnectDelayMs?: number;\n /** Max buffered batch items before force flush (default: 100) */\n batchMaxSize?: number;\n /** Provide a WebSocket implementation for Node.js (e.g. `ws`) */\n webSocketImpl?: typeof WebSocket;\n /** Max reconnect attempts (default: Infinity) */\n maxReconnectAttempts?: number;\n consoleLogEnabled?: boolean;\n};\n\nexport class CNSDevToolsTransportWs implements ICNSDevToolsTransport {\n private ws?: WebSocket;\n private connecting = false;\n private closed = false;\n private reconnectAttempts = 0;\n private hasConnectedOnce = false;\n\n private readonly pendingBatches: TCNSAppBatchMessageDTO[] = [];\n private lastTopologyBatch?: TCNSAppBatchMessageDTO;\n private onCommandHandler?: (cmd: any) => void;\n\n constructor(private readonly opts: TCNSDevToolsTransportWsOptions) {}\n\n // ─── ICNSDevToolsTransport ────────────────────────────────────────────────────\n\n async sendBatch(message: TCNSAppBatchMessageDTO): Promise<void> {\n if (message.items.some(i => i.type === 'topology')) {\n this.lastTopologyBatch = message;\n }\n this.pendingBatches.push(message);\n await this.flush();\n }\n\n onStimulationCommand(handler: (cmd: any) => void): () => void {\n this.onCommandHandler = handler;\n return () => { this.onCommandHandler = undefined; };\n }\n\n // ─── Connection management ────────────────────────────────────────────────────\n\n private get WS(): typeof WebSocket {\n const impl = this.opts.webSocketImpl\n ?? (typeof WebSocket !== 'undefined' ? WebSocket : undefined);\n if (!impl) throw new Error('No WebSocket implementation. Pass webSocketImpl option.');\n return impl;\n }\n\n private openSocket(): Promise<void> {\n if (this.ws?.readyState === 1) return Promise.resolve();\n if (this.connecting) {\n return new Promise(resolve => {\n const poll = () => this.ws?.readyState === 1 ? resolve() : setTimeout(poll, 50);\n poll();\n });\n }\n this.connecting = true;\n return new Promise(resolve => {\n const ws = new this.WS(this.opts.url, this.opts.protocols) as WebSocket;\n this.ws = ws;\n\n ws.onopen = () => {\n this.connecting = false;\n this.reconnectAttempts = 0;\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Connected');\n\n if (this.hasConnectedOnce && this.lastTopologyBatch) {\n this.pendingBatches.unshift(this.lastTopologyBatch);\n }\n this.hasConnectedOnce = true;\n this.flush().then(resolve);\n };\n\n ws.onclose = () => {\n this.connecting = false;\n if (!this.closed && this.reconnectAttempts < (this.opts.maxReconnectAttempts ?? Infinity)) {\n this.reconnectAttempts++;\n if (this.opts.consoleLogEnabled) {\n console.log(`[DevTools] Disconnected, reconnecting (attempt ${this.reconnectAttempts})`);\n }\n setTimeout(() => this.openSocket().catch(() => {}), this.opts.reconnectDelayMs ?? 1000);\n } else if (this.reconnectAttempts >= (this.opts.maxReconnectAttempts ?? Infinity)) {\n if (this.opts.consoleLogEnabled) console.log('[DevTools] Max reconnect attempts reached');\n }\n };\n\n ws.onerror = () => { try { ws.close(); } catch {} };\n\n ws.onmessage = (ev: MessageEvent) => {\n try {\n const msg = JSON.parse(typeof ev.data === 'string' ? ev.data : String(ev.data));\n if (\n msg?.type === 'cns.stimulation.resume' ||\n msg?.type === 'cns.stimulation.launch'\n ) {\n const result = CNSAppCommandDTOSchema.safeParse(msg);\n if (result.success) {\n this.onCommandHandler?.(result.data);\n } else if (this.opts.consoleLogEnabled) {\n console.warn('[DevTools] Invalid stimulation command:', result.error.message);\n }\n }\n } catch {}\n };\n });\n }\n\n private async flush(): Promise<void> {\n if (this.pendingBatches.length === 0) return;\n await this.openSocket();\n if (this.ws?.readyState !== 1) return;\n\n const batches = this.pendingBatches.splice(0, this.pendingBatches.length);\n if (batches.length === 0) return;\n try {\n const items = batches.flatMap(b => b.items);\n if (items.length === 0) return;\n this.ws.send(JSON.stringify({ type: 'batch', items }));\n } catch {\n this.pendingBatches.unshift(...batches);\n }\n }\n\n close(): void {\n this.closed = true;\n try { this.ws?.close(); } catch {}\n this.ws = undefined;\n }\n\n get isConnected(): boolean {\n return this.ws?.readyState === 1;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@cnstra/devtools-transport-ws",
3
- "version": "1.2.0",
3
+ "version": "2.0.1",
4
4
  "description": "WebSocket transport for CNStra DevTools",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/abaikov/cnstra.git",
8
+ "directory": "packages/devtools-transport-ws"
9
+ },
5
10
  "keywords": [
6
11
  "devtools",
7
12
  "cnstra",
@@ -27,19 +32,19 @@
27
32
  "test": "jest",
28
33
  "test:watch": "jest --watch",
29
34
  "test:coverage": "jest --coverage",
30
- "prepublishOnly": "npm run test && npm run build"
35
+ "prepublishOnly": "node ../../scripts/check-drift.mjs --gate && npm run test && npm run build"
31
36
  },
32
37
  "files": [
33
38
  "dist"
34
39
  ],
35
40
  "dependencies": {
36
- "@cnstra/devtools-dto": "^2.0.0"
41
+ "@cnstra/devtools-dto": "^3.0.0"
37
42
  },
38
43
  "peerDependencies": {
39
- "@cnstra/devtools": "^2.0.0"
44
+ "@cnstra/devtools": "^3.0.0"
40
45
  },
41
46
  "devDependencies": {
42
- "@cnstra/devtools": "^2.0.0",
47
+ "@cnstra/devtools": "^3.0.0",
43
48
  "@types/jest": "^29.5.5",
44
49
  "jest": "^29.7.0",
45
50
  "jest-environment-jsdom": "^29.7.0",