@testsmith/api-spector 0.4.6 → 0.4.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.
@@ -2515,11 +2515,11 @@ function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
2515
2515
  nativeEventTarget
2516
2516
  ), dispatchQueue.push({ event: nativeEvent, listeners: doc2 }), nativeEvent.target = activeElement)));
2517
2517
  }
2518
- function makePrefixMap(styleProp, eventName) {
2518
+ function makePrefixMap(styleProp, eventName2) {
2519
2519
  var prefixes = {};
2520
- prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
2521
- prefixes["Webkit" + styleProp] = "webkit" + eventName;
2522
- prefixes["Moz" + styleProp] = "moz" + eventName;
2520
+ prefixes[styleProp.toLowerCase()] = eventName2.toLowerCase();
2521
+ prefixes["Webkit" + styleProp] = "webkit" + eventName2;
2522
+ prefixes["Moz" + styleProp] = "moz" + eventName2;
2523
2523
  return prefixes;
2524
2524
  }
2525
2525
  var vendorPrefixes = {
@@ -2532,14 +2532,14 @@ var vendorPrefixes = {
2532
2532
  transitionend: makePrefixMap("Transition", "TransitionEnd")
2533
2533
  }, prefixedEventNames = {}, style = {};
2534
2534
  canUseDOM && (style = document.createElement("div").style, "AnimationEvent" in window || (delete vendorPrefixes.animationend.animation, delete vendorPrefixes.animationiteration.animation, delete vendorPrefixes.animationstart.animation), "TransitionEvent" in window || delete vendorPrefixes.transitionend.transition);
2535
- function getVendorPrefixedEventName(eventName) {
2536
- if (prefixedEventNames[eventName]) return prefixedEventNames[eventName];
2537
- if (!vendorPrefixes[eventName]) return eventName;
2538
- var prefixMap = vendorPrefixes[eventName], styleProp;
2535
+ function getVendorPrefixedEventName(eventName2) {
2536
+ if (prefixedEventNames[eventName2]) return prefixedEventNames[eventName2];
2537
+ if (!vendorPrefixes[eventName2]) return eventName2;
2538
+ var prefixMap = vendorPrefixes[eventName2], styleProp;
2539
2539
  for (styleProp in prefixMap)
2540
2540
  if (prefixMap.hasOwnProperty(styleProp) && styleProp in style)
2541
- return prefixedEventNames[eventName] = prefixMap[styleProp];
2542
- return eventName;
2541
+ return prefixedEventNames[eventName2] = prefixMap[styleProp];
2542
+ return eventName2;
2543
2543
  }
2544
2544
  var ANIMATION_END = getVendorPrefixedEventName("animationend"), ANIMATION_ITERATION = getVendorPrefixedEventName("animationiteration"), ANIMATION_START = getVendorPrefixedEventName("animationstart"), TRANSITION_RUN = getVendorPrefixedEventName("transitionrun"), TRANSITION_START = getVendorPrefixedEventName("transitionstart"), TRANSITION_CANCEL = getVendorPrefixedEventName("transitioncancel"), TRANSITION_END = getVendorPrefixedEventName("transitionend"), topLevelEventsToReactNames = /* @__PURE__ */ new Map(), simpleEventPluginEvents = "abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(
2545
2545
  " "
@@ -13403,6 +13403,25 @@ const createWsSlice = (set2) => ({
13403
13403
  if (s.wsConnections[requestId]) s.wsConnections[requestId].messages = [];
13404
13404
  })
13405
13405
  });
13406
+ const LIVE_EVENT_CAP = 5e3;
13407
+ const createStreamSlice = (set2) => ({
13408
+ liveStream: null,
13409
+ startLiveStream: (tabId, streamId) => set2((s) => {
13410
+ s.liveStream = { streamId, tabId, events: [], streaming: true };
13411
+ }),
13412
+ pushLiveStreamEvents: (streamId, events) => set2((s) => {
13413
+ if (!s.liveStream || s.liveStream.streamId !== streamId) return;
13414
+ s.liveStream.events.push(...events);
13415
+ const overflow = s.liveStream.events.length - LIVE_EVENT_CAP;
13416
+ if (overflow > 0) s.liveStream.events.splice(0, overflow);
13417
+ }),
13418
+ finishLiveStream: (streamId) => set2((s) => {
13419
+ if (s.liveStream?.streamId === streamId) s.liveStream.streaming = false;
13420
+ }),
13421
+ clearLiveStream: (tabId) => set2((s) => {
13422
+ if (s.liveStream?.tabId === tabId) s.liveStream = null;
13423
+ })
13424
+ });
13406
13425
  const HISTORY_CAP = 200;
13407
13426
  let saveTimer = null;
13408
13427
  function persistIfEnabled(get2) {
@@ -13508,6 +13527,7 @@ function makeTab(requestId, collectionId, opts = {}) {
13508
13527
  return {
13509
13528
  id: v4(),
13510
13529
  requestId,
13530
+ exampleId: opts.exampleId ?? null,
13511
13531
  collectionId,
13512
13532
  lastResponse: null,
13513
13533
  lastScriptResult: null,
@@ -13536,7 +13556,7 @@ const createTabsSlice = (set2, get2) => ({
13536
13556
  activeCollectionId: null,
13537
13557
  openInTab: (requestId, collectionId) => set2((s) => {
13538
13558
  s.collectionPanelOpen = false;
13539
- const existing = s.tabs.find((t2) => t2.requestId === requestId);
13559
+ const existing = s.tabs.find((t2) => t2.requestId === requestId && !t2.exampleId);
13540
13560
  if (existing) {
13541
13561
  s.activeTabId = existing.id;
13542
13562
  s.activeCollectionId = collectionId;
@@ -13853,6 +13873,117 @@ const createCollectionsSlice = (set2, get2) => ({
13853
13873
  Object.assign(entry.data.requests[id2], patch);
13854
13874
  entry.dirty = true;
13855
13875
  }),
13876
+ // ── Request examples ────────────────────────────────────────────────────────
13877
+ addExample: (requestId, snapshot) => {
13878
+ const id2 = v4();
13879
+ set2((s) => {
13880
+ const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13881
+ if (!entry) return;
13882
+ const req = entry.data.requests[requestId];
13883
+ const list2 = req.examples ?? (req.examples = []);
13884
+ const example = {
13885
+ id: id2,
13886
+ name: snapshot.name || uniqueName("Example", list2.map((e) => e.name)),
13887
+ request: snapshot.request,
13888
+ response: snapshot.response ?? null,
13889
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
13890
+ source: "saved"
13891
+ };
13892
+ list2.push(example);
13893
+ entry.dirty = true;
13894
+ });
13895
+ return id2;
13896
+ },
13897
+ addExampleFromRequest: (requestId) => {
13898
+ const entry = Object.values(get2().collections).find((c) => c.data.requests[requestId]);
13899
+ if (!entry) return null;
13900
+ const req = entry.data.requests[requestId];
13901
+ const id2 = v4();
13902
+ const example = {
13903
+ id: id2,
13904
+ name: uniqueName("Example", (req.examples ?? []).map((e) => e.name)),
13905
+ request: {
13906
+ method: req.method,
13907
+ url: req.url,
13908
+ headers: structuredClone(req.headers),
13909
+ params: structuredClone(req.params),
13910
+ auth: structuredClone(req.auth),
13911
+ body: structuredClone(req.body)
13912
+ },
13913
+ response: null,
13914
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
13915
+ source: "saved"
13916
+ };
13917
+ set2((s) => {
13918
+ const e = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13919
+ if (!e) return;
13920
+ const r = e.data.requests[requestId];
13921
+ (r.examples ?? (r.examples = [])).push(example);
13922
+ e.dirty = true;
13923
+ });
13924
+ return id2;
13925
+ },
13926
+ updateExampleRequest: (requestId, exampleId, patch) => set2((s) => {
13927
+ const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13928
+ const ex = entry?.data.requests[requestId].examples?.find((e) => e.id === exampleId);
13929
+ if (!entry || !ex) return;
13930
+ ex.request = { ...ex.request ?? {}, ...patch };
13931
+ entry.dirty = true;
13932
+ }),
13933
+ updateExampleResponse: (requestId, exampleId, response) => set2((s) => {
13934
+ const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13935
+ const ex = entry?.data.requests[requestId].examples?.find((e) => e.id === exampleId);
13936
+ if (!entry || !ex) return;
13937
+ ex.response = response;
13938
+ entry.dirty = true;
13939
+ }),
13940
+ renameExample: (requestId, exampleId, name2) => set2((s) => {
13941
+ const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13942
+ const ex = entry?.data.requests[requestId].examples?.find((e) => e.id === exampleId);
13943
+ if (!entry || !ex) return;
13944
+ ex.name = name2;
13945
+ entry.dirty = true;
13946
+ }),
13947
+ deleteExample: (requestId, exampleId) => set2((s) => {
13948
+ const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13949
+ const req = entry?.data.requests[requestId];
13950
+ if (!entry || !req?.examples) return;
13951
+ req.examples = req.examples.filter((e) => e.id !== exampleId);
13952
+ entry.dirty = true;
13953
+ const idx = s.tabs.findIndex((t2) => t2.requestId === requestId && t2.exampleId === exampleId);
13954
+ if (idx !== -1) {
13955
+ const wasActive = s.tabs[idx].id === s.activeTabId;
13956
+ s.tabs.splice(idx, 1);
13957
+ if (wasActive) s.activeTabId = (s.tabs[idx] ?? s.tabs[idx - 1] ?? null)?.id ?? null;
13958
+ }
13959
+ }),
13960
+ duplicateExample: (requestId, exampleId) => set2((s) => {
13961
+ const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13962
+ const list2 = entry?.data.requests[requestId].examples;
13963
+ const ex = list2?.find((e) => e.id === exampleId);
13964
+ if (!entry || !list2 || !ex) return;
13965
+ list2.push({
13966
+ ...structuredClone(ex),
13967
+ id: v4(),
13968
+ name: uniqueName(ex.name, list2.map((e) => e.name)),
13969
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
13970
+ });
13971
+ entry.dirty = true;
13972
+ }),
13973
+ openExample: (requestId, collectionId, exampleId) => set2((s) => {
13974
+ const existing = s.tabs.find((t2) => t2.requestId === requestId && t2.exampleId === exampleId);
13975
+ if (existing) {
13976
+ s.activeTabId = existing.id;
13977
+ s.activeCollectionId = collectionId;
13978
+ return;
13979
+ }
13980
+ const ex = Object.values(s.collections).find((c) => c.data.requests[requestId])?.data.requests[requestId].examples?.find((e) => e.id === exampleId);
13981
+ const tab = makeTab(requestId, collectionId, { protocol: protocolFor(s, requestId), exampleId });
13982
+ tab.lastResponse = ex?.response ?? null;
13983
+ s.tabs.push(tab);
13984
+ s.activeTabId = tab.id;
13985
+ s.activeCollectionId = collectionId;
13986
+ }),
13856
13987
  renameRequest: (id2, name2) => set2((s) => {
13857
13988
  const entry = Object.values(s.collections).find((c) => c.data.requests[id2]);
13858
13989
  if (!entry) return;
@@ -14206,6 +14337,7 @@ const useStore = create()(
14206
14337
  immer((set2, get2, api) => ({
14207
14338
  // ── Slice composition ─────────────────────────────────────────────────────
14208
14339
  ...createWsSlice(set2),
14340
+ ...createStreamSlice(set2),
14209
14341
  ...createHistorySlice(set2, get2),
14210
14342
  ...createRunnerSlice(set2),
14211
14343
  ...createRecorderSlice(set2),
@@ -14298,7 +14430,7 @@ const useStore = create()(
14298
14430
  })
14299
14431
  }))
14300
14432
  );
14301
- const { electron: electron$s } = window;
14433
+ const { electron: electron$v } = window;
14302
14434
  function useAutoSave() {
14303
14435
  const collections = useStore((s) => s.collections);
14304
14436
  useStore((s) => s.environments);
@@ -14314,7 +14446,7 @@ function useAutoSave() {
14314
14446
  for (const { relPath, data, dirty } of dirtyCollections) {
14315
14447
  if (!dirty) continue;
14316
14448
  try {
14317
- await electron$s.saveCollection(relPath, data);
14449
+ await electron$v.saveCollection(relPath, data);
14318
14450
  markCollectionClean(data.id);
14319
14451
  } catch (e) {
14320
14452
  console.error("Auto-save failed for", relPath, e);
@@ -14330,7 +14462,7 @@ function useAutoSave() {
14330
14462
  if (wsTimerRef.current) clearTimeout(wsTimerRef.current);
14331
14463
  wsTimerRef.current = setTimeout(async () => {
14332
14464
  try {
14333
- await electron$s.saveWorkspace(workspace);
14465
+ await electron$v.saveWorkspace(workspace);
14334
14466
  } catch {
14335
14467
  }
14336
14468
  }, 300);
@@ -14339,7 +14471,7 @@ function useAutoSave() {
14339
14471
  };
14340
14472
  }, [workspace]);
14341
14473
  }
14342
- const { electron: electron$r } = window;
14474
+ const { electron: electron$u } = window;
14343
14475
  function useWorkspaceLoader() {
14344
14476
  const loadCollection = useStore((s) => s.loadCollection);
14345
14477
  const loadEnvironment = useStore((s) => s.loadEnvironment);
@@ -14363,7 +14495,7 @@ function useWorkspaceLoader() {
14363
14495
  if (typeof ws2.settings?.zoom === "number") setZoom(ws2.settings.zoom);
14364
14496
  if (ws2.settings?.persistHistory) {
14365
14497
  try {
14366
- const entries = await electron$r.loadHistory();
14498
+ const entries = await electron$u.loadHistory();
14367
14499
  useStore.getState().setHistory(entries);
14368
14500
  } catch {
14369
14501
  }
@@ -14372,14 +14504,14 @@ function useWorkspaceLoader() {
14372
14504
  }
14373
14505
  for (const colPath of ws2.collections ?? []) {
14374
14506
  try {
14375
- const col = await electron$r.loadCollection(colPath);
14507
+ const col = await electron$u.loadCollection(colPath);
14376
14508
  loadCollection(colPath, col);
14377
14509
  } catch {
14378
14510
  }
14379
14511
  }
14380
14512
  for (const envPath of ws2.environments ?? []) {
14381
14513
  try {
14382
- const env = await electron$r.loadEnvironment(envPath);
14514
+ const env = await electron$u.loadEnvironment(envPath);
14383
14515
  loadEnvironment(envPath, env);
14384
14516
  } catch {
14385
14517
  }
@@ -14398,19 +14530,19 @@ function useWorkspaceLoader() {
14398
14530
  }
14399
14531
  for (const relPath of ws2.mocks ?? []) {
14400
14532
  try {
14401
- const mockData = await electron$r.loadMock(relPath);
14533
+ const mockData = await electron$u.loadMock(relPath);
14402
14534
  loadMock(relPath, mockData);
14403
14535
  } catch {
14404
14536
  }
14405
14537
  }
14406
14538
  try {
14407
- const snapshots = await electron$r.listContractSnapshots(ws2.contracts ?? []);
14539
+ const snapshots = await electron$u.listContractSnapshots(ws2.contracts ?? []);
14408
14540
  for (const { relPath, snapshot } of snapshots) loadContractSnapshot(relPath, snapshot);
14409
14541
  } catch {
14410
14542
  }
14411
14543
  if ((ws2.collections ?? []).length > 0) {
14412
14544
  try {
14413
- const firstCol = await electron$r.loadCollection(ws2.collections[0]);
14545
+ const firstCol = await electron$u.loadCollection(ws2.collections[0]);
14414
14546
  setActiveCollection(firstCol.id);
14415
14547
  } catch {
14416
14548
  }
@@ -34460,6 +34592,81 @@ const HEADERS_METHODS = [
34460
34592
  { label: "get", type: "function", detail: "(name)", info: "Get a header value by name (case-insensitive)" },
34461
34593
  { label: "toObject", type: "function", detail: "()", info: "Return all headers as a plain object" }
34462
34594
  ];
34595
+ const CHAI_METHODS = [
34596
+ { label: "equal", type: "function", detail: "(value)", info: "Strict (===) equality" },
34597
+ { label: "eql", type: "function", detail: "(value)", info: "Deep equality" },
34598
+ { label: "include", type: "function", detail: "(value)", info: "Target includes value (string / array / object)" },
34599
+ { label: "contain", type: "function", detail: "(value)", info: "Alias of include" },
34600
+ { label: "match", type: "function", detail: "(regexp)", info: "String matches a regular expression" },
34601
+ { label: "above", type: "function", detail: "(n)", info: "Greater than n" },
34602
+ { label: "below", type: "function", detail: "(n)", info: "Less than n" },
34603
+ { label: "least", type: "function", detail: "(n)", info: "Greater than or equal to n" },
34604
+ { label: "most", type: "function", detail: "(n)", info: "Less than or equal to n" },
34605
+ { label: "within", type: "function", detail: "(min, max)", info: "Number within an inclusive range" },
34606
+ { label: "closeTo", type: "function", detail: "(expected, delta)", info: "Number close to expected within delta" },
34607
+ { label: "a", type: "function", detail: "(type)", info: "Type check, e.g. .to.be.a('string')" },
34608
+ { label: "an", type: "function", detail: "(type)", info: "Type check, e.g. .to.be.an('array')" },
34609
+ { label: "property", type: "function", detail: "(name, [value])", info: "Object has a property (optionally equal to value)" },
34610
+ { label: "lengthOf", type: "function", detail: "(n)", info: "Array / string length equals n" },
34611
+ { label: "keys", type: "function", detail: "(...keys)", info: "Object has the given keys" },
34612
+ { label: "members", type: "function", detail: "(array)", info: "Array has the given members" },
34613
+ { label: "oneOf", type: "function", detail: "(array)", info: "Value is one of the array" },
34614
+ { label: "throw", type: "function", detail: "([error])", info: "Function throws" },
34615
+ { label: "satisfy", type: "function", detail: "(fn)", info: "Value satisfies a predicate" }
34616
+ ];
34617
+ const CHAI_GETTERS = [
34618
+ { label: "true", type: "property", info: "Value is true" },
34619
+ { label: "false", type: "property", info: "Value is false" },
34620
+ { label: "null", type: "property", info: "Value is null" },
34621
+ { label: "undefined", type: "property", info: "Value is undefined" },
34622
+ { label: "NaN", type: "property", info: "Value is NaN" },
34623
+ { label: "ok", type: "property", info: "Value is truthy" },
34624
+ { label: "empty", type: "property", info: "Array / string / object is empty" },
34625
+ { label: "exist", type: "property", info: "Value is neither null nor undefined" }
34626
+ ];
34627
+ const chaiWords = (words) => words.map((w) => ({ label: w, type: "property", info: "Chai chain" }));
34628
+ const chaiPick = (labels) => CHAI_METHODS.filter((c) => labels.includes(c.label));
34629
+ const CHAI_AFTER = {
34630
+ "": [...chaiWords(["to", "not"]), ...chaiPick(["equal"])],
34631
+ // right after expect(...)
34632
+ to: [...chaiWords(["be", "have", "include", "deep", "not", "a", "an"]), ...CHAI_METHODS],
34633
+ not: [...chaiWords(["be", "have", "include", "deep", "a", "an"]), ...CHAI_METHODS],
34634
+ be: [...CHAI_GETTERS, ...chaiPick(["above", "below", "within", "a", "an", "oneOf", "closeTo", "empty"])],
34635
+ been: [...CHAI_GETTERS, ...chaiPick(["above", "below", "within"])],
34636
+ have: chaiPick(["property", "lengthOf", "keys", "members"]),
34637
+ has: chaiPick(["property", "lengthOf", "keys", "members"]),
34638
+ deep: chaiPick(["equal", "include", "members", "property"])
34639
+ };
34640
+ const CHAI_DEFAULT = [
34641
+ ...chaiWords(["to", "be", "have", "include", "not", "deep"]),
34642
+ ...CHAI_METHODS,
34643
+ ...CHAI_GETTERS
34644
+ ];
34645
+ function chaiChainCompletion(textBefore, pos) {
34646
+ const start = textBefore.lastIndexOf("sp.expect(");
34647
+ if (start === -1) return null;
34648
+ let depth = 0, close = -1;
34649
+ for (let i = start + "sp.expect".length; i < textBefore.length; i++) {
34650
+ const ch = textBefore[i];
34651
+ if (ch === "(") depth++;
34652
+ else if (ch === ")") {
34653
+ depth--;
34654
+ if (depth === 0) {
34655
+ close = i;
34656
+ break;
34657
+ }
34658
+ }
34659
+ }
34660
+ if (close === -1) return null;
34661
+ const chain = textBefore.slice(close + 1);
34662
+ const m = /^((?:\.\w+)*)\.(\w*)$/.exec(chain);
34663
+ if (!m) return null;
34664
+ const priorWords = m[1] ? m[1].split(".").filter(Boolean) : [];
34665
+ const lastWord = priorWords.length ? priorWords[priorWords.length - 1] : "";
34666
+ const partial = m[2].toLowerCase();
34667
+ const options = (CHAI_AFTER[lastWord] ?? CHAI_DEFAULT).filter((o) => o.label.toLowerCase().includes(partial));
34668
+ return { from: pos - m[2].length, options, validFor: /^\w*$/ };
34669
+ }
34463
34670
  const FAKER_NAMESPACES = [
34464
34671
  { label: "string", type: "property", info: "String generators" },
34465
34672
  { label: "number", type: "property", info: "Number generators" },
@@ -34597,6 +34804,8 @@ function makeAtCompletionSource(varNames) {
34597
34804
  validFor: /^\w*$/
34598
34805
  };
34599
34806
  }
34807
+ const chai = chaiChainCompletion(textBefore, context.pos);
34808
+ if (chai) return chai;
34600
34809
  const headersMatch = /\bsp\.response\.headers\.(\w*)$/.exec(textBefore);
34601
34810
  if (headersMatch) {
34602
34811
  return { from: context.pos - headersMatch[1].length, options: HEADERS_METHODS, validFor: /^\w*$/ };
@@ -36219,7 +36428,7 @@ function CollectionSettingsModal({ collection, onClose }) {
36219
36428
  }
36220
36429
  );
36221
36430
  }
36222
- const { electron: electron$q } = window;
36431
+ const { electron: electron$t } = window;
36223
36432
  function normalisePath(url) {
36224
36433
  let path = url.replace(/^\{\{[^}]+\}\}/, "").replace(/^https?:\/\/[^/]+/, "");
36225
36434
  if (!path.startsWith("/")) path = "/" + path;
@@ -36306,7 +36515,7 @@ function SchemaSyncModal({
36306
36515
  setLoading(true);
36307
36516
  setError(null);
36308
36517
  try {
36309
- const entries = await electron$q.extractOpenApiSchemas();
36518
+ const entries = await electron$t.extractOpenApiSchemas();
36310
36519
  if (!entries) {
36311
36520
  setLoading(false);
36312
36521
  return;
@@ -36325,7 +36534,7 @@ function SchemaSyncModal({
36325
36534
  setLoading(true);
36326
36535
  setError(null);
36327
36536
  try {
36328
- const entries = await electron$q.extractOpenApiSchemasFromUrl(trimmed);
36537
+ const entries = await electron$t.extractOpenApiSchemasFromUrl(trimmed);
36329
36538
  setSpecEntries(entries);
36330
36539
  autoSelectChanged(entries);
36331
36540
  } catch (err) {
@@ -36358,7 +36567,7 @@ function SchemaSyncModal({
36358
36567
  }
36359
36568
  const entry = useStore.getState().collections[collectionId];
36360
36569
  if (entry) {
36361
- await electron$q.saveCollection(entry.relPath, entry.data);
36570
+ await electron$t.saveCollection(entry.relPath, entry.data);
36362
36571
  markCollectionClean(collectionId);
36363
36572
  }
36364
36573
  onClose();
@@ -36435,7 +36644,7 @@ function SchemaSyncModal({
36435
36644
  className: "accent-blue-500"
36436
36645
  }
36437
36646
  ),
36438
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-mono font-bold w-14 shrink-0 ${methodColor$2(m.specEntry.method)}`, children: m.specEntry.method }),
36647
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-mono font-bold w-14 shrink-0 ${methodColor$3(m.specEntry.method)}`, children: m.specEntry.method }),
36439
36648
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
36440
36649
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-surface-200 truncate", children: m.request.name }),
36441
36650
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-surface-500 font-mono truncate", children: m.specEntry.pathTemplate })
@@ -36548,7 +36757,7 @@ function SchemaSyncModal({
36548
36757
  }
36549
36758
  );
36550
36759
  }
36551
- function methodColor$2(method) {
36760
+ function methodColor$3(method) {
36552
36761
  switch (method) {
36553
36762
  case "GET":
36554
36763
  return "text-emerald-400";
@@ -36564,6 +36773,284 @@ function methodColor$2(method) {
36564
36773
  return "text-surface-300";
36565
36774
  }
36566
36775
  }
36776
+ function folderChainTo(root2, targetId) {
36777
+ if (root2.id === targetId) return [root2];
36778
+ for (const sub of root2.folders) {
36779
+ const chain = folderChainTo(sub, targetId);
36780
+ if (chain.length > 0) return [root2, ...chain];
36781
+ }
36782
+ return [];
36783
+ }
36784
+ function makeHook(req, collectionVars, hookType, scopeId, scopeAncestors, scopePath, mainRequestId) {
36785
+ return { request: req, collectionVars, isHook: true, hookType, scopeId, scopeAncestors, scopePath, mainRequestId };
36786
+ }
36787
+ function buildFolderPlan(folder, requests, collectionVars, filterTags, scopeId, ancestorIds, parentPath, wrappers, isRoot) {
36788
+ const result = [];
36789
+ const scopePath = isRoot ? [] : [...parentPath, folder.name];
36790
+ const folderReqs = folder.requestIds.map((id2) => requests[id2]).filter((r) => r && !r.disabled);
36791
+ const beforeAllHooks = folderReqs.filter((r) => r.hookType === "beforeAll");
36792
+ const beforeHooks = folderReqs.filter((r) => r.hookType === "before");
36793
+ const afterHooks = folderReqs.filter((r) => r.hookType === "after");
36794
+ const afterAllHooks = folderReqs.filter((r) => r.hookType === "afterAll");
36795
+ const regularReqs = folderReqs.filter((r) => !r.hookType);
36796
+ const myWrapper = { scopeId, ancestors: ancestorIds, scopePath, before: beforeHooks, after: afterHooks };
36797
+ const allWrappers = [...wrappers, myWrapper];
36798
+ for (const req of beforeAllHooks) {
36799
+ result.push(makeHook(req, collectionVars, "beforeAll", scopeId, ancestorIds, scopePath));
36800
+ }
36801
+ for (const req of regularReqs) {
36802
+ const tags2 = req.meta?.tags ?? [];
36803
+ if (filterTags.length > 0 && !filterTags.some((t2) => tags2.includes(t2))) continue;
36804
+ for (const w of allWrappers) {
36805
+ for (const hookReq of w.before) {
36806
+ result.push(makeHook(hookReq, collectionVars, "before", w.scopeId, w.ancestors, w.scopePath, req.id));
36807
+ }
36808
+ }
36809
+ result.push({ request: req, collectionVars, scopeId, scopeAncestors: ancestorIds, scopePath });
36810
+ for (const w of [...allWrappers].reverse()) {
36811
+ for (const hookReq of w.after) {
36812
+ result.push(makeHook(hookReq, collectionVars, "after", w.scopeId, w.ancestors, w.scopePath, req.id));
36813
+ }
36814
+ }
36815
+ }
36816
+ for (const sub of folder.folders) {
36817
+ const folderTags = sub.tags ?? [];
36818
+ const effectiveFilter = filterTags.length === 0 ? filterTags : folderTags.some((t2) => filterTags.includes(t2)) ? [] : filterTags;
36819
+ result.push(...buildFolderPlan(
36820
+ sub,
36821
+ requests,
36822
+ collectionVars,
36823
+ effectiveFilter,
36824
+ sub.id,
36825
+ [...ancestorIds, scopeId],
36826
+ scopePath,
36827
+ allWrappers,
36828
+ false
36829
+ ));
36830
+ }
36831
+ for (const req of afterAllHooks) {
36832
+ result.push(makeHook(req, collectionVars, "afterAll", scopeId, ancestorIds, scopePath));
36833
+ }
36834
+ return result;
36835
+ }
36836
+ function collectTagged(folder, requests, collectionVars, filterTags, parentPath = [], isRoot = true) {
36837
+ const results = [];
36838
+ const scopePath = isRoot ? parentPath : [...parentPath, folder.name];
36839
+ for (const reqId of folder.requestIds) {
36840
+ const req = requests[reqId];
36841
+ if (!req || req.hookType || req.disabled) continue;
36842
+ const tags2 = req.meta?.tags ?? [];
36843
+ if (filterTags.length > 0 && !filterTags.some((t2) => tags2.includes(t2))) continue;
36844
+ results.push({ request: req, collectionVars, scopePath });
36845
+ }
36846
+ for (const sub of folder.folders) {
36847
+ const folderTags = sub.tags ?? [];
36848
+ const effectiveTags = filterTags.length === 0 ? filterTags : folderTags.some((t2) => filterTags.includes(t2)) ? [] : filterTags;
36849
+ results.push(...collectTagged(sub, requests, collectionVars, effectiveTags, scopePath, false));
36850
+ }
36851
+ return results;
36852
+ }
36853
+ function collectAllTags(folder, requests) {
36854
+ const tags2 = /* @__PURE__ */ new Set();
36855
+ function walk(f) {
36856
+ (f.tags ?? []).forEach((t2) => tags2.add(t2));
36857
+ for (const reqId of f.requestIds) {
36858
+ (requests[reqId]?.meta?.tags ?? []).forEach((t2) => tags2.add(t2));
36859
+ }
36860
+ f.folders.forEach(walk);
36861
+ }
36862
+ walk(folder);
36863
+ return Array.from(tags2).sort();
36864
+ }
36865
+ function folderPathTo(root2, requestId) {
36866
+ if (root2.requestIds.includes(requestId)) return [root2];
36867
+ for (const sub of root2.folders) {
36868
+ const path = folderPathTo(sub, requestId);
36869
+ if (path.length > 0) return [root2, ...path];
36870
+ }
36871
+ return [];
36872
+ }
36873
+ function getHooksForRequest(requestId, collection) {
36874
+ const path = folderPathTo(collection.rootFolder, requestId);
36875
+ const before = [];
36876
+ const afterReversed = [];
36877
+ for (const folder of path) {
36878
+ const reqs = folder.requestIds.map((id2) => collection.requests[id2]).filter((r) => r && !r.disabled);
36879
+ const bAll = reqs.filter((r) => r.hookType === "beforeAll");
36880
+ const bEach = reqs.filter((r) => r.hookType === "before");
36881
+ const aEach = reqs.filter((r) => r.hookType === "after");
36882
+ const aAll = reqs.filter((r) => r.hookType === "afterAll");
36883
+ before.push(...bAll, ...bEach);
36884
+ afterReversed.push(...aEach, ...aAll);
36885
+ }
36886
+ return { before, after: afterReversed.reverse() };
36887
+ }
36888
+ function authIsConfigured(auth) {
36889
+ if (!auth || auth.type === "none") return false;
36890
+ const set2 = (s) => !!(s && s.trim());
36891
+ switch (auth.type) {
36892
+ case "bearer":
36893
+ return set2(auth.token) || set2(auth.tokenSecretRef);
36894
+ case "basic":
36895
+ case "digest":
36896
+ return set2(auth.username) || set2(auth.password) || set2(auth.passwordSecretRef);
36897
+ case "ntlm":
36898
+ return set2(auth.username) || set2(auth.password) || set2(auth.passwordSecretRef);
36899
+ case "apikey":
36900
+ return set2(auth.apiKeyValue) || set2(auth.apiKeySecretRef);
36901
+ case "oauth2":
36902
+ return set2(auth.oauth2TokenUrl) || set2(auth.oauth2ClientId) || set2(auth.oauth2CachedToken);
36903
+ default:
36904
+ return true;
36905
+ }
36906
+ }
36907
+ function resolveInheritedAuthAndHeaders(requestId, collection) {
36908
+ let inheritedAuth = collection.auth && collection.auth.type !== "none" ? collection.auth : null;
36909
+ let inheritedHeaders = collection.headers?.filter((h) => h.enabled && h.key) ?? [];
36910
+ const path = folderPathTo(collection.rootFolder, requestId);
36911
+ for (const folder of path) {
36912
+ if (folder.auth && folder.auth.type !== "none") inheritedAuth = folder.auth;
36913
+ if (folder.headers?.length) {
36914
+ inheritedHeaders = [...inheritedHeaders, ...folder.headers.filter((h) => h.enabled && h.key)];
36915
+ }
36916
+ }
36917
+ return { auth: inheritedAuth, headers: inheritedHeaders };
36918
+ }
36919
+ function buildRunPlan(collection, folderId, filterTags) {
36920
+ const collectionVars = collection.collectionVariables ?? {};
36921
+ if (!folderId) {
36922
+ return buildFolderPlan(
36923
+ collection.rootFolder,
36924
+ collection.requests,
36925
+ collectionVars,
36926
+ filterTags,
36927
+ collection.rootFolder.id,
36928
+ [],
36929
+ [],
36930
+ [],
36931
+ true
36932
+ );
36933
+ }
36934
+ const chain = folderChainTo(collection.rootFolder, folderId);
36935
+ if (chain.length === 0) return [];
36936
+ const targetFolder = chain[chain.length - 1];
36937
+ const ancestors = chain.slice(0, -1);
36938
+ const result = [];
36939
+ const ancestorWrappers = [];
36940
+ const ancestorIds = [];
36941
+ for (const f of ancestors) {
36942
+ const reqs = f.requestIds.map((id2) => collection.requests[id2]).filter((r) => r && !r.disabled);
36943
+ const beforeAllH = reqs.filter((r) => r.hookType === "beforeAll");
36944
+ const beforeH = reqs.filter((r) => r.hookType === "before");
36945
+ const afterH = reqs.filter((r) => r.hookType === "after");
36946
+ for (const req of beforeAllH) {
36947
+ result.push(makeHook(req, collectionVars, "beforeAll", f.id, [...ancestorIds], []));
36948
+ }
36949
+ ancestorWrappers.push({
36950
+ scopeId: f.id,
36951
+ ancestors: [...ancestorIds],
36952
+ scopePath: [],
36953
+ // ancestor hooks render with no folder heading
36954
+ before: beforeH,
36955
+ after: afterH
36956
+ });
36957
+ ancestorIds.push(f.id);
36958
+ }
36959
+ result.push(...buildFolderPlan(
36960
+ targetFolder,
36961
+ collection.requests,
36962
+ collectionVars,
36963
+ filterTags,
36964
+ targetFolder.id,
36965
+ ancestorIds,
36966
+ [],
36967
+ ancestorWrappers,
36968
+ true
36969
+ ));
36970
+ for (let i = ancestors.length - 1; i >= 0; i--) {
36971
+ const f = ancestors[i];
36972
+ const reqs = f.requestIds.map((id2) => collection.requests[id2]).filter((r) => r && !r.disabled);
36973
+ const afterAllH = reqs.filter((r) => r.hookType === "afterAll");
36974
+ const myAncestors = ancestorIds.slice(0, i);
36975
+ for (const req of afterAllH) {
36976
+ result.push(makeHook(req, collectionVars, "afterAll", f.id, myAncestors, []));
36977
+ }
36978
+ }
36979
+ return result;
36980
+ }
36981
+ function hasContract(r) {
36982
+ const c = r.contract;
36983
+ return !!c && (c.statusCode != null || !!c.bodySchema || !!c.bodyMatcher || (c.headers?.length ?? 0) > 0 || (c.providerStates?.length ?? 0) > 0);
36984
+ }
36985
+ const { electron: electron$s } = window;
36986
+ function cloudEnabled() {
36987
+ return Boolean(useStore.getState().workspace?.settings?.cloud?.enabled);
36988
+ }
36989
+ function assertEnabled() {
36990
+ if (!cloudEnabled()) throw new Error("Cloud is off. Enable it in Settings → Cloud.");
36991
+ }
36992
+ async function pushContractToCloud(requests, opts) {
36993
+ assertEnabled();
36994
+ return electron$s.cloudPushPact({
36995
+ consumer: opts.consumer,
36996
+ provider: opts.provider,
36997
+ consumerVersion: opts.version,
36998
+ requests
36999
+ });
37000
+ }
37001
+ async function pushProviderSpecToCloud(opts) {
37002
+ assertEnabled();
37003
+ return electron$s.cloudPushSpec(opts);
37004
+ }
37005
+ function openCloudMatrix() {
37006
+ void electron$s.cloudOpenMatrix();
37007
+ }
37008
+ async function getCloudMockRoutes(name2) {
37009
+ if (!cloudEnabled()) return null;
37010
+ try {
37011
+ const res = await electron$s.cloudGetMock(name2);
37012
+ return res.exists ? res.routes ?? [] : null;
37013
+ } catch {
37014
+ return null;
37015
+ }
37016
+ }
37017
+ async function pushMockToCloud(server, routeIds) {
37018
+ assertEnabled();
37019
+ const routes = routeIds ? (server.routes ?? []).filter((r) => routeIds.includes(r.id)) : server.routes ?? [];
37020
+ return electron$s.cloudPushMock({ ...server, routes });
37021
+ }
37022
+ async function pushRequestAsMonitor(request, opts) {
37023
+ assertEnabled();
37024
+ const s = useStore.getState();
37025
+ const colEntry = Object.values(s.collections).find((c) => c.data.requests[request.id]);
37026
+ const collectionVars = {
37027
+ ...colEntry?.data.collectionVariables ?? {},
37028
+ ...s.getInheritedVariables(request.id),
37029
+ ...s.sessionVars
37030
+ };
37031
+ const setup = colEntry ? getHooksForRequest(request.id, colEntry.data).before : [];
37032
+ const environment = resolveEnvironmentById(s.environments, s.activeEnvironmentId);
37033
+ const globals = { ...s.globals };
37034
+ const inherited = s.getInheritedAuthAndHeaders(request.id);
37035
+ const mergedAuth = authIsConfigured(request.auth) ? request.auth : inherited.auth ?? request.auth;
37036
+ const mergedHeaders = [
37037
+ ...inherited.headers.filter((h) => h.enabled),
37038
+ ...request.headers
37039
+ ];
37040
+ const mergedRequest = { ...request, auth: mergedAuth, headers: mergedHeaders };
37041
+ return electron$s.cloudPushMonitor(
37042
+ {
37043
+ request: mergedRequest,
37044
+ setup,
37045
+ environment,
37046
+ collectionVars,
37047
+ globals,
37048
+ name: request.name,
37049
+ intervalSeconds: opts?.intervalSeconds,
37050
+ expectedStatus: opts?.expectedStatus
37051
+ }
37052
+ );
37053
+ }
36567
37054
  const METHOD_COLORS$1 = {
36568
37055
  GET: "text-emerald-400",
36569
37056
  POST: "text-blue-400",
@@ -36587,6 +37074,143 @@ const STATUS_COLORS = {
36587
37074
  function getStatusColor(status) {
36588
37075
  return STATUS_COLORS[String(status)[0]] ?? "text-gray-400";
36589
37076
  }
37077
+ function PushContractModal({ requests, defaultConsumer, onClose }) {
37078
+ const snapshots = useStore((s) => s.contractSnapshots);
37079
+ const snapshotEntries = Object.entries(snapshots);
37080
+ const withContract = requests.filter(hasContract);
37081
+ const [mode, setMode] = reactExports.useState("consumer");
37082
+ const [consumer, setConsumer] = reactExports.useState(defaultConsumer);
37083
+ const [provider, setProvider] = reactExports.useState("");
37084
+ const [version, setVersion] = reactExports.useState("");
37085
+ const [selected, setSelected] = reactExports.useState(new Set(withContract.map((r) => r.id)));
37086
+ const [pacticipant, setPacticipant] = reactExports.useState("");
37087
+ const [snapshotPath, setSnapshotPath] = reactExports.useState(snapshotEntries[0]?.[0] ?? "");
37088
+ const [status, setStatus] = reactExports.useState({ state: "idle" });
37089
+ function toggle(id2) {
37090
+ setSelected((prev) => {
37091
+ const n = new Set(prev);
37092
+ if (n.has(id2)) n.delete(id2);
37093
+ else n.add(id2);
37094
+ return n;
37095
+ });
37096
+ }
37097
+ const canPush = mode === "consumer" ? Boolean(consumer.trim() && provider.trim() && version.trim() && selected.size) : Boolean(pacticipant.trim() && version.trim() && snapshotPath);
37098
+ async function push2() {
37099
+ setStatus({ state: "pushing" });
37100
+ try {
37101
+ if (mode === "consumer") {
37102
+ const chosen = withContract.filter((r2) => selected.has(r2.id));
37103
+ const r = await pushContractToCloud(chosen, { consumer: consumer.trim(), provider: provider.trim(), version: version.trim() });
37104
+ const published = `Published ${consumer} → ${provider} (${chosen.length} interactions).`;
37105
+ if (!r.verification) {
37106
+ setStatus({ state: "warn", msg: `${published} Not verified yet: ${provider} has not published its OpenAPI spec.` });
37107
+ } else if (r.verification.success) {
37108
+ setStatus({ state: "ok", msg: `${published} Compatible with ${provider}. ✓` });
37109
+ } else {
37110
+ const failing = r.verification.checks.filter((c) => !c.passed);
37111
+ setStatus({ state: "err", msg: `${published} NOT compatible with ${provider} (${failing.length} failing).`, detail: failing.map((c) => `${c.interaction}: ${c.error}`) });
37112
+ }
37113
+ } else {
37114
+ const spec = snapshots[snapshotPath]?.spec ?? "";
37115
+ const r = await pushProviderSpecToCloud({ pacticipant: pacticipant.trim(), version: version.trim(), spec });
37116
+ const failing = r.results.filter((x) => !x.success);
37117
+ if (r.results.length === 0) {
37118
+ setStatus({ state: "warn", msg: `Published ${pacticipant} spec. No consumer contracts to verify yet.` });
37119
+ } else if (failing.length === 0) {
37120
+ setStatus({ state: "ok", msg: `Published ${pacticipant} spec. All ${r.results.length} consumer contract(s) compatible. ✓` });
37121
+ } else {
37122
+ setStatus({ state: "err", msg: `Published ${pacticipant} spec. ${failing.length} of ${r.results.length} consumer(s) now incompatible.`, detail: failing.map((x) => `${x.consumer} ${x.version}`) });
37123
+ }
37124
+ }
37125
+ } catch (e) {
37126
+ setStatus({ state: "err", msg: e.message });
37127
+ }
37128
+ }
37129
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
37130
+ Modal,
37131
+ {
37132
+ onClose,
37133
+ overlayClassName: "bg-black/50 z-50 flex items-start justify-center pt-24",
37134
+ panelClassName: "bg-surface-900 border border-surface-800 rounded-lg shadow-2xl w-[520px] flex flex-col max-h-[75vh]",
37135
+ title: "Push contract to cloud",
37136
+ children: [
37137
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1 px-4 pt-3 flex-shrink-0", children: ["consumer", "provider"].map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(
37138
+ "button",
37139
+ {
37140
+ onClick: () => setMode(m),
37141
+ disabled: m === "provider" && snapshotEntries.length === 0,
37142
+ className: `px-3 py-1.5 text-xs rounded transition-colors disabled:opacity-30 ${mode === m ? "bg-blue-600 text-white" : "bg-surface-800 hover:bg-surface-700 text-surface-300"}`,
37143
+ children: m === "consumer" ? "Consumer pact" : "Provider spec (OpenAPI)"
37144
+ },
37145
+ m
37146
+ )) }),
37147
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 flex flex-col gap-3 text-xs", children: mode === "consumer" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
37148
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
37149
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
37150
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "Consumer" }),
37151
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { value: consumer, onChange: (e) => setConsumer(e.target.value), className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500" })
37152
+ ] }),
37153
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
37154
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "Provider" }),
37155
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { value: provider, onChange: (e) => setProvider(e.target.value), placeholder: "orders-api", className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600" })
37156
+ ] })
37157
+ ] }),
37158
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
37159
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-500", children: [
37160
+ "Version ",
37161
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600", children: "(git sha / build)" })
37162
+ ] }),
37163
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { value: version, onChange: (e) => setVersion(e.target.value), placeholder: "1.4.0", className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600" })
37164
+ ] }),
37165
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
37166
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-surface-500 mb-1.5", children: [
37167
+ "Interactions ",
37168
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600", children: "(requests with a contract)" })
37169
+ ] }),
37170
+ withContract.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500 px-1 py-3", children: "No requests here have a contract yet. Add expected status/schema on a request's Contract tab first." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-lg border border-surface-800 max-h-56 overflow-y-auto", children: withContract.map((r) => /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 px-2 py-1.5 hover:bg-surface-800/50 cursor-pointer", children: [
37171
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "checkbox", checked: selected.has(r.id), onChange: () => toggle(r.id), className: "accent-blue-500" }),
37172
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold w-12 flex-shrink-0", style: { color: getMethodColor(r.method) }, children: r.method }),
37173
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300 truncate", children: r.name }),
37174
+ r.contract?.statusCode && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-auto text-surface-500 flex-shrink-0", children: r.contract.statusCode })
37175
+ ] }, r.id)) })
37176
+ ] })
37177
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
37178
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500", children: "Publishes a pinned OpenAPI spec as the provider contract. Consumer pacts are then verified against it without running the provider." }),
37179
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
37180
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
37181
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "Provider (pacticipant)" }),
37182
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { value: pacticipant, onChange: (e) => setPacticipant(e.target.value), placeholder: "orders-api", className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600" })
37183
+ ] }),
37184
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
37185
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "Version" }),
37186
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { value: version, onChange: (e) => setVersion(e.target.value), placeholder: "2.1.0", className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600" })
37187
+ ] })
37188
+ ] }),
37189
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
37190
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "Spec snapshot" }),
37191
+ /* @__PURE__ */ jsxRuntimeExports.jsx("select", { value: snapshotPath, onChange: (e) => setSnapshotPath(e.target.value), className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500", children: snapshotEntries.map(([path, snap]) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: path, children: snap.name ?? path }, path)) })
37192
+ ] })
37193
+ ] }) }),
37194
+ (status.state === "ok" || status.state === "warn" || status.state === "err") && status.detail?.length ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-4 mb-2 px-3 py-2 rounded bg-surface-800/60 border border-surface-700 text-[11px] text-surface-300 max-h-24 overflow-y-auto flex-shrink-0", children: status.detail.map((d, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono truncate", children: [
37195
+ "• ",
37196
+ d
37197
+ ] }, i)) }) : null,
37198
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-t border-surface-800 flex items-center gap-2 flex-shrink-0", children: status.state === "ok" || status.state === "warn" || status.state === "err" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
37199
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `text-[11px] flex-1 ${status.state === "ok" ? "text-green-400" : status.state === "warn" ? "text-amber-400" : "text-red-400"}`, children: [
37200
+ status.state === "ok" ? "✓" : status.state === "warn" ? "⚠" : "✗",
37201
+ " ",
37202
+ status.msg
37203
+ ] }),
37204
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => openCloudMatrix(), className: "px-3 py-1.5 bg-surface-800 hover:bg-surface-700 rounded text-xs", children: "View matrix ↗" }),
37205
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "px-4 py-1.5 bg-blue-600 hover:bg-blue-500 rounded text-xs font-medium", children: "Done" })
37206
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
37207
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: push2, disabled: !canPush || status.state === "pushing", className: "ml-auto px-4 py-1.5 bg-blue-600 hover:bg-blue-500 disabled:opacity-40 rounded text-xs font-medium", children: status.state === "pushing" ? "Publishing…" : "Publish" }),
37208
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "px-4 py-1.5 bg-surface-800 hover:bg-surface-700 rounded text-xs", children: "Cancel" })
37209
+ ] }) })
37210
+ ]
37211
+ }
37212
+ );
37213
+ }
36590
37214
  function MethodBadge({ method, size = "sm" }) {
36591
37215
  const color = getMethodColor(method);
36592
37216
  const cls = size === "xs" ? "text-[10px] font-bold w-10" : "text-xs font-bold w-12";
@@ -36759,6 +37383,20 @@ function DotsBtn({ items: items2 }) {
36759
37383
  menu && /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenu, { items: items2, x: menu.x, y: menu.y, onClose: () => setMenu(null) })
36760
37384
  ] });
