bunite-core 0.17.1 → 0.17.2

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.
@@ -12,6 +12,7 @@ const BLOCKED_SCHEME_RE = /^(javascript|data|vbscript|file|about):/i;
12
12
  // before scheme detection. Mirror that so embedded controls (e.g. `java\nscript:`)
13
13
  // can't bypass the scheme guard.
14
14
  function normalizeForSchemeCheck(src: string): string {
15
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional, see above.
15
16
  return src.replace(/[\t\n\r]/g, "").replace(/^[\x00-\x20]+/, "");
16
17
  }
17
18
 
@@ -42,17 +43,22 @@ function definePolyfillClass(): CustomElementConstructor {
42
43
  if (!this._iframe) return false;
43
44
  try {
44
45
  return this._iframe.contentDocument != null;
45
- } catch { return false; }
46
+ } catch {
47
+ return false;
48
+ }
46
49
  }
47
50
 
48
51
  private modifierBag(mods?: string[]): {
49
- shiftKey: boolean; ctrlKey: boolean; altKey: boolean; metaKey: boolean;
52
+ shiftKey: boolean;
53
+ ctrlKey: boolean;
54
+ altKey: boolean;
55
+ metaKey: boolean;
50
56
  } {
51
57
  return {
52
58
  shiftKey: !!mods?.includes("shift"),
53
- ctrlKey: !!mods?.includes("ctrl"),
54
- altKey: !!mods?.includes("alt"),
55
- metaKey: !!mods?.includes("meta"),
59
+ ctrlKey: !!mods?.includes("ctrl"),
60
+ altKey: !!mods?.includes("alt"),
61
+ metaKey: !!mods?.includes("meta"),
56
62
  };
57
63
  }
58
64
 
