capybara-simulated 0.2.0 → 0.4.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.
@@ -417,7 +417,7 @@
417
417
  this._currentTarget = v;
418
418
  }
419
419
  });
420
- var DOMException = class _DOMException extends Error {
420
+ var DOMException2 = class _DOMException extends Error {
421
421
  static {
422
422
  __name(this, "DOMException");
423
423
  }
@@ -480,8 +480,8 @@
480
480
  INVALID_NODE_TYPE_ERR: 24,
481
481
  DATA_CLONE_ERR: 25
482
482
  }).forEach(([k, v]) => {
483
- Object.defineProperty(DOMException, k, { value: v, enumerable: true });
484
- Object.defineProperty(DOMException.prototype, k, { value: v, enumerable: true });
483
+ Object.defineProperty(DOMException2, k, { value: v, enumerable: true });
484
+ Object.defineProperty(DOMException2.prototype, k, { value: v, enumerable: true });
485
485
  });
486
486
  var CustomEvent = class extends Event {
487
487
  static {
@@ -1798,7 +1798,7 @@
1798
1798
  globalThis.__isVisibleNode = isVisibleNode;
1799
1799
  globalThis.__isLaidOutNode = isLaidOutNode;
1800
1800
  function selfHidden(el, ignoreVisibility = false) {
1801
- if (el._attrs.hidden != null) return true;
1801
+ const hidden = el._attrs.hidden != null;
1802
1802
  if (el._tag === "dialog" && el._attrs.open == null) return true;
1803
1803
  const inline = inlineHideDecl(el);
1804
1804
  if (inline && !state.hasImportantHideRule) {
@@ -1808,7 +1808,7 @@
1808
1808
  const visibilitySettled = ignoreVisibility || inline.visibility != null;
1809
1809
  if (displaySettled && visibilitySettled) return false;
1810
1810
  }
1811
- return matchesAnyHideRule(el, ignoreVisibility, inline);
1811
+ return matchesAnyHideRule(el, ignoreVisibility, inline, hidden);
1812
1812
  }
1813
1813
  __name(selfHidden, "selfHidden");
1814
1814
  function inlineHideDecl(el) {
@@ -2297,13 +2297,22 @@
2297
2297
  }
2298
2298
  __name(forEachCandidateRule, "forEachCandidateRule");
2299
2299
  var LAYOUT_PROPS = [...CAPTURED_PROPS, "text-transform", "white-space"];
2300
+ var IMPORTANT_RE = /\s*!\s*important\s*$/i;
2301
+ function splitImportant(value) {
2302
+ if (typeof value !== "string" || value.indexOf("!") < 0) return { value, important: false };
2303
+ return IMPORTANT_RE.test(value) ? { value: value.replace(IMPORTANT_RE, "").trim(), important: true } : { value, important: false };
2304
+ }
2305
+ __name(splitImportant, "splitImportant");
2300
2306
  function parseInlineLayout(el) {
2301
2307
  const out = {};
2302
2308
  const s = el._attrs && el._attrs.style;
2303
2309
  if (!s) return out;
2304
2310
  for (const part of String(s).split(";")) {
2305
2311
  const m = /^\s*(top|left|width|height)\s*:\s*([^;]+?)\s*$/.exec(part);
2306
- if (m) out[m[1]] = { value: m[2], important: /\s+!important\s*$/.test(m[2]) };
2312
+ if (m) {
2313
+ const d = splitImportant(m[2]);
2314
+ out[m[1]] = { value: d.value, important: d.important };
2315
+ }
2307
2316
  }
2308
2317
  return out;
2309
2318
  }
@@ -2343,8 +2352,8 @@
2343
2352
  return candSource >= current.source;
2344
2353
  }
2345
2354
  __name(winsProp, "winsProp");
2346
- function matchesAnyHideRule(el, ignoreVisibility = false, inline = null) {
2347
- if (state.hideRules.length === 0 && !inline) return false;
2355
+ function matchesAnyHideRule(el, ignoreVisibility = false, inline = null, hidden = false) {
2356
+ if (state.hideRules.length === 0 && !inline) return hidden;
2348
2357
  let bestD = null, bestV = null;
2349
2358
  if (inline) {
2350
2359
  if (inline.display != null) {
@@ -2367,6 +2376,7 @@
2367
2376
  });
2368
2377
  }
2369
2378
  if (bestD && bestD.value === "none") return true;
2379
+ if (hidden && bestD == null) return true;
2370
2380
  if (bestV && (bestV.value === "hidden" || bestV.value === "collapse")) return true;
2371
2381
  return false;
2372
2382
  }
@@ -2523,11 +2533,11 @@
2523
2533
  }
2524
2534
  __name(cascadedProperty, "cascadedProperty");
