rerender-lens 0.5.0 → 0.7.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +50 -1
  2. package/README.md +23 -4
  3. package/dist/{budget-CRswn-nC.d.cts → budget-CA1MS7lB.d.cts} +1 -1
  4. package/dist/{budget-BnJlYwrV.d.ts → budget-ok1VDJ4t.d.ts} +1 -1
  5. package/dist/cli.cjs +78 -26
  6. package/dist/cli.cjs.map +1 -1
  7. package/dist/cli.js +78 -26
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{devtools-BYWat7nQ.d.ts → devtools-Cl5n_52v.d.ts} +11 -2
  10. package/dist/{devtools-DJi_6AfJ.d.cts → devtools-UwQx2VYS.d.cts} +11 -2
  11. package/dist/index.cjs +99 -9
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +13 -7
  14. package/dist/index.d.ts +13 -7
  15. package/dist/index.js +99 -10
  16. package/dist/index.js.map +1 -1
  17. package/dist/jest-setup.cjs +20 -3
  18. package/dist/jest-setup.cjs.map +1 -1
  19. package/dist/jest-setup.d.cts +4 -4
  20. package/dist/jest-setup.d.ts +4 -4
  21. package/dist/jest-setup.js +20 -3
  22. package/dist/jest-setup.js.map +1 -1
  23. package/dist/jest.cjs +20 -3
  24. package/dist/jest.cjs.map +1 -1
  25. package/dist/jest.d.cts +4 -4
  26. package/dist/jest.d.ts +4 -4
  27. package/dist/jest.js +20 -3
  28. package/dist/jest.js.map +1 -1
  29. package/dist/{notifiers-DwKlvv4H.d.cts → notifiers-B5poAEvE.d.cts} +2 -2
  30. package/dist/{notifiers-FNzKHs0a.d.ts → notifiers-CqQW0Eum.d.ts} +2 -2
  31. package/dist/playwright.d.cts +4 -4
  32. package/dist/playwright.d.ts +4 -4
  33. package/dist/relay.cjs +65 -21
  34. package/dist/relay.cjs.map +1 -1
  35. package/dist/relay.d.cts +25 -3
  36. package/dist/relay.d.ts +25 -3
  37. package/dist/relay.js +65 -21
  38. package/dist/relay.js.map +1 -1
  39. package/dist/rerender-lens.iife.js +105 -12
  40. package/dist/{runner-report-CW8X5RwK.d.ts → runner-report-Bvqoe89M.d.ts} +2 -2
  41. package/dist/{runner-report-B8Sc3FZO.d.cts → runner-report-CvpDg2zt.d.cts} +2 -2
  42. package/dist/setup.cjs +98 -9
  43. package/dist/setup.cjs.map +1 -1
  44. package/dist/setup.js +98 -9
  45. package/dist/setup.js.map +1 -1
  46. package/dist/{types-DW5-N2XH.d.cts → types-DwNN5cWe.d.cts} +28 -1
  47. package/dist/{types-DW5-N2XH.d.ts → types-DwNN5cWe.d.ts} +28 -1
  48. package/dist/vite.d.cts +1 -1
  49. package/dist/vite.d.ts +1 -1
  50. package/dist/vitest-setup.cjs +20 -3
  51. package/dist/vitest-setup.cjs.map +1 -1
  52. package/dist/vitest-setup.d.cts +4 -4
  53. package/dist/vitest-setup.d.ts +4 -4
  54. package/dist/vitest-setup.js +20 -3
  55. package/dist/vitest-setup.js.map +1 -1
  56. package/dist/vitest.cjs +20 -3
  57. package/dist/vitest.cjs.map +1 -1
  58. package/dist/vitest.d.cts +5 -5
  59. package/dist/vitest.d.ts +5 -5
  60. package/dist/vitest.js +20 -3
  61. package/dist/vitest.js.map +1 -1
  62. package/package.json +3 -2
  63. package/panel/panel.css +48 -0
  64. package/panel/panel.js +348 -20
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@ import { writeFileSync, readFileSync, existsSync } from 'fs';
3
3
  import { createServer } from 'http';
4
4
  import { join, dirname } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
+ import { randomBytes } from 'crypto';
6
7
 
7
8
  // src/diff.ts
8
9
  var AVOIDABLE_KINDS = /* @__PURE__ */ new Set(["deep-equal", "function", "element"]);
