@codeyam-editor/codeyam-editor 0.1.14 → 0.1.15

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/npm/utils.js CHANGED
@@ -3,6 +3,7 @@ const net = require("net");
3
3
  const path = require("path");
4
4
  const fs = require("fs");
5
5
  const readline = require("readline");
6
+ const { logLauncherBootEvent } = require("./launcher-boot-log");
6
7
 
7
8
  const rootDir = path.resolve(__dirname, "..");
8
9
 
@@ -823,6 +824,24 @@ function formatServerStartTimeoutError(port, timeoutMs) {
823
824
  ].join("\n");
824
825
  }
825
826
 
827
+ /**
828
+ * Build the "server is not answering, so the browser was not opened" failure
829
+ * message — the launcher's last word when its final identity check finds no
830
+ * live server of this project. Pure string builder: names the outcome (no
831
+ * editor page was opened) and the three logs that say why, as absolute paths
832
+ * under `projectDir` so they are clickable from any cwd.
833
+ */
834
+ function formatServerNotAnsweringError(port, projectDir) {
835
+ const logs = path.join(projectDir, ".codeyam", "logs");
836
+ return [
837
+ `codeyam-editor server is not answering on port ${port}, so the editor was not opened.`,
838
+ "Where to look:",
839
+ ` ${path.join(logs, "editor-server.log")}`,
840
+ ` ${path.join(logs, "editor-server.out.log")}`,
841
+ ` ${path.join(logs, "launcher-boot.log")}`,
842
+ ].join("\n");
843
+ }
844
+
826
845
  /**
827
846
  * Classify whatever is listening on `port` relative to `projectDir`.
828
847
  * Returns one of:
@@ -1097,6 +1116,34 @@ function logLauncherTeardownSignal(signal, projectDir, { interactive, forwarded
1097
1116
  }
1098
1117
  }
1099
1118
 
1119
+ /**
1120
+ * The identity of the server on `port` when it belongs to THIS launch's
1121
+ * project — a same-project editor, or the Project Launcher (selector) that a
1122
+ * multi-project launch diverts to — else `null`. This is the "ask the server"
1123
+ * check every launcher-initiated kill and every browser open is gated on: a TCP
1124
+ * probe cannot tell this project's live server from a stranger or a corpse.
1125
+ */
1126
+ async function ownServerIdentity(port, projectDir, classify = classifyPortHolder) {
1127
+ const holder = await classify(port, projectDir);
1128
+ return holder.kind === "same-project" || holder.kind === "launcher" ? holder.identity : null;
1129
+ }
1130
+
1131
+ /**
1132
+ * Poll `ownServerIdentityFn(port)` until it answers or `timeoutMs` elapses.
1133
+ * Returns the identity, or `null` when nothing of this project's answered in the
1134
+ * window. A single sub-second identity probe can miss a live server that is busy
1135
+ * finishing its boot, so the browser gate polls instead of trusting one miss.
1136
+ */
1137
+ async function waitForOwnServer(port, { ownServerIdentityFn, timeoutMs = 5000, intervalMs = 250 }) {
1138
+ const deadline = Date.now() + timeoutMs;
1139
+ for (;;) {
1140
+ const identity = await ownServerIdentityFn(port);
1141
+ if (identity) return identity;
1142
+ if (Date.now() >= deadline) return null;
1143
+ await new Promise((r) => setTimeout(r, intervalMs));
1144
+ }
1145
+ }
1146
+
1100
1147
  /**
1101
1148
  * Send the platform-correct termination signal to a spawned child process.
1102
1149
  * Used by editor.js / editor-dev.js / container.js when the user hits Ctrl+C —
@@ -1227,6 +1274,16 @@ async function restartServer(port = 14199, projectDir = process.cwd()) {
1227
1274
  return ensureServer(port, projectDir);
1228
1275
  }
1229
1276
 
1277
+ /**
1278
+ * The argv `ensureServer` spawns the server with. `--port` only says where to
1279
+ * bind; with 2+ registered projects the server still lands on the project
1280
+ * selector there (`boot_route` in `start_boot_route.rs`, whose test pins this
1281
+ * same argv from the Rust side).
1282
+ */
1283
+ function serverStartArgs(port) {
1284
+ return ["start", "--no-open", "--port", String(port)];
1285
+ }
1286
+
1230
1287
  /**
1231
1288
  * Start the Rust backend server if not already running, refusing to attach
1232
1289
  * if the existing listener belongs to a different project (or to a foreign
@@ -1293,6 +1350,7 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
1293
1350
 
1294
1351
  const binary = ensureBinary();
1295
1352
  const startTimeoutMs = resolveStartTimeoutMs();
1353
+ const trace = (event, fields) => logLauncherBootEvent(projectDir, event, fields);
1296
1354
 
1297
1355
  // Spawn a fresh server child and race its boot against `timeoutMs`. Factored
1298
1356
  // into a closure so the auto-retry path re-runs an identical spawn with a
@@ -1313,7 +1371,7 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
1313
1371
  const logFd = openServerOutputLog(projectDir);
1314
1372
  const child = spawn(
1315
1373
  binary,
1316
- ["start", "--no-open", "--port", String(port)],
1374
+ serverStartArgs(port),
1317
1375
  buildServerSpawnOptions({
1318
1376
  cwd: projectDir,
1319
1377
  env: serverSpawnEnv(process.env, { devCheckout: isDevCheckout() }),
@@ -1328,25 +1386,36 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
1328
1386
  }
1329
1387
  }
1330
1388
  child.unref();
1389
+ trace("spawn", { child_pid: child.pid, port, budget_ms: timeoutMs });
1331
1390
  const race = await awaitServerReadyOrChildExit(child, port, (p) =>
1332
1391
  waitForPort(p, timeoutMs),
1333
1392
  );
1334
1393
  return { child, ...race };
1335
1394
  };
1336
1395
 
1337
- // Orchestrate the boot (re-check-before-kill + one auto-retry) with the real
1338
- // effects injected, then map the discriminated result onto the process-exit
1339
- // policy. Keeping the orchestration in `resolveBootedServer` (pure control
1340
- // flow over injected effects) is what makes the re-check guard and the retry
1396
+ // Orchestrate the boot (ask-before-kill + adopt-on-lost-race + one auto-retry)
1397
+ // with the real effects injected, then map the discriminated result onto the
1398
+ // process-exit policy. Keeping the orchestration in `resolveBootedServer`
1399
+ // (pure control flow over injected effects) is what makes those guards
1341
1400
  // unit-testable without spawning the real binary or stubbing process.exit.
1342
1401
  const result = await resolveBootedServer({
1343
1402
  port,
1344
1403
  startTimeoutMs,
1345
1404
  spawnAndAwait,
1346
1405
  isPortInUseFn: isPortInUse,
1406
+ fetchIdentityFn: (p) => ownServerIdentity(p, projectDir),
1407
+ waitForPortToCloseFn: (p) => waitForPortToClose(p),
1408
+ trace,
1347
1409
  });
1410
+ trace("outcome", { kind: result.kind, port });
1348
1411
  switch (result.kind) {
1349
1412
  case "ready":
1413
+ if (result.adopted) {
1414
+ console.error(
1415
+ `Another launch of this project bound port ${port} first ` +
1416
+ `(pid ${result.adopted.pid}); using that server.`,
1417
+ );
1418
+ }
1350
1419
  return result.child;
1351
1420
  case "early-exit": {
1352
1421
  // A genuine early exit (preflight bail) is always a hard fail — even on
@@ -1370,39 +1439,79 @@ async function ensureServer(port = 14199, projectDir = process.cwd()) {
1370
1439
 
1371
1440
  /**
1372
1441
  * Boot orchestration for `ensureServer`, isolated from the process-exit policy
1373
- * and from the spawn/port effects so the re-check-before-kill guard and the
1374
- * single auto-retry are unit-testable. Pure control flow over two injected
1375
- * effects:
1442
+ * and from the spawn/port effects so the keep-before-kill guard, the adoption
1443
+ * path and the single auto-retry are unit-testable. Pure control flow over
1444
+ * injected effects:
1376
1445
  * - `spawnAndAwait(timeoutMs)` → `{ child, outcome, exitedBeforeBind }` —
1377
1446
  * one boot attempt racing the port against the child exit.
1378
- * - `isPortInUseFn(port)` → bool — the final re-check probe.
1447
+ * - `fetchIdentityFn(port)` → identity | null — does THIS project's server
1448
+ * (or the Project Launcher) answer on the port? (`ownServerIdentity`)
1449
+ * - `isPortInUseFn(port)` → bool — the TCP fallback re-check.
1450
+ * - `waitForPortToCloseFn(port)` → bool — lets a killed attempt release the
1451
+ * port before the retry binds it.
1452
+ * - `trace(event, fields)` — launch-attempt forensics (`logLauncherBootEvent`).
1379
1453
  *
1380
1454
  * Returns a discriminated result the caller maps to an action:
1381
1455
  * - `{ kind: "ready", child }` — server bound; keep this child.
1456
+ * - `{ kind: "ready", child: null, adopted }` — this attempt lost a bind race
1457
+ * to a live server of this project; that server is adopted, not fought.
1382
1458
  * - `{ kind: "early-exit", exitedBeforeBind }` — child exited before bind
1383
- * (preflight bail); a hard fail, never retried.
1459
+ * and nothing of this project answers the port; a hard fail, never retried.
1384
1460
  * - `{ kind: "timeout-exhausted", retryTimeoutMs }` — both the budget and a
1385
1461
  * longer-budget retry missed; give up with actionable guidance.
1386
1462
  *
1387
- * The destructive `child.kill()` only fires after a re-check confirms the port
1388
- * is NOT listening — a server that bound a hair after the deadline is kept, not
1389
- * torn down (the reported race). The retry runs at most once.
1463
+ * Invariant: a server that has bound is never torn down by its own launch. The
1464
+ * destructive `child.kill()` only fires after the server is asked
1465
+ * (`fetchIdentityFn`) AND the port is probed, and both come back empty. The
1466
+ * retry waits for the killed attempt to release the port, and runs at most once.
1390
1467
  */
