@codeyam-editor/codeyam-editor 0.1.7 → 0.1.8

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 (30) hide show
  1. package/npm/cloud.js +116 -23
  2. package/npm/fleet-add-failure.js +59 -12
  3. package/npm/fleet-memory.js +29 -4
  4. package/npm/fleet-queue.js +141 -0
  5. package/npm/fleet-vm-access.js +162 -0
  6. package/npm/ingress-invoke.js +46 -2
  7. package/npm/launch-config.js +43 -0
  8. package/npm/scenario-check.js +11 -1
  9. package/npm/scenario-handlers.js +18 -0
  10. package/npm/scenario-issues.js +11 -0
  11. package/npm/scenario-mocks.js +245 -39
  12. package/npm/scenario-playwright.js +70 -12
  13. package/package.json +5 -5
  14. package/ui/dist/assets/{ContainerInspector-Cu1tSN-o.js → ContainerInspector-DSVZvEkB.js} +1 -1
  15. package/ui/dist/assets/EditorShell-DnXdHrar.js +256 -0
  16. package/ui/dist/assets/EditorShell-FvbXyqqD.css +1 -0
  17. package/ui/dist/assets/{ProjectLauncherScreen-Dmm6Z0mv.js → ProjectLauncherScreen-E0NYYiwy.js} +1 -1
  18. package/ui/dist/assets/ScenarioDataPanel-6qJE-0lA.js +15 -0
  19. package/ui/dist/assets/{ScenarioDataPanelFullPage-BqMxWVKW.js → ScenarioDataPanelFullPage-Cyk6efiI.js} +1 -1
  20. package/ui/dist/assets/ScenarioRenderer-Uyt54vHI.js +21 -0
  21. package/ui/dist/assets/{index-VuyqBbGJ.js → index-BPLKgwon.js} +9 -9
  22. package/ui/dist/assets/index-DKpgJLo3.css +1 -0
  23. package/ui/dist/assets/useServerIdentity-CVuHTzjg.js +1 -0
  24. package/ui/dist/index.html +2 -2
  25. package/ui/dist/assets/EditorShell--V2ExYQ6.js +0 -260
  26. package/ui/dist/assets/EditorShell-DP5UY20G.css +0 -1
  27. package/ui/dist/assets/ScenarioDataPanel-DjprZq2L.js +0 -9
  28. package/ui/dist/assets/ScenarioRenderer-DqLVVMYS.js +0 -21
  29. package/ui/dist/assets/index-CjxWhkSy.css +0 -1
  30. package/ui/dist/assets/useServerIdentity-BdXy8TQv.js +0 -1
@@ -1,22 +1,46 @@
1
- // Route matcher shared, semantically, with the injected live-preview shim
2
- // (crates/proxy-http/src/fetch_patch.js::matchUrl / matchPath / matchQuerySpec).
3
- // This is a VERBATIM port of that shim's matcher — the two interception paths
4
- // MUST agree on which requests a mock route covers. When they drifted (the
5
- // shim gained `:param` + query-spec parity but this capture-harness matcher was
6
- // left doing exact string matching), a parameterized or query-spec'd mock
7
- // rendered in the Live Preview but escaped Playwright's `page.route` during
8
- // capture and rendered an empty list. `scenario-mocks.test.js` pins the two to
9
- // identical verdicts on a shared fixture table so a future divergence is a
10
- // test failure, not another lost build session.
1
+ // Route matcher for the capture harness — one of THREE implementations of a
2
+ // single semantic. The authority is the Rust engine
3
+ // (crates/mock-engine/src/route_parser.rs), where the contract is written down;
4
+ // this file and the injected live-preview shim
5
+ // (crates/proxy-http/src/fetch_patch.js) are ports of it.
11
6
  //
