@testsmith/api-spector 0.5.8 → 0.5.9

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.
@@ -13317,6 +13317,23 @@ function v4(options, buf, offset) {
13317
13317
  rnds[8] = rnds[8] & 63 | 128;
13318
13318
  return unsafeStringify(rnds);
13319
13319
  }
13320
+ function orderedChildren(folder2) {
13321
+ const reqSet = new Set(folder2.requestIds);
13322
+ const folderSet = new Set(folder2.folders.map((f) => f.id));
13323
+ const seen2 = /* @__PURE__ */ new Set();
13324
+ const out = [];
13325
+ for (const ref2 of folder2.childOrder ?? []) {
13326
+ const key = `${ref2.type}:${ref2.id}`;
13327
+ if (seen2.has(key)) continue;
13328
+ if (ref2.type === "request" && reqSet.has(ref2.id) || ref2.type === "folder" && folderSet.has(ref2.id)) {
13329
+ out.push({ type: ref2.type, id: ref2.id });
13330
+ seen2.add(key);
13331
+ }
13332
+ }
13333
+ for (const id2 of folder2.requestIds) if (!seen2.has(`request:${id2}`)) out.push({ type: "request", id: id2 });
13334
+ for (const f of folder2.folders) if (!seen2.has(`folder:${f.id}`)) out.push({ type: "folder", id: f.id });
13335
+ return out;
13336
+ }
13320
13337
  function findFolder$1(root2, id2) {
13321
13338
  if (root2.id === id2) return root2;
13322
13339
  for (const sub of root2.folders) {
@@ -14121,6 +14138,30 @@ const createCollectionsSlice = (set2, get2) => ({
14121
14138
  }
14122
14139
  s.collections[collectionId].dirty = true;
14123
14140
  }),
14141
+ reorderChild: (collectionId, ref2, destParentFolderId, destIndex) => set2((s) => {
14142
+ const col = s.collections[collectionId]?.data;
14143
+ if (!col) return;
14144
+ const destParent = findFolder$1(col.rootFolder, destParentFolderId);
14145
+ if (!destParent) return;
14146
+ if (ref2.type === "request") {
14147
+ if (!col.requests[ref2.id]) return;
14148
+ removeFromFolder(col.rootFolder, ref2.id);
14149
+ if (!destParent.requestIds.includes(ref2.id)) destParent.requestIds.push(ref2.id);
14150
+ } else {
14151
+ const moving = findFolder$1(col.rootFolder, ref2.id);
14152
+ if (!moving) return;
14153
+ if (ref2.id === destParentFolderId || findFolder$1(moving, destParentFolderId)) return;
14154
+ const srcParent = findFolderParent(col.rootFolder, ref2.id) ?? col.rootFolder;
14155
+ const srcIdx = srcParent.folders.findIndex((f) => f.id === ref2.id);
14156
+ if (srcIdx >= 0) srcParent.folders.splice(srcIdx, 1);
14157
+ if (!destParent.folders.some((f) => f.id === ref2.id)) destParent.folders.push(moving);
14158
+ }
14159
+ const others = orderedChildren(destParent).filter((c) => !(c.type === ref2.type && c.id === ref2.id));
14160
+ const idx = Math.max(0, Math.min(destIndex, others.length));
14161
+ others.splice(idx, 0, { type: ref2.type, id: ref2.id });
14162
+ destParent.childOrder = others;
14163
+ s.collections[collectionId].dirty = true;
14164
+ }),
14124
14165
  // ── Tags ────────────────────────────────────────────────────────────────────
14125
14166
  updateFolderTags: (collectionId, folderId, tags2) => set2((s) => {
14126
14167
  const col = s.collections[collectionId]?.data;
@@ -35180,6 +35221,16 @@ function useVarNames() {
35180
35221
  const col = collections[activeCollectionId]?.data;
35181
35222
  if (col) {
35182
35223
  Object.keys(col.collectionVariables ?? {}).forEach((k) => names2.add(k));
35224
+ (col.dataSet?.columns ?? []).forEach((c) => {
35225
+ if (c) names2.add(c);
35226
+ });
35227
+ const addFolderCols = (f) => {
35228
+ (f.dataSet?.columns ?? []).forEach((c) => {
35229
+ if (c) names2.add(c);
35230
+ });
35231
+ f.folders.forEach(addFolderCols);
35232
+ };
35233
+ addFolderCols(col.rootFolder);
35183
35234
  for (const req of Object.values(col.requests)) {
35184
35235
  extractScriptVarNames(req.preRequestScript).forEach((k) => names2.add(k));
35185
35236
  extractScriptVarNames(req.postRequestScript).forEach((k) => names2.add(k));
@@ -35417,6 +35468,8 @@ const Basic$6 = "Basic";
35417
35468
  const Standard$6 = "Standard";
35418
35469
  const Aggressive$6 = "Aggressive";
35419
35470
  const Next$6 = "Next";
35471
+ const More$6 = "More";
35472
+ const or$6 = "or";
35420
35473
  const en = {
35421
35474
  "Close tab": "Close tab",
35422
35475
  Collections: Collections$6,
@@ -36455,7 +36508,20 @@ const en = {
36455
36508
  "Replay the guided walkthrough of collections, environments and requests.": "Replay the guided walkthrough of collections, environments and requests.",
36456
36509
  "Schema: a local scratch check.": "Schema: a local scratch check.",
36457
36510
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.",
36458
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab."
36511
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.",
36512
+ More: More$6,
36513
+ "Response sections": "Response sections",
36514
+ "More response sections": "More response sections",
36515
+ "More actions": "More actions",
36516
+ "Body view mode": "Body view mode",
36517
+ "Cell values can also use variables and faker, e.g.": "Cell values can also use variables and faker, e.g.",
36518
+ or: or$6,
36519
+ "Script errors": "Script errors",
36520
+ "Pre-script:": "Pre-script:",
36521
+ "Post-script:": "Post-script:",
36522
+ "No data row": "No data row",
36523
+ "Row :n": "Row :n",
36524
+ "Run this request with the variables from one data table row": "Run this request with the variables from one data table row"
36459
36525
  };
36460
36526
  const Collections$5 = "Sammlungen";
36461
36527
  const History$5 = "Verlauf";
@@ -36655,6 +36721,8 @@ const Basic$5 = "Einfach";
36655
36721
  const Standard$5 = "Standard";
36656
36722
  const Aggressive$5 = "Aggressiv";
36657
36723
  const Next$5 = "Weiter";
36724
+ const More$5 = "Mehr";
36725
+ const or$5 = "oder";
36658
36726
  const de = {
36659
36727
  "Close tab": "Tab schließen",
36660
36728
  Collections: Collections$5,
@@ -37693,7 +37761,20 @@ const de = {
37693
37761
  "Replay the guided walkthrough of collections, environments and requests.": "Wiederhole die geführte Tour durch Sammlungen, Umgebungen und Requests.",
37694
37762
  "Schema: a local scratch check.": "Schema: eine lokale Schnellprüfung.",
37695
37763
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Validiere die letzte Antwort dieses Requests hier direkt gegen ein JSON Schema. Sie wird **nicht im Contract gespeichert und nicht veröffentlicht**. Nur eine Sanity-Prüfung zur Entwicklungszeit. Um zu definieren, was der Provider zurückgeben *muss* (was Contract-Tests steuert), verwende den **Contract**-Tab.",
37696
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Was dieses Request vom Provider *erwartet*: Status, Header und Body-Form (ein JSON Schema). Es wird im **Contract**-Panel gegen die Antwort geprüft und ist die Consumer-Seite von Contract-Tests (Consumer- und bidirektionale Verifikation), die den `deploy-check`-Gate speist. Für eine schnelle Body-Prüfung bei jeder Sendung verwende den **Schema**-Tab."
37764
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Was dieses Request vom Provider *erwartet*: Status, Header und Body-Form (ein JSON Schema). Es wird im **Contract**-Panel gegen die Antwort geprüft und ist die Consumer-Seite von Contract-Tests (Consumer- und bidirektionale Verifikation), die den `deploy-check`-Gate speist. Für eine schnelle Body-Prüfung bei jeder Sendung verwende den **Schema**-Tab.",
37765
+ More: More$5,
37766
+ "Response sections": "Antwortbereiche",
37767
+ "More response sections": "Weitere Antwortbereiche",
37768
+ "More actions": "Weitere Aktionen",
37769
+ "Body view mode": "Body-Ansichtsmodus",
37770
+ "Cell values can also use variables and faker, e.g.": "Zellwerte können auch Variablen und faker verwenden, z. B.",
37771
+ or: or$5,
37772
+ "Script errors": "Skriptfehler",
37773
+ "Pre-script:": "Pre-Script:",
37774
+ "Post-script:": "Post-Script:",
37775
+ "No data row": "Keine Datenzeile",
37776
+ "Row :n": "Zeile :n",
37777
+ "Run this request with the variables from one data table row": "Diesen Request mit den Variablen einer Datentabellenzeile ausführen"
37697
37778
  };
37698
37779
  const Collections$4 = "Collections";
37699
37780
  const History$4 = "Historique";
@@ -37893,6 +37974,8 @@ const Basic$4 = "Basique";
37893
37974
  const Standard$4 = "Standard";
37894
37975
  const Aggressive$4 = "Agressif";
37895
37976
  const Next$4 = "Suivant";
37977
+ const More$4 = "Plus";
37978
+ const or$4 = "ou";
37896
37979
  const fr = {
37897
37980
  "Close tab": "Fermer l'onglet",
37898
37981
  Collections: Collections$4,
@@ -38931,7 +39014,20 @@ const fr = {
38931
39014
  "Replay the guided walkthrough of collections, environments and requests.": "Rejouez la visite guidée des collections, environnements et requests.",
38932
39015
  "Schema: a local scratch check.": "Schema : une vérification locale de travail.",
38933
39016
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Validez la dernière réponse de ce request directement ici par rapport à un JSON Schema. Elle n'est **pas enregistrée dans le contract ni publiée**. Juste une vérification de bon sens en développement. Pour définir ce que le provider *doit* renvoyer (ce qui pilote les tests de contract), utilisez l'onglet **Contract**.",
38934
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Ce que ce request *attend* du provider : statut, en-têtes et forme du body (un JSON Schema). C'est vérifié par rapport à la réponse dans le panneau **Contract** et c'est le côté consommateur des tests de contract (vérification consommateur et bidirectionnelle), qui alimente la gate `deploy-check`. Pour une vérification rapide du body à chaque envoi, utilisez l'onglet **Schema**."
39017
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Ce que ce request *attend* du provider : statut, en-têtes et forme du body (un JSON Schema). C'est vérifié par rapport à la réponse dans le panneau **Contract** et c'est le côté consommateur des tests de contract (vérification consommateur et bidirectionnelle), qui alimente la gate `deploy-check`. Pour une vérification rapide du body à chaque envoi, utilisez l'onglet **Schema**.",
39018
+ More: More$4,
39019
+ "Response sections": "Sections de réponse",
39020
+ "More response sections": "Autres sections de réponse",
39021
+ "More actions": "Autres actions",
39022
+ "Body view mode": "Mode d’affichage du body",
39023
+ "Cell values can also use variables and faker, e.g.": "Les valeurs de cellule peuvent aussi utiliser des variables et faker, p. ex.",
39024
+ or: or$4,
39025
+ "Script errors": "Erreurs de script",
39026
+ "Pre-script:": "Pré-script :",
39027
+ "Post-script:": "Post-script :",
39028
+ "No data row": "Aucune ligne de données",
39029
+ "Row :n": "Ligne :n",
39030
+ "Run this request with the variables from one data table row": "Exécuter ce request avec les variables d'une ligne de la table de données"
38935
39031
  };
38936
39032
  const Collections$3 = "Colecciones";
38937
39033
  const History$3 = "Historial";
@@ -39131,6 +39227,8 @@ const Basic$3 = "Básico";
39131
39227
  const Standard$3 = "Estándar";
39132
39228
  const Aggressive$3 = "Agresivo";
39133
39229
  const Next$3 = "Siguiente";
39230
+ const More$3 = "Más";
39231
+ const or$3 = "o";
39134
39232
  const es = {
39135
39233
  "Close tab": "Cerrar pestaña",
39136
39234
  Collections: Collections$3,
@@ -40169,7 +40267,20 @@ const es = {
40169
40267
  "Replay the guided walkthrough of collections, environments and requests.": "Vuelve a ver el recorrido guiado por colecciones, entornos y requests.",
40170
40268
  "Schema: a local scratch check.": "Schema: una comprobación local rápida.",
40171
40269
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Valida la última respuesta de este request aquí mismo contra un JSON Schema. **No se guarda en el contract ni se publica**. Solo una comprobación de validez en desarrollo. Para definir lo que el provider *debe* devolver (lo que impulsa las pruebas de contract), usa la pestaña **Contract**.",
40172
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Lo que este request *espera* del provider: estado, headers y forma del body (un JSON Schema). Se comprueba contra la respuesta en el panel **Contract** y es el lado consumidor de las pruebas de contract (verificación de consumidor y bidireccional), que alimenta la gate `deploy-check`. Para una comprobación rápida del body en cada envío, usa la pestaña **Schema**."
40270
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Lo que este request *espera* del provider: estado, headers y forma del body (un JSON Schema). Se comprueba contra la respuesta en el panel **Contract** y es el lado consumidor de las pruebas de contract (verificación de consumidor y bidireccional), que alimenta la gate `deploy-check`. Para una comprobación rápida del body en cada envío, usa la pestaña **Schema**.",
40271
+ More: More$3,
40272
+ "Response sections": "Secciones de respuesta",
40273
+ "More response sections": "Más secciones de respuesta",
40274
+ "More actions": "Más acciones",
40275
+ "Body view mode": "Modo de vista del body",
40276
+ "Cell values can also use variables and faker, e.g.": "Los valores de celda también pueden usar variables y faker, p. ej.",
40277
+ or: or$3,
40278
+ "Script errors": "Errores de script",
40279
+ "Pre-script:": "Pre-script:",
40280
+ "Post-script:": "Post-script:",
40281
+ "No data row": "Sin fila de datos",
40282
+ "Row :n": "Fila :n",
40283
+ "Run this request with the variables from one data table row": "Ejecuta este request con las variables de una fila de la tabla de datos"
40173
40284
  };
40174
40285
  const Collections$2 = "Raccolte";
40175
40286
  const History$2 = "Cronologia";
@@ -40369,6 +40480,8 @@ const Basic$2 = "Base";
40369
40480
  const Standard$2 = "Standard";
40370
40481
  const Aggressive$2 = "Aggressivo";
40371
40482
  const Next$2 = "Avanti";
40483
+ const More$2 = "Altro";
40484
+ const or$2 = "o";
40372
40485
  const it = {
40373
40486
  "Close tab": "Chiudi scheda",
40374
40487
  Collections: Collections$2,
@@ -41407,7 +41520,20 @@ const it = {
41407
41520
  "Replay the guided walkthrough of collections, environments and requests.": "Riproduci la panoramica guidata di raccolte, ambienti e requests.",
41408
41521
  "Schema: a local scratch check.": "Schema: un controllo locale rapido.",
41409
41522
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Valida l'ultima risposta di questo request qui stesso rispetto a un JSON Schema. **Non viene salvata nel contract né pubblicata**. Solo un controllo di validità in fase di sviluppo. Per definire ciò che il provider *deve* restituire (ciò che guida i test di contract), usa la scheda **Contract**.",
41410
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Ciò che questo request *si aspetta* dal provider: stato, header e forma del body (un JSON Schema). Viene verificato rispetto alla risposta nel pannello **Contract** ed è il lato consumer dei test di contract (verifica consumer e bidirezionale), che alimenta la gate `deploy-check`. Per un controllo rapido del body a ogni invio, usa la scheda **Schema**."
41523
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Ciò che questo request *si aspetta* dal provider: stato, header e forma del body (un JSON Schema). Viene verificato rispetto alla risposta nel pannello **Contract** ed è il lato consumer dei test di contract (verifica consumer e bidirezionale), che alimenta la gate `deploy-check`. Per un controllo rapido del body a ogni invio, usa la scheda **Schema**.",
41524
+ More: More$2,
41525
+ "Response sections": "Sezioni della risposta",
41526
+ "More response sections": "Altre sezioni della risposta",
41527
+ "More actions": "Altre azioni",
41528
+ "Body view mode": "Modalità di visualizzazione del body",
41529
+ "Cell values can also use variables and faker, e.g.": "I valori delle celle possono usare anche variabili e faker, ad es.",
41530
+ or: or$2,
41531
+ "Script errors": "Errori di script",
41532
+ "Pre-script:": "Pre-script:",
41533
+ "Post-script:": "Post-script:",
41534
+ "No data row": "Nessuna riga di dati",
41535
+ "Row :n": "Riga :n",
41536
+ "Run this request with the variables from one data table row": "Esegui questo request con le variabili di una riga della tabella dati"
41411
41537
  };
41412
41538
  const Collections$1 = "Collecties";
41413
41539
  const History$1 = "Geschiedenis";
@@ -41607,6 +41733,8 @@ const Basic$1 = "Basis";
41607
41733
  const Standard$1 = "Standaard";
41608
41734
  const Aggressive$1 = "Agressief";
41609
41735
  const Next$1 = "Volgende";
41736
+ const More$1 = "Meer";
41737
+ const or$1 = "of";
41610
41738
  const nl = {
41611
41739
  "Close tab": "Tabblad sluiten",
41612
41740
  Collections: Collections$1,
@@ -42645,7 +42773,20 @@ const nl = {
42645
42773
  "Replay the guided walkthrough of collections, environments and requests.": "Speel de rondleiding door collecties, omgevingen en requests opnieuw af.",
42646
42774
  "Schema: a local scratch check.": "Schema: een lokale kladcontrole.",
42647
42775
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Valideer de laatste respons van dit request hier direct tegen een JSON Schema. Het wordt **niet opgeslagen in het contract en niet gepubliceerd**. Alleen een sanity check tijdens ontwikkeling. Om te definiëren wat de provider *moet* retourneren (wat contracttesten aanstuurt), gebruik je het **Contract** tabblad.",
42648
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Wat dit request *verwacht* van de provider: status, headers en body-vorm (een JSON Schema). Het wordt gecontroleerd tegen de respons in het **Contract** paneel en is de consumerzijde van contracttesten (consumer- en bidirectionele verificatie), wat de `deploy-check` gate voedt. Gebruik voor een snelle body-controle bij elke verzending het **Schema** tabblad."
42776
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "Wat dit request *verwacht* van de provider: status, headers en body-vorm (een JSON Schema). Het wordt gecontroleerd tegen de respons in het **Contract** paneel en is de consumerzijde van contracttesten (consumer- en bidirectionele verificatie), wat de `deploy-check` gate voedt. Gebruik voor een snelle body-controle bij elke verzending het **Schema** tabblad.",
42777
+ More: More$1,
42778
+ "Response sections": "Responssecties",
42779
+ "More response sections": "Meer responssecties",
42780
+ "More actions": "Meer acties",
42781
+ "Body view mode": "Body-weergavemodus",
42782
+ "Cell values can also use variables and faker, e.g.": "Celwaarden kunnen ook variabelen en faker gebruiken, bijv.",
42783
+ or: or$1,
42784
+ "Script errors": "Scriptfouten",
42785
+ "Pre-script:": "Pre-script:",
42786
+ "Post-script:": "Post-script:",
42787
+ "No data row": "Geen datarij",
42788
+ "Row :n": "Rij :n",
42789
+ "Run this request with the variables from one data table row": "Voer dit request uit met de variabelen van één datatabelrij"
42649
42790
  };
42650
42791
  const Collections = "Coleções";
42651
42792
  const History = "Histórico";
@@ -42845,6 +42986,8 @@ const Basic = "Básico";
42845
42986
  const Standard = "Padrão";
42846
42987
  const Aggressive = "Agressivo";
42847
42988
  const Next = "Seguinte";
42989
+ const More = "Mais";
42990
+ const or = "ou";
42848
42991
  const pt = {
42849
42992
  "Close tab": "Fechar separador",
42850
42993
  Collections,
@@ -43883,7 +44026,20 @@ const pt = {
43883
44026
  "Replay the guided walkthrough of collections, environments and requests.": "Reproduz o guia de coleções, ambientes e requests.",
43884
44027
  "Schema: a local scratch check.": "Schema: uma verificação local rápida.",
43885
44028
  "Validate this request's last response against a JSON Schema, right here. It is **not saved to the contract and not published**. A dev-time sanity check only. To define what the provider *must* return (which drives contract testing), use the **Contract** tab.": "Valida a última resposta deste request aqui mesmo contra um JSON Schema. **Não é guardada no contract nem publicada**. Apenas uma verificação de sanidade em desenvolvimento. Para definir o que o provider *deve* devolver (o que orienta os testes de contract), usa o separador **Contract**.",
43886
- "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "O que este request *espera* do provider: estado, headers e forma do body (um JSON Schema). É verificado contra a resposta no painel **Contract** e é o lado consumidor dos testes de contract (verificação de consumidor e bidirecional), que alimenta a gate `deploy-check`. Para uma verificação rápida do body a cada envio, usa o separador **Schema**."
44029
+ "What this request *expects* from the provider: status, headers, and body shape (a JSON Schema). It is checked against the response in the **Contract** panel and is the consumer side of contract testing (consumer and bi-directional verify), feeding the `deploy-check` gate. For a quick body check that runs on every send, use the **Schema** tab.": "O que este request *espera* do provider: estado, headers e forma do body (um JSON Schema). É verificado contra a resposta no painel **Contract** e é o lado consumidor dos testes de contract (verificação de consumidor e bidirecional), que alimenta a gate `deploy-check`. Para uma verificação rápida do body a cada envio, usa o separador **Schema**.",
44030
+ More,
44031
+ "Response sections": "Secções da resposta",
44032
+ "More response sections": "Mais secções da resposta",
44033
+ "More actions": "Mais ações",
44034
+ "Body view mode": "Modo de visualização do body",
44035
+ "Cell values can also use variables and faker, e.g.": "Os valores das células também podem usar variáveis e faker, por ex.",
44036
+ or,
44037
+ "Script errors": "Erros de script",
44038
+ "Pre-script:": "Pré-script:",
44039
+ "Post-script:": "Pós-script:",
44040
+ "No data row": "Sem linha de dados",
44041
+ "Row :n": "Linha :n",
44042
+ "Run this request with the variables from one data table row": "Executa este request com as variáveis de uma linha da tabela de dados"
43887
44043
  };
43888
44044
  const CATALOGS = { en, de, fr, es, it, nl, pt };
43889
44045
  const LOCALES = {
@@ -43902,8 +44058,7 @@ function detectLocale() {
43902
44058
  if (saved && saved in CATALOGS) return saved;
43903
44059
  } catch {
43904
44060
  }
43905
- const nav2 = (typeof navigator !== "undefined" ? navigator.language : "en").slice(0, 2);
43906
- return nav2 in CATALOGS ? nav2 : "en";
44061
+ return "en";
43907
44062
  }
43908
44063
  function interpolate(str, vars) {
43909
44064
  if (!vars) return str;
@@ -43951,6 +44106,18 @@ function useLocale() {
43951
44106
  const { locale, setLocale } = reactExports.useContext(I18nContext);
43952
44107
  return [locale, setLocale];
43953
44108
  }
44109
+ const DAYJS_OPTIONS = [
44110
+ { label: "dayjs().format('YYYY-MM-DD')", insert: "dayjs().format('YYYY-MM-DD')" },
44111
+ { label: "dayjs().toISOString()", insert: "dayjs().toISOString()" },
44112
+ { label: "dayjs().valueOf()", insert: "dayjs().valueOf()" },
44113
+ { label: "dayjs().subtract(1,'day').format(…)", insert: "dayjs().subtract(1,'day').format('YYYY-MM-DD')" },
44114
+ { label: "dayjs().add(1,'day').format(…)", insert: "dayjs().add(1,'day').format('YYYY-MM-DD')" }
44115
+ ];
44116
+ const ICON = {
44117
+ variable: { glyph: "𝑥", cls: "text-emerald-400" },
44118
+ function: { glyph: "ƒ", cls: "text-violet-400" },
44119
+ property: { glyph: "◆", cls: "text-blue-400" }
44120
+ };
43954
44121
  function parseVarTokens(str) {
43955
44122
  const found = [];
43956
44123
  const re = /\{\{([^}]+)\}\}/g;
@@ -43972,23 +44139,23 @@ function PortalDropdown({
43972
44139
  /* @__PURE__ */ jsxRuntimeExports.jsx(
43973
44140
  "ul",
43974
44141
  {
43975
- style: { position: "fixed", top: pos.top, left: pos.left, minWidth: pos.minWidth, zIndex: 9999 },
43976
- className: "max-h-52 overflow-y-auto bg-surface-800 border border-surface-600 rounded shadow-xl text-xs",
43977
- children: items2.map((s, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
44142
+ style: { position: "fixed", top: pos.top, left: pos.left, minWidth: Math.max(pos.minWidth, 220), zIndex: 9999 },
44143
+ className: "max-h-56 overflow-y-auto bg-surface-800 border border-surface-600 rounded shadow-xl text-xs py-0.5",
44144
+ children: items2.map((s, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
43978
44145
  "button",
43979
44146
  {
43980
44147
  onMouseDown: (e) => {
43981
44148
  e.preventDefault();
43982
44149
  onPick(s);
43983
44150
  },
43984
- className: `w-full text-left px-3 py-1.5 font-mono transition-colors ${i === activeIndex ? "bg-blue-600 text-white" : "hover:bg-surface-700"}`,
43985
- children: mode === "var" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
43986
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "{{" }),
43987
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: s }),
43988
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500", children: "}}" })
43989
- ] }) : s
44151
+ className: `w-full text-left px-2 py-1 font-mono transition-colors flex items-center gap-1.5 ${i === activeIndex ? "bg-blue-600 text-white" : "hover:bg-surface-700"}`,
44152
+ children: [
44153
+ mode === "var" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-3 shrink-0 text-center ${i === activeIndex ? "text-white/80" : ICON[s.type].cls}`, children: ICON[s.type].glyph }) : null,
44154
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: s.label }),
44155
+ s.detail && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `ml-auto pl-2 shrink-0 italic ${i === activeIndex ? "text-white/70" : "text-surface-500"}`, children: s.detail })
44156
+ ]
43990
44157
  }
43991
- ) }, s))
44158
+ ) }, s.label + i))
43992
44159
  }
43993
44160
  ),
43994
44161
  document.body
@@ -44004,6 +44171,7 @@ function VarInput({ value: value2, onChange, className, wrapperClassName, static
44004
44171
  const [activeIndex, setActiveIndex] = reactExports.useState(-1);
44005
44172
  const [dropPos, setDropPos] = reactExports.useState(null);
44006
44173
  const [showPreview, setShowPreview] = reactExports.useState(false);
44174
+ const partialLen = reactExports.useRef(0);
44007
44175
  reactExports.useEffect(() => {
44008
44176
  if (suggestions.length === 0) {
44009
44177
  setDropPos(null);
@@ -44014,28 +44182,60 @@ function VarInput({ value: value2, onChange, className, wrapperClassName, static
44014
44182
  const r = el.getBoundingClientRect();
44015
44183
  setDropPos({ top: r.bottom + 2, left: r.left, minWidth: r.width });
44016
44184
  }, [suggestions.length]);
44185
+ function show(items2, from) {
44186
+ partialLen.current = from;
44187
+ setSuggestions(items2);
44188
+ setSuggestionMode("var");
44189
+ setActiveIndex(-1);
44190
+ }
44017
44191
  function detectQuery(val, cursor2) {
44018
44192
  const before = val.slice(0, cursor2);
44019
- const varMatch = /\{\{(\w*)$/.exec(before);
44020
- if (varMatch) {
44021
- const q = varMatch[1].toLowerCase();
44022
- const filtered = varNames.filter((n) => n.toLowerCase().includes(q));
44023
- setSuggestions(filtered);
44024
- setSuggestionMode("var");
44025
- setActiveIndex(-1);
44026
- } else if (staticSuggestions) {
44193
+ const sortByMatch = (items2, q) => items2.map((it2, i) => ({ it: it2, i })).sort((a, b) => {
44194
+ const ap = a.it.label.toLowerCase().startsWith(q) ? 0 : 1;
44195
+ const bp = b.it.label.toLowerCase().startsWith(q) ? 0 : 1;
44196
+ return ap - bp || (b.it.boost ?? 0) - (a.it.boost ?? 0) || a.i - b.i;
44197
+ }).map((x) => x.it);
44198
+ let m = /\{\{faker\.(\w+)\.(\w*)$/.exec(before);
44199
+ if (m) {
44200
+ const q = m[2].toLowerCase();
44201
+ const subs = (FAKER_SUB[m[1]] ?? []).filter((c) => c.label.toLowerCase().includes(q));
44202
+ show(sortByMatch(subs.map((c) => ({ label: c.label, type: "function", detail: c.detail ?? "()", insert: `${c.label}()`, close: true })), q), m[2].length);
44203
+ return;
44204
+ }
44205
+ m = /\{\{faker\.(\w*)$/.exec(before);
44206
+ if (m) {
44207
+ const q = m[1].toLowerCase();
44208
+ const items2 = FAKER_NAMESPACES.filter((c) => c.label.toLowerCase().includes(q)).map((c) => ({ label: c.label, type: "property", insert: `${c.label}.` }));
44209
+ show(sortByMatch(items2, q), m[1].length);
44210
+ return;
44211
+ }
44212
+ m = /\{\{(dayjs\b[^}]*)$/.exec(before);
44213
+ if (m) {
44214
+ const q = m[1].toLowerCase();
44215
+ show(DAYJS_OPTIONS.filter((o) => o.label.toLowerCase().includes(q)).map((o) => ({ label: o.label, type: "function", insert: o.insert, close: true })), m[1].length);
44216
+ return;
44217
+ }
44218
+ m = /\{\{(\$?\w*)$/.exec(before);
44219
+ if (m) {
44220
+ const q = m[1].toLowerCase();
44221
+ const vars = varNames.filter((n) => n.toLowerCase().includes(q)).map((n) => ({ label: n, insert: n, type: "variable", close: true, boost: n.startsWith("$") ? 1 : 0 }));
44222
+ const starters = [
44223
+ { label: "faker", insert: "faker.", type: "property" },
44224
+ { label: "dayjs", insert: "dayjs().", type: "function" }
44225
+ ].filter((s) => s.label.includes(q));
44226
+ show([...sortByMatch(vars, q), ...starters], m[1].length);
44227
+ return;
44228
+ }
44229
+ if (staticSuggestions) {
44027
44230
  const q = val.toLowerCase();
44028
- const filtered = q ? staticSuggestions.filter((s) => s.toLowerCase().includes(q)).sort((a, b) => {
44029
- const aP = a.toLowerCase().startsWith(q) ? 0 : 1;
44030
- const bP = b.toLowerCase().startsWith(q) ? 0 : 1;
44031
- return aP - bP;
44032
- }).slice(0, 20) : staticSuggestions.slice(0, 20);
44033
- setSuggestions(filtered);
44231
+ const filtered = q ? staticSuggestions.filter((s) => s.toLowerCase().includes(q)).sort((a, b) => (a.toLowerCase().startsWith(q) ? 0 : 1) - (b.toLowerCase().startsWith(q) ? 0 : 1)).slice(0, 20) : staticSuggestions.slice(0, 20);
44232
+ partialLen.current = 0;
44233
+ setSuggestions(filtered.map((s) => ({ label: s, insert: s, type: "variable" })));
44034
44234
  setSuggestionMode("static");
44035
44235
  setActiveIndex(-1);
44036
- } else {
44037
- setSuggestions([]);
44236
+ return;
44038
44237
  }
44238
+ setSuggestions([]);
44039
44239
  }
44040
44240
  function handleChange(e) {
44041
44241
  const newVal = e.target.value;
@@ -44062,9 +44262,9 @@ function VarInput({ value: value2, onChange, className, wrapperClassName, static
44062
44262
  setSuggestions([]);
44063
44263
  }
44064
44264
  }
44065
- const apply = reactExports.useCallback((suggestion) => {
44265
+ function apply(item) {
44066
44266
  if (suggestionMode === "static") {
44067
- onChange(suggestion);
44267
+ onChange(item.insert);
44068
44268
  setSuggestions([]);
44069
44269
  return;
44070
44270
  }
@@ -44072,14 +44272,22 @@ function VarInput({ value: value2, onChange, className, wrapperClassName, static
44072
44272
  const cursor2 = el?.selectionStart ?? value2.length;
44073
44273
  const before = value2.slice(0, cursor2);
44074
44274
  const after = value2.slice(cursor2);
44075
- const match = /\{\{(\w*)$/.exec(before);
44076
- if (!match) return;
44077
- const newVal = before.slice(0, match.index) + `{{${suggestion}}}` + after;
44078
- const newCursor = match.index + suggestion.length + 4;
44275
+ const start = cursor2 - partialLen.current;
44276
+ const insert2 = item.insert + (item.close ? "}}" : "");
44277
+ const newVal = before.slice(0, start) + insert2 + after;
44278
+ const newCursor = start + insert2.length;
44079
44279
  onChange(newVal);
44080
- setSuggestions([]);
44081
- requestAnimationFrame(() => el?.setSelectionRange(newCursor, newCursor));
44082
- }, [suggestionMode, value2, onChange]);
44280
+ if (item.close) {
44281
+ setSuggestions([]);
44282
+ requestAnimationFrame(() => el?.setSelectionRange(newCursor, newCursor));
44283
+ } else {
44284
+ requestAnimationFrame(() => {
44285
+ el?.focus();
44286
+ el?.setSelectionRange(newCursor, newCursor);
44287
+ detectQuery(newVal, newCursor);
44288
+ });
44289
+ }
44290
+ }
44083
44291
  function handleBlur(e) {
44084
44292
  setTimeout(() => setSuggestions([]), 150);
44085
44293
  setShowPreview(false);
@@ -44919,6 +45127,19 @@ function DataSetEditor({ ds, onChange, exportName, scopeLabel = "collection" })
44919
45127
  " ",
44920
45128
  t2("placeholders.")
44921
45129
  ] }),
45130
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-surface-600 text-[11px]", children: [
45131
+ t2("Cell values can also use variables and faker, e.g."),
45132
+ " ",
45133
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-surface-500", children: "{{baseUrl}}" }),
45134
+ ",",
45135
+ " ",
45136
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-surface-500", children: "{{$randomEmail}}" }),
45137
+ " ",
45138
+ t2("or"),
45139
+ " ",
45140
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-surface-500", children: "{{faker.person.firstName()}}" }),
45141
+ "."
45142
+ ] }),
44922
45143
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
44923
45144
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: addColumn, className: "px-2.5 py-1 bg-surface-700 hover:bg-surface-600 rounded transition-colors", children: t2("+ Column") }),
44924
45145
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: addRow, disabled: !hasColumns, className: "px-2.5 py-1 bg-surface-700 hover:bg-surface-600 disabled:opacity-40 rounded transition-colors", children: t2("+ Row") }),
@@ -44957,11 +45178,12 @@ function DataSetEditor({ ds, onChange, exportName, scopeLabel = "collection" })
44957
45178
  ds.rows.map((row, ri) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "group border-b border-surface-800/60 hover:bg-surface-800/30", children: [
44958
45179
  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-1 text-surface-600", children: ri + 1 }),
44959
45180
  ds.columns.map((_, ci) => /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-1 py-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
44960
- "input",
45181
+ VarInput,
44961
45182
  {
44962
45183
  value: row[ci] ?? "",
44963
- onChange: (e) => setCell(ri, ci, e.target.value),
44964
- className: "w-full bg-surface-800 border border-transparent rounded px-1.5 py-0.5 font-mono focus:outline-none focus:border-blue-500 hover:border-surface-600"
45184
+ onChange: (v) => setCell(ri, ci, v),
45185
+ wrapperClassName: "w-full",
45186
+ className: "bg-surface-800 border border-transparent rounded px-1.5 py-0.5 font-mono focus:outline-none focus:border-blue-500 hover:border-surface-600"
44965
45187
  }
44966
45188
  ) }, ci)),
44967
45189
  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-1 py-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => removeRow(ri), className: "text-surface-400 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-all", children: "×" }) })
@@ -45574,36 +45796,43 @@ function buildFolderPlan(folder2, requests, collectionVars, filterTags, scopeId,
45574
45796
  for (const req of beforeAllHooks) {
45575
45797
  result.push(makeHook(req, collectionVars, "beforeAll", scopeId, ancestorIds, scopePath));
45576
45798
  }
45577
- for (const req of regularReqs) {
45578
- const tags2 = req.meta?.tags ?? [];
45579
- if (filterTags.length > 0 && !filterTags.some((t2) => tags2.includes(t2))) continue;
45580
- for (const w of allWrappers) {
45581
- for (const hookReq of w.before) {
45582
- result.push(makeHook(hookReq, collectionVars, "before", w.scopeId, w.ancestors, w.scopePath, req.id));
45799
+ const regularById = new Map(regularReqs.map((r) => [r.id, r]));
45800
+ const subById = new Map(folder2.folders.map((f) => [f.id, f]));
45801
+ for (const child of orderedChildren(folder2)) {
45802
+ if (child.type === "request") {
45803
+ const req = regularById.get(child.id);
45804
+ if (!req) continue;
45805
+ const tags2 = req.meta?.tags ?? [];
45806
+ if (filterTags.length > 0 && !filterTags.some((t2) => tags2.includes(t2))) continue;
45807
+ for (const w of allWrappers) {
45808
+ for (const hookReq of w.before) {
45809
+ result.push(makeHook(hookReq, collectionVars, "before", w.scopeId, w.ancestors, w.scopePath, req.id));
45810
+ }
45583
45811
  }
45584
- }
45585
- result.push({ request: req, collectionVars, scopeId, scopeAncestors: ancestorIds, scopePath });
45586
- for (const w of [...allWrappers].reverse()) {
45587
- for (const hookReq of w.after) {
45588
- result.push(makeHook(hookReq, collectionVars, "after", w.scopeId, w.ancestors, w.scopePath, req.id));
45812
+ result.push({ request: req, collectionVars, scopeId, scopeAncestors: ancestorIds, scopePath });
45813
+ for (const w of [...allWrappers].reverse()) {
45814
+ for (const hookReq of w.after) {
45815
+ result.push(makeHook(hookReq, collectionVars, "after", w.scopeId, w.ancestors, w.scopePath, req.id));
45816
+ }
45589
45817
  }
45818
+ } else {
45819
+ const sub = subById.get(child.id);
45820
+ if (!sub) continue;
45821
+ const folderTags = sub.tags ?? [];
45822
+ const effectiveFilter = filterTags.length === 0 ? filterTags : folderTags.some((t2) => filterTags.includes(t2)) ? [] : filterTags;
45823
+ result.push(...buildFolderPlan(
45824
+ sub,
45825
+ requests,
45826
+ collectionVars,
45827
+ effectiveFilter,
45828
+ sub.id,
45829
+ [...ancestorIds, scopeId],
45830
+ scopePath,
45831
+ allWrappers,
45832
+ false
45833
+ ));
45590
45834
  }
45591
45835
  }
45592
- for (const sub of folder2.folders) {
45593
- const folderTags = sub.tags ?? [];
45594
- const effectiveFilter = filterTags.length === 0 ? filterTags : folderTags.some((t2) => filterTags.includes(t2)) ? [] : filterTags;
45595
- result.push(...buildFolderPlan(
45596
- sub,
45597
- requests,
45598
- collectionVars,
45599
- effectiveFilter,
45600
- sub.id,
45601
- [...ancestorIds, scopeId],
45602
- scopePath,
45603
- allWrappers,
45604
- false
45605
- ));
45606
- }
45607
45836
  for (const req of afterAllHooks) {
45608
45837
  result.push(makeHook(req, collectionVars, "afterAll", scopeId, ancestorIds, scopePath));
45609
45838
  }
@@ -45754,6 +45983,42 @@ function buildRunPlan(collection2, folderId, filterTags) {
45754
45983
  }
45755
45984
  return result;
45756
45985
  }
45986
+ function expandRunPlanWithData(items2, ds) {
45987
+ if (!ds || ds.rows.length === 0) return items2;
45988
+ return ds.rows.flatMap((row, ri) => {
45989
+ const rowVars = {};
45990
+ ds.columns.forEach((col, ci) => {
45991
+ if (col) rowVars[col] = row[ci] ?? "";
45992
+ });
45993
+ return items2.map((item) => ({ ...item, dataRow: { ...item.dataRow, ...rowVars }, iterationLabel: `${ri + 1}/${ds.rows.length}` }));
45994
+ });
45995
+ }
45996
+ function nearestFolderDataSet(item, collection2) {
45997
+ const chain = [item.scopeId, ...item.scopeAncestors ?? []].filter(Boolean);
45998
+ for (const id2 of chain) {
45999
+ const f = findFolder$1(collection2.rootFolder, id2);
46000
+ if (f?.dataSet && f.dataSet.rows.length > 0) return f.dataSet;
46001
+ }
46002
+ return void 0;
46003
+ }
46004
+ function expandFolderDataSets(items2, collection2) {
46005
+ const out = [];
46006
+ for (const item of items2) {
46007
+ const ds = item.isHook ? void 0 : nearestFolderDataSet(item, collection2);
46008
+ if (!ds) {
46009
+ out.push(item);
46010
+ continue;
46011
+ }
46012
+ ds.rows.forEach((row, ri) => {
46013
+ const rowVars = {};
46014
+ ds.columns.forEach((col, ci) => {
46015
+ if (col) rowVars[col] = row[ci] ?? "";
46016
+ });
46017
+ out.push({ ...item, dataRow: { ...item.dataRow, ...rowVars }, iterationLabel: `${ri + 1}/${ds.rows.length}` });
46018
+ });
46019
+ }
46020
+ return out;
46021
+ }
45757
46022
  function hasContract(r) {
45758
46023
  const c = r.contract;
45759
46024
  return !!c && (c.statusCode != null || !!c.bodySchema || !!c.bodyMatcher || (c.headers?.length ?? 0) > 0 || (c.providerStates?.length ?? 0) > 0);
@@ -46204,8 +46469,6 @@ const AUTH_BADGE_LABELS = {
46204
46469
  function RequestRow({
46205
46470
  reqId,
46206
46471
  collectionId,
46207
- folderId,
46208
- reqIndex,
46209
46472
  name: name2,
46210
46473
  url,
46211
46474
  method,
@@ -46237,7 +46500,6 @@ function RequestRow({
46237
46500
  const [examplesOpen, setExamplesOpen] = reactExports.useState(true);
46238
46501
  const [addingTag, setAddingTag] = reactExports.useState(false);
46239
46502
  const [showSchemaSync, setShowSchemaSync] = reactExports.useState(false);
46240
- const [dropPos, setDropPos] = reactExports.useState(null);
46241
46503
  const dragCtx = reactExports.useContext(DragCtx);
46242
46504
  const sel = reactExports.useContext(SelectionCtx);
46243
46505
  const selected = sel.isSelected(collectionId, reqId);
@@ -46266,21 +46528,7 @@ function RequestRow({
46266
46528
  label: (hookType === ht ? "✓ " : " ") + t2(HOOK_LABELS[ht]),
46267
46529
  onClick: () => onSetHookType(hookType === ht ? void 0 : ht)
46268
46530
  }));
46269
- function handleDragOver(e) {
46270
- if (!dragCtx.dragging || dragCtx.dragging.type !== "request" || dragCtx.dragging.requestId === reqId) return;
46271
- e.preventDefault();
46272
- const rect = e.currentTarget.getBoundingClientRect();
46273
- setDropPos(e.clientY < rect.top + rect.height / 2 ? "before" : "after");
46274
- }
46275
- function handleDrop(e) {
46276
- e.preventDefault();
46277
- e.stopPropagation();
46278
- const insertIndex = dropPos === "before" ? reqIndex : reqIndex + 1;
46279
- dragCtx.onDropRequest(collectionId, folderId, insertIndex);
46280
- setDropPos(null);
46281
- }
46282
46531
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
46283
- dropPos === "before" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" }),
46284
46532
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
46285
46533
  "div",
46286
46534
  {
@@ -46295,11 +46543,7 @@ function RequestRow({
46295
46543
  },
46296
46544
  onDragEnd: () => {
46297
46545
  dragCtx.setDragging(null);
46298
- setDropPos(null);
46299
46546
  },
46300
- onDragOver: handleDragOver,
46301
- onDragLeave: () => setDropPos(null),
46302
- onDrop: handleDrop,
46303
46547
  children: [
46304
46548
  /* @__PURE__ */ jsxRuntimeExports.jsx(
46305
46549
  "input",
@@ -46395,8 +46639,7 @@ function RequestRow({
46395
46639
  { type: "separator" },
46396
46640
  { type: "item", label: t2("Delete"), icon: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}), danger: true, onClick: onDelete }
46397
46641
  ] }) }),
46398
- toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed bottom-4 right-4 z-[100] w-96", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Toast, { toast }) }),
46399
- dropPos === "after" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" })
46642
+ toast && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed bottom-4 right-4 z-[100] w-96", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Toast, { toast }) })
46400
46643
  ]
46401
46644
  }
46402
46645
  ),
@@ -46493,7 +46736,33 @@ function ConfirmDialog({ message, onConfirm, onCancel }) {
46493
46736
  const DragCtx = reactExports.createContext({ dragging: null, setDragging: () => {
46494
46737
  }, onDropRequest: () => {
46495
46738
  }, onDropFolder: () => {
46739
+ }, onReorderChild: () => {
46496
46740
  } });
46741
+ function DropLine({ collectionId, parentFolderId, index: index2, indent }) {
46742
+ const dragCtx = reactExports.useContext(DragCtx);
46743
+ const [over, setOver] = reactExports.useState(false);
46744
+ if (!dragCtx.dragging) return null;
46745
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
46746
+ "div",
46747
+ {
46748
+ style: { paddingLeft: indent },
46749
+ className: "relative h-2 -my-1 z-20",
46750
+ onDragOver: (e) => {
46751
+ e.preventDefault();
46752
+ e.stopPropagation();
46753
+ setOver(true);
46754
+ },
46755
+ onDragLeave: () => setOver(false),
46756
+ onDrop: (e) => {
46757
+ e.preventDefault();
46758
+ e.stopPropagation();
46759
+ setOver(false);
46760
+ dragCtx.onReorderChild(collectionId, parentFolderId, index2);
46761
+ },
46762
+ children: over && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-x-2 top-1/2 -translate-y-1/2 h-0.5 bg-blue-500 rounded pointer-events-none" })
46763
+ }
46764
+ );
46765
+ }
46497
46766
  const SelectionCtx = reactExports.createContext({ active: false, isSelected: () => false, toggle: () => {
46498
46767
  }, clear: () => {
46499
46768
  } });
@@ -46620,6 +46889,7 @@ function CollectionTree() {
46620
46889
  const openRunner = useStore((s) => s.openRunner);
46621
46890
  const moveRequest = useStore((s) => s.moveRequest);
46622
46891
  const moveFolder = useStore((s) => s.moveFolder);
46892
+ const reorderChild = useStore((s) => s.reorderChild);
46623
46893
  const colList = Object.values(collections);
46624
46894
  const [query, setQuery] = reactExports.useState("");
46625
46895
  const q = query.trim().toLowerCase();
@@ -46664,7 +46934,17 @@ function CollectionTree() {
46664
46934
  moveFolder(dragging.collectionId, dragging.folderId, destParentFolderId, destIndex);
46665
46935
  setDragging(null);
46666
46936
  }
46667
- 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: [
46937
+ function onReorderChild(destCollectionId, destParentFolderId, destIndex) {
46938
+ if (!dragging) return;
46939
+ if (dragging.collectionId === destCollectionId) {
46940
+ const ref2 = dragging.type === "folder" ? { type: "folder", id: dragging.folderId } : { type: "request", id: dragging.requestId };
46941
+ reorderChild(destCollectionId, ref2, destParentFolderId, destIndex);
46942
+ } else if (dragging.type === "request") {
46943
+ moveRequest(dragging.collectionId, dragging.requestId, destCollectionId, destParentFolderId);
46944
+ }
46945
+ setDragging(null);
46946
+ }
46947
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(DragCtx.Provider, { value: { dragging, setDragging, onDropRequest, onDropFolder, onReorderChild }, 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: [
46668
46948
  pendingConfirm && /* @__PURE__ */ jsxRuntimeExports.jsx(
46669
46949
  ConfirmDialog,
46670
46950
  {
@@ -46951,8 +47231,6 @@ function CollectionNode({
46951
47231
  function FolderRow({
46952
47232
  folder: folder2,
46953
47233
  collectionId,
46954
- parentFolderId,
46955
- folderIndex,
46956
47234
  depth,
46957
47235
  expandCtrl,
46958
47236
  onAddRequest,
@@ -46975,7 +47253,7 @@ function FolderRow({
46975
47253
  const [showPushContract, setShowPushContract] = reactExports.useState(false);
46976
47254
  const [addingTag, setAddingTag] = reactExports.useState(false);
46977
47255
  const folderCollection = useStore((s) => s.collections[collectionId]?.data);
46978
- const [dropPos, setDropPos] = reactExports.useState(null);
47256
+ const [dropInside, setDropInside] = reactExports.useState(false);
46979
47257
  const dragCtx = reactExports.useContext(DragCtx);
46980
47258
  const tags2 = folder2.tags ?? [];
46981
47259
  const indent = depth * 12 + 8;
@@ -46984,40 +47262,22 @@ function FolderRow({
46984
47262
  if (!dragCtx.dragging) return;
46985
47263
  if (dragCtx.dragging.type === "folder" && dragCtx.dragging.folderId === folder2.id) return;
46986
47264
  e.preventDefault();
46987
- const rect = e.currentTarget.getBoundingClientRect();
46988
- const y = e.clientY - rect.top;
46989
- const zone = y / rect.height;
46990
- if (dragCtx.dragging.type === "folder") {
46991
- if (zone < 0.25) setDropPos("before");
46992
- else if (zone > 0.75) setDropPos("after");
46993
- else setDropPos("inside");
46994
- } else {
46995
- setDropPos("inside");
46996
- }
47265
+ setDropInside(true);
46997
47266
  }
46998
47267
  function handleFolderDrop(e) {
46999
47268
  e.preventDefault();
47000
47269
  e.stopPropagation();
47270
+ setDropInside(false);
47001
47271
  if (!dragCtx.dragging) return;
47002
- if (dragCtx.dragging.type === "folder") {
47003
- if (dropPos === "inside") {
47004
- dragCtx.onDropFolder(collectionId, folder2.id);
47005
- } else {
47006
- const insertIndex = dropPos === "before" ? folderIndex : folderIndex + 1;
47007
- dragCtx.onDropFolder(collectionId, parentFolderId, insertIndex);
47008
- }
47009
- } else {
47010
- dragCtx.onDropRequest(collectionId, folder2.id);
47011
- }
47012
- setDropPos(null);
47272
+ if (dragCtx.dragging.type === "folder") dragCtx.onDropFolder(collectionId, folder2.id);
47273
+ else dragCtx.onDropRequest(collectionId, folder2.id);
47013
47274
  }
47014
47275
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
47015
- dropPos === "before" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" }),
47016
47276
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
47017
47277
  "div",
47018
47278
  {
47019
47279
  draggable: true,
47020
- className: `group flex items-start gap-1 py-1 hover:bg-surface-800 transition-colors cursor-pointer text-surface-400 ${dropPos === "inside" ? "outline outline-1 outline-blue-500 rounded" : ""}`,
47280
+ className: `group flex items-start gap-1 py-1 hover:bg-surface-800 transition-colors cursor-pointer text-surface-400 ${dropInside ? "outline outline-1 outline-blue-500 rounded" : ""}`,
47021
47281
  style: { paddingLeft: indent },
47022
47282
  onClick: () => setExpanded((e) => !e),
47023
47283
  onDragStart: (e) => {
@@ -47027,13 +47287,12 @@ function FolderRow({
47027
47287
  },
47028
47288
  onDragEnd: () => {
47029
47289
  dragCtx.setDragging(null);
47030
- setDropPos(null);
47290
+ setDropInside(false);
47031
47291
  },
47032
47292
  onDragOver: handleFolderDragOver,
47033
- onDragLeave: () => setDropPos(null),
47293
+ onDragLeave: () => setDropInside(false),
47034
47294
  onDrop: handleFolderDrop,
47035
47295
  children: [
47036
- dropPos === "after" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 inset-x-0 h-0.5 bg-blue-500 z-10 pointer-events-none" }),
47037
47296
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] w-3 text-center shrink-0 mt-0.5", children: expanded ? "▾" : "▸" }),
47038
47297
  /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, { className: `shrink-0 mt-0.5 ${hasInheritedConfig ? "text-blue-500" : "text-amber-600"}` }),
47039
47298
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
@@ -47137,94 +47396,101 @@ function FolderContents({
47137
47396
  const renameExample = useStore((s) => s.renameExample);
47138
47397
  const deleteExample = useStore((s) => s.deleteExample);
47139
47398
  const duplicateExample = useStore((s) => s.duplicateExample);
47140
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
47141
- folder2.folders.map((sub, subIndex) => /* @__PURE__ */ jsxRuntimeExports.jsx(
47142
- FolderRow,
47143
- {
47144
- folder: sub,
47145
- collectionId,
47146
- parentFolderId: folder2.id,
47147
- folderIndex: subIndex,
47148
- depth: depth + 1,
47149
- expandCtrl,
47150
- onAddRequest: () => onAddRequest(sub.id),
47151
- onAddFolder: () => onAddFolder(sub.id, "New Folder"),
47152
- onRename: (name2) => onRenameFolder(sub.id, name2),
47153
- onDelete: () => onDeleteFolder(sub.id),
47154
- onDuplicate: () => onDuplicateFolder(sub.id),
47155
- onUpdateTags: (tags2) => onUpdateFolderTags(sub.id, tags2),
47156
- onRun: () => onRunFolder(sub.id),
47157
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
47158
- FolderContents,
47159
- {
47160
- folder: sub,
47161
- collectionId,
47162
- requests,
47163
- activeRequestId,
47164
- depth: depth + 1,
47165
- expandCtrl,
47166
- newRequestId,
47167
- onSelectRequest,
47168
- onAddRequest,
47169
- onAddFolder,
47170
- onRenameFolder,
47171
- onDeleteFolder,
47172
- onDuplicateFolder,
47173
- onRenameRequest,
47174
- onDeleteRequest,
47175
- onDuplicateRequest,
47176
- onUpdateFolderTags,
47177
- onUpdateRequestTags,
47178
- onSetRequestHookType,
47179
- onToggleRequestDisabled,
47180
- onRunFolder
47181
- }
47182
- )
47183
- },
47184
- sub.id
47185
- )),
47186
- folder2.requestIds.map((reqId, reqIndex) => {
47187
- const req = requests[reqId];
47188
- if (!req) return null;
47399
+ const dropIndent = (depth + 1) * 12 + 8;
47400
+ const renderChild = (child) => {
47401
+ if (child.type === "folder") {
47402
+ const sub = folder2.folders.find((f) => f.id === child.id);
47403
+ if (!sub) return null;
47189
47404
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
47190
- RequestRow,
47405
+ FolderRow,
47191
47406
  {
47192
- reqId: req.id,
47407
+ folder: sub,
47193
47408
  collectionId,
47194
- folderId: folder2.id,
47195
- reqIndex,
47196
- name: req.name,
47197
- url: req.url,
47198
- method: req.method,
47199
- protocol: req.protocol,
47200
- authType: req.auth.type,
47201
- hookType: req.hookType,
47202
- disabled: req.disabled,
47203
- tags: req.meta?.tags ?? [],
47204
- isActive: req.id === activeRequestId,
47205
- autoRename: req.id === newRequestId,
47206
- indent: (depth + 1) * 12 + 8,
47207
- examples: req.examples?.map((e) => ({ id: e.id, name: e.name })),
47208
- activeExampleId: req.id === activeRequestId ? activeExampleId : null,
47209
- onSelect: () => onSelectRequest(req.id),
47210
- onRename: (name2) => onRenameRequest(req.id, name2),
47211
- onDelete: () => onDeleteRequest(req.id),
47212
- onDuplicate: () => onDuplicateRequest(req.id),
47213
- onUpdateTags: (tags2) => onUpdateRequestTags(req.id, tags2),
47214
- onSetHookType: (ht) => onSetRequestHookType(req.id, ht),
47215
- onToggleDisabled: () => onToggleRequestDisabled(req.id),
47216
- onAddExample: () => {
47217
- const exId = addExampleFromRequest(req.id);
47218
- if (exId) openExample(req.id, collectionId, exId);
47219
- },
47220
- onOpenExample: (exId) => openExample(req.id, collectionId, exId),
47221
- onRenameExample: (exId, name2) => renameExample(req.id, exId, name2),
47222
- onDeleteExample: (exId) => deleteExample(req.id, exId),
47223
- onDuplicateExample: (exId) => duplicateExample(req.id, exId)
47224
- },
47225
- req.id
47409
+ parentFolderId: folder2.id,
47410
+ depth: depth + 1,
47411
+ expandCtrl,
47412
+ onAddRequest: () => onAddRequest(sub.id),
47413
+ onAddFolder: () => onAddFolder(sub.id, "New Folder"),
47414
+ onRename: (name2) => onRenameFolder(sub.id, name2),
47415
+ onDelete: () => onDeleteFolder(sub.id),
47416
+ onDuplicate: () => onDuplicateFolder(sub.id),
47417
+ onUpdateTags: (tags2) => onUpdateFolderTags(sub.id, tags2),
47418
+ onRun: () => onRunFolder(sub.id),
47419
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
47420
+ FolderContents,
47421
+ {
47422
+ folder: sub,
47423
+ collectionId,
47424
+ requests,
47425
+ activeRequestId,
47426
+ depth: depth + 1,
47427
+ expandCtrl,
47428
+ newRequestId,
47429
+ onSelectRequest,
47430
+ onAddRequest,
47431
+ onAddFolder,
47432
+ onRenameFolder,
47433
+ onDeleteFolder,
47434
+ onDuplicateFolder,
47435
+ onRenameRequest,
47436
+ onDeleteRequest,
47437
+ onDuplicateRequest,
47438
+ onUpdateFolderTags,
47439
+ onUpdateRequestTags,
47440
+ onSetRequestHookType,
47441
+ onToggleRequestDisabled,
47442
+ onRunFolder
47443
+ }
47444
+ )
47445
+ }
47226
47446
  );
47227
- })
47447
+ }
47448
+ const req = requests[child.id];
47449
+ if (!req) return null;
47450
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
47451
+ RequestRow,
47452
+ {
47453
+ reqId: req.id,
47454
+ collectionId,
47455
+ folderId: folder2.id,
47456
+ name: req.name,
47457
+ url: req.url,
47458
+ method: req.method,
47459
+ protocol: req.protocol,
47460
+ authType: req.auth.type,
47461
+ hookType: req.hookType,
47462
+ disabled: req.disabled,
47463
+ tags: req.meta?.tags ?? [],
47464
+ isActive: req.id === activeRequestId,
47465
+ autoRename: req.id === newRequestId,
47466
+ indent: (depth + 1) * 12 + 8,
47467
+ examples: req.examples?.map((e) => ({ id: e.id, name: e.name })),
47468
+ activeExampleId: req.id === activeRequestId ? activeExampleId : null,
47469
+ onSelect: () => onSelectRequest(req.id),
47470
+ onRename: (name2) => onRenameRequest(req.id, name2),
47471
+ onDelete: () => onDeleteRequest(req.id),
47472
+ onDuplicate: () => onDuplicateRequest(req.id),
47473
+ onUpdateTags: (tags2) => onUpdateRequestTags(req.id, tags2),
47474
+ onSetHookType: (ht) => onSetRequestHookType(req.id, ht),
47475
+ onToggleDisabled: () => onToggleRequestDisabled(req.id),
47476
+ onAddExample: () => {
47477
+ const exId = addExampleFromRequest(req.id);
47478
+ if (exId) openExample(req.id, collectionId, exId);
47479
+ },
47480
+ onOpenExample: (exId) => openExample(req.id, collectionId, exId),
47481
+ onRenameExample: (exId, name2) => renameExample(req.id, exId, name2),
47482
+ onDeleteExample: (exId) => deleteExample(req.id, exId),
47483
+ onDuplicateExample: (exId) => duplicateExample(req.id, exId)
47484
+ }
47485
+ );
47486
+ };
47487
+ const children = orderedChildren(folder2);
47488
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
47489
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropLine, { collectionId, parentFolderId: folder2.id, index: 0, indent: dropIndent }),
47490
+ children.map((child, k) => /* @__PURE__ */ jsxRuntimeExports.jsxs(React$2.Fragment, { children: [
47491
+ renderChild(child),
47492
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DropLine, { collectionId, parentFolderId: folder2.id, index: k + 1, indent: dropIndent })
47493
+ ] }, child.type + ":" + child.id))
47228
47494
  ] });
47229
47495
  }
47230
47496
  function safeDecode(s) {
@@ -52982,8 +53248,8 @@ function jsonWithComments() {
52982
53248
  function xmlWithComments() {
52983
53249
  return [xml(), commentKeymap];
52984
53250
  }
52985
- const GraphQLEditor = reactExports.lazy(() => __vitePreload(() => import("./GraphQLEditor-CkrzU3x2.js"), true ? [] : void 0, import.meta.url).then((m) => ({ default: m.GraphQLEditor })));
52986
- const SoapEditor = reactExports.lazy(() => __vitePreload(() => import("./SoapEditor-KEOZCmWl.js"), true ? [] : void 0, import.meta.url).then((m) => ({ default: m.SoapEditor })));
53251
+ const GraphQLEditor = reactExports.lazy(() => __vitePreload(() => import("./GraphQLEditor-D8v3dLPR.js"), true ? [] : void 0, import.meta.url).then((m) => ({ default: m.GraphQLEditor })));
53252
+ const SoapEditor = reactExports.lazy(() => __vitePreload(() => import("./SoapEditor-vPPiu9kt.js"), true ? [] : void 0, import.meta.url).then((m) => ({ default: m.SoapEditor })));
52987
53253
  function BodyTab({ request: request2, onChange }) {
52988
53254
  const t2 = useT();
52989
53255
  const editorFallback = /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-xs text-surface-500", children: t2("Loading editor…") });
@@ -55169,10 +55435,10 @@ var scope = {};
55169
55435
  }
55170
55436
  exports$1.and = and;
55171
55437
  const orCode = mappend(exports$1.operators.OR);
55172
- function or(...args) {
55438
+ function or2(...args) {
55173
55439
  return args.reduce(orCode);
55174
55440
  }
55175
- exports$1.or = or;
55441
+ exports$1.or = or2;
55176
55442
  function mappend(op) {
55177
55443
  return (x, y) => x === code_12.nil ? y : y === code_12.nil ? x : (0, code_12._)`${par(x)} ${op} ${par(y)}`;
55178
55444
  }
@@ -61431,6 +61697,25 @@ function RequestBuilder({ request: request2 }) {
61431
61697
  return next;
61432
61698
  });
61433
61699
  }
61700
+ const applicableDataSet = reactExports.useMemo(() => {
61701
+ const col = activeCollectionId ? collections[activeCollectionId]?.data : null;
61702
+ if (!col) return null;
61703
+ const folder2 = findFolderContaining(col.rootFolder, request2.id);
61704
+ if (folder2?.dataSet && folder2.dataSet.rows.length > 0) return folder2.dataSet;
61705
+ if (col.dataSet && col.dataSet.rows.length > 0) return col.dataSet;
61706
+ return null;
61707
+ }, [activeCollectionId, collections, request2.id]);
61708
+ const [dataRowIdx, setDataRowIdx] = reactExports.useState(-1);
61709
+ function selectedDataRow() {
61710
+ if (!applicableDataSet || dataRowIdx < 0) return void 0;
61711
+ const row = applicableDataSet.rows[dataRowIdx];
61712
+ if (!row) return void 0;
61713
+ const dr = {};
61714
+ applicableDataSet.columns.forEach((c, ci) => {
61715
+ if (c) dr[c] = row[ci] ?? "";
61716
+ });
61717
+ return dr;
61718
+ }
61434
61719
  function update(patch) {
61435
61720
  if (activeAppTab?.exampleId) updateExampleRequest(request2.id, activeAppTab.exampleId, patch);
61436
61721
  else updateRequest(request2.id, patch);
@@ -61550,7 +61835,8 @@ function RequestBuilder({ request: request2 }) {
61550
61835
  request: mergedRequest,
61551
61836
  collectionVars: { ...collectionVars, ...freshSessionVars },
61552
61837
  globals: liveGlobals,
61553
- streamId
61838
+ streamId,
61839
+ ...selectedDataRow() ? { dataRow: selectedDataRow() } : {}
61554
61840
  });
61555
61841
  } finally {
61556
61842
  finishLiveStream(streamId);
@@ -61669,135 +61955,156 @@ function RequestBuilder({ request: request2 }) {
61669
61955
  request2.examples?.find((e) => e.id === activeAppTab.exampleId)?.name ?? ""
61670
61956
  ] })
61671
61957
  ] }) }),
61672
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-4 py-2 flex-shrink-0", children: [
61673
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-surface-800 border border-surface-700 rounded overflow-hidden text-xs font-bold flex-shrink-0", children: [
61674
- /* @__PURE__ */ jsxRuntimeExports.jsx(
61675
- "button",
61958
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 px-4 py-2 flex-shrink-0", children: [
61959
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
61960
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-surface-800 border border-surface-700 rounded overflow-hidden text-xs font-bold flex-shrink-0", children: [
61961
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
61962
+ "button",
61963
+ {
61964
+ onClick: () => update({ protocol: "http" }),
61965
+ className: `px-2 py-1.5 transition-colors ${!isWs && !isSoap && !isGrpc ? "bg-blue-600 text-white" : "text-surface-500 hover:text-white"}`,
61966
+ title: t2("HTTP request"),
61967
+ children: "HTTP"
61968
+ }
61969
+ ),
61970
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
61971
+ "button",
61972
+ {
61973
+ onClick: () => update({ protocol: "websocket" }),
61974
+ className: `px-2 py-1.5 transition-colors ${isWs ? "bg-cyan-700 text-cyan-200" : "text-surface-500 hover:text-white"}`,
61975
+ title: t2("WebSocket"),
61976
+ children: "WS"
61977
+ }
61978
+ ),
61979
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
61980
+ "button",
61981
+ {
61982
+ onClick: () => {
61983
+ update({
61984
+ protocol: "soap",
61985
+ method: "POST",
61986
+ body: request2.body.mode === "soap" ? request2.body : { ...request2.body, mode: "soap", soap: request2.body.soap ?? { wsdlUrl: "", envelope: "" } }
61987
+ });
61988
+ if (activeTabId) setTabRequestTab(activeTabId, "body");
61989
+ },
61990
+ className: `px-2 py-1.5 transition-colors ${isSoap ? "bg-amber-700 text-amber-100" : "text-surface-500 hover:text-white"}`,
61991
+ title: t2("SOAP - endpoint and method are derived from the WSDL"),
61992
+ children: "SOAP"
61993
+ }
61994
+ ),
61995
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
61996
+ "button",
61997
+ {
61998
+ onClick: () => update({
61999
+ protocol: "grpc",
62000
+ body: request2.body.mode === "grpc" ? request2.body : { ...request2.body, mode: "grpc", grpc: request2.body.grpc ?? { message: "{}", metadata: [], plaintext: false } }
62001
+ }),
62002
+ className: `px-2 py-1.5 transition-colors ${isGrpc ? "bg-violet-700 text-violet-100" : "text-surface-500 hover:text-white"}`,
62003
+ title: t2("gRPC - proto-defined services over HTTP/2"),
62004
+ children: "gRPC"
62005
+ }
62006
+ )
62007
+ ] }),
62008
+ !isWs && !isSoap && !isGrpc && (customVerb ? /* @__PURE__ */ jsxRuntimeExports.jsx(
62009
+ "input",
61676
62010
  {
61677
- onClick: () => update({ protocol: "http" }),
61678
- className: `px-2 py-1.5 transition-colors ${!isWs && !isSoap && !isGrpc ? "bg-blue-600 text-white" : "text-surface-500 hover:text-white"}`,
61679
- title: t2("HTTP request"),
61680
- children: "HTTP"
62011
+ autoFocus: true,
62012
+ value: request2.method,
62013
+ onChange: (e) => update({ method: e.target.value.toUpperCase() }),
62014
+ onBlur: () => setCustomVerb(false),
62015
+ onKeyDown: (e) => {
62016
+ if (e.key === "Enter") {
62017
+ e.preventDefault();
62018
+ setCustomVerb(false);
62019
+ }
62020
+ },
62021
+ placeholder: t2("VERB"),
62022
+ title: t2("Type any HTTP method"),
62023
+ className: "w-24 bg-surface-800 border border-blue-500 rounded px-2 py-1.5 text-xs font-bold uppercase focus:outline-none text-fuchsia-400 placeholder-surface-600"
61681
62024
  }
61682
- ),
61683
- /* @__PURE__ */ jsxRuntimeExports.jsx(
61684
- "button",
62025
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
62026
+ "select",
61685
62027
  {
61686
- onClick: () => update({ protocol: "websocket" }),
61687
- className: `px-2 py-1.5 transition-colors ${isWs ? "bg-cyan-700 text-cyan-200" : "text-surface-500 hover:text-white"}`,
61688
- title: t2("WebSocket"),
61689
- children: "WS"
62028
+ value: METHODS.includes(request2.method) ? request2.method : "__current__",
62029
+ onChange: (e) => {
62030
+ if (e.target.value === "__custom__") setCustomVerb(true);
62031
+ else if (e.target.value !== "__current__") update({ method: e.target.value });
62032
+ },
62033
+ className: `bg-surface-800 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold focus:outline-none focus:border-blue-500 ${METHOD_COLORS[request2.method] ?? "text-fuchsia-400"}`,
62034
+ children: [
62035
+ METHODS.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m, className: "text-white", children: m }, m)),
62036
+ !METHODS.includes(request2.method) && /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "__current__", className: "text-white", children: request2.method }),
62037
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "__custom__", className: "text-white", children: t2("Custom…") })
62038
+ ]
61690
62039
  }
61691
- ),
61692
- /* @__PURE__ */ jsxRuntimeExports.jsx(
61693
- "button",
62040
+ )),
62041
+ isSoap && /* @__PURE__ */ jsxRuntimeExports.jsx(
62042
+ "span",
61694
62043
  {
61695
- onClick: () => {
61696
- update({
61697
- protocol: "soap",
61698
- method: "POST",
61699
- body: request2.body.mode === "soap" ? request2.body : { ...request2.body, mode: "soap", soap: request2.body.soap ?? { wsdlUrl: "", envelope: "" } }
61700
- });
61701
- if (activeTabId) setTabRequestTab(activeTabId, "body");
61702
- },
61703
- className: `px-2 py-1.5 transition-colors ${isSoap ? "bg-amber-700 text-amber-100" : "text-surface-500 hover:text-white"}`,
61704
- title: t2("SOAP - endpoint and method are derived from the WSDL"),
61705
- children: "SOAP"
62044
+ className: "bg-surface-900 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold text-amber-400 select-none",
62045
+ title: t2("SOAP requests are always POST"),
62046
+ children: "POST"
61706
62047
  }
61707
62048
  ),
61708
- /* @__PURE__ */ jsxRuntimeExports.jsx(
61709
- "button",
62049
+ isGrpc && /* @__PURE__ */ jsxRuntimeExports.jsx(
62050
+ "span",
61710
62051
  {
61711
- onClick: () => update({
61712
- protocol: "grpc",
61713
- body: request2.body.mode === "grpc" ? request2.body : { ...request2.body, mode: "grpc", grpc: request2.body.grpc ?? { message: "{}", metadata: [], plaintext: false } }
61714
- }),
61715
- className: `px-2 py-1.5 transition-colors ${isGrpc ? "bg-violet-700 text-violet-100" : "text-surface-500 hover:text-white"}`,
61716
- title: t2("gRPC - proto-defined services over HTTP/2"),
62052
+ className: "bg-surface-900 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold text-violet-400 select-none",
62053
+ title: t2("gRPC target is host:port"),
61717
62054
  children: "gRPC"
61718
62055
  }
61719
62056
  )
61720
62057
  ] }),
61721
- !isWs && !isSoap && !isGrpc && (customVerb ? /* @__PURE__ */ jsxRuntimeExports.jsx(
61722
- "input",
61723
- {
61724
- autoFocus: true,
61725
- value: request2.method,
61726
- onChange: (e) => update({ method: e.target.value.toUpperCase() }),
61727
- onBlur: () => setCustomVerb(false),
61728
- onKeyDown: (e) => {
61729
- if (e.key === "Enter") {
61730
- e.preventDefault();
61731
- setCustomVerb(false);
61732
- }
61733
- },
61734
- placeholder: t2("VERB"),
61735
- title: t2("Type any HTTP method"),
61736
- className: "w-24 bg-surface-800 border border-blue-500 rounded px-2 py-1.5 text-xs font-bold uppercase focus:outline-none text-fuchsia-400 placeholder-surface-600"
61737
- }
61738
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
61739
- "select",
61740
- {
61741
- value: METHODS.includes(request2.method) ? request2.method : "__current__",
61742
- onChange: (e) => {
61743
- if (e.target.value === "__custom__") setCustomVerb(true);
61744
- else if (e.target.value !== "__current__") update({ method: e.target.value });
61745
- },
61746
- className: `bg-surface-800 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold focus:outline-none focus:border-blue-500 ${METHOD_COLORS[request2.method] ?? "text-fuchsia-400"}`,
61747
- children: [
61748
- METHODS.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m, className: "text-white", children: m }, m)),
61749
- !METHODS.includes(request2.method) && /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "__current__", className: "text-white", children: request2.method }),
61750
- /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "__custom__", className: "text-white", children: t2("Custom…") })
61751
- ]
61752
- }
61753
- )),
61754
- isSoap && /* @__PURE__ */ jsxRuntimeExports.jsx(
61755
- "span",
61756
- {
61757
- className: "bg-surface-900 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold text-amber-400 select-none",
61758
- title: t2("SOAP requests are always POST"),
61759
- children: "POST"
61760
- }
61761
- ),
61762
- isGrpc && /* @__PURE__ */ jsxRuntimeExports.jsx(
61763
- "span",
61764
- {
61765
- className: "bg-surface-900 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold text-violet-400 select-none",
61766
- title: t2("gRPC target is host:port"),
61767
- children: "gRPC"
61768
- }
61769
- ),
61770
- /* @__PURE__ */ jsxRuntimeExports.jsx(
61771
- VarInput,
61772
- {
61773
- value: request2.url,
61774
- onChange: (url) => update({ url }),
61775
- onPaste: handleUrlPaste,
61776
- placeholder: isWs ? "ws://example.com/socket" : isSoap ? t2("Endpoint (auto-filled from WSDL <soap:address>)") : isGrpc ? "localhost:50051" : "https://api.example.com/endpoint",
61777
- wrapperClassName: "flex-1",
61778
- 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"}`
61779
- }
61780
- ),
61781
- !isWs && !isGrpc && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
62058
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
61782
62059
  /* @__PURE__ */ jsxRuntimeExports.jsx(
61783
- "button",
62060
+ VarInput,
61784
62061
  {
61785
- onClick: toggleRunHooks,
61786
- title: runHooks ? t2("Hooks enabled - click to disable") : t2("Hooks disabled - click to enable"),
61787
- className: `px-2 py-1.5 rounded text-xs font-medium transition-colors border ${runHooks ? "border-violet-500 text-violet-400 hover:bg-violet-500/10" : "border-surface-700 text-surface-500 hover:text-surface-300"}`,
61788
- children: t2("hooks")
62062
+ value: request2.url,
62063
+ onChange: (url) => update({ url }),
62064
+ onPaste: handleUrlPaste,
62065
+ placeholder: isWs ? "ws://example.com/socket" : isSoap ? t2("Endpoint (auto-filled from WSDL <soap:address>)") : isGrpc ? "localhost:50051" : "https://api.example.com/endpoint",
62066
+ wrapperClassName: "flex-1",
62067
+ 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"}`
61789
62068
  }
61790
62069
  ),
61791
- /* @__PURE__ */ jsxRuntimeExports.jsx(
61792
- "button",
62070
+ !isWs && !isGrpc && applicableDataSet && /* @__PURE__ */ jsxRuntimeExports.jsxs(
62071
+ "select",
61793
62072
  {
61794
- onClick: sendRequest,
61795
- disabled: isSending || !request2.url,
61796
- title: t2("Send (Cmd/Ctrl+Enter)"),
61797
- className: "px-4 py-1.5 bg-blue-600 hover:bg-blue-500 disabled:bg-surface-800 disabled:text-surface-400 rounded text-sm font-medium transition-colors min-w-[72px]",
61798
- children: isSending ? "..." : t2("Send")
62073
+ value: dataRowIdx,
62074
+ onChange: (e) => setDataRowIdx(Number(e.target.value)),
62075
+ title: t2("Run this request with the variables from one data table row"),
62076
+ className: "bg-surface-800 border border-surface-700 rounded px-2 py-1.5 text-xs focus:outline-none focus:border-blue-500 max-w-[150px]",
62077
+ style: { color: "var(--text-primary)" },
62078
+ children: [
62079
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: -1, children: t2("No data row") }),
62080
+ applicableDataSet.rows.map((row, ri) => /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: ri, children: [
62081
+ t2("Row :n", { n: ri + 1 }),
62082
+ row[0] ? ` · ${row[0]}` : ""
62083
+ ] }, ri))
62084
+ ]
61799
62085
  }
61800
- )
62086
+ ),
62087
+ !isWs && !isGrpc && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
62088
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
62089
+ "button",
62090
+ {
62091
+ onClick: toggleRunHooks,
62092
+ title: runHooks ? t2("Hooks enabled - click to disable") : t2("Hooks disabled - click to enable"),
62093
+ className: `px-2 py-1.5 rounded text-xs font-medium transition-colors border ${runHooks ? "border-violet-500 text-violet-400 hover:bg-violet-500/10" : "border-surface-700 text-surface-500 hover:text-surface-300"}`,
62094
+ children: t2("hooks")
62095
+ }
62096
+ ),
62097
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
62098
+ "button",
62099
+ {
62100
+ onClick: sendRequest,
62101
+ disabled: isSending || !request2.url,
62102
+ title: t2("Send (Cmd/Ctrl+Enter)"),
62103
+ className: "px-4 py-1.5 bg-blue-600 hover:bg-blue-500 disabled:bg-surface-800 disabled:text-surface-400 rounded text-sm font-medium transition-colors min-w-[72px]",
62104
+ children: isSending ? "..." : t2("Send")
62105
+ }
62106
+ )
62107
+ ] })
61801
62108
  ] })
61802
62109
  ] }),