1391
- async function resolveBootedServer({ port, startTimeoutMs, spawnAndAwait, isPortInUseFn }) {
1392
- // One boot attempt, with the re-check folded in: a post-deadline bind is
1393
- // reported as "ready" rather than "timeout" so the caller never kills it.
1394
- const attempt = async (timeoutMs) => {
1468
+ async function resolveBootedServer({
1469
+ port,
1470
+ startTimeoutMs,
1471
+ spawnAndAwait,
1472
+ isPortInUseFn,
1473
+ fetchIdentityFn = async () => null,
1474
+ waitForPortToCloseFn = async () => true,
1475
+ trace = () => {},
1476
+ }) {
1477
+ // One boot attempt, with the keep/adopt checks folded in: a post-deadline bind
1478
+ // is reported as "ready" rather than "timeout" so the caller never kills it.
1479
+ const attempt = async (timeoutMs, label) => {
1395
1480
  const { child, outcome, exitedBeforeBind } = await spawnAndAwait(timeoutMs);
1481
+ trace("attempt-result", {
1482
+ attempt: label,
1483
+ child_pid: child && child.pid,
1484
+ port,
1485
+ budget_ms: timeoutMs,
1486
+ outcome,
1487
+ exited: exitedBeforeBind ?? undefined,
1488
+ });
1396
1489
  if (outcome === "ready") return { kind: "ready", child };
1397
1490
  if (outcome === "exited" || (outcome === "timeout" && exitedBeforeBind !== null)) {
1491
+ // The child died before binding. When the port is held by a live server of
1492
+ // THIS project (a concurrent launch won the bind), adopt it rather than
1493
+ // ending the launch with zero servers.
1494
+ const holder = await fetchIdentityFn(port);
1495
+ if (holder) {
1496
+ trace("adopt", { attempt: label, port, holder_pid: holder.pid, reason: "lost-bind-race" });
1497
+ return { kind: "ready", child: null, adopted: holder };
1498
+ }
1398
1499
  return { kind: "early-exit", exitedBeforeBind };
1399
1500
  }
1400
- // Genuine timeout — re-check before reporting a miss.
1401
- if (await isPortInUseFn(port)) return { kind: "ready", child };
1501
+ // Genuine timeout — ask the server before reporting a miss.
1502
+ const identity = await fetchIdentityFn(port);
1503
+ if (identity) {
1504
+ trace("keep", { attempt: label, child_pid: child.pid, port, decided_by: "identity" });
1505
+ return { kind: "ready", child };
1506
+ }
1507
+ if (await isPortInUseFn(port)) {
1508
+ trace("keep", { attempt: label, child_pid: child.pid, port, decided_by: "tcp" });
1509
+ return { kind: "ready", child };
1510
+ }
1402
1511
  return { kind: "timeout", child };
1403
1512
  };
1404
1513
 
1405
- const first = await attempt(startTimeoutMs);
1514
+ const first = await attempt(startTimeoutMs, "first");
1406
1515
  if (first.kind !== "timeout") return first;
1407
1516
 
1408
1517
  // The boot was just slow (cold caches, broker respawn). Kill the stalled
@@ -1413,11 +1522,17 @@ async function resolveBootedServer({ port, startTimeoutMs, spawnAndAwait, isPort
1413
1522
  `Server did not bind port ${port} within ${Math.round(startTimeoutMs / 1000)}s; ` +
1414
1523
  `retrying once with a longer budget...`,
1415
1524
  );
1525
+ trace("kill", { child_pid: first.child.pid, port, reason: "no-identity-no-listener-after-budget" });
1416
1526
  first.child.kill();
1527
+ // Let the killed attempt release the port, so a late bind of the dying child
1528
+ // cannot make the retry fail with "port held" (the double-spawn shape).
1529
+ const released = await waitForPortToCloseFn(port);
1530
+ trace("retry-spawn", { port, budget_ms: retryTimeoutMs, prior_port_released: released });
1417
1531
 
1418
- const second = await attempt(retryTimeoutMs);
1532
+ const second = await attempt(retryTimeoutMs, "retry");
1419
1533
  if (second.kind !== "timeout") return second;
1420
1534
 
1535
+ trace("kill", { child_pid: second.child.pid, port, reason: "retry-budget-exhausted" });
1421
1536
  second.child.kill();
1422
1537
  return { kind: "timeout-exhausted", retryTimeoutMs };
1423
1538
  }
@@ -1480,6 +1595,7 @@ module.exports = {
1480
1595
  formatLaunchFailureError,
1481
1596
  ensureProxyPortAvailable,
1482
1597
  formatServerStartTimeoutError,
1598
+ formatServerNotAnsweringError,
1483
1599
  resolveStartTimeoutMs,
1484
1600
  tryKillPid,
1485
1601
  findListeningPids,
@@ -1487,6 +1603,7 @@ module.exports = {
1487
1603
  stopServer,
1488
1604
  restartServer,
1489
1605
  ensureServer,
1606
+ serverStartArgs,
1490
1607
  resolveBootedServer,
1491
1608
  awaitServerReadyOrChildExit,
1492
1609
  isInteractiveLauncher,
@@ -1496,6 +1613,8 @@ module.exports = {
1496
1613
  openServerOutputLog,
1497
1614
  formatLauncherSignalRecord,
1498
1615
  logLauncherTeardownSignal,
1616
+ ownServerIdentity,
1617
+ waitForOwnServer,
1499
1618
  killChildProcess,
1500
1619
  deepMergeJson,
1501
1620
  stripUnderscoreTopLevelKeys,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeyam-editor/codeyam-editor",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Language-agnostic managed execution sandbox for scenario-driven development",
5
5
  "bin": {
6
6
  "codeyam-editor": "./npm/cli.js"
@@ -9,10 +9,10 @@
9
9
  "playwright": "^1.58.2"
10
10
  },
11
11
  "optionalDependencies": {
12
- "@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.14",
13
- "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.14",
14
- "@codeyam-editor/codeyam-editor-linux-x64": "0.1.14",
15
- "@codeyam-editor/codeyam-editor-win32-x64": "0.1.14"
12
+ "@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.15",
13
+ "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.15",
14
+ "@codeyam-editor/codeyam-editor-linux-x64": "0.1.15",
15
+ "@codeyam-editor/codeyam-editor-win32-x64": "0.1.15"
16
16
  },
17
17
  "keywords": [
18
18
  "codeyam",
@@ -1,2 +1,2 @@
1
- import{j as e}from"./markdown-C9tbsVHX.js";import{b as a}from"./react-nrLBr15I.js";import{u as O}from"./useEvents-DNb1olaj.js";import{m as E}from"./index-CqKB9WVm.js";function P({connected:t}){return e.jsx("span",{className:`inline-block rounded-full px-2 py-0.5 text-xs font-medium text-[var(--text-primary)] ${t?"bg-[var(--accent-green)]":"bg-[var(--accent-red)]"}`,children:t?"connected":"reconnecting…"})}const re=Object.freeze(Object.defineProperty({__proto__:null,ConnectionBadge:P},Symbol.toStringTag,{value:"Module"}));function N(){const[t,r]=a.useState(null),[o,s]=a.useState(!0),{events:l}=O(),n=a.useCallback(async()=>{try{const c=await(await fetch("/api/inspector/status")).json();r(c)}catch{}finally{s(!1)}},[]);return a.useEffect(()=>{n()},[n]),a.useEffect(()=>{const i=l[l.length-1];i&&i.type==="scenario_switch"&&n()},[l,n]),{scenario:(t==null?void 0:t.scenario)??null,previewScenario:(t==null?void 0:t.previewScenario)??null,routes:(t==null?void 0:t.routes)??[],routeCount:(t==null?void 0:t.routeCount)??0,loading:o,refetch:n}}function v({title:t}){return e.jsx("h2",{style:{fontSize:14,fontWeight:600,color:"#f0f6fc",margin:"0 0 8px 0",borderBottom:"1px solid #30363d",paddingBottom:8},children:t})}function f({color:t,children:r}){return e.jsx("span",{style:{background:`${t}22`,color:t,border:`1px solid ${t}55`,borderRadius:4,padding:"2px 8px",fontSize:11,fontWeight:600,letterSpacing:"0.5px",whiteSpace:"nowrap"},children:r})}const $={GET:"#D7FF63",POST:"#238636",PUT:"#d29922",DELETE:"#da3633",PATCH:"#bc8cff"};function k({method:t}){const r=$[t.toUpperCase()]??"#8b949e";return e.jsx("span",{style:{color:r,fontWeight:600,fontSize:11,minWidth:48,display:"inline-block"},children:t.toUpperCase()})}function C({color:t,children:r}){return e.jsx("div",{style:{padding:"12px 16px",borderRadius:6,border:`1px solid ${t}44`,background:`${t}11`,color:t},children:r})}const u={padding:"8px 12px",textAlign:"center",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px"},h={padding:"6px 12px"},S={background:"#161b22",border:"1px solid #30363d",borderRadius:6,color:"#c9d1d9",padding:"6px 12px",fontSize:13,fontFamily:"inherit"},R={...S,appearance:"none",paddingRight:24,backgroundImage:`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238b949e' d='M6 8L1 3h10z'/%3E%3C/svg%3E")`,backgroundRepeat:"no-repeat",backgroundPosition:"right 8px center"},oe=Object.freeze(Object.defineProperty({__proto__:null,Badge:f,MethodBadge:k,SectionHeader:v,StatusBox:C,inputStyle:S,selectStyle:R,tdStyle:h,thStyle:u},Symbol.toStringTag,{value:"Module"}));function z({routes:t}){return e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d"},children:[e.jsx("th",{style:u,children:"Method"}),e.jsx("th",{style:{...u,textAlign:"left"},children:"Pattern"}),e.jsx("th",{style:u,children:"Status"})]})}),e.jsx("tbody",{children:t.map((r,o)=>e.jsxs("tr",{style:{borderBottom:"1px solid #21262d"},children:[e.jsx("td",{style:{...h,textAlign:"center"},children:e.jsx(k,{method:r.method})}),e.jsx("td",{style:h,children:r.pattern}),e.jsx("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:r.status})]},o))})]})}const se=Object.freeze(Object.defineProperty({__proto__:null,InspectorRouteTable:z},Symbol.toStringTag,{value:"Module"}));function A(){const{scenario:t,previewScenario:r,routes:o,routeCount:s,loading:l}=N();return e.jsxs("section",{children:[e.jsx(v,{title:"Active Scenario"}),l?e.jsx(C,{color:"#8b949e",children:"Loading..."}):t?e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx(f,{color:"#238636",children:"ACTIVE"}),e.jsx("span",{style:{color:"#f0f6fc",fontWeight:500},children:t.name}),e.jsxs("span",{style:{color:"#8b949e"},children:["(",t.slug,")"]})]}),e.jsxs("div",{style:{color:"#8b949e",fontSize:12},children:[s," mock route",s!==1?"s":""," loaded"]}),o.length>0&&e.jsx(z,{routes:o})]}):r?e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx(f,{color:"#D7FF63",children:"PREVIEW"}),e.jsx("span",{style:{color:"#f0f6fc",fontWeight:500},children:r.name}),e.jsxs("span",{style:{color:"#8b949e"},children:["(",r.slug,")"]})]}),e.jsx("div",{style:{color:"#8b949e",fontSize:12},children:"Component preview — no HTTP mocks active"})]}):e.jsx(C,{color:"#d29922",children:"No scenario active — all requests pass through to upstream"})]})}const ne=Object.freeze(Object.defineProperty({__proto__:null,InspectorScenarioSection:A},Symbol.toStringTag,{value:"Module"}));function F({textUrlFilter:t,onTextUrlFilterChange:r,methodFilter:o,onMethodFilterChange:s,sourceFilter:l,onSourceFilterChange:n}){return e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12,alignItems:"center"},children:[e.jsx("input",{type:"text",placeholder:"Filter by URL...",value:t,onChange:i=>r(i.target.value),style:{...S,flex:1,minWidth:0}}),e.jsxs("select",{value:o,onChange:i=>s(i.target.value),style:R,children:[e.jsx("option",{value:"ALL",children:"All Methods"}),e.jsx("option",{value:"GET",children:"GET"}),e.jsx("option",{value:"POST",children:"POST"}),e.jsx("option",{value:"PUT",children:"PUT"}),e.jsx("option",{value:"DELETE",children:"DELETE"}),e.jsx("option",{value:"PATCH",children:"PATCH"})]}),e.jsxs("select",{value:l,onChange:i=>n(i.target.value),style:R,children:[e.jsx("option",{value:"ALL",children:"All Sources"}),e.jsx("option",{value:"MOCKED",children:"Mocked"}),e.jsx("option",{value:"PASSTHROUGH",children:"Passthrough"})]})]})}const ie=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficFilterBar:F},Symbol.toStringTag,{value:"Module"})),G={color:"#8b949e",fontSize:11,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.5px",marginBottom:4},M={background:"#161b22",border:"1px solid #30363d",borderRadius:4,padding:"8px 12px",margin:0,fontSize:11,color:"#c9d1d9",fontFamily:"monospace",maxHeight:200,overflow:"auto",whiteSpace:"pre-wrap",wordBreak:"break-all"};function _({label:t,children:r}){return e.jsxs("div",{style:{marginBottom:12},children:[e.jsx("div",{style:G,children:t}),r]})}function B({event:t}){let r=[];try{const n=new URL(t.url,"http://localhost");r=Array.from(n.searchParams.entries())}catch{}const o=t.requestHeaders&&Object.keys(t.requestHeaders).length>0,s=t.requestBody!=null,l=t.responseBody!=null;return e.jsxs("div",{style:{background:"#0d1117",padding:"12px 16px",borderTop:"1px solid #30363d"},children:[r.length>0&&e.jsx(_,{label:"Query Parameters",children:e.jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"2px 12px",fontSize:12},children:r.map(([n,i],c)=>e.jsxs("div",{style:{display:"contents"},children:[e.jsx("span",{style:{color:"#D7FF63",fontFamily:"monospace"},children:n}),e.jsx("span",{style:{color:"#c9d1d9",fontFamily:"monospace"},children:i})]},c))})}),o&&e.jsx(_,{label:"Request Headers",children:e.jsx("pre",{style:M,children:Object.entries(t.requestHeaders).map(([n,i])=>`${n}: ${i}`).join(`
1
+ import{j as e}from"./markdown-C9tbsVHX.js";import{b as a}from"./react-nrLBr15I.js";import{u as O}from"./useEvents-DNb1olaj.js";import{m as E}from"./index-ClrdYzlw.js";function P({connected:t}){return e.jsx("span",{className:`inline-block rounded-full px-2 py-0.5 text-xs font-medium text-[var(--text-primary)] ${t?"bg-[var(--accent-green)]":"bg-[var(--accent-red)]"}`,children:t?"connected":"reconnecting…"})}const re=Object.freeze(Object.defineProperty({__proto__:null,ConnectionBadge:P},Symbol.toStringTag,{value:"Module"}));function N(){const[t,r]=a.useState(null),[o,s]=a.useState(!0),{events:l}=O(),n=a.useCallback(async()=>{try{const c=await(await fetch("/api/inspector/status")).json();r(c)}catch{}finally{s(!1)}},[]);return a.useEffect(()=>{n()},[n]),a.useEffect(()=>{const i=l[l.length-1];i&&i.type==="scenario_switch"&&n()},[l,n]),{scenario:(t==null?void 0:t.scenario)??null,previewScenario:(t==null?void 0:t.previewScenario)??null,routes:(t==null?void 0:t.routes)??[],routeCount:(t==null?void 0:t.routeCount)??0,loading:o,refetch:n}}function v({title:t}){return e.jsx("h2",{style:{fontSize:14,fontWeight:600,color:"#f0f6fc",margin:"0 0 8px 0",borderBottom:"1px solid #30363d",paddingBottom:8},children:t})}function f({color:t,children:r}){return e.jsx("span",{style:{background:`${t}22`,color:t,border:`1px solid ${t}55`,borderRadius:4,padding:"2px 8px",fontSize:11,fontWeight:600,letterSpacing:"0.5px",whiteSpace:"nowrap"},children:r})}const $={GET:"#D7FF63",POST:"#238636",PUT:"#d29922",DELETE:"#da3633",PATCH:"#bc8cff"};function k({method:t}){const r=$[t.toUpperCase()]??"#8b949e";return e.jsx("span",{style:{color:r,fontWeight:600,fontSize:11,minWidth:48,display:"inline-block"},children:t.toUpperCase()})}function C({color:t,children:r}){return e.jsx("div",{style:{padding:"12px 16px",borderRadius:6,border:`1px solid ${t}44`,background:`${t}11`,color:t},children:r})}const u={padding:"8px 12px",textAlign:"center",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px"},h={padding:"6px 12px"},S={background:"#161b22",border:"1px solid #30363d",borderRadius:6,color:"#c9d1d9",padding:"6px 12px",fontSize:13,fontFamily:"inherit"},R={...S,appearance:"none",paddingRight:24,backgroundImage:`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238b949e' d='M6 8L1 3h10z'/%3E%3C/svg%3E")`,backgroundRepeat:"no-repeat",backgroundPosition:"right 8px center"},oe=Object.freeze(Object.defineProperty({__proto__:null,Badge:f,MethodBadge:k,SectionHeader:v,StatusBox:C,inputStyle:S,selectStyle:R,tdStyle:h,thStyle:u},Symbol.toStringTag,{value:"Module"}));function z({routes:t}){return e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d"},children:[e.jsx("th",{style:u,children:"Method"}),e.jsx("th",{style:{...u,textAlign:"left"},children:"Pattern"}),e.jsx("th",{style:u,children:"Status"})]})}),e.jsx("tbody",{children:t.map((r,o)=>e.jsxs("tr",{style:{borderBottom:"1px solid #21262d"},children:[e.jsx("td",{style:{...h,textAlign:"center"},children:e.jsx(k,{method:r.method})}),e.jsx("td",{style:h,children:r.pattern}),e.jsx("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:r.status})]},o))})]})}const se=Object.freeze(Object.defineProperty({__proto__:null,InspectorRouteTable:z},Symbol.toStringTag,{value:"Module"}));function A(){const{scenario:t,previewScenario:r,routes:o,routeCount:s,loading:l}=N();return e.jsxs("section",{children:[e.jsx(v,{title:"Active Scenario"}),l?e.jsx(C,{color:"#8b949e",children:"Loading..."}):t?e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx(f,{color:"#238636",children:"ACTIVE"}),e.jsx("span",{style:{color:"#f0f6fc",fontWeight:500},children:t.name}),e.jsxs("span",{style:{color:"#8b949e"},children:["(",t.slug,")"]})]}),e.jsxs("div",{style:{color:"#8b949e",fontSize:12},children:[s," mock route",s!==1?"s":""," loaded"]}),o.length>0&&e.jsx(z,{routes:o})]}):r?e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8},children:[e.jsx(f,{color:"#D7FF63",children:"PREVIEW"}),e.jsx("span",{style:{color:"#f0f6fc",fontWeight:500},children:r.name}),e.jsxs("span",{style:{color:"#8b949e"},children:["(",r.slug,")"]})]}),e.jsx("div",{style:{color:"#8b949e",fontSize:12},children:"Component preview — no HTTP mocks active"})]}):e.jsx(C,{color:"#d29922",children:"No scenario active — all requests pass through to upstream"})]})}const ne=Object.freeze(Object.defineProperty({__proto__:null,InspectorScenarioSection:A},Symbol.toStringTag,{value:"Module"}));function F({textUrlFilter:t,onTextUrlFilterChange:r,methodFilter:o,onMethodFilterChange:s,sourceFilter:l,onSourceFilterChange:n}){return e.jsxs("div",{style:{display:"flex",gap:8,marginBottom:12,alignItems:"center"},children:[e.jsx("input",{type:"text",placeholder:"Filter by URL...",value:t,onChange:i=>r(i.target.value),style:{...S,flex:1,minWidth:0}}),e.jsxs("select",{value:o,onChange:i=>s(i.target.value),style:R,children:[e.jsx("option",{value:"ALL",children:"All Methods"}),e.jsx("option",{value:"GET",children:"GET"}),e.jsx("option",{value:"POST",children:"POST"}),e.jsx("option",{value:"PUT",children:"PUT"}),e.jsx("option",{value:"DELETE",children:"DELETE"}),e.jsx("option",{value:"PATCH",children:"PATCH"})]}),e.jsxs("select",{value:l,onChange:i=>n(i.target.value),style:R,children:[e.jsx("option",{value:"ALL",children:"All Sources"}),e.jsx("option",{value:"MOCKED",children:"Mocked"}),e.jsx("option",{value:"PASSTHROUGH",children:"Passthrough"})]})]})}const ie=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficFilterBar:F},Symbol.toStringTag,{value:"Module"})),G={color:"#8b949e",fontSize:11,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.5px",marginBottom:4},M={background:"#161b22",border:"1px solid #30363d",borderRadius:4,padding:"8px 12px",margin:0,fontSize:11,color:"#c9d1d9",fontFamily:"monospace",maxHeight:200,overflow:"auto",whiteSpace:"pre-wrap",wordBreak:"break-all"};function _({label:t,children:r}){return e.jsxs("div",{style:{marginBottom:12},children:[e.jsx("div",{style:G,children:t}),r]})}function B({event:t}){let r=[];try{const n=new URL(t.url,"http://localhost");r=Array.from(n.searchParams.entries())}catch{}const o=t.requestHeaders&&Object.keys(t.requestHeaders).length>0,s=t.requestBody!=null,l=t.responseBody!=null;return e.jsxs("div",{style:{background:"#0d1117",padding:"12px 16px",borderTop:"1px solid #30363d"},children:[r.length>0&&e.jsx(_,{label:"Query Parameters",children:e.jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"2px 12px",fontSize:12},children:r.map(([n,i],c)=>e.jsxs("div",{style:{display:"contents"},children:[e.jsx("span",{style:{color:"#D7FF63",fontFamily:"monospace"},children:n}),e.jsx("span",{style:{color:"#c9d1d9",fontFamily:"monospace"},children:i})]},c))})}),o&&e.jsx(_,{label:"Request Headers",children:e.jsx("pre",{style:M,children:Object.entries(t.requestHeaders).map(([n,i])=>`${n}: ${i}`).join(`
2
2
  `)})}),s&&e.jsx(_,{label:"Request Body",children:e.jsx("pre",{style:M,children:typeof t.requestBody=="string"?t.requestBody:JSON.stringify(t.requestBody,null,2)})}),l&&e.jsx(_,{label:"Response Body",children:e.jsx("pre",{style:M,children:typeof t.responseBody=="string"?t.responseBody:JSON.stringify(t.responseBody,null,2)})}),r.length===0&&!o&&!s&&!l&&e.jsx("div",{style:{color:"#8b949e",fontSize:12},children:"No additional details available for this request"})]})}const le=Object.freeze(Object.defineProperty({__proto__:null,DetailSection:_,InspectorTrafficDetail:B},Symbol.toStringTag,{value:"Module"}));function L({event:t}){const[r,o]=a.useState(!1),s=new Date(t.timestampMs).toLocaleTimeString(),l=t.mocked;return e.jsxs(e.Fragment,{children:[e.jsxs("tr",{onClick:()=>o(!r),style:{borderBottom:"1px solid #21262d",background:l?"transparent":"rgba(210, 153, 34, 0.05)",cursor:"pointer"},children:[e.jsxs("td",{style:{...h,color:"#8b949e",textAlign:"center"},children:[e.jsx("span",{style:{display:"inline-block",width:12,marginRight:4,fontSize:10,color:"#8b949e",transition:"transform 0.15s",transform:r?"rotate(90deg)":"rotate(0deg)"},children:"▶"}),s]}),e.jsx("td",{style:{...h,textAlign:"center"},children:e.jsx(k,{method:t.method})}),e.jsx("td",{style:{...h,maxWidth:400,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:t.url}),e.jsx("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:t.status??"---"}),e.jsxs("td",{style:{...h,textAlign:"center",color:"#8b949e"},children:[t.durationMs,"ms"]}),e.jsx("td",{style:{...h,textAlign:"center"},children:l?t.mockSource==="default"?e.jsx(f,{color:"#1f6feb",children:"MOCKED (default)"}):e.jsx(f,{color:"#238636",children:"MOCKED (scenario)"}):e.jsx(f,{color:"#d29922",children:"PASSTHROUGH"})})]}),r&&e.jsx("tr",{children:e.jsx("td",{colSpan:6,style:{padding:0},children:e.jsx(B,{event:t})})})]})}const ae=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficRow:L},Symbol.toStringTag,{value:"Module"}));function D({events:t}){return e.jsx("div",{style:{maxHeight:300,overflowY:"auto",border:"1px solid #30363d",borderRadius:6},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d",position:"sticky",top:0,background:"#0d1117"},children:[e.jsx("th",{style:u,children:"Time"}),e.jsx("th",{style:u,children:"Method"}),e.jsx("th",{style:{...u,textAlign:"left"},children:"URL"}),e.jsx("th",{style:u,children:"Status"}),e.jsx("th",{style:u,children:"Duration"}),e.jsx("th",{style:u,children:"Source"})]})}),e.jsx("tbody",{children:t.map(r=>e.jsx(L,{event:r},r.id))})]})})}const ce=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficTable:D},Symbol.toStringTag,{value:"Module"}));function K(t){if(t===void 0)return"checked";if(t==="checked")return"excluded"}function I({chips:t,chipStates:r,onChipStatesChange:o}){if(t.length===0)return null;const s=n=>{const i=new Map(r),c=K(i.get(n));c===void 0?i.delete(n):i.set(n,c),o(i)},l=()=>{o(new Map)};return e.jsxs("div",{style:{display:"flex",flexWrap:"wrap",gap:6,marginBottom:12},children:[r.size>0&&e.jsx("button",{onClick:l,style:{background:"#da363322",color:"#da3633",border:"1px solid #da363355",borderRadius:4,padding:"2px 8px",fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:"inherit"},children:"Clear all"}),t.map(({path:n,count:i})=>{const c=r.get(n),d=c==="checked",g=c==="excluded";let m="#30363d",b="#8b949e",T="#30363d",y="";return d?(m="#D7FF6322",b="#D7FF63",T="#D7FF6355",y="✓ "):g&&(m="#da363322",b="#da3633",T="#da363355",y="✗ "),e.jsxs("button",{onClick:()=>s(n),style:{background:m,color:b,border:`1px solid ${T}`,borderRadius:4,padding:"2px 8px",fontSize:11,fontFamily:"monospace",cursor:"pointer",whiteSpace:"nowrap"},children:[y,n,e.jsxs("span",{style:{marginLeft:4,opacity:.6,fontFamily:"inherit"},children:["(",i,")"]})]},n)})]})}const de=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficUrlChips:I},Symbol.toStringTag,{value:"Module"}));function H(){const{events:t}=O(),[r,o]=a.useState(""),[s,l]=a.useState(()=>new Map),[n,i]=a.useState("ALL"),[c,d]=a.useState("ALL"),g=t.filter(p=>p.type==="http_request"),m=a.useMemo(()=>{const p=new Map;for(const x of g){const j=E(x.url);p.set(j,(p.get(j)||0)+1)}return Array.from(p.entries()).sort((x,j)=>j[1]-x[1]).slice(0,10).map(([x,j])=>({path:x,count:j}))},[g]),b=a.useMemo(()=>Array.from(s.values()).some(p=>p==="checked"),[s]),y=g.filter(p=>{if(r&&!p.url.toLowerCase().includes(r.toLowerCase()))return!1;const x=E(p.url);return!(s.get(x)==="excluded"||b&&s.get(x)!=="checked"||n!=="ALL"&&p.method.toUpperCase()!==n||c==="MOCKED"&&!p.mocked||c==="PASSTHROUGH"&&p.mocked)}).slice(-50).reverse();return e.jsxs("section",{children:[e.jsx(v,{title:"Live Traffic"}),e.jsx(F,{textUrlFilter:r,onTextUrlFilterChange:o,methodFilter:n,onMethodFilterChange:i,sourceFilter:c,onSourceFilterChange:p=>d(p)}),e.jsx(I,{chips:m,chipStates:s,onChipStatesChange:l}),y.length===0?e.jsx(C,{color:"#8b949e",children:g.length>0?"No matching traffic — try adjusting the filters":"No HTTP traffic yet — requests will appear here in real-time"}):e.jsx(D,{events:y})]})}const pe=Object.freeze(Object.defineProperty({__proto__:null,InspectorTrafficSection:H},Symbol.toStringTag,{value:"Module"}));function J(){const[t,r]=a.useState(null),[o,s]=a.useState(!1);return{explain:a.useCallback(async(n,i)=>{s(!0);try{const d=await(await fetch("/api/inspector/explain",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({method:n,url:i})})).json();r(d)}catch{}finally{s(!1)}},[]),result:t,loading:o}}function U({result:t}){return e.jsxs("div",{style:{border:"1px solid #30363d",borderRadius:6,padding:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:12},children:[e.jsxs("span",{style:{color:"#f0f6fc",fontWeight:500},children:[t.method," ",t.url]}),t.wouldMatch?e.jsx(f,{color:"#238636",children:"WOULD MATCH"}):e.jsx(f,{color:"#da3633",children:"NO MATCH"})]}),t.attempts.length===0?e.jsx("div",{style:{color:"#8b949e"},children:"No routes loaded to match against"}):e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:6},children:t.attempts.map((r,o)=>e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,padding:"4px 8px",borderRadius:4,background:r.failure?"transparent":"rgba(35, 134, 54, 0.1)"},children:[e.jsx("span",{style:{color:r.failure?"#da3633":"#238636",fontWeight:600,width:16},children:r.failure?"x":"v"}),e.jsx(k,{method:r.method}),e.jsx("span",{children:r.pattern}),r.failure&&e.jsx("span",{style:{color:"#8b949e",fontSize:11},children:r.failure.reason==="methodMismatch"?`method: expected ${r.failure.expected}, got ${r.failure.actual}`:`path "${r.failure.path}" does not match "${r.failure.pattern}"`})]},o))})]})}const ue=Object.freeze(Object.defineProperty({__proto__:null,InspectorExplainerResult:U},Symbol.toStringTag,{value:"Module"}));function W(){const{explain:t,result:r,loading:o}=J(),[s,l]=a.useState("GET"),[n,i]=a.useState(""),c=d=>{d.preventDefault(),n.trim()&&t(s,n.trim())};return e.jsxs("section",{children:[e.jsx(v,{title:"Match Explainer"}),e.jsxs("form",{onSubmit:c,style:{display:"flex",gap:8,marginBottom:12},children:[e.jsx("select",{value:s,onChange:d=>l(d.target.value),style:{...S,width:100},children:["GET","POST","PUT","DELETE","PATCH"].map(d=>e.jsx("option",{value:d,children:d},d))}),e.jsx("input",{type:"text",value:n,onChange:d=>i(d.target.value),placeholder:"/api/users",style:{...S,flex:1}}),e.jsx("button",{type:"submit",disabled:o||!n.trim(),style:{background:"#238636",color:"#fff",border:"none",borderRadius:6,padding:"6px 16px",cursor:"pointer",fontSize:12,fontWeight:500,opacity:o||!n.trim()?.5:1},children:o?"Checking...":"Explain"})]}),r&&e.jsx(U,{result:r})]})}const he=Object.freeze(Object.defineProperty({__proto__:null,InspectorExplainerSection:W},Symbol.toStringTag,{value:"Module"})),w={"/api/health":"Health check — confirms the proxy and mock engine are running","/api/events":"SSE stream of real-time events (traffic, errors) powering this Inspector","/api/config":"Returns current CodeYam configuration for the project","/api/editor-dev-server":"Polls/controls the dev server status (start, stop, restart)","/api/inspector/status":"Returns active scenario and loaded mock routes","/api/inspector/explain":"Tests which mock route would match a given method + URL","/api/scenarios":"Lists all defined scenarios with metadata","/api/tests":"Returns the AI-maintained test registry joined with per-test-evidence, grouped by file","/api/test-results":"Returns latest test execution results","/api/screenshots/*":"Serves captured scenario screenshots","/api/session-info":"Returns current editor session metadata","/api/data-structure":"Returns the app's data structure definition","/api/dependency-graph":"Returns the project's component dependency graph","/api/app-route-entities":"Lists app routes and their associated entities","/api/glossary":"Returns the project's component glossary","/api/step":"Returns current editor workflow step","/api/editor-commands":"Lists available editor commands"};function V(t){if(w[t])return w[t];for(const[r,o]of Object.entries(w))if(r.endsWith("/*")){const s=r.slice(0,-1);if(t.startsWith(s))return o}}function Y(t){const r=new Set([...t,...Object.keys(w)]),o=[];for(const s of Array.from(r).sort()){if(s.endsWith("/*"))continue;const l=V(s);o.push({path:s,description:l??"Application endpoint"})}return o}function q(){const{events:t}=O(),r=a.useMemo(()=>{const o=[];for(const s of t)s.type==="http_request"&&o.push(E(s.url));return Y(o)},[t]);return r.length===0?null:e.jsxs("section",{children:[e.jsx(v,{title:"URL Glossary"}),e.jsx("div",{style:{border:"1px solid #30363d",borderRadius:6,overflow:"hidden"},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{borderBottom:"1px solid #30363d",background:"#161b22"},children:[e.jsx("th",{style:{padding:"6px 12px",textAlign:"left",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px",width:"30%"},children:"Path"}),e.jsx("th",{style:{padding:"6px 12px",textAlign:"left",color:"#8b949e",fontWeight:500,fontSize:11,textTransform:"uppercase",letterSpacing:"0.5px"},children:"Description"})]})}),e.jsx("tbody",{children:r.map(({path:o,description:s})=>e.jsxs("tr",{style:{borderBottom:"1px solid #21262d"},children:[e.jsx("td",{style:{padding:"4px 12px",fontFamily:"monospace",color:"#D7FF63",fontSize:11},children:o}),e.jsx("td",{style:{padding:"4px 12px",color:"#8b949e"},children:s})]},o))})]})})]})}const xe=Object.freeze(Object.defineProperty({__proto__:null,InspectorUrlGlossary:q},Symbol.toStringTag,{value:"Module"}));function Q(){const{connected:t}=O();return e.jsxs("div",{style:{background:"#0d1117",color:"#c9d1d9",minHeight:"100vh",fontFamily:"'SF Mono', 'Fira Code', 'Cascadia Code', Menlo, monospace",fontSize:13,padding:24,display:"flex",flexDirection:"column",gap:24},children:[e.jsxs("header",{style:{display:"flex",alignItems:"center",gap:12},children:[e.jsx("h1",{style:{fontSize:18,fontWeight:600,color:"#f0f6fc",margin:0},children:"Container Inspector"}),e.jsx("span",{style:{color:"#8b949e",fontSize:12},children:"Read-only view of proxy and mock engine state"}),e.jsx(P,{connected:t})]}),e.jsx(A,{}),e.jsx(H,{}),e.jsx(W,{}),e.jsx(q,{})]})}const fe=Object.freeze(Object.defineProperty({__proto__:null,ContainerInspector:Q},Symbol.toStringTag,{value:"Module"}));export{f as B,Q as C,_ as D,L as I,k as M,v as S,xe as _,C as a,q as b,I as c,D as d,F as e,B as f,U as g,W as h,H as i,z as j,A as k,P as l,de as m,ce as n,pe as o,ae as p,ie as q,le as r,ne as s,u as t,se as u,oe as v,he as w,ue as x,fe as y,re as z};