twd-relay 0.2.1 → 1.0.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
+ var I=Object.create;var O=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var z=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var j=(t,o,l,p)=>{if(o&&typeof o=="object"||typeof o=="function")for(let u of M(o))!B.call(t,u)&&u!==l&&O(t,u,{get:()=>o[u],enumerable:!(p=J(o,u))||p.enumerable});return t};var U=(t,o,l)=>(l=t!=null?I(z(t)):{},j(o||!t||!t.__esModule?O(l,"default",{value:t,enumerable:!0}):l,t));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function x(t){return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}${t}`}function g(t,o){if(!t.parent)return"";const l=o.get(t.parent);return l?l.name:""}function F(t){const o=t?.url??x(t?.path??"/__twd/ws"),l=t?.reconnect??!0,p=t?.reconnectInterval??2e3,u=t?.log??!1,C="[twd-relay]";function w(...n){u&&console.info(C,...n)}function N(...n){console.warn(C,...n)}let r=null,h=!1,S=null;function s(n){r&&r.readyState===WebSocket.OPEN&&r.send(JSON.stringify(n))}function f(){window.dispatchEvent(new CustomEvent("twd:state-change"))}async function R(n){const i=window.__TWD_STATE__;if(!i){N("TWD not initialized — make sure twd-js is loaded before running tests"),s({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const d=i.handlers;let c;if(n&&n.length>0){const e=n.map(m=>m.toLowerCase()),a=[];for(const[,m]of d)if(m.type==="test"){const y=m.name.toLowerCase();e.some(P=>y.includes(P))&&a.push(m.id)}if(a.length===0){const m=Array.from(d.values()).filter(y=>y.type==="test").map(y=>y.name);s({type:"run:start",testCount:0}),s({type:"error",code:"NO_MATCH",message:`No tests matched: ${JSON.stringify(n)}. Available tests: ${JSON.stringify(m)}`}),s({type:"run:complete",passed:0,failed:0,skipped:0,duration:0});return}c=a}s({type:"run:start",testCount:c?c.length:Array.from(d.values()).filter(e=>e.type==="test").length});let _=0,b=0,v=0;const T=performance.now(),L={onStart(e){e.status="running",f(),s({type:"test:start",id:e.id,name:e.name,suite:g(e,d)})},onPass(e){_++,e.status="pass",f(),s({type:"test:pass",id:e.id,name:e.name,suite:g(e,d),duration:performance.now()-T})},onFail(e,a){b++,e.status="fail",e.logs=[a.message],f(),s({type:"test:fail",id:e.id,name:e.name,suite:g(e,d),error:a.message,duration:performance.now()-T})},onSkip(e){v++,e.status="skip",f(),s({type:"test:skip",id:e.id,name:e.name,suite:g(e,d)})},onSuiteStart(e){e.status="running",f()},onSuiteEnd(e){e.status="idle",f()}};try{const{TestRunner:e}=await import("twd-js/runner"),a=new e(L);c?await a.runByIds(c):await a.runAll()}catch(e){const a=e instanceof Error?e.message:String(e);N("Runner error:",a),s({type:"error",code:"RUNNER_ERROR",message:a})}const $=performance.now()-T;s({type:"run:complete",passed:_,failed:b,skipped:v,duration:$}),f()}function W(){const n=window.__TWD_STATE__;if(!n){s({type:"error",code:"NO_TWD",message:"TWD not initialized"});return}const i=n.handlers,d=[];for(const[,c]of i)c.type==="test"&&d.push({id:c.id,name:c.name,suite:g(c,i),status:c.status??"idle"});s({type:"status:result",tests:d})}function k(n){let i;try{i=JSON.parse(n.data)}catch{return}i.type==="run"?(w("Received run command — running tests..."),R(Array.isArray(i.testNames)?i.testNames:void 0)):i.type==="status"&&W()}function A(){l&&!h&&(w(`Reconnecting in ${p}ms...`),S=setTimeout(()=>{E()},p))}function E(){r&&(r.readyState===WebSocket.OPEN||r.readyState===WebSocket.CONNECTING)||(h=!1,w("Connecting to",o),r=new WebSocket(o),r.addEventListener("open",()=>{s({type:"hello",role:"browser"}),w("Connected to relay — ready to receive run/status commands")}),r.addEventListener("message",k),r.addEventListener("close",n=>{if(r=null,n.reason==="Replaced by new browser"){N("Another browser instance connected — this instance will not reconnect");return}h||w("Disconnected",n.code?`(code ${n.code})`:"",n.reason||""),A()}),r.addEventListener("error",()=>{}))}function D(){h=!0,S&&(clearTimeout(S),S=null),r&&(r.close(1e3,"Client disconnecting"),r=null)}return{connect:E,disconnect:D,get connected(){return r!==null&&r.readyState===WebSocket.OPEN}}}exports.createBrowserClient=F;
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,141 +1,194 @@
1
- function O() {
2
- return `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}/__twd/ws`;
1
+ function I(i) {
2
+ return `${window.location.protocol === "https:" ? "wss:" : "ws:"}//${window.location.host}${i}`;
3
3
  }