36761
37385
  }
37386
+ function useToast(durationMs = 3e3) {
37387
+ const [toast, setToast] = reactExports.useState(null);
37388
+ const timer = reactExports.useRef(null);
37389
+ function show(msg, ok) {
37390
+ if (timer.current) clearTimeout(timer.current);
37391
+ setToast({ msg, ok });
37392
+ timer.current = setTimeout(() => setToast(null), durationMs);
37393
+ }
37394
+ return { toast, show };
37395
+ }
37396
+ function Toast({ toast }) {
37397
+ if (!toast) return null;
37398
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `mx-3 mb-2 px-2 py-1.5 rounded text-[11px] flex-shrink-0 ${toast.ok ? "bg-emerald-900/50 text-emerald-300 border border-emerald-800/50" : "bg-red-900/50 text-red-300 border border-red-800/50"}`, children: toast.msg });
37399
+ }
36762
37400
  function requestPath(url) {
36763
37401
  const trimmed = url.trim().replace(/^\{\{[^}]+\}\}/, "").replace(/^https?:\/\/[^/]+/i, "");
36764
37402
  return trimmed || url.trim();
@@ -36799,19 +37437,49 @@ function RequestRow({
36799
37437
  isActive,
36800
37438
  indent: indent2,
36801
37439
  autoRename = false,
37440
+ examples = [],
37441
+ activeExampleId,
36802
37442
  onSelect,
36803
37443
  onRename,
36804
37444
  onDelete,
36805
37445
  onDuplicate,
36806
37446
  onUpdateTags,
36807
37447
  onSetHookType,
36808
- onToggleDisabled
37448
+ onToggleDisabled,
37449
+ onAddExample,
37450
+ onOpenExample,
37451
+ onRenameExample,
37452
+ onDeleteExample,
37453
+ onDuplicateExample
36809
37454
  }) {
36810
37455
  const [renaming, setRenaming] = reactExports.useState(autoRename);
37456
+ const [examplesOpen, setExamplesOpen] = reactExports.useState(true);
36811
37457
  const [addingTag, setAddingTag] = reactExports.useState(false);
36812
37458
  const [showSchemaSync, setShowSchemaSync] = reactExports.useState(false);
36813
37459
  const [dropPos, setDropPos] = reactExports.useState(null);
36814
37460
  const dragCtx = reactExports.useContext(DragCtx);
37461
+ const sel = reactExports.useContext(SelectionCtx);
37462
+ const selected = sel.isSelected(collectionId, reqId);
37463
+ const { toast, show } = useToast();
37464
+ function handleRowClick(e) {
37465
+ if (e.metaKey || e.ctrlKey) {
37466
+ e.preventDefault();
37467
+ sel.toggle(collectionId, reqId);
37468
+ return;
37469
+ }
37470
+ if (sel.active) sel.clear();
37471
+ onSelect();
37472
+ }
37473
+ async function pushMonitor() {
37474
+ const req = useStore.getState().collections[collectionId]?.data.requests[reqId];
37475
+ if (!req) return;
37476
+ try {
37477
+ const r = await pushRequestAsMonitor(req);
37478
+ show(`Pushed "${req.name}" as monitor #${r.id} to the cloud`, true);
37479
+ } catch (e) {
37480
+ show(e.message, false);
37481
+ }
37482
+ }
36815
37483
  const hookMenuItems = ["beforeAll", "before", "after", "afterAll"].map((ht) => ({
36816
37484
  type: "item",
36817
37485
  label: (hookType === ht ? "✓ " : " ") + HOOK_LABELS[ht],
@@ -36836,9 +37504,9 @@ function RequestRow({
36836
37504
  "div",
36837
37505
  {
36838
37506
  draggable: true,
36839
- className: `group flex items-start gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${disabled ? "opacity-40" : ""} ${isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-300 hover:bg-surface-800"}`,
37507
+ className: `group flex items-start gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${disabled ? "opacity-40" : ""} ${selected ? "bg-blue-950/40 ring-1 ring-inset ring-blue-500" : isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-300 hover:bg-surface-800"}`,
36840
37508
  style: { paddingLeft: indent2 },
36841
- onClick: onSelect,
37509
+ onClick: handleRowClick,
36842
37510
  onDoubleClick: () => setRenaming(true),
36843
37511
  onDragStart: (e) => {
36844
37512
  e.dataTransfer.effectAllowed = "move";
@@ -36852,6 +37520,29 @@ function RequestRow({
36852
37520
  onDragLeave: () => setDropPos(null),
36853
37521
  onDrop: handleDrop,
36854
37522
  children: [
37523
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
37524
+ "input",
37525
+ {
37526
+ type: "checkbox",
37527
+ checked: selected,
37528
+ onClick: (e) => e.stopPropagation(),
37529
+ onChange: () => sel.toggle(collectionId, reqId),
37530
+ title: "Select request (or Cmd/Ctrl+click the row)",
37531
+ className: `shrink-0 mt-0.5 accent-blue-500 cursor-pointer ${sel.active || selected ? "" : "opacity-0 group-hover:opacity-100"}`
37532
+ }
37533
+ ),
37534
+ examples.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
37535
+ "button",
37536
+ {
37537
+ onClick: (e) => {
37538
+ e.stopPropagation();
37539
+ setExamplesOpen((o) => !o);
37540
+ },
37541
+ className: "shrink-0 w-3 text-surface-500 hover:text-white leading-none",
37542
+ title: examplesOpen ? "Hide examples" : `Show ${examples.length} example(s)`,
37543
+ children: examplesOpen ? "▾" : "▸"
37544
+ }
37545
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 w-3" }),
36855
37546
  protocol === "soap" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
36856
37547
  "span",
36857
37548
  {
@@ -36909,19 +37600,38 @@ function RequestRow({
36909
37600
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DotsBtn, { items: [
36910
37601
  { type: "item", label: "Rename", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}), onClick: () => setRenaming(true) },
36911
37602
  { type: "item", label: "Duplicate", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: onDuplicate },
37603
+ ...onAddExample ? [{ type: "item", label: "Add Example", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: () => {
37604
+ onAddExample();
37605
+ setExamplesOpen(true);
37606
+ } }] : [],
36912
37607
  { type: "item", label: "Add tag", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, {}), onClick: () => setAddingTag(true) },
36913
37608
  { type: "item", label: "Sync schema", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(SyncIcon, {}), onClick: () => setShowSchemaSync(true) },
36914
37609
  { type: "item", label: disabled ? "Enable" : "Disable", onClick: onToggleDisabled },
37610
+ ...cloudEnabled() ? [{ type: "item", label: "Push as monitor to cloud", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(SyncIcon, {}), onClick: pushMonitor }] : [],
36915
37611
  { type: "separator" },
36916
37612
  { type: "header", label: "Hook type" },
36917
37613
  ...hookMenuItems,
36918
37614
  { type: "separator" },
36919
37615
  { type: "item", label: "Delete", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}), danger: true, onClick: onDelete }
36920
37616
  ] }) }),
37617
+ toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed bottom-4 right-4 z-[100] w-96", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Toast, { toast }) }),
36921
37618
  dropPos === "after" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" })
36922
37619
  ]
36923
37620
  }
36924
37621
  ),
37622
+ examplesOpen && examples.map((ex) => /* @__PURE__ */ jsxRuntimeExports.jsx(
37623
+ ExampleRow,
37624
+ {
37625
+ name: ex.name,
37626
+ indent: indent2 + 18,
37627
+ isActive: activeExampleId === ex.id,
37628
+ onOpen: () => onOpenExample?.(ex.id),
37629
+ onRename: (n) => onRenameExample?.(ex.id, n),
37630
+ onDelete: () => onDeleteExample?.(ex.id),
37631
+ onDuplicate: () => onDuplicateExample?.(ex.id)
37632
+ },
37633
+ ex.id
37634
+ )),
36925
37635
  showSchemaSync && /* @__PURE__ */ jsxRuntimeExports.jsx(
36926
37636
  SchemaSyncModal,
36927
37637
  {
@@ -36932,6 +37642,40 @@ function RequestRow({
36932
37642
  )
36933
37643
  ] });
36934
37644
  }
37645
+ function ExampleRow({ name: name2, indent: indent2, isActive, onOpen, onRename, onDelete, onDuplicate }) {
37646
+ const [renaming, setRenaming] = reactExports.useState(false);
37647
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
37648
+ "div",
37649
+ {
37650
+ className: `group flex items-center gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-400 hover:bg-surface-800"}`,
37651
+ style: { paddingLeft: indent2 },
37652
+ onClick: onOpen,
37653
+ onDoubleClick: () => setRenaming(true),
37654
+ children: [
37655
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-[8px] font-bold px-1 py-px rounded bg-surface-700 text-surface-300", title: "Example", children: "EX" }),
37656
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0", children: renaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(
37657
+ InlineEdit,
37658
+ {
37659
+ value: name2,
37660
+ onCommit: (v) => {
37661
+ onRename(v);
37662
+ setRenaming(false);
37663
+ },
37664
+ onCancel: () => setRenaming(false),
37665
+ className: "w-full text-xs"
37666
+ }
37667
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs truncate", children: name2 }) }),
37668
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DotsBtn, { items: [
37669
+ { type: "item", label: "Open", onClick: onOpen },
37670
+ { type: "item", label: "Rename", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}), onClick: () => setRenaming(true) },
37671
+ { type: "item", label: "Duplicate", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: onDuplicate },
37672
+ { type: "separator" },
37673
+ { type: "item", label: "Delete", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}), danger: true, onClick: onDelete }
37674
+ ] }) })
37675
+ ]
37676
+ }
37677
+ );
37678
+ }
36935
37679
  function ConfirmDialog({ message, onConfirm, onCancel }) {
36936
37680
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
36937
37681
  Modal,
@@ -36967,6 +37711,9 @@ const DragCtx = reactExports.createContext({ dragging: null, setDragging: () =>
36967
37711
  }, onDropRequest: () => {
36968
37712
  }, onDropFolder: () => {
36969
37713
  } });
37714
+ const SelectionCtx = reactExports.createContext({ active: false, isSelected: () => false, toggle: () => {
37715
+ }, clear: () => {
37716
+ } });
36970
37717
  function TagChips({
36971
37718
  tags: tags2,
36972
37719
  onRemove,
@@ -37031,6 +37778,36 @@ function TagChips({
37031
37778
  ) : null
37032
37779
  ] });
37033
37780
  }