2525
2535
  function parseInlinePropertyValue(style, prop) {
2526
- const re = new RegExp("(?:^|;)\\s*" + prop + "\\s*:\\s*([^;!]+?)\\s*(?:!important)?\\s*(?:;|$)", "i");
2536
+ const re = new RegExp("(?:^|;)\\s*" + prop + "\\s*:\\s*([^;!]+?)\\s*(!\\s*important)?\\s*(?:;|$)", "i");
2527
2537
  const m = re.exec(String(style));
2528
2538
  if (!m) return null;
2529
2539
  const lower = prop === "display" || prop === "visibility" || prop === "text-transform" || prop === "white-space";
2530
- return { value: lower ? m[1].toLowerCase() : m[1], important: /!important/i.test(style) };
2540
+ return { value: lower ? m[1].toLowerCase() : m[1], important: m[2] != null };
2531
2541
  }
2532
2542
  __name(parseInlinePropertyValue, "parseInlinePropertyValue");
2533
2543
  function rulesIndexHas(prop) {
@@ -4532,6 +4542,18 @@
4532
4542
  return globalThis.__csim_transferStash(view) | 0;
4533
4543
  }
4534
4544
  __name(stashTransfer, "stashTransfer");
4545
+ function detachTransferables(transferList) {
4546
+ if (!Array.isArray(transferList)) return;
4547
+ for (const t of transferList) {
4548
+ if (t instanceof ArrayBuffer && typeof t.transfer === "function") {
4549
+ try {
4550
+ t.transfer();
4551
+ } catch (_) {
4552
+ }
4553
+ }
4554
+ }
4555
+ }
4556
+ __name(detachTransferables, "detachTransferables");
4535
4557
 
4536
4558
  // lib/capybara/simulated/js/src/workers.js
4537
4559
  function hasWorkers() {
@@ -4540,28 +4562,51 @@
4540
4562
  }
4541
4563
  __name(hasWorkers, "hasWorkers");
4542
4564
  var TRANSFER_STASH_MIN = 64 * 1024;