4
- function l(s, d) {
5
- if (!s.parent) return "";
6
- const f = d.get(s.parent);
7
- return f ? f.name : "";
4
+ function m(i, p) {
5
+ if (!i.parent) return "";
6
+ const w = p.get(i.parent);
7
+ return w ? w.name : "";
8
8
  }
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) {
12
- N && console.info(h, ...n);
9
+ function P(i) {
10
+ const p = i?.url ?? I(i?.path ?? "/__twd/ws"), w = i?.reconnect ?? !0, N = i?.reconnectInterval ?? 2e3, v = i?.log ?? !1, T = "[twd-relay]";
11
+ function u(...t) {
12
+ v && console.info(T, ...t);
13
13
  }
14
- function g(...n) {
15
- console.warn(h, ...n);
14
+ function h(...t) {
15
+ console.warn(T, ...t);
16
16
  }
17
- let t = null, p = !1, m = null;
18
- function r(n) {
19
- t && t.readyState === WebSocket.OPEN && t.send(JSON.stringify(n));
17
+ let n = null, y = !1, g = null;
18
+ function r(t) {
19
+ n && n.readyState === WebSocket.OPEN && n.send(JSON.stringify(t));
20
20
  }
21
- function i() {
21
+ function l() {
22
22
  window.dispatchEvent(new CustomEvent("twd:state-change"));
23
23
  }
24
- async function C() {
25
- const n = window.__TWD_STATE__;
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" });
24
+ async function O(t) {
25
+ const o = window.__TWD_STATE__;
26
+ if (!o) {
27
+ h("TWD not initialized — make sure twd-js is loaded before running tests"), r({
28
+ type: "error",
29
+ code: "NO_TWD",
30
+ message: "TWD not initialized"
31
+ });
28
32
  return;
29
33
  }
30
- const o = n.handlers, w = Array.from(o.values()).filter((e) => e.type === "test").length;
31
- r({ type: "run:start", testCount: w });
32
- let a = 0, T = 0, _ = 0;
33
- const y = performance.now(), v = {
34
+ const c = o.handlers;
35
+ let a;
36
+ if (t && t.length > 0) {
37
+ const e = t.map((d) => d.toLowerCase()), s = [];
38
+ for (const [, d] of c) if (d.type === "test") {
39
+ const f = d.name.toLowerCase();
40
+ e.some(($) => f.includes($)) && s.push(d.id);
41
+ }
42
+ if (s.length === 0) {
43
+ const d = Array.from(c.values()).filter((f) => f.type === "test").map((f) => f.name);
44
+ r({
45
+ type: "run:start",
46
+ testCount: 0
47
+ }), r({
48
+ type: "error",
49
+ code: "NO_MATCH",
50
+ message: `No tests matched: ${JSON.stringify(t)}. Available tests: ${JSON.stringify(d)}`
51
+ }), r({
52
+ type: "run:complete",
53
+ passed: 0,
54
+ failed: 0,
55
+ skipped: 0,
56
+ duration: 0
57
+ });
58
+ return;
59
+ }
60
+ a = s;
61
+ }
62
+ r({
63
+ type: "run:start",
64
+ testCount: a ? a.length : Array.from(c.values()).filter((e) => e.type === "test").length
65
+ });
66
+ let E = 0, _ = 0, b = 0;
67
+ const S = performance.now(), D = {
34
68
  onStart(e) {
35
- e.status = "running", i(), r({
69
+ e.status = "running", l(), r({
36
70
  type: "test:start",
37
71
  id: e.id,
38
72
  name: e.name,
39
- suite: l(e, o)
73
+ suite: m(e, c)
40
74
  });
41
75
  },
42
76
  onPass(e) {
43
- a++, e.status = "pass", i(), r({
77
+ E++, e.status = "pass", l(), r({
44
78
  type: "test:pass",
45
79
  id: e.id,
46
80
  name: e.name,
47
- suite: l(e, o),
48
- duration: performance.now() - y
81
+ suite: m(e, c),
82
+ duration: performance.now() - S
49
83
  });
50
84
  },
51
- onFail(e, c) {
52
- T++, e.status = "fail", e.logs = [c.message], i(), r({
85
+ onFail(e, s) {
86
+ _++, e.status = "fail", e.logs = [s.message], l(), r({
53
87
  type: "test:fail",
54
88
  id: e.id,
55
89
  name: e.name,
56
- suite: l(e, o),
57
- error: c.message,
58
- duration: performance.now() - y
90
+ suite: m(e, c),
91
+ error: s.message,
92
+ duration: performance.now() - S
59
93
  });
60
94
  },
61
95
  onSkip(e) {
62
- _++, e.status = "skip", i(), r({
96
+ b++, e.status = "skip", l(), r({
63
97
  type: "test:skip",
64
98
  id: e.id,
65
99
  name: e.name,
66
- suite: l(e, o)
100
+ suite: m(e, c)
67
101
  });
68
102
  },
69
103
  onSuiteStart(e) {
70
- e.status = "running", i();
104
+ e.status = "running", l();
71
105
  },
72
106
  onSuiteEnd(e) {
73
- e.status = "idle", i();
107
+ e.status = "idle", l();
74
108
  }
75
109
  };
76
110
  try {
77
- const { TestRunner: e } = await import("twd-js/runner");
78
- await new e(v).runAll();
111
+ const { TestRunner: e } = await import("twd-js/runner"), s = new e(D);
112
+ a ? await s.runByIds(a) : await s.runAll();
79
113
  } catch (e) {
80
- const c = e instanceof Error ? e.message : String(e);
81
- g("Runner error:", c), r({ type: "error", code: "RUNNER_ERROR", message: c });
114
+ const s = e instanceof Error ? e.message : String(e);
115
+ h("Runner error:", s), r({
116
+ type: "error",
117
+ code: "RUNNER_ERROR",
118
+ message: s
119
+ });
82
120
  }
83
- const D = performance.now() - y;
84
- r({ type: "run:complete", passed: a, failed: T, skipped: _, duration: D }), i();
121
+ const L = performance.now() - S;
122
+ r({
123
+ type: "run:complete",
124
+ passed: E,
125
+ failed: _,
126
+ skipped: b,
127
+ duration: L
128
+ }), l();
85
129
  }
86
130
  function R() {
87
- const n = window.__TWD_STATE__;
88
- if (!n) {
89
- r({ type: "error", code: "NO_TWD", message: "TWD not initialized" });
131
+ const t = window.__TWD_STATE__;
132
+ if (!t) {
133
+ r({
134
+ type: "error",
135
+ code: "NO_TWD",
136
+ message: "TWD not initialized"
137
+ });
90
138
  return;
91
139
  }
92
- const o = n.handlers, w = [];
93
- for (const [, a] of o)
94
- a.type === "test" && w.push({
95
- id: a.id,
96
- name: a.name,
97
- suite: l(a, o),
98
- status: a.status ?? "idle"
99
- });
100
- r({ type: "status:result", tests: w });
140
+ const o = t.handlers, c = [];
141
+ for (const [, a] of o) a.type === "test" && c.push({
142
+ id: a.id,
143
+ name: a.name,
144
+ suite: m(a, o),
145
+ status: a.status ?? "idle"
146
+ });
147
+ r({
148
+ type: "status:result",
149
+ tests: c
150
+ });
101
151
  }
102
- function W(n) {
152
+ function W(t) {
103
153
  let o;
104
154
  try {
105
- o = JSON.parse(n.data);
155
+ o = JSON.parse(t.data);
106
156
  } catch {
107
157
  return;
108
158
  }
109
- o.type === "run" ? (u("Received run command — running tests..."), C()) : o.type === "status" && R();
159
+ o.type === "run" ? (u("Received run command — running tests..."), O(Array.isArray(o.testNames) ? o.testNames : void 0)) : o.type === "status" && R();
110
160
  }
111
- function b() {
112
- f && !p && (u(`Reconnecting in ${S}ms...`), m = setTimeout(() => {
113
- E();
114
- }, S));
161
+ function k() {
162
+ w && !y && (u(`Reconnecting in ${N}ms...`), g = setTimeout(() => {
163
+ C();
164
+ }, N));
115
165
  }
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");
119
- }), t.addEventListener("message", W), t.addEventListener("close", (n) => {
120
- if (t = null, n.reason === "Replaced by new browser") {
121
- g("Another browser instance connected this instance will not reconnect");
166
+ function C() {
167
+ n && (n.readyState === WebSocket.OPEN || n.readyState === WebSocket.CONNECTING) || (y = !1, u("Connecting to", p), n = new WebSocket(p), n.addEventListener("open", () => {
168
+ r({
169
+ type: "hello",
170
+ role: "browser"
171
+ }), u("Connected to relayready to receive run/status commands");
172
+ }), n.addEventListener("message", W), n.addEventListener("close", (t) => {
173
+ if (n = null, t.reason === "Replaced by new browser") {
174
+ h("Another browser instance connected — this instance will not reconnect");
122
175
  return;
123
176
  }
124
- p || u("Disconnected", n.code ? `(code ${n.code})` : "", n.reason || ""), b();
125
- }), t.addEventListener("error", () => {
177
+ y || u("Disconnected", t.code ? `(code ${t.code})` : "", t.reason || ""), k();
178
+ }), n.addEventListener("error", () => {
126
179
  }));
127
180
  }
128
- function k() {
129
- p = !0, m && (clearTimeout(m), m = null), t && (t.close(1e3, "Client disconnecting"), t = null);
181
+ function A() {
182
+ y = !0, g && (clearTimeout(g), g = null), n && (n.close(1e3, "Client disconnecting"), n = null);
130
183
  }
131
184
  return {
132
- connect: E,
133
- disconnect: k,
185
+ connect: C,
186
+ disconnect: A,
134
187
  get connected() {
135
- return t !== null && t.readyState === WebSocket.OPEN;
188
+ return n !== null && n.readyState === WebSocket.OPEN;
136
189
  }
137
190
  };
138
191
  }
139
192
  export {
140
- A as createBrowserClient
193
+ P as createBrowserClient
141
194
  };