37781
+ function methodColor$2(method) {
37782
+ switch (method) {
37783
+ case "GET":
37784
+ return "text-emerald-400";
37785
+ case "POST":
37786
+ return "text-amber-400";
37787
+ case "PUT":
37788
+ return "text-sky-400";
37789
+ case "PATCH":
37790
+ return "text-violet-400";
37791
+ case "DELETE":
37792
+ return "text-red-400";
37793
+ default:
37794
+ return "text-surface-300";
37795
+ }
37796
+ }
37797
+ function collectMatches(col, q) {
37798
+ const out = [];
37799
+ const walk = (folder, path) => {
37800
+ for (const reqId of folder.requestIds) {
37801
+ const req = col.requests[reqId];
37802
+ if (!req) continue;
37803
+ const hay = [req.name, req.url, req.method, ...req.meta?.tags ?? []].join(" ").toLowerCase();
37804
+ if (hay.includes(q)) out.push({ collectionId: col.id, collectionName: col.name, req, path });
37805
+ }
37806
+ for (const sub of folder.folders) walk(sub, [...path, sub.name]);
37807
+ };
37808
+ walk(col.rootFolder, []);
37809
+ return out;
37810
+ }
37034
37811
  function CollectionTree() {
37035
37812
  const collections = useStore((s) => s.collections);
37036
37813
  const activeCollectionId = useStore((s) => s.activeCollectionId);
@@ -37059,7 +37836,30 @@ function CollectionTree() {
37059
37836
  const moveRequest = useStore((s) => s.moveRequest);
37060
37837
  const moveFolder = useStore((s) => s.moveFolder);
37061
37838
  const colList = Object.values(collections);
37839
+ const [query, setQuery] = reactExports.useState("");
37840
+ const q = query.trim().toLowerCase();
37841
+ const matches = q ? colList.flatMap(({ data }) => collectMatches(data, q)) : [];
37062
37842
  const [pendingConfirm, setPendingConfirm] = reactExports.useState(null);
37843
+ const [selected, setSelected] = reactExports.useState(() => /* @__PURE__ */ new Set());
37844
+ const selKey = (c, r) => `${c}\0${r}`;
37845
+ const selectionCtx = {
37846
+ active: selected.size > 0,
37847
+ isSelected: (c, r) => selected.has(selKey(c, r)),
37848
+ toggle: (c, r) => setSelected((prev) => {
37849
+ const next = new Set(prev);
37850
+ const k = selKey(c, r);
37851
+ if (next.has(k)) next.delete(k);
37852
+ else next.add(k);
37853
+ return next;
37854
+ }),
37855
+ clear: () => setSelected(/* @__PURE__ */ new Set())
37856
+ };
37857
+ function forEachSelected(fn) {
37858
+ selected.forEach((k) => {
37859
+ const i = k.indexOf("\0");
37860
+ fn(k.slice(0, i), k.slice(i + 1));
37861
+ });
37862
+ }
37063
37863
  const [newRequestId, setNewRequestId] = reactExports.useState(null);
37064
37864
  const [dragging, setDragging] = reactExports.useState(null);
37065
37865
  function confirmThen(message, action) {
@@ -37079,7 +37879,7 @@ function CollectionTree() {
37079
37879
  moveFolder(dragging.collectionId, dragging.folderId, destParentFolderId, destIndex);
37080
37880
  setDragging(null);
37081
37881
  }
37082
- return /* @__PURE__ */ jsxRuntimeExports.jsx(DragCtx.Provider, { value: { dragging, setDragging, onDropRequest, onDropFolder }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 select-none", children: [
37882
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(DragCtx.Provider, { value: { dragging, setDragging, onDropRequest, onDropFolder }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectionCtx.Provider, { value: selectionCtx, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 select-none", children: [
37083
37883
  pendingConfirm && /* @__PURE__ */ jsxRuntimeExports.jsx(
37084
37884
  ConfirmDialog,
37085
37885
  {
@@ -37088,7 +37888,55 @@ function CollectionTree() {
37088
37888
  onCancel: () => setPendingConfirm(null)
37089
37889
  }
37090
37890
  ),
37091
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
37891
+ colList.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 py-1.5 border-b border-surface-800 shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
37892
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
37893
+ "input",
37894
+ {
37895
+ value: query,
37896
+ onChange: (e) => setQuery(e.target.value),
37897
+ onKeyDown: (e) => {
37898
+ if (e.key === "Escape") setQuery("");
37899
+ },
37900
+ placeholder: "Search requests…",
37901
+ className: "w-full text-xs bg-surface-800 border border-surface-700 rounded pl-2 pr-6 py-1 focus:outline-none focus:border-blue-500 placeholder-surface-500"
37902
+ }
37903
+ ),
37904
+ query && /* @__PURE__ */ jsxRuntimeExports.jsx(
37905
+ "button",
37906
+ {
37907
+ onClick: () => setQuery(""),
37908
+ title: "Clear (Esc)",
37909
+ className: "absolute right-1 top-1/2 -translate-y-1/2 w-4 h-4 flex items-center justify-center text-surface-500 hover:text-surface-200 leading-none",
37910
+ children: "×"
37911
+ }
37912
+ )
37913
+ ] }) }),
37914
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto", children: q ? matches.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "px-3 py-3 text-xs text-surface-500", children: [
37915
+ "No requests match “",
37916
+ query.trim(),
37917
+ "”."
37918
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "py-1", children: [
37919
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "px-3 pb-1 text-[10px] uppercase tracking-wider text-surface-500", children: [
37920
+ matches.length,
37921
+ " ",
37922
+ matches.length === 1 ? "match" : "matches"
37923
+ ] }),
37924
+ matches.map(({ collectionId, collectionName, req, path }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
37925
+ "button",
37926
+ {
37927
+ onClick: () => openInTab(req.id, collectionId),
37928
+ className: `w-full text-left flex items-baseline gap-2 px-3 py-1 hover:bg-surface-800 transition-colors ${req.id === activeRequestId ? "bg-surface-800" : ""}`,
37929
+ children: [
37930
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] font-mono font-bold w-10 shrink-0 ${methodColor$2(req.method)}`, children: req.method }),
37931
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex-1 min-w-0", children: [
37932
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-surface-200 truncate block", children: req.name }),
37933
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-500 truncate block", children: [collectionName, ...path].join(" › ") })
37934
+ ] })
37935
+ ]
37936
+ },
37937
+ `${collectionId}:${req.id}`
37938
+ ))
37939
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
37092
37940
  colList.map(({ data: col }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
37093
37941
  CollectionNode,
37094
37942
  {
@@ -37130,8 +37978,66 @@ function CollectionTree() {
37130
37978
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => addCollection("New Collection"), className: "text-blue-400 hover:text-blue-300 transition-colors", children: "+ New collection" }),
37131
37979
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "pt-1", children: "or import from Postman / OpenAPI above." })
37132
37980
  ] })
37981
+ ] }) }),
37982
+ selected.size > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "shrink-0 border-t border-surface-700 bg-surface-900 px-2 py-1.5 flex items-center gap-1 text-xs", children: [
37983
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-300 mr-auto", children: [
37984
+ selected.size,
37985
+ " selected"
37986
+ ] }),
37987
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
37988
+ "button",
37989
+ {
37990
+ onClick: () => forEachSelected((_c, r) => updateRequest(r, { disabled: false })),
37991
+ className: "px-2 py-0.5 rounded hover:bg-surface-800 text-surface-300 transition-colors",
37992
+ title: "Enable selected requests",
37993
+ children: "Enable"
37994
+ }
37995
+ ),
37996
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
37997
+ "button",
37998
+ {
37999
+ onClick: () => forEachSelected((_c, r) => updateRequest(r, { disabled: true })),
38000
+ className: "px-2 py-0.5 rounded hover:bg-surface-800 text-surface-300 transition-colors",
38001
+ title: "Disable selected requests",
38002
+ children: "Disable"
38003
+ }
38004
+ ),
38005
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
38006
+ "button",
38007
+ {
38008
+ onClick: () => {
38009
+ forEachSelected((c, r) => duplicateRequest(c, r));
38010
+ selectionCtx.clear();
38011
+ },
38012
+ className: "px-2 py-0.5 rounded hover:bg-surface-800 text-surface-300 transition-colors",
38013
+ children: "Duplicate"
38014
+ }
38015
+ ),
38016
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
38017
+ "button",
38018
+ {
38019
+ onClick: () => confirmThen(
38020
+ `Delete ${selected.size} request${selected.size === 1 ? "" : "s"}?`,
38021
+ () => {
38022
+ forEachSelected((c, r) => deleteRequest(c, r));
38023
+ selectionCtx.clear();
38024
+ }
38025
+ ),
38026
+ className: "px-2 py-0.5 rounded hover:bg-red-900/40 text-red-400 transition-colors",
38027
+ children: "Delete"
38028
+ }
38029
+ ),
38030
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
38031
+ "button",
38032
+ {
38033
+ onClick: selectionCtx.clear,
38034
+ className: "px-1.5 py-0.5 rounded hover:bg-surface-800 text-surface-500 transition-colors",
38035
+ title: "Clear selection",
38036
+ children: "×"
38037
+ }
38038
+ )
37133
38039
  ] })
37134
- ] }) });
38040
+ ] }) }) });
37135
38041
  }
37136
38042
  function CollectionNode({
37137
38043
  col,
@@ -37163,6 +38069,7 @@ function CollectionNode({
37163
38069
  const [renaming, setRenaming] = reactExports.useState(false);
37164
38070
  const [showSettings, setShowSettings] = reactExports.useState(false);
37165
38071
  const [showSchemaSync, setShowSchemaSync] = reactExports.useState(false);
38072
+ const [showPushContract, setShowPushContract] = reactExports.useState(false);
37166
38073
  const [expandCtrl, setExpandCtrl] = reactExports.useState({ value: true, seq: 0 });
37167
38074
  const [dropOver, setDropOver] = reactExports.useState(false);
37168
38075
  const dragCtx = reactExports.useContext(DragCtx);
@@ -37219,6 +38126,7 @@ function CollectionNode({
37219
38126
  { type: "item", label: "Collection data", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TableIcon, {}), onClick: onSelectCollection },
37220
38127
  { type: "item", label: "Settings", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(GearIcon, {}), onClick: () => setShowSettings(true) },
37221
38128
  { type: "item", label: "Sync schemas", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(SyncIcon, {}), onClick: () => setShowSchemaSync(true) },
38129
+ ...cloudEnabled() ? [{ type: "item", label: "Push contract to cloud", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(SyncIcon, {}), onClick: () => setShowPushContract(true) }] : [],
37222
38130
  { type: "item", label: "Rename", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}), onClick: () => setRenaming(true) },
37223
38131
  { type: "item", label: "Duplicate", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: onDuplicateCollection },
37224
38132
  { type: "separator" },
@@ -37253,6 +38161,14 @@ function CollectionNode({
37253
38161
  onRunFolder
37254
38162
  }
37255
38163
  ),
38164
+ showPushContract && /* @__PURE__ */ jsxRuntimeExports.jsx(
38165
+ PushContractModal,
38166
+ {
38167
+ requests: collectTagged(col.rootFolder, col.requests, col.collectionVariables ?? {}, []).map((c) => c.request),
38168
+ defaultConsumer: col.name,
38169
+ onClose: () => setShowPushContract(false)
38170
+ }
38171
+ ),
37256
38172
  showSettings && /* @__PURE__ */ jsxRuntimeExports.jsx(CollectionSettingsModal, { collection: col, onClose: () => setShowSettings(false) }),
37257
38173
  showSchemaSync && /* @__PURE__ */ jsxRuntimeExports.jsx(SchemaSyncModal, { collectionId: col.id, scope: { type: "collection" }, onClose: () => setShowSchemaSync(false) })
37258
38174
  ] });
@@ -37280,7 +38196,9 @@ function FolderRow({
37280
38196
  const [renaming, setRenaming] = reactExports.useState(false);
37281
38197
  const [showSettings, setShowSettings] = reactExports.useState(false);
37282
38198
  const [showSchemaSync, setShowSchemaSync] = reactExports.useState(false);
38199
+ const [showPushContract, setShowPushContract] = reactExports.useState(false);
37283
38200
  const [addingTag, setAddingTag] = reactExports.useState(false);
38201
+ const folderCollection = useStore((s) => s.collections[collectionId]?.data);
37284
38202
  const [dropPos, setDropPos] = reactExports.useState(null);
37285
38203
  const dragCtx = reactExports.useContext(DragCtx);
37286
38204
  const tags2 = folder.tags ?? [];
@@ -37374,6 +38292,7 @@ function FolderRow({
37374
38292
  { type: "separator" },
37375
38293
  { type: "item", label: "Settings", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(KeyIcon, {}), onClick: () => setShowSettings(true) },
37376
38294
  { type: "item", label: "Sync schemas", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(SyncIcon, {}), onClick: () => setShowSchemaSync(true) },
38295
+ ...cloudEnabled() ? [{ type: "item", label: "Push contract to cloud", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(SyncIcon, {}), onClick: () => setShowPushContract(true) }] : [],
37377
38296
  { type: "item", label: "Add tag", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TagIcon, {}), onClick: () => setAddingTag(true) },
37378
38297
  { type: "item", label: "Rename", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}), onClick: () => setRenaming(true) },
37379
38298
  { type: "item", label: "Duplicate", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyIcon, {}), onClick: onDuplicate },
@@ -37384,6 +38303,14 @@ function FolderRow({
37384
38303
  }
37385
38304
  ),
37386
38305
  expanded && children,
38306
+ showPushContract && folderCollection && /* @__PURE__ */ jsxRuntimeExports.jsx(
38307
+ PushContractModal,
38308
+ {
38309
+ requests: collectTagged(folder, folderCollection.requests, {}, []).map((c) => c.request),
38310
+ defaultConsumer: folder.name,
38311
+ onClose: () => setShowPushContract(false)
38312
+ }
38313
+ ),
37387
38314
  showSettings && /* @__PURE__ */ jsxRuntimeExports.jsx(
37388
38315
  FolderSettingsModal,
37389
38316
  {
@@ -37425,6 +38352,15 @@ function FolderContents({
37425
38352
  onToggleRequestDisabled,
37426
38353
  onRunFolder
37427
38354
  }) {
38355
+ const activeExampleId = useStore((s) => {
38356
+ const t2 = s.tabs.find((x) => x.id === s.activeTabId);
38357
+ return t2?.exampleId ?? null;
38358
+ });
38359
+ const addExampleFromRequest = useStore((s) => s.addExampleFromRequest);
38360
+ const openExample = useStore((s) => s.openExample);
38361
+ const renameExample = useStore((s) => s.renameExample);
38362
+ const deleteExample = useStore((s) => s.deleteExample);
38363
+ const duplicateExample = useStore((s) => s.duplicateExample);
37428
38364
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
37429
38365
  folder.folders.map((sub, subIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx(
37430
38366
  FolderRow,
@@ -37492,206 +38428,61 @@ function FolderContents({
37492
38428
  isActive: req.id === activeRequestId,
37493
38429
  autoRename: req.id === newRequestId,
37494
38430
  indent: (depth + 1) * 12 + 8,
38431
+ examples: req.examples?.map((e) => ({ id: e.id, name: e.name })),
38432
+ activeExampleId: req.id === activeRequestId ? activeExampleId : null,
37495
38433
  onSelect: () => onSelectRequest(req.id),
37496
38434
  onRename: (name2) => onRenameRequest(req.id, name2),
37497
38435
  onDelete: () => onDeleteRequest(req.id),
37498
38436
  onDuplicate: () => onDuplicateRequest(req.id),
37499
38437
  onUpdateTags: (tags2) => onUpdateRequestTags(req.id, tags2),
37500
38438
  onSetHookType: (ht) => onSetRequestHookType(req.id, ht),
37501
- onToggleDisabled: () => onToggleRequestDisabled(req.id)
38439
+ onToggleDisabled: () => onToggleRequestDisabled(req.id),
38440
+ onAddExample: () => {
38441
+ const exId = addExampleFromRequest(req.id);
38442
+ if (exId) openExample(req.id, collectionId, exId);
38443
+ },
38444
+ onOpenExample: (exId) => openExample(req.id, collectionId, exId),
38445
+ onRenameExample: (exId, name2) => renameExample(req.id, exId, name2),
38446
+ onDeleteExample: (exId) => deleteExample(req.id, exId),
38447
+ onDuplicateExample: (exId) => duplicateExample(req.id, exId)
37502
38448
  },
37503
38449
  req.id
37504
38450
  );
37505
38451
  })
37506
38452
  ] });
37507
38453
  }
37508
- function folderChainTo(root2, targetId) {
37509
- if (root2.id === targetId) return [root2];
37510
- for (const sub of root2.folders) {
37511
- const chain = folderChainTo(sub, targetId);
37512
- if (chain.length > 0) return [root2, ...chain];
37513
- }
37514
- return [];
37515
- }
37516
- function makeHook(req, collectionVars, hookType, scopeId, scopeAncestors, scopePath, mainRequestId) {
37517
- return { request: req, collectionVars, isHook: true, hookType, scopeId, scopeAncestors, scopePath, mainRequestId };
37518
- }
37519
- function buildFolderPlan(folder, requests, collectionVars, filterTags, scopeId, ancestorIds, parentPath, wrappers, isRoot) {
37520
- const result = [];
37521
- const scopePath = isRoot ? [] : [...parentPath, folder.name];
37522
- const folderReqs = folder.requestIds.map((id2) => requests[id2]).filter((r) => r && !r.disabled);
37523
- const beforeAllHooks = folderReqs.filter((r) => r.hookType === "beforeAll");
37524
- const beforeHooks = folderReqs.filter((r) => r.hookType === "before");
37525
- const afterHooks = folderReqs.filter((r) => r.hookType === "after");
37526
- const afterAllHooks = folderReqs.filter((r) => r.hookType === "afterAll");
37527
- const regularReqs = folderReqs.filter((r) => !r.hookType);
37528
- const myWrapper = { scopeId, ancestors: ancestorIds, scopePath, before: beforeHooks, after: afterHooks };
37529
- const allWrappers = [...wrappers, myWrapper];
37530
- for (const req of beforeAllHooks) {
37531
- result.push(makeHook(req, collectionVars, "beforeAll", scopeId, ancestorIds, scopePath));
37532
- }
37533
- for (const req of regularReqs) {
37534
- const tags2 = req.meta?.tags ?? [];
37535
- if (filterTags.length > 0 && !filterTags.some((t2) => tags2.includes(t2))) continue;
37536
- for (const w of allWrappers) {
37537
- for (const hookReq of w.before) {
37538
- result.push(makeHook(hookReq, collectionVars, "before", w.scopeId, w.ancestors, w.scopePath, req.id));
37539
- }
37540
- }
37541
- result.push({ request: req, collectionVars, scopeId, scopeAncestors: ancestorIds, scopePath });
37542
- for (const w of [...allWrappers].reverse()) {
37543
- for (const hookReq of w.after) {
37544
- result.push(makeHook(hookReq, collectionVars, "after", w.scopeId, w.ancestors, w.scopePath, req.id));
37545
- }
37546
- }
37547
- }
37548
- for (const sub of folder.folders) {
37549
- const folderTags = sub.tags ?? [];
37550
- const effectiveFilter = filterTags.length === 0 ? filterTags : folderTags.some((t2) => filterTags.includes(t2)) ? [] : filterTags;
37551
- result.push(...buildFolderPlan(
37552
- sub,
37553
- requests,
37554
- collectionVars,
37555
- effectiveFilter,
37556
- sub.id,
37557
- [...ancestorIds, scopeId],
37558
- scopePath,
37559
- allWrappers,
37560
- false
37561
- ));
37562
- }
37563
- for (const req of afterAllHooks) {
37564
- result.push(makeHook(req, collectionVars, "afterAll", scopeId, ancestorIds, scopePath));
37565
- }
37566
- return result;
37567
- }
37568
- function collectAllTags(folder, requests) {
37569
- const tags2 = /* @__PURE__ */ new Set();
37570
- function walk(f) {
37571
- (f.tags ?? []).forEach((t2) => tags2.add(t2));
37572
- for (const reqId of f.requestIds) {
37573
- (requests[reqId]?.meta?.tags ?? []).forEach((t2) => tags2.add(t2));
37574
- }
37575
- f.folders.forEach(walk);
37576
- }
37577
- walk(folder);
37578
- return Array.from(tags2).sort();
37579
- }
37580
- function folderPathTo(root2, requestId) {
37581
- if (root2.requestIds.includes(requestId)) return [root2];
37582
- for (const sub of root2.folders) {
37583
- const path = folderPathTo(sub, requestId);
37584
- if (path.length > 0) return [root2, ...path];
37585
- }
37586
- return [];
37587
- }
37588
- function getHooksForRequest(requestId, collection) {
37589
- const path = folderPathTo(collection.rootFolder, requestId);
37590
- const before = [];
37591
- const afterReversed = [];
37592
- for (const folder of path) {
37593
- const reqs = folder.requestIds.map((id2) => collection.requests[id2]).filter((r) => r && !r.disabled);
37594
- const bAll = reqs.filter((r) => r.hookType === "beforeAll");
37595
- const bEach = reqs.filter((r) => r.hookType === "before");
37596
- const aEach = reqs.filter((r) => r.hookType === "after");
37597
- const aAll = reqs.filter((r) => r.hookType === "afterAll");
37598
- before.push(...bAll, ...bEach);
37599
- afterReversed.push(...aEach, ...aAll);
37600
- }
37601
- return { before, after: afterReversed.reverse() };
37602
- }
37603
- function authIsConfigured(auth) {
37604
- if (!auth || auth.type === "none") return false;
37605
- const set2 = (s) => !!(s && s.trim());
37606
- switch (auth.type) {
37607
- case "bearer":
37608
- return set2(auth.token) || set2(auth.tokenSecretRef);
37609
- case "basic":
37610
- case "digest":
37611
- return set2(auth.username) || set2(auth.password) || set2(auth.passwordSecretRef);
37612
- case "ntlm":
37613
- return set2(auth.username) || set2(auth.password) || set2(auth.passwordSecretRef);
37614
- case "apikey":
37615
- return set2(auth.apiKeyValue) || set2(auth.apiKeySecretRef);
37616
- case "oauth2":
37617
- return set2(auth.oauth2TokenUrl) || set2(auth.oauth2ClientId) || set2(auth.oauth2CachedToken);
37618
- default:
37619
- return true;
37620
- }
37621
- }
37622
- function resolveInheritedAuthAndHeaders(requestId, collection) {
37623
- let inheritedAuth = collection.auth && collection.auth.type !== "none" ? collection.auth : null;
37624
- let inheritedHeaders = collection.headers?.filter((h) => h.enabled && h.key) ?? [];
37625
- const path = folderPathTo(collection.rootFolder, requestId);
37626
- for (const folder of path) {
37627
- if (folder.auth && folder.auth.type !== "none") inheritedAuth = folder.auth;
37628
- if (folder.headers?.length) {
37629
- inheritedHeaders = [...inheritedHeaders, ...folder.headers.filter((h) => h.enabled && h.key)];
37630
- }
37631
- }
37632
- return { auth: inheritedAuth, headers: inheritedHeaders };
37633
- }
37634
- function buildRunPlan(collection, folderId, filterTags) {
37635
- const collectionVars = collection.collectionVariables ?? {};
37636
- if (!folderId) {
37637
- return buildFolderPlan(
37638
- collection.rootFolder,
37639
- collection.requests,
37640
- collectionVars,
37641
- filterTags,
37642
- collection.rootFolder.id,
37643
- [],
37644
- [],
37645
- [],
37646
- true
37647
- );
37648
- }
37649
- const chain = folderChainTo(collection.rootFolder, folderId);
37650
- if (chain.length === 0) return [];
37651
- const targetFolder = chain[chain.length - 1];
37652
- const ancestors = chain.slice(0, -1);
37653
- const result = [];
37654
- const ancestorWrappers = [];
37655
- const ancestorIds = [];
37656
- for (const f of ancestors) {
37657
- const reqs = f.requestIds.map((id2) => collection.requests[id2]).filter((r) => r && !r.disabled);
37658
- const beforeAllH = reqs.filter((r) => r.hookType === "beforeAll");
37659
- const beforeH = reqs.filter((r) => r.hookType === "before");
37660
- const afterH = reqs.filter((r) => r.hookType === "after");
37661
- for (const req of beforeAllH) {
37662
- result.push(makeHook(req, collectionVars, "beforeAll", f.id, [...ancestorIds], []));
37663
- }
37664
- ancestorWrappers.push({
37665
- scopeId: f.id,
37666
- ancestors: [...ancestorIds],
37667
- scopePath: [],
37668
- // ancestor hooks render with no folder heading
37669
- before: beforeH,
37670
- after: afterH
37671
- });
37672
- ancestorIds.push(f.id);
37673
- }
37674
- result.push(...buildFolderPlan(
37675
- targetFolder,
37676
- collection.requests,
37677
- collectionVars,
37678
- filterTags,
37679
- targetFolder.id,
37680
- ancestorIds,
37681
- [],
37682
- ancestorWrappers,
37683
- true
37684
- ));
37685
- for (let i = ancestors.length - 1; i >= 0; i--) {
37686
- const f = ancestors[i];
37687
- const reqs = f.requestIds.map((id2) => collection.requests[id2]).filter((r) => r && !r.disabled);
37688
- const afterAllH = reqs.filter((r) => r.hookType === "afterAll");
37689
- const myAncestors = ancestorIds.slice(0, i);
37690
- for (const req of afterAllH) {
37691
- result.push(makeHook(req, collectionVars, "afterAll", f.id, myAncestors, []));
37692
- }
38454
+ function safeDecode(s) {
38455
+ try {
38456
+ return decodeURIComponent(s);
38457
+ } catch {
38458
+ return s;
38459
+ }
38460
+ }
38461
+ function extractQueryParams(rawUrl, existing) {
38462
+ const qIdx = rawUrl.indexOf("?");
38463
+ if (qIdx === -1) return { url: rawUrl, params: existing, changed: false };
38464
+ const base2 = rawUrl.slice(0, qIdx);
38465
+ let query = rawUrl.slice(qIdx + 1);
38466
+ let fragment = "";
38467
+ const hashIdx = query.indexOf("#");
38468
+ if (hashIdx !== -1) {
38469
+ fragment = query.slice(hashIdx);
38470
+ query = query.slice(0, hashIdx);
38471
+ }
38472
+ const parsed = [];
38473
+ for (const pair2 of query.split("&")) {
38474
+ if (!pair2) continue;
38475
+ const eq = pair2.indexOf("=");
38476
+ const key = safeDecode(eq === -1 ? pair2 : pair2.slice(0, eq));
38477
+ if (!key) continue;
38478
+ const value = eq === -1 ? "" : safeDecode(pair2.slice(eq + 1));
38479
+ parsed.push({ key, value, enabled: true });
37693
38480
  }
37694
- return result;
38481
+ const nextUrl = base2 + fragment;
38482
+ if (parsed.length === 0) return { url: nextUrl, params: existing, changed: nextUrl !== rawUrl };
38483
+ const pastedKeys = new Set(parsed.map((p2) => p2.key));
38484
+ const kept = (existing ?? []).filter((p2) => (p2.key || p2.value) && !pastedKeys.has(p2.key));
38485
+ return { url: nextUrl, params: [...kept, ...parsed], changed: true };
37695
38486
  }
37696
38487
  function ParamsTab({ request, onChange }) {
37697
38488
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -42951,6 +43742,393 @@ const jsonLanguage = /* @__PURE__ */ LRLanguage.define({
42951
43742
  function json() {
42952
43743
  return new LanguageSupport(jsonLanguage);
42953
43744
  }
43745
+ const StartTag = 1, StartCloseTag = 2, MissingCloseTag = 3, mismatchedStartCloseTag = 4, incompleteStartCloseTag = 5, commentContent$1 = 36, piContent$1 = 37, cdataContent$1 = 38, Element$1 = 11, OpenTag = 13;
43746
+ function nameChar(ch) {
43747
+ return ch == 45 || ch == 46 || ch == 58 || ch >= 65 && ch <= 90 || ch == 95 || ch >= 97 && ch <= 122 || ch >= 161;
43748
+ }
43749
+ function isSpace(ch) {
43750
+ return ch == 9 || ch == 10 || ch == 13 || ch == 32;
43751
+ }
43752
+ let cachedName = null, cachedInput = null, cachedPos = 0;
43753
+ function tagNameAfter(input, offset) {
43754
+ let pos = input.pos + offset;
43755
+ if (cachedInput == input && cachedPos == pos) return cachedName;
43756
+ while (isSpace(input.peek(offset))) offset++;
43757
+ let name2 = "";
43758
+ for (; ; ) {
43759
+ let next = input.peek(offset);
43760
+ if (!nameChar(next)) break;
43761
+ name2 += String.fromCharCode(next);
43762
+ offset++;
43763
+ }
43764
+ cachedInput = input;
43765
+ cachedPos = pos;
43766
+ return cachedName = name2 || null;
43767
+ }
43768
+ function ElementContext(name2, parent) {
43769
+ this.name = name2;
43770
+ this.parent = parent;
43771
+ }
43772
+ const elementContext = new ContextTracker({
43773
+ start: null,
43774
+ shift(context, term, stack, input) {
43775
+ return term == StartTag ? new ElementContext(tagNameAfter(input, 1) || "", context) : context;
43776
+ },
43777
+ reduce(context, term) {
43778
+ return term == Element$1 && context ? context.parent : context;
43779
+ },
43780
+ reuse(context, node, _stack, input) {
43781
+ let type2 = node.type.id;
43782
+ return type2 == StartTag || type2 == OpenTag ? new ElementContext(tagNameAfter(input, 1) || "", context) : context;
43783
+ },
43784
+ strict: false
43785
+ });
43786
+ const startTag = new ExternalTokenizer((input, stack) => {
43787
+ if (input.next != 60) return;
43788
+ input.advance();
43789
+ if (input.next == 47) {
43790
+ input.advance();
43791
+ let name2 = tagNameAfter(input, 0);
43792
+ if (!name2) return input.acceptToken(incompleteStartCloseTag);
43793
+ if (stack.context && name2 == stack.context.name) return input.acceptToken(StartCloseTag);
43794
+ for (let cx = stack.context; cx; cx = cx.parent) if (cx.name == name2) return input.acceptToken(MissingCloseTag, -2);
43795
+ input.acceptToken(mismatchedStartCloseTag);
43796
+ } else if (input.next != 33 && input.next != 63) {
43797
+ return input.acceptToken(StartTag);
43798
+ }
43799
+ }, { contextual: true });
43800
+ function scanTo(type2, end) {
43801
+ return new ExternalTokenizer((input) => {
43802
+ let len = 0, first = end.charCodeAt(0);
43803
+ scan: for (; ; input.advance(), len++) {
43804
+ if (input.next < 0) break;
43805
+ if (input.next == first) {
43806
+ for (let i = 1; i < end.length; i++)
43807
+ if (input.peek(i) != end.charCodeAt(i)) continue scan;
43808
+ break;
43809
+ }
43810
+ }
43811
+ if (len) input.acceptToken(type2);
43812
+ });
43813
+ }
43814
+ const commentContent = scanTo(commentContent$1, "-->");
43815
+ const piContent = scanTo(piContent$1, "?>");
43816
+ const cdataContent = scanTo(cdataContent$1, "]]>");
43817
+ const xmlHighlighting = styleTags({
43818
+ Text: tags.content,
43819
+ "StartTag StartCloseTag EndTag SelfCloseEndTag": tags.angleBracket,
43820
+ TagName: tags.tagName,
43821
+ "MismatchedCloseTag/TagName": [tags.tagName, tags.invalid],
43822
+ AttributeName: tags.attributeName,
43823
+ AttributeValue: tags.attributeValue,
43824
+ Is: tags.definitionOperator,
43825
+ "EntityReference CharacterReference": tags.character,
43826
+ Comment: tags.blockComment,
43827
+ ProcessingInst: tags.processingInstruction,
43828
+ DoctypeDecl: tags.documentMeta,
43829
+ Cdata: tags.special(tags.string)
43830
+ });
43831
+ const parser$2 = LRParser.deserialize({
43832
+ version: 14,
43833
+ states: ",lOQOaOOOrOxO'#CfOzOpO'#CiO!tOaO'#CgOOOP'#Cg'#CgO!{OrO'#CrO#TOtO'#CsO#]OpO'#CtOOOP'#DT'#DTOOOP'#Cv'#CvQQOaOOOOOW'#Cw'#CwO#eOxO,59QOOOP,59Q,59QOOOO'#Cx'#CxO#mOpO,59TO#uO!bO,59TOOOP'#C|'#C|O$TOaO,59RO$[OpO'#CoOOOP,59R,59ROOOQ'#C}'#C}O$dOrO,59^OOOP,59^,59^OOOS'#DO'#DOO$lOtO,59_OOOP,59_,59_O$tOpO,59`O$|OpO,59`OOOP-E6t-E6tOOOW-E6u-E6uOOOP1G.l1G.lOOOO-E6v-E6vO%UO!bO1G.oO%UO!bO1G.oO%dOpO'#CkO%lO!bO'#CyO%zO!bO1G.oOOOP1G.o1G.oOOOP1G.w1G.wOOOP-E6z-E6zOOOP1G.m1G.mO&VOpO,59ZO&_OpO,59ZOOOQ-E6{-E6{OOOP1G.x1G.xOOOS-E6|-E6|OOOP1G.y1G.yO&gOpO1G.zO&gOpO1G.zOOOP1G.z1G.zO&oO!bO7+$ZO&}O!bO7+$ZOOOP7+$Z7+$ZOOOP7+$c7+$cO'YOpO,59VO'bOpO,59VO'mO!bO,59eOOOO-E6w-E6wO'{OpO1G.uO'{OpO1G.uOOOP1G.u1G.uO(TOpO7+$fOOOP7+$f7+$fO(]O!bO<<GuOOOP<<Gu<<GuOOOP<<G}<<G}O'bOpO1G.qO'bOpO1G.qO(hO#tO'#CnO(vO&jO'#CnOOOO1G.q1G.qO)UOpO7+$aOOOP7+$a7+$aOOOP<<HQ<<HQOOOPAN=aAN=aOOOPAN=iAN=iO'bOpO7+$]OOOO7+$]7+$]OOOO'#Cz'#CzO)^O#tO,59YOOOO,59Y,59YOOOO'#C{'#C{O)lO&jO,59YOOOP<<G{<<G{OOOO<<Gw<<GwOOOO-E6x-E6xOOOO1G.t1G.tOOOO-E6y-E6y",
43834
+ stateData: ")z~OPQOSVOTWOVWOWWOXWOiXOyPO!QTO!SUO~OvZOx]O~O^`Oz^O~OPQOQcOSVOTWOVWOWWOXWOyPO!QTO!SUO~ORdO~P!SOteO!PgO~OuhO!RjO~O^lOz^O~OvZOxoO~O^qOz^O~O[vO`sOdwOz^O~ORyO~P!SO^{Oz^O~OteO!P}O~OuhO!R!PO~O^!QOz^O~O[!SOz^O~O[!VO`sOd!WOz^O~Oa!YOz^O~Oz^O[mX`mXdmX~O[!VO`sOd!WO~O^!]Oz^O~O[!_Oz^O~O[!aOz^O~O[!cO`sOd!dOz^O~O[!cO`sOd!dO~Oa!eOz^O~Oz^O{!gO}!hO~Oz^O[ma`madma~O[!kOz^O~O[!lOz^O~O[!mO`sOd!nO~OW!qOX!qO{!sO|!qO~OW!tOX!tO}!sO!O!tO~O[!vOz^O~OW!qOX!qO{!yO|!qO~OW!tOX!tO}!yO!O!tO~O",
43835
+ goto: "%cxPPPPPPPPPPyyP!PP!VPP!`!jP!pyyyP!v!|#S$[$k$q$w$}%TPPPP%ZXWORYbXRORYb_t`qru!T!U!bQ!i!YS!p!e!fR!w!oQdRRybXSORYbQYORmYQ[PRn[Q_QQkVjp_krz!R!T!X!Z!^!`!f!j!oQr`QzcQ!RlQ!TqQ!XsQ!ZtQ!^{Q!`!QQ!f!YQ!j!]R!o!eQu`S!UqrU![u!U!bR!b!TQ!r!gR!x!rQ!u!hR!z!uQbRRxbQfTR|fQiUR!OiSXOYTaRb",
43836
+ nodeNames: "⚠ StartTag StartCloseTag MissingCloseTag StartCloseTag StartCloseTag Document Text EntityReference CharacterReference Cdata Element EndTag OpenTag TagName Attribute AttributeName Is AttributeValue CloseTag SelfCloseEndTag SelfClosingTag Comment ProcessingInst MismatchedCloseTag DoctypeDecl",
43837
+ maxTerm: 50,
43838
+ context: elementContext,
43839
+ nodeProps: [
43840
+ ["closedBy", 1, "SelfCloseEndTag EndTag", 13, "CloseTag MissingCloseTag"],
43841
+ ["openedBy", 12, "StartTag StartCloseTag", 19, "OpenTag", 20, "StartTag"],
43842
+ ["isolate", -6, 13, 18, 19, 21, 22, 24, ""]
43843
+ ],
43844
+ propSources: [xmlHighlighting],
43845
+ skippedNodes: [0],
43846
+ repeatNodeCount: 9,
43847
+ tokenData: "!)v~R!YOX$qXY)iYZ)iZ]$q]^)i^p$qpq)iqr$qrs*vsv$qvw+fwx/ix}$q}!O0[!O!P$q!P!Q2z!Q![$q![!]4n!]!^$q!^!_8U!_!`!#t!`!a!$l!a!b!%d!b!c$q!c!}4n!}#P$q#P#Q!'W#Q#R$q#R#S4n#S#T$q#T#o4n#o%W$q%W%o4n%o%p$q%p&a4n&a&b$q&b1p4n1p4U$q4U4d4n4d4e$q4e$IS4n$IS$I`$q$I`$Ib4n$Ib$Kh$q$Kh%#t4n%#t&/x$q&/x&Et4n&Et&FV$q&FV;'S4n;'S;:j8O;:j;=`)c<%l?&r$q?&r?Ah4n?Ah?BY$q?BY?Mn4n?MnO$qi$zXVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qa%nVVP!O`Ov%gwx&Tx!^%g!^!_&o!_;'S%g;'S;=`'W<%lO%gP&YTVPOv&Tw!^&T!_;'S&T;'S;=`&i<%lO&TP&lP;=`<%l&T`&tS!O`Ov&ox;'S&o;'S;=`'Q<%lO&o`'TP;=`<%l&oa'ZP;=`<%l%gX'eWVP|WOr'^rs&Tsv'^w!^'^!^!_'}!_;'S'^;'S;=`(i<%lO'^W(ST|WOr'}sv'}w;'S'};'S;=`(c<%lO'}W(fP;=`<%l'}X(lP;=`<%l'^h(vV|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oh)`P;=`<%l(oi)fP;=`<%l$qo)t`VP|W!O`zUOX$qXY)iYZ)iZ]$q]^)i^p$qpq)iqr$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk+PV{YVP!O`Ov%gwx&Tx!^%g!^!_&o!_;'S%g;'S;=`'W<%lO%g~+iast,n![!]-r!c!}-r#R#S-r#T#o-r%W%o-r%p&a-r&b1p-r4U4d-r4e$IS-r$I`$Ib-r$Kh%#t-r&/x&Et-r&FV;'S-r;'S;:j/c?&r?Ah-r?BY?Mn-r~,qQ!Q![,w#l#m-V~,zQ!Q![,w!]!^-Q~-VOX~~-YR!Q![-c!c!i-c#T#Z-c~-fS!Q![-c!]!^-Q!c!i-c#T#Z-c~-ug}!O-r!O!P-r!Q![-r![!]-r!]!^/^!c!}-r#R#S-r#T#o-r$}%O-r%W%o-r%p&a-r&b1p-r1p4U-r4U4d-r4e$IS-r$I`$Ib-r$Je$Jg-r$Kh%#t-r&/x&Et-r&FV;'S-r;'S;:j/c?&r?Ah-r?BY?Mn-r~/cOW~~/fP;=`<%l-rk/rW}bVP|WOr'^rs&Tsv'^w!^'^!^!_'}!_;'S'^;'S;=`(i<%lO'^k0eZVP|W!O`Or$qrs%gsv$qwx'^x}$q}!O1W!O!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk1aZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a2S!a;'S$q;'S;=`)c<%lO$qk2_X!PQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qm3TZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a3v!a;'S$q;'S;=`)c<%lO$qm4RXdSVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qo4{!P`S^QVP|W!O`Or$qrs%gsv$qwx'^x}$q}!O4n!O!P4n!P!Q$q!Q![4n![!]4n!]!^$q!^!_(o!_!c$q!c!}4n!}#R$q#R#S4n#S#T$q#T#o4n#o$}$q$}%O4n%O%W$q%W%o4n%o%p$q%p&a4n&a&b$q&b1p4n1p4U4n4U4d4n4d4e$q4e$IS4n$IS$I`$q$I`$Ib4n$Ib$Je$q$Je$Jg4n$Jg$Kh$q$Kh%#t4n%#t&/x$q&/x&Et4n&Et&FV$q&FV;'S4n;'S;:j8O;:j;=`)c<%l?&r$q?&r?Ah4n?Ah?BY$q?BY?Mn4n?MnO$qo8RP;=`<%l4ni8]Y|W!O`Oq(oqr8{rs&osv(owx'}x!a(o!a!b!#U!b;'S(o;'S;=`)]<%lO(oi9S_|W!O`Or(ors&osv(owx'}x}(o}!O:R!O!f(o!f!g;e!g!}(o!}#ODh#O#W(o#W#XLp#X;'S(o;'S;=`)]<%lO(oi:YX|W!O`Or(ors&osv(owx'}x}(o}!O:u!O;'S(o;'S;=`)]<%lO(oi;OV!QP|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oi;lX|W!O`Or(ors&osv(owx'}x!q(o!q!r<X!r;'S(o;'S;=`)]<%lO(oi<`X|W!O`Or(ors&osv(owx'}x!e(o!e!f<{!f;'S(o;'S;=`)]<%lO(oi=SX|W!O`Or(ors&osv(owx'}x!v(o!v!w=o!w;'S(o;'S;=`)]<%lO(oi=vX|W!O`Or(ors&osv(owx'}x!{(o!{!|>c!|;'S(o;'S;=`)]<%lO(oi>jX|W!O`Or(ors&osv(owx'}x!r(o!r!s?V!s;'S(o;'S;=`)]<%lO(oi?^X|W!O`Or(ors&osv(owx'}x!g(o!g!h?y!h;'S(o;'S;=`)]<%lO(oi@QY|W!O`Or?yrs@psv?yvwA[wxBdx!`?y!`!aCr!a;'S?y;'S;=`Db<%lO?ya@uV!O`Ov@pvxA[x!`@p!`!aAy!a;'S@p;'S;=`B^<%lO@pPA_TO!`A[!`!aAn!a;'SA[;'S;=`As<%lOA[PAsOiPPAvP;=`<%lA[aBQSiP!O`Ov&ox;'S&o;'S;=`'Q<%lO&oaBaP;=`<%l@pXBiX|WOrBdrsA[svBdvwA[w!`Bd!`!aCU!a;'SBd;'S;=`Cl<%lOBdXC]TiP|WOr'}sv'}w;'S'};'S;=`(c<%lO'}XCoP;=`<%lBdiC{ViP|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oiDeP;=`<%l?yiDoZ|W!O`Or(ors&osv(owx'}x!e(o!e!fEb!f#V(o#V#WIr#W;'S(o;'S;=`)]<%lO(oiEiX|W!O`Or(ors&osv(owx'}x!f(o!f!gFU!g;'S(o;'S;=`)]<%lO(oiF]X|W!O`Or(ors&osv(owx'}x!c(o!c!dFx!d;'S(o;'S;=`)]<%lO(oiGPX|W!O`Or(ors&osv(owx'}x!v(o!v!wGl!w;'S(o;'S;=`)]<%lO(oiGsX|W!O`Or(ors&osv(owx'}x!c(o!c!dH`!d;'S(o;'S;=`)]<%lO(oiHgX|W!O`Or(ors&osv(owx'}x!}(o!}#OIS#O;'S(o;'S;=`)]<%lO(oiI]V|W!O`yPOr(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oiIyX|W!O`Or(ors&osv(owx'}x#W(o#W#XJf#X;'S(o;'S;=`)]<%lO(oiJmX|W!O`Or(ors&osv(owx'}x#T(o#T#UKY#U;'S(o;'S;=`)]<%lO(oiKaX|W!O`Or(ors&osv(owx'}x#h(o#h#iK|#i;'S(o;'S;=`)]<%lO(oiLTX|W!O`Or(ors&osv(owx'}x#T(o#T#UH`#U;'S(o;'S;=`)]<%lO(oiLwX|W!O`Or(ors&osv(owx'}x#c(o#c#dMd#d;'S(o;'S;=`)]<%lO(oiMkX|W!O`Or(ors&osv(owx'}x#V(o#V#WNW#W;'S(o;'S;=`)]<%lO(oiN_X|W!O`Or(ors&osv(owx'}x#h(o#h#iNz#i;'S(o;'S;=`)]<%lO(oi! RX|W!O`Or(ors&osv(owx'}x#m(o#m#n! n#n;'S(o;'S;=`)]<%lO(oi! uX|W!O`Or(ors&osv(owx'}x#d(o#d#e!!b#e;'S(o;'S;=`)]<%lO(oi!!iX|W!O`Or(ors&osv(owx'}x#X(o#X#Y?y#Y;'S(o;'S;=`)]<%lO(oi!#_V!SP|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(ok!$PXaQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qo!$wX[UVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk!%mZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a!&`!a;'S$q;'S;=`)c<%lO$qk!&kX!RQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk!'aZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_#P$q#P#Q!(S#Q;'S$q;'S;=`)c<%lO$qk!(]ZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a!)O!a;'S$q;'S;=`)c<%lO$qk!)ZXxQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$q",
43848
+ tokenizers: [startTag, commentContent, piContent, cdataContent, 0, 1, 2, 3, 4],
43849
+ topRules: { "Document": [0, 6] },
43850
+ tokenPrec: 0
43851
+ });
43852
+ function tagName(doc2, tag) {
43853
+ let name2 = tag && tag.getChild("TagName");
43854
+ return name2 ? doc2.sliceString(name2.from, name2.to) : "";
43855
+ }
43856
+ function elementName$1(doc2, tree) {
43857
+ let tag = tree && tree.firstChild;
43858
+ return !tag || tag.name != "OpenTag" ? "" : tagName(doc2, tag);
43859
+ }
43860
+ function attrName(doc2, tag, pos) {
43861
+ let attr = tag && tag.getChildren("Attribute").find((a) => a.from <= pos && a.to >= pos);
43862
+ let name2 = attr && attr.getChild("AttributeName");
43863
+ return name2 ? doc2.sliceString(name2.from, name2.to) : "";
43864
+ }
43865
+ function findParentElement(tree) {
43866
+ for (let cur2 = tree && tree.parent; cur2; cur2 = cur2.parent)
43867
+ if (cur2.name == "Element")
43868
+ return cur2;
43869
+ return null;
43870
+ }
43871
+ function findLocation(state, pos) {
43872
+ var _a2;
43873
+ let at = syntaxTree(state).resolveInner(pos, -1), inTag = null;
43874
+ for (let cur2 = at; !inTag && cur2.parent; cur2 = cur2.parent)
43875
+ if (cur2.name == "OpenTag" || cur2.name == "CloseTag" || cur2.name == "SelfClosingTag" || cur2.name == "MismatchedCloseTag")
43876
+ inTag = cur2;
43877
+ if (inTag && (inTag.to > pos || inTag.lastChild.type.isError)) {
43878
+ let elt = inTag.parent;
43879
+ if (at.name == "TagName")
43880
+ return inTag.name == "CloseTag" || inTag.name == "MismatchedCloseTag" ? { type: "closeTag", from: at.from, context: elt } : { type: "openTag", from: at.from, context: findParentElement(elt) };
43881
+ if (at.name == "AttributeName")
43882
+ return { type: "attrName", from: at.from, context: inTag };
43883
+ if (at.name == "AttributeValue")
43884
+ return { type: "attrValue", from: at.from, context: inTag };
43885
+ let before = at == inTag || at.name == "Attribute" ? at.childBefore(pos) : at;
43886
+ if ((before === null || before === void 0 ? void 0 : before.name) == "StartTag")
43887
+ return { type: "openTag", from: pos, context: findParentElement(elt) };
43888
+ if ((before === null || before === void 0 ? void 0 : before.name) == "StartCloseTag" && before.to <= pos)
43889
+ return { type: "closeTag", from: pos, context: elt };
43890
+ if ((before === null || before === void 0 ? void 0 : before.name) == "Is")
43891
+ return { type: "attrValue", from: pos, context: inTag };
43892
+ if (before)
43893
+ return { type: "attrName", from: pos, context: inTag };
43894
+ return null;
43895
+ } else if (at.name == "StartCloseTag") {
43896
+ return { type: "closeTag", from: pos, context: at.parent };
43897
+ }
43898
+ while (at.parent && at.to == pos && !((_a2 = at.lastChild) === null || _a2 === void 0 ? void 0 : _a2.type.isError))
43899
+ at = at.parent;
43900
+ if (at.name == "Element" || at.name == "Text" || at.name == "Document")
43901
+ return { type: "tag", from: pos, context: at.name == "Element" ? at : findParentElement(at) };
43902
+ return null;
43903
+ }
43904
+ class Element {
43905
+ constructor(spec, attrs, attrValues) {
43906
+ this.attrs = attrs;
43907
+ this.attrValues = attrValues;
43908
+ this.children = [];
43909
+ this.name = spec.name;
43910
+ this.completion = Object.assign(Object.assign({ type: "type" }, spec.completion || {}), { label: this.name });
43911
+ this.openCompletion = Object.assign(Object.assign({}, this.completion), { label: "<" + this.name });
43912
+ this.closeCompletion = Object.assign(Object.assign({}, this.completion), { label: "</" + this.name + ">", boost: 2 });
43913
+ this.closeNameCompletion = Object.assign(Object.assign({}, this.completion), { label: this.name + ">" });
43914
+ this.text = spec.textContent ? spec.textContent.map((s) => ({ label: s, type: "text" })) : [];
43915
+ }
43916
+ }
43917
+ const Identifier$1 = /^[:\-\.\w\u00b7-\uffff]*$/;
43918
+ function attrCompletion(spec) {
43919
+ return Object.assign(Object.assign({ type: "property" }, spec.completion || {}), { label: spec.name });
43920
+ }
43921
+ function valueCompletion(spec) {
43922
+ return typeof spec == "string" ? { label: `"${spec}"`, type: "constant" } : /^"/.test(spec.label) ? spec : Object.assign(Object.assign({}, spec), { label: `"${spec.label}"` });
43923
+ }
43924
+ function completeFromSchema(eltSpecs, attrSpecs) {
43925
+ let allAttrs = [], globalAttrs = [];
43926
+ let attrValues = /* @__PURE__ */ Object.create(null);
43927
+ for (let s of attrSpecs) {
43928
+ let completion2 = attrCompletion(s);
43929
+ allAttrs.push(completion2);
43930
+ if (s.global)
43931
+ globalAttrs.push(completion2);
43932
+ if (s.values)
43933
+ attrValues[s.name] = s.values.map(valueCompletion);
43934
+ }
43935
+ let allElements = [], topElements = [];
43936
+ let byName = /* @__PURE__ */ Object.create(null);
43937
+ for (let s of eltSpecs) {
43938
+ let attrs = globalAttrs, attrVals = attrValues;
43939
+ if (s.attributes)
43940
+ attrs = attrs.concat(s.attributes.map((s2) => {
43941
+ if (typeof s2 == "string")
43942
+ return allAttrs.find((a) => a.label == s2) || { label: s2, type: "property" };
43943
+ if (s2.values) {
43944
+ if (attrVals == attrValues)
43945
+ attrVals = Object.create(attrVals);
43946
+ attrVals[s2.name] = s2.values.map(valueCompletion);
43947
+ }
43948
+ return attrCompletion(s2);
43949
+ }));
43950
+ let elt = new Element(s, attrs, attrVals);
43951
+ byName[elt.name] = elt;
43952
+ allElements.push(elt);
43953
+ if (s.top)
43954
+ topElements.push(elt);
43955
+ }
43956
+ if (!topElements.length)
43957
+ topElements = allElements;
43958
+ for (let i = 0; i < allElements.length; i++) {
43959
+ let s = eltSpecs[i], elt = allElements[i];
43960
+ if (s.children) {
43961
+ for (let ch of s.children)
43962
+ if (byName[ch])
43963
+ elt.children.push(byName[ch]);
43964
+ } else {
43965
+ elt.children = allElements;
43966
+ }
43967
+ }
43968
+ return (cx) => {
43969
+ var _a2;
43970
+ let { doc: doc2 } = cx.state, loc = findLocation(cx.state, cx.pos);
43971
+ if (!loc || loc.type == "tag" && !cx.explicit)
43972
+ return null;
43973
+ let { type: type2, from, context } = loc;
43974
+ if (type2 == "openTag") {
43975
+ let children = topElements;
43976
+ let parentName = elementName$1(doc2, context);
43977
+ if (parentName) {
43978
+ let parent = byName[parentName];
43979
+ children = (parent === null || parent === void 0 ? void 0 : parent.children) || allElements;
43980
+ }
43981
+ return {
43982
+ from,
43983
+ options: children.map((ch) => ch.completion),
43984
+ validFor: Identifier$1
43985
+ };
43986
+ } else if (type2 == "closeTag") {
43987
+ let parentName = elementName$1(doc2, context);
43988
+ return parentName ? {
43989
+ from,
43990
+ to: cx.pos + (doc2.sliceString(cx.pos, cx.pos + 1) == ">" ? 1 : 0),
43991
+ options: [((_a2 = byName[parentName]) === null || _a2 === void 0 ? void 0 : _a2.closeNameCompletion) || { label: parentName + ">", type: "type" }],
43992
+ validFor: Identifier$1
43993
+ } : null;
43994
+ } else if (type2 == "attrName") {
43995
+ let parent = byName[tagName(doc2, context)];
43996
+ return {
43997
+ from,
43998
+ options: (parent === null || parent === void 0 ? void 0 : parent.attrs) || globalAttrs,
43999
+ validFor: Identifier$1
44000
+ };
44001
+ } else if (type2 == "attrValue") {
44002
+ let attr = attrName(doc2, context, from);
44003
+ if (!attr)
44004
+ return null;
44005
+ let parent = byName[tagName(doc2, context)];
44006
+ let values = ((parent === null || parent === void 0 ? void 0 : parent.attrValues) || attrValues)[attr];
44007
+ if (!values || !values.length)
44008
+ return null;
44009
+ return {
44010
+ from,
44011
+ to: cx.pos + (doc2.sliceString(cx.pos, cx.pos + 1) == '"' ? 1 : 0),
44012
+ options: values,
44013
+ validFor: /^"[^"]*"?$/
44014
+ };
44015
+ } else if (type2 == "tag") {
44016
+ let parentName = elementName$1(doc2, context), parent = byName[parentName];
44017
+ let closing2 = [], last = context && context.lastChild;
44018
+ if (parentName && (!last || last.name != "CloseTag" || tagName(doc2, last) != parentName))
44019
+ closing2.push(parent ? parent.closeCompletion : { label: "</" + parentName + ">", type: "type", boost: 2 });
44020
+ let options = closing2.concat(((parent === null || parent === void 0 ? void 0 : parent.children) || (context ? allElements : topElements)).map((e) => e.openCompletion));
44021
+ if (context && (parent === null || parent === void 0 ? void 0 : parent.text.length)) {
44022
+ let openTag = context.firstChild;
44023
+ if (openTag.to > cx.pos - 20 && !/\S/.test(cx.state.sliceDoc(openTag.to, cx.pos)))
44024
+ options = options.concat(parent.text);
44025
+ }
44026
+ return {
44027
+ from,
44028
+ options,
44029
+ validFor: /^<\/?[:\-\.\w\u00b7-\uffff]*$/
44030
+ };
44031
+ } else {
44032
+ return null;
44033
+ }
44034
+ };
44035
+ }
44036
+ const xmlLanguage = /* @__PURE__ */ LRLanguage.define({
44037
+ name: "xml",
44038
+ parser: /* @__PURE__ */ parser$2.configure({
44039
+ props: [
44040
+ /* @__PURE__ */ indentNodeProp.add({
44041
+ Element(context) {
44042
+ let closed = /^\s*<\//.test(context.textAfter);
44043
+ return context.lineIndent(context.node.from) + (closed ? 0 : context.unit);
44044
+ },
44045
+ "OpenTag CloseTag SelfClosingTag"(context) {
44046
+ return context.column(context.node.from) + context.unit;
44047
+ }
44048
+ }),
44049
+ /* @__PURE__ */ foldNodeProp.add({
44050
+ Element(subtree) {
44051
+ let first = subtree.firstChild, last = subtree.lastChild;
44052
+ if (!first || first.name != "OpenTag")
44053
+ return null;
44054
+ return { from: first.to, to: last.name == "CloseTag" ? last.from : subtree.to };
44055
+ }
44056
+ }),
44057
+ /* @__PURE__ */ bracketMatchingHandle.add({
44058
+ "OpenTag CloseTag": (node) => node.getChild("TagName")
44059
+ })
44060
+ ]
44061
+ }),
44062
+ languageData: {
44063
+ commentTokens: { block: { open: "<!--", close: "-->" } },
44064
+ indentOnInput: /^\s*<\/$/
44065
+ }
44066
+ });
44067
+ function xml(conf = {}) {
44068
+ let support = [xmlLanguage.data.of({
44069
+ autocomplete: completeFromSchema(conf.elements || [], conf.attributes || [])
44070
+ })];
44071
+ if (conf.autoCloseTags !== false)
44072
+ support.push(autoCloseTags$1);
44073
+ return new LanguageSupport(xmlLanguage, support);
44074
+ }
44075
+ function elementName$2(doc2, tree, max = doc2.length) {
44076
+ if (!tree)
44077
+ return "";
44078
+ let tag = tree.firstChild;
44079
+ let name2 = tag && tag.getChild("TagName");
44080
+ return name2 ? doc2.sliceString(name2.from, Math.min(name2.to, max)) : "";
44081
+ }
44082
+ const autoCloseTags$1 = /* @__PURE__ */ EditorView.inputHandler.of((view, from, to, text, insertTransaction) => {
44083
+ if (view.composing || view.state.readOnly || from != to || text != ">" && text != "/" || !xmlLanguage.isActiveAt(view.state, from, -1))
44084
+ return false;
44085
+ let base2 = insertTransaction(), { state } = base2;
44086
+ let closeTags = state.changeByRange((range) => {
44087
+ var _a2, _b, _c;
44088
+ let { head } = range;
44089
+ let didType = state.doc.sliceString(head - 1, head) == text;
44090
+ let after = syntaxTree(state).resolveInner(head, -1), name2;
44091
+ if (didType && text == ">" && after.name == "EndTag") {
44092
+ let tag = after.parent;
44093
+ if (((_b = (_a2 = tag.parent) === null || _a2 === void 0 ? void 0 : _a2.lastChild) === null || _b === void 0 ? void 0 : _b.name) != "CloseTag" && (name2 = elementName$2(state.doc, tag.parent, head))) {
44094
+ let to2 = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
44095
+ let insert2 = `</${name2}>`;
44096
+ return { range, changes: { from: head, to: to2, insert: insert2 } };
44097
+ }
44098
+ } else if (didType && text == "/" && after.name == "StartCloseTag") {
44099
+ let base3 = after.parent;
44100
+ if (after.from == head - 2 && ((_c = base3.lastChild) === null || _c === void 0 ? void 0 : _c.name) != "CloseTag" && (name2 = elementName$2(state.doc, base3, head))) {
44101
+ let to2 = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
44102
+ let insert2 = `${name2}>`;
44103
+ return {
44104
+ range: EditorSelection.cursor(head + insert2.length, -1),
44105
+ changes: { from: head, to: to2, insert: insert2 }
44106
+ };
44107
+ }
44108
+ }
44109
+ return { range };
44110
+ });
44111
+ if (closeTags.changes.empty)
44112
+ return false;
44113
+ view.dispatch([
44114
+ base2,
44115
+ state.update(closeTags, {
44116
+ userEvent: "input.complete",
44117
+ scrollIntoView: true
44118
+ })
44119
+ ]);
44120
+ return true;
44121
+ });
44122
+ const commentKeymap = keymap.of([
44123
+ { key: "Mod-/", run: toggleComment, preventDefault: true }
44124
+ ]);
44125
+ function jsonWithComments() {
44126
+ const lang = json();
44127
+ return [lang, lang.language.data.of({ commentTokens: { line: "//" } }), commentKeymap];
44128
+ }
44129
+ function xmlWithComments() {
44130
+ return [xml(), commentKeymap];
44131
+ }
42954
44132
  function devAssert(condition, message) {
42955
44133
  const booleanCondition = Boolean(condition);
42956
44134
  if (!booleanCondition) {
@@ -54542,7 +55720,7 @@ const stateExtensions = (schema, opts) => [
54542
55720
  optionsStateField.init(() => Object.assign(Object.assign({}, defaultOpts), opts))
54543
55721
  ];
54544
55722
  const spec_name = { __proto__: null, query: 241, mutation: 243, subscription: 245, on: 86, fragment: 92, schema: 102, scalar: 114, type: 120, implements: 126, interface: 140, union: 146, enum: 154, input: 164, directive: 172, repeatable: 176, extend: 192 };
54545
- const parser$2 = /* @__PURE__ */ LRParser.deserialize({
55723
+ const parser$1 = /* @__PURE__ */ LRParser.deserialize({
54546
55724
  version: 14,
54547
55725
  states: "LWOYQPOOO!WQPO'#C}O!`QPO'#C_OOQO'#C_'#C_O!iQPO'#DYOOQO'#Ev'#EvOOQO'#D_'#D_O!nQPO'#D^O#_QPO'#D^O!iQPO'#DhO!iQPO'#DrO!iQPO'#DuO!iQPO'#DyO!iQPO'#EOOOQO'#Dd'#DdO#fQPO'#ESOOQO'#D]'#D]O!vQPO'#E^OOQO'#Ea'#EaOOQO'#E]'#E]OOQO'#FT'#FTOOQO'#Eu'#EuOOQO'#Eh'#EhQYQPOOOOQO'#C`'#C`OOQO'#DZ'#DZOOQO'#D`'#D`OOQO'#Di'#DiOOQO'#Ds'#DsOOQO'#Dv'#DvOOQO'#Dz'#DzOOQO'#EP'#EPOOQO'#ET'#ETOOQO'#E_'#E_O#kQPO'#CaO$VQPO'#DQO$[QPO'#DPO$sQPO'#DPO$xQPO'#DSO%WQPO'#DOOOQO'#En'#EnO%fQPO,59iOOQO'#Ca'#CaO%qQPO'#CxOOQO'#El'#ElO'hQPO'#CwO)[QPO'#CdOOQO,58y,58yO)aQPO,58yO)fQPO,58yO)nQPO,58yOOQO'#DT'#DTO)yQPO,59tO{QPO'#FUOOQO'#Db'#DbOOQO,59x,59xO*OQPO,59xO!nQPO,59xO!iQPO,5:PO!iQPO,5:SO!iQPO,5:^O!iQPO,5:aO!iQPO,5:eO!iQPO,5:jO*TQPO,5:nOOQO'#Df'#DfO*YQPO,5:SO+aQPO,5:^O,hQPO,5:aO-oQPO,5:eO.sQPO,5:jO/wQPO,5:nO!nQPO,5:xO!iQPO,5:|O!iQPO,5:}O!iQPO,5;OO!iQPO,5;PO!iQPO,5;QO!iQPO,5;ROOQO-E8f-E8fOOQO,59l,59lO0VQPO'#CzOOQO,59k,59kO0[QPO,59kO0mQPO,59kOOQO'#DR'#DRO0tQPO,59kO1OQPO,59nO!iQPO'#DVOOQO,59p,59pO)aQPO,59pO)fQPO,59pOOQO'#DW'#DWOOQO,59j,59jOOQO-E8l-E8lOOQO1G/T1G/TOOQO,59d,59dOOQO-E8j-E8jO1aQPO'#CeOOQO'#Ei'#EiO1fQPO,59OOOQO1G.e1G.eO)aQPO1G.eO)fQPO1G.eO)fQPO1G/`O1nQPO'#DcO1sQPO,5;pOOQO1G/d1G/dO*OQPO1G/dO1xQPO1G/kO4QQPO1G/nO5]QPO1G/xO6hQPO1G/{O7pQPO1G0PO8xQPO1G0UO/wQPO1G0YO9PQPO1G/nO9WQPO'#DkO9`QPO'#FWOOQO'#Dn'#DnOOQO1G/n1G/nO9hQPO1G/nOOQO'#Dl'#DlO:iQPO1G/xOOQO1G/x1G/xO:pQPO1G/xO;qQPO1G/{O;xQPO'#DxO5dQPO1G/{O<QQQO'#FYOOQO'#D|'#D|OOQO1G0P1G0PO6oQPO1G0PO9`QPO'#FZOOQO'#ER'#EROOQO1G0U1G0UO7wQPO1G0UO<YQPO'#DqO!iQPO'#DqOOQO'#Ep'#EpO<_QPO'#DpO<pQSO1G0YO)yQPO1G0YO>QQPO1G0YOOQO'#EV'#EVOOQO1G0d1G0dO>YQPO1G0dO!qQPO1G0hO?ZQPO1G0iO?ZQPO1G0jO?fQPO1G0kO?nQPO1G0lO?vQPO1G0mOOQO'#C|'#C|O@OQPO'#C{OOQO'#Em'#EmO@TQPO,59fOOQO1G/V1G/VO@]QPO1G/VO@nQPO1G/VOOQO1G/Y1G/YOOQO'#Cg'#CgOOQO,59q,59qOOQO1G/[1G/[O)aQPO1G/[O@uQPO,59POOQO-E8g-E8gOOQO1G.j1G.jOOQO7+$P7+$PO)aQPO7+$POOQO7+$z7+$zO)aQPO7+$zO!iQPO,59}OOQO1G1[1G1[OOQO7+%O7+%OOOQO7+%V7+%VOBOQPO7+%YOOQO7+%Y7+%YO@}QPO7+%YOCZQPO7+%dOOQO7+%d7+%dOBYQPO7+%dODfQPO7+%gODmQPO7+%gOOQO7+%k7+%kODtQPO7+%kOOQO7+%p7+%pOEuQPO7+%pO<pQSO7+%tO)yQPO7+%tO>QQPO7+%tO!iQPO,5:VOOQO,5:V,5:VOFvQPO'#DoO!iQPO'#DoOOQO'#Eo'#EoOGRQPO,5;rO!iQPO,5:dOOQO,5:d,5:dOG^QQO'#D}OGlQQO'#D}OOQO'#Eq'#EqOGqQQO,5;tOG|QPO,5;uO@uQPO,5:]OHXQPO,5:]OOQO-E8n-E8nOOQO'#EZ'#EZOOQO'#E['#E[OOQO'#EY'#EYOH^QPO7+%tOOQO'#EX'#EXO<sQSO'#EXOOQO7+&O7+&OOOQO7+&S7+&SOJcQPO7+&TOOQO7+&T7+&TOIbQPO7+&TOKnQPO7+&UOOQO7+&U7+&UOJmQPO7+&UOLyQPO7+&VOMQQPO7+&VOOQO7+&W7+&WOMXQPO7+&WOOQO7+&X7+&XONYQPO7+&XO! ZQQO,59gOOQO-E8k-E8kOOQO1G/Q1G/QOOQO7+$q7+$qO! xQPO7+$qOOQO7+$v7+$vO@uQPO'#CjO!!ZQPO'#E|OOQO'#E|'#E|O!#UQPO1G.kOOQO<<Gk<<GkOOQO<<Hf<<HfOOQO1G/i1G/iOOQO<<Ht<<HtO!#gQPO<<HtOOQO<<IO<<IOO!$hQPO<<IOO!%iQPO<<IROOQO<<IV<<IVOOQO<<I[<<I[O!&mQPO<<I`O<pQSO<<I`O)yQPO<<I`OOQO1G/q1G/qO@uQPO,5:ZO!'qQPO,5:ZO!'vQPO,5:ZOOQO-E8m-E8mOOQO1G1^1G1^OOQO1G0O1G0OO!(RQQO'#CxO!(dQQO'#CwOOQO,5:i,5:iO!(rQQO,5:iOOQO-E8o-E8oOOQO1G1`1G1`OOQO1G1a1G1aO!)QQPO1G/wO@uQPO1G/wO<sQSO,5:sOOQO,5:s,5:sOOQO<<Io<<IoO!)lQPO<<IoOOQO<<Ip<<IpO!*mQPO<<IpO!+nQPO<<IqOOQO<<Ir<<IrOOQO<<Is<<IsO!,rQQO'#CsO!,yQPO'#CvOOQO'#FP'#FPO!-RQPO1G/ROOQO<<H]<<H]O!-^QPO,59UOOQO,59V,59VO! ZQQO'#ClOOQO7+$V7+$VO!-cQPO7+$VO!-nQPO7+$VOOQOAN>`AN>`OOQOAN>jAN>jO!-|QPOAN>zO<pQSOAN>zO!/QQPO1G/uO@uQPO1G/uO!/`QPO1G/uOOQO1G0T1G0TOOQO7+%c7+%cO!/eQPO7+%cO!/|QPO7+%cOOQO1G0_1G0_OOQOAN?ZAN?ZOOQOAN?[AN?[O!0TQQO'#EjO!0xQQO,59_OOQO,59_,59_O!1PQPO'#FROOQO'#Ek'#EkO!1UQPO,59bOOQO,59b,59bOOQO7+$m7+$mOOQO1G.p1G.pOOQO,59W,59WOOQO<<Gq<<GqO!1^QPO<<GqO!1iQPOG24fOOQO7+%a7+%aO!2mQPO7+%aO@uQPO7+%aOOQO<<H}<<H}O!2{QPO<<H}OOQO,5;U,5;UOOQO-E8h-E8hOOQO1G.y1G.yO! ZQQO,5;mOOQO-E8i-E8iOOQO1G.|1G.|OOQOAN=]AN=]OOQO<<H{<<H{O!3dQPO<<H{OOQOAN>iAN>iO!3rQPO1G1XOOQOAN>gAN>gOOQO7+&s7+&s",
54548
55726
  stateData: "!4S~O#hOSPOS~OcUOiPO!OiO!TjO!^kO!hlO!kmO!onO!toO!xpO#SqO#lhO#mhO#nhO~O#krO#vvO~OV!OOiPOm{O#kzO~Oi!VOm{O~O!TjO!Z!cO!^kO!hlO!kmO!onO!toO~O!xpO~P!vOm!iO~OVuXhuXiuXmuX#kuX#oTX#tuX#vuX~O#o!rO~OV!sOiPOm{OhsX#ksX#tsX#vsX~O#k!wO~OiPOm{O{#OO#kzO~O#t#POhrX#krX#vrX~Oh#RO#krO#vvO~OV!sOilXmlXhlX#klX#tlX#vlXclX!OlX!TlX!^lX!hlX!klX!olX!tlX!xlX#SlX#flX#llX#mlX#nlX#rlXUlXYlX{lX!zlX#olX~Om{OikXhkX#kkX#tkX#vkXckX!OkX!TkX!^kX!hkX!kkX!okX!tkX!xkX#SkX#fkX#lkX#mkX#nkX#rkXUkXYkX{kX!zkX#okX~OY#UO~OiPO~OiPOm{O~OV!OOiPOm{O~O{#OO~Oi!VO~Om#gO~Oi#jOm{O!a#nOc![a!O![a!T![a!^![a!h![a!k![a!o![a!t![a!x![a#S![a#f![a#l![a#m![a#n![a~Oi#jOm{O!a#nOc!fa!O!fa!T!fa!^!fa!h!fa!k!fa!o!fa!t!fa!x!fa#S!fa#f!fa#l!fa#m!fa#n!fa~Om{O#r#sOc!iai!ia!O!ia!T!ia!^!ia!h!ia!k!ia!o!ia!t!ia!x!ia#S!ia#f!ia#l!ia#m!ia#n!ia~Oi#uOm{Oc!ma!O!ma!T!ma!^!ma!h!ma!k!ma!o!ma!t!ma!x!ma#S!ma#f!ma#l!ma#m!ma#n!ma~Oi#yOm{Oc!ra!O!ra!T!ra!^!ra!h!ra!k!ra!o!ra!t!ra!x!ra#S!ra#f!ra#l!ra#m!ra#n!ra~OcUO{#OO!z$UO#kzO~O#k$_O~OiPOhsa#ksa#tsa#vsa~Om{O~P0[OV!sOm{O~P0[Om{Ohva#kva#tva#vva~O#o$kO~OU$mOY#UO~O#o$rO~Oh$sO~Om{Oc!Xii!Xi!O!Xi!T!Xi!^!Xi!h!Xi!k!Xi!o!Xi!t!Xi!x!Xi#S!Xi#f!Xi#l!Xi#m!Xi#n!Xi~Oi#jOm{Oc![i!O![i!T![i!^![i!h![i!k![i!o![i!t![i!x![i#S![i#f![i#l![i#m![i#n![i~O!a#nO~P2|Oi#jOm{Oc!fi!O!fi!T!fi!^!fi!h!fi!k!fi!o!fi!t!fi!x!fi#S!fi#f!fi#l!fi#m!fi#n!fi~O!a#nO~P4XO#r#sOc!iii!ii!O!ii!T!ii!^!ii!h!ii!k!ii!o!ii!t!ii!x!ii#S!ii#f!ii#l!ii#m!ii#n!ii~Om{O~P5dOi#uOc!mi!O!mi!T!mi!^!mi!h!mi!k!mi!o!mi!t!mi!x!mi#S!mi#f!mi#l!mi#m!mi#n!mi~Om{O~P6oOi#yOc!ri!O!ri!T!ri!^!ri!h!ri!k!ri!o!ri!t!ri!x!ri#S!ri#f!ri#l!ri#m!ri#n!ri~Om{O~P7wO#y%VO~P2|O#kzO#y%VO~OcUO#kzO~Oi#jOc![i!O![i!T![i!^![i!h![i!k![i!o![i!t![i!x![i#S![i#f![i#l![i#m![i#n![i~O#y%VO~P4XOi#jOc!fi!O!fi!T!fi!^!fi!h!fi!k!fi!o!fi!t!fi!x!fi#S!fi#f!fi#l!fi#m!fi#n!fi~O#{%]O~P5gO#kzO#{%]O~OcUOf%_O~O#o%dO~OcUO#kzO{!dX!z!dX#o!dX~O#{%lO$O%gO$P%gO$Q%gO$R%gO$S%gO$T%gO$U%gO$V%gO$W%hO$X%hO$Y%hO$Z%hO$[%hO$]%hO$^%hO$_%hO$`%hO$a%hO$b%hO~O{#OO!z$UO~Oi!VOc#Qi!O#Qi!T#Qi!^#Qi!h#Qi!k#Qi!o#Qi!t#Qi!x#Qi#S#Qi#f#Qi#l#Qi#m#Qi#n#Qi~Oi#jOm{O!a#nO~Om{O#r#sO~Oi#uOm{O~Oi#yOm{O~O#o%{O~OU%}O#k$_O~OiPOhsi#ksi#tsi#vsi~Om{O~P@]O]&RO#kzO~Oi#jOc![q!O![q!T![q!^![q!h![q!k![q!o![q!t![q!x![q#S![q#f![q#l![q#m![q#n![q~Om{O#y%VO~P@}Oi#jOc!fq!O!fq!T!fq!^!fq!h!fq!k!fq!o!fq!t!fq!x!fq#S!fq#f!fq#l!fq#m!fq#n!fq~Om{O#y%VO~PBYOc!iqi!iq!O!iq!T!iq!^!iq!h!iq!k!iq!o!iq!t!iq!x!iq#S!iq#f!iq#l!iq#m!iq#n!iq~O#{%]O~PCeO#r#sO~PCeOi#uOc!mq!O!mq!T!mq!^!mq!h!mq!k!mq!o!mq!t!mq!x!mq#S!mq#f!mq#l!mq#m!mq#n!mq~Oi#yOc!rq!O!rq!T!rq!^!rq!h!rq!k!rq!o!rq!t!rq!x!rq#S!rq#f!rq#l!rq#m!rq#n!rq~OcUO#kzO#o&eO~OcUOh&iO#kzO~Om&kOc!qXf!qXh!qX~Of&nO~OcUOf%_Oh&pO~OcUOh&qO#kzO~O#o&sO~O#{&tOc!vqi!vq!O!vq!T!vq!^!vq!h!vq!k!vq!o!vq!t!vq!x!vq#S!vq#f!vq#l!vq#m!vq#n!vq~Oi#jOc#Vq!O#Vq!T#Vq!^#Vq!h#Vq!k#Vq!o#Vq!t#Vq!x#Vq#S#Vq#f#Vq#l#Vq#m#Vq#n#Vq~Om{O#y%VO~PIbOi#jOc#Wq!O#Wq!T#Wq!^#Wq!h#Wq!k#Wq!o#Wq!t#Wq!x#Wq#S#Wq#f#Wq#l#Wq#m#Wq#n#Wq~Om{O#y%VO~PJmOc#Xqi#Xq!O#Xq!T#Xq!^#Xq!h#Xq!k#Xq!o#Xq!t#Xq!x#Xq#S#Xq#f#Xq#l#Xq#m#Xq#n#Xq~O#{%]O~PKxO#r#sO~PKxOi#uOc#Yq!O#Yq!T#Yq!^#Yq!h#Yq!k#Yq!o#Yq!t#Yq!x#Yq#S#Yq#f#Yq#l#Yq#m#Yq#n#Yq~Oi#yOc#Zq!O#Zq!T#Zq!^#Zq!h#Zq!k#Zq!o#Zq!t#Zq!x#Zq#S#Zq#f#Zq#l#Zq#m#Zq#n#Zq~OY'PO]&}Oa'POb'POc'POd'POe'POf'POi'OO~OiPOhsq#ksq#tsq#vsq~O#q'TOU#pXY#pXm#pX#r#pX#t#pXc#pX{#pX!z#pX#k#pX[#pXh#pX#o#pX~Om{O#r'UO#t'VOUXiYXi~Oi#jOc![y!O![y!T![y!^![y!h![y!k![y!o![y!t![y!x![y#S![y#f![y#l![y#m![y#n![y~Oi#jOc!fy!O!fy!T!fy!^!fy!h!fy!k!fy!o!fy!t!fy!x!fy#S!fy#f!fy#l!fy#m!fy#n!fy~O#{%]Oc!iyi!iy!O!iy!T!iy!^!iy!h!iy!k!iy!o!iy!t!iy!x!iy#S!iy#f!iy#l!iy#m!iy#n!iy~O#{&tOc!vyi!vy!O!vy!T!vy!^!vy!h!vy!k!vy!o!vy!t!vy!x!vy#S!vy#f!vy#l!vy#m!vy#n!vy~O#o'_O~OcUO#kzO#o'_O~OV!sOclXflXhlXmlX~Om&kOckXfkXhkX~Om&kOc!qaf!qah!qa~Om{O#r'UOc!ei{!ei!z!ei#k!eih!ei#o!ei~Oi#jOc#Vy!O#Vy!T#Vy!^#Vy!h#Vy!k#Vy!o#Vy!t#Vy!x#Vy#S#Vy#f#Vy#l#Vy#m#Vy#n#Vy~Oi#jOc#Wy!O#Wy!T#Wy!^#Wy!h#Wy!k#Wy!o#Wy!t#Wy!x#Wy#S#Wy#f#Wy#l#Wy#m#Wy#n#Wy~O#{%]Oc#Xyi#Xy!O#Xy!T#Xy!^#Xy!h#Xy!k#Xy!o#Xy!t#Xy!x#Xy#S#Xy#f#Xy#l#Xy#m#Xy#n#Xy~O['jO~P! ZOh'nO#kzO~O#t'oOUoi#koi~O['pO~O#t'rOUXqYXq~Om{O#t'rOUXqYXq~O#{&tOc!v!Ri!v!R!O!v!R!T!v!R!^!v!R!h!v!R!k!v!R!o!v!R!t!v!R!x!v!R#S!v!R#f!v!R#l!v!R#m!v!R#n!v!R~Om{Oc!cih!ci#k!ci~O#o'wO~Om{Oc!eq{!eq!z!eq#k!eqh!eq#o!eq~O#r'UO~P!/eO#t'zOY#^X[#^X]#^Xa#^Xb#^Xc#^Xd#^Xe#^Xf#^Xi#^X~O['|O~P! ZO#o'}O~Oh(PO#kzO~O#t(QOUXyYXy~O#{&tOc!v!Zi!v!Z!O!v!Z!T!v!Z!^!v!Z!h!v!Z!k!v!Z!o!v!Z!t!v!Z!x!v!Z#S!v!Z#f!v!Z#l!v!Z#m!v!Z#n!v!Z~Om{Oc!cqh!cq#k!cq~Om{Oc!ey{!ey!z!ey#k!eyh!ey#o!ey~Om{Oc!cyh!cy#k!cy~O#t(WOh#ui#k#ui~Odefe~",
@@ -54565,7 +55743,7 @@ const nodesWithBraces = "RootTypeDefinition InputFieldsDefinition EnumValuesDefi
54565
55743
  const keywords$1 = "scalar type interface union enum input implements fragment extend schema directive on repeatable";
54566
55744
  const punctuations = "( ) { } : [ ]";
54567
55745
  const graphqlLanguage = /* @__PURE__ */ LRLanguage.define({
54568
- parser: /* @__PURE__ */ parser$2.configure({
55746
+ parser: /* @__PURE__ */ parser$1.configure({
54569
55747
  props: [
54570
55748
  /* @__PURE__ */ styleTags({
54571
55749
  Variable: tags.variableName,
@@ -55259,383 +56437,6 @@ function GraphQLEditor({ request, onChange }) {
55259
56437
  ] })
55260
56438
  ] });
55261
56439
  }
55262
- const StartTag = 1, StartCloseTag = 2, MissingCloseTag = 3, mismatchedStartCloseTag = 4, incompleteStartCloseTag = 5, commentContent$1 = 36, piContent$1 = 37, cdataContent$1 = 38, Element$1 = 11, OpenTag = 13;
55263
- function nameChar(ch) {
55264
- return ch == 45 || ch == 46 || ch == 58 || ch >= 65 && ch <= 90 || ch == 95 || ch >= 97 && ch <= 122 || ch >= 161;
55265
- }
55266
- function isSpace(ch) {
55267
- return ch == 9 || ch == 10 || ch == 13 || ch == 32;
55268
- }
55269
- let cachedName = null, cachedInput = null, cachedPos = 0;
55270
- function tagNameAfter(input, offset) {
55271
- let pos = input.pos + offset;
55272
- if (cachedInput == input && cachedPos == pos) return cachedName;
55273
- while (isSpace(input.peek(offset))) offset++;
55274
- let name2 = "";
55275
- for (; ; ) {
55276
- let next = input.peek(offset);
55277
- if (!nameChar(next)) break;
55278
- name2 += String.fromCharCode(next);
55279
- offset++;
55280
- }
55281
- cachedInput = input;
55282
- cachedPos = pos;
55283
- return cachedName = name2 || null;
55284
- }
55285
- function ElementContext(name2, parent) {
55286
- this.name = name2;
55287
- this.parent = parent;
55288
- }
55289
- const elementContext = new ContextTracker({
55290
- start: null,
55291
- shift(context, term, stack, input) {
55292
- return term == StartTag ? new ElementContext(tagNameAfter(input, 1) || "", context) : context;
55293
- },
55294
- reduce(context, term) {
55295
- return term == Element$1 && context ? context.parent : context;
55296
- },
55297
- reuse(context, node, _stack, input) {
55298
- let type2 = node.type.id;
55299
- return type2 == StartTag || type2 == OpenTag ? new ElementContext(tagNameAfter(input, 1) || "", context) : context;
55300
- },
55301
- strict: false
55302
- });
55303
- const startTag = new ExternalTokenizer((input, stack) => {
55304
- if (input.next != 60) return;
55305
- input.advance();
55306
- if (input.next == 47) {
55307
- input.advance();
55308
- let name2 = tagNameAfter(input, 0);
55309
- if (!name2) return input.acceptToken(incompleteStartCloseTag);
55310
- if (stack.context && name2 == stack.context.name) return input.acceptToken(StartCloseTag);
55311
- for (let cx = stack.context; cx; cx = cx.parent) if (cx.name == name2) return input.acceptToken(MissingCloseTag, -2);
55312
- input.acceptToken(mismatchedStartCloseTag);
55313
- } else if (input.next != 33 && input.next != 63) {
55314
- return input.acceptToken(StartTag);
55315
- }
55316
- }, { contextual: true });
55317
- function scanTo(type2, end) {
55318
- return new ExternalTokenizer((input) => {
55319
- let len = 0, first = end.charCodeAt(0);
55320
- scan: for (; ; input.advance(), len++) {
55321
- if (input.next < 0) break;
55322
- if (input.next == first) {
55323
- for (let i = 1; i < end.length; i++)
55324
- if (input.peek(i) != end.charCodeAt(i)) continue scan;
55325
- break;
55326
- }
55327
- }
55328
- if (len) input.acceptToken(type2);
55329
- });
55330
- }
55331
- const commentContent = scanTo(commentContent$1, "-->");
55332
- const piContent = scanTo(piContent$1, "?>");
55333
- const cdataContent = scanTo(cdataContent$1, "]]>");
55334
- const xmlHighlighting = styleTags({
55335
- Text: tags.content,
55336
- "StartTag StartCloseTag EndTag SelfCloseEndTag": tags.angleBracket,
55337
- TagName: tags.tagName,
55338
- "MismatchedCloseTag/TagName": [tags.tagName, tags.invalid],
55339
- AttributeName: tags.attributeName,
55340
- AttributeValue: tags.attributeValue,
55341
- Is: tags.definitionOperator,
55342
- "EntityReference CharacterReference": tags.character,
55343
- Comment: tags.blockComment,
55344
- ProcessingInst: tags.processingInstruction,
55345
- DoctypeDecl: tags.documentMeta,
55346
- Cdata: tags.special(tags.string)
55347
- });
55348
- const parser$1 = LRParser.deserialize({
55349
- version: 14,
55350
- states: ",lOQOaOOOrOxO'#CfOzOpO'#CiO!tOaO'#CgOOOP'#Cg'#CgO!{OrO'#CrO#TOtO'#CsO#]OpO'#CtOOOP'#DT'#DTOOOP'#Cv'#CvQQOaOOOOOW'#Cw'#CwO#eOxO,59QOOOP,59Q,59QOOOO'#Cx'#CxO#mOpO,59TO#uO!bO,59TOOOP'#C|'#C|O$TOaO,59RO$[OpO'#CoOOOP,59R,59ROOOQ'#C}'#C}O$dOrO,59^OOOP,59^,59^OOOS'#DO'#DOO$lOtO,59_OOOP,59_,59_O$tOpO,59`O$|OpO,59`OOOP-E6t-E6tOOOW-E6u-E6uOOOP1G.l1G.lOOOO-E6v-E6vO%UO!bO1G.oO%UO!bO1G.oO%dOpO'#CkO%lO!bO'#CyO%zO!bO1G.oOOOP1G.o1G.oOOOP1G.w1G.wOOOP-E6z-E6zOOOP1G.m1G.mO&VOpO,59ZO&_OpO,59ZOOOQ-E6{-E6{OOOP1G.x1G.xOOOS-E6|-E6|OOOP1G.y1G.yO&gOpO1G.zO&gOpO1G.zOOOP1G.z1G.zO&oO!bO7+$ZO&}O!bO7+$ZOOOP7+$Z7+$ZOOOP7+$c7+$cO'YOpO,59VO'bOpO,59VO'mO!bO,59eOOOO-E6w-E6wO'{OpO1G.uO'{OpO1G.uOOOP1G.u1G.uO(TOpO7+$fOOOP7+$f7+$fO(]O!bO<<GuOOOP<<Gu<<GuOOOP<<G}<<G}O'bOpO1G.qO'bOpO1G.qO(hO#tO'#CnO(vO&jO'#CnOOOO1G.q1G.qO)UOpO7+$aOOOP7+$a7+$aOOOP<<HQ<<HQOOOPAN=aAN=aOOOPAN=iAN=iO'bOpO7+$]OOOO7+$]7+$]OOOO'#Cz'#CzO)^O#tO,59YOOOO,59Y,59YOOOO'#C{'#C{O)lO&jO,59YOOOP<<G{<<G{OOOO<<Gw<<GwOOOO-E6x-E6xOOOO1G.t1G.tOOOO-E6y-E6y",
55351
- stateData: ")z~OPQOSVOTWOVWOWWOXWOiXOyPO!QTO!SUO~OvZOx]O~O^`Oz^O~OPQOQcOSVOTWOVWOWWOXWOyPO!QTO!SUO~ORdO~P!SOteO!PgO~OuhO!RjO~O^lOz^O~OvZOxoO~O^qOz^O~O[vO`sOdwOz^O~ORyO~P!SO^{Oz^O~OteO!P}O~OuhO!R!PO~O^!QOz^O~O[!SOz^O~O[!VO`sOd!WOz^O~Oa!YOz^O~Oz^O[mX`mXdmX~O[!VO`sOd!WO~O^!]Oz^O~O[!_Oz^O~O[!aOz^O~O[!cO`sOd!dOz^O~O[!cO`sOd!dO~Oa!eOz^O~Oz^O{!gO}!hO~Oz^O[ma`madma~O[!kOz^O~O[!lOz^O~O[!mO`sOd!nO~OW!qOX!qO{!sO|!qO~OW!tOX!tO}!sO!O!tO~O[!vOz^O~OW!qOX!qO{!yO|!qO~OW!tOX!tO}!yO!O!tO~O",
55352
- goto: "%cxPPPPPPPPPPyyP!PP!VPP!`!jP!pyyyP!v!|#S$[$k$q$w$}%TPPPP%ZXWORYbXRORYb_t`qru!T!U!bQ!i!YS!p!e!fR!w!oQdRRybXSORYbQYORmYQ[PRn[Q_QQkVjp_krz!R!T!X!Z!^!`!f!j!oQr`QzcQ!RlQ!TqQ!XsQ!ZtQ!^{Q!`!QQ!f!YQ!j!]R!o!eQu`S!UqrU![u!U!bR!b!TQ!r!gR!x!rQ!u!hR!z!uQbRRxbQfTR|fQiUR!OiSXOYTaRb",
55353
- nodeNames: "⚠ StartTag StartCloseTag MissingCloseTag StartCloseTag StartCloseTag Document Text EntityReference CharacterReference Cdata Element EndTag OpenTag TagName Attribute AttributeName Is AttributeValue CloseTag SelfCloseEndTag SelfClosingTag Comment ProcessingInst MismatchedCloseTag DoctypeDecl",
55354
- maxTerm: 50,
55355
- context: elementContext,
55356
- nodeProps: [
55357
- ["closedBy", 1, "SelfCloseEndTag EndTag", 13, "CloseTag MissingCloseTag"],
55358
- ["openedBy", 12, "StartTag StartCloseTag", 19, "OpenTag", 20, "StartTag"],
55359
- ["isolate", -6, 13, 18, 19, 21, 22, 24, ""]
55360
- ],
55361
- propSources: [xmlHighlighting],
55362
- skippedNodes: [0],
55363
- repeatNodeCount: 9,
55364
- tokenData: "!)v~R!YOX$qXY)iYZ)iZ]$q]^)i^p$qpq)iqr$qrs*vsv$qvw+fwx/ix}$q}!O0[!O!P$q!P!Q2z!Q![$q![!]4n!]!^$q!^!_8U!_!`!#t!`!a!$l!a!b!%d!b!c$q!c!}4n!}#P$q#P#Q!'W#Q#R$q#R#S4n#S#T$q#T#o4n#o%W$q%W%o4n%o%p$q%p&a4n&a&b$q&b1p4n1p4U$q4U4d4n4d4e$q4e$IS4n$IS$I`$q$I`$Ib4n$Ib$Kh$q$Kh%#t4n%#t&/x$q&/x&Et4n&Et&FV$q&FV;'S4n;'S;:j8O;:j;=`)c<%l?&r$q?&r?Ah4n?Ah?BY$q?BY?Mn4n?MnO$qi$zXVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qa%nVVP!O`Ov%gwx&Tx!^%g!^!_&o!_;'S%g;'S;=`'W<%lO%gP&YTVPOv&Tw!^&T!_;'S&T;'S;=`&i<%lO&TP&lP;=`<%l&T`&tS!O`Ov&ox;'S&o;'S;=`'Q<%lO&o`'TP;=`<%l&oa'ZP;=`<%l%gX'eWVP|WOr'^rs&Tsv'^w!^'^!^!_'}!_;'S'^;'S;=`(i<%lO'^W(ST|WOr'}sv'}w;'S'};'S;=`(c<%lO'}W(fP;=`<%l'}X(lP;=`<%l'^h(vV|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oh)`P;=`<%l(oi)fP;=`<%l$qo)t`VP|W!O`zUOX$qXY)iYZ)iZ]$q]^)i^p$qpq)iqr$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk+PV{YVP!O`Ov%gwx&Tx!^%g!^!_&o!_;'S%g;'S;=`'W<%lO%g~+iast,n![!]-r!c!}-r#R#S-r#T#o-r%W%o-r%p&a-r&b1p-r4U4d-r4e$IS-r$I`$Ib-r$Kh%#t-r&/x&Et-r&FV;'S-r;'S;:j/c?&r?Ah-r?BY?Mn-r~,qQ!Q![,w#l#m-V~,zQ!Q![,w!]!^-Q~-VOX~~-YR!Q![-c!c!i-c#T#Z-c~-fS!Q![-c!]!^-Q!c!i-c#T#Z-c~-ug}!O-r!O!P-r!Q![-r![!]-r!]!^/^!c!}-r#R#S-r#T#o-r$}%O-r%W%o-r%p&a-r&b1p-r1p4U-r4U4d-r4e$IS-r$I`$Ib-r$Je$Jg-r$Kh%#t-r&/x&Et-r&FV;'S-r;'S;:j/c?&r?Ah-r?BY?Mn-r~/cOW~~/fP;=`<%l-rk/rW}bVP|WOr'^rs&Tsv'^w!^'^!^!_'}!_;'S'^;'S;=`(i<%lO'^k0eZVP|W!O`Or$qrs%gsv$qwx'^x}$q}!O1W!O!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk1aZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a2S!a;'S$q;'S;=`)c<%lO$qk2_X!PQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qm3TZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a3v!a;'S$q;'S;=`)c<%lO$qm4RXdSVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qo4{!P`S^QVP|W!O`Or$qrs%gsv$qwx'^x}$q}!O4n!O!P4n!P!Q$q!Q![4n![!]4n!]!^$q!^!_(o!_!c$q!c!}4n!}#R$q#R#S4n#S#T$q#T#o4n#o$}$q$}%O4n%O%W$q%W%o4n%o%p$q%p&a4n&a&b$q&b1p4n1p4U4n4U4d4n4d4e$q4e$IS4n$IS$I`$q$I`$Ib4n$Ib$Je$q$Je$Jg4n$Jg$Kh$q$Kh%#t4n%#t&/x$q&/x&Et4n&Et&FV$q&FV;'S4n;'S;:j8O;:j;=`)c<%l?&r$q?&r?Ah4n?Ah?BY$q?BY?Mn4n?MnO$qo8RP;=`<%l4ni8]Y|W!O`Oq(oqr8{rs&osv(owx'}x!a(o!a!b!#U!b;'S(o;'S;=`)]<%lO(oi9S_|W!O`Or(ors&osv(owx'}x}(o}!O:R!O!f(o!f!g;e!g!}(o!}#ODh#O#W(o#W#XLp#X;'S(o;'S;=`)]<%lO(oi:YX|W!O`Or(ors&osv(owx'}x}(o}!O:u!O;'S(o;'S;=`)]<%lO(oi;OV!QP|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oi;lX|W!O`Or(ors&osv(owx'}x!q(o!q!r<X!r;'S(o;'S;=`)]<%lO(oi<`X|W!O`Or(ors&osv(owx'}x!e(o!e!f<{!f;'S(o;'S;=`)]<%lO(oi=SX|W!O`Or(ors&osv(owx'}x!v(o!v!w=o!w;'S(o;'S;=`)]<%lO(oi=vX|W!O`Or(ors&osv(owx'}x!{(o!{!|>c!|;'S(o;'S;=`)]<%lO(oi>jX|W!O`Or(ors&osv(owx'}x!r(o!r!s?V!s;'S(o;'S;=`)]<%lO(oi?^X|W!O`Or(ors&osv(owx'}x!g(o!g!h?y!h;'S(o;'S;=`)]<%lO(oi@QY|W!O`Or?yrs@psv?yvwA[wxBdx!`?y!`!aCr!a;'S?y;'S;=`Db<%lO?ya@uV!O`Ov@pvxA[x!`@p!`!aAy!a;'S@p;'S;=`B^<%lO@pPA_TO!`A[!`!aAn!a;'SA[;'S;=`As<%lOA[PAsOiPPAvP;=`<%lA[aBQSiP!O`Ov&ox;'S&o;'S;=`'Q<%lO&oaBaP;=`<%l@pXBiX|WOrBdrsA[svBdvwA[w!`Bd!`!aCU!a;'SBd;'S;=`Cl<%lOBdXC]TiP|WOr'}sv'}w;'S'};'S;=`(c<%lO'}XCoP;=`<%lBdiC{ViP|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oiDeP;=`<%l?yiDoZ|W!O`Or(ors&osv(owx'}x!e(o!e!fEb!f#V(o#V#WIr#W;'S(o;'S;=`)]<%lO(oiEiX|W!O`Or(ors&osv(owx'}x!f(o!f!gFU!g;'S(o;'S;=`)]<%lO(oiF]X|W!O`Or(ors&osv(owx'}x!c(o!c!dFx!d;'S(o;'S;=`)]<%lO(oiGPX|W!O`Or(ors&osv(owx'}x!v(o!v!wGl!w;'S(o;'S;=`)]<%lO(oiGsX|W!O`Or(ors&osv(owx'}x!c(o!c!dH`!d;'S(o;'S;=`)]<%lO(oiHgX|W!O`Or(ors&osv(owx'}x!}(o!}#OIS#O;'S(o;'S;=`)]<%lO(oiI]V|W!O`yPOr(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(oiIyX|W!O`Or(ors&osv(owx'}x#W(o#W#XJf#X;'S(o;'S;=`)]<%lO(oiJmX|W!O`Or(ors&osv(owx'}x#T(o#T#UKY#U;'S(o;'S;=`)]<%lO(oiKaX|W!O`Or(ors&osv(owx'}x#h(o#h#iK|#i;'S(o;'S;=`)]<%lO(oiLTX|W!O`Or(ors&osv(owx'}x#T(o#T#UH`#U;'S(o;'S;=`)]<%lO(oiLwX|W!O`Or(ors&osv(owx'}x#c(o#c#dMd#d;'S(o;'S;=`)]<%lO(oiMkX|W!O`Or(ors&osv(owx'}x#V(o#V#WNW#W;'S(o;'S;=`)]<%lO(oiN_X|W!O`Or(ors&osv(owx'}x#h(o#h#iNz#i;'S(o;'S;=`)]<%lO(oi! RX|W!O`Or(ors&osv(owx'}x#m(o#m#n! n#n;'S(o;'S;=`)]<%lO(oi! uX|W!O`Or(ors&osv(owx'}x#d(o#d#e!!b#e;'S(o;'S;=`)]<%lO(oi!!iX|W!O`Or(ors&osv(owx'}x#X(o#X#Y?y#Y;'S(o;'S;=`)]<%lO(oi!#_V!SP|W!O`Or(ors&osv(owx'}x;'S(o;'S;=`)]<%lO(ok!$PXaQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qo!$wX[UVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk!%mZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a!&`!a;'S$q;'S;=`)c<%lO$qk!&kX!RQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$qk!'aZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_#P$q#P#Q!(S#Q;'S$q;'S;=`)c<%lO$qk!(]ZVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_!`$q!`!a!)O!a;'S$q;'S;=`)c<%lO$qk!)ZXxQVP|W!O`Or$qrs%gsv$qwx'^x!^$q!^!_(o!_;'S$q;'S;=`)c<%lO$q",
55365
- tokenizers: [startTag, commentContent, piContent, cdataContent, 0, 1, 2, 3, 4],
55366
- topRules: { "Document": [0, 6] },
55367
- tokenPrec: 0
55368
- });
55369
- function tagName(doc2, tag) {
55370
- let name2 = tag && tag.getChild("TagName");
55371
- return name2 ? doc2.sliceString(name2.from, name2.to) : "";
55372
- }
55373
- function elementName$1(doc2, tree) {
55374
- let tag = tree && tree.firstChild;
55375
- return !tag || tag.name != "OpenTag" ? "" : tagName(doc2, tag);
55376
- }
55377
- function attrName(doc2, tag, pos) {
55378
- let attr = tag && tag.getChildren("Attribute").find((a) => a.from <= pos && a.to >= pos);
55379
- let name2 = attr && attr.getChild("AttributeName");
55380
- return name2 ? doc2.sliceString(name2.from, name2.to) : "";
55381
- }
55382
- function findParentElement(tree) {
55383
- for (let cur2 = tree && tree.parent; cur2; cur2 = cur2.parent)
55384
- if (cur2.name == "Element")
55385
- return cur2;
55386
- return null;
55387
- }
55388
- function findLocation(state, pos) {
55389
- var _a2;
55390
- let at = syntaxTree(state).resolveInner(pos, -1), inTag = null;
55391
- for (let cur2 = at; !inTag && cur2.parent; cur2 = cur2.parent)
55392
- if (cur2.name == "OpenTag" || cur2.name == "CloseTag" || cur2.name == "SelfClosingTag" || cur2.name == "MismatchedCloseTag")
55393
- inTag = cur2;
55394
- if (inTag && (inTag.to > pos || inTag.lastChild.type.isError)) {
55395
- let elt = inTag.parent;
55396
- if (at.name == "TagName")
55397
- return inTag.name == "CloseTag" || inTag.name == "MismatchedCloseTag" ? { type: "closeTag", from: at.from, context: elt } : { type: "openTag", from: at.from, context: findParentElement(elt) };
55398
- if (at.name == "AttributeName")
55399
- return { type: "attrName", from: at.from, context: inTag };
55400
- if (at.name == "AttributeValue")
55401
- return { type: "attrValue", from: at.from, context: inTag };
55402
- let before = at == inTag || at.name == "Attribute" ? at.childBefore(pos) : at;
55403
- if ((before === null || before === void 0 ? void 0 : before.name) == "StartTag")
55404
- return { type: "openTag", from: pos, context: findParentElement(elt) };
55405
- if ((before === null || before === void 0 ? void 0 : before.name) == "StartCloseTag" && before.to <= pos)
55406
- return { type: "closeTag", from: pos, context: elt };
55407
- if ((before === null || before === void 0 ? void 0 : before.name) == "Is")
55408
- return { type: "attrValue", from: pos, context: inTag };
55409
- if (before)
55410
- return { type: "attrName", from: pos, context: inTag };
55411
- return null;
55412
- } else if (at.name == "StartCloseTag") {
55413
- return { type: "closeTag", from: pos, context: at.parent };
55414
- }
55415
- while (at.parent && at.to == pos && !((_a2 = at.lastChild) === null || _a2 === void 0 ? void 0 : _a2.type.isError))
55416
- at = at.parent;
55417
- if (at.name == "Element" || at.name == "Text" || at.name == "Document")
55418
- return { type: "tag", from: pos, context: at.name == "Element" ? at : findParentElement(at) };
55419
- return null;
55420
- }
55421
- class Element {
55422
- constructor(spec, attrs, attrValues) {
55423
- this.attrs = attrs;
55424
- this.attrValues = attrValues;
55425
- this.children = [];
55426
- this.name = spec.name;
55427
- this.completion = Object.assign(Object.assign({ type: "type" }, spec.completion || {}), { label: this.name });
55428
- this.openCompletion = Object.assign(Object.assign({}, this.completion), { label: "<" + this.name });
55429
- this.closeCompletion = Object.assign(Object.assign({}, this.completion), { label: "</" + this.name + ">", boost: 2 });
55430
- this.closeNameCompletion = Object.assign(Object.assign({}, this.completion), { label: this.name + ">" });
55431
- this.text = spec.textContent ? spec.textContent.map((s) => ({ label: s, type: "text" })) : [];
55432
- }
55433
- }
55434
- const Identifier$1 = /^[:\-\.\w\u00b7-\uffff]*$/;
55435
- function attrCompletion(spec) {
55436
- return Object.assign(Object.assign({ type: "property" }, spec.completion || {}), { label: spec.name });
55437
- }
55438
- function valueCompletion(spec) {
55439
- return typeof spec == "string" ? { label: `"${spec}"`, type: "constant" } : /^"/.test(spec.label) ? spec : Object.assign(Object.assign({}, spec), { label: `"${spec.label}"` });
55440
- }
55441
- function completeFromSchema(eltSpecs, attrSpecs) {
55442
- let allAttrs = [], globalAttrs = [];
55443
- let attrValues = /* @__PURE__ */ Object.create(null);
55444
- for (let s of attrSpecs) {
55445
- let completion2 = attrCompletion(s);
55446
- allAttrs.push(completion2);
55447
- if (s.global)
55448
- globalAttrs.push(completion2);
55449
- if (s.values)
55450
- attrValues[s.name] = s.values.map(valueCompletion);
55451
- }
55452
- let allElements = [], topElements = [];
55453
- let byName = /* @__PURE__ */ Object.create(null);
55454
- for (let s of eltSpecs) {
55455
- let attrs = globalAttrs, attrVals = attrValues;
55456
- if (s.attributes)
55457
- attrs = attrs.concat(s.attributes.map((s2) => {
55458
- if (typeof s2 == "string")
55459
- return allAttrs.find((a) => a.label == s2) || { label: s2, type: "property" };
55460
- if (s2.values) {
55461
- if (attrVals == attrValues)
55462
- attrVals = Object.create(attrVals);
55463
- attrVals[s2.name] = s2.values.map(valueCompletion);
55464
- }
55465
- return attrCompletion(s2);
55466
- }));
55467
- let elt = new Element(s, attrs, attrVals);
55468
- byName[elt.name] = elt;
55469
- allElements.push(elt);
55470
- if (s.top)
55471
- topElements.push(elt);
55472
- }
55473
- if (!topElements.length)
55474
- topElements = allElements;
55475
- for (let i = 0; i < allElements.length; i++) {
55476
- let s = eltSpecs[i], elt = allElements[i];
55477
- if (s.children) {
55478
- for (let ch of s.children)
55479
- if (byName[ch])
55480
- elt.children.push(byName[ch]);
55481
- } else {
55482
- elt.children = allElements;
55483
- }
55484
- }
55485
- return (cx) => {
55486
- var _a2;
55487
- let { doc: doc2 } = cx.state, loc = findLocation(cx.state, cx.pos);
55488
- if (!loc || loc.type == "tag" && !cx.explicit)
55489
- return null;
55490
- let { type: type2, from, context } = loc;
55491
- if (type2 == "openTag") {
55492
- let children = topElements;
55493
- let parentName = elementName$1(doc2, context);
55494
- if (parentName) {
55495
- let parent = byName[parentName];
55496
- children = (parent === null || parent === void 0 ? void 0 : parent.children) || allElements;
55497
- }
55498
- return {
55499
- from,
55500
- options: children.map((ch) => ch.completion),
55501
- validFor: Identifier$1
55502
- };
55503
- } else if (type2 == "closeTag") {
55504
- let parentName = elementName$1(doc2, context);
55505
- return parentName ? {
55506
- from,
55507
- to: cx.pos + (doc2.sliceString(cx.pos, cx.pos + 1) == ">" ? 1 : 0),
55508
- options: [((_a2 = byName[parentName]) === null || _a2 === void 0 ? void 0 : _a2.closeNameCompletion) || { label: parentName + ">", type: "type" }],
55509
- validFor: Identifier$1
55510
- } : null;
55511
- } else if (type2 == "attrName") {
55512
- let parent = byName[tagName(doc2, context)];
55513
- return {
55514
- from,
55515
- options: (parent === null || parent === void 0 ? void 0 : parent.attrs) || globalAttrs,
55516
- validFor: Identifier$1
55517
- };
55518
- } else if (type2 == "attrValue") {
55519
- let attr = attrName(doc2, context, from);
55520
- if (!attr)
55521
- return null;
55522
- let parent = byName[tagName(doc2, context)];
55523
- let values = ((parent === null || parent === void 0 ? void 0 : parent.attrValues) || attrValues)[attr];
55524
- if (!values || !values.length)
55525
- return null;
55526
- return {
55527
- from,
55528
- to: cx.pos + (doc2.sliceString(cx.pos, cx.pos + 1) == '"' ? 1 : 0),
55529
- options: values,
55530
- validFor: /^"[^"]*"?$/
55531
- };
55532
- } else if (type2 == "tag") {
55533
- let parentName = elementName$1(doc2, context), parent = byName[parentName];
55534
- let closing2 = [], last = context && context.lastChild;
55535
- if (parentName && (!last || last.name != "CloseTag" || tagName(doc2, last) != parentName))
55536
- closing2.push(parent ? parent.closeCompletion : { label: "</" + parentName + ">", type: "type", boost: 2 });
55537
- let options = closing2.concat(((parent === null || parent === void 0 ? void 0 : parent.children) || (context ? allElements : topElements)).map((e) => e.openCompletion));
55538
- if (context && (parent === null || parent === void 0 ? void 0 : parent.text.length)) {
55539
- let openTag = context.firstChild;
55540
- if (openTag.to > cx.pos - 20 && !/\S/.test(cx.state.sliceDoc(openTag.to, cx.pos)))
55541
- options = options.concat(parent.text);
55542
- }
55543
- return {
55544
- from,
55545
- options,
55546
- validFor: /^<\/?[:\-\.\w\u00b7-\uffff]*$/
55547
- };
55548
- } else {
55549
- return null;
55550
- }
55551
- };
55552
- }
55553
- const xmlLanguage = /* @__PURE__ */ LRLanguage.define({
55554
- name: "xml",
55555
- parser: /* @__PURE__ */ parser$1.configure({
55556
- props: [
55557
- /* @__PURE__ */ indentNodeProp.add({
55558
- Element(context) {
55559
- let closed = /^\s*<\//.test(context.textAfter);
55560
- return context.lineIndent(context.node.from) + (closed ? 0 : context.unit);
55561
- },
55562
- "OpenTag CloseTag SelfClosingTag"(context) {
55563
- return context.column(context.node.from) + context.unit;
55564
- }
55565
- }),
55566
- /* @__PURE__ */ foldNodeProp.add({
55567
- Element(subtree) {
55568
- let first = subtree.firstChild, last = subtree.lastChild;
55569
- if (!first || first.name != "OpenTag")
55570
- return null;
55571
- return { from: first.to, to: last.name == "CloseTag" ? last.from : subtree.to };
55572
- }
55573
- }),
55574
- /* @__PURE__ */ bracketMatchingHandle.add({
55575
- "OpenTag CloseTag": (node) => node.getChild("TagName")
55576
- })
55577
- ]
55578
- }),
55579
- languageData: {
55580
- commentTokens: { block: { open: "<!--", close: "-->" } },
55581
- indentOnInput: /^\s*<\/$/
55582
- }
55583
- });
55584
- function xml(conf = {}) {
55585
- let support = [xmlLanguage.data.of({
55586
- autocomplete: completeFromSchema(conf.elements || [], conf.attributes || [])
55587
- })];
55588
- if (conf.autoCloseTags !== false)
55589
- support.push(autoCloseTags$1);
55590
- return new LanguageSupport(xmlLanguage, support);
55591
- }
55592
- function elementName$2(doc2, tree, max = doc2.length) {
55593
- if (!tree)
55594
- return "";
55595
- let tag = tree.firstChild;
55596
- let name2 = tag && tag.getChild("TagName");
55597
- return name2 ? doc2.sliceString(name2.from, Math.min(name2.to, max)) : "";
55598
- }
55599
- const autoCloseTags$1 = /* @__PURE__ */ EditorView.inputHandler.of((view, from, to, text, insertTransaction) => {
55600
- if (view.composing || view.state.readOnly || from != to || text != ">" && text != "/" || !xmlLanguage.isActiveAt(view.state, from, -1))
55601
- return false;
55602
- let base2 = insertTransaction(), { state } = base2;
55603
- let closeTags = state.changeByRange((range) => {
55604
- var _a2, _b, _c;
55605
- let { head } = range;
55606
- let didType = state.doc.sliceString(head - 1, head) == text;
55607
- let after = syntaxTree(state).resolveInner(head, -1), name2;
55608
- if (didType && text == ">" && after.name == "EndTag") {
55609
- let tag = after.parent;
55610
- if (((_b = (_a2 = tag.parent) === null || _a2 === void 0 ? void 0 : _a2.lastChild) === null || _b === void 0 ? void 0 : _b.name) != "CloseTag" && (name2 = elementName$2(state.doc, tag.parent, head))) {
55611
- let to2 = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
55612
- let insert2 = `</${name2}>`;
55613
- return { range, changes: { from: head, to: to2, insert: insert2 } };
55614
- }
55615
- } else if (didType && text == "/" && after.name == "StartCloseTag") {
55616
- let base3 = after.parent;
55617
- if (after.from == head - 2 && ((_c = base3.lastChild) === null || _c === void 0 ? void 0 : _c.name) != "CloseTag" && (name2 = elementName$2(state.doc, base3, head))) {
55618
- let to2 = head + (state.doc.sliceString(head, head + 1) === ">" ? 1 : 0);
55619
- let insert2 = `${name2}>`;
55620
- return {
55621
- range: EditorSelection.cursor(head + insert2.length, -1),
55622
- changes: { from: head, to: to2, insert: insert2 }
55623
- };
55624
- }
55625
- }
55626
- return { range };
55627
- });
55628
- if (closeTags.changes.empty)
55629
- return false;
55630
- view.dispatch([
55631
- base2,
55632
- state.update(closeTags, {
55633
- userEvent: "input.complete",
55634
- scrollIntoView: true
55635
- })
55636
- ]);
55637
- return true;
55638
- });
55639
56440
  const SOAP_11_CONTENT_TYPE = "text/xml; charset=utf-8";
55640
56441
  const SOAP_12_CONTENT_TYPE = "application/soap+xml; charset=utf-8";
55641
56442
  function contentTypeForSoap(version) {
@@ -55647,7 +56448,7 @@ function withContentType(headers, value) {
55647
56448
  if (idx === -1) return [...headers, next];
55648
56449
  return headers.map((h, i) => i === idx ? { ...h, value, enabled: true } : h);
55649
56450
  }
55650
- const { electron: electron$p } = window;
56451
+ const { electron: electron$r } = window;
55651
56452
  function ParamTree({ params, depth = 0 }) {
55652
56453
  if (params.length === 0) {
55653
56454
  return /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 italic", children: "No parameters declared in WSDL." });
@@ -55673,7 +56474,7 @@ function SoapEditor({ request, onChange }) {
55673
56474
  let cancelled = false;
55674
56475
  (async () => {
55675
56476
  try {
55676
- const result = await electron$p.wsdlFetch(url);
56477
+ const result = await electron$r.wsdlFetch(url);
55677
56478
  if (cancelled) return;
55678
56479
  setOperations(result.operations);
55679
56480
  setEndpoints(result.endpoints);
@@ -55710,7 +56511,7 @@ function SoapEditor({ request, onChange }) {
55710
56511
  setFetching(true);
55711
56512
  setFetchError(null);
55712
56513
  try {
55713
- const result = await electron$p.wsdlFetch(soap.wsdlUrl.trim());
56514
+ const result = await electron$r.wsdlFetch(soap.wsdlUrl.trim());
55714
56515
  setOperations(result.operations);
55715
56516
  setEndpoints(result.endpoints);
55716
56517
  setTargetNs(result.targetNamespace);
@@ -55836,7 +56637,7 @@ function SoapEditor({ request, onChange }) {
55836
56637
  value: soap.envelope ?? "",
55837
56638
  height: "100%",
55838
56639
  theme: oneDark,
55839
- extensions: [xml()],
56640
+ extensions: [xml(), commentKeymap],
55840
56641
  onChange: (val) => updateSoap({ envelope: val }),
55841
56642
  basicSetup: { lineNumbers: true, foldGutter: true }
55842
56643
  }
@@ -55869,7 +56670,7 @@ function SoapEditor({ request, onChange }) {
55869
56670
  value: soap.envelope ?? "",
55870
56671
  height: "100%",
55871
56672
  theme: oneDark,
55872
- extensions: [xml()],
56673
+ extensions: [xml(), commentKeymap],
55873
56674
  onChange: (val) => updateSoap({ envelope: val }),
55874
56675
  basicSetup: { lineNumbers: true, foldGutter: true }
55875
56676
  }
@@ -55938,7 +56739,7 @@ function BodyTab({ request, onChange }) {
55938
56739
  height: "300px",
55939
56740
  maxHeight: "50vh",
55940
56741
  theme: oneDark,
55941
- extensions: [json(), varExt],
56742
+ extensions: [jsonWithComments(), varExt],
55942
56743
  onChange: (val) => onChange({ body: { ...body, json: val } }),
55943
56744
  basicSetup: { lineNumbers: true, foldGutter: false, autocompletion: false }
55944
56745
  }
@@ -55970,7 +56771,7 @@ function BodyTab({ request, onChange }) {
55970
56771
  height: "300px",
55971
56772
  maxHeight: "50vh",
55972
56773
  theme: oneDark,
55973
- extensions: [varExt],
56774
+ extensions: /xml/i.test(body.rawContentType ?? "") ? [xmlWithComments(), varExt] : [commentKeymap, varExt],
55974
56775
  onChange: (val) => onChange({ body: { ...body, raw: val } }),
55975
56776
  basicSetup: { lineNumbers: true, foldGutter: false, autocompletion: false }
55976
56777
  }
@@ -55980,7 +56781,7 @@ function BodyTab({ request, onChange }) {
55980
56781
  mode === "soap" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SoapEditor, { request, onChange }) })
55981
56782
  ] });
55982
56783
  }
55983
- const { electron: electron$o } = window;
56784
+ const { electron: electron$q } = window;
55984
56785
  const AUTH_TYPES = ["none", "bearer", "basic", "digest", "ntlm", "apikey", "oauth2"];
55985
56786
  function AuthTab({ request, onChange }) {
55986
56787
  const auth = request.auth;
@@ -55994,7 +56795,7 @@ function AuthTab({ request, onChange }) {
55994
56795
  }
55995
56796
  async function saveSecret(ref2) {
55996
56797
  if (!secretValue || !ref2) return;
55997
- await electron$o.setSecret(ref2, secretValue);
56798
+ await electron$q.setSecret(ref2, secretValue);
55998
56799
  setSaved(true);
55999
56800
  setSecretValue("");
56000
56801
  setTimeout(() => setSaved(false), 2e3);
@@ -56006,7 +56807,7 @@ function AuthTab({ request, onChange }) {
56006
56807
  setOauth2Error("");
56007
56808
  try {
56008
56809
  const vars = {};
56009
- const result = await electron$o.oauth2StartFlow(oauth2Auth, vars);
56810
+ const result = await electron$q.oauth2StartFlow(oauth2Auth, vars);
56010
56811
  setAuth({
56011
56812
  oauth2CachedToken: result.accessToken,
56012
56813
  oauth2TokenExpiry: result.expiresAt
@@ -56024,7 +56825,7 @@ function AuthTab({ request, onChange }) {
56024
56825
  setOauth2Status("fetching");
56025
56826
  setOauth2Error("");
56026
56827
  try {
56027
- const result = await electron$o.oauth2RefreshToken(oauth2Auth, {}, oauth2RefreshToken);
56828
+ const result = await electron$q.oauth2RefreshToken(oauth2Auth, {}, oauth2RefreshToken);
56028
56829
  setAuth({
56029
56830
  oauth2CachedToken: result.accessToken,
56030
56831
  oauth2TokenExpiry: result.expiresAt
@@ -56046,14 +56847,14 @@ function AuthTab({ request, onChange }) {
56046
56847
  if (auth.type !== "oauth2") return null;
56047
56848
  const t2 = auth.oauth2CachedToken;
56048
56849
  if (!t2) return null;
56049
- const preview = t2.length > 16 ? `${t2.slice(0, 6)}…${t2.slice(-6)}` : t2;
56850
+ const preview2 = t2.length > 16 ? `${t2.slice(0, 6)}…${t2.slice(-6)}` : t2;
56050
56851
  const expiry = auth.oauth2TokenExpiry;
56051
56852
  let expiryLabel = "";
56052
56853
  if (expiry) {
56053
56854
  const secsLeft = Math.round((expiry - Date.now()) / 1e3);
56054
56855
  expiryLabel = secsLeft > 0 ? ` (expires in ${secsLeft}s)` : " (EXPIRED)";
56055
56856
  }
56056
- return `${preview}${expiryLabel}`;
56857
+ return `${preview2}${expiryLabel}`;
56057
56858
  })();
56058
56859
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
56059
56860
  AuthEditor,
@@ -63268,6 +64069,17 @@ function SchemaTab({ request, onChange }) {
63268
64069
  }
63269
64070
  }
63270
64071
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3", children: [
64072
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border border-amber-700/40 bg-amber-950/20 px-3 py-2 text-[11px] leading-relaxed text-amber-200/90", children: [
64073
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-amber-300", children: "Schema — a local scratch check." }),
64074
+ " ",
64075
+ "Validate this request's last response against a JSON Schema, right here. It is ",
64076
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "not saved to the contract and not published" }),
64077
+ " — a dev-time sanity check only. To define what the provider ",
64078
+ /* @__PURE__ */ jsxRuntimeExports.jsx("em", { children: "must" }),
64079
+ " return (which drives contract testing), use the ",
64080
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Contract" }),
64081
+ " tab."
64082
+ ] }),
63271
64083
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
63272
64084
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-600 uppercase tracking-wider font-medium", children: "JSON Schema (draft-07+)" }),
63273
64085
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
@@ -63291,7 +64103,7 @@ function SchemaTab({ request, onChange }) {
63291
64103
  )
63292
64104
  ] })
63293
64105
  ] }),
63294
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600", children: "Standalone schema for ad-hoc validation. Independent of the contract - edits here don't affect it." }),
64106
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600", children: "Standalone edits here don't touch the contract. Use “Derive from contract” to start from it." }),
63295
64107
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-surface-700 rounded overflow-hidden", children: [
63296
64108
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end px-2 py-0.5 bg-surface-800/50 border-b border-surface-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
63297
64109
  "button",
@@ -63336,7 +64148,7 @@ function SchemaTab({ request, onChange }) {
63336
64148
  ] }) })
63337
64149
  ] });
63338
64150
  }
63339
- const { electron: electron$n } = window;
64151
+ const { electron: electron$p } = window;
63340
64152
  const EMPTY = { statusCode: 200, headers: [], bodySchema: "" };
63341
64153
  function ContractTab({ request, onChange }) {
63342
64154
  const activeTab = useStore((s) => s.tabs.find((t2) => t2.id === s.activeTabId));
@@ -63350,7 +64162,7 @@ function ContractTab({ request, onChange }) {
63350
64162
  if (!lastResponse?.body) return;
63351
64163
  setInferring(true);
63352
64164
  try {
63353
- const schema = await electron$n.inferContractSchema(lastResponse.body);
64165
+ const schema = await electron$p.inferContractSchema(lastResponse.body);
63354
64166
  if (schema) update({ bodySchema: schema });
63355
64167
  } finally {
63356
64168
  setInferring(false);
@@ -63367,11 +64179,24 @@ function ContractTab({ request, onChange }) {
63367
64179
  function removeHeader(i) {
63368
64180
  update({ headers: (contract.headers ?? []).filter((_, idx) => idx !== i) });
63369
64181
  }
63370
- const hasContract = contract.statusCode !== void 0 || contract.bodySchema?.trim() || contract.headers?.some((h) => h.key);
64182
+ const hasContract2 = contract.statusCode !== void 0 || contract.bodySchema?.trim() || contract.headers?.some((h) => h.key);
63371
64183
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-4 h-full min-h-0", children: [
63372
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-2 px-3 py-2 rounded-lg text-xs border ${hasContract ? "bg-blue-950/40 border-blue-700 text-blue-300" : "bg-surface-800 border-surface-700 text-surface-500"}`, children: [
63373
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-2 h-2 rounded-full ${hasContract ? "bg-blue-400" : "bg-surface-600"}` }),
63374
- hasContract ? "Contract defined - will be verified in Contract panel" : "No contract defined yet"
64184
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border border-blue-700/50 bg-blue-950/30 px-3 py-2 text-[11px] leading-relaxed text-blue-200/90", children: [
64185
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-blue-300", children: "Contract — the published expectation." }),
64186
+ " ",
64187
+ "What this request ",
64188
+ /* @__PURE__ */ jsxRuntimeExports.jsx("em", { children: "needs" }),
64189
+ " from the provider: status, body shape, headers. It becomes the ",
64190
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "consumer pact" }),
64191
+ " used for contract testing (bi-directional verify & ",
64192
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: "can-i-deploy" }),
64193
+ "). For a throwaway local response check, use the ",
64194
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Schema" }),
64195
+ " tab."
64196
+ ] }),
64197
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-2 px-3 py-2 rounded-lg text-xs border ${hasContract2 ? "bg-blue-950/40 border-blue-700 text-blue-300" : "bg-surface-800 border-surface-700 text-surface-500"}`, children: [
64198
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-2 h-2 rounded-full ${hasContract2 ? "bg-blue-400" : "bg-surface-600"}` }),
64199
+ hasContract2 ? "Contract defined - will be verified in Contract panel" : "No contract defined yet"
63375
64200
  ] }),