61803
62110
  showFuzz && /* @__PURE__ */ jsxRuntimeExports.jsx(FuzzModal, { request: request2, onClose: () => setShowFuzz(false) }),
@@ -62737,14 +63044,14 @@ function HookResultsPanel({ results }) {
62737
63044
  ),
62738
63045
  open2 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pb-2 flex flex-col gap-0.5", children: results.map((r, i) => {
62739
63046
  const badge = HOOK_BADGE$1[r.hookType ?? ""];
62740
- const hasDetail = (r.consoleOutput?.length ?? 0) > 0 || (r.testResults?.length ?? 0) > 0 || r.error || r.preScriptError || r.postScriptError;
63047
+ const hasDetail2 = (r.consoleOutput?.length ?? 0) > 0 || (r.testResults?.length ?? 0) > 0 || r.error || r.preScriptError || r.postScriptError;
62741
63048
  const isExpanded = expanded.has(r.requestId + i);
62742
63049
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs", children: [
62743
63050
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
62744
63051
  "div",
62745
63052
  {
62746
- className: `flex items-center gap-2 py-0.5 ${hasDetail ? "cursor-pointer hover:bg-surface-800/20 rounded px-1 -mx-1" : ""}`,
62747
- onClick: () => hasDetail && toggle(r.requestId + i),
63053
+ className: `flex items-center gap-2 py-0.5 ${hasDetail2 ? "cursor-pointer hover:bg-surface-800/20 rounded px-1 -mx-1" : ""}`,
63054
+ onClick: () => hasDetail2 && toggle(r.requestId + i),
62748
63055
  children: [
62749
63056
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-1.5 h-1.5 rounded-full shrink-0 ${r.status === "passed" ? "bg-emerald-500" : r.status === "failed" ? "bg-red-500" : "bg-orange-500"}` }),
62750
63057
  badge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] font-bold px-1 py-0.5 rounded uppercase tracking-wide shrink-0 ${badge.cls}`, children: t2(badge.label) }),