12
- // `:param` path segments match any single non-empty segment; a `?k=v` / `?k=*`
13
- // query-spec suffix constrains the query as a subset match (`k=*` matches any
14
- // value; an empty spec matches any query). Full-URL patterns (`http://…`) match
15
- // by prefix on the raw URL. Mirrors crates/mock-engine's server matcher.
7
+ // This is NOT a verbatim copy of the shim, and describing it as one is what let
8
+ // the drift hide: the two JS matchers agreed with EACH OTHER while both
9
+ // disagreed with the server (they prefix-matched full-URL patterns and returned
10
+ // before applying any query spec, where the server anchored). All three are now
11
+ // pinned against one shared fixture table,
12
+ // crates/mock-engine/fixtures/route-match-fixtures.json, asserted with its
13
+ // expected verdicts by both `scenario-mocks.test.js` and the Rust tests — so a
14
+ // change to one implementation the others do not follow is a test failure, and
15
+ // two matchers agreeing on a wrong answer no longer passes.
16
+ //
17
+ // The semantic, in short (see route_parser.rs for the full contract): `:name`
18
+ // and `*` each match one non-empty path segment; a TRAILING `**` matches the
19
+ // remaining path including `/` and including an empty remainder; a `?k=v` /
20
+ // `?k=*` suffix constrains the query as a subset match. A full-URL pattern
21
+ // (`http://…`) with no wildcard matches by PREFIX — the compatibility rule
22
+ // every shipped external mock relies on — while a wildcarded one matches by the
23
+ // wildcard rules.
16
24
  function matchUrl(pattern, url) {
17
- // Full-URL patterns (external APIs) match by prefix on the raw URL.
18
- if (pattern.startsWith("http://") || pattern.startsWith("https://")) {
19
- return url.indexOf(pattern) === 0;
25
+ // Split the query spec off FIRST — for full-URL and path-only patterns
26
+ // alike. Returning early for full URLs (as this did) meant a spec like
27
+ // `?k=*` on an external mock was silently ignored.
28
+ const pqi = pattern.indexOf("?");
29
+ const patPath = pqi >= 0 ? pattern.slice(0, pqi) : pattern;
30
+ const patSpec = pqi >= 0 ? pattern.slice(pqi + 1) : "";
31
+
32
+ // Full-URL patterns (external APIs) match against the whole URL, with its
33
+ // own query stripped before the compare.
34
+ if (patPath.startsWith("http://") || patPath.startsWith("https://")) {
35
+ const [rawPath, rawQuery] = splitUrlQuery(url);
36
+ // Compatibility rule: a wildcard-free full-URL pattern prefix-matches,
37
+ // which is what every external mock relies on today. A wildcarded one
38
+ // matches by the wildcard rules, with no prefix fallback.
39
+ const urlMatched = hasWildcard(patPath)
40
+ ? matchPath(patPath, rawPath)
41
+ : rawPath.indexOf(patPath) === 0;
42
+ if (!urlMatched) return false;
43
+ return matchQuerySpec(patSpec, rawQuery);
20
44
  }
21
45
 
22
46
  let reqPath = url;
@@ -26,35 +50,71 @@ function matchUrl(pattern, url) {
26
50
  reqPath = u.pathname;
27
51
  reqQuery = u.search.replace(/^\?/, "");
28
52
  } catch {
29
- const qi = url.indexOf("?");
30
- if (qi >= 0) {
31
- reqPath = url.slice(0, qi);
32
- reqQuery = url.slice(qi + 1);
33
- }
53
+ [reqPath, reqQuery] = splitUrlQuery(url);
34
54
  }
35
55
 
36
- const patParts = pattern.split("?");
37
- if (!matchPath(patParts[0], reqPath)) return false;
38
- return matchQuerySpec(patParts.length > 1 ? patParts[1] : "", reqQuery);
56
+ if (!matchPath(patPath, reqPath)) return false;
57
+ return matchQuerySpec(patSpec, reqQuery);
58
+ }
59
+
60
+ // Split a raw URL into [everything-before-the-query, query]. The `#fragment`
61
+ // is dropped from both halves, mirroring the server's strip_url_query.
62
+ function splitUrlQuery(url) {
63
+ const qi = url.indexOf("?");
64
+ const hi = url.indexOf("#");
65
+ const cut = qi < 0 ? hi : hi < 0 ? qi : Math.min(qi, hi);
66
+ const head = cut < 0 ? url : url.slice(0, cut);
67
+ if (qi < 0) return [head, ""];
68
+ const query = url.slice(qi + 1);
69
+ const fi = query.indexOf("#");
70
+ return [head, fi >= 0 ? query.slice(0, fi) : query];
71
+ }
72
+
73
+ // True when a segment is a valid `:name` param. A `:`-prefixed segment with
74
+ // an empty or punctuated name is matched literally, same as the server.
75
+ function isParamSegment(seg) {
76
+ return /^:[\p{L}\p{N}_]+$/u.test(seg);
39
77
  }
40
78
 
41
- // Segment-wise path match with `:param` wildcards. A `:name` segment matches
42
- // any single non-empty segment; every other segment must be equal, and the
43
- // segment counts must match — no implicit prefix match, same as the server.
79
+ // True when a segment is any wildcard: `:name`, `*`, or `**`.
80
+ function isWildcardSegment(seg) {
81
+ return seg === "*" || seg === "**" || isParamSegment(seg);
82
+ }
83
+
84
+ // True when the pattern contains any wildcard segment.
85
+ function hasWildcard(pattern) {
86
+ return pattern.split("/").some(isWildcardSegment);
87
+ }
88
+
89
+ // Segment-wise path match. `:name` and `*` each match any single non-empty
90
+ // segment; a TRAILING `**` matches the remaining path including `/` and
91
+ // including an empty remainder (`/api/**` covers `/api`); every other segment
92
+ // must be equal. No implicit prefix match, same as the server.
44
93
  function matchPath(pattern, path) {
45
94
  const pp = pattern.split("/");
46
95
  const rp = path.split("/");
96
+ if (pp[pp.length - 1] === "**") {
97
+ const head = pp.length - 1;
98
+ if (rp.length < head) return false;
99
+ for (let i = 0; i < head; i++) {
100
+ if (!matchSegment(pp[i], rp[i])) return false;
101
+ }
102
+ return true;
103
+ }
47
104
  if (pp.length !== rp.length) return false;
48
105
  for (let i = 0; i < pp.length; i++) {
49
- if (pp[i].charAt(0) === ":") {
50
- if (rp[i] === "") return false;
51
- continue;
52
- }
53
- if (pp[i] !== rp[i]) return false;
106
+ if (!matchSegment(pp[i], rp[i])) return false;
54
107
  }
55
108
  return true;
56
109
  }
57
110
 
111
+ // One segment. `:name`, `*`, and a non-final `**` each match any single
112
+ // non-empty segment; anything else must be equal.
113
+ function matchSegment(pat, seg) {
114
+ if (isWildcardSegment(pat)) return seg !== "";
115
+ return pat === seg;
116
+ }
117
+
58
118
  // Query-spec match. An empty spec matches any query (a path-only route still
59
119
  // matches a request that carries query params). Otherwise every `k=v` pair in
60
120
  // the spec must be present in the request query; `k=*` matches any value.
@@ -82,7 +142,11 @@ function splitMockKey(key) {
82
142
  return { method: key.slice(0, spaceIdx), route: key.slice(spaceIdx + 1) };
83
143
  }
84
144
 
85
- function findHttpMock(httpMocks, request) {
145
+ // The matching mock together with the KEY that declared it, or null. The key is
146
+ // what the usage tally counts — reporting "which declared mock fired" needs the
147
+ // declaration's identity, not just its body, since two keys can carry
148
+ // structurally identical mocks.
149
+ function findHttpMockEntry(httpMocks, request) {
86
150
  const method = request.method().toUpperCase();
87
151
  const url = request.url();
88
152
  for (const [key, mock] of Object.entries(httpMocks)) {
@@ -90,11 +154,16 @@ function findHttpMock(httpMocks, request) {
90
154
  if (!parsed) continue;
91
155
  // Method stays an exact check; only the route is matched by pattern.
92
156
  if (parsed.method.toUpperCase() !== method) continue;
93
- if (matchUrl(parsed.route, url)) return mock;
157
+ if (matchUrl(parsed.route, url)) return { key, mock };
94
158
  }
95
159
  return null;
96
160
  }
97
161
 
162
+ function findHttpMock(httpMocks, request) {
163
+ const entry = findHttpMockEntry(httpMocks, request);
164
+ return entry ? entry.mock : null;
165
+ }
166
+
98
167
  // The set of path/URL route patterns declared by the mock keys. A key is
99
168
  // `"<METHOD> <route>"` (e.g. `"GET /api/plans"`); we strip the method so the
100
169
  // route matcher can decide whether a request *could* match any mock without
@@ -121,8 +190,134 @@ function requestTargetsMock(targets, url) {
121
190
  return false;
122
191
  }
123
192
 
124
- async function attachHttpMocks(page, httpMocks) {
125
- if (!httpMocks || Object.keys(httpMocks).length === 0) return;
193
+ // The origin half of a URL, or the raw string when it will not parse. Grouping
194
+ // by origin is what turns twenty tile URLs into one readable line.
195
+ function originOf(url) {
196
+ try {
197
+ return new URL(url).origin;
198
+ } catch {
199
+ return url;
200
+ }
201
+ }
202
+
203
+ // Which declared mock keys actually served a response.
204
+ //
205
+ // This exists because a declared mock whose key never matches any request is
206
+ // silently inert: the capture still reports success, and the frame that lands
207
+ // is the healthy state under a name promising a degraded one. The declared keys
208
+ // were always known; nothing ever compared them against what fired.
209
+ function createMockUsageTally(httpMocks) {
210
+ const declared = Object.keys(httpMocks || {});
211
+ const usedKeys = new Set();
212
+ return {
213
+ recordFulfilled(key) {
214
+ usedKeys.add(key);
215
+ },
216
+ // Declared order is preserved in both lists so the reported set reads the
217
+ // same way the scenario's mock block does.
218
+ get used() {
219
+ return declared.filter((key) => usedKeys.has(key));
220
+ },
221
+ get unused() {
222
+ return declared.filter((key) => !usedKeys.has(key));
223
+ },
224
+ };
225
+ }
226
+
227
+ // Every request the page made, collapsed to one row per `(origin, mocked)` pair.
228
+ // A full URL list is noise (twenty tile URLs); origin + count + a representative
229
+ // URL is the line that ends a "why is this page blank" misdiagnosis.
230
+ function createRequestInventory(appOrigin) {
231
+ // Keyed by `origin|mocked` rather than origin alone: an origin serving both a
232
+ // mocked API call and unmocked sub-resources would otherwise collapse into one
233
+ // row whose `mocked` flag is a coin toss.
234
+ const groups = new Map();
235
+
236
+ function groupFor(url, mocked) {
237
+ const origin = originOf(url);
238
+ const groupKey = `${origin}|${mocked ? "mocked" : "unmocked"}`;
239
+ let row = groups.get(groupKey);
240
+ if (!row) {
241
+ row = {
242
+ origin,
243
+ count: 0,
244
+ sampleUrl: url,
245
+ mocked: !!mocked,
246
+ failed: false,
247
+ // `false` when the capture URL did not parse — the caller then has no
248
+ // origin to compare against, and treating everything as cross-origin
249
+ // over-reports rather than hiding a request.
250
+ sameOrigin: !!appOrigin && origin === appOrigin,
251
+ };
252
+ groups.set(groupKey, row);
253
+ }
254
+ return row;
255
+ }
256
+
257
+ return {
258
+ recordRequest(url, mocked) {
259
+ groupFor(url, mocked).count += 1;
260
+ },
261
+ recordFailure(url, mocked) {
262
+ groupFor(url, mocked).failed = true;
263
+ },
264
+ // Busiest origin first — the one worth explaining leads the report.
265
+ get groups() {
266
+ return [...groups.values()].sort(
267
+ (a, b) => b.count - a.count || a.origin.localeCompare(b.origin),
268
+ );
269
+ },
270
+ };
271
+ }
272
+
273
+ // Composes the two independent halves above into the single handle the capture
274
+ // harness threads through.
275
+ //
276
+ // Purely observational. Nothing here changes which requests are intercepted:
277
+ // `attachHttpMocks` scopes `page.route` to declared targets so unmocked requests
278
+ // load natively, and that scoping is load-bearing (a blanket catch-all route
279
+ // predicate breaks Vite ESM module loading and blanks the SPA).
280
+ function createMockObserver(httpMocks, appOrigin) {
281
+ const usage = createMockUsageTally(httpMocks);
282
+ const inventory = createRequestInventory(appOrigin);
283
+ return {
284
+ recordFulfilled: (key) => usage.recordFulfilled(key),
285
+ recordRequest: (url, mocked) => inventory.recordRequest(url, mocked),
286
+ recordFailure: (url, mocked) => inventory.recordFailure(url, mocked),
287
+ get used() {
288
+ return usage.used;
289
+ },
290
+ get unused() {
291
+ return usage.unused;
292
+ },
293
+ get externalRequests() {
294
+ return inventory.groups;
295
+ },
296
+ };
297
+ }
298
+
299
+ // Passive request inventory. `page.on` observes; it never routes, so attaching
300
+ // this cannot change what the page loads.
301
+ function attachRequestInventory(page, observer, httpMocks) {
302
+ const targets = mockedTargets(httpMocks || {});
303
+ page.on("request", (request) => {
304
+ const url = request.url();
305
+ observer.recordRequest(url, requestTargetsMock(targets, url));
306
+ });
307
+ page.on("requestfailed", (request) => {
308
+ const url = request.url();
309
+ observer.recordFailure(url, requestTargetsMock(targets, url));
310
+ });
311
+ }
312
+
313
+ // Returns the capture's mock observer (see `createMockObserver`). Always returns
314
+ // one, including for a scenario that declares no mocks at all — that is exactly
315
+ // the case where an unexplained third-party fetch is hardest to account for.
316
+ async function attachHttpMocks(page, httpMocks, options = {}) {
317
+ const observer = createMockObserver(httpMocks, options.appOrigin);
318
+ attachRequestInventory(page, observer, httpMocks);
319
+
320
+ if (!httpMocks || Object.keys(httpMocks).length === 0) return observer;
126
321
 
127
322
  // Intercept ONLY requests whose path matches a declared mock — not every
128
323
  // request. A blanket `page.route("**/*")` intercepts the dev server's ESM
@@ -134,11 +329,15 @@ async function attachHttpMocks(page, httpMocks) {
134
329
  await page.route(
135
330
  (url) => requestTargetsMock(targets, url),
136
331
  async (route) => {
137
- const mock = findHttpMock(httpMocks, route.request());
138
- if (!mock) {
332
+ const entry = findHttpMockEntry(httpMocks, route.request());
333
+ if (!entry) {
139
334
  await route.continue();
140
335
  return;
141
336
  }
337
+ const { key, mock } = entry;
338
+ // Counted at the fulfil branch, not at match time: a key is "used" only
339
+ // when it actually served a response.
340
+ observer.recordFulfilled(key);
142
341
 
143
342
  const headers = { ...(mock.headers || {}) };
144
343
  let body;
@@ -175,6 +374,8 @@ async function attachHttpMocks(page, httpMocks) {
175
374
  body: "[]",
176
375
  });
177
376
  });
377
+
378
+ return observer;
178
379
  }
179
380
 
180
381
 
@@ -222,8 +423,13 @@ module.exports = {
222
423
  matchPath,
223
424
  matchQuerySpec,
224
425
  findHttpMock,
426
+ findHttpMockEntry,
225
427
  mockedTargets,
226
428
  requestTargetsMock,
429
+ originOf,
430
+ createMockUsageTally,
431
+ createRequestInventory,
432
+ createMockObserver,
227
433
  attachHttpMocks,
228
434
  isDeclaredErrorMock,
229
435
  unmockedRouteFrom,
@@ -334,16 +334,36 @@ async function collectVisibleTextLength(target) {
334
334
  }
335
335
 
336
336
  // Inject a capture-only stylesheet that snaps entrance animations to their
337
- // FINAL frame: remove animation/transition timing and force the common
338
- // "hidden until revealed" symptoms (`opacity:0`, an entrance `transform`) back
339
- // to their resting values. This is the belt to `scrollThroughDocument`'s
340
- // suspenders — it covers pure-CSS keyframe entrances that are mid-flight or
341
- // stuck at an `opacity:0` start state even after the observers fired. It
342
- // targets the generic CSS symptom, not any framework's reveal class, so it
343
- // works for any stack. The caller gates this OFF when a scenario declares an
344
- // interactive state, so an intentionally animated/collapsed interactive frame
345
- // is never clobbered. Idempotent (a single injected style id) and best-effort.
346
- // Returns true when the style is present after the call.
337
+ // FINAL frame: remove animation/transition timing, then reveal the elements an
338
+ // entrance animation left INVISIBLE. This is the belt to
339
+ // `scrollThroughDocument`'s suspenders — it covers pure-CSS keyframe entrances
340
+ // that are mid-flight or stuck at an `opacity:0` start state even after the
341
+ // observers fired. It targets the generic CSS symptom, not any framework's
342
+ // reveal class, so it works for any stack. The caller gates this OFF when a
343
+ // scenario declares an interactive state, so an intentionally animated /
344
+ // collapsed interactive frame is never clobbered. Idempotent (a single injected
345
+ // style id) and best-effort. Returns true when the style is present after the
346
+ // call.
347
+ //
348
+ // The reveal is per-element and conditional ON THE ELEMENT BEING INVISIBLE,
349
+ // never a blanket `opacity: 1 !important` / `transform: none !important` over
350
+ // `*`. A blanket rule cannot tell an entrance animation's `opacity: 0` from
351
+ // DELIBERATE, resting state — a disabled control's dim, a muted row, a
352
+ // collapsed chevron's rotation — so it silently flattened every one of them out
353
+ // of every screenshot the capture pipeline produced. Two scenarios differing
354
+ // only in such a state then captured byte-identically and collided in the
355
+ // distinct-capture gate, and the collision was unfixable in the component: the
356
+ // state rendered correctly in a real browser (verified: computed opacity 0.4 vs
357
+ // 1) and was erased only at capture time. Anything already visible is now left
358
+ // exactly as the app rendered it.
359
+ //
360
+ // The reveal's TRANSFORM half additionally stops at the SVG boundary, because a
361
+ // CSS `transform` overrides the SVG `transform` presentation attribute: inside
362
+ // an `<svg>` it erases the static geometry that CONSTRUCTS the drawing instead
363
+ // of neutralizing an entrance animation, so a revealed SVG node lands on the
364
+ // origin. Invisible SVG nodes are therefore revealed in place — opacity forced,
365
+ // transform left alone. See the loop below for why the boundary is
366
+ // `ownerSVGElement` rather than `closest("svg")`.
347
367
  async function forceFinalVisualState(target) {
348
368
  return target.evaluate(() => {
349
369
  const STYLE_ID = "__codeyam_force_final_state";
@@ -356,16 +376,54 @@ async function forceFinalVisualState(target) {
356
376
  if (typeof document.createElement !== "function") return false;
357
377
  const style = document.createElement("style");
358
378
  style.id = STYLE_ID;
379
+ // Temporal properties only — these carry no resting state, so removing
380
+ // them cannot erase anything the app meant to show.
359
381
  style.textContent =
360
382
  "*, *::before, *::after {" +
361
383
  "animation: none !important;" +
362
384
  "transition: none !important;" +
363
- "opacity: 1 !important;" +
364
- "transform: none !important;" +
365
385
  "}";
366
386
  const head = document.head || document.documentElement;
367
387
  if (!head || typeof head.appendChild !== "function") return false;
368
388
  head.appendChild(style);
389
+
390
+ // With animations disabled above, an element held back by an entrance
391
+ // animation now computes to its pre-animation resting state — typically
392
+ // `opacity: 0`, often paired with a translate/scale that parks it offscreen.
393
+ // Reveal exactly those, and only those: a fully transparent element shows
394
+ // nothing either way, so forcing it can hide no real state, while an element
395
+ // at any visible opacity is left untouched.
396
+ //
397
+ // The transform half of the reveal STOPS at the SVG boundary. A CSS
398
+ // `transform` overrides the SVG `transform` presentation attribute, so
399
+ // inside an `<svg>` a forced `transform: none` does not neutralize an
400
+ // entrance animation — it erases the static rotate/translate/scale that
401
+ // CONSTRUCTS the drawing, revealing the node collapsed on the origin. The
402
+ // opacity half is still right there (a node at ~0 shows nothing either
403
+ // way), so an invisible SVG node is revealed IN PLACE. The boundary test is
404
+ // `ownerSVGElement` and NOT `closest("svg")`: HTML inside a
405
+ // `<foreignObject>` is a real HTMLElement with no `ownerSVGElement`, so it
406
+ // keeps the full reveal — `closest("svg")` would silently strand its
407
+ // genuine CSS entrance transform.
408
+ if (typeof document.querySelectorAll !== "function") return true;
409
+ const INVISIBLE_EPSILON = 0.01;
410
+ for (const el of document.querySelectorAll("*")) {
411
+ let computed = null;
412
+ try {
413
+ computed = getComputedStyle(el);
414
+ } catch (_) {
415
+ continue;
416
+ }
417
+ if (!computed) continue;
418
+ const opacity = parseFloat(computed.opacity);
419
+ if (!Number.isFinite(opacity) || opacity > INVISIBLE_EPSILON) continue;
420
+ if (!el.style || typeof el.style.setProperty !== "function") continue;
421
+ const inSvg =
422
+ el.ownerSVGElement != null ||
423
+ (typeof el.tagName === "string" && el.tagName.toLowerCase() === "svg");
424
+ el.style.setProperty("opacity", "1", "important");
425
+ if (!inSvg) el.style.setProperty("transform", "none", "important");
426
+ }
369
427
  return true;
370
428
  });
371
429
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeyam-editor/codeyam-editor",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
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.7",
13
- "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.7",
14
- "@codeyam-editor/codeyam-editor-linux-x64": "0.1.7",
15
- "@codeyam-editor/codeyam-editor-win32-x64": "0.1.7"
12
+ "@codeyam-editor/codeyam-editor-darwin-arm64": "0.1.8",
13
+ "@codeyam-editor/codeyam-editor-darwin-x64": "0.1.8",
14
+ "@codeyam-editor/codeyam-editor-linux-x64": "0.1.8",
15
+ "@codeyam-editor/codeyam-editor-win32-x64": "0.1.8"
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-S3Qk1Y_d.js";import{b as E}from"./index-VuyqBbGJ.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-S3Qk1Y_d.js";import{b as E}from"./index-BPLKgwon.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};