63376
64201
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
63377
64202
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] text-surface-500 uppercase tracking-wider font-medium block mb-1.5", children: "Expected Status Code" }),
@@ -63476,7 +64301,66 @@ function ContractTab({ request, onChange }) {
63476
64301
  ] })
63477
64302
  ] });
63478
64303
  }
63479
- const { electron: electron$m } = window;
64304
+ const DEFAULTS = { idleSec: 60, totalSec: 300 };
64305
+ function StreamTab({ request, onChange }) {
64306
+ const stream = request.stream ?? {};
64307
+ function setField(key, seconds) {
64308
+ const next = { ...stream };
64309
+ if (seconds.trim() === "") {
64310
+ delete next[key];
64311
+ } else {
64312
+ const n = Number(seconds);
64313
+ if (Number.isNaN(n) || n < 0) return;
64314
+ next[key] = Math.round(n * 1e3);
64315
+ }
64316
+ onChange({ stream: Object.keys(next).length ? next : void 0 });
64317
+ }
64318
+ const toSec = (ms) => ms === void 0 ? "" : String(ms / 1e3);
64319
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-4 p-1 text-xs max-w-md", children: [
64320
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-surface-400 leading-relaxed", children: [
64321
+ "Applies when the response is a stream (",
64322
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: "text/event-stream" }),
64323
+ ", NDJSON, or chunked). A stream stays open until the server ends it, you hit Stop, or one of these limits trips. Leave blank for the default; set 0 to disable."
64324
+ ] }),
64325
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
64326
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300 font-medium", children: "Idle timeout (seconds)" }),
64327
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
64328
+ "input",
64329
+ {
64330
+ type: "number",
64331
+ min: 0,
64332
+ value: toSec(stream.idleMs),
64333
+ onChange: (e) => setField("idleMs", e.target.value),
64334
+ placeholder: `${DEFAULTS.idleSec} (default)`,
64335
+ className: "w-40 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 placeholder-surface-600"
64336
+ }
64337
+ ),
64338
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-surface-500", children: "Close if no frame arrives for this long." })
64339
+ ] }),
64340
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
64341
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300 font-medium", children: "Max duration (seconds)" }),
64342
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
64343
+ "input",
64344
+ {
64345
+ type: "number",
64346
+ min: 0,
64347
+ value: toSec(stream.maxMs),
64348
+ onChange: (e) => setField("maxMs", e.target.value),
64349
+ placeholder: `${DEFAULTS.totalSec} (default)`,
64350
+ className: "w-40 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 placeholder-surface-600"
64351
+ }
64352
+ ),
64353
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-surface-500", children: "Total wall-clock cap for the whole stream." })
64354
+ ] }),
64355
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] text-surface-500", children: [
64356
+ "When a limit trips, the stream ends with status ",
64357
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: "timed out" }),
64358
+ " ",
64359
+ "and keeps whatever frames arrived. There is also a hard cap of 5000 events."
64360
+ ] })
64361
+ ] });
64362
+ }
64363
+ const { electron: electron$o } = window;
63480
64364
  function formatTime$1(ts) {
63481
64365
  const d = new Date(ts);
63482
64366
  const hh = String(d.getHours()).padStart(2, "0");
@@ -63496,14 +64380,14 @@ function WebSocketPanel({ request }) {
63496
64380
  const [sendText, setSendText] = reactExports.useState("");
63497
64381
  const logEndRef = reactExports.useRef(null);
63498
64382
  reactExports.useEffect(() => {
63499
- electron$m.onWsMessage(({ requestId, message }) => {
64383
+ electron$o.onWsMessage(({ requestId, message }) => {
63500
64384
  addWsMessage(requestId, message);
63501
64385
  });
63502
- electron$m.onWsStatus(({ requestId, status, error: error2 }) => {
64386
+ electron$o.onWsStatus(({ requestId, status, error: error2 }) => {
63503
64387
  setWsStatus(requestId, status, error2);
63504
64388
  });
63505
64389
  return () => {
63506
- electron$m.offWsEvents();
64390
+ electron$o.offWsEvents();
63507
64391
  };
63508
64392
  }, [addWsMessage, setWsStatus]);
63509
64393
  reactExports.useEffect(() => {
@@ -63516,19 +64400,19 @@ function WebSocketPanel({ request }) {
63516
64400
  if (h.enabled && h.key) headers[h.key] = h.value;
63517
64401
  }
63518
64402
  try {
63519
- await electron$m.wsConnect(request.id, request.url, headers);
64403
+ await electron$o.wsConnect(request.id, request.url, headers);
63520
64404
  } catch (err) {
63521
64405
  setWsStatus(request.id, "error", err instanceof Error ? err.message : String(err));
63522
64406
  }
63523
64407
  }
63524
64408
  async function disconnect() {
63525
- await electron$m.wsDisconnect(request.id);
64409
+ await electron$o.wsDisconnect(request.id);
63526
64410
  }
63527
64411
  async function sendMessage() {
63528
64412
  const text = sendText.trim();
63529
64413
  if (!text || !isConnected) return;
63530
64414
  try {
63531
- await electron$m.wsSend(request.id, text);
64415
+ await electron$o.wsSend(request.id, text);
63532
64416
  const msg = {
63533
64417
  id: crypto.randomUUID(),
63534
64418
  direction: "sent",
@@ -63631,20 +64515,6 @@ function WebSocketPanel({ request }) {
63631
64515
  ] })
63632
64516
  ] });
63633
64517
  }
63634
- function useToast(durationMs = 3e3) {
63635
- const [toast, setToast] = reactExports.useState(null);
63636
- const timer = reactExports.useRef(null);
63637
- function show(msg, ok) {
63638
- if (timer.current) clearTimeout(timer.current);
63639
- setToast({ msg, ok });
63640
- timer.current = setTimeout(() => setToast(null), durationMs);
63641
- }
63642
- return { toast, show };
63643
- }
63644
- function Toast({ toast }) {
63645
- if (!toast) return null;
63646
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `mx-3 mb-2 px-2 py-1.5 rounded text-[11px] flex-shrink-0 ${toast.ok ? "bg-emerald-900/50 text-emerald-300 border border-emerald-800/50" : "bg-red-900/50 text-red-300 border border-red-800/50"}`, children: toast.msg });
63647
- }
63648
64518
  function statusColor$3(code2) {
63649
64519
  const d = String(code2)[0];
63650
64520
  return d === "2" ? "text-emerald-400" : d === "3" ? "text-amber-400" : "text-red-400";
@@ -63900,7 +64770,7 @@ function FuzzResultsPanel({ report, onClear }) {
63900
64770
  ] }) })