@@ -402,6 +403,8 @@ function checkBudget(reports, budget) {
402
403
  function toBudget(reports) {
403
404
  return { "*": 0, ...avoidableCounts(reports) };
404
405
  }
406
+ var MARKER = "__rerenderLens";
407
+ var PROTOCOL = 2;
405
408
  var MIME = { ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8" };
406
409
  function findPanelDir() {
407
410
  const here = dirname(fileURLToPath(import.meta.url));
@@ -436,26 +439,55 @@ function createRelayServer(options = {}) {
436
439
  const host = options.host ?? "127.0.0.1";
437
440
  const panelDir = options.panelDir === void 0 ? findPanelDir() : options.panelDir;
438
441
  const keepAliveMs = options.keepAliveMs ?? 15e3;
439
- const apps = /* @__PURE__ */ new Set();
442
+ const token = options.token || null;
443
+ const apps = /* @__PURE__ */ new Map();
440
444
  const panels = /* @__PURE__ */ new Set();
441
445
  let url = "";
442
- const send = (targets, message) => {
443
- const line = `data: ${JSON.stringify(message)}
446
+ let nextAppId = 1;
447
+ const connectUrl = () => token ? `${url}?token=${encodeURIComponent(token)}` : url;
448
+ const line = (message) => `data: ${JSON.stringify(message)}
444
449
 
445
450
  `;
446
- for (const res of targets) {
451
+ const sendTo = (res, message) => {
452
+ try {
453
+ res.write(line(message));
454
+ } catch {
455
+ apps.delete(res);
456
+ panels.delete(res);
457
+ }
458
+ };
459
+ const send = (targets, message) => {
460
+ const text = line(message);
461
+ for (const res of [...targets]) {
447
462
  try {
448
- res.write(line);
463
+ res.write(text);
449
464
  } catch {
450
- targets.delete(res);
465
+ apps.delete(res);
466
+ panels.delete(res);
451
467
  }
452
468
  }
453
469
  };
454
- const route = (message) => {
470
+ const appList = () => [...apps.values()];
471
+ const roster = () => ({ [MARKER]: true, version: PROTOCOL, type: "relay", payload: { apps: apps.size, list: appList() } });
472
+ const streamOf = (id) => {
473
+ for (const [res, app] of apps) if (app.id === id) return res;
474
+ return null;
475
+ };
476
+ const route = (message, from) => {
455
477
  if (!message || typeof message !== "object") return;
456
478
  const m = message;
457
- if (m.__rerenderLensCmd === true) send(apps, m);
458
- else if (m.__rerenderLens === true || m.__rerenderLensReply === true) send(panels, m);
479
+ if (m.__rerenderLensCmd === true) {
480
+ const target = typeof m.app === "string" ? streamOf(m.app) : null;
481
+ if (target) sendTo(target, m);
482
+ else send(apps.keys(), m);
483
+ } else if (m.__rerenderLens === true || m.__rerenderLensReply === true) {
484
+ send(panels, from ? { ...m, app: from.id } : m);
485
+ }
486
+ };
487
+ const authorized = (req, u) => {
488
+ if (!token) return true;
489
+ const given = u.searchParams.get("token") || (typeof req.headers["x-rerender-lens-token"] === "string" ? req.headers["x-rerender-lens-token"] : "");
490
+ return given === token;
459
491
  };
460
492
  const server = createServer(async (req, res) => {
461
493
  cors(res);
@@ -467,18 +499,26 @@ function createRelayServer(options = {}) {
467
499
  }
468
500
  if (u.pathname === "/" && req.method === "GET") {
469
501
  res.statusCode = 302;
470
- res.setHeader("Location", `/panel.html?relay=${encodeURIComponent(url)}`);
502
+ res.setHeader("Location", `/panel.html?relay=${encodeURIComponent(connectUrl())}`);
471
503
  res.end();
472
504
  return;
473
505
  }
506
+ if (/^\/(events|message|status)$/.test(u.pathname) && !authorized(req, u)) {
507
+ res.statusCode = 401;
508
+ res.setHeader("Content-Type", "text/plain");
509
+ res.end("rerender-lens relay: this relay needs the token from the URL it printed (?token=...)");
510
+ return;
511
+ }
474
512
  if (u.pathname === "/events" && req.method === "GET") {
475
- const role = u.searchParams.get("role") === "app" ? apps : panels;
513
+ const isApp = u.searchParams.get("role") === "app";
476
514
  res.statusCode = 200;
477
515
  res.setHeader("Content-Type", "text/event-stream");
478
516
  res.setHeader("Cache-Control", "no-store");
479
517
  res.setHeader("Connection", "keep-alive");
480
518
  res.write(": connected\n\n");
481
- role.add(res);
519
+ const app = isApp ? { id: `a${nextAppId++}`, label: (u.searchParams.get("label") || req.headers.origin || "").slice(0, 120) || `app ${nextAppId - 1}` } : null;
520
+ if (app) apps.set(res, app);
521
+ else panels.add(res);
482
522
  const ping = setInterval(() => {
483
523
  try {
484
524
  res.write(": ping\n\n");
@@ -487,14 +527,15 @@ function createRelayServer(options = {}) {
487
527
  }, keepAliveMs);
488
528
  req.on("close", () => {
489
529
  clearInterval(ping);
490
- role.delete(res);
530
+ apps.delete(res);
531
+ panels.delete(res);
491
532
  });
492
- const count = () => ({ __rerenderLens: true, version: 2, type: "relay", payload: { apps: apps.size } });
493
- if (role === apps) {
494
- send(panels, count());
495
- req.on("close", () => send(panels, count()));
533
+ if (app) {
534
+ sendTo(res, { __rerenderLensRelay: true, version: PROTOCOL, app: app.id });
535
+ send(panels, roster());
536
+ req.on("close", () => send(panels, roster()));
496
537
  } else {
497
- send(/* @__PURE__ */ new Set([res]), count());
538
+ sendTo(res, roster());
498
539
  }
499
540
  return;
500
541
  }
@@ -506,7 +547,9 @@ function createRelayServer(options = {}) {
506
547
  if (u.pathname === "/message" && req.method === "POST") {
507
548
  try {
508
549
  const parsed = JSON.parse(await readBody(req) || "null");
509
- for (const m of Array.isArray(parsed) ? parsed : [parsed]) route(m);
550
+ const sender = u.searchParams.get("app");
551
+ const from = sender ? appList().find((a) => a.id === sender) ?? void 0 : void 0;
552
+ for (const m of Array.isArray(parsed) ? parsed : [parsed]) route(m, from);
510
553
  res.statusCode = 204;
511
554
  res.end();
512
555
  } catch (e) {
@@ -519,7 +562,7 @@ function createRelayServer(options = {}) {
519
562
  if (u.pathname === "/status" && req.method === "GET") {
520
563
  res.statusCode = 200;
521
564
  res.setHeader("Content-Type", "application/json");
522
- res.end(JSON.stringify({ ok: true, apps: apps.size, panels: panels.size }));
565
+ res.end(JSON.stringify({ ok: true, apps: apps.size, panels: panels.size, list: appList() }));
523
566
  return;
524
567
  }
525
568
  const file = u.pathname.replace(/^\//, "");
@@ -549,10 +592,12 @@ function createRelayServer(options = {}) {
549
592
  resolve({
550
593
  server,
551
594
  url,
595
+ connectUrl: connectUrl(),
552
596
  port,
553
597
  counts: () => ({ apps: apps.size, panels: panels.size }),
598
+ appList,
554
599
  close: () => new Promise((done) => {
555
- for (const res of [...apps, ...panels]) res.end();
600
+ for (const res of [...apps.keys(), ...panels]) res.end();
556
601
  apps.clear();
557
602
  panels.clear();
558
603
  server.close(() => done());
@@ -561,8 +606,6 @@ function createRelayServer(options = {}) {
561
606
  });
562
607
  });
563
608
  }
564
-
565
- // src/cli.ts
566
609
  var read = (file) => JSON.parse(readFileSync(file, "utf8"));
567
610
  function summaryOf(file) {
568
611
  const parsed = parseExport(read(file));
@@ -570,14 +613,20 @@ function summaryOf(file) {
570
613
  if (parsed.sessions.length && !parsed.reports.length) return parsed.sessions[parsed.sessions.length - 1];
571
614
  return summarizeReports(parsed.reports, { name: file.replace(/^.*[\\/]/, "").replace(/\.json$/, "") });
572
615
  }
616
+ var isLoopback = (host) => host === "127.0.0.1" || host === "localhost" || host === "::1";
573
617
  async function panelCommand(args) {
574
618
  const flag = (name) => {
575
619
  const i = args.indexOf(name);
576
620
  return i >= 0 ? args[i + 1] ?? "" : null;
577
621
  };
578
- const relay = await createRelayServer({ port: Number(flag("--port") || 4141), host: flag("--host") || "127.0.0.1" });
579
- console.log(`rerender-lens panel: ${relay.url}/`);
580
- console.log(`in the app: createDevtoolsNotifier({ relay: '${relay.url}' }) or window.__RERENDER_LENS_RELAY__ = '${relay.url}'`);
622
+ const host = flag("--host") || "127.0.0.1";
623
+ const given = flag("--token");
624
+ const token = args.includes("--no-token") ? null : given || (isLoopback(host) ? null : randomToken());
625
+ const relay = await createRelayServer({ port: Number(flag("--port") || 4141), host, token: token ?? void 0 });
626
+ console.log(`rerender-lens panel: ${relay.connectUrl}/`);
627
+ console.log(`in the app: createDevtoolsNotifier({ relay: '${relay.connectUrl}' }) or window.__RERENDER_LENS_RELAY__ = '${relay.connectUrl}'`);
628
+ if (token) console.log("The token in that URL is required: reports carry your app's prop, state and context values.");
629
+ else if (!isLoopback(host)) console.log(`WARNING: ${host} is reachable by other machines and --no-token was given: anyone who can reach this port can read every report.`);
581
630
  console.log("Ctrl+C to stop.");
582
631
  return new Promise((resolve) => {
583
632
  const stop = () => void relay.close().then(() => resolve(0));
@@ -586,6 +635,9 @@ async function panelCommand(args) {
586
635
  relay.server.once("close", () => resolve(0));
587
636
  });
588
637
  }
638
+ function randomToken() {
639
+ return randomBytes(16).toString("base64url");
640
+ }
589
641
  function main(argv) {
590
642
  const [cmd, ...args] = argv;
591
643
  if (cmd === "panel") return panelCommand(args).catch((e) => (console.error(e.message), 1));