@@ -62754,7 +63061,7 @@ function HookResultsPanel({ results }) {
62754
63061
  r.durationMs,
62755
63062
  "ms"
62756
63063
  ] }),
62757
- hasDetail && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500 shrink-0", children: isExpanded ? "▾" : "▸" })
63064
+ hasDetail2 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500 shrink-0", children: isExpanded ? "▾" : "▸" })
62758
63065
  ]
62759
63066
  }
62760
63067
  ),
@@ -63177,6 +63484,88 @@ function ConsolePanel({ scriptResult }) {
63177
63484
  )) })
63178
63485
  ] });
63179
63486
  }
63487
+ function OverflowMenu({ button, ariaLabel, title: title2, wrapperClassName = "", buttonClassName = "", align = "right", children }) {
63488
+ const [open2, setOpen] = reactExports.useState(false);
63489
+ const wrapRef = reactExports.useRef(null);
63490
+ const menuRef = reactExports.useRef(null);
63491
+ const btnRef = reactExports.useRef(null);
63492
+ const menuId = reactExports.useId();
63493
+ function items2() {
63494
+ if (!menuRef.current) return [];
63495
+ return Array.from(menuRef.current.querySelectorAll('[role="menuitem"]')).filter((el) => el.offsetParent !== null && !el.hasAttribute("disabled"));
63496
+ }
63497
+ reactExports.useEffect(() => {
63498
+ if (!open2) return;
63499
+ function onDown(e) {
63500
+ if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
63501
+ }
63502
+ document.addEventListener("mousedown", onDown, true);
63503
+ return () => document.removeEventListener("mousedown", onDown, true);
63504
+ }, [open2]);
63505
+ reactExports.useEffect(() => {
63506
+ if (open2) items2()[0]?.focus();
63507
+ }, [open2]);
63508
+ function onMenuKeyDown(e) {
63509
+ const list = items2();
63510
+ const idx = list.indexOf(document.activeElement);
63511
+ if (e.key === "ArrowDown") {
63512
+ e.preventDefault();
63513
+ list[(idx + 1) % list.length]?.focus();
63514
+ } else if (e.key === "ArrowUp") {
63515
+ e.preventDefault();
63516
+ list[(idx - 1 + list.length) % list.length]?.focus();
63517
+ } else if (e.key === "Home") {
63518
+ e.preventDefault();
63519
+ list[0]?.focus();
63520
+ } else if (e.key === "End") {
63521
+ e.preventDefault();
63522
+ list[list.length - 1]?.focus();
63523
+ } else if (e.key === "Escape") {
63524
+ e.preventDefault();
63525
+ setOpen(false);
63526
+ btnRef.current?.focus();
63527
+ } else if (e.key === "Tab") {
63528
+ setOpen(false);
63529
+ }
63530
+ }
63531
+ function onTriggerKeyDown(e) {
63532
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
63533
+ e.preventDefault();
63534
+ setOpen(true);
63535
+ }
63536
+ }
63537
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: wrapRef, className: `relative ${wrapperClassName}`, children: [
63538
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
63539
+ "button",
63540
+ {
63541
+ ref: btnRef,
63542
+ type: "button",
63543
+ "aria-haspopup": "menu",
63544
+ "aria-expanded": open2,
63545
+ "aria-controls": open2 ? menuId : void 0,
63546
+ "aria-label": ariaLabel,
63547
+ title: title2 ?? ariaLabel,
63548
+ onClick: () => setOpen((o) => !o),
63549
+ onKeyDown: onTriggerKeyDown,
63550
+ className: buttonClassName,
63551
+ children: button
63552
+ }
63553
+ ),
63554
+ open2 && /* @__PURE__ */ jsxRuntimeExports.jsx(
63555
+ "div",
63556
+ {
63557
+ ref: menuRef,
63558
+ id: menuId,
63559
+ role: "menu",
63560
+ "aria-label": ariaLabel,
63561
+ onKeyDown: onMenuKeyDown,
63562
+ onClick: () => setOpen(false),
63563
+ className: `absolute top-full mt-1 z-50 min-w-[150px] bg-surface-900 border border-surface-700 rounded-lg shadow-2xl py-1 flex flex-col ${align === "right" ? "right-0" : "left-0"}`,
63564
+ children
63565
+ }
63566
+ )
63567
+ ] });
63568
+ }
63180
63569
  function header(headers, name2) {
63181
63570
  const lower = name2.toLowerCase();
63182
63571
  for (const [k, v] of Object.entries(headers)) {
@@ -63358,6 +63747,26 @@ function HistoryTabRow({ entry, onLoad, onResend }) {
63358
63747
  ] })