63901
64771
  ] });
63902
64772
  }
63903
- const { electron: electron$l } = window;
64773
+ const { electron: electron$n } = window;
63904
64774
  const WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
63905
64775
  function FuzzModal({ request, onClose }) {
63906
64776
  const environments = useStore((s) => s.environments);
@@ -63926,7 +64796,7 @@ function FuzzModal({ request, onClose }) {
63926
64796
  const env = resolveEnvironmentById(environments, activeEnvironmentId);
63927
64797
  const envVars = env ? Object.fromEntries(env.variables.filter((v) => v.enabled).map((v) => [v.key, v.value])) : {};
63928
64798
  const collectionVars = activeCollectionId ? collections[activeCollectionId]?.data.collectionVariables ?? {} : {};
63929
- const result = await electron$l.fuzzContracts({
64799
+ const result = await electron$n.fuzzContracts({
63930
64800
  requests: [request],
63931
64801
  envVars,
63932
64802
  collectionVars,
@@ -64018,7 +64888,7 @@ function FuzzModal({ request, onClose }) {
64018
64888
  }
64019
64889
  );
64020
64890
  }
64021
- const { electron: electron$k } = window;
64891
+ const { electron: electron$m } = window;
64022
64892
  const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "QUERY"];
64023
64893
  const METHOD_COLORS = {
64024
64894
  GET: "text-emerald-400",
@@ -64037,8 +64907,14 @@ function deriveHookStatus(r) {
64037
64907
  if (tests.length === 0) return "skipped";
64038
64908
  return tests.every((t2) => t2.passed) ? "passed" : "failed";
64039
64909
  }
64910
+ const TAB_HINTS = {
64911
+ schema: "Schema — a local, throwaway JSON-Schema check of the last response. Not saved to the contract, not published.",
64912
+ contract: "Contract — the published expectation (status, body shape, headers) that drives contract testing: consumer pact, bi-directional verify, can-i-deploy.",
64913
+ stream: "Stream — idle and total timeouts for streamed responses (SSE / NDJSON / chunked)."
64914
+ };
64040
64915
  function RequestBuilder({ request }) {
64041
64916
  const updateRequest = useStore((s) => s.updateRequest);
64917
+ const updateExampleRequest = useStore((s) => s.updateExampleRequest);
64042
64918
  const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
64043
64919
  const activeCollectionId = useStore((s) => s.activeCollectionId);
64044
64920
  const environments = useStore((s) => s.environments);
@@ -64051,6 +64927,8 @@ function RequestBuilder({ request }) {
64051
64927
  const setTabRequestTab = useStore((s) => s.setTabRequestTab);
64052
64928
  const addHistoryEntry = useStore((s) => s.addHistoryEntry);
64053
64929
  const applyScriptUpdates = useStore((s) => s.applyScriptUpdates);
64930
+ const startLiveStream = useStore((s) => s.startLiveStream);
64931
+ const finishLiveStream = useStore((s) => s.finishLiveStream);
64054
64932
  const workspaceSettings = useStore((s) => s.workspace?.settings);
64055
64933
  const collectionTls = useStore((s) => activeCollectionId ? s.collections[activeCollectionId]?.data.tls : void 0);
64056
64934
  const activeAppTab = useStore((s) => s.tabs.find((t2) => t2.id === s.activeTabId));
@@ -64071,7 +64949,21 @@ function RequestBuilder({ request }) {
64071
64949
  });
64072
64950
  }
64073
64951
  function update(patch) {
64074
- updateRequest(request.id, patch);
64952
+ if (activeAppTab?.exampleId) updateExampleRequest(request.id, activeAppTab.exampleId, patch);
64953
+ else updateRequest(request.id, patch);
64954
+ }
64955
+ function handleUrlPaste(e) {
64956
+ const pasted = e.clipboardData.getData("text");
64957
+ if (!pasted.includes("?")) return;
64958
+ const input = e.currentTarget;
64959
+ const start = input.selectionStart ?? request.url.length;
64960
+ const end = input.selectionEnd ?? request.url.length;
64961
+ const resulting = request.url.slice(0, start) + pasted + request.url.slice(end);
64962
+ const split = extractQueryParams(resulting, request.params ?? []);
64963
+ if (split.changed) {
64964
+ e.preventDefault();
64965
+ update({ url: split.url, params: split.params });
64966
+ }
64075
64967
  }
64076
64968
  const sendSignal = useStore((s) => s.sendSignal);
64077
64969
  const lastSendSignal = reactExports.useRef(sendSignal);
@@ -64121,7 +65013,7 @@ function RequestBuilder({ request }) {
64121
65013
  activeEnvironmentId
64122
65014
  );
64123
65015
  const hookSessionVars = useStore.getState().sessionVars;
64124
- const r = await electron$k.sendRequest({
65016
+ const r = await electron$m.sendRequest({
64125
65017
  ...basePayload,
64126
65018
  environment: hookEnv,
64127
65019
  request: hook,
@@ -64165,13 +65057,21 @@ function RequestBuilder({ request }) {
64165
65057
  activeEnvironmentId
64166
65058
  );
64167
65059
  const freshSessionVars = useStore.getState().sessionVars;
64168
- const result = await electron$k.sendRequest({
64169
- ...basePayload,
64170
- environment: freshEnv,
64171
- request: mergedRequest,
64172
- collectionVars: { ...collectionVars, ...freshSessionVars },
64173
- globals: liveGlobals
64174
- });
65060
+ const streamId = crypto.randomUUID();
65061
+ startLiveStream(activeTabId, streamId);
65062
+ let result;
65063
+ try {
65064
+ result = await electron$m.sendRequest({
65065
+ ...basePayload,
65066
+ environment: freshEnv,
65067
+ request: mergedRequest,
65068
+ collectionVars: { ...collectionVars, ...freshSessionVars },
65069
+ globals: liveGlobals,
65070
+ streamId
65071
+ });
65072
+ } finally {
65073
+ finishLiveStream(streamId);
65074
+ }
64175
65075
  setTabResponse(activeTabId, result.response, result.scriptResult, result.sentRequest);
64176
65076
  applyScriptUpdates(result.scriptResult);
64177
65077
  collectionVars = { ...collectionVars, ...result.scriptResult.updatedCollectionVars };
@@ -64193,7 +65093,7 @@ function RequestBuilder({ request }) {
64193
65093
  activeEnvironmentId
64194
65094
  );
64195
65095
  const hookSessionVars = useStore.getState().sessionVars;
64196
- const r = await electron$k.sendRequest({
65096
+ const r = await electron$m.sendRequest({
64197
65097
  ...basePayload,
64198
65098
  environment: hookEnv,
64199
65099
  request: hook,
@@ -64243,6 +65143,7 @@ function RequestBuilder({ request }) {
64243
65143
  const hasPostScript = Boolean(request.postRequestScript?.trim());
64244
65144
  const isWs = request.protocol === "websocket";
64245
65145
  const isSoap = request.protocol === "soap";
65146
+ const isExample = !!activeAppTab?.exampleId;
64246
65147
  const tabs = [
64247
65148
  // SOAP collapses Params + Body into a single "SOAP" tab — the WSDL drives both.
64248
65149
  ...!isSoap ? [{ id: "params", label: "Params", count: request.params.filter((p2) => p2.enabled && p2.key).length }] : [],
@@ -64250,30 +65151,39 @@ function RequestBuilder({ request }) {
64250
65151
  ...!isWs ? [
64251
65152
  { id: "body", label: isSoap ? "SOAP" : "Body", count: request.body.mode !== "none" ? 1 : 0 },
64252
65153
  { id: "auth", label: "Auth", count: request.auth.type !== "none" ? 1 : 0 },
64253
- { id: "scripts", label: "Scripts", count: (hasPreScript ? 1 : 0) + (hasPostScript ? 1 : 0) },
64254
- { id: "schema", label: "Schema", count: request.schema?.trim() ? 1 : 0 },
64255
- { id: "contract", label: "Contract", count: request.contract?.statusCode !== void 0 || request.contract?.bodySchema?.trim() || request.contract?.headers?.some((h) => h.key) ? 1 : 0 }
65154
+ ...!isExample ? [
65155
+ { id: "scripts", label: "Scripts", count: (hasPreScript ? 1 : 0) + (hasPostScript ? 1 : 0) },
65156
+ { id: "schema", label: "Schema", count: request.schema?.trim() ? 1 : 0 },
65157
+ { id: "contract", label: "Contract", count: request.contract?.statusCode !== void 0 || request.contract?.bodySchema?.trim() || request.contract?.headers?.some((h) => h.key) ? 1 : 0 },
65158
+ { id: "stream", label: "Stream", count: request.stream?.idleMs !== void 0 || request.stream?.maxMs !== void 0 ? 1 : 0 }
65159
+ ] : []
64256
65160
  ] : []
64257
65161
  ];
64258
65162
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full", children: [
64259
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-3 pb-1 flex-shrink-0", children: editingName ? /* @__PURE__ */ jsxRuntimeExports.jsx(
64260
- "input",
64261
- {
64262
- autoFocus: true,
64263
- value: request.name,
64264
- onChange: (e) => update({ name: e.target.value }),
64265
- onBlur: () => setEditingName(false),
64266
- onKeyDown: (e) => e.key === "Enter" && setEditingName(false),
64267
- className: "text-sm font-medium bg-transparent border-b border-blue-500 focus:outline-none w-full"
64268
- }
64269
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
64270
- "button",
64271
- {
64272
- onClick: () => setEditingName(true),
64273
- className: "text-sm font-medium text-white hover:text-blue-400 transition-colors text-left",
64274
- children: request.name
64275
- }
64276
- ) }),
65163
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-3 pb-1 flex-shrink-0 flex items-center justify-between gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex items-center gap-2", children: [
65164
+ editingName ? /* @__PURE__ */ jsxRuntimeExports.jsx(
65165
+ "input",
65166
+ {
65167
+ autoFocus: true,
65168
+ value: request.name,
65169
+ onChange: (e) => update({ name: e.target.value }),
65170
+ onBlur: () => setEditingName(false),
65171
+ onKeyDown: (e) => e.key === "Enter" && setEditingName(false),
65172
+ className: "text-sm font-medium bg-transparent border-b border-blue-500 focus:outline-none w-full"
65173
+ }
65174
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
65175
+ "button",
65176
+ {
65177
+ onClick: () => setEditingName(true),
65178
+ className: "text-sm font-medium text-white hover:text-blue-400 transition-colors text-left truncate",
65179
+ children: request.name
65180
+ }
65181
+ ),
65182
+ activeAppTab?.exampleId && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 text-[10px] uppercase tracking-wider text-amber-400 border border-amber-500/30 rounded px-1.5 py-0.5", children: [
65183
+ "Example: ",
65184
+ request.examples?.find((e) => e.id === activeAppTab.exampleId)?.name ?? ""
65185
+ ] })
65186
+ ] }) }),
64277
65187
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-4 py-2 flex-shrink-0", children: [
64278
65188
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-surface-800 border border-surface-700 rounded overflow-hidden text-xs font-bold flex-shrink-0", children: [
64279
65189
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -64357,6 +65267,7 @@ function RequestBuilder({ request }) {
64357
65267
  {
64358
65268
  value: request.url,
64359
65269
  onChange: (url) => update({ url }),
65270
+ onPaste: handleUrlPaste,
64360
65271
  placeholder: isWs ? "ws://example.com/socket" : isSoap ? "Endpoint (auto-filled from WSDL <soap:address>)" : "https://api.example.com/endpoint",
64361
65272
  wrapperClassName: "flex-1",
64362
65273
  className: `border rounded px-3 py-1.5 text-sm focus:outline-none focus:border-blue-500 font-mono placeholder-surface-700 ${isSoap ? "bg-surface-900 border-amber-900/50 text-surface-300" : "bg-surface-800 border-surface-700"}`
@@ -64390,6 +65301,7 @@ function RequestBuilder({ request }) {
64390
65301
  "button",
64391
65302
  {
64392
65303
  onClick: () => setActiveTab(tab.id),
65304
+ title: TAB_HINTS[tab.id],
64393
65305
  className: `px-3 py-1.5 text-xs transition-colors border-b-2 -mb-px ${activeTab === tab.id ? "border-blue-500 text-white" : "border-transparent text-surface-400 hover:text-white"}`,
64394
65306
  children: [
64395
65307
  tab.label,
@@ -64409,15 +65321,19 @@ function RequestBuilder({ request }) {
64409
65321
  }
64410
65322
  )
64411
65323
  ] }),
64412
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 flex-1 overflow-y-auto min-h-0", children: [
64413
- activeTab === "params" && /* @__PURE__ */ jsxRuntimeExports.jsx(ParamsTab, { request, onChange: update }),
64414
- activeTab === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsx(HeadersTab, { request, onChange: update }),
64415
- activeTab === "body" && /* @__PURE__ */ jsxRuntimeExports.jsx(BodyTab, { request, onChange: update }),
64416
- activeTab === "auth" && /* @__PURE__ */ jsxRuntimeExports.jsx(AuthTab, { request, onChange: update }),
64417
- activeTab === "scripts" && /* @__PURE__ */ jsxRuntimeExports.jsx(ScriptsTab, { request, onChange: update }),
64418
- activeTab === "schema" && /* @__PURE__ */ jsxRuntimeExports.jsx(SchemaTab, { request, onChange: update }),
64419
- activeTab === "contract" && /* @__PURE__ */ jsxRuntimeExports.jsx(ContractTab, { request, onChange: update })
64420
- ] })
65324
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3 flex-1 overflow-y-auto min-h-0", children: /* @__PURE__ */ (() => {
65325
+ const shown = isExample && (activeTab === "scripts" || activeTab === "schema" || activeTab === "contract" || activeTab === "stream") ? "body" : activeTab;
65326
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
65327
+ shown === "params" && /* @__PURE__ */ jsxRuntimeExports.jsx(ParamsTab, { request, onChange: update }),
65328
+ shown === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsx(HeadersTab, { request, onChange: update }),
65329
+ shown === "body" && /* @__PURE__ */ jsxRuntimeExports.jsx(BodyTab, { request, onChange: update }),
65330
+ shown === "auth" && /* @__PURE__ */ jsxRuntimeExports.jsx(AuthTab, { request, onChange: update }),
65331
+ !isExample && shown === "scripts" && /* @__PURE__ */ jsxRuntimeExports.jsx(ScriptsTab, { request, onChange: update }),
65332
+ !isExample && shown === "schema" && /* @__PURE__ */ jsxRuntimeExports.jsx(SchemaTab, { request, onChange: update }),
65333
+ !isExample && shown === "contract" && /* @__PURE__ */ jsxRuntimeExports.jsx(ContractTab, { request, onChange: update }),
65334
+ !isExample && shown === "stream" && /* @__PURE__ */ jsxRuntimeExports.jsx(StreamTab, { request, onChange: update })
65335
+ ] });
65336
+ })() })
64421
65337
  ] })
