cindel 1.0.5 → 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 CHANGED
@@ -1572,15 +1572,15 @@ var FileLoader = class {
1572
1572
  fetch(url).then((r) => {
1573
1573
  if (!r.ok) throw new Error(`Failed to fetch CSS: ${path} (${r.status})`);
1574
1574
  return r.text();
1575
- }).then((code) => {
1576
- this._inject("css", code, path);
1577
- })
1575
+ }).then((code) => this._inject("css", code, path))
1578
1576
  );
1579
1577
  }
1580
1578
  if (toParent) {
1581
1579
  ops.push(this._loadCSSInParent(path, url));
1582
1580
  }
1583
- await Promise.all(ops);
1581
+ const results = await Promise.allSettled(ops);
1582
+ const failed = results.find((r) => r.status === "rejected");
1583
+ if (failed) throw failed.reason;
1584
1584
  return true;
1585
1585
  }
1586
1586
  _loadCSSInParent(path, url) {
@@ -1608,7 +1608,7 @@ var FileLoader = class {
1608
1608
  if (!r.ok) throw new Error(`Failed to fetch module: ${path} (${r.status})`);
1609
1609
  return r.text();
1610
1610
  });
1611
- this._inject("module", code, path);
1611
+ await this._inject("module", code, path);
1612
1612
  return true;
1613
1613
  }
1614
1614
  await import(url);
@@ -1623,14 +1623,18 @@ var FileLoader = class {
1623
1623
  if (!r.ok) throw new Error(`Failed to fetch script: ${path} (${r.status})`);
1624
1624
  return r.text();
1625
1625
  });
1626
- this._inject("script", code, path);
1626
+ await this._inject("script", code, path);
1627
1627
  return true;
1628
1628
  }
1629
1629
  const script = document.createElement("script");
1630
1630
  script.src = url;
1631
1631
  script.setAttribute("data-file", path);
1632
1632
  return new Promise((resolve, reject) => {
1633
- script.onload = () => resolve(true);
1633
+ script.onload = () => {
1634
+ const previous = document.querySelector(`script[data-file="${path}"]:not([src="${url}"])`);
1635
+ if (previous) previous.remove();
1636
+ resolve(true);
1637
+ };
1634
1638
  script.onerror = () => reject(new Error(`Failed to load script: ${path}`));
1635
1639
  document.head.appendChild(script);
1636
1640
  });
@@ -1672,8 +1676,7 @@ var FileLoader = class {
1672
1676
  this.loadQueue.delete(path);
1673
1677
  }