63359
63748
  ] });
63360
63749
  }
63750
+ const TAB_INLINE_VIS = {
63751
+ error: "flex",
63752
+ request: "flex",
63753
+ body: "flex",
63754
+ headers: "flex",
63755
+ tests: "hidden @min-[520px]:flex",
63756
+ console: "hidden @min-[520px]:flex",
63757
+ history: "hidden @min-[720px]:flex",
63758
+ http: "hidden @min-[720px]:flex"
63759
+ };
63760
+ const TAB_MENU_VIS = {
63761
+ error: "hidden",
63762
+ request: "hidden",
63763
+ body: "hidden",
63764
+ headers: "hidden",
63765
+ tests: "flex @min-[520px]:hidden",
63766
+ console: "flex @min-[520px]:hidden",
63767
+ history: "flex @min-[720px]:hidden",
63768
+ http: "flex @min-[720px]:hidden"
63769
+ };
63361
63770
  function ResponseViewer() {
63362
63771
  const t2 = useT();
63363
63772
  const activeTab = useStore((s) => s.tabs.find((t22) => t22.id === s.activeTabId));
@@ -63500,112 +63909,223 @@ function ResponseViewer() {
63500
63909
  },
63501
63910
  entry.id
63502
63911
  )) });
63503
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col", children: [
63504
- hookResults && hookResults.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(HookResultsPanel, { results: hookResults }),
63505
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 px-4 py-1.5 border-b border-surface-800 flex-shrink-0 overflow-x-auto", children: [
63506
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `text-sm font-bold shrink-0 ${getStatusColor(response.status)}`, children: [
63507
- response.status,
63508
- " ",
63509
- response.statusText
63510
- ] }),
63511
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-surface-400 shrink-0", children: [
63512
- response.durationMs,
63513
- "ms"
63514
- ] }),
63515
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-surface-400 shrink-0", children: [
63516
- (response.bodySize / 1024).toFixed(1),
63517
- " KB"
63518
- ] }),
63519
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-0 ml-2 shrink-0", children: tabList.map((t22) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
63912
+ function renderTabButton(def2, variant) {
63913
+ const active2 = tab === def2.id;
63914
+ const badge = def2.badge !== void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] px-1 rounded ${def2.error ? "bg-red-800 text-red-200" : def2.id === "tests" && passedCount < totalCount ? "bg-red-800 text-red-200" : "bg-surface-700 text-white"}`, children: def2.badge }) : null;
63915
+ if (variant === "menu") {
63916
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
63520
63917
  "button",
63521
63918
  {
63522
- onClick: () => setTab(t22.id),
63523
- className: `px-3 py-1 text-xs rounded transition-colors flex items-center gap-1 ${tab === t22.id ? "bg-surface-800 text-white" : "text-surface-400 hover:text-white"}`,
63919
+ role: "menuitem",
63920
+ tabIndex: -1,
63921
+ onClick: () => setTab(def2.id),
63922
+ className: `${active2 ? "hidden" : TAB_MENU_VIS[def2.id]} w-full items-center justify-between gap-3 px-3 py-1.5 text-xs ${active2 ? "" : "text-surface-300 hover:bg-surface-800 hover:text-white"}`,
63524
63923
  children: [
63525
- t22.label,
63526
- t22.badge !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] px-1 rounded ${t22.error ? "bg-red-800 text-red-200" : t22.id === "tests" && passedCount < totalCount ? "bg-red-800 text-red-200" : "bg-surface-700 text-white"}`, children: t22.badge })
63924
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: def2.label }),
63925
+ badge
63527
63926
  ]