64422
65338
  ] });
64423
65339
  }
@@ -64667,13 +65583,13 @@ function AssertMenu({ state, onClose, onConfirm }) {
64667
65583
  if (state.type === "json") {
64668
65584
  const { path, value, root: root2 } = state;
64669
65585
  const isStr = typeof value === "string";
64670
- const preview = isStr ? `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"` : String(value);
65586
+ const preview2 = isStr ? `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"` : String(value);
64671
65587
  title2 = jsonPathLabel(path);
64672
65588
  options = [
64673
- { label: `equals ${preview}`, snippet: makeJsonSnippet(path, value, "equals") },
65589
+ { label: `equals ${preview2}`, snippet: makeJsonSnippet(path, value, "equals") },
64674
65590
  { label: "exists (not null/undefined)", snippet: makeJsonSnippet(path, value, "exists") },
64675
65591
  { label: `is ${value === null ? "null" : typeof value}`, snippet: makeJsonSnippet(path, value, "type") },
64676
- ...isStr ? [{ label: `contains ${preview}`, snippet: makeJsonSnippet(path, value, "contains") }] : []
65592
+ ...isStr ? [{ label: `contains ${preview2}`, snippet: makeJsonSnippet(path, value, "contains") }] : []
64677
65593
  ];
64678
65594
  const arrayIdx = [...path].reverse().findIndex((k) => typeof k === "number");
64679
65595
  if (arrayIdx >= 0) {
@@ -64697,12 +65613,12 @@ function AssertMenu({ state, onClose, onConfirm }) {
64697
65613
  }
64698
65614
  } else {
64699
65615
  const { selector, value } = state;
64700
- const preview = `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"`;
65616
+ const preview2 = `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"`;
64701
65617
  title2 = selector;
64702
65618
  options = [
64703
- { label: `equals ${preview}`, snippet: makeXmlSnippet(selector, value, "equals") },
65619
+ { label: `equals ${preview2}`, snippet: makeXmlSnippet(selector, value, "equals") },
64704
65620
  { label: "exists", snippet: makeXmlSnippet(selector, value, "exists") },
64705
- { label: `contains ${preview}`, snippet: makeXmlSnippet(selector, value, "contains") }
65621
+ { label: `contains ${preview2}`, snippet: makeXmlSnippet(selector, value, "contains") }
64706
65622
  ];
64707
65623
  }
64708
65624
  const x = Math.min(state.x, window.innerWidth - 280);
@@ -64912,6 +65828,128 @@ function InteractiveBody({ body, contentType, onAssert }) {
64912
65828
  treeContent
64913
65829
  ] });
64914
65830
  }