4543
- function encode(data) {
4565
+ function transferSetFrom(transferList) {
4566
+ if (!transferList || !transferList.length) return null;
4567
+ const set = /* @__PURE__ */ new Set();
4568
+ for (const t of transferList) set.add(t instanceof ArrayBuffer ? t : t && t.buffer || t);
4569
+ return set;
4570
+ }
4571
+ __name(transferSetFrom, "transferSetFrom");
4572
+ function encode(data, transferSet) {
4573
+ const NS = globalThis.RustyRacer;
4574
+ const canTransfer = transferSet && NS && typeof NS.transferOut === "function";
4544
4575
  return JSON.stringify(data, function(_key, value) {
4545
4576
  const isU8 = value instanceof Uint8Array;
4546
4577
  const isAB = !isU8 && value instanceof ArrayBuffer;
4547
4578
  if (!isU8 && !isAB) return value;
4579
+ const type = isU8 ? "Uint8Array" : "ArrayBuffer";
4580
+ const buf = isU8 ? value.buffer : value;
4581
+ if (canTransfer && transferSet.has(buf)) {
4582
+ const token = NS.transferOut(value) | 0;
4583
+ if (token > 0) {
4584
+ if (globalThis.__csim_transferIssued) globalThis.__csim_transferIssued(token);
4585
+ return isU8 ? { __csimType: type, xfer: token, byteOffset: value.byteOffset, length: value.length } : { __csimType: type, xfer: token };
4586
+ }
4587
+ }
4548
4588
  const view = isU8 ? value : new Uint8Array(value);
4549
4589
  if (view.byteLength >= TRANSFER_STASH_MIN) {
4550
4590
  const refId = stashTransfer(view);
4551
- if (refId > 0) return { __csimType: isU8 ? "Uint8Array" : "ArrayBuffer", refId };
4591
+ if (refId > 0) return { __csimType: type, refId };
4552
4592
  }
4553
- return {
4554
- __csimType: isU8 ? "Uint8Array" : "ArrayBuffer",
4555
- b64: globalThis.btoa(bytesToLatin1(view))
4556
- };
4593
+ return { __csimType: type, b64: globalThis.btoa(bytesToLatin1(view)) };
4557
4594
  });
4558
4595
  }
4559
4596
  __name(encode, "encode");
4560
4597
  function decode(s) {
4598
+ const NS = globalThis.RustyRacer;
4561
4599
  return JSON.parse(s, function(_key, value) {
4562
4600
  if (!value || typeof value !== "object") return value;
4563
4601
  const tag = value.__csimType;
4564
4602
  if (tag !== "Uint8Array" && tag !== "ArrayBuffer") return value;
4603
+ if (value.xfer != null && NS && typeof NS.transferIn === "function") {
4604
+ const ab = NS.transferIn(value.xfer);
4605
+ if (ab) {
4606
+ return tag === "ArrayBuffer" ? ab : new Uint8Array(ab, value.byteOffset || 0, value.length != null ? value.length : ab.byteLength);
4607
+ }
4608
+ return tag === "ArrayBuffer" ? new ArrayBuffer(0) : new Uint8Array(0);
4609
+ }
4565
4610
  const u = fetchTransfer(value.refId) || latin1ToBytes(globalThis.atob(value.b64 || ""));
4566
4611
  return tag === "ArrayBuffer" ? u.buffer : u;
4567
4612
  });
@@ -4585,15 +4630,16 @@
4585
4630
  this._handle = globalThis.__csim_workerSpawn(this.url) | 0;
4586
4631
  if (this._handle > 0) byHandle.set(this._handle, this);
4587
4632
  }
4588
- postMessage(data, _transferList) {
4633
+ postMessage(data, transferList) {
4589
4634
  if (this._handle <= 0) return;
4590
4635
  let payload;
4591
4636
  try {
4592
- payload = encode(data);
4637
+ payload = encode(data, transferSetFrom(transferList));
4593
4638
  } catch (_) {
4594
4639
  payload = "null";
4595
4640
  }
4596
4641
  globalThis.__csim_workerPostToWorker(this._handle, payload);
4642
+ detachTransferables(transferList);
4597
4643
  }
4598
4644
  terminate() {
4599
4645
  if (this._handle <= 0) return;
@@ -4658,14 +4704,15 @@
4658
4704
  globalThis.DedicatedWorkerGlobalScope = globalThis.DedicatedWorkerGlobalScope || /* @__PURE__ */ __name(function DedicatedWorkerGlobalScope() {
4659
4705
  }, "DedicatedWorkerGlobalScope");
4660
4706
  if (typeof globalThis.postMessage !== "function") {
4661
- globalThis.postMessage = function(data, _transferList) {
4707
+ globalThis.postMessage = function(data, transferList) {
4662
4708
  let payload;
4663
4709
  try {
4664
- payload = encode(data);
4710
+ payload = encode(data, transferSetFrom(transferList));
4665
4711
  } catch (_) {
4666
4712
  payload = "null";
4667
4713
  }
4668
4714
  globalThis.__csim_workerPostMessage(payload);
4715
+ detachTransferables(transferList);
4669
4716
  };
4670
4717
  }
4671
4718
  globalThis.importScripts = function(...urls) {
@@ -4780,6 +4827,10 @@
4780
4827
  this.lastModified = i.lastModified || Date.now();
4781
4828
  }
4782
4829
  };
4830
+ function utf8Latin1(s) {
4831
+ return bytesToLatin1(new globalThis.TextEncoder().encode(String(s)));
4832
+ }
4833
+ __name(utf8Latin1, "utf8Latin1");
4783
4834
  function serializeMultipart(formData) {
4784
4835
  const boundary = "----csimFormBoundary" + Math.random().toString(36).slice(2);
4785
4836
  let body = "";
@@ -4788,13 +4839,13 @@
4788
4839
  if (value instanceof Blob) {
4789
4840
  const filename = value.name != null ? String(value.name) : "blob";
4790
4841
  const contentType = value.type || "application/octet-stream";
4791
- body += 'Content-Disposition: form-data; name="' + key + '"; filename="' + filename + '"\r\n';
4842
+ body += 'Content-Disposition: form-data; name="' + utf8Latin1(key) + '"; filename="' + utf8Latin1(filename) + '"\r\n';
4792
4843
  body += "Content-Type: " + contentType + "\r\n\r\n";
4793
4844
  body += blobBytes(value);
4794
4845
  body += "\r\n";
4795
4846
  } else {
4796
- body += 'Content-Disposition: form-data; name="' + key + '"\r\n\r\n';
4797
- body += String(value) + "\r\n";
4847
+ body += 'Content-Disposition: form-data; name="' + utf8Latin1(key) + '"\r\n\r\n';
4848
+ body += utf8Latin1(value) + "\r\n";
4798
4849
  }
4799
4850
  });
4800
4851
  body += "--" + boundary + "--\r\n";
@@ -5450,7 +5501,8 @@
5450
5501
  get(_t, prop) {
5451
5502
  if (prop === "cssText") return el._attrs.style || "";
5452
5503
  if (prop === "getPropertyValue") return (name) => readCssProp(el, String(name));
5453
- if (prop === "setProperty") return (n, v) => writeCssProp(el, String(n), String(v));
5504
+ if (prop === "getPropertyPriority") return (name) => readCssPriority(el, String(name));
5505
+ if (prop === "setProperty") return (n, v, priority) => writeCssProp(el, String(n), String(v), priority);
5454
5506
  if (prop === "removeProperty") return (name) => removeCssProp(el, String(name));
5455
5507
  if (prop === "length") return Object.keys(parsedDecls(el)).length;
5456
5508
  if (typeof prop !== "string") return void 0;
@@ -5467,7 +5519,7 @@
5467
5519
  return true;
5468
5520
  },
5469
5521
  has(_t, prop) {
5470
- if (prop === "cssText" || prop === "getPropertyValue" || prop === "setProperty" || prop === "removeProperty" || prop === "length") return true;
5522
+ if (prop === "cssText" || prop === "getPropertyValue" || prop === "getPropertyPriority" || prop === "setProperty" || prop === "removeProperty" || prop === "length") return true;
5471
5523
  return readCssProp(el, camelToKebab(String(prop))) !== "";
5472
5524
  }
5473
5525
  };
@@ -5487,17 +5539,34 @@
5487
5539
  return el._declCache;
5488
5540
  }