@@ -60,8 +66,11 @@ function definePolyfillClass(): CustomElementConstructor {
60
66
  if (event.type === "navigate") {
61
67
  this._epoch++;
62
68
  // Avoid pushing duplicate / same-as-top entries (reload doesn't grow history).
63
- if (this._currentUrl && this._currentUrl !== event.url &&
64
- this._history[this._history.length - 1] !== this._currentUrl) {
69
+ if (
70
+ this._currentUrl &&
71
+ this._currentUrl !== event.url &&
72
+ this._history[this._history.length - 1] !== this._currentUrl
73
+ ) {
65
74
  this._history.push(this._currentUrl);
66
75
  }
67
76
  this._currentUrl = event.url;
@@ -172,20 +181,28 @@ function definePolyfillClass(): CustomElementConstructor {
172
181
  goBack() {
173
182
  // Same-origin path uses native history. Cross-origin throws → fall back
174
183
  // to our tracked stack (push on every navigate; pop here).
175
- try { this._iframe?.contentWindow?.history.back(); return; } catch {}
184
+ try {
185
+ this._iframe?.contentWindow?.history.back();
186
+ return;
187
+ } catch {}
176
188
  const prev = this._history.pop();
177
189
  if (prev && this._iframe) this._iframe.src = prev;
178
190
  }
179
191
 
180
192
  reload() {
181
- try { this._iframe?.contentWindow?.location.reload(); return; } catch {}
193
+ try {
194
+ this._iframe?.contentWindow?.location.reload();
195
+ return;
196
+ } catch {}
182
197
  // Cross-origin fallback: reassigning the same src is a no-op in WHATWG;
183
198
  // cycle via about:blank to force a fresh navigation.
184
199
  const iframe = this._iframe;
185
200
  if (!iframe) return;
186
201
  const url = this._currentUrl || iframe.src;
187
202
  iframe.src = "about:blank";
188
- requestAnimationFrame(() => { if (this._iframe) this._iframe.src = url; });
203
+ requestAnimationFrame(() => {
204
+ if (this._iframe) this._iframe.src = url;
205
+ });
189
206
  }
190
207
 
191
208
  setHidden(hidden: boolean) {
@@ -200,7 +217,11 @@ function definePolyfillClass(): CustomElementConstructor {
200
217
  // synthesised DOM events is always false → `nativeInputTrusted` stays false.
201
218
  async evaluate(script: string) {
202
219
  if (!this.isReachable()) {
203
- return { ok: false as const, code: "cross_origin" as const, message: "iframe content not same-origin" };
220
+ return {
221
+ ok: false as const,
222
+ code: "cross_origin" as const,
223
+ message: "iframe content not same-origin",
224
+ };
204
225
  }
205
226
  try {
206
227
  const win = this._iframe!.contentWindow as Window & { eval(s: string): unknown };
@@ -208,33 +229,55 @@ function definePolyfillClass(): CustomElementConstructor {
208
229
  } catch (e: unknown) {
209
230
  const err = e as { name?: string; message?: string };
210
231
  if (err?.name === "SecurityError") {
211
- return { ok: false as const, code: "cross_origin" as const, message: err.message ?? "SecurityError" };
232
+ return {
233
+ ok: false as const,
234
+ code: "cross_origin" as const,
235
+ message: err.message ?? "SecurityError",
236
+ };
212
237
  }
213
- return { ok: false as const, code: "runtime_error" as const, message: err?.message ?? String(e) };
238
+ return {
239
+ ok: false as const,
240
+ code: "runtime_error" as const,
241
+ message: err?.message ?? String(e),
242
+ };
214
243
  }
215
244
  }
216
245
 
217
246
  async capabilities() {
218
247
  const reachable = this.isReachable();
219
248
  return {
220
- evaluate: reachable, crossOriginEval: false, surfaceEvents: true,
249
+ evaluate: reachable,
250
+ crossOriginEval: false,
251
+ surfaceEvents: true,
221
252
  nativeInputTrusted: false,
222
- click: reachable, type: reachable, press: reachable, scroll: reachable,
223
- mouse: reachable, dialogs: false, console: false,
253
+ click: reachable,
254
+ type: reachable,
255
+ press: reachable,
256
+ scroll: reachable,
257
+ mouse: reachable,
258
+ dialogs: false,
259
+ console: false,
224
260
  screenshot: false,
225
261
  };
226
262
  }
227
263
 
228
264
  async sendClick(args: {
229
- x: number; y: number; button?: string; clickCount?: number; modifiers?: string[];
265
+ x: number;
266
+ y: number;
267
+ button?: string;
268
+ clickCount?: number;
269
+ modifiers?: string[];
230
270
  }) {
231
271
  if (!this.isReachable()) return;
232
272
  const doc = this._iframe!.contentDocument!;
233
273
  const target = doc.elementFromPoint(args.x, args.y) ?? doc.body;
234
274
  if (!target) return;
235
275
  const init: MouseEventInit = {
236
- bubbles: true, cancelable: true, view: this._iframe!.contentWindow,
237
- clientX: args.x, clientY: args.y,
276
+ bubbles: true,
277
+ cancelable: true,
278
+ view: this._iframe!.contentWindow,
279
+ clientX: args.x,
280
+ clientY: args.y,
238
281
  button: args.button === "right" ? 2 : args.button === "middle" ? 1 : 0,
239
282
  detail: args.clickCount ?? 1,
240
283
  ...this.modifierBag(args.modifiers),
@@ -245,31 +288,63 @@ function definePolyfillClass(): CustomElementConstructor {
245
288
  }
246
289
 
247
290
  async resolveAndClick(_selector: string, _opts?: unknown) {
248
- return { ok: false as const, code: "not_supported" as const, message: "polyfill iframe: atomic resolveAndClick not supported" };
291
+ return {
292
+ ok: false as const,
293
+ code: "not_supported" as const,
294
+ message: "polyfill iframe: atomic resolveAndClick not supported",
295
+ };
249
296
  }
250
297
 
251
298
  async getBoundingRect(selector: string, _opts?: unknown) {
252
- if (!this.isReachable()) return { ok: false as const, code: "not_supported" as const, message: "iframe not reachable" };
299
+ if (!this.isReachable())
300
+ return {
301
+ ok: false as const,
302
+ code: "not_supported" as const,
303
+ message: "iframe not reachable",
304
+ };
253
305
  try {
254
306
  const el = this._iframe!.contentDocument!.querySelector(selector) as Element | null;
255
- if (!el) return { ok: false as const, code: "not_found" as const, message: `selector ${selector} not found` };
307
+ if (!el)
308
+ return {
309
+ ok: false as const,
310
+ code: "not_found" as const,
311
+ message: `selector ${selector} not found`,
312
+ };
256
313
  const r = el.getBoundingClientRect();
257
314
  const win = this._iframe!.contentWindow!;
258
- const visible = r.width > 0 && r.height > 0 && r.bottom > 0 && r.right > 0
259
- && r.top < win.innerHeight && r.left < win.innerWidth;
260
- return { ok: true as const, rect: { x: r.x, y: r.y, width: r.width, height: r.height }, visible };
315
+ const visible =
316
+ r.width > 0 &&
317
+ r.height > 0 &&
318
+ r.bottom > 0 &&
319
+ r.right > 0 &&
320
+ r.top < win.innerHeight &&
321
+ r.left < win.innerWidth;
322
+ return {
323
+ ok: true as const,
324
+ rect: { x: r.x, y: r.y, width: r.width, height: r.height },
325
+ visible,
326
+ };
261
327
  } catch (e: any) {
262
- const code = e?.name === "SecurityError" ? "cross_origin" as const : "runtime_error" as const;
328
+ const code =
329
+ e?.name === "SecurityError" ? ("cross_origin" as const) : ("runtime_error" as const);
263
330
  return { ok: false as const, code, message: e?.message ?? String(e) };
264
331
  }
265
332
  }
266
333
 
267
334
  async listFrames() {
268
- return { ok: false as const, code: "not_supported" as const, message: "polyfill: not implemented" };
335
+ return {
336
+ ok: false as const,
337
+ code: "not_supported" as const,
338
+ message: "polyfill: not implemented",
339
+ };
269
340
  }
270
341
 
271
342
  async accessibilitySnapshot(_opts?: unknown) {
272
- return { ok: false as const, code: "not_supported" as const, message: "polyfill: not implemented" };
343
+ return {
344
+ ok: false as const,
345
+ code: "not_supported" as const,
346
+ message: "polyfill: not implemented",
347
+ };
273
348
  }
274
349
 
275
350
  async setDownloadPolicy(_policy: unknown, _dir?: unknown) {
@@ -277,11 +352,19 @@ function definePolyfillClass(): CustomElementConstructor {
277
352
  }
278
353
 
279
354
  async waitForDownload(_opts?: unknown) {
280
- return { ok: false as const, code: "not_supported" as const, message: "polyfill: not implemented" };
355
+ return {
356
+ ok: false as const,
357
+ code: "not_supported" as const,
358
+ message: "polyfill: not implemented",
359
+ };
281
360
  }
282
361
 
283
362
  async acceptPopup(_opts?: unknown) {
284
- return { ok: false as const, code: "not_found" as const, message: "polyfill: no popup orchestration" };
363
+ return {
364
+ ok: false as const,
365
+ code: "not_found" as const,
366
+ message: "polyfill: no popup orchestration",
367
+ };
285
368
  }
286
369
 
287
370
  async dismissPopup(_id?: unknown) {
@@ -289,20 +372,26 @@ function definePolyfillClass(): CustomElementConstructor {
289
372
  }
290
373
 
291
374
  async extendAdoptionTimeout(_id?: unknown, _ms?: unknown) {
292
- return { ok: false as const, code: "not_found" as const, message: "polyfill: no popup orchestration" };
375
+ return {
376
+ ok: false as const,
377
+ code: "not_found" as const,
378
+ message: "polyfill: no popup orchestration",
379
+ };
293
380
  }
294
381
 
295
382
  async sendType(text: string) {
296
383
  if (!this.isReachable()) return;
297
384
  const doc = this._iframe!.contentDocument!;
298
- const target = doc.activeElement as (HTMLInputElement | HTMLTextAreaElement | null);
385
+ const target = doc.activeElement as HTMLInputElement | HTMLTextAreaElement | null;
299
386
  if (!target || !("setRangeText" in target)) return;
300
387
  // setRangeText preserves selection + caret; the `data` field on an
301
388
  // InputEvent + bubbling lets React-style controllers detect the change.
302
389
  const start = target.selectionStart ?? target.value.length;
303
390
  const end = target.selectionEnd ?? target.value.length;
304
391
  target.setRangeText(text, start, end, "end");
305
- target.dispatchEvent(new InputEvent("input", { bubbles: true, data: text, inputType: "insertText" }));
392
+ target.dispatchEvent(
393
+ new InputEvent("input", { bubbles: true, data: text, inputType: "insertText" }),
394
+ );
306
395
  }
307
396
 
308
397
  async sendPress(key: string, modifiers?: string[], action?: "down" | "up" | "both") {
@@ -311,7 +400,10 @@ function definePolyfillClass(): CustomElementConstructor {
311
400
  const target = (doc.activeElement ?? doc.body) as Element | null;
312
401
  if (!target) return;
313
402
  const init: KeyboardEventInit = {
314
- bubbles: true, cancelable: true, key, ...this.modifierBag(modifiers),
403
+ bubbles: true,
404
+ cancelable: true,
405
+ key,
406
+ ...this.modifierBag(modifiers),
315
407
  };
316
408
  const a = action ?? "both";
317
409
  if (a !== "up") target.dispatchEvent(new KeyboardEvent("keydown", init));
@@ -319,14 +411,21 @@ function definePolyfillClass(): CustomElementConstructor {
319
411
  if (a !== "down") target.dispatchEvent(new KeyboardEvent("keyup", init));
320
412
  }
321
413
 
322
- async sendScroll(args: { dx: number; dy: number; x?: number; y?: number; modifiers?: string[] }) {
414
+ async sendScroll(args: {
415
+ dx: number;
416
+ dy: number;
417
+ x?: number;
418
+ y?: number;
419
+ modifiers?: string[];
420
+ }) {
323
421
  if (!this.isReachable()) return;
324
422
  this._iframe!.contentWindow!.scrollBy(args.dx, args.dy);
325
423
  }
326
424
 
327
425
  async sendMouse(args: {
328
426
  action: "move" | "down" | "up";
329
- x: number; y: number;
427
+ x: number;
428
+ y: number;
330
429
  button?: string;
331
430
  modifiers?: string[];
332
431
  }) {
@@ -334,10 +433,14 @@ function definePolyfillClass(): CustomElementConstructor {
334
433
  const doc = this._iframe!.contentDocument!;
335
434
  const target = doc.elementFromPoint(args.x, args.y) ?? doc.body;
336
435
  if (!target) return;
337
- const type = args.action === "move" ? "mousemove" : args.action === "down" ? "mousedown" : "mouseup";
436
+ const type =
437
+ args.action === "move" ? "mousemove" : args.action === "down" ? "mousedown" : "mouseup";
338
438
  const init: MouseEventInit = {
339
- bubbles: true, cancelable: true, view: this._iframe!.contentWindow,
340
- clientX: args.x, clientY: args.y,
439
+ bubbles: true,
440
+ cancelable: true,
441
+ view: this._iframe!.contentWindow,
442
+ clientX: args.x,
443
+ clientY: args.y,
341
444
  button: args.button === "right" ? 2 : args.button === "middle" ? 1 : 0,
342
445
  ...this.modifierBag(args.modifiers),
343
446
  };
@@ -348,11 +451,17 @@ function definePolyfillClass(): CustomElementConstructor {
348
451
  // iframe sandbox forbids cross-frame dialog interception; nothing to do.
349
452
  }
350
453
 
351
- async setDialogTimeout(_ms: number | null) { /* no-op */ }
454
+ async setDialogTimeout(_ms: number | null) {
455
+ /* no-op */
456
+ }
352
457
 
353
458
  async waitForSelector(selector: string, timeoutMs = 5000) {
354
459
  if (!this.isReachable()) {
355
- return { ok: false as const, code: "runtime_error" as const, message: "iframe content not same-origin" };
460
+ return {
461
+ ok: false as const,
462
+ code: "runtime_error" as const,
463
+ message: "iframe content not same-origin",
464
+ };
356
465
  }
357
466
  const doc = this._iframe!.contentDocument!;
358
467
  const deadline = Date.now() + timeoutMs;
@@ -360,12 +469,23 @@ function definePolyfillClass(): CustomElementConstructor {
360
469
  if (doc.querySelector(selector)) return { ok: true as const };
361
470
  await new Promise((r) => setTimeout(r, 50));
362
471
  }
363
- return { ok: false as const, code: "timeout" as const, message: `selector ${JSON.stringify(selector)} not found within ${timeoutMs}ms` };
472
+ return {
473
+ ok: false as const,
474
+ code: "timeout" as const,
475
+ message: `selector ${JSON.stringify(selector)} not found within ${timeoutMs}ms`,
476
+ };
364
477
  }
365
478
 
366
- async waitForFunction(expression: string, opts?: { timeoutMs?: number; pollIntervalMs?: number }) {
479
+ async waitForFunction(
480
+ expression: string,
481
+ opts?: { timeoutMs?: number; pollIntervalMs?: number },
482
+ ) {
367
483
  if (!this.isReachable()) {
368
- return { ok: false as const, code: "runtime_error" as const, message: "iframe content not same-origin" };
484
+ return {
485
+ ok: false as const,
486
+ code: "runtime_error" as const,
487
+ message: "iframe content not same-origin",
488
+ };
369
489
  }
370
490
  const win = this._iframe!.contentWindow as Window & { eval(s: string): unknown };
371
491
  const deadline = Date.now() + (opts?.timeoutMs ?? 5000);
@@ -374,11 +494,19 @@ function definePolyfillClass(): CustomElementConstructor {
374
494
  try {
375
495
  if (win.eval(expression)) return { ok: true as const };
376
496
  } catch (e) {
377
- return { ok: false as const, code: "runtime_error" as const, message: (e as Error).message };
497
+ return {
498
+ ok: false as const,
499
+ code: "runtime_error" as const,
500
+ message: (e as Error).message,
501
+ };
378
502
  }
379
503
  await new Promise((r) => setTimeout(r, interval));
380
504
  }
381
- return { ok: false as const, code: "timeout" as const, message: `function did not satisfy within ${opts?.timeoutMs ?? 5000}ms` };
505
+ return {
506
+ ok: false as const,
507
+ code: "timeout" as const,
508
+ message: `function did not satisfy within ${opts?.timeoutMs ?? 5000}ms`,
509
+ };
382
510
  }
383
511
 
384
512
  async getConsoleBuffer(_opts?: { clear?: boolean }) {
@@ -387,11 +515,19 @@ function definePolyfillClass(): CustomElementConstructor {
387
515
  }
388
516
 
389
517
  async getNavigationState() {
390
- return { lastLoadEpoch: this._epoch, isLoading: this._isLoading, currentUrl: this._currentUrl };
518
+ return {
519
+ lastLoadEpoch: this._epoch,
520
+ isLoading: this._isLoading,
521
+ currentUrl: this._currentUrl,
522
+ };
391
523
  }
392
524
 
393
525
  async screenshot(_args?: { format?: "png" | "jpeg"; quality?: number }) {
394
- return { ok: false as const, code: "not_supported" as const, message: "iframe polyfill does not support screenshot" };
526
+ return {
527
+ ok: false as const,
528
+ code: "not_supported" as const,
529
+ message: "iframe polyfill does not support screenshot",
530
+ };
395
531
  }
396
532
  }
397
533