63528
63927
  },
63529
- t22.id
63530
- )) }),
63531
- !response.error && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-1 shrink-0", children: [
63532
- assertToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-emerald-400 font-medium px-1", children: assertToast.toast.msg }),
63533
- contractToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-blue-400 font-medium px-1", children: contractToast.toast.msg }),
63534
- tab === "body" && supportsTree && !response.streamed && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded overflow-hidden border border-surface-800 mr-1", children: [
63928
+ def2.id
63929
+ );
63930
+ }
63931
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
63932
+ "button",
63933
+ {
63934
+ role: "tab",
63935
+ "aria-selected": active2,
63936
+ onClick: () => setTab(def2.id),
63937
+ className: `${active2 ? "flex" : TAB_INLINE_VIS[def2.id]} px-3 py-1 text-xs rounded transition-colors items-center gap-1 ${active2 ? "bg-surface-800 text-white" : "text-surface-400 hover:text-white"}`,
63938
+ children: [
63939
+ def2.label,
63940
+ badge
63941
+ ]
63942
+ },
63943
+ def2.id
63944
+ );
63945
+ }
63946
+ function onTablistKey(e) {
63947
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
63948
+ const btns = Array.from(e.currentTarget.querySelectorAll('[role="tab"]')).filter((el) => el.offsetParent !== null);
63949
+ const idx = btns.indexOf(document.activeElement);
63950
+ e.preventDefault();
63951
+ if (e.key === "ArrowRight") btns[(idx + 1) % btns.length]?.focus();
63952
+ else if (e.key === "ArrowLeft") btns[(idx - 1 + btns.length) % btns.length]?.focus();
63953
+ else if (e.key === "Home") btns[0]?.focus();
63954
+ else btns[btns.length - 1]?.focus();
63955
+ }
63956
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col", children: [
63957
+ hookResults && hookResults.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(HookResultsPanel, { results: hookResults }),
63958
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "@container flex-shrink-0 border-b border-surface-800", children: [
63959
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-4 py-1.5 min-w-0", children: [
63960
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 shrink-0", children: [
63961
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `text-sm font-bold ${getStatusColor(response.status)}`, children: [
63962
+ response.status,
63963
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "hidden @min-[440px]:inline", children: [
63964
+ " ",
63965
+ response.statusText
63966
+ ] })
63967
+ ] }),
63968
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-surface-400", children: [
63969
+ response.durationMs,
63970
+ "ms"
63971
+ ] }),
63972
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-surface-400", children: [
63973
+ (response.bodySize / 1024).toFixed(1),
63974
+ " KB"
63975
+ ] })
63976
+ ] }),
63977
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "tablist", "aria-label": t2("Response sections"), onKeyDown: onTablistKey, className: "flex items-center gap-0 min-w-0 ml-1", children: [
63978
+ tabList.map((def2) => renderTabButton(def2, "inline")),
63979
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
63980
+ OverflowMenu,
63981
+ {
63982
+ wrapperClassName: "flex @min-[720px]:hidden",
63983
+ buttonClassName: "px-2 py-1 text-xs rounded text-surface-400 hover:text-white hover:bg-surface-800 flex items-center gap-1",
63984
+ button: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
63985
+ t2("More"),
63986
+ " ",
63987
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": "true", children: "▾" })
63988
+ ] }),
63989
+ ariaLabel: t2("More response sections"),
63990
+ align: "left",
63991
+ children: tabList.map((def2) => renderTabButton(def2, "menu"))
63992
+ }
63993
+ )
63994
+ ] })
63995
+ ] }),
63996
+ !response.error && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-4 py-1 border-t border-surface-800/60 min-w-0", children: [
63997
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] uppercase tracking-wider text-surface-500 font-medium truncate", children: tabList.find((x) => x.id === tab)?.label ?? t2("Body") }),
63998
+ assertToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-emerald-400 font-medium px-1 shrink-0", children: assertToast.toast.msg }),
63999
+ contractToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-blue-400 font-medium px-1 shrink-0", children: contractToast.toast.msg }),
64000
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-1 shrink-0", children: [
64001
+ tab === "body" && supportsTree && !response.streamed && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "group", "aria-label": t2("Body view mode"), className: "flex rounded overflow-hidden border border-surface-800 mr-1", children: [
64002
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
64003
+ "button",
64004
+ {
64005
+ onClick: () => setBodyView("tree"),
64006
+ "aria-pressed": bodyView === "tree",
64007
+ className: `px-2 py-0.5 text-[10px] transition-colors ${bodyView === "tree" ? "bg-surface-700 text-white" : "text-surface-600 hover:text-white"}`,
64008
+ title: t2("Interactive tree view - click values to add assertions"),
64009
+ children: t2("Tree")
64010
+ }
64011
+ ),
64012
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
64013
+ "button",
64014
+ {
64015
+ onClick: () => setBodyView("raw"),
64016
+ "aria-pressed": bodyView === "raw",
64017
+ className: `px-2 py-0.5 text-[10px] transition-colors ${bodyView === "raw" ? "bg-surface-700 text-white" : "text-surface-600 hover:text-white"}`,
64018
+ title: t2("Raw body view"),
64019
+ children: t2("Raw")
64020
+ }
64021
+ ),
64022
+ showTable && /* @__PURE__ */ jsxRuntimeExports.jsx(
64023
+ "button",
64024
+ {
64025
+ onClick: () => setBodyView("table"),
64026
+ "aria-pressed": bodyView === "table",
64027
+ className: `px-2 py-0.5 text-[10px] transition-colors ${bodyView === "table" ? "bg-surface-700 text-white" : "text-surface-600 hover:text-white"}`,
64028
+ title: t2("Show an array in the response as a sortable table"),
64029
+ children: t2("Table")
64030
+ }
64031
+ )
64032
+ ] }),
63535
64033
  /* @__PURE__ */ jsxRuntimeExports.jsx(
63536
64034
  "button",
63537
64035
  {
63538
- onClick: () => setBodyView("tree"),
63539
- className: `px-2 py-0.5 text-[10px] transition-colors ${bodyView === "tree" ? "bg-surface-700 text-white" : "text-surface-600 hover:text-white"}`,
63540
- title: t2("Interactive tree view - click values to add assertions"),
63541
- children: t2("Tree")
64036
+ onClick: () => setPinned(response),
64037
+ "aria-pressed": pinnedResponse === response,
64038
+ title: t2("Pin this response to compare against later responses"),
64039
+ className: `px-2 py-0.5 text-[10px] rounded transition-colors shrink-0 ${pinnedResponse === response ? "bg-blue-700 text-white" : "bg-surface-800 hover:bg-surface-700"}`,
64040
+ children: t2("Pin")
63542
64041
  }
63543
64042
  ),