65831
+ const { electron: electron$l } = window;
65832
+ const RENDER_TAIL = 500;
65833
+ const CLOSE_LABEL = {
65834
+ complete: "closed",
65835
+ stopped: "stopped",
65836
+ error: "error",
65837
+ timeout: "timed out"
65838
+ };
65839
+ const CLOSE_COLOR = {
65840
+ complete: "text-emerald-400",
65841
+ stopped: "text-surface-400",
65842
+ error: "text-red-400",
65843
+ timeout: "text-amber-400"
65844
+ };
65845
+ function eventName(ev) {
65846
+ if (ev.kind === "sse") return ev.name ?? "message";
65847
+ if (ev.kind === "ndjson") return "json";
65848
+ return null;
65849
+ }
65850
+ function preview(ev) {
65851
+ if (ev.json !== void 0) {
65852
+ try {
65853
+ return JSON.stringify(ev.json);
65854
+ } catch {
65855
+ }
65856
+ }
65857
+ return ev.data;
65858
+ }
65859
+ function StreamView({ events, streaming, streamId, streamClose, firstEventMs }) {
65860
+ const [mode, setMode] = reactExports.useState("events");
65861
+ const [stopping, setStopping] = reactExports.useState(false);
65862
+ const bottomRef = reactExports.useRef(null);
65863
+ reactExports.useEffect(() => {
65864
+ if (streaming && mode === "events") bottomRef.current?.scrollIntoView({ block: "nearest" });
65865
+ }, [events.length, streaming, mode]);
65866
+ const shown = events.length > RENDER_TAIL ? events.slice(-RENDER_TAIL) : events;
65867
+ const firstMs = firstEventMs ?? events[0]?.tMs;
65868
+ async function stop() {
65869
+ if (!streamId) return;
65870
+ setStopping(true);
65871
+ try {
65872
+ await electron$l.stopStream(streamId);
65873
+ } catch {
65874
+ }
65875
+ }
65876
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col min-h-0", children: [
65877
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-3 py-1.5 border-b border-surface-800 text-[11px] shrink-0", children: [
65878
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1.5", children: streaming ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
65879
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-emerald-400 animate-pulse" }),
65880
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400 font-medium", children: "streaming" })
65881
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
65882
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-2 h-2 rounded-full ${streamClose === "error" ? "bg-red-400" : streamClose === "timeout" ? "bg-amber-400" : "bg-surface-500"}` }),
65883
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: streamClose ? CLOSE_COLOR[streamClose] : "text-surface-400", children: streamClose ? CLOSE_LABEL[streamClose] : "closed" })
65884
+ ] }) }),
65885
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-400 font-mono", children: [
65886
+ events.length,
65887
+ " ",
65888
+ events.length === 1 ? "event" : "events"
65889
+ ] }),
65890
+ firstMs !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-500 font-mono", title: "Time to first event", children: [
65891
+ "first +",
65892
+ firstMs,
65893
+ "ms"
65894
+ ] }),
65895
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
65896
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded border border-surface-700 overflow-hidden", children: [
65897
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
65898
+ "button",
65899
+ {
65900
+ onClick: () => setMode("events"),
65901
+ className: `px-2 py-0.5 text-[10px] transition-colors ${mode === "events" ? "bg-surface-700 text-white" : "text-surface-500 hover:text-white"}`,
65902
+ children: "Events"
65903
+ }
65904
+ ),
65905
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
65906
+ "button",
65907
+ {
65908
+ onClick: () => setMode("merged"),
65909
+ className: `px-2 py-0.5 text-[10px] transition-colors ${mode === "merged" ? "bg-surface-700 text-white" : "text-surface-500 hover:text-white"}`,
65910
+ children: "Merged"
65911
+ }
65912
+ )
65913
+ ] }),
65914
+ streaming && streamId && /* @__PURE__ */ jsxRuntimeExports.jsx(
65915
+ "button",
65916
+ {
65917
+ onClick: stop,
65918
+ disabled: stopping,
65919
+ className: "px-2 py-0.5 text-[10px] rounded bg-red-900/40 text-red-300 hover:bg-red-900/60 disabled:opacity-50 transition-colors",
65920
+ children: stopping ? "Stopping…" : "Stop"
65921
+ }
65922
+ )
65923
+ ] })
65924
+ ] }),
65925
+ events.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center text-surface-500 text-xs", children: streaming ? "Waiting for the first event…" : "No events received." }) : mode === "merged" ? /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "flex-1 overflow-auto m-0 px-3 py-2 text-[11px] font-mono text-surface-300 whitespace-pre-wrap break-words", children: shown.map((e) => e.data).join("\n") }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-auto min-h-0", children: [
65926
+ events.length > RENDER_TAIL && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "px-3 py-1 text-[10px] text-surface-500 bg-surface-900/50 sticky top-0", children: [
65927
+ "showing the last ",
65928
+ RENDER_TAIL,
65929
+ " of ",
65930
+ events.length,
65931
+ " events"
65932
+ ] }),
65933
+ shown.map((ev) => {
65934
+ const name2 = eventName(ev);
65935
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline gap-2 px-3 py-1 border-b border-surface-800/60 hover:bg-surface-800/40", children: [
65936
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-mono text-surface-600 w-10 shrink-0 text-right tabular-nums", children: [
65937
+ "#",
65938
+ ev.seq
65939
+ ] }),
65940
+ name2 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-mono px-1 py-px rounded bg-surface-700/60 text-surface-300 shrink-0", children: name2 }),
65941
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] font-mono text-surface-600 shrink-0 tabular-nums", title: "Time since request start", children: [
65942
+ "+",
65943
+ ev.tMs,
65944
+ "ms"
65945
+ ] }),
65946
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] font-mono text-surface-200 truncate", title: ev.data, children: preview(ev) })
65947
+ ] }, ev.seq);
65948
+ }),
65949
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: bottomRef })
65950
+ ] })
65951
+ ] });
65952
+ }
64915
65953
  const HOOK_BADGE$1 = {
64916
65954
  beforeAll: { label: "BEFORE ALL", cls: "bg-violet-700 text-white" },
64917
65955
  before: { label: "BEFORE", cls: "bg-violet-600 text-white" },
@@ -64993,7 +66031,7 @@ function HookResultsPanel({ results }) {
64993
66031
  }) })
64994
66032
  ] });
64995
66033
  }
64996
- const { electron: electron$j } = window;
66034
+ const { electron: electron$k } = window;
64997
66035
  function extractPath(url) {
64998
66036
  try {
64999
66037
  return new URL(url).pathname || "/";
@@ -65047,14 +66085,14 @@ function SaveAsMockModal({ onClose }) {
65047
66085
  const entry = state.mocks[serverId];
65048
66086
  const updated = { ...entry.data, name: newServerName, port: Number(newServerPort), routes: [route] };
65049
66087
  updateMock(serverId, updated);
65050
- await electron$j.saveMock(entry.relPath, updated);
66088
+ await electron$k.saveMock(entry.relPath, updated);
65051
66089
  const ws2 = useStore.getState().workspace;
65052
- if (ws2) await electron$j.saveWorkspace(ws2);
66090
+ if (ws2) await electron$k.saveWorkspace(ws2);
65053
66091
  } else {
65054
66092
  const entry = useStore.getState().mocks[serverId];
65055
66093
  const updated = { ...entry.data, routes: [...entry.data.routes, route] };
65056
66094
  updateMock(serverId, updated);
65057
- await electron$j.saveMock(entry.relPath, updated);
66095
+ await electron$k.saveMock(entry.relPath, updated);
65058
66096
  }
65059
66097
  onClose();
65060
66098
  } finally {
@@ -65481,7 +66519,7 @@ function validateHttpSemantics(res, opts = {}) {
65481
66519
  const order = { error: 0, warning: 1, hint: 2 };
65482
66520
  return f.sort((a, b) => order[a.severity] - order[b.severity]);
65483
66521
  }
65484
- const { electron: electron$i } = window;
66522
+ const { electron: electron$j } = window;
65485
66523
  function xmlWellFormed(body) {
65486
66524
  try {
65487
66525
  const doc2 = new DOMParser().parseFromString(body, "application/xml");
@@ -65573,6 +66611,8 @@ function ResponseViewer() {
65573
66611
  const setTabRequestTab = useStore((s) => s.setTabRequestTab);
65574
66612
  const setTabScriptTab = useStore((s) => s.setTabScriptTab);
65575
66613
  const isSending = activeTab?.isSending ?? false;
66614
+ const liveStream = useStore((s) => s.liveStream);
66615
+ const streamForTab = liveStream && liveStream.tabId === activeTabId ? liveStream : null;
65576
66616
  const response = activeTab?.lastResponse ?? null;
65577
66617
  const scriptResult = activeTab?.lastScriptResult ?? null;
65578
66618
  const sentRequest = activeTab?.lastSentRequest ?? null;
@@ -65617,7 +66657,7 @@ function ResponseViewer() {
65617
66657
  const contractToast = useToast(2500);
65618
66658
  async function saveAsContract() {
65619
66659
  if (!response || !requestId || !activeTabId) return;
65620
- const schema = response.body ? await electron$i.inferContractSchema(response.body) : null;
66660
+ const schema = response.body ? await electron$j.inferContractSchema(response.body) : null;
65621
66661
  const contentType2 = response.headers["content-type"];
65622
66662
  const headers = contentType2 ? [{ key: "content-type", value: contentType2, required: true }] : [];
65623
66663
  updateRequest(requestId, {
@@ -65645,6 +66685,16 @@ function ResponseViewer() {
65645
66685
  assertToast.show("✓ Assertion added", true);
65646
66686
  }
65647
66687
  if (isSending) {
66688
+ if (streamForTab && (streamForTab.streaming || streamForTab.events.length > 0)) {
66689
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
66690
+ StreamView,
66691
+ {
66692
+ events: streamForTab.events,
66693
+ streaming: streamForTab.streaming,
66694
+ streamId: streamForTab.streamId
66695
+ }
66696
+ );
66697
+ }
65648
66698
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full flex items-center justify-center text-surface-400 text-sm", children: "Sending..." });
65649
66699
  }
65650
66700
  if (!response) {
@@ -65723,7 +66773,7 @@ function ResponseViewer() {
65723
66773
  !response.error && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-1 shrink-0", children: [
65724
66774
  assertToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-emerald-400 font-medium px-1", children: assertToast.toast.msg }),
65725
66775
  contractToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-blue-400 font-medium px-1", children: contractToast.toast.msg }),
65726
- tab === "body" && supportsTree && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded overflow-hidden border border-surface-800 mr-1", children: [
66776
+ tab === "body" && supportsTree && !response.streamed && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded overflow-hidden border border-surface-800 mr-1", children: [
65727
66777
  /* @__PURE__ */ jsxRuntimeExports.jsx(
65728
66778
  "button",
65729
66779
  {
@@ -65785,7 +66835,15 @@ function ResponseViewer() {
65785
66835
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 flex flex-col overflow-y-auto", children: response.error ? tab === "request" ? /* @__PURE__ */ jsxRuntimeExports.jsx(RequestPanel, { sentRequest }) : tab === "history" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: historyContent }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col p-4 gap-2", children: [
65786
66836
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-red-400 text-sm font-medium", children: "Request failed" }),
65787
66837
  /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-red-300 whitespace-pre-wrap", children: response.error })
65788
- ] }) : diffMode && pinnedResponse ? /* @__PURE__ */ jsxRuntimeExports.jsx(DiffView, { pinned: pinnedResponse, current: response }) : tab === "body" && supportsTree && bodyView === "tree" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
66838
+ ] }) : diffMode && pinnedResponse ? /* @__PURE__ */ jsxRuntimeExports.jsx(DiffView, { pinned: pinnedResponse, current: response }) : tab === "body" && response.streamed ? /* @__PURE__ */ jsxRuntimeExports.jsx(
66839
+ StreamView,
66840
+ {
66841
+ events: response.events ?? [],
66842
+ streaming: false,
66843
+ streamClose: response.streamClose,
66844
+ firstEventMs: response.firstEventMs
66845
+ }
66846
+ ) : tab === "body" && supportsTree && bodyView === "tree" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
65789
66847
  InteractiveBody,
65790
66848
  {
65791
66849
  body: response.body,
@@ -65907,7 +66965,7 @@ function ResponseViewer() {
65907
66965
  ] }) })
65908
66966
  ] });
65909
66967
  }
65910
- const { electron: electron$h } = window;
66968
+ const { electron: electron$i } = window;
65911
66969
  const TARGETS = [
65912
66970
  { id: "robot_framework", label: "Robot Framework", description: "Python RequestsLibrary keywords + test suite" },
65913
66971
  { id: "playwright_ts", label: "Playwright TS", description: "TypeScript page-object API classes + spec files" },
@@ -65948,7 +67006,7 @@ function GeneratorPanel() {
65948
67006
  try {
65949
67007
  const col = collections[selectedCollectionId]?.data;
65950
67008
  const env = resolveEnvironmentById(environments, activeEnvironmentId);
65951
- const generated = await electron$h.generateCode({ collection: col, environment: env, target });
67009
+ const generated = await electron$i.generateCode({ collection: col, environment: env, target });
65952
67010
  setFiles(generated);
65953
67011
  setSelectedFile(generated[0]?.path ?? null);
65954
67012
  } catch (e) {
@@ -65960,7 +67018,7 @@ function GeneratorPanel() {
65960
67018
  async function saveZip() {
65961
67019
  if (files.length === 0) return;
65962
67020
  const col = collections[selectedCollectionId]?.data;
65963
- await electron$h.saveGeneratedFilesAsZip(files, col?.name ?? "api-tests", target);
67021
+ await electron$i.saveGeneratedFilesAsZip(files, col?.name ?? "api-tests", target);
65964
67022
  }
65965
67023
  const selectedContent = files.find((f) => f.path === selectedFile)?.content ?? "";
65966
67024
  const activeTarget = TARGETS.find((t2) => t2.id === target);
@@ -66144,7 +67202,7 @@ function historyToHar(entries, creatorVersion = "1.0") {
66144
67202
  }
66145
67203
  }, null, 2);
66146
67204
  }
66147
- const { electron: electron$g } = window;
67205
+ const { electron: electron$h } = window;
66148
67206
  const STATUS_COLOR = {
66149
67207
  "2": "text-emerald-400",
66150
67208
  "3": "text-amber-400",
@@ -66192,7 +67250,7 @@ function HistoryPanel() {
66192
67250
  }
66193
67251
  async function downloadHar() {
66194
67252
  const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[:T]/g, "-");
66195
- await electron$g.saveResults(historyToHar(history2), `api-spector-history-${stamp}.har`);
67253
+ await electron$h.saveResults(historyToHar(history2), `api-spector-history-${stamp}.har`);
66196
67254
  }
66197
67255
  function open(entry) {
66198
67256
  setSelected(entry);
@@ -66323,30 +67381,30 @@ function HistoryRow({
66323
67381
  }
66324
67382
  );
66325
67383
  }
66326
- const { electron: electron$f } = window;
67384
+ const { electron: electron$g } = window;
66327
67385
  function WelcomeScreen() {
66328
67386
  const { applyWorkspace } = useWorkspaceLoader();
66329
67387
  const [recents, setRecents] = reactExports.useState([]);
66330
67388
  const [update, setUpdate] = reactExports.useState(null);
66331
67389
  reactExports.useEffect(() => {
66332
- electron$f.getRecentWorkspaces().then(setRecents).catch(() => setRecents([]));
66333
- electron$f.checkForUpdate().then((info) => {
67390
+ electron$g.getRecentWorkspaces().then(setRecents).catch(() => setRecents([]));
67391
+ electron$g.checkForUpdate().then((info) => {
66334
67392
  if (info?.updateAvailable) setUpdate(info);
66335
67393
  }).catch(() => {
66336
67394
  });
66337
67395
  }, []);
66338
67396
  async function openWorkspace() {
66339
- const result = await electron$f.openWorkspace();
67397
+ const result = await electron$g.openWorkspace();
66340
67398
  if (!result) return;
66341
67399
  await applyWorkspace(result.workspace, result.workspacePath);
66342
67400
  }
66343
67401
  async function newWorkspace() {
66344
- const result = await electron$f.newWorkspace();
67402
+ const result = await electron$g.newWorkspace();
66345
67403
  if (!result) return;
66346
67404
  await applyWorkspace(result.workspace, result.workspacePath);
66347
67405
  }
66348
67406
  async function openRecent(path) {
66349
- const result = await electron$f.openWorkspacePath(path);
67407
+ const result = await electron$g.openWorkspacePath(path);
66350
67408
  if (!result) {
66351
67409
  setRecents((prev) => prev.filter((r) => r.path !== path));
66352
67410
  return;
@@ -66386,7 +67444,7 @@ function WelcomeScreen() {
66386
67444
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-sm max-w-sm", children: "Local-first API testing with Robot Framework & Playwright code generation. Secrets stay on your machine." }),
66387
67445
  /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: [
66388
67446
  "version ",
66389
- "0.4.6"
67447
+ "0.4.8"
66390
67448
  ] })
66391
67449
  ] }),
66392
67450
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 w-64", children: [
@@ -66430,7 +67488,7 @@ function WelcomeScreen() {
66430
67488
  ] })
66431
67489
  ] });
66432
67490
  }
66433
- const { electron: electron$e } = window;
67491
+ const { electron: electron$f } = window;
66434
67492
  const EXAMPLES = [
66435
67493
  {
66436
67494
  label: "macOS / Linux (~/.zshrc or ~/.bashrc)",
@@ -66454,7 +67512,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
66454
67512
  setError("Password cannot be empty.");
66455
67513
  return;
66456
67514
  }
66457
- await electron$e.setMasterKey(password);
67515
+ await electron$f.setMasterKey(password);
66458
67516
  onSuccess(password);
66459
67517
  }
66460
67518
  function copy(idx, text) {
@@ -66538,7 +67596,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
66538
67596
  }
66539
67597
  );
66540
67598
  }
66541
- const { electron: electron$d } = window;
67599
+ const { electron: electron$e } = window;
66542
67600
  async function shortHash(value) {
66543
67601
  const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
66544
67602
  return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 8);
@@ -66665,7 +67723,7 @@ function EnvironmentEditor({ onClose }) {
66665
67723
  async function saveEncrypted(idx) {
66666
67724
  const plaintext = secretInputs[idx] ?? "";
66667
67725
  if (!plaintext) return;
66668
- const { set: set2 } = await electron$d.checkMasterKey();
67726
+ const { set: set2 } = await electron$e.checkMasterKey();
66669
67727
  if (!set2) {
66670
67728
  setPendingEncryptIdx(idx);
66671
67729
  return;
@@ -66702,9 +67760,9 @@ function EnvironmentEditor({ onClose }) {
66702
67760
  } : state.workspace
66703
67761
  }));
66704
67762
  const ws2 = useStore.getState().workspace;
66705
- if (ws2) await electron$d.saveWorkspace(ws2);
67763
+ if (ws2) await electron$e.saveWorkspace(ws2);
66706
67764
  }
66707
- await electron$d.saveEnvironment(newRelPath, env);
67765
+ await electron$e.saveEnvironment(newRelPath, env);
66708
67766
  }
66709
67767
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
66710
67768
  pendingEncryptIdx !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -67014,7 +68072,7 @@ function EnvironmentEditor({ onClose }) {
67014
68072
  )
67015
68073
  ] });
67016
68074
  }
67017
- const { electron: electron$c } = window;
68075
+ const { electron: electron$d } = window;
67018
68076
  function EnvironmentBar({ inline = false }) {
67019
68077
  const environments = useStore((s) => s.environments);
67020
68078
  const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
@@ -67029,7 +68087,7 @@ function EnvironmentBar({ inline = false }) {
67029
68087
  if (id2) {
67030
68088
  const hasSecrets = environments[id2]?.data.variables.some((v) => v.enabled && v.secret);
67031
68089
  if (hasSecrets) {
67032
- const { set: set2 } = await electron$c.checkMasterKey();
68090
+ const { set: set2 } = await electron$d.checkMasterKey();
67033
68091
  if (!set2) {
67034
68092
  setPendingEnvId(id2);
67035
68093
  return;
@@ -67083,7 +68141,7 @@ function EnvironmentBar({ inline = false }) {
67083
68141
  controls
67084
68142
  ] });
67085
68143
  }
67086
- const { electron: electron$b } = window;
68144
+ const { electron: electron$c } = window;
67087
68145
  const DEFAULT_PII_PATTERNS = ["authorization", "password", "token", "secret", "api-key", "x-api-key"];
67088
68146
  const ZOOM_STEPS = [0.75, 0.9, 1, 1.1, 1.25, 1.5];
67089
68147
  function WorkspaceSettingsModal({ onClose }) {
@@ -67109,6 +68167,20 @@ function WorkspaceSettingsModal({ onClose }) {
67109
68167
  // default true
67110
68168
  );
67111
68169
  const [dashboardUrl, setDashboardUrl] = reactExports.useState(existing.dashboardUrl ?? "");
68170
+ const [cloudEnabled2, setCloudEnabled] = reactExports.useState(existing.cloud?.enabled ?? false);
68171
+ const [cloudToken, setCloudToken] = reactExports.useState("");
68172
+ const cloudTokenSet = existing.cloud?.tokenSet ?? false;
68173
+ const [cloudTest, setCloudTest] = reactExports.useState({ status: "idle" });
68174
+ async function testCloud() {
68175
+ setCloudTest({ status: "testing" });
68176
+ try {
68177
+ if (cloudToken.trim()) await electron$c.setSecret("cloud:token", cloudToken.trim());
68178
+ const me = await electron$c.cloudTest();
68179
+ setCloudTest({ status: "ok", msg: `Connected as ${me.email} · ${me.organization} (${me.plan})` });
68180
+ } catch (e) {
68181
+ setCloudTest({ status: "err", msg: e.message });
68182
+ }
68183
+ }
67112
68184
  const [patterns, setPatterns] = reactExports.useState(
67113
68185
  existing.piiMaskPatterns ?? DEFAULT_PII_PATTERNS
67114
68186
  );
@@ -67142,15 +68214,24 @@ function WorkspaceSettingsModal({ onClose }) {
67142
68214
  settings.piiMaskPatterns = patterns;
67143
68215
  if (dashboardUrl.trim()) settings.dashboardUrl = dashboardUrl.trim();
67144
68216
  else delete settings.dashboardUrl;
68217
+ if (cloudEnabled2 || cloudTokenSet || cloudToken.trim()) {
68218
+ settings.cloud = {
68219
+ enabled: cloudEnabled2,
68220
+ tokenSet: cloudTokenSet || Boolean(cloudToken.trim())
68221
+ };
68222
+ } else {
68223
+ delete settings.cloud;
68224
+ }
68225
+ if (cloudToken.trim()) await electron$c.setSecret("cloud:token", cloudToken.trim());
67145
68226
  if (defaultEnvironment) settings.defaultEnvironment = defaultEnvironment;
67146
68227
  else delete settings.defaultEnvironment;
67147
68228
  if (persistHistory) settings.persistHistory = true;
67148
68229
  else delete settings.persistHistory;
67149
68230
  updateWorkspaceSettings(settings);
67150
68231
  const updated = useStore.getState().workspace;
67151
- if (updated) await electron$b.saveWorkspace(updated);
68232
+ if (updated) await electron$c.saveWorkspace(updated);
67152
68233
  if (persistHistory) {
67153
- await electron$b.saveHistory(useStore.getState().history).catch(() => {
68234
+ await electron$c.saveHistory(useStore.getState().history).catch(() => {
67154
68235
  });
67155
68236
  }
67156
68237
  onClose();
@@ -67166,7 +68247,8 @@ function WorkspaceSettingsModal({ onClose }) {
67166
68247
  { id: "proxy", label: "Proxy" },
67167
68248
  { id: "tls", label: "TLS / Certificates" },
67168
68249
  { id: "privacy", label: "Privacy" },
67169
- { id: "contracts", label: "Contracts" }
68250
+ { id: "contracts", label: "Contracts" },
68251
+ { id: "cloud", label: "Cloud" }
67170
68252
  ];
67171
68253
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
67172
68254
  Modal,
@@ -67323,6 +68405,56 @@ function WorkspaceSettingsModal({ onClose }) {
67323
68405
  ] }),
67324
68406
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[11px]", children: 'Where your contract dashboard is served (api-spector contract report --serve, locally or as a docker container). Adds an "Open dashboard" link to the contract results panel. The link is view-only: recorded results reach the dashboard through the workspace files, not through this URL.' })
67325
68407
  ] }),
68408
+ activeTab === "cloud" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
68409
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-start gap-2 cursor-pointer", children: [
68410
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
68411
+ "input",
68412
+ {
68413
+ type: "checkbox",
68414
+ checked: cloudEnabled2,
68415
+ onChange: (e) => setCloudEnabled(e.target.checked),
68416
+ className: "mt-0.5 accent-blue-500"
68417
+ }
68418
+ ),
68419
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex flex-col gap-0.5", children: [
68420
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-200", children: "Enable API Spector Cloud" }),
68421
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600 text-[11px]", children: 'Push mocks and monitors to a hosted instance. When on, "Push to cloud" actions appear on mocks and requests.' })
68422
+ ] })
68423
+ ] }),
68424
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
68425
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "API token" }),
68426
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
68427
+ "input",
68428
+ {
68429
+ type: "password",
68430
+ value: cloudToken,
68431
+ onChange: (e) => setCloudToken(e.target.value),
68432
+ placeholder: cloudTokenSet ? "•••••••• (saved — type to replace)" : "Paste a token from the cloud dashboard",
68433
+ className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
68434
+ }
68435
+ ),
68436
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600 text-[11px]", children: "Stored in your OS keychain, never in the workspace file. Create one under Tokens in the cloud dashboard." })
68437
+ ] }),
68438
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
68439
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
68440
+ "button",
68441
+ {
68442
+ onClick: testCloud,
68443
+ disabled: cloudTest.status === "testing",
68444
+ className: "px-3 py-1.5 bg-surface-700 hover:bg-surface-600 disabled:opacity-40 rounded transition-colors",
68445
+ children: cloudTest.status === "testing" ? "Testing…" : "Test connection"
68446
+ }
68447
+ ),
68448
+ cloudTest.status === "ok" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-green-400 text-[11px]", children: [
68449
+ "✓ ",
68450
+ cloudTest.msg
68451
+ ] }),
68452
+ cloudTest.status === "err" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-400 text-[11px]", children: [
68453
+ "✗ ",
68454
+ cloudTest.msg
68455
+ ] })
68456
+ ] })
68457
+ ] }),
67326
68458
  activeTab === "tls" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
67327
68459
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
67328
68460
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "CA Certificate path" }),
@@ -67441,7 +68573,7 @@ function WorkspaceSettingsModal({ onClose }) {
67441
68573
  }
67442
68574
  );
67443
68575
  }
67444
- const { electron: electron$a } = window;
68576
+ const { electron: electron$b } = window;
67445
68577
  function DocsGeneratorModal({ onClose }) {
67446
68578
  const collections = useStore((s) => s.collections);
67447
68579
  const collectionList = Object.values(collections);
@@ -67450,7 +68582,7 @@ function DocsGeneratorModal({ onClose }) {
67450
68582
  );
67451
68583
  const [format2, setFormat] = reactExports.useState("markdown");
67452
68584
  const [generating, setGenerating] = reactExports.useState(false);
67453
- const [preview, setPreview] = reactExports.useState(null);
68585
+ const [preview2, setPreview] = reactExports.useState(null);
67454
68586
  const [error2, setError] = reactExports.useState(null);
67455
68587
  function toggleCollection(id2) {
67456
68588
  setSelectedIds((prev) => {
@@ -67481,9 +68613,9 @@ function DocsGeneratorModal({ onClose }) {
67481
68613
  setGenerating(true);
67482
68614
  setError(null);
67483
68615
  try {
67484
- const content2 = await electron$a.generateDocs(buildPayload());
68616
+ const content2 = await electron$b.generateDocs(buildPayload());
67485
68617
  const filename = format2 === "html" ? "api-docs.html" : "api-docs.md";
67486
- await electron$a.saveResults(content2, filename);
68618
+ await electron$b.saveResults(content2, filename);
67487
68619
  } catch (err) {
67488
68620
  setError(err instanceof Error ? err.message : String(err));
67489
68621
  } finally {
@@ -67494,7 +68626,7 @@ function DocsGeneratorModal({ onClose }) {
67494
68626
  setGenerating(true);
67495
68627
  setError(null);
67496
68628
  try {
67497
- const content2 = await electron$a.generateDocs(buildPayload());
68629
+ const content2 = await electron$b.generateDocs(buildPayload());
67498
68630
  setPreview(content2);
67499
68631
  } catch (err) {
67500
68632
  setError(err instanceof Error ? err.message : String(err));
@@ -67562,7 +68694,7 @@ function DocsGeneratorModal({ onClose }) {
67562
68694
  ] }, f)) })
67563
68695
  ] }),
67564
68696
  error2 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-400", children: error2 }),
67565
- preview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
68697
+ preview2 !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
67566
68698
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
67567
68699
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] font-semibold uppercase tracking-wider text-surface-600", children: "Preview" }),
67568
68700
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -67577,7 +68709,7 @@ function DocsGeneratorModal({ onClose }) {
67577
68709
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded overflow-hidden border border-surface-700", style: { height: 280 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
67578
68710
  ReactCodeMirror,
67579
68711
  {
67580
- value: preview,
68712
+ value: preview2,
67581
68713
  height: "280px",
67582
68714
  theme: oneDark,
67583
68715
  extensions: [],
@@ -67843,7 +68975,7 @@ function parseCurl(command2) {
67843
68975
  }
67844
68976
  return { name: name2, method: resolvedMethod, url, headers, params: [], auth, body };
67845
68977
  }
67846
- const { electron: electron$9 } = window;
68978
+ const { electron: electron$a } = window;
67847
68979
  const OPTIONS = [
67848
68980
  { id: "postman", label: "Postman", description: "Collection v2.1 JSON" },
67849
68981
  { id: "openapi", label: "OpenAPI", description: "JSON or YAML (v3.x)", supportsUrl: true },
@@ -67918,12 +69050,12 @@ function ImportModal({ onImport, onClose }) {
67918
69050
  setError(null);
67919
69051
  try {
67920
69052
  let col = null;
67921
- if (opt2.id === "postman") col = await electron$9.importPostman();
67922
- if (opt2.id === "openapi") col = await electron$9.importOpenApi();
67923
- if (opt2.id === "insomnia") col = await electron$9.importInsomnia();
67924
- if (opt2.id === "bruno") col = await electron$9.importBruno();
67925
- if (opt2.id === "http") col = await electron$9.importHttpFile();
67926
- if (opt2.id === "spector") col = await electron$9.importSpectorCollection();
69053
+ if (opt2.id === "postman") col = await electron$a.importPostman();
69054
+ if (opt2.id === "openapi") col = await electron$a.importOpenApi();
69055
+ if (opt2.id === "insomnia") col = await electron$a.importInsomnia();
69056
+ if (opt2.id === "bruno") col = await electron$a.importBruno();
69057
+ if (opt2.id === "http") col = await electron$a.importHttpFile();
69058
+ if (opt2.id === "spector") col = await electron$a.importSpectorCollection();
67927
69059
  if (!col) {
67928
69060
  setLoading(false);
67929
69061
  return;
@@ -67946,7 +69078,7 @@ function ImportModal({ onImport, onClose }) {
67946
69078
  setLoading(true);
67947
69079
  setError(null);
67948
69080
  try {
67949
- const col = await electron$9.importOpenApiFromUrl(trimmed);
69081
+ const col = await electron$a.importOpenApiFromUrl(trimmed);
67950
69082
  if (col) enterPreview(col);
67951
69083
  } catch (err) {
67952
69084
  setError(err instanceof Error ? err.message : String(err));
@@ -68053,7 +69185,7 @@ function ImportModal({ onImport, onClose }) {
68053
69185
  mergeIntoCollection(target, prunedRoot, prunedRequests);
68054
69186
  const entry = useStore.getState().collections[target];
68055
69187
  if (entry) {
68056
- await electron$9.saveCollection(entry.relPath, entry.data);
69188
+ await electron$a.saveCollection(entry.relPath, entry.data);
68057
69189
  markCollectionClean(target);
68058
69190
  }
68059
69191
  setActiveCollection(target);
@@ -68080,7 +69212,7 @@ function ImportModal({ onImport, onClose }) {
68080
69212
  variables: [{ key: name2, value, enabled: true }]
68081
69213
  };
68082
69214
  const relPath = envRelPath(finalName, envId);
68083
- await electron$9.saveEnvironment(relPath, env);
69215
+ await electron$a.saveEnvironment(relPath, env);
68084
69216
  useStore.setState((s) => {
68085
69217
  s.environments[envId] = { relPath, data: env };
68086
69218
  if (!s.activeEnvironmentId) s.activeEnvironmentId = envId;
@@ -68090,7 +69222,7 @@ function ImportModal({ onImport, onClose }) {
68090
69222
  return s;
68091
69223
  });
68092
69224
  const ws2 = useStore.getState().workspace;
68093
- if (ws2) await electron$9.saveWorkspace(ws2);
69225
+ if (ws2) await electron$a.saveWorkspace(ws2);
68094
69226
  } else {
68095
69227
  const entry = state.environments[envTarget];
68096
69228
  if (!entry) throw new Error("Target environment not found");
@@ -68105,7 +69237,7 @@ function ImportModal({ onImport, onClose }) {
68105
69237
  } else {
68106
69238
  updated.variables = [...updated.variables, { key: name2, value, enabled: true }];
68107
69239
  }
68108
- await electron$9.saveEnvironment(entry.relPath, updated);
69240
+ await electron$a.saveEnvironment(entry.relPath, updated);
68109
69241
  useStore.getState().updateEnvironment(envTarget, updated);
68110
69242
  }
68111
69243
  }
@@ -68481,7 +69613,7 @@ function collectRequestsByFolder(folder, src) {
68481
69613
  walk(folder);
68482
69614
  return out;
68483
69615
  }
68484
- const { electron: electron$8 } = window;
69616
+ const { electron: electron$9 } = window;
68485
69617
  function Toolbar({ onOpenDocs: _onOpenDocs }) {
68486
69618
  const { applyWorkspace } = useWorkspaceLoader();
68487
69619
  const workspace = useStore((s) => s.workspace);
@@ -68504,13 +69636,13 @@ function Toolbar({ onOpenDocs: _onOpenDocs }) {
68504
69636
  try {
68505
69637
  for (const { relPath, data, dirty } of Object.values(collections)) {
68506
69638
  if (!dirty) continue;
68507
- await electron$8.saveCollection(relPath, data);
69639
+ await electron$9.saveCollection(relPath, data);
68508
69640
  markCollectionClean(data.id);
68509
69641
  }
68510
69642
  for (const { relPath, data } of Object.values(environments)) {
68511
- await electron$8.saveEnvironment(relPath, data);
69643
+ await electron$9.saveEnvironment(relPath, data);
68512
69644
  }
68513
- if (workspace) await electron$8.saveWorkspace(workspace);
69645
+ if (workspace) await electron$9.saveWorkspace(workspace);
68514
69646
  } finally {
68515
69647
  setSaving(false);
68516
69648
  }
@@ -68518,14 +69650,14 @@ function Toolbar({ onOpenDocs: _onOpenDocs }) {
68518
69650
  async function afterImport(col) {
68519
69651
  if (!col) return;
68520
69652
  const relPath = colRelPath(col.name, col.id);
68521
- await electron$8.saveCollection(relPath, col);
69653
+ await electron$9.saveCollection(relPath, col);
68522
69654
  loadCollection(relPath, col);
68523
69655
  setActiveCollection(col.id);
68524
69656
  const ws2 = useStore.getState().workspace;
68525
69657
  if (ws2 && !ws2.collections.includes(relPath)) {
68526
69658
  const updated = { ...ws2, collections: [...ws2.collections, relPath] };
68527
69659
  useStore.setState({ workspace: updated });
68528
- await electron$8.saveWorkspace(updated);
69660
+ await electron$9.saveWorkspace(updated);
68529
69661
  }
68530
69662
  }
68531
69663
  if (!workspace) return null;
@@ -68614,7 +69746,7 @@ function Toolbar({ onOpenDocs: _onOpenDocs }) {
68614
69746
  "button",
68615
69747
  {
68616
69748
  onClick: async () => {
68617
- const result = await electron$8.openWorkspace();
69749
+ const result = await electron$9.openWorkspace();
68618
69750
  if (result) await applyWorkspace(result.workspace, result.workspacePath);
68619
69751
  },
68620
69752
  className: "px-2.5 py-1 text-xs bg-surface-800 hover:bg-surface-700 rounded transition-colors",
@@ -68626,7 +69758,7 @@ function Toolbar({ onOpenDocs: _onOpenDocs }) {
68626
69758
  "button",
68627
69759
  {
68628
69760
  onClick: async () => {
68629
- const result = await electron$8.newWorkspace();
69761
+ const result = await electron$9.newWorkspace();
68630
69762
  if (result) await applyWorkspace(result.workspace, result.workspacePath);
68631
69763
  },
68632
69764
  className: "px-2.5 py-1 text-xs bg-surface-800 hover:bg-surface-700 rounded transition-colors",
@@ -68638,7 +69770,7 @@ function Toolbar({ onOpenDocs: _onOpenDocs }) {
68638
69770
  "button",
68639
69771
  {
68640
69772
  onClick: async () => {
68641
- await electron$8.closeWorkspace();
69773
+ await electron$9.closeWorkspace();
68642
69774
  closeWorkspace();
68643
69775
  },
68644
69776
  className: "px-2.5 py-1 text-xs bg-surface-800 hover:bg-surface-700 rounded transition-colors",
@@ -69089,7 +70221,7 @@ api-tests:
69089
70221
  function EmptyState({ message }) {
69090
70222
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-24 text-surface-400 text-xs", children: message });
69091
70223
  }
69092
- const { electron: electron$7 } = window;
70224
+ const { electron: electron$8 } = window;
69093
70225
  const HOOK_BADGE = {
69094
70226
  beforeAll: { label: "BEFORE ALL", cls: "bg-violet-700 text-white" },
69095
70227
  before: { label: "BEFORE", cls: "bg-violet-600 text-white" },
@@ -69209,13 +70341,13 @@ function RunnerModal() {
69209
70341
  setSummary(null);
69210
70342
  setRunnerRunning(true);
69211
70343
  progressIdxRef.current = 0;
69212
- electron$7.onRunProgress((result) => {
70344
+ electron$8.onRunProgress((result) => {
69213
70345
  const idx = progressIdxRef.current;
69214
70346
  patchRunnerResult(idx, result);
69215
70347
  if (result.status !== "running") progressIdxRef.current++;
69216
70348
  });
69217
70349
  try {
69218
- const s = await electron$7.runCollection({
70350
+ const s = await electron$8.runCollection({
69219
70351
  items: items2,
69220
70352
  environment: env,
69221
70353
  globals,
@@ -69226,7 +70358,7 @@ function RunnerModal() {
69226
70358
  });
69227
70359
  setSummary(s);
69228
70360
  } finally {
69229
- electron$7.offRunProgress();
70361
+ electron$8.offRunProgress();
69230
70362
  setRunnerRunning(false);
69231
70363
  }
69232
70364
  }, [collectionId, folderId, filterTags, selectedEnvId, environments, globals, colEntry, requestDelay, workspaceSettings, setRunnerResults, patchRunnerResult, setRunnerRunning]);
@@ -69452,7 +70584,7 @@ function RunnerModal() {
69452
70584
  };
69453
70585
  const content2 = exportFormat === "junit" ? buildJUnitReport(runnerResults, summary, meta2) : exportFormat === "html" ? buildHtmlReport(runnerResults, summary, meta2) : buildJsonReport(runnerResults, summary, meta2);
69454
70586
  const ext = exportFormat === "junit" ? "xml" : exportFormat === "html" ? "html" : "json";
69455
- electron$7.saveResults(content2, `spector-results.${ext}`);
70587
+ electron$8.saveResults(content2, `spector-results.${ext}`);
69456
70588
  },
69457
70589
  className: "px-2.5 py-0.5 bg-surface-800 hover:bg-surface-700 rounded transition-colors text-[11px] whitespace-nowrap",
69458
70590
  children: "Export results"
@@ -69536,8 +70668,117 @@ function CollectionPanel() {
69536
70668
  ] })
69537
70669
  ] });
69538
70670
  }
69539
- const { electron: electron$6 } = window;
70671
+ function routeKey(method, path) {
70672
+ return `${(method || "GET").toUpperCase()} /${String(path || "").replace(/^\/+/, "")}`;
70673
+ }
70674
+ function PushToCloudModal({ mock, onClose }) {
70675
+ const routes = mock.routes ?? [];
70676
+ const [selected, setSelected] = reactExports.useState(new Set(routes.map((r) => r.id)));
70677
+ const [status, setStatus] = reactExports.useState({ state: "idle" });
70678
+ const [existing, setExisting] = reactExports.useState(null);
70679
+ reactExports.useEffect(() => {
70680
+ let live = true;
70681
+ getCloudMockRoutes(mock.name).then((cloudRoutes) => {
70682
+ if (!live) return;
70683
+ setExisting(cloudRoutes ? new Set(cloudRoutes.map((r) => routeKey(r.method, r.path))) : /* @__PURE__ */ new Set());
70684
+ });
70685
+ return () => {
70686
+ live = false;
70687
+ };
70688
+ }, [mock.name]);
70689
+ const isExisting = (r) => existing?.has(routeKey(r.method, r.path)) ?? false;
70690
+ const selectedRoutes = routes.filter((r) => selected.has(r.id));
70691
+ const overwriteCount = selectedRoutes.filter(isExisting).length;
70692
+ const addCount = selectedRoutes.length - overwriteCount;
70693
+ const allSelected = routes.length > 0 && selected.size === routes.length;
70694
+ function toggle(id2) {
70695
+ setSelected((prev) => {
70696
+ const next = new Set(prev);
70697
+ if (next.has(id2)) next.delete(id2);
70698
+ else next.add(id2);
70699
+ return next;
70700
+ });
70701
+ }
70702
+ async function push2() {
70703
+ setStatus({ state: "pushing" });
70704
+ try {
70705
+ const r = await pushMockToCloud(mock, [...selected]);
70706
+ setStatus({ state: "ok", msg: r.url });
70707
+ } catch (e) {
70708
+ setStatus({ state: "err", msg: e.message });
70709
+ }
70710
+ }
70711
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
70712
+ Modal,
70713
+ {
70714
+ onClose,
70715
+ overlayClassName: "bg-black/50 z-50 flex items-start justify-center pt-24",
70716
+ panelClassName: "bg-surface-900 border border-surface-800 rounded-lg shadow-2xl w-[480px] flex flex-col max-h-[70vh]",
70717
+ title: `Push "${mock.name}" to cloud`,
70718
+ children: [
70719
+ overwriteCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-4 mt-3 px-3 py-2 rounded bg-amber-900/30 border border-amber-800/50 text-amber-300 text-[11px] flex-shrink-0", children: [
70720
+ "⚠ ",
70721
+ overwriteCount,
70722
+ " selected ",
70723
+ overwriteCount === 1 ? "route" : "routes",
70724
+ " already exist in the cloud and will be overwritten. New routes are added; others are kept."
70725
+ ] }),
70726
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-2.5 border-b border-surface-800 flex items-center justify-between text-xs flex-shrink-0", children: [
70727
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-400", children: [
70728
+ selected.size,
70729
+ " of ",
70730
+ routes.length,
70731
+ " routes selected"
70732
+ ] }),
70733
+ routes.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
70734
+ "button",
70735
+ {
70736
+ onClick: () => setSelected(allSelected ? /* @__PURE__ */ new Set() : new Set(routes.map((r) => r.id))),
70737
+ className: "text-blue-400 hover:text-blue-300",
70738
+ children: allSelected ? "Deselect all" : "Select all"
70739
+ }
70740
+ )
70741
+ ] }),
70742
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto px-2 py-2", children: routes.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500 text-xs px-2 py-6 text-center", children: "This mock has no routes." }) : routes.map((r) => /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 px-2 py-1.5 rounded hover:bg-surface-800/50 cursor-pointer text-xs", children: [
70743
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "checkbox", checked: selected.has(r.id), onChange: () => toggle(r.id), className: "accent-blue-500" }),
70744
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold w-14 flex-shrink-0", style: { color: getMethodColor(r.method) }, children: r.method }),
70745
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-surface-300 truncate", children: r.path }),
70746
+ isExisting(r) && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase tracking-wider text-amber-400 border border-amber-800/50 rounded px-1 py-0.5 flex-shrink-0", children: "in cloud" }),
70747
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-auto text-surface-500 flex-shrink-0", children: r.statusCode })
70748
+ ] }, r.id)) }),
70749
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3 border-t border-surface-800 flex items-center gap-2 flex-shrink-0", children: status.state === "ok" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
70750
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-green-400 text-[11px] flex-1 truncate", children: [
70751
+ "✓ Pushed ",
70752
+ selected.size,
70753
+ " route",
70754
+ selected.size !== 1 ? "s" : "",
70755
+ " → ",
70756
+ status.msg
70757
+ ] }),
70758
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "px-4 py-1.5 bg-blue-600 hover:bg-blue-500 rounded text-xs font-medium", children: "Done" })
70759
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
70760
+ status.state === "err" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-400 text-[11px] flex-1 truncate", children: [
70761
+ "✗ ",
70762
+ status.msg
70763
+ ] }),
70764
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
70765
+ "button",
70766
+ {
70767
+ onClick: push2,
70768
+ disabled: status.state === "pushing" || selected.size === 0,
70769
+ className: "ml-auto px-4 py-1.5 bg-blue-600 hover:bg-blue-500 disabled:opacity-40 rounded text-xs font-medium",
70770
+ children: status.state === "pushing" ? "Pushing…" : overwriteCount > 0 ? `Push ${selected.size} (${addCount} new, ${overwriteCount} overwrite)` : `Push ${selected.size} route${selected.size !== 1 ? "s" : ""}`
70771
+ }
70772
+ ),
70773
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "px-4 py-1.5 bg-surface-800 hover:bg-surface-700 rounded text-xs", children: "Cancel" })
70774
+ ] }) })
70775
+ ]
70776
+ }
70777
+ );
70778
+ }
70779
+ const { electron: electron$7 } = window;
69540
70780
  function MockPanel() {
70781
+ const [pushTarget, setPushTarget] = reactExports.useState(null);
69541
70782
  const mocks = useStore((s) => s.mocks);
69542
70783
  const activeMockId = useStore((s) => s.activeMockId);
69543
70784
  const setActiveMock = useStore((s) => s.setActiveMockId);
@@ -69565,15 +70806,15 @@ function MockPanel() {
69565
70806
  setWsdlImporting(true);
69566
70807
  try {
69567
70808
  const existingPorts = mockList.map((m) => m.data.port);
69568
- const { mock } = await electron$6.wsdlImport({ url: wsdlUrl.trim(), existingMockPorts: existingPorts });
70809
+ const { mock } = await electron$7.wsdlImport({ url: wsdlUrl.trim(), existingMockPorts: existingPorts });
69569
70810
  const relPath = `mocks/${mock.id}.mock.json`;
69570
70811
  loadMock(relPath, mock);
69571
- await electron$6.saveMock(relPath, mock);
70812
+ await electron$7.saveMock(relPath, mock);
69572
70813
  const ws2 = useStore.getState().workspace;
69573
70814
  if (ws2) {
69574
70815
  if (!ws2.mocks) ws2.mocks = [];
69575
70816
  ws2.mocks.push(relPath);
69576
- await electron$6.saveWorkspace(ws2);
70817
+ await electron$7.saveWorkspace(ws2);
69577
70818
  }
69578
70819
  setActiveMock(mock.id);
69579
70820
  setWsdlOpen(false);
@@ -69587,12 +70828,12 @@ function MockPanel() {
69587
70828
  async function handleAddMock() {
69588
70829
  addMock();
69589
70830
  const ws2 = useStore.getState().workspace;
69590
- if (ws2) await electron$6.saveWorkspace(ws2);
70831
+ if (ws2) await electron$7.saveWorkspace(ws2);
69591
70832
  const state = useStore.getState();
69592
70833
  const newId = state.activeMockId;
69593
70834
  if (newId) {
69594
70835
  const entry = state.mocks[newId];
69595
- await electron$6.saveMock(entry.relPath, entry.data);
70836
+ await electron$7.saveMock(entry.relPath, entry.data);
69596
70837
  setActiveMock(newId);
69597
70838
  }
69598
70839
  }
@@ -69600,15 +70841,15 @@ function MockPanel() {
69600
70841
  e.stopPropagation();
69601
70842
  const entry = useStore.getState().mocks[mockId];
69602
70843
  if (!entry) return;
69603
- if (entry.running) await electron$6.mockStop(mockId);
70844
+ if (entry.running) await electron$7.mockStop(mockId);
69604
70845
  deleteMock(mockId);
69605
70846
  const ws2 = useStore.getState().workspace;
69606
- if (ws2) await electron$6.saveWorkspace(ws2);
70847
+ if (ws2) await electron$7.saveWorkspace(ws2);
69607
70848
  }
69608
70849
  async function handleStartRecorder() {
69609
70850
  setRecorderError("");
69610
70851
  try {
69611
- await electron$6.recordStart({ upstream: recorderUpstream, port: recorderPort });
70852
+ await electron$7.recordStart({ upstream: recorderUpstream, port: recorderPort });
69612
70853
  setRecorderRunning(true);
69613
70854
  } catch (err) {
69614
70855
  setRecorderError(err instanceof Error ? err.message : String(err));
@@ -69620,18 +70861,19 @@ function MockPanel() {
69620
70861
  if (!entry) return;
69621
70862
  try {
69622
70863
  if (entry.running) {
69623
- await electron$6.mockStop(mockId);
70864
+ await electron$7.mockStop(mockId);
69624
70865
  setRunning(mockId, false);
69625
70866
  } else {
69626
70867
  const latest2 = useStore.getState().mocks[mockId].data;
69627
- await electron$6.saveMock(entry.relPath, latest2);
69628
- await electron$6.mockStart(latest2);
70868
+ await electron$7.saveMock(entry.relPath, latest2);
70869
+ await electron$7.mockStart(latest2);
69629
70870
  setRunning(mockId, true);
69630
70871
  }
69631
70872
  } catch {
69632
70873
  }
69633
70874
  }
69634
70875
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [
70876
+ pushTarget && /* @__PURE__ */ jsxRuntimeExports.jsx(PushToCloudModal, { mock: pushTarget, onClose: () => setPushTarget(null) }),
69635
70877
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-surface-800 flex-shrink-0", children: [
69636
70878
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
69637
70879
  "button",
@@ -69793,6 +71035,18 @@ function MockPanel() {
69793
71035
  children: entry.running ? "■" : "▶"
69794
71036
  }
69795
71037
  ),
71038
+ cloudEnabled() && /* @__PURE__ */ jsxRuntimeExports.jsx(
71039
+ "button",
71040
+ {
71041
+ onClick: (e) => {
71042
+ e.stopPropagation();
71043
+ setPushTarget(mock);
71044
+ },
71045
+ className: "text-[10px] px-1 py-0.5 rounded text-surface-500 hover:text-blue-400 opacity-0 group-hover:opacity-100 transition-colors",
71046
+ title: "Push to cloud (choose routes)",
71047
+ children: "☁"
71048
+ }
71049
+ ),
69796
71050
  /* @__PURE__ */ jsxRuntimeExports.jsx(
69797
71051
  "button",
69798
71052
  {
@@ -69810,7 +71064,7 @@ function MockPanel() {
69810
71064
  }) })
69811
71065
  ] });
69812
71066
  }
69813
- const { electron: electron$5 } = window;
71067
+ const { electron: electron$6 } = window;
69814
71068
  const METHODS_PLUS_ANY = ["ANY", "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "QUERY"];
69815
71069
  function RouteRow({
69816
71070
  route,
@@ -70207,25 +71461,26 @@ function MockDetailPanel({ mockId }) {
70207
71461
  const [error2, setError] = reactExports.useState(null);
70208
71462
  const [newRouteId, setNewRouteId] = reactExports.useState(null);
70209
71463
  const [activeTab, setActiveTab] = reactExports.useState("routes");
71464
+ const [showPush, setShowPush] = reactExports.useState(false);
70210
71465
  if (!entry) return null;
70211
71466
  const { data: mock, running } = entry;
70212
71467
  const routes = mock.routes ?? [];
70213
71468
  async function save(updated) {
70214
71469
  updateMock(mock.id, updated);
70215
- await electron$5.saveMock(entry.relPath, updated);
70216
- if (running) await electron$5.mockUpdateRoutes(mock.id, updated.routes ?? []);
70217
- if (workspace) await electron$5.saveWorkspace(workspace);
71470
+ await electron$6.saveMock(entry.relPath, updated);
71471
+ if (running) await electron$6.mockUpdateRoutes(mock.id, updated.routes ?? []);
71472
+ if (workspace) await electron$6.saveWorkspace(workspace);
70218
71473
  }
70219
71474
  async function toggleRunning() {
70220
71475
  setError(null);
70221
71476
  try {
70222
71477
  if (running) {
70223
- await electron$5.mockStop(mock.id);
71478
+ await electron$6.mockStop(mock.id);
70224
71479
  setRunning(mock.id, false);
70225
71480
  } else {
70226
71481
  const latest2 = useStore.getState().mocks[mock.id].data;
70227
- await electron$5.saveMock(entry.relPath, latest2);
70228
- await electron$5.mockStart(latest2);
71482
+ await electron$6.saveMock(entry.relPath, latest2);
71483
+ await electron$6.mockStart(latest2);
70229
71484
  setRunning(mock.id, true);
70230
71485
  }
70231
71486
  } catch (e) {
@@ -70316,10 +71571,20 @@ function MockDetailPanel({ mockId }) {
70316
71571
  }
70317
71572
  ),
70318
71573
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
71574
+ showPush && /* @__PURE__ */ jsxRuntimeExports.jsx(PushToCloudModal, { mock, onClose: () => setShowPush(false) }),
70319
71575
  error2 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-red-400 max-w-xs truncate", title: error2, children: [
70320
71576
  "⚠ ",
70321
71577
  error2
70322
71578
  ] }),
71579
+ cloudEnabled() && /* @__PURE__ */ jsxRuntimeExports.jsx(
71580
+ "button",
71581
+ {
71582
+ onClick: () => setShowPush(true),
71583
+ className: "px-3 py-1.5 rounded text-sm font-medium bg-surface-800 hover:bg-surface-700 text-surface-300 border border-surface-700",
71584
+ title: "Push this mock to API Spector Cloud (choose routes)",
71585
+ children: "☁ Push to cloud"
71586
+ }
71587
+ ),
70323
71588
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative group/cli flex items-center", children: [
70324
71589
  /* @__PURE__ */ jsxRuntimeExports.jsx(
70325
71590
  "button",
@@ -70338,7 +71603,7 @@ function MockDetailPanel({ mockId }) {
70338
71603
  "button",
70339
71604
  {
70340
71605
  onClick: () => {
70341
- if (running) electron$5.mockStop(mock.id);
71606
+ if (running) electron$6.mockStop(mock.id);
70342
71607
  deleteMock(mock.id);
70343
71608
  setActive2(null);
70344
71609
  },
@@ -70405,7 +71670,7 @@ function MockDetailPanel({ mockId }) {
70405
71670
  ] })
70406
71671
  ] });
70407
71672
  }
70408
- const { electron: electron$4 } = window;
71673
+ const { electron: electron$5 } = window;
70409
71674
  function statusColor$1(status) {
70410
71675
  if (status === 0) return "text-yellow-400";
70411
71676
  if (status < 300) return "text-emerald-400";
@@ -70438,7 +71703,7 @@ function RecorderPanel({ onImportMock, onClose, defaultTargetMockId }) {
70438
71703
  const upstream = useStore((s) => s.recorderUpstream);
70439
71704
  const mockList = Object.values(useStore((s) => s.mocks));
70440
71705
  reactExports.useEffect(() => {
70441
- electron$4.onRecordHit((entry) => {
71706
+ electron$5.onRecordHit((entry) => {
70442
71707
  setEntries((prev) => {
70443
71708
  const next = [...prev, entry];
70444
71709
  setTimeout(() => {
@@ -70448,12 +71713,12 @@ function RecorderPanel({ onImportMock, onClose, defaultTargetMockId }) {
70448
71713
  });
70449
71714
  });
70450
71715
  return () => {
70451
- electron$4.offRecordHit();
71716
+ electron$5.offRecordHit();
70452
71717
  };
70453
71718
  }, []);
70454
71719
  async function handleStop() {
70455
71720
  try {
70456
- const s = await electron$4.recordStop();
71721
+ const s = await electron$5.recordStop();
70457
71722
  setSession(s);
70458
71723
  setStopped(true);
70459
71724
  } catch {
@@ -70649,6 +71914,467 @@ function HeadersTable({ headers }) {
70649
71914
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300 break-all", children: v })
70650
71915
  ] }, k)) });
70651
71916
  }
71917
+ function kvToHeaders(kv) {
71918
+ const out = {};
71919
+ for (const p2 of kv ?? []) {
71920
+ if (p2.enabled === false || !p2.key) continue;
71921
+ out[p2.key] = p2.value;
71922
+ }
71923
+ return out;
71924
+ }
71925
+ function designContractToMock(cc, port = 4100) {
71926
+ const routes = cc.interactions.map((it, i) => ({
71927
+ id: it.id || `route-${i}`,
71928
+ // Pact path templates use {id}; the mock router uses :id.
71929
+ method: (it.request.method || "GET").toUpperCase(),
71930
+ path: (it.request.path || "/").replace(/\{([^}]+)\}/g, ":$1"),
71931
+ statusCode: it.response.status,
71932
+ headers: { "Content-Type": "application/json", ...kvToHeaders(it.response.headers) },
71933
+ body: it.response.body?.trim() ? it.response.body : "",
71934
+ description: it.description || void 0
71935
+ }));
71936
+ return {
71937
+ version: "1.0",
71938
+ id: `mock-${cc.id}`,
71939
+ name: `${cc.consumer} → ${cc.provider} (contract mock)`,
71940
+ port,
71941
+ routes
71942
+ };
71943
+ }
71944
+ const { electron: electron$4 } = window;
71945
+ function uid() {
71946
+ try {
71947
+ return crypto.randomUUID();
71948
+ } catch {
71949
+ return `id-${Date.now()}-${Math.floor(performance.now())}`;
71950
+ }
71951
+ }
71952
+ function newInteraction() {
71953
+ return {
71954
+ id: uid(),
71955
+ description: "get a resource",
71956
+ request: { method: "GET", path: "/resource/{id}", headers: [] },
71957
+ response: { status: 200, body: '{\n "id": 1\n}' }
71958
+ };
71959
+ }
71960
+ function newContract() {
71961
+ return { id: uid(), consumer: "my-consumer", provider: "my-provider", interactions: [newInteraction()] };
71962
+ }
71963
+ function KVRows({ label, rows, onChange, keyPlaceholder = "name", valuePlaceholder = "value" }) {
71964
+ const list2 = rows ?? [];
71965
+ const set2 = (i, patch) => onChange(list2.map((r, j) => j === i ? { ...r, ...patch } : r));
71966
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
71967
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: label }),
71968
+ list2.map((r, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
71969
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
71970
+ "input",
71971
+ {
71972
+ value: r.key,
71973
+ onChange: (e) => set2(i, { key: e.target.value }),
71974
+ placeholder: keyPlaceholder,
71975
+ spellCheck: false,
71976
+ className: "flex-1 min-w-0 text-[11px] font-mono bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
71977
+ }
71978
+ ),
71979
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
71980
+ "input",
71981
+ {
71982
+ value: r.value,
71983
+ onChange: (e) => set2(i, { value: e.target.value }),
71984
+ placeholder: valuePlaceholder,
71985
+ spellCheck: false,
71986
+ className: "flex-1 min-w-0 text-[11px] font-mono bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
71987
+ }
71988
+ ),
71989
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => onChange(list2.filter((_, j) => j !== i)), className: "text-surface-600 hover:text-red-400 text-xs px-0.5", title: "Remove", children: "✕" })
71990
+ ] }, i)),
71991
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => onChange([...list2, { key: "", value: "", enabled: true }]), className: "self-start text-[11px] text-blue-400 hover:text-blue-300", children: "+ add" })
71992
+ ] });
71993
+ }
71994
+ function ContractDesignerModal({ onClose }) {
71995
+ const workspace = useStore((s) => s.workspace);
71996
+ const workspacePath = useStore((s) => s.workspacePath);
71997
+ const setWorkspace = useStore((s) => s.setWorkspace);
71998
+ const addMock = useStore((s) => s.addMock);
71999
+ const updateMock = useStore((s) => s.updateMock);
72000
+ const cloudConnected = useStore((s) => Boolean(s.workspace?.settings?.cloud?.enabled));
72001
+ const { toast, show } = useToast();
72002
+ const contracts = workspace?.designContracts ?? [];
72003
+ const [selectedId, setSelectedId] = reactExports.useState(contracts[0]?.id ?? null);
72004
+ const [version, setVersion] = reactExports.useState("0.1.0");
72005
+ const [busy, setBusy] = reactExports.useState(false);
72006
+ const selected = contracts.find((c) => c.id === selectedId) ?? null;
72007
+ async function persist(next) {
72008
+ if (!workspace) return;
72009
+ const updated = { ...workspace, designContracts: next };
72010
+ setWorkspace(updated, workspacePath ?? "");
72011
+ try {
72012
+ await electron$4.saveWorkspace(updated);
72013
+ } catch (e) {
72014
+ show(e.message, false);
72015
+ }
72016
+ }
72017
+ function upsert(contract) {
72018
+ const stamped = { ...contract, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
72019
+ const exists = contracts.some((c) => c.id === contract.id);
72020
+ persist(exists ? contracts.map((c) => c.id === contract.id ? stamped : c) : [...contracts, stamped]);
72021
+ }
72022
+ function addContract() {
72023
+ const c = newContract();
72024
+ setSelectedId(c.id);
72025
+ persist([...contracts, c]);
72026
+ }
72027
+ function deleteContract(id2) {
72028
+ const next = contracts.filter((c) => c.id !== id2);
72029
+ if (selectedId === id2) setSelectedId(next[0]?.id ?? null);
72030
+ persist(next);
72031
+ }
72032
+ function patchInteraction(ix, patch) {
72033
+ if (!selected) return;
72034
+ const interactions = selected.interactions.map((it, i) => i === ix ? { ...it, ...patch } : it);
72035
+ upsert({ ...selected, interactions });
72036
+ }
72037
+ async function saveLocally() {
72038
+ if (!selected) return;
72039
+ try {
72040
+ const relPath = await electron$4.exportDesignPact(selected);
72041
+ if (relPath) show(`Saved ${relPath} in the workspace`, true);
72042
+ } catch (e) {
72043
+ show(e.message, false);
72044
+ }
72045
+ }
72046
+ async function createMock() {
72047
+ if (!selected) return;
72048
+ try {
72049
+ addMock();
72050
+ const st = useStore.getState();
72051
+ const id2 = st.activeMockId;
72052
+ const entry = id2 ? st.mocks[id2] : null;
72053
+ if (!id2 || !entry) {
72054
+ show("Could not create the mock — open a workspace first.", false);
72055
+ return;
72056
+ }
72057
+ const mock = { ...designContractToMock(selected), id: id2, name: `${selected.consumer} → ${selected.provider} (contract mock)` };
72058
+ updateMock(id2, mock);
72059
+ await electron$4.saveMock(entry.relPath, mock);
72060
+ const ws2 = useStore.getState().workspace;
72061
+ if (ws2) await electron$4.saveWorkspace(ws2);
72062
+ show(`Created mock with ${mock.routes.length} route${mock.routes.length === 1 ? "" : "s"} — open the Mocks panel to run it`, true);
72063
+ } catch (e) {
72064
+ show(e.message, false);
72065
+ }
72066
+ }
72067
+ async function publish() {
72068
+ if (!selected) return;
72069
+ setBusy(true);
72070
+ try {
72071
+ const res = await electron$4.cloudPushDesignContract({ contract: selected, consumerVersion: version });
72072
+ const v = res.verification;
72073
+ if (v && !v.success) {
72074
+ const failed = v.checks.filter((c) => !c.passed).map((c) => c.interaction).join(", ");
72075
+ show(`Published, but bi-directional check failed: ${failed || "see matrix"}`, false);
72076
+ } else {
72077
+ show(`Published ${selected.consumer}@${version} to the cloud`, true);
72078
+ }
72079
+ } catch (e) {
72080
+ show(e.message, false);
72081
+ } finally {
72082
+ setBusy(false);
72083
+ }
72084
+ }
72085
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
72086
+ Modal,
72087
+ {
72088
+ onClose,
72089
+ title: "Contract Designer",
72090
+ subtitle: "Design a consumer-driven contract up front — no endpoint required — then publish it to API Spector Cloud.",
72091
+ panelClassName: "bg-surface-900 border border-surface-700 rounded-xl w-[min(1000px,94vw)] h-[min(760px,90vh)] flex flex-col",
72092
+ children: [
72093
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 min-h-0", children: [
72094
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-56 shrink-0 border-r border-surface-800 flex flex-col", children: [
72095
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 py-2 border-b border-surface-800 flex items-center justify-between", children: [
72096
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500 font-semibold", children: "Contracts" }),
72097
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: addContract, className: "text-xs text-blue-400 hover:text-blue-300", title: "New contract", children: "+ New" })
72098
+ ] }),
72099
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
72100
+ contracts.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "px-3 py-3 text-xs text-surface-500", children: [
72101
+ "No contracts yet. Click ",
72102
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: "+ New" }),
72103
+ "."
72104
+ ] }),
72105
+ contracts.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
72106
+ "button",
72107
+ {
72108
+ onClick: () => setSelectedId(c.id),
72109
+ className: `w-full text-left px-3 py-2 border-b border-surface-800/60 transition-colors ${c.id === selectedId ? "bg-surface-800" : "hover:bg-surface-800/50"}`,
72110
+ children: [
72111
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-surface-200 truncate", children: [
72112
+ c.consumer,
72113
+ " ",
72114
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600", children: "→" }),
72115
+ " ",
72116
+ c.provider
72117
+ ] }),
72118
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-surface-500", children: [
72119
+ c.interactions.length,
72120
+ " interaction",
72121
+ c.interactions.length === 1 ? "" : "s"
72122
+ ] })
72123
+ ]
72124
+ },
72125
+ c.id
72126
+ ))
72127
+ ] })
72128
+ ] }),
72129
+ !selected ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center text-surface-500 text-sm", children: "Select or create a contract." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col min-h-0", children: [
72130
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 border-b border-surface-800 flex flex-wrap items-end gap-3", children: [
72131
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
72132
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: "Consumer" }),
72133
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72134
+ "input",
72135
+ {
72136
+ value: selected.consumer,
72137
+ onChange: (e) => upsert({ ...selected, consumer: e.target.value }),
72138
+ className: "text-xs bg-surface-800 border border-surface-700 rounded px-2 py-1 w-44 focus:outline-none focus:border-blue-500"
72139
+ }
72140
+ )
72141
+ ] }),
72142
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600 pb-1.5", children: "→" }),
72143
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
72144
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: "Provider" }),
72145
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72146
+ "input",
72147
+ {
72148
+ value: selected.provider,
72149
+ onChange: (e) => upsert({ ...selected, provider: e.target.value }),
72150
+ className: "text-xs bg-surface-800 border border-surface-700 rounded px-2 py-1 w-44 focus:outline-none focus:border-blue-500"
72151
+ }
72152
+ )
72153
+ ] }),
72154
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-end gap-2", children: [
72155
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
72156
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: "Version" }),
72157
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72158
+ "input",
72159
+ {
72160
+ value: version,
72161
+ onChange: (e) => setVersion(e.target.value),
72162
+ className: "text-xs bg-surface-800 border border-surface-700 rounded px-2 py-1 w-24 focus:outline-none focus:border-blue-500"
72163
+ }
72164
+ )
72165
+ ] }),
72166
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72167
+ "button",
72168
+ {
72169
+ onClick: createMock,
72170
+ title: "Turn this contract into a runnable mock the consumer can develop against (no provider needed)",
72171
+ className: "px-3 py-1.5 text-xs border border-surface-600 text-surface-200 hover:border-blue-500 hover:text-white rounded transition-colors whitespace-nowrap",
72172
+ children: "Create mock"
72173
+ }
72174
+ ),
72175
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72176
+ "button",
72177
+ {
72178
+ onClick: saveLocally,
72179
+ title: "Write the compiled pact to pacts/ in this workspace (git-committable, no cloud needed)",
72180
+ className: "px-3 py-1.5 text-xs border border-surface-600 text-surface-200 hover:border-blue-500 hover:text-white rounded transition-colors whitespace-nowrap",
72181
+ children: "Save to workspace"
72182
+ }
72183
+ ),
72184
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72185
+ "button",
72186
+ {
72187
+ onClick: publish,
72188
+ disabled: busy || !cloudConnected,
72189
+ title: cloudConnected ? "Publish the compiled pact to API Spector Cloud" : "Connect to cloud in Settings → Cloud first",
72190
+ className: "px-3 py-1.5 text-xs bg-blue-700 hover:bg-blue-600 disabled:bg-surface-800 disabled:text-surface-600 rounded transition-colors whitespace-nowrap",
72191
+ children: busy ? "Publishing…" : "Publish to Cloud"
72192
+ }
72193
+ ),
72194
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72195
+ "button",
72196
+ {
72197
+ onClick: () => deleteContract(selected.id),
72198
+ className: "px-2 py-1.5 text-xs text-red-400 hover:bg-red-900/30 rounded transition-colors",
72199
+ children: "Delete"
72200
+ }
72201
+ )
72202
+ ] })
72203
+ ] }),
72204
+ !cloudConnected && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "px-4 py-1.5 text-[11px] text-amber-400 bg-amber-950/20 border-b border-surface-800", children: "Not connected to API Spector Cloud — connect in Settings → Cloud to publish. You can still design and save the contract." }),
72205
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-4", children: [
72206
+ selected.interactions.map((it, ix) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-surface-800 rounded-lg p-3 flex flex-col gap-2.5", children: [
72207
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
72208
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72209
+ "input",
72210
+ {
72211
+ value: it.description,
72212
+ onChange: (e) => patchInteraction(ix, { description: e.target.value }),
72213
+ placeholder: "what this interaction is",
72214
+ className: "flex-1 text-xs font-medium bg-transparent border-b border-surface-800 focus:border-blue-500 focus:outline-none py-0.5"
72215
+ }
72216
+ ),
72217
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72218
+ "button",
72219
+ {
72220
+ onClick: () => upsert({ ...selected, interactions: selected.interactions.filter((_, i) => i !== ix) }),
72221
+ className: "text-surface-600 hover:text-red-400 text-xs",
72222
+ title: "Remove interaction",
72223
+ children: "✕"
72224
+ }
72225
+ )
72226
+ ] }),
72227
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
72228
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72229
+ "select",
72230
+ {
72231
+ value: it.request.method,
72232
+ onChange: (e) => patchInteraction(ix, { request: { ...it.request, method: e.target.value } }),
72233
+ className: "text-xs bg-surface-800 border border-surface-700 rounded px-1.5 py-1 focus:outline-none focus:border-blue-500",
72234
+ children: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"].map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { children: m }, m))
72235
+ }
72236
+ ),
72237
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72238
+ "input",
72239
+ {
72240
+ value: it.request.path,
72241
+ onChange: (e) => patchInteraction(ix, { request: { ...it.request, path: e.target.value } }),
72242
+ placeholder: "/brands/{id}",
72243
+ className: "flex-1 text-xs font-mono bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
72244
+ }
72245
+ )
72246
+ ] }),
72247
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72248
+ "input",
72249
+ {
72250
+ value: it.providerState ?? "",
72251
+ onChange: (e) => patchInteraction(ix, { providerState: e.target.value }),
72252
+ placeholder: 'provider state (e.g. "brand 1 exists") — optional',
72253
+ className: "text-xs bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
72254
+ }
72255
+ ),
72256
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
72257
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2.5 border-r border-surface-800 pr-4", children: [
72258
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-400 font-semibold", children: "Request" }),
72259
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72260
+ KVRows,
72261
+ {
72262
+ label: "Query params",
72263
+ rows: it.request.query,
72264
+ onChange: (q) => patchInteraction(ix, { request: { ...it.request, query: q } }),
72265
+ keyPlaceholder: "e.g. discontinued",
72266
+ valuePlaceholder: "true"
72267
+ }
72268
+ ),
72269
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72270
+ KVRows,
72271
+ {
72272
+ label: "Headers",
72273
+ rows: it.request.headers,
72274
+ onChange: (h) => patchInteraction(ix, { request: { ...it.request, headers: h } }),
72275
+ keyPlaceholder: "Accept",
72276
+ valuePlaceholder: "application/json"
72277
+ }
72278
+ ),
72279
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
72280
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: "Body (JSON, optional)" }),
72281
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72282
+ "textarea",
72283
+ {
72284
+ value: it.request.body ?? "",
72285
+ onChange: (e) => patchInteraction(ix, { request: { ...it.request, body: e.target.value } }),
72286
+ rows: 3,
72287
+ placeholder: "{ }",
72288
+ spellCheck: false,
72289
+ className: "text-[11px] font-mono bg-surface-800 border border-surface-700 rounded px-2 py-1.5 focus:outline-none focus:border-blue-500 resize-y"
72290
+ }
72291
+ )
72292
+ ] })
72293
+ ] }),
72294
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2.5", children: [
72295
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] uppercase tracking-wider text-surface-400 font-semibold flex items-center gap-2", children: [
72296
+ "Expected response",
72297
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72298
+ "input",
72299
+ {
72300
+ type: "number",
72301
+ value: it.response.status,
72302
+ title: "expected status code",
72303
+ onChange: (e) => patchInteraction(ix, { response: { ...it.response, status: Number(e.target.value) || 0 } }),
72304
+ className: "w-16 text-[11px] bg-surface-900 border border-surface-700 rounded px-1 py-0.5 focus:outline-none focus:border-blue-500"
72305
+ }
72306
+ )
72307
+ ] }),
72308
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72309
+ KVRows,
72310
+ {
72311
+ label: "Headers",
72312
+ rows: it.response.headers,
72313
+ onChange: (h) => patchInteraction(ix, { response: { ...it.response, headers: h } }),
72314
+ keyPlaceholder: "Content-Type",
72315
+ valuePlaceholder: "application/json"
72316
+ }
72317
+ ),
72318
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
72319
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500", children: "Body" }),
72320
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72321
+ "textarea",
72322
+ {
72323
+ value: it.response.body ?? "",
72324
+ onChange: (e) => patchInteraction(ix, { response: { ...it.response, body: e.target.value } }),
72325
+ rows: 3,
72326
+ placeholder: "[{ id: string, name: string, slug: string }] — or a JSON example",
72327
+ spellCheck: false,
72328
+ className: "text-[11px] font-mono bg-surface-800 border border-surface-700 rounded px-2 py-1.5 focus:outline-none focus:border-blue-500 resize-y"
72329
+ }
72330
+ ),
72331
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-surface-500 leading-relaxed", children: [
72332
+ "A JSON example (matched by type when the toggle is on), or a ",
72333
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300", children: "type shape" }),
72334
+ " to check each property's type: ",
72335
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-surface-300", children: "string, number, integer, boolean, null" }),
72336
+ " plus nested ",
72337
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: "{ }" }),
72338
+ " / ",
72339
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: "[ ]" }),
72340
+ ". E.g. ",
72341
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-surface-300", children: "[{ id: string, qty: integer }]" }),
72342
+ ". Compiles to Pact ",
72343
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: "matchingRules" }),
72344
+ "."
72345
+ ] })
72346
+ ] })
72347
+ ] })
72348
+ ] }),
72349
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-[11px] text-surface-400", children: [
72350
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72351
+ "input",
72352
+ {
72353
+ type: "checkbox",
72354
+ checked: it.looseMatch !== false,
72355
+ onChange: (e) => patchInteraction(ix, { looseMatch: e.target.checked }),
72356
+ className: "accent-blue-500"
72357
+ }
72358
+ ),
72359
+ "Match a JSON example by type, not exact value (tolerant — recommended)"
72360
+ ] })
72361
+ ] }, it.id)),
72362
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72363
+ "button",
72364
+ {
72365
+ onClick: () => upsert({ ...selected, interactions: [...selected.interactions, newInteraction()] }),
72366
+ className: "self-start text-xs text-blue-400 hover:text-blue-300",
72367
+ children: "+ Add interaction"
72368
+ }
72369
+ )
72370
+ ] })
72371
+ ] })
72372
+ ] }),
72373
+ toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed bottom-4 right-4 z-[120] w-96", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Toast, { toast }) })
72374
+ ]
72375
+ }
72376
+ );
72377
+ }
70652
72378
  const { electron: electron$3 } = window;
70653
72379
  function ContractPanel({ fuzzReport, setFuzzReport }) {
70654
72380
  const collections = useStore((s) => s.collections);
@@ -70664,6 +72390,29 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
70664
72390
  const removeContractSnapshot = useStore((s) => s.removeContractSnapshot);
70665
72391
  const workspace = useStore((s) => s.workspace);
70666
72392
  const [mode, setMode] = reactExports.useState("consumer");
72393
+ const [showDesigner, setShowDesigner] = reactExports.useState(false);
72394
+ const cloudConnected = useStore((s) => Boolean(s.workspace?.settings?.cloud?.enabled));
72395
+ const [providerName, setProviderName] = reactExports.useState("");
72396
+ const [specVersion, setSpecVersion] = reactExports.useState("");
72397
+ const [publishingSpec, setPublishingSpec] = reactExports.useState(false);
72398
+ const [publishNote, setPublishNote] = reactExports.useState(null);
72399
+ async function publishSpecToCloud() {
72400
+ setError(null);
72401
+ setPublishNote(null);
72402
+ setPublishingSpec(true);
72403
+ try {
72404
+ const res = await electron$3.cloudPushSpec({
72405
+ pacticipant: providerName.trim(),
72406
+ version: specVersion.trim(),
72407
+ specUrl: specUrl.trim()
72408
+ });
72409
+ setPublishNote(`Published ${providerName.trim()}@${specVersion.trim()} — ${res.verified_contracts} contract(s) re-verified`);
72410
+ } catch (e) {
72411
+ setError(e.message);
72412
+ } finally {
72413
+ setPublishingSpec(false);
72414
+ }
72415
+ }
70667
72416
  const [specUrl, setSpecUrl] = reactExports.useState("");
70668
72417
  const [requestBaseUrl, setRequestBaseUrl] = reactExports.useState("");
70669
72418
  const [providerBaseUrl, setProviderBaseUrl] = reactExports.useState("");
@@ -70683,6 +72432,7 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
70683
72432
  const contractRequests = allRequests.filter(
70684
72433
  (r) => r.contract && (r.contract.statusCode !== void 0 || r.contract.bodySchema || r.contract.bodyMatcher || r.contract.headers?.length)
70685
72434
  );
72435
+ const designInteractionCount = (workspace?.designContracts ?? []).reduce((n, cc) => n + cc.interactions.length, 0);
70686
72436
  const isFuzz = mode === "fuzz";
70687
72437
  const needsSpec = mode === "provider" || mode === "bidirectional";
70688
72438
  const showSpec = needsSpec || isFuzz;
@@ -70709,6 +72459,9 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
70709
72459
  const result = await electron$3.runContracts({
70710
72460
  mode,
70711
72461
  requests,
72462
+ // Let the main process add design-first contracts (Designer + pacts/) for
72463
+ // the contract-bearing modes, so they run without a manual pact-import.
72464
+ designContracts: workspace?.designContracts,
70712
72465
  envVars,
70713
72466
  collectionVars,
70714
72467
  specUrl: specUrl.trim() || void 0,
@@ -70794,6 +72547,15 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
70794
72547
  }
70795
72548
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [
70796
72549
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 px-3 py-3 border-b border-surface-800 flex-shrink-0", children: [
72550
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72551
+ "button",
72552
+ {
72553
+ onClick: () => setShowDesigner(true),
72554
+ className: "flex items-center justify-center gap-1.5 text-xs px-3 py-1.5 rounded-lg border border-dashed border-surface-600 text-surface-300 hover:border-blue-500 hover:text-white transition-colors",
72555
+ title: "Design a consumer-driven contract up front, with no endpoints, then publish it to the cloud",
72556
+ children: "✎ Design a contract (no endpoint needed)"
72557
+ }
72558
+ ),
70797
72559
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1 bg-surface-800 rounded-lg p-0.5", children: [
70798
72560
  ["consumer", "Consumer"],
70799
72561
  ["provider", "Provider"],
@@ -70914,7 +72676,44 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
70914
72676
  }
70915
72677
  )
70916
72678
  ] }),
70917
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 mt-1 leading-relaxed", children: "Pin a snapshot to run against a specific spec version later, even after the provider ships an update." })
72679
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 mt-1 leading-relaxed", children: "Pin a snapshot to run against a specific spec version later, even after the provider ships an update." }),
72680
+ cloudConnected && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 pt-2 border-t border-surface-800 flex flex-col gap-1.5", children: [
72681
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] text-surface-500 uppercase tracking-wider font-medium", children: [
72682
+ "Publish spec to cloud ",
72683
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600 normal-case tracking-normal", children: "(provider side of bi-directional)" })
72684
+ ] }),
72685
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1", children: [
72686
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72687
+ "input",
72688
+ {
72689
+ value: providerName,
72690
+ onChange: (e) => setProviderName(e.target.value),
72691
+ placeholder: "provider name",
72692
+ className: "flex-1 text-xs bg-surface-800 border border-surface-700 rounded px-2 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600"
72693
+ }
72694
+ ),
72695
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72696
+ "input",
72697
+ {
72698
+ value: specVersion,
72699
+ onChange: (e) => setSpecVersion(e.target.value),
72700
+ placeholder: "version (git SHA)",
72701
+ className: "w-32 text-xs bg-surface-800 border border-surface-700 rounded px-2 py-1.5 focus:outline-none focus:border-blue-500 font-mono placeholder-surface-600"
72702
+ }
72703
+ ),
72704
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
72705
+ "button",
72706
+ {
72707
+ onClick: publishSpecToCloud,
72708
+ disabled: publishingSpec || !specUrl.trim() || !providerName.trim() || !specVersion.trim(),
72709
+ title: "Publish this OpenAPI spec to the broker; consumers' pacts are re-verified against it",
72710
+ className: "px-2.5 text-xs bg-blue-700 hover:bg-blue-600 disabled:bg-surface-800 disabled:text-surface-600 rounded transition-colors",
72711
+ children: publishingSpec ? "…" : "Publish"
72712
+ }
72713
+ )
72714
+ ] }),
72715
+ publishNote && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-emerald-400 leading-relaxed", children: publishNote })
72716
+ ] })
70918
72717
  ] }),
70919
72718
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
70920
72719
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] text-surface-500 uppercase tracking-wider font-medium block mb-1", children: [
@@ -71012,7 +72811,7 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
71012
72811
  ] })
71013
72812
  ] }),
71014
72813
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
71015
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-500", children: mode === "provider" || isFuzz ? `${allRequests.length} request${allRequests.length !== 1 ? "s" : ""}` : `${contractRequests.length} contract${contractRequests.length !== 1 ? "s" : ""} defined` }),
72814
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-500", children: mode === "provider" || isFuzz ? `${allRequests.length} request${allRequests.length !== 1 ? "s" : ""}` : `${contractRequests.length + designInteractionCount} contract${contractRequests.length + designInteractionCount !== 1 ? "s" : ""} defined` }),
71016
72815
  /* @__PURE__ */ jsxRuntimeExports.jsx(
71017
72816
  "button",
71018
72817
  {
@@ -71051,7 +72850,8 @@ function ContractPanel({ fuzzReport, setFuzzReport }) {
71051
72850
  " cases"
71052
72851
  ] })
71053
72852
  ] })
71054
- ] })
72853
+ ] }),
72854
+ showDesigner && /* @__PURE__ */ jsxRuntimeExports.jsx(ContractDesignerModal, { onClose: () => setShowDesigner(false) })
71055
72855
  ] });
71056
72856
  }
71057
72857
  function statusColor(code2) {
@@ -72010,7 +73810,7 @@ function CiTab() {
72010
73810
  const [platform, setPlatform] = reactExports.useState("unknown");
72011
73811
  const [envId, setEnvId] = reactExports.useState("");
72012
73812
  const [tags2, setTags] = reactExports.useState("");
72013
- const [preview, setPreview] = reactExports.useState("");
73813
+ const [preview2, setPreview] = reactExports.useState("");
72014
73814
  const [written, setWritten] = reactExports.useState(false);
72015
73815
  const [error2, setError] = reactExports.useState(null);
72016
73816
  reactExports.useEffect(() => {
@@ -72032,7 +73832,7 @@ function CiTab() {
72032
73832
  async function write() {
72033
73833
  try {
72034
73834
  setError(null);
72035
- await electron$2.gitWriteCiFile(ciFilePath(platform), preview);
73835
+ await electron$2.gitWriteCiFile(ciFilePath(platform), preview2);
72036
73836
  setWritten(true);
72037
73837
  } catch (e) {
72038
73838
  setError(String(e));
@@ -72117,7 +73917,7 @@ function CiTab() {
72117
73917
  ] }),
72118
73918
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-auto", children: [
72119
73919
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "px-3 pt-2 pb-1 text-[10px] uppercase tracking-widest text-surface-600 font-semibold", children: "Preview" }),
72120
- /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "px-3 pb-3 text-[10px] font-mono text-surface-400 leading-relaxed whitespace-pre-wrap", children: preview })
73920
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "px-3 pb-3 text-[10px] font-mono text-surface-400 leading-relaxed whitespace-pre-wrap", children: preview2 })
72121
73921
  ] })
72122
73922
  ] });
72123
73923
  }
@@ -72498,6 +74298,7 @@ function App() {
72498
74298
  const setCommandPaletteOpen = useStore((s) => s.setCommandPaletteOpen);
72499
74299
  const setWsStatus = useStore((s) => s.setWsStatus);
72500
74300
  const addWsMessage = useStore((s) => s.addWsMessage);
74301
+ const pushLiveStreamEvents = useStore((s) => s.pushLiveStreamEvents);
72501
74302
  const [fuzzReport, setFuzzReport] = reactExports.useState(null);
72502
74303
  const [sidebarOpen, setSidebarOpen] = reactExports.useState(true);
72503
74304
  const [responseOpen, setResponseOpen] = reactExports.useState(false);
@@ -72548,6 +74349,12 @@ function App() {
72548
74349
  });
72549
74350
  return () => electron.offWsEvents();
72550
74351
  }, [addWsMessage, setWsStatus]);
74352
+ reactExports.useEffect(() => {
74353
+ electron.onRequestStreamEvent(({ streamId, events }) => {
74354
+ pushLiveStreamEvents(streamId, events);
74355
+ });
74356
+ return () => electron.offRequestStreamEvent();
74357
+ }, [pushLiveStreamEvents]);
72551
74358
  reactExports.useEffect(() => {
72552
74359
  function handleKeyDown(e) {
72553
74360
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
@@ -72575,7 +74382,21 @@ function App() {
72575
74382
  return map;
72576
74383
  }, [collections]);
72577
74384
  const activeTab = reactExports.useMemo(() => tabs.find((t2) => t2.id === activeTabId) ?? null, [tabs, activeTabId]);
72578
- const activeRequest = activeTab?.requestId ? requestsById.get(activeTab.requestId) ?? null : null;
74385
+ const activeBaseRequest = activeTab?.requestId ? requestsById.get(activeTab.requestId) ?? null : null;
74386
+ const activeRequest = reactExports.useMemo(() => {
74387
+ if (!activeBaseRequest) return null;
74388
+ if (!activeTab?.exampleId) return activeBaseRequest;
74389
+ const ex = activeBaseRequest.examples?.find((e) => e.id === activeTab.exampleId);
74390
+ if (!ex) return activeBaseRequest;
74391
+ const o = ex.request ?? {};
74392
+ return {
74393
+ ...activeBaseRequest,
74394
+ ...o,
74395
+ params: o.params ?? [],
74396
+ headers: o.headers ?? [],
74397
+ body: o.body ?? { mode: "none" }
74398
+ };
74399
+ }, [activeBaseRequest, activeTab?.exampleId]);
72579
74400
  const openTabContextMenu = reactExports.useCallback((tabId, x, y) => {
72580
74401
  setTabContextMenu({ x, y, tabId });
72581
74402
  }, []);
@@ -72600,7 +74421,7 @@ function App() {
72600
74421
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
72601
74422
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
72602
74423
  "v",
72603
- "0.4.6"
74424
+ "0.4.8"
72604
74425
  ] }),
72605
74426
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
72606
74427
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -72717,12 +74538,13 @@ function App() {
72717
74538
  tabs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border-b border-surface-800 bg-surface-950 flex-shrink-0", children: [
72718
74539
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center overflow-x-auto flex-1 min-w-0", children: tabs.map((tab) => {
72719
74540
  const req = tab.requestId ? requestsById.get(tab.requestId) ?? null : null;
74541
+ const exampleName = tab.exampleId ? req?.examples?.find((e) => e.id === tab.exampleId)?.name : null;
72720
74542
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
72721
74543
  TabRow,
72722
74544
  {
72723
74545
  tabId: tab.id,
72724
74546
  method: req?.method,
72725
- name: req?.name ?? "Untitled",
74547
+ name: exampleName ? `${req?.name ?? "Untitled"} · ${exampleName}` : req?.name ?? "Untitled",
72726
74548
  isActive: tab.id === activeTabId,
72727
74549
  onActivate: setActiveTabId,
72728
74550
  onClose: closeTab,