twd-relay 0.2.1 → 0.3.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.
@@ -1 +1 @@
1
- "use strict";var P=Object.create;var C=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var I=(n,o,i,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let l of L(o))!z.call(n,l)&&l!==i&&C(n,l,{get:()=>o[l],enumerable:!(f=A(o,l))||f.enumerable});return n};var M=(n,o,i)=>(i=n!=null?P($(n)):{},I(o||!n||!n.__esModule?C(i,"default",{value:n,enumerable:!0}):i,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function j(){return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/__twd/ws`}function p(n,o){if(!n.parent)return"";const i=o.get(n.parent);return i?i.name:""}function B(n){const o=n?.url??j(),i=n?.reconnect??!0,f=n?.reconnectInterval??2e3,l=n?.log??!1,T="[twd-relay]";function m(...t){l&&console.info(T,...t)}function S(...t){console.warn(T,...t)}let r=null,w=!1,g=null;function a(t){r&&r.readyState===WebSocket.OPEN&&r.send(JSON.stringify(t))}function u(){window.dispatchEvent(new CustomEvent("twd:state-change"))}async function N(){const t=window.__TWD_STATE__;if(!t){S("TWD not initialized — make sure twd-js is loaded before running tests"),a({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const s=t.handlers,y=Array.from(s.values()).filter(e=>e.type==="test").length;a({type:"run:start",testCount:y});let c=0,_=0,b=0;const h=performance.now(),O={onStart(e){e.status="running",u(),a({type:"test:start",id:e.id,name:e.name,suite:p(e,s)})},onPass(e){c++,e.status="pass",u(),a({type:"test:pass",id:e.id,name:e.name,suite:p(e,s),duration:performance.now()-h})},onFail(e,d){_++,e.status="fail",e.logs=[d.message],u(),a({type:"test:fail",id:e.id,name:e.name,suite:p(e,s),error:d.message,duration:performance.now()-h})},onSkip(e){b++,e.status="skip",u(),a({type:"test:skip",id:e.id,name:e.name,suite:p(e,s)})},onSuiteStart(e){e.status="running",u()},onSuiteEnd(e){e.status="idle",u()}};try{const{TestRunner:e}=await import("twd-js/runner");await new e(O).runAll()}catch(e){const d=e instanceof Error?e.message:String(e);S("Runner error:",d),a({type:"error",code:"RUNNER_ERROR",message:d})}const D=performance.now()-h;a({type:"run:complete",passed:c,failed:_,skipped:b,duration:D}),u()}function R(){const t=window.__TWD_STATE__;if(!t){a({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const s=t.handlers,y=[];for(const[,c]of s)c.type==="test"&&y.push({id:c.id,name:c.name,suite:p(c,s),status:c.status??"idle"});a({type:"status:result",tests:y})}function W(t){let s;try{s=JSON.parse(t.data)}catch{return}s.type==="run"?(m("Received run command — running tests..."),N()):s.type==="status"&&R()}function k(){i&&!w&&(m(`Reconnecting in ${f}ms...`),g=setTimeout(()=>{E()},f))}function E(){r&&(r.readyState===WebSocket.OPEN||r.readyState===WebSocket.CONNECTING)||(w=!1,m("Connecting to",o),r=new WebSocket(o),r.addEventListener("open",()=>{a({type:"hello",role:"browser"}),m("Connected to relay — ready to receive run/status commands")}),r.addEventListener("message",W),r.addEventListener("close",t=>{if(r=null,t.reason==="Replaced by new browser"){S("Another browser instance connected — this instance will not reconnect");return}w||m("Disconnected",t.code?`(code ${t.code})`:"",t.reason||""),k()}),r.addEventListener("error",()=>{}))}function v(){w=!0,g&&(clearTimeout(g),g=null),r&&(r.close(1e3,"Client disconnecting"),r=null)}return{connect:E,disconnect:v,get connected(){return r!==null&&r.readyState===WebSocket.OPEN}}}exports.createBrowserClient=B;
1
+ "use strict";var P=Object.create;var C=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var I=(n,o,i,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let l of L(o))!z.call(n,l)&&l!==i&&C(n,l,{get:()=>o[l],enumerable:!(f=A(o,l))||f.enumerable});return n};var M=(n,o,i)=>(i=n!=null?P($(n)):{},I(o||!n||!n.__esModule?C(i,"default",{value:n,enumerable:!0}):i,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function j(n){return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}${n}`}function p(n,o){if(!n.parent)return"";const i=o.get(n.parent);return i?i.name:""}function B(n){const o=n?.url??j(n?.path??"/__twd/ws"),i=n?.reconnect??!0,f=n?.reconnectInterval??2e3,l=n?.log??!1,T="[twd-relay]";function m(...t){l&&console.info(T,...t)}function S(...t){console.warn(T,...t)}let r=null,w=!1,g=null;function a(t){r&&r.readyState===WebSocket.OPEN&&r.send(JSON.stringify(t))}function u(){window.dispatchEvent(new CustomEvent("twd:state-change"))}async function N(){const t=window.__TWD_STATE__;if(!t){S("TWD not initialized — make sure twd-js is loaded before running tests"),a({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const s=t.handlers,y=Array.from(s.values()).filter(e=>e.type==="test").length;a({type:"run:start",testCount:y});let c=0,_=0,b=0;const h=performance.now(),O={onStart(e){e.status="running",u(),a({type:"test:start",id:e.id,name:e.name,suite:p(e,s)})},onPass(e){c++,e.status="pass",u(),a({type:"test:pass",id:e.id,name:e.name,suite:p(e,s),duration:performance.now()-h})},onFail(e,d){_++,e.status="fail",e.logs=[d.message],u(),a({type:"test:fail",id:e.id,name:e.name,suite:p(e,s),error:d.message,duration:performance.now()-h})},onSkip(e){b++,e.status="skip",u(),a({type:"test:skip",id:e.id,name:e.name,suite:p(e,s)})},onSuiteStart(e){e.status="running",u()},onSuiteEnd(e){e.status="idle",u()}};try{const{TestRunner:e}=await import("twd-js/runner");await new e(O).runAll()}catch(e){const d=e instanceof Error?e.message:String(e);S("Runner error:",d),a({type:"error",code:"RUNNER_ERROR",message:d})}const D=performance.now()-h;a({type:"run:complete",passed:c,failed:_,skipped:b,duration:D}),u()}function R(){const t=window.__TWD_STATE__;if(!t){a({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const s=t.handlers,y=[];for(const[,c]of s)c.type==="test"&&y.push({id:c.id,name:c.name,suite:p(c,s),status:c.status??"idle"});a({type:"status:result",tests:y})}function W(t){let s;try{s=JSON.parse(t.data)}catch{return}s.type==="run"?(m("Received run command — running tests..."),N()):s.type==="status"&&R()}function k(){i&&!w&&(m(`Reconnecting in ${f}ms...`),g=setTimeout(()=>{E()},f))}function E(){r&&(r.readyState===WebSocket.OPEN||r.readyState===WebSocket.CONNECTING)||(w=!1,m("Connecting to",o),r=new WebSocket(o),r.addEventListener("open",()=>{a({type:"hello",role:"browser"}),m("Connected to relay — ready to receive run/status commands")}),r.addEventListener("message",W),r.addEventListener("close",t=>{if(r=null,t.reason==="Replaced by new browser"){S("Another browser instance connected — this instance will not reconnect");return}w||m("Disconnected",t.code?`(code ${t.code})`:"",t.reason||""),k()}),r.addEventListener("error",()=>{}))}function v(){w=!0,g&&(clearTimeout(g),g=null),r&&(r.close(1e3,"Client disconnecting"),r=null)}return{connect:E,disconnect:v,get connected(){return r!==null&&r.readyState===WebSocket.OPEN}}}exports.createBrowserClient=B;
package/dist/browser.d.ts CHANGED
@@ -7,6 +7,8 @@ export declare interface BrowserClient {
7
7
  export declare interface BrowserClientOptions {
8
8
  /** WebSocket URL. Default: auto-detected from window.location */
9
9
  url?: string;
10
+ /** WebSocket path. Default: '/__twd/ws'. Ignored when `url` is set. */
11
+ path?: string;
10
12
  /** Auto-reconnect on disconnect. Default: true */
11
13
  reconnect?: boolean;
12
14
  /** Milliseconds between reconnect attempts. Default: 2000 */
@@ -1,21 +1,21 @@
1
- function O() {
2
- return `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/__twd/ws`;
1
+ function O(s) {
2
+ return `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}${s}`;
3
3
  }
4
- function l(s, d) {
4
+ function d(s, u) {
5
5
  if (!s.parent) return "";
6
- const f = d.get(s.parent);
6
+ const f = u.get(s.parent);
7
7
  return f ? f.name : "";
8
8
  }
9
9
  function A(s) {
10
- const d = s?.url ?? O(), f = s?.reconnect ?? !0, S = s?.reconnectInterval ?? 2e3, N = s?.log ?? !1, h = "[twd-relay]";
11
- function u(...n) {
10
+ const u = s?.url ?? O(s?.path ?? "/__twd/ws"), f = s?.reconnect ?? !0, S = s?.reconnectInterval ?? 2e3, N = s?.log ?? !1, h = "[twd-relay]";
11
+ function l(...n) {
12
12
  N && console.info(h, ...n);
13
13
  }
14
14
  function g(...n) {
15
15
  console.warn(h, ...n);
16
16
  }
17
17
  let t = null, p = !1, m = null;
18
- function r(n) {
18
+ function o(n) {
19
19
  t && t.readyState === WebSocket.OPEN && t.send(JSON.stringify(n));
20
20
  }
21
21
  function i() {
@@ -24,46 +24,46 @@ function A(s) {
24
24
  async function C() {
25
25
  const n = window.__TWD_STATE__;
26
26
  if (!n) {
27
- g("TWD not initialized — make sure twd-js is loaded before running tests"), r({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
27
+ g("TWD not initialized — make sure twd-js is loaded before running tests"), o({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
28
28
  return;
29
29
  }
30
- const o = n.handlers, w = Array.from(o.values()).filter((e) => e.type === "test").length;
31
- r({ type: "run:start", testCount: w });
30
+ const r = n.handlers, w = Array.from(r.values()).filter((e) => e.type === "test").length;
31
+ o({ type: "run:start", testCount: w });
32
32
  let a = 0, T = 0, _ = 0;
33
33
  const y = performance.now(), v = {
34
34
  onStart(e) {
35
- e.status = "running", i(), r({
35
+ e.status = "running", i(), o({
36
36
  type: "test:start",
37
37
  id: e.id,
38
38
  name: e.name,
39
- suite: l(e, o)
39
+ suite: d(e, r)
40
40
  });
41
41
  },
42
42
  onPass(e) {
43
- a++, e.status = "pass", i(), r({
43
+ a++, e.status = "pass", i(), o({
44
44
  type: "test:pass",
45
45
  id: e.id,
46
46
  name: e.name,
47
- suite: l(e, o),
47
+ suite: d(e, r),
48
48
  duration: performance.now() - y
49
49
  });
50
50
  },
51
51
  onFail(e, c) {
52
- T++, e.status = "fail", e.logs = [c.message], i(), r({
52
+ T++, e.status = "fail", e.logs = [c.message], i(), o({
53
53
  type: "test:fail",
54
54
  id: e.id,
55
55
  name: e.name,
56
- suite: l(e, o),
56
+ suite: d(e, r),
57
57
  error: c.message,
58
58
  duration: performance.now() - y
59
59
  });
60
60
  },
61
61
  onSkip(e) {
62
- _++, e.status = "skip", i(), r({
62
+ _++, e.status = "skip", i(), o({
63
63
  type: "test:skip",
64
64
  id: e.id,
65
65
  name: e.name,
66
- suite: l(e, o)
66
+ suite: d(e, r)
67
67
  });
68
68
  },
69
69
  onSuiteStart(e) {
@@ -78,50 +78,50 @@ function A(s) {
78
78
  await new e(v).runAll();
79
79
  } catch (e) {
80
80
  const c = e instanceof Error ? e.message : String(e);
81
- g("Runner error:", c), r({ type: "error", code: "RUNNER_ERROR", message: c });
81
+ g("Runner error:", c), o({ type: "error", code: "RUNNER_ERROR", message: c });
82
82
  }
83
83
  const D = performance.now() - y;
84
- r({ type: "run:complete", passed: a, failed: T, skipped: _, duration: D }), i();
84
+ o({ type: "run:complete", passed: a, failed: T, skipped: _, duration: D }), i();
85
85
  }
86
86
  function R() {
87
87
  const n = window.__TWD_STATE__;
88
88
  if (!n) {
89
- r({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
89
+ o({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
90
90
  return;
91
91
  }
92
- const o = n.handlers, w = [];
93
- for (const [, a] of o)
92
+ const r = n.handlers, w = [];
93
+ for (const [, a] of r)
94
94
  a.type === "test" && w.push({
95
95
  id: a.id,
96
96
  name: a.name,
97
- suite: l(a, o),
97
+ suite: d(a, r),
98
98
  status: a.status ?? "idle"
99
99
  });
100
- r({ type: "status:result", tests: w });
100
+ o({ type: "status:result", tests: w });
101
101
  }
102
102
  function W(n) {
103
- let o;
103
+ let r;
104
104
  try {
105
- o = JSON.parse(n.data);
105
+ r = JSON.parse(n.data);
106
106
  } catch {
107
107
  return;
108
108
  }
109
- o.type === "run" ? (u("Received run command — running tests..."), C()) : o.type === "status" && R();
109
+ r.type === "run" ? (l("Received run command — running tests..."), C()) : r.type === "status" && R();
110
110
  }
111
111
  function b() {
112
- f && !p && (u(`Reconnecting in ${S}ms...`), m = setTimeout(() => {
112
+ f && !p && (l(`Reconnecting in ${S}ms...`), m = setTimeout(() => {
113
113
  E();
114
114
  }, S));
115
115
  }
116
116
  function E() {
117
- t && (t.readyState === WebSocket.OPEN || t.readyState === WebSocket.CONNECTING) || (p = !1, u("Connecting to", d), t = new WebSocket(d), t.addEventListener("open", () => {
118
- r({ type: "hello", role: "browser" }), u("Connected to relay — ready to receive run/status commands");
117
+ t && (t.readyState === WebSocket.OPEN || t.readyState === WebSocket.CONNECTING) || (p = !1, l("Connecting to", u), t = new WebSocket(u), t.addEventListener("open", () => {
118
+ o({ type: "hello", role: "browser" }), l("Connected to relay — ready to receive run/status commands");
119
119
  }), t.addEventListener("message", W), t.addEventListener("close", (n) => {
120
120
  if (t = null, n.reason === "Replaced by new browser") {
121
121
  g("Another browser instance connected — this instance will not reconnect");
122
122
  return;
123
123
  }
124
- p || u("Disconnected", n.code ? `(code ${n.code})` : "", n.reason || ""), b();
124
+ p || l("Disconnected", n.code ? `(code ${n.code})` : "", n.reason || ""), b();
125
125
  }), t.addEventListener("error", () => {
126
126
  }));
127
127
  }
package/dist/cli.js CHANGED
@@ -167,8 +167,8 @@ function createTwdRelay(server, options) {
167
167
  };
168
168
  }
169
169
  function run(options) {
170
- const { port, timeout } = options;
171
- const url = `ws://localhost:${port}/__twd/ws`;
170
+ const { port, timeout, path, host } = options;
171
+ const url = `ws://${host}:${port}${path}`;
172
172
  console.log(`Connecting to ${url}...`);
173
173
  const ws = new WebSocket$1(url);
174
174
  let runSent = false;
@@ -261,15 +261,20 @@ Commands:
261
261
 
262
262
  Options for serve:
263
263
  --port <port> Port to listen on (default: 9876)
264
+ --path <path> WebSocket path (default: /__twd/ws)
264
265
 
265
266
  Options for run:
266
267
  --port <port> Relay port to connect to (default: 5173)
268
+ --host <host> Relay host to connect to (default: localhost)
269
+ --path <path> WebSocket path (default: /__twd/ws)
267
270
  --timeout <ms> Timeout in ms (default: 180000)
268
271
 
269
272
  Examples:
270
273
  twd-relay # start relay on port 9876
274
+ twd-relay serve --path /app/__twd/ws
271
275
  twd-relay run # trigger run via Vite dev server on 5173
272
276
  twd-relay run --port 9876 # trigger run on custom port
277
+ twd-relay run --host 192.168.1.10 --path /app/__twd/ws
273
278
  twd-relay run --timeout 30000 # custom timeout`);
274
279
  }
275
280
  if (args.includes("--help") || args.includes("-h")) {
@@ -279,6 +284,8 @@ if (args.includes("--help") || args.includes("-h")) {
279
284
  if (subcommand === "run") {
280
285
  const portStr = parseFlag("--port");
281
286
  const timeoutStr = parseFlag("--timeout");
287
+ const pathFlag = parseFlag("--path") ?? "/__twd/ws";
288
+ const hostFlag = parseFlag("--host") ?? "localhost";
282
289
  const port = portStr ? parseInt(portStr, 10) : 5173;
283
290
  if (isNaN(port)) {
284
291
  console.error("Invalid port number:", portStr);
@@ -289,7 +296,7 @@ if (subcommand === "run") {
289
296
  console.error("Invalid timeout value:", timeoutStr);
290
297
  process.exit(1);
291
298
  }
292
- run({ port, timeout });
299
+ run({ port, timeout, path: pathFlag, host: hostFlag });
293
300
  } else if (!subcommand || subcommand === "serve") {
294
301
  let shutdown = function() {
295
302
  console.log("\nShutting down...");
@@ -299,6 +306,7 @@ if (subcommand === "run") {
299
306
  });
300
307
  };
301
308
  const portStr = parseFlag("--port");
309
+ const pathFlag = parseFlag("--path") ?? "/__twd/ws";
302
310
  let port = 9876;
303
311
  if (portStr) {
304
312
  port = parseInt(portStr, 10);
@@ -309,12 +317,13 @@ if (subcommand === "run") {
309
317
  }
310
318
  const server = createServer();
311
319
  const relay = createTwdRelay(server, {
320
+ path: pathFlag,
312
321
  onError(err) {
313
322
  console.error("[twd-relay] Error:", err.message);
314
323
  }
315
324
  });
316
325
  server.listen(port, () => {
317
- console.log(`TWD Relay running on ws://localhost:${port}/__twd/ws`);
326
+ console.log(`TWD Relay running on ws://localhost:${port}${pathFlag}`);
318
327
  console.log("Waiting for connections...");
319
328
  });
320
329
  process.on("SIGINT", shutdown);
package/dist/vite.cjs.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./createTwdRelay-VRmjHboP.cjs");function n(t){return{name:"twd-relay",configureServer(e){if(!e.httpServer)return;const r=o.c(e.httpServer,{path:t?.path??"/__twd/ws"});e.httpServer.on("close",()=>r.close())}}}exports.twdRemote=n;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("./createTwdRelay-VRmjHboP.cjs");function a(r){let t="/";return{name:"twd-relay",configResolved(e){t=e.base},configureServer(e){if(!e.httpServer)return;const o=r?.path??t.replace(/\/$/,"")+"/__twd/ws",c=n.c(e.httpServer,{path:o});e.httpServer.on("close",()=>c.close())}}}exports.twdRemote=a;
package/dist/vite.d.ts CHANGED
@@ -9,6 +9,9 @@ export declare interface TwdRemoteOptions {
9
9
 
10
10
  declare interface VitePlugin {
11
11
  name: string;
12
+ configResolved?: (config: {
13
+ base: string;
14
+ }) => void;
12
15
  configureServer?: (server: {
13
16
  httpServer: Server | null;
14
17
  }) => void;
package/dist/vite.es.js CHANGED
@@ -1,16 +1,18 @@
1
- import { c as o } from "./createTwdRelay-22etCW_8.js";
2
- function n(e) {
1
+ import { c } from "./createTwdRelay-22etCW_8.js";
2
+ function l(r) {
3
+ let t = "/";
3
4
  return {
4
5
  name: "twd-relay",
5
- configureServer(t) {
6
- if (!t.httpServer) return;
7
- const r = o(t.httpServer, {
8
- path: e?.path ?? "/__twd/ws"
9
- });
10
- t.httpServer.on("close", () => r.close());
6
+ configResolved(e) {
7
+ t = e.base;
8
+ },
9
+ configureServer(e) {
10
+ if (!e.httpServer) return;
11
+ const o = r?.path ?? t.replace(/\/$/, "") + "/__twd/ws", a = c(e.httpServer, { path: o });
12
+ e.httpServer.on("close", () => a.close());
11
13
  }
12
14
  };
13
15
  }
14
16
  export {
15
- n as twdRemote
17
+ l as twdRemote
16
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "twd-relay",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "WebSocket relay for TWD — enables AI agents and external tools to run and observe in-browser tests",
5
5
  "license": "MIT",
6
6
  "author": "BRIKEV",