63544
- /* @__PURE__ */ jsxRuntimeExports.jsx(
64043
+ pinnedResponse && /* @__PURE__ */ jsxRuntimeExports.jsx(
63545
64044
  "button",
63546
64045
  {
63547
- onClick: () => setBodyView("raw"),
63548
- className: `px-2 py-0.5 text-[10px] transition-colors ${bodyView === "raw" ? "bg-surface-700 text-white" : "text-surface-600 hover:text-white"}`,
63549
- title: t2("Raw body view"),
63550
- children: t2("Raw")
64046
+ onClick: () => setDiffMode((d) => !d),
64047
+ "aria-pressed": diffMode,
64048
+ title: t2("Toggle diff view against pinned response"),
64049
+ className: `hidden @min-[350px]:flex px-2 py-0.5 text-[10px] rounded transition-colors ${diffMode ? "bg-amber-700 text-white" : "bg-surface-800 hover:bg-surface-700"}`,
64050
+ children: t2("Diff")
63551
64051
  }
63552
64052
  ),
63553
- showTable && /* @__PURE__ */ jsxRuntimeExports.jsx(
64053
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
64054
+ "button",
64055
+ {
64056
+ onClick: saveAsContract,
64057
+ className: "hidden @min-[350px]:flex px-2 py-0.5 text-[10px] bg-surface-800 hover:bg-surface-700 rounded transition-colors",
64058
+ title: t2("Capture this response as a contract expectation"),
64059
+ children: [
64060
+ "↓ ",
64061
+ t2("Contract")
64062
+ ]
64063
+ }
64064
+ ),
64065
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
63554
64066
  "button",
63555
64067
  {
63556
- onClick: () => setBodyView("table"),
63557
- className: `px-2 py-0.5 text-[10px] transition-colors ${bodyView === "table" ? "bg-surface-700 text-white" : "text-surface-600 hover:text-white"}`,
63558
- title: t2("Show an array in the response as a sortable table"),
63559
- children: t2("Table")
64068
+ onClick: () => setShowMockModal(true),
64069
+ className: "hidden @min-[350px]:flex px-2 py-0.5 text-[10px] bg-surface-800 hover:bg-surface-700 rounded transition-colors",
64070
+ title: t2("Save this response as a mock route"),
64071
+ children: [
64072
+ "↓ ",
64073
+ t2("Mock")
64074
+ ]
64075
+ }
64076
+ ),
64077
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
64078
+ OverflowMenu,
64079
+ {
64080
+ wrapperClassName: "flex @min-[350px]:hidden",
64081
+ buttonClassName: "px-1.5 py-1 text-[10px] rounded bg-surface-800 hover:bg-surface-700 text-surface-300 flex items-center",
64082
+ button: /* @__PURE__ */ jsxRuntimeExports.jsx(DotsHorizontalIcon, {}),
64083
+ ariaLabel: t2("More actions"),
64084
+ children: [
64085
+ pinnedResponse && /* @__PURE__ */ jsxRuntimeExports.jsx(
64086
+ "button",
64087
+ {
64088
+ role: "menuitem",
64089
+ tabIndex: -1,
64090
+ onClick: () => setDiffMode((d) => !d),
64091
+ className: "flex @min-[350px]:hidden w-full items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-800 hover:text-white",
64092
+ children: t2("Diff")
64093
+ }
64094
+ ),
64095
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
64096
+ "button",
64097
+ {
64098
+ role: "menuitem",
64099
+ tabIndex: -1,
64100
+ onClick: saveAsContract,
64101
+ className: "flex @min-[350px]:hidden w-full items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-800 hover:text-white",
64102
+ children: [
64103
+ "↓ ",
64104
+ t2("Contract")
64105
+ ]
64106
+ }
64107
+ ),
64108
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
64109
+ "button",
64110
+ {
64111
+ role: "menuitem",
64112
+ tabIndex: -1,
64113
+ onClick: () => setShowMockModal(true),
64114
+ className: "flex @min-[350px]:hidden w-full items-center gap-2 px-3 py-1.5 text-xs text-surface-300 hover:bg-surface-800 hover:text-white",
64115
+ children: [
64116
+ "↓ ",
64117
+ t2("Mock")
64118
+ ]
64119
+ }
64120
+ )
64121
+ ]
63560
64122
  }