5489
5541
  __name(parsedDecls, "parsedDecls");
5542
+ var IMPORTANT_SUFFIX_RE = /\s*!\s*important\s*$/i;
5543
+ function stripImportant(v) {
5544
+ if (typeof v !== "string" || v.indexOf("!") < 0) return v;
5545
+ return v.replace(IMPORTANT_SUFFIX_RE, "").trim();
5546
+ }
5547
+ __name(stripImportant, "stripImportant");
5490
5548
  function readCssProp(el, name) {
5491
5549
  const decls = parsedDecls(el);
5492
- return decls[name] != null ? decls[name] : "";
5550
+ return decls[name] != null ? stripImportant(decls[name]) : "";
5493
5551
  }
5494
5552
  __name(readCssProp, "readCssProp");
5495
- function writeCssProp(el, name, value) {
5553
+ function readCssPriority(el, name) {
5554
+ const v = parsedDecls(el)[name];
5555
+ return v != null && splitImportant(v).important ? "important" : "";
5556
+ }
5557
+ __name(readCssPriority, "readCssPriority");
5558
+ function writeCssProp(el, name, value, priority) {
5496
5559
  const decls = parseStyleDecls(el._attrs.style || "");
5497
5560
  if (value === "" || value == null) {
5498
5561
  delete decls[name];
5499
5562
  } else {
5500
- decls[name] = String(value);
5563
+ let v = String(value);
5564
+ if (/^\s*important\s*$/i.test(String(priority == null ? "" : priority))) {
5565
+ v = stripImportant(v) + " !important";
5566
+ } else if (priority != null && priority !== "") {
5567
+ return;
5568
+ }
5569
+ decls[name] = v;
5501
5570
  }
5502
5571
  el.setAttribute("style", serializeStyleDecls(decls));
5503
5572
  }
@@ -5620,10 +5689,9 @@
5620
5689
  const inlineStyle = el._attrs.style;
5621
5690
  if (inlineStyle) {
5622
5691
  const m = /(^|;|\s)display\s*:\s*([^;]+)/i.exec(inlineStyle);
5623
- if (m) return m[2].trim();
5692
+ if (m) return stripImportant(m[2].trim());
5624
5693
  }
5625
- if (el._attrs.hidden != null) return "none";
5626
- if (matchesAnyHideRule(el)) return "none";
5694
+ if (matchesAnyHideRule(el, true, null, el._attrs.hidden != null)) return "none";
5627
5695
  return DEFAULT_DISPLAY[el._tag] || "block";
5628
5696
  }
5629
5697
  __name(computedDisplayFor, "computedDisplayFor");
@@ -5631,7 +5699,7 @@
5631
5699
  const inlineStyle = el._attrs.style;
5632
5700
  if (inlineStyle) {
5633
5701
  const m = /(^|;|\s)visibility\s*:\s*([^;]+)/i.exec(inlineStyle);
5634
- if (m) return m[2].trim();
5702
+ if (m) return stripImportant(m[2].trim());
5635
5703
  }
5636
5704
  return "";
5637
5705
  }
@@ -10030,6 +10098,15 @@
10030
10098
  };
10031
10099
  return list;
10032
10100
  }
10101
+ // HTML lets you assign a `FileList` to a file input programmatically — the
10102
+ // canonical pattern is `input.files = dataTransfer.files` (drag-drop libraries,
10103
+ // and the kamalog `attach-images` Stimulus controller, do exactly this). Accept
10104
+ // any array-like of File objects; null/undefined clears the selection.
10105
+ set files(value) {
10106
+ if (this._tag !== "input") return;
10107
+ if ((this._attrs.type || "").toLowerCase() !== "file") return;
10108
+ this._files = value == null ? [] : Array.from(value);
10109
+ }
10033
10110
  get innerHTML() {
10034
10111
  return serializeChildren(this);
10035
10112
  }
