haltija 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.
package/dist/client.js ADDED
@@ -0,0 +1,387 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, {
21
+ get: all[name],
22
+ enumerable: true,
23
+ configurable: true,
24
+ set: (newValue) => all[name] = () => newValue
25
+ });
26
+ };
27
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
28
+ var __require = import.meta.require;
29
+
30
+ // src/client.ts
31
+ class DevChannelClient {
32
+ baseUrl;
33
+ constructor(serverUrl = "http://localhost:8700") {
34
+ this.baseUrl = serverUrl;
35
+ }
36
+ async status() {
37
+ const res = await fetch(`${this.baseUrl}/status`);
38
+ return res.json();
39
+ }
40
+ async send(channel, action, payload) {
41
+ const res = await fetch(`${this.baseUrl}/send`, {
42
+ method: "POST",
43
+ headers: { "Content-Type": "application/json" },
44
+ body: JSON.stringify({ channel, action, payload })
45
+ });
46
+ return res.json();
47
+ }
48
+ async request(channel, action, payload, timeout = 5000) {
49
+ const res = await fetch(`${this.baseUrl}/request`, {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify({ channel, action, payload, timeout })
53
+ });
54
+ return res.json();
55
+ }
56
+ async query(selector) {
57
+ const res = await fetch(`${this.baseUrl}/query`, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify({ selector, all: false })
61
+ });
62
+ const response = await res.json();
63
+ return response.success ? response.data : null;
64
+ }
65
+ async queryAll(selector) {
66
+ const res = await fetch(`${this.baseUrl}/query`, {
67
+ method: "POST",
68
+ headers: { "Content-Type": "application/json" },
69
+ body: JSON.stringify({ selector, all: true })
70
+ });
71
+ const response = await res.json();
72
+ return response.success ? response.data : [];
73
+ }
74
+ async getConsole(since = 0) {
75
+ const res = await fetch(`${this.baseUrl}/console?since=${since}`);
76
+ const response = await res.json();
77
+ return response.success ? response.data : [];
78
+ }
79
+ async clearConsole() {
80
+ await this.request("console", "clear", {});
81
+ }
82
+ async eval(code) {
83
+ const res = await fetch(`${this.baseUrl}/eval`, {
84
+ method: "POST",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify({ code })
87
+ });
88
+ const response = await res.json();
89
+ if (!response.success) {
90
+ throw new Error(response.error || "Eval failed");
91
+ }
92
+ return response.data;
93
+ }
94
+ async click(selector, options) {
95
+ const res = await fetch(`${this.baseUrl}/click`, {
96
+ method: "POST",
97
+ headers: { "Content-Type": "application/json" },
98
+ body: JSON.stringify({ selector, options })
99
+ });
100
+ const response = await res.json();
101
+ if (!response.success) {
102
+ throw new Error(response.error || `Click failed on ${selector}`);
103
+ }
104
+ }
105
+ async type(selector, text) {
106
+ const res = await fetch(`${this.baseUrl}/type`, {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/json" },
109
+ body: JSON.stringify({ selector, text })
110
+ });
111
+ const response = await res.json();
112
+ if (!response.success) {
113
+ throw new Error(response.error || `Type failed on ${selector}`);
114
+ }
115
+ }
116
+ async dispatch(event) {
117
+ const response = await this.request("events", "dispatch", event);
118
+ if (!response.success) {
119
+ throw new Error(response.error || "Dispatch failed");
120
+ }
121
+ }
122
+ async focus(selector) {
123
+ await this.dispatch({ selector, event: "focus" });
124
+ }
125
+ async blur(selector) {
126
+ await this.dispatch({ selector, event: "blur" });
127
+ }
128
+ async press(key, modifiers) {
129
+ await this.dispatch({
130
+ selector: "body",
131
+ event: "keydown",
132
+ options: {
133
+ key,
134
+ altKey: modifiers?.alt,
135
+ ctrlKey: modifiers?.ctrl,
136
+ metaKey: modifiers?.meta,
137
+ shiftKey: modifiers?.shift
138
+ }
139
+ });
140
+ }
141
+ async refresh(hard = false) {
142
+ const res = await fetch(`${this.baseUrl}/refresh`, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify({ hard })
146
+ });
147
+ const response = await res.json();
148
+ if (!response.success) {
149
+ throw new Error(response.error || "Refresh failed");
150
+ }
151
+ }
152
+ async navigate(url) {
153
+ const res = await fetch(`${this.baseUrl}/navigate`, {
154
+ method: "POST",
155
+ headers: { "Content-Type": "application/json" },
156
+ body: JSON.stringify({ url })
157
+ });
158
+ const response = await res.json();
159
+ if (!response.success) {
160
+ throw new Error(response.error || "Navigate failed");
161
+ }
162
+ }
163
+ async getLocation() {
164
+ const res = await fetch(`${this.baseUrl}/location`);
165
+ const response = await res.json();
166
+ if (!response.success) {
167
+ throw new Error(response.error || "Get location failed");
168
+ }
169
+ return response.data;
170
+ }
171
+ async startRecording(name) {
172
+ const res = await fetch(`${this.baseUrl}/recording/start`, {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/json" },
175
+ body: JSON.stringify({ name })
176
+ });
177
+ const response = await res.json();
178
+ if (!response.success) {
179
+ throw new Error(response.error || "Start recording failed");
180
+ }
181
+ return response.data.sessionId;
182
+ }
183
+ async stopRecording() {
184
+ const res = await fetch(`${this.baseUrl}/recording/stop`, {
185
+ method: "POST"
186
+ });
187
+ const response = await res.json();
188
+ if (!response.success) {
189
+ throw new Error(response.error || "Stop recording failed");
190
+ }
191
+ return response.data;
192
+ }
193
+ async replayRecording(session, speed = 1) {
194
+ const response = await this.request("recording", "replay", { session, speed });
195
+ if (!response.success) {
196
+ throw new Error(response.error || "Replay failed");
197
+ }
198
+ }
199
+ async publishBuild(event) {
200
+ await fetch(`${this.baseUrl}/build`, {
201
+ method: "POST",
202
+ headers: { "Content-Type": "application/json" },
203
+ body: JSON.stringify(event)
204
+ });
205
+ }
206
+ async watchEvents(options) {
207
+ const response = await this.request("events", "watch", options);
208
+ if (!response.success) {
209
+ throw new Error(response.error || "Watch failed");
210
+ }
211
+ return response.data.watchId;
212
+ }
213
+ async unwatchEvents(watchId) {
214
+ const response = await this.request("events", "unwatch", { watchId });
215
+ if (!response.success) {
216
+ throw new Error(response.error || "Unwatch failed");
217
+ }
218
+ }
219
+ async watchMutations(options) {
220
+ const res = await fetch(`${this.baseUrl}/mutations/watch`, {
221
+ method: "POST",
222
+ headers: { "Content-Type": "application/json" },
223
+ body: JSON.stringify(options || {})
224
+ });
225
+ const response = await res.json();
226
+ if (!response.success) {
227
+ throw new Error(response.error || "Watch mutations failed");
228
+ }
229
+ }
230
+ async unwatchMutations() {
231
+ const res = await fetch(`${this.baseUrl}/mutations/unwatch`, {
232
+ method: "POST"
233
+ });
234
+ const response = await res.json();
235
+ if (!response.success) {
236
+ throw new Error(response.error || "Unwatch mutations failed");
237
+ }
238
+ }
239
+ async getMutationStatus() {
240
+ const res = await fetch(`${this.baseUrl}/mutations/status`);
241
+ const response = await res.json();
242
+ if (!response.success) {
243
+ throw new Error(response.error || "Get mutation status failed");
244
+ }
245
+ return response.data;
246
+ }
247
+ async getMessages(since = 0) {
248
+ const res = await fetch(`${this.baseUrl}/messages?since=${since}`);
249
+ return res.json();
250
+ }
251
+ async getInteractiveElements() {
252
+ const [buttons, links, inputs] = await Promise.all([
253
+ this.queryAll("button"),
254
+ this.queryAll("a[href]"),
255
+ this.queryAll("input, textarea, select")
256
+ ]);
257
+ return {
258
+ buttons: buttons.map((el) => ({
259
+ selector: el.id ? `#${el.id}` : `button`,
260
+ text: el.innerText?.slice(0, 50) || ""
261
+ })),
262
+ links: links.map((el) => ({
263
+ selector: el.id ? `#${el.id}` : `a[href="${el.attributes?.href}"]`,
264
+ text: el.innerText?.slice(0, 50) || "",
265
+ href: el.attributes?.href || ""
266
+ })),
267
+ inputs: inputs.map((el) => ({
268
+ selector: el.id ? `#${el.id}` : `${el.tagName}[name="${el.attributes?.name}"]`,
269
+ type: el.attributes?.type,
270
+ name: el.attributes?.name,
271
+ placeholder: el.attributes?.placeholder
272
+ }))
273
+ };
274
+ }
275
+ async getCustomElements() {
276
+ return this.eval(`
277
+ Array.from(document.querySelectorAll('*'))
278
+ .filter(el => el.tagName.includes('-'))
279
+ .reduce((acc, el) => {
280
+ const tag = el.tagName.toLowerCase()
281
+ if (!acc[tag]) acc[tag] = { count: 0, examples: [] }
282
+ acc[tag].count++
283
+ if (acc[tag].examples.length < 3) {
284
+ acc[tag].examples.push({
285
+ id: el.id || null,
286
+ classes: el.className?.split?.(' ')?.slice(0, 3) || [],
287
+ text: el.textContent?.slice(0, 50)
288
+ })
289
+ }
290
+ return acc
291
+ }, {})
292
+ `);
293
+ }
294
+ async doAndWait(action, options) {
295
+ const timeout = options?.timeout || 2000;
296
+ const debounce = options?.debounce || 100;
297
+ await this.watchMutations({ debounce });
298
+ const startTime = Date.now();
299
+ await action();
300
+ let mutations = [];
301
+ let lastMutationTime = Date.now();
302
+ while (Date.now() - startTime < timeout) {
303
+ const messages = await this.getMessages(startTime);
304
+ const newMutations = messages.filter((m) => m.channel === "mutations" && m.action === "batch");
305
+ if (newMutations.length > mutations.length) {
306
+ mutations = newMutations;
307
+ lastMutationTime = Date.now();
308
+ } else if (mutations.length > 0 && Date.now() - lastMutationTime > debounce * 2) {
309
+ break;
310
+ }
311
+ await new Promise((r) => setTimeout(r, 50));
312
+ }
313
+ await this.unwatchMutations();
314
+ return {
315
+ mutations,
316
+ duration: Date.now() - startTime
317
+ };
318
+ }
319
+ async validateTest(test) {
320
+ const res = await fetch(`${this.baseUrl}/test/validate`, {
321
+ method: "POST",
322
+ headers: { "Content-Type": "application/json" },
323
+ body: JSON.stringify(test)
324
+ });
325
+ return res.json();
326
+ }
327
+ async runTest(test, options) {
328
+ const res = await fetch(`${this.baseUrl}/test/run`, {
329
+ method: "POST",
330
+ headers: { "Content-Type": "application/json" },
331
+ body: JSON.stringify({ test, ...options })
332
+ });
333
+ const result = await res.json();
334
+ return {
335
+ test,
336
+ passed: result.passed,
337
+ startTime: Date.now() - result.duration,
338
+ endTime: Date.now(),
339
+ steps: result.steps,
340
+ error: result.steps.find((s) => !s.passed)?.error
341
+ };
342
+ }
343
+ async runTestSuite(tests, options) {
344
+ const res = await fetch(`${this.baseUrl}/test/suite`, {
345
+ method: "POST",
346
+ headers: { "Content-Type": "application/json" },
347
+ body: JSON.stringify({ tests, ...options })
348
+ });
349
+ return res.json();
350
+ }
351
+ async loadTest(source) {
352
+ if (source.startsWith("http://") || source.startsWith("https://")) {
353
+ const res = await fetch(source);
354
+ return res.json();
355
+ }
356
+ return JSON.parse(source);
357
+ }
358
+ formatTestResult(result) {
359
+ const lines = [];
360
+ const icon = result.passed ? "\u2713" : "\u2717";
361
+ const status = result.passed ? "PASSED" : "FAILED";
362
+ lines.push(`${icon} ${result.test.name} - ${status}`);
363
+ lines.push(` Duration: ${result.endTime - result.startTime}ms`);
364
+ lines.push("");
365
+ for (const step of result.steps) {
366
+ const stepIcon = step.passed ? " \u2713" : " \u2717";
367
+ const desc = step.description || `Step ${step.index + 1}`;
368
+ lines.push(`${stepIcon} ${desc}`);
369
+ if (!step.passed && step.error) {
370
+ lines.push(` Error: ${step.error}`);
371
+ if (step.purpose) {
372
+ lines.push(` Purpose: ${step.purpose}`);
373
+ }
374
+ if (step.context) {
375
+ lines.push(` Context: ${JSON.stringify(step.context)}`);
376
+ }
377
+ }
378
+ }
379
+ return lines.join(`
380
+ `);
381
+ }
382
+ }
383
+ var devChannel = new DevChannelClient;
384
+ export {
385
+ devChannel,
386
+ DevChannelClient
387
+ };