63561
64123
  )
63562
- ] }),
63563
- /* @__PURE__ */ jsxRuntimeExports.jsx(
63564
- "button",
63565
- {
63566
- onClick: () => setPinned(response),
63567
- title: t2("Pin this response to compare against later responses"),
63568
- className: `px-2 py-0.5 text-[10px] rounded transition-colors ${pinnedResponse === response ? "bg-blue-700 text-white" : "bg-surface-800 hover:bg-surface-700"}`,
63569
- children: t2("Pin")
63570
- }
63571
- ),
63572
- pinnedResponse && /* @__PURE__ */ jsxRuntimeExports.jsx(
63573
- "button",
63574
- {
63575
- onClick: () => setDiffMode((d) => !d),
63576
- title: t2("Toggle diff view against pinned response"),
63577
- className: `px-2 py-0.5 text-[10px] rounded transition-colors ${diffMode ? "bg-amber-700 text-white" : "bg-surface-800 hover:bg-surface-700"}`,
63578
- children: t2("Diff")
63579
- }
63580
- ),
63581
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
63582
- "button",
63583
- {
63584
- onClick: saveAsContract,
63585
- className: "px-2 py-0.5 text-[10px] bg-surface-800 hover:bg-surface-700 rounded transition-colors",
63586
- title: t2("Capture this response as a contract expectation"),
63587
- children: [
63588
- "↓ ",
63589
- t2("Contract")
63590
- ]
63591
- }
63592
- ),
63593
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
63594
- "button",
63595
- {
63596
- onClick: () => setShowMockModal(true),
63597
- className: "px-2 py-0.5 text-[10px] bg-surface-800 hover:bg-surface-700 rounded transition-colors",
63598
- title: t2("Save this response as a mock route"),
63599
- children: [
63600
- "↓ ",
63601
- t2("Mock")
63602
- ]
63603
- }
63604
- )
64124
+ ] })
63605
64125
  ] })