1674
1678
  if (this.iframeTarget) {
1675
- this._post({ type: "hmr:remove", file: path });
1676
- await Promise.resolve();
1679
+ await this._postAndAwaitAck({ type: "hmr:remove", file: path });
1677
1680
  } else {
1678
1681
  const el = document.querySelector(`[data-file="${path}"]`);
1679
1682
  if (el) {
@@ -1689,13 +1692,27 @@ var FileLoader = class {
1689
1692
  this.versions.set(path, v);
1690
1693
  return `${this.httpUrl}${path}?v=${v}`;
1691
1694
  }
1692
- // Send a raw postMessage to the iframe target
1693
- _post(message) {
1694
- this.iframeTarget.postMessage(message, this.iframeOrigin);
1695
+ // Post a message and resolve once the stub sends back hmr:ack
1696
+ _postAndAwaitAck(message) {
1697
+ return new Promise((resolve) => {
1698
+ const onAck = (e) => {
1699
+ if (e.source !== this.iframeTarget) return;
1700
+ let data;
1701
+ try {
1702
+ data = JSON.parse(e.data);
1703
+ } catch {
1704
+ return;
1705
+ }
1706
+ if (data?.type !== "hmr:ack") return;
1707
+ window.removeEventListener("message", onAck);
1708
+ resolve();
1709
+ };
1710
+ window.addEventListener("message", onAck);
1711
+ this.iframeTarget.postMessage(JSON.stringify(message), this.iframeOrigin);
1712
+ });
1695
1713
  }
1696
- // Forward a file payload to the iframe target
1697
1714
  _inject(kind, code, file) {
1698
- this._post({ type: "hmr:inject", kind, code, file });
1715
+ return this._postAndAwaitAck({ type: "hmr:inject", kind, code, file });
1699
1716
  }
1700
1717
  };
1701
1718
 
@@ -2199,15 +2216,22 @@ var HMRClient = class {
2199
2216
  this._processingMessages = false;
2200
2217
  }
2201
2218
  // Wait for stub's hmr:ready signal. Stub fires it proactively on run.
2202
- // Times out after 5s if stub was never injected.
2219
+ // Times out after 5s and resolves anyway, a missing stub degrades gracefully.
2203
2220
  _waitForStub() {
2204
- return new Promise((resolve, reject) => {
2221
+ return new Promise((resolve) => {
2205
2222
  const timer = setTimeout(() => {
2206
2223
  window.removeEventListener("message", onReady);
2207
- reject(new Error("Timed out waiting for hmr:ready. Was HMR.stub() called in the iframe?"));
2224
+ this.log("warning", "Timed out waiting for hmr:ready. Was HMR.stub() called in the iframe?");
2225
+ resolve();
2208
2226
  }, 5e3);
2209
2227
  const onReady = (e) => {
2210
- if (e.data?.type !== "hmr:ready") return;
2228
+ let data;
2229
+ try {
2230
+ data = JSON.parse(e.data);
2231
+ } catch {
2232
+ return;
2233
+ }
2234
+ if (data?.type !== "hmr:ready") return;
2211
2235
  const originOk = this._iframeOrigin === "*" || e.origin === this._iframeOrigin;
2212
2236
  if (!originOk) return;
2213
2237
  clearTimeout(timer);
@@ -2227,15 +2251,25 @@ var HMRClient = class {
2227
2251
  _listenForReattach() {
2228
2252
  if (this._onReattach) return;
2229
2253
  this._onReattach = async (e) => {
2230
- if (e.data?.type !== "hmr:ready") return;
2254
+ let data;
2255
+ try {
2256
+ data = JSON.parse(e.data);
2257
+ } catch {
2258
+ return;
2259
+ }
2260
+ if (data?.type !== "hmr:ready") return;
2231
2261
  const originOk = this._iframeOrigin === "*" || e.origin === this._iframeOrigin;
2232
2262
  if (!originOk) return;
2233
2263
  if (e.source === this._iframeTarget) return;
2234
2264
  this._iframeTarget = e.source;
2235
2265
  this.fileLoader.iframeTarget = e.source;
2236
2266
  this.log("success", "HMR reattached to new iframe");
2237
- for (const path of this.fileLoader.versions.keys()) {
2238
- await this.fileLoader.loadFile(path);
2267
+ for (const path of this.sortFiles([...this.fileLoader.versions.keys()])) {
2268
+ try {
2269
+ await this.fileLoader.loadFile(path);
2270
+ } catch (e2) {
2271
+ this.log("error", `Reattach failed to load ${path}: ${e2.message}`);
2272
+ }
2239
2273
  }
2240
2274
  };
2241
2275
  window.addEventListener("message", this._onReattach);
@@ -2271,15 +2305,8 @@ var HMRClient = class {
2271
2305
  this._messageQueue = [];
2272
2306
  this._processingMessages = true;
2273
2307
  if (this._iframeTarget || this._stubManaged) {
2274
- try {
2275
- await this._waitForStub();
2276
- if (this._stubManaged) this._listenForReattach();
2277
- } catch (e) {
2278
- this.log("error", e.message);
2279
- this._processingMessages = false;
2280
- reject(e);
2281
- return;
2282
- }
2308
+ await this._waitForStub();
2309
+ if (this._stubManaged) this._listenForReattach();
2283
2310
  }
2284
2311
  this._processingMessages = false;
2285
2312
  if (this._messageQueue.length > 0) this._drainMessageQueue();
@@ -2355,36 +2382,72 @@ function stub() {
2355
2382
  const removeIfExists = (el) => {
2356
2383
  if (el) el.remove();
2357
2384
  };
2358
- const injectScript = (kind, code, file) => {
2359
- removeIfExists(byFile("script", file));
2360
- const script = document.createElement("script");
2361
- if (kind === "module") script.type = "module";
2362
- script.textContent = code;
2363
- script.dataset.file = file;
2364
- document.documentElement.appendChild(script);
2365
- script.remove();
2385
+ const injectScript = async (kind, code, file) => {
2386
+ const url = URL.createObjectURL(new Blob([code + `
2387
+ //# sourceURL=${file}`], { type: "text/javascript" }));
2388
+ try {
2389
+ if (kind === "module") {
2390
+ await import(url);
2391
+ } else {
2392
+ const script = document.createElement("script");
2393
+ script.src = url;
2394
+ await new Promise((resolve, reject) => {
2395
+ script.onload = resolve;
2396
+ script.onerror = () => reject(new Error(`Failed to execute script: ${file}`));
2397
+ document.documentElement.appendChild(script);
2398
+ });
2399
+ }
2400
+ } finally {
2401
+ URL.revokeObjectURL(url);
2402
+ }
2366
2403
  };
2367
- const injectStyle = (code, file) => {
2368
- const existing = byFile("style", file);
2369
- const style = document.createElement("style");
2370
- style.textContent = code;
2371
- style.dataset.file = file;
2372
- document.head.appendChild(style);
2404
+ const injectStyle = async (code, file) => {
2405
+ const existing = byFile("link", file);
2406
+ const url = URL.createObjectURL(new Blob([code + `
2407
+ /*# sourceURL=${file} */`], { type: "text/css" }));
2408
+ const link = document.createElement("link");
2409
+ link.rel = "stylesheet";
2410
+ link.href = url;
2411
+ link.dataset.file = file;
2412
+ await new Promise((resolve, reject) => {
2413
+ link.onload = () => {
2414
+ URL.revokeObjectURL(url);
2415
+ resolve();
2416
+ };
2417
+ link.onerror = () => {
2418
+ URL.revokeObjectURL(url);
2419
+ reject(new Error(`Failed to load CSS: ${file}`));
2420
+ };
2421
+ document.head.appendChild(link);
2422
+ });
2373
2423
  removeIfExists(existing);
2374
2424
  };
2375
- window.addEventListener("message", (e) => {
2376
- const data = e.data;
2425
+ window.addEventListener("message", async (e) => {
2426
+ let data;
2427
+ try {
2428
+ data = JSON.parse(e.data);
2429
+ } catch {
2430
+ return;
2431
+ }
2377
2432
  if (!data?.type) return;
2433
+ const ackOrigin = e.origin && e.origin !== "null" ? e.origin : "*";
2434
+ const ack = () => e.source?.postMessage(JSON.stringify({ type: "hmr:ack" }), ackOrigin);
2378
2435
  if (data.type === "hmr:remove") {
2379
2436
  removeIfExists(document.querySelector(`[data-file="${CSS.escape(data.file)}"]`));
2437
+ ack();
2380
2438
  return;
2381
2439
  }
2382
2440
  if (data.type !== "hmr:inject") return;
2383
2441
  const { kind, code, file } = data;
2384
- if (kind === "script" || kind === "module") injectScript(kind, code, file);
2385
- else if (kind === "css") injectStyle(code, file);
2442
+ try {
2443
+ if (kind === "script" || kind === "module") await injectScript(kind, code, file);
2444
+ else if (kind === "css") await injectStyle(code, file);
2445
+ } catch (err) {
2446
+ console.error(`Failed to inject ${file}:`, err.message ?? err, "\n" + err.stack);
2447
+ }
2448
+ ack();
2386
2449
  });
2387
- window.parent.postMessage({ type: "hmr:ready" }, "*");
2450
+ window.parent.postMessage(JSON.stringify({ type: "hmr:ready" }), "*");
2388
2451
  }
2389
2452
  export {
2390
2453
  HMRClient,