@@ -10843,8 +10920,8 @@
10843
10920
  function windowNamedLookup(name) {
10844
10921
  const doc = globalThis.document;
10845
10922
  if (!doc) return void 0;
10846
- const byId2 = doc.getElementById && doc.getElementById(name);
10847
- if (byId2) return byId2;
10923
+ const byId3 = doc.getElementById && doc.getElementById(name);
10924
+ if (byId3) return byId3;
10848
10925
  if (!WINDOW_NAME_VALUES.has(name)) return void 0;
10849
10926
  let found;
10850
10927
  walkSubtree(doc, (el) => {
@@ -13162,7 +13239,7 @@
13162
13239
 
13163
13240
  // lib/capybara/simulated/js/src/abort.js
13164
13241
  function defaultAbortReason() {
13165
- return new DOMException("signal is aborted without reason", "AbortError");
13242
+ return new DOMException2("signal is aborted without reason", "AbortError");
13166
13243
  }
13167
13244
  __name(defaultAbortReason, "defaultAbortReason");
13168
13245
  var AbortSignal = class _AbortSignal extends EventTarget {
@@ -13194,7 +13271,7 @@
13194
13271
  // AbortSignal.timeout(ms)}` is the canonical timeout idiom.
13195
13272
  static timeout(ms) {
13196
13273
  const s = new _AbortSignal();
13197
- globalThis.setTimeout(() => s._markAborted(new DOMException("signal timed out", "TimeoutError")), Number(ms) || 0);
13274
+ globalThis.setTimeout(() => s._markAborted(new DOMException2("signal timed out", "TimeoutError")), Number(ms) || 0);
13198
13275
  return s;
13199
13276
  }
13200
13277
  // Spec: returns a signal that aborts when any input signal aborts.
@@ -14137,6 +14214,113 @@
14137
14214
  };
14138
14215
  globalThis.EventSource = EventSource;
14139
14216
 
14217
+ // lib/capybara/simulated/js/src/websocket.js
14218
+ var byId2 = /* @__PURE__ */ new Map();
14219
+ globalThis.__csim_webSocketById = byId2;
14220
+ var WebSocket = class extends EventTarget {
14221
+ static {
14222
+ __name(this, "WebSocket");
14223
+ }
14224
+ constructor(url, protocols) {
14225
+ super();
14226
+ this.url = String(url);
14227
+ this.readyState = 0;
14228
+ this.bufferedAmount = 0;
14229
+ this.extensions = "";
14230
+ this.protocol = "";
14231
+ this.binaryType = "blob";
14232
+ this.onopen = null;
14233
+ this.onmessage = null;
14234
+ this.onclose = null;
14235
+ this.onerror = null;
14236
+ const list = protocols == null ? [] : Array.isArray(protocols) ? protocols.map(String) : [String(protocols)];
14237
+ this._id = globalThis.__csim_wsOpen(this.url, list) | 0;
14238
+ if (this._id > 0) byId2.set(this._id, this);
14239
+ }
14240
+ send(data) {
14241
+ if (this.readyState === 0) throw new DOMException("Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.", "InvalidStateError");
14242
+ if (this.readyState !== 1) return;
14243
+ if (typeof data === "string") {
14244
+ globalThis.__csim_wsSend(this._id, data, false, false);
14245
+ return;
14246
+ }
14247
+ let bytes;
14248
+ if (data instanceof ArrayBuffer) bytes = new Uint8Array(data);
14249
+ else if (ArrayBuffer.isView(data)) bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
14250
+ else bytes = new Uint8Array(0);
14251
+ if (globalThis.RustyRacer) {
14252
+ globalThis.__csim_wsSend(this._id, bytes, true, false);
14253
+ } else {
14254
+ globalThis.__csim_wsSend(this._id, globalThis.btoa(bytesToLatin1(bytes)), true, true);
14255
+ }
14256
+ }
14257
+ close(code, reason) {
14258
+ if (this.readyState === 2 || this.readyState === 3) return;
14259
+ this.readyState = 2;
14260
+ if (this._id > 0) globalThis.__csim_wsClose(this._id, code == null ? 1e3 : code | 0, reason == null ? "" : String(reason));
14261
+ }
14262
+ };
14263
+ WebSocket.CONNECTING = 0;
14264
+ WebSocket.OPEN = 1;
14265
+ WebSocket.CLOSING = 2;
14266
+ WebSocket.CLOSED = 3;
14267
+ globalThis.__csim_deliverWebSocketEvents = function(events) {
14268
+ if (!events || !events.length) return 0;
14269
+ let delivered = 0;
14270
+ for (const e of events) {
14271
+ const ws = byId2.get(e.id | 0);
14272
+ if (!ws) continue;
14273
+ if (e.type === "__open") {
14274
+ if (ws.readyState === 0) {
14275
+ ws.readyState = 1;
14276
+ if (e.protocol) ws.protocol = String(e.protocol);
14277
+ dispatchWithOnHandler(ws, new Event("open"));
14278
+ delivered++;
14279
+ }
14280
+ continue;
14281
+ }
14282
+ if (e.type === "__close" || e.type === "__error") {
14283
+ if (e.type === "__error") {
14284
+ const err = new Event("error");
14285
+ if (e.message) try {
14286
+ err.message = String(e.message);
14287
+ } catch (_) {
14288
+ }
14289
+ dispatchWithOnHandler(ws, err);
14290
+ }
14291
+ if (ws.readyState !== 3) {
14292
+ ws.readyState = 3;
14293
+ const ev = new Event("close");
14294
+ try {
14295
+ ev.code = e.code == null ? 1006 : e.code | 0;
14296
+ ev.reason = e.reason == null ? "" : String(e.reason);
14297
+ ev.wasClean = e.type === "__close" && e.code != null;
14298
+ } catch (_) {
14299
+ }
14300
+ dispatchWithOnHandler(ws, ev);
14301
+ }
14302
+ byId2.delete(e.id | 0);
14303
+ delivered++;
14304
+ continue;
14305
+ }
14306
+ let data;
14307
+ if (e.binary) {
14308
+ const bytes = fetchedToBytes(e.data) || new Uint8Array(0);
14309
+ if (ws.binaryType === "arraybuffer") {
14310
+ data = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes.buffer : bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
14311
+ } else {
14312
+ data = typeof globalThis.Blob === "function" ? new globalThis.Blob([bytes]) : bytes.buffer;
14313
+ }
14314
+ } else {
14315
+ data = e.data == null ? "" : e.data;
14316
+ }
14317
+ dispatchWithOnHandler(ws, new MessageEvent("message", { data, origin: ws.url }));
14318
+ delivered++;
14319
+ }
14320
+ return delivered;
14321
+ };
14322
+ globalThis.WebSocket = WebSocket;
14323
+
14140
14324
  // lib/capybara/simulated/js/src/request-body.js
14141
14325
  function setContentTypeIfMissing(headers, value) {
14142
14326
  if (!("Content-Type" in headers) && !("content-type" in headers)) {
@@ -14518,6 +14702,18 @@
14518
14702
  if (spec && Array.isArray(spec.fields)) {
14519
14703
  for (const pair of spec.fields) this._entries.push([String(pair[0]), String(pair[1])]);
14520
14704
  }
14705
+ if (spec && Array.isArray(spec.fileInputs)) {
14706
+ const File2 = globalThis.File;
14707
+ for (const fi of spec.fileInputs) {
14708
+ const el = lookup(fi.handle);
14709
+ const files = el && el.files;
14710
+ if (files && files.length) {
14711
+ for (const f of files) this._entries.push([String(fi.name), f]);
14712
+ } else if (File2) {
14713
+ this._entries.push([String(fi.name), new File2([], "", { type: "application/octet-stream" })]);
14714
+ }
14715
+ }
14716
+ }
14521
14717
  if (submitter && submitter._attrs && submitter._attrs.name) {
14522
14718
  this._entries.push([String(submitter._attrs.name), String(submitter._attrs.value || "")]);
14523
14719
  }
@@ -15546,7 +15742,12 @@
15546
15742
  continue;
15547
15743
  }
15548
15744
  if (type === "file") {
15549
- fileInputs.push({ name, handle: f._id });
15745
+ const files = (f._files || []).map((file) => ({
15746
+ name: String(file.name || ""),
15747
+ handle: file && file._csimHost ? file._handle : null,
15748
+ index: file && file._csimHost ? file._index : null
15749
+ }));
15750
+ fileInputs.push({ name, handle: f._id, files });
15550
15751
  continue;
15551
15752
  }
15552
15753
  fields.push([name, f._attrs.value != null ? f._attrs.value : ""]);
@@ -17779,7 +17980,16 @@
17779
17980
  this.dropEffect = "none";
17780
17981
  this.effectAllowed = "all";
17781
17982
  this.types = [];
17782
- this.files = [];
17983
+ }
17984
+ // `files` is the FileList view of the file-kind items — derived so it stays in
17985
+ // sync however items were added (`items.add(file)`, drag-drop construction, …).
17986
+ get files() {
17987
+ const out = [];
17988
+ for (const it of this.items) if (it.kind === "file" && it._file) out.push(it._file);
17989
+ out.item = function(i) {
17990
+ return this[i] || null;
17991
+ };
17992
+ return out;
17783
17993
  }
17784
17994
  getData(type) {
17785
17995
  for (const it of this.items) if (it.kind === "string" && it.type === type) return it._value;
@@ -17885,7 +18095,55 @@
17885
18095
  }
17886
18096
  };
17887
18097
  globalThis.BroadcastChannel = BroadcastChannel;
18098
+ globalThis.__csimTransferDropAll = function(tokens) {
18099
+ const NS = globalThis.RustyRacer;
18100
+ if (!tokens || !NS || typeof NS.transferDrop !== "function") return;
18101
+ for (let i = 0; i < tokens.length; i++) NS.transferDrop(tokens[i]);
18102
+ };
17888
18103
  var __csimWindowProxies = /* @__PURE__ */ new Map();
18104
+ function csimMaybeTransferOut(data, transfer) {
18105
+ if (!transfer || !transfer.length) return null;
18106
+ const NS = globalThis.RustyRacer;
18107
+ if (!NS || typeof NS.transferOut !== "function") return null;
18108
+ const isAB = data instanceof ArrayBuffer;
18109
+ const isView = !isAB && ArrayBuffer.isView(data);
18110
+ if (!isAB && !isView) return null;
18111
+ const buf = isAB ? data : data.buffer;
18112
+ let inList = false;
18113
+ for (let i = 0; i < transfer.length; i++) {
18114
+ const t = transfer[i];
18115
+ if (t === buf || t && t.buffer === buf) {
18116
+ inList = true;
18117
+ break;
18118
+ }
18119
+ }
18120
+ if (!inList) return null;
18121
+ const token = NS.transferOut(data) | 0;
18122
+ if (token <= 0) return null;
18123
+ if (globalThis.__csim_transferIssued) globalThis.__csim_transferIssued(token);
18124
+ return isAB ? { __csimXfer: token, kind: "ArrayBuffer" } : {
18125
+ __csimXfer: token,
18126
+ kind: data.constructor && data.constructor.name || "Uint8Array",
18127
+ byteOffset: data.byteOffset,
18128
+ length: data.length
18129
+ };
18130
+ }
18131
+ __name(csimMaybeTransferOut, "csimMaybeTransferOut");
18132
+ function csimMaybeTransferIn(data) {
18133
+ if (!data || typeof data !== "object" || data.__csimXfer == null) return data;
18134
+ const NS = globalThis.RustyRacer;
18135
+ if (!NS || typeof NS.transferIn !== "function") return data;
18136
+ const ab = NS.transferIn(data.__csimXfer);
18137
+ if (!ab) return new ArrayBuffer(0);
18138
+ if (data.kind === "ArrayBuffer") return ab;
18139
+ const Ctor = globalThis[data.kind] || globalThis.Uint8Array;
18140
+ try {
18141
+ return new Ctor(ab, data.byteOffset || 0, data.length);
18142
+ } catch (_) {
18143
+ return new Uint8Array(ab);
18144
+ }
18145
+ }
18146
+ __name(csimMaybeTransferIn, "csimMaybeTransferIn");
17889
18147
  function csimWindowProxy(handle) {
17890
18148
  if (handle == null || handle === "") return null;
17891
18149
  let proxy = __csimWindowProxies.get(handle);
@@ -17925,8 +18183,10 @@
17925
18183
  // lost — fine for the JSON-ish payloads postMessage carries in practice.
17926
18184
  // `targetOrigin` is accepted but, like the single-origin model elsewhere,
17927
18185
  // not enforced.
17928
- postMessage(data, targetOrigin, _transfer) {
17929
- globalThis.__csimWindowPostMessage(handle, data, String(targetOrigin == null ? "*" : targetOrigin));
18186
+ postMessage(data, targetOrigin, transfer) {
18187
+ const xfer = csimMaybeTransferOut(data, transfer);
18188
+ globalThis.__csimWindowPostMessage(handle, xfer || data, String(targetOrigin == null ? "*" : targetOrigin));
18189
+ detachTransferables(transfer);
17930
18190
  },
17931
18191
  get location() {
17932
18192
  return location;
@@ -17972,7 +18232,7 @@
17972
18232
  for (const ev of events) {
17973
18233
  const source = ev && ev.sourceHandle ? csimWindowProxy(ev.sourceHandle) : null;
17974
18234
  dispatchWithOnHandler(globalThis, new MessageEvent("message", {
17975
- data: ev ? ev.data : void 0,
18235
+ data: csimMaybeTransferIn(ev ? ev.data : void 0),
17976
18236
  origin: ev && ev.origin || "",
17977
18237
  source,
17978
18238
  lastEventId: "",
@@ -18966,7 +19226,7 @@
18966
19226
  (function() {
18967
19227
  "use strict";
18968
19228
  globalThis.Event = Event;
18969
- globalThis.DOMException = DOMException;
19229
+ globalThis.DOMException = DOMException2;
18970
19230
  globalThis.CustomEvent = CustomEvent;
18971
19231
  globalThis.MouseEvent = MouseEvent;
18972
19232
  globalThis.KeyboardEvent = KeyboardEvent;
@@ -19731,7 +19991,6 @@
19731
19991
  if (it.kind === "file") {
19732
19992
  const file = { name: it.name, type: "", size: 0 };
19733
19993
  dt.items.push(new globalThis.DataTransferItem("file", "application/octet-stream", null, file));
19734
- dt.files.push(file);
19735
19994
  if (!dt.types.includes("Files")) dt.types.push("Files");
19736
19995
  } else {
19737
19996
  dt.items.push(new globalThis.DataTransferItem("string", it.type, it.value, null));
@@ -19798,7 +20057,6 @@
19798
20057
  function __isTabbable(el) {
19799
20058
  if (!el || el.nodeType !== NODE_ELEMENT) return false;
19800
20059
  if (el._attrs.disabled != null) return false;
19801
- if (el._attrs.hidden != null) return false;
19802
20060
  if (selfHidden(el)) return false;
19803
20061
  const ti = el._attrs.tabindex;
19804
20062
  if (ti != null) {
@@ -66,6 +66,9 @@ module Capybara
66
66
  '__csim_logConsole' => ->(b, *a) { b.log_console(a[0], a[1]); nil },
67
67
  '__csim_eventSourceOpen' => ->(b, *a) { b.event_source_open(a[0]) },
68
68
  '__csim_eventSourceClose' => ->(b, *a) { b.event_source_close(a[0]); nil },
69
+ '__csim_wsOpen' => ->(b, *a) { b.ws_open(a[0], a[1]) },
70
+ '__csim_wsSend' => ->(b, *a) { b.ws_send(a[0], a[1], a[2], a[3]); nil },
71
+ '__csim_wsClose' => ->(b, *a) { b.ws_close(a[0], a[1], a[2]); nil },
69
72
  '__csim_rackFetchAsync' => ->(b, *a) { b.rack_fetch_async(a[0], a[1], a[2], a[3]) },
70
73
  '__csim_rackFetchAsyncAbort' => ->(b, *a) { b.rack_fetch_async_abort(a[0]); nil },
71
74
  # Cross-window references (window.open / opener / postMessage). Each
@@ -87,6 +90,8 @@ module Capybara
87
90
  '__csim_blobUnregister' => ->(b, *a) { b.blob_unregister(a[0]); nil },
88
91
  '__csim_transferStash' => ->(b, *a) { b.transfer_buffer_stash(a[0]) },
89
92
  '__csim_transferFetch' => ->(b, *a) { b.transfer_buffer_fetch_for_js(a[0]) },
93
+ # Zero-copy postMessage transfer-token bookkeeping (see Browser#drop_pending_transfers).
94
+ '__csim_transferIssued' => ->(b, *a) { b.transfer_token_issued(a[0]); nil },
90
95
  '__csim_decodeVideoFrame' => ->(b, *a) { b.decode_video_frame(a[0]) },
91
96
  '__csim_encodeImage' => ->(b, *a) { b.encode_image(a[0], a[1], a[2], a[3], a[4]) },
92
97
  # WebAuthn create / get raise `WebauthnState::Error` carrying
@@ -411,13 +411,20 @@ module Capybara
411
411
  # keeps the new realm's `parent`/`top` wired to the owning realm. The
412
412
  # Browser then re-points the iframe element at the new id (`__csimRebindFrameRealm`).
413
413
  def reload_frame_realm(old_id, parent_id, url, body, content_type)
414
- if (fr = frame_realms.delete(old_id))
415
- @realm_module_handles&.delete(old_id)
416
- fr.dispose rescue nil
417
- end
414
+ dispose_frame_realm(old_id)
418
415
  create_frame_realm(ctx, url, body, content_type, parent_id)
419
416
  end
420
417
 
418
+ # Tear down a single frame realm (e.g. a descendant frame destroyed when an
419
+ # ancestor frame re-navigates). No-op for nil/0/unknown ids.
420
+ def dispose_frame_realm(id)
421
+ return if id.nil? || id.zero?
422
+ @realm_module_handles&.delete(id)
423
+ fr = frame_realms.delete(id)
424
+ fr.dispose rescue nil if fr
425
+ nil
426
+ end
427
+
421
428
  # One native microtask checkpoint — a checkpoint runs the queue until
422
429
  # empty, and rusty already performs one at the end of every top-level
423
430
  # eval/call (V8's default kAuto policy), so a single explicit checkpoint
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Simulated
5
- VERSION = '0.2.0'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end