63606
64126
  ] }),
63607
64127
  showMockModal && /* @__PURE__ */ jsxRuntimeExports.jsx(SaveAsMockModal, { onClose: () => setShowMockModal(false) }),
63608
- /* @__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: [
64128
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { role: "tabpanel", "aria-label": tabList.find((x) => x.id === tab)?.label, 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: [
63609
64129
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-red-400 text-sm font-medium", children: t2("Request failed") }),
63610
64130
  /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-red-300 whitespace-pre-wrap", children: response.error })
63611
64131
  ] }) : diffMode && pinnedResponse ? /* @__PURE__ */ jsxRuntimeExports.jsx(DiffView, { pinned: pinnedResponse, current: response }) : tab === "body" && response.streamed ? /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -64285,7 +64805,7 @@ function WelcomeScreen() {
64285
64805
  )
64286
64806
  ] }),
64287
64807
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-sm max-w-sm", children: t2("Local-first API testing with Robot Framework & Playwright code generation. Secrets stay on your machine.") }),
64288
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: t2("version :version", { version: "0.5.8" }) })
64808
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: t2("version :version", { version: "0.5.9" }) })
64289
64809
  ] }),
64290
64810
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 w-64", children: [
64291
64811
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -67538,6 +68058,68 @@ function StatusDot({ status }) {
67538
68058
  };
67539
68059
  return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `inline-block w-2 h-2 rounded-full shrink-0 ${colors[status] ?? "bg-surface-700"}` });
67540
68060
  }
68061
+ function hasDetail(r) {
68062
+ return !!(r.testResults?.length || r.consoleOutput?.length || r.preScriptError || r.postScriptError || r.sentRequest || r.receivedResponse || r.error && !r.error.startsWith("Skipped"));
68063
+ }
68064
+ function DetailSection({ label, children }) {
68065
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
68066
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase tracking-wider text-surface-600 font-semibold", children: label }),
68067
+ children
68068
+ ] });
68069
+ }
68070
+ function KVRows$1({ rows }) {
68071
+ if (rows.length === 0) return null;
68072
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-0.5", children: rows.map(([k, v]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 font-mono text-[10px]", children: [
68073
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-500 shrink-0", children: [
68074
+ k,
68075
+ ":"
68076
+ ] }),
68077
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300 break-all", children: v })
68078
+ ] }, k)) });
68079
+ }
68080
+ const PRE_CLS = "text-[10px] font-mono text-surface-300 bg-surface-900 border border-surface-800 rounded px-2 py-1.5 overflow-x-auto whitespace-pre-wrap break-words max-h-56 overflow-y-auto";
68081
+ function ResultDetail({ r }) {
68082
+ const t2 = useT();
68083
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 text-xs", children: [
68084
+ r.testResults && r.testResults.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DetailSection, { label: t2("Tests"), children: r.testResults.map((tr, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-baseline gap-2", children: [
68085
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: tr.passed ? "text-emerald-400" : "text-red-400", children: tr.passed ? "✓" : "✗" }),
68086
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300", children: tr.name }),
68087
+ tr.error && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-400 font-mono text-[10px] break-all", children: tr.error })
68088
+ ] }, i)) }),
68089
+ (r.preScriptError || r.postScriptError) && /* @__PURE__ */ jsxRuntimeExports.jsxs(DetailSection, { label: t2("Script errors"), children: [
68090
+ r.preScriptError && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-red-300 font-mono break-all", children: [
68091
+ t2("Pre-script:"),
68092
+ " ",
68093
+ r.preScriptError
68094
+ ] }),
68095
+ r.postScriptError && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-red-300 font-mono break-all", children: [
68096
+ t2("Post-script:"),
68097
+ " ",
68098
+ r.postScriptError
68099
+ ] })
68100
+ ] }),
68101
+ r.error && !r.error.startsWith("Skipped") && /* @__PURE__ */ jsxRuntimeExports.jsx(DetailSection, { label: t2("Error"), children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-red-300 font-mono break-all", children: r.error }) }),
68102
+ r.consoleOutput && r.consoleOutput.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DetailSection, { label: t2("Console"), children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: PRE_CLS, children: r.consoleOutput.join("\n") }) }),
68103
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DetailSection, { label: t2("Request"), children: [
68104
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "font-mono text-[10px] text-surface-300 break-all", children: [
68105
+ r.method,
68106
+ " ",
68107
+ r.resolvedUrl
68108
+ ] }),
68109
+ r.sentRequest && /* @__PURE__ */ jsxRuntimeExports.jsx(KVRows$1, { rows: Object.entries(r.sentRequest.headers ?? {}) }),
68110
+ r.sentRequest?.body && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: PRE_CLS, children: r.sentRequest.body })
68111
+ ] }),
68112
+ r.receivedResponse && /* @__PURE__ */ jsxRuntimeExports.jsxs(DetailSection, { label: t2("Response"), children: [
68113
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-mono text-[10px]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: r.receivedResponse.status < 400 ? "text-emerald-400" : "text-red-400", children: [
68114
+ r.receivedResponse.status,
68115
+ " ",
68116
+ r.receivedResponse.statusText
68117
+ ] }) }),
68118
+ /* @__PURE__ */ jsxRuntimeExports.jsx(KVRows$1, { rows: Object.entries(r.receivedResponse.headers ?? {}) }),
68119
+ r.receivedResponse.body && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: PRE_CLS, children: r.receivedResponse.body })
68120
+ ] })
68121
+ ] });
68122
+ }
67541
68123
  function RunnerModal() {
67542
68124
  const t2 = useT();
67543
68125
  const runnerModal = useStore((s) => s.runnerModal);
@@ -67558,6 +68140,13 @@ function RunnerModal() {
67558
68140
  const [copiedKey, setCopiedKey] = reactExports.useState(null);
67559
68141
  const [exportFormat, setExportFormat] = reactExports.useState("json");
67560
68142
  const [requestDelay, setRequestDelay] = reactExports.useState(0);
68143
+ const [expandedRows, setExpandedRows] = reactExports.useState(/* @__PURE__ */ new Set());
68144
+ const toggleRow = (idx) => setExpandedRows((prev) => {
68145
+ const next = new Set(prev);
68146
+ if (next.has(idx)) next.delete(idx);
68147
+ else next.add(idx);
68148
+ return next;
68149
+ });
67561
68150
  const { collectionId, folderId } = runnerModal;
67562
68151
  const colEntry = collectionId ? collections[collectionId] : null;
67563
68152
  const colName = colEntry?.data.name ?? t2("Collection");
@@ -67574,6 +68163,7 @@ function RunnerModal() {
67574
68163
  setFilterTags(initFilterTagsRef.current);
67575
68164
  setSelectedEnvId(initEnvIdRef.current ?? "");
67576
68165
  setSummary(null);
68166
+ setExpandedRows(/* @__PURE__ */ new Set());
67577
68167
  progressIdxRef.current = 0;
67578
68168
  }, [runnerModal.open]);
67579
68169
  const toggleTag = (tag) => setFilterTags((prev) => prev.includes(tag) ? prev.filter((tg) => tg !== tag) : [...prev, tag]);
@@ -67581,22 +68171,9 @@ function RunnerModal() {
67581
68171
  const ds = (folderId && colEntry ? findFolder$1(colEntry.data.rootFolder, folderId)?.dataSet : null) ?? colEntry?.data.dataSet ?? { columns: [], rows: [] };
67582
68172
  const baseItems = collectionId ? collectRequests(collectionId, folderId, filterTags) : [];
67583
68173
  if (baseItems.length === 0) return;
67584
- let items2;
67585
- if (ds.rows.length > 0) {
67586
- items2 = ds.rows.flatMap((row, ri) => {
67587
- const dataRow = {};
67588
- ds.columns.forEach((col2, ci) => {
67589
- dataRow[col2] = row[ci] ?? "";
67590
- });
67591
- return baseItems.map((item) => ({
67592
- ...item,
67593
- dataRow,
67594
- iterationLabel: `${ri + 1}/${ds.rows.length}`
67595
- }));
67596
- });
67597
- } else {
67598
- items2 = baseItems;
67599
- }
68174
+ let items2 = baseItems;
68175
+ if (!folderId && colEntry) items2 = expandFolderDataSets(items2, colEntry.data);
68176
+ items2 = expandRunPlanWithData(items2, ds);
67600
68177
  const col = colEntry?.data;
67601
68178
  if (col) {
67602
68179
  items2 = items2.map((item) => {
@@ -67613,6 +68190,7 @@ function RunnerModal() {
67613
68190
  });
67614
68191
  }
67615
68192
  const env2 = resolveEnvironmentById(environments, selectedEnvId || null);
68193
+ setExpandedRows(/* @__PURE__ */ new Set());
67616
68194
  setRunnerResults(items2.map((item) => ({
67617
68195
  requestId: item.request.id,
67618
68196
  name: item.request.name,
@@ -67779,9 +68357,13 @@ function RunnerModal() {
67779
68357
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
67780
68358
  "tr",
67781
68359
  {
67782
- className: `border-b border-surface-800/50 hover:bg-surface-800/30 ${r.isHook ? "opacity-80" : ""}`,
68360
+ onClick: () => hasDetail(r) && toggleRow(idx),
68361
+ className: `border-b border-surface-800/50 hover:bg-surface-800/30 ${r.isHook ? "opacity-80" : ""} ${hasDetail(r) ? "cursor-pointer" : ""}`,
67783
68362
  children: [
67784
- /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 w-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(StatusDot, { status: r.status }) }),
68363
+ /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-4 py-2 w-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
68364
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 text-surface-600 text-[9px]", children: hasDetail(r) ? expandedRows.has(idx) ? "▾" : "▸" : "" }),
68365
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusDot, { status: r.status })
68366
+ ] }) }),
67785
68367
  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-2 pr-2 w-20", children: r.isHook && r.hookType ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] font-bold px-1.5 py-0.5 rounded uppercase tracking-wide whitespace-nowrap ${HOOK_BADGE[r.hookType]?.cls ?? ""}`, children: t2(HOOK_BADGE[r.hookType]?.label ?? "") }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold ${getMethodColor(r.method)}`, children: r.method }) }),
67786
68368
  /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "py-2 pr-2", children: [
67787
68369
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `truncate max-w-[260px] ${r.isHook ? "text-surface-400 italic" : "text-[var(--text-primary)]"}`, children: [
@@ -67805,7 +68387,8 @@ function RunnerModal() {
67805
68387
  ] })
67806
68388
  ]
67807
68389
  }
67808
- )
68390
+ ),
68391
+ expandedRows.has(idx) && hasDetail(r) && /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { className: "border-b border-surface-800/50 bg-surface-950/40", children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 6, className: "px-6 py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ResultDetail, { r }) }) })
67809
68392
  ] }, idx);
67810
68393
  }) }) }) }),
67811
68394
  summary && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-4 py-2 border-t border-surface-800 bg-surface-800/30 flex-shrink-0 text-xs", children: [
@@ -72665,7 +73248,7 @@ function App() {
72665
73248
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
72666
73249
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
72667
73250
  "v",
72668
- "0.5.8"
73251
+ "0.5.9"
72669
73252
  ] }),
72670
73253
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
72671
73254
  /* @__PURE__ */ jsxRuntimeExports.jsx(