state.svelte.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. import { Channel, invoke } from "@tauri-apps/api/core";
  2. import type {
  3. Workspace,
  4. WorkspaceEntryBase,
  5. RequestBody,
  6. WorkspaceEntry,
  7. WorkspaceEnvironment,
  8. EnvVariable,
  9. RequestUrl,
  10. RequestHeader,
  11. PathParam,
  12. Authentication,
  13. AuthType,
  14. HttpResponse,
  15. ResponseResult,
  16. QueryParam,
  17. } from "./types";
  18. import { getSetting, setSetting } from "./settings.svelte";
  19. export type WorkspaceState = {
  20. /**
  21. * Currently selected workspace.
  22. */
  23. workspace: Workspace | null;
  24. /**
  25. * Currently selected workspace entry.
  26. */
  27. entry: WorkspaceEntry | null;
  28. /**
  29. * Workspace root entries.
  30. */
  31. roots: number[];
  32. /**
  33. * Workspace entry root => children mappings.
  34. */
  35. children: Record<number, number[]>;
  36. /**
  37. * All workspace entries.
  38. */
  39. indexes: Record<number, WorkspaceEntry>;
  40. /**
  41. * Currently selected workspace environments.
  42. */
  43. environments: WorkspaceEnvironment[];
  44. /**
  45. * Currently selected environment.
  46. */
  47. environment: WorkspaceEnvironment | null;
  48. /**
  49. * All workspace authentication schemes.
  50. */
  51. auth: Authentication[];
  52. /**
  53. * Set of pending sent requests.
  54. */
  55. pendingRequests: number[];
  56. /**
  57. * Maps request IDs to their latest response.
  58. */
  59. responses: Record<number, HttpResponse>;
  60. /**
  61. * Holds entry selection history.
  62. */
  63. entryHistory: number[];
  64. /**
  65. * Holds the currently selected entry index.
  66. */
  67. entryIndex: number;
  68. };
  69. export const state: WorkspaceState = $state({
  70. workspace: null,
  71. entry: null,
  72. roots: [],
  73. children: {},
  74. indexes: {},
  75. environments: [],
  76. environment: null,
  77. auth: [],
  78. pendingRequests: [],
  79. responses: {},
  80. entryHistory: [],
  81. entryIndex: 0,
  82. });
  83. export function isRequestSending() {
  84. return state.pendingRequests.includes(state.entry!!.id);
  85. }
  86. const index = (entry: WorkspaceEntry) => {
  87. console.log("indexing", entry);
  88. state.indexes[entry.id] = entry;
  89. if (entry.parent_id != null) {
  90. if (state.children[entry.parent_id]) {
  91. state.children[entry.parent_id].push(entry.id);
  92. } else {
  93. state.children[entry.parent_id] = [entry.id];
  94. }
  95. } else {
  96. state.roots.push(entry.id);
  97. }
  98. };
  99. const reIndex = (entry: WorkspaceEntry) => {
  100. console.log("re-indexing", entry);
  101. state.indexes[entry.id] = entry;
  102. };
  103. function reset() {
  104. state.children = {};
  105. state.indexes = {};
  106. state.roots = [];
  107. state.entry = null;
  108. state.environment = null;
  109. state.environments = [];
  110. state.auth = [];
  111. state.pendingRequests = [];
  112. state.responses = {};
  113. state.entryHistory = [];
  114. state.entryIndex = 0;
  115. }
  116. /**
  117. * Given a root entry, traverse its children depth first and call `fn` on each, including
  118. * the root.
  119. */
  120. export function traverseEntries(id: number, fn: (e: WorkspaceEntry) => void) {
  121. if (state.indexes[id] == null) {
  122. return;
  123. }
  124. fn(state.indexes[id]);
  125. if (state.children[id] == null) {
  126. return;
  127. }
  128. for (const child of state.children[id]) {
  129. traverseEntries(child, fn);
  130. }
  131. }
  132. export async function selectEnvironment(
  133. id: number | null,
  134. save: boolean = true,
  135. ) {
  136. if (id === null) {
  137. state.environment = null;
  138. let env = await getSetting("lastEnvironment");
  139. if (env) {
  140. env[state.workspace!!.id] = null;
  141. } else {
  142. env = { [state.workspace!!.id]: null };
  143. }
  144. setSetting("lastEnvironment", env);
  145. return;
  146. }
  147. state.environment = state.environments.find((e) => e.id === id) ?? null;
  148. console.debug("selected environment:", state.environment?.name);
  149. if (!save) {
  150. return;
  151. }
  152. let env = await getSetting("lastEnvironment");
  153. if (env) {
  154. env[state.workspace!!.id] = id;
  155. } else {
  156. env = { [state.workspace!!.id]: id };
  157. }
  158. setSetting("lastEnvironment", env);
  159. }
  160. export function selectWorkspace(ws: Workspace) {
  161. console.debug("selecting workspace:", ws.name);
  162. state.workspace = ws;
  163. }
  164. export async function selectPreviousEntry() {
  165. if (state.entryHistory.length === 0) {
  166. return;
  167. }
  168. if (state.entryIndex === 0) {
  169. return;
  170. }
  171. state.entryIndex -= 1;
  172. state.entry = state.indexes[state.entryHistory[state.entryIndex]];
  173. console.log($state.snapshot(state.entryIndex));
  174. console.log($state.snapshot(state.entryHistory));
  175. }
  176. export async function selectNextEntry() {
  177. if (state.entryHistory.length === 0) {
  178. return;
  179. }
  180. if (state.entryIndex === state.entryHistory.length - 1) {
  181. return;
  182. }
  183. state.entryIndex += 1;
  184. state.entry = state.indexes[state.entryHistory[state.entryIndex]];
  185. console.log($state.snapshot(state.entryIndex));
  186. console.log($state.snapshot(state.entryHistory));
  187. }
  188. function pushEntry(id: number) {
  189. if (state.entryIndex < state.entryHistory.length - 1) {
  190. state.entryHistory.splice(state.entryIndex + 1);
  191. }
  192. if (state.entryHistory[state.entryHistory.length - 1] !== id) {
  193. state.entryHistory.push(id);
  194. }
  195. state.entryIndex = state.entryHistory.length - 1;
  196. console.log($state.snapshot(state.entryIndex));
  197. console.log($state.snapshot(state.entryHistory));
  198. }
  199. export async function selectEntry(id: number) {
  200. if (state.entry?.id === id) {
  201. return;
  202. }
  203. const open = state.indexes[id].open;
  204. const entry = await invoke<WorkspaceEntryResponse>("get_workspace_entry", {
  205. entryId: id,
  206. });
  207. switch (entry.type) {
  208. case "Collection": {
  209. state.entry = entry.data;
  210. state.entry.open = open;
  211. break;
  212. }
  213. case "Request": {
  214. state.entry = {
  215. ...entry.data.entry,
  216. method: entry.data.method,
  217. url: entry.data.url,
  218. headers: entry.data.headers,
  219. body: entry.data.body,
  220. path: entry.data.path_params,
  221. query: entry.data.query_params,
  222. open,
  223. };
  224. break;
  225. }
  226. }
  227. reIndex(state.entry);
  228. pushEntry(id);
  229. console.log("selected entry:", $state.snapshot(state.entry));
  230. if (state.entry.parent_id != null) {
  231. let parent = state.indexes[state.entry.parent_id];
  232. while (parent) {
  233. parent.open = true;
  234. if (parent.parent_id == null) {
  235. break;
  236. }
  237. parent = state.indexes[parent.parent_id];
  238. }
  239. }
  240. if (state.entry.type === "Request") {
  241. expandUrl()
  242. .then(() =>
  243. console.debug(
  244. "expanded URL:",
  245. $state.snapshot(state.entry.expandedUrl),
  246. ),
  247. )
  248. .catch((e) => {
  249. console.error("error expanding URL", e);
  250. });
  251. }
  252. }
  253. // COMMANDS
  254. export async function createWorkspace(name: string): Promise<Workspace> {
  255. return invoke<Workspace>("create_workspace", { name });
  256. }
  257. export async function listWorkspaces(): Promise<Workspace[]> {
  258. return invoke<Workspace[]>("list_workspaces");
  259. }
  260. export async function loadWorkspace(ws: Workspace) {
  261. reset();
  262. state.workspace = ws;
  263. const entries = await invoke<WorkspaceEntryBase[]>("list_workspace_entries", {
  264. id: state.workspace.id,
  265. });
  266. for (const entry of entries) {
  267. // if (entry.type === "Request") {
  268. // } else {
  269. index(entry);
  270. // }
  271. }
  272. await loadEnvironments(state.workspace.id);
  273. await loadAuths(state.workspace.id);
  274. }
  275. export function createRequest(parentId?: number) {
  276. if (state.workspace == null) {
  277. console.warn("create request called with no active workspace");
  278. return;
  279. }
  280. const data = {
  281. Request: {
  282. name: "",
  283. workspace_id: state.workspace.id,
  284. parent_id: parentId,
  285. method: "GET",
  286. url: "",
  287. auth_inherit: parentId !== undefined,
  288. },
  289. };
  290. invoke<WorkspaceEntryBase>("create_workspace_entry", {
  291. data,
  292. }).then((entry) => {
  293. index(entry);
  294. selectEntry(entry.id);
  295. console.log("request created:", entry);
  296. });
  297. }
  298. export function createCollection(parentId?: number) {
  299. if (state.workspace == null) {
  300. console.warn("create collection called with no active workspace");
  301. return;
  302. }
  303. const data = {
  304. Collection: {
  305. name: "",
  306. workspace_id: state.workspace.id,
  307. parent_id: parentId,
  308. auth_inherit: parentId !== undefined,
  309. },
  310. };
  311. invoke<WorkspaceEntryBase>("create_workspace_entry", {
  312. data,
  313. }).then((entry) => {
  314. index(entry);
  315. selectEntry(entry.id);
  316. console.log("collection created:", entry);
  317. });
  318. }
  319. export async function loadEnvironments(workspaceId: number) {
  320. state.environments = await invoke<WorkspaceEnvironment[]>(
  321. "list_environments",
  322. { workspaceId },
  323. );
  324. const lastEnv = await getSetting("lastEnvironment");
  325. if (lastEnv && lastEnv[workspaceId] !== undefined) {
  326. selectEnvironment(lastEnv[workspaceId], false);
  327. }
  328. }
  329. export async function loadAuths(workspaceId: number) {
  330. state.auth = await invoke<Authentication[]>("list_auth", { workspaceId });
  331. }
  332. export async function createEnvironment(workspaceId: number, name: string) {
  333. console.debug("creating environment in", workspaceId);
  334. const env = await invoke<WorkspaceEnvironment>("create_env", {
  335. workspaceId,
  336. name,
  337. });
  338. state.environment = env;
  339. state.environments.push(state.environment);
  340. }
  341. export async function updateEnvironment() {
  342. if (!state.environment) {
  343. console.warn("attempted to persist null env");
  344. return;
  345. }
  346. console.debug("updating environment", state.environment);
  347. await invoke("update_env", {
  348. id: state.environment.id,
  349. name: state.environment.name,
  350. });
  351. }
  352. export async function sendRequest(): Promise<void> {
  353. const reqId = state.entry!!.id;
  354. if (state.pendingRequests.includes(reqId)) {
  355. console.warn("request is already pending", reqId);
  356. }
  357. console.log("sending request", reqId);
  358. const onComplete = new Channel<ResponseResult>();
  359. console.time("request-" + reqId);
  360. onComplete.onmessage = (response) => {
  361. console.log("received response", response);
  362. switch (response.type) {
  363. case "Ok": {
  364. state.responses[state.entry!!.id] = response.data;
  365. console.log(state.responses);
  366. break;
  367. }
  368. case "Err": {
  369. console.error("received response error", response.data);
  370. break;
  371. }
  372. default: {
  373. console.error("unrecognized response type", response.type);
  374. break;
  375. }
  376. }
  377. console.timeEnd("request-" + reqId);
  378. state.pendingRequests = state.pendingRequests.filter((id) => id !== reqId);
  379. };
  380. await invoke<HttpResponse>("send_request", {
  381. reqId,
  382. envId: state.environment?.id,
  383. onComplete,
  384. });
  385. state.pendingRequests.push(reqId);
  386. }
  387. export async function cancelRequest(): Promise<void> {
  388. if (!state.pendingRequests.includes(state.entry!!.id)) {
  389. console.warn("nothing to cancel!");
  390. return;
  391. }
  392. console.log("cancelling request");
  393. await invoke("cancel_request", { reqId: state.entry!!.id });
  394. console.timeEnd("request-" + state.entry!!.id);
  395. state.pendingRequests = state.pendingRequests.filter(
  396. (id) => id !== state.entry!!.id,
  397. );
  398. }
  399. export async function updateEntryName(name: string) {
  400. if (!state.entry) {
  401. console.warn("attempted to persist null entry");
  402. return;
  403. }
  404. console.debug("entry name update", state.entry.id, name);
  405. await invoke("update_workspace_entry", {
  406. entryId: state.entry.id,
  407. name,
  408. });
  409. state.indexes[state.entry.id].name = name;
  410. }
  411. /**
  412. * Whether the URL string was edited directly in the URL bar,
  413. * in a path parameter, or a query parameter.
  414. */
  415. export type UrlUpdate =
  416. | { type: "URL"; url: string }
  417. | { type: "Path"; url: string; param: PathParam }
  418. | { type: "Query"; url: string; param: QueryParam };
  419. /**
  420. * Update a request's URL string. If `usePathparams` is true, path entries
  421. * from state.entry.path will be used to replace those at the same position and should
  422. * be set to true whenever this is called from an input field of a destructured URL.
  423. */
  424. export async function updateUrl(up: UrlUpdate) {
  425. let update;
  426. switch (up.type) {
  427. case "URL": {
  428. update = { URL: up.url };
  429. break;
  430. }
  431. case "Path": {
  432. update = { Path: [up.url, up.param] };
  433. break;
  434. }
  435. case "Query": {
  436. if (up.param.position == null) {
  437. await invoke("update_query_param_values", {
  438. id: up.param.id,
  439. key: up.param.key,
  440. value: up.param.value,
  441. });
  442. return;
  443. }
  444. update = { Query: [up.url, up.param] };
  445. break;
  446. }
  447. }
  448. const params = await invoke<{
  449. url: RequestUrl;
  450. full: string;
  451. path: PathParam[];
  452. query: QueryParam[];
  453. }>("update_url", {
  454. entryId: state.entry!!.id,
  455. update,
  456. });
  457. switch (up.type) {
  458. case "URL": {
  459. // state.entry.url is already updated
  460. // Direct URL updates must always fully update the parameters
  461. state.entry!!.path = params.path;
  462. state.entry!!.query = params.query;
  463. break;
  464. }
  465. // Path updates are guaranteed not to modify the updated path param position
  466. // They also guarantee the same amount of path parameters as before the update
  467. case "Path": {
  468. state.entry!!.url = params.full;
  469. state.entry!!.query = params.query;
  470. let i = 0;
  471. for (const newParam of params.path) {
  472. if (newParam.position <= up.param.position) {
  473. i += 1;
  474. continue;
  475. }
  476. state.entry!!.path[i] = newParam;
  477. i += 1;
  478. }
  479. break;
  480. }
  481. // Query updates have the same guarantees as path updates
  482. case "Query": {
  483. state.entry!!.url = params.full;
  484. let i = 0;
  485. for (const newParam of params.query) {
  486. if (newParam.position <= up.param.position) {
  487. i += 1;
  488. continue;
  489. }
  490. state.entry!!.query[i] = newParam;
  491. i += 1;
  492. }
  493. break;
  494. }
  495. }
  496. expandUrl();
  497. console.debug("updated", $state.snapshot(state.entry));
  498. }
  499. export async function deleteQueryParam(param: QueryParam) {
  500. console.log($state.snapshot(state.entry.query));
  501. const url = await invoke("delete_query_param", {
  502. id: param.id,
  503. url: state.entry.url,
  504. reqId: state.entry.id,
  505. });
  506. for (const q of state.entry.query) {
  507. if (q.position < param.position!!) {
  508. continue;
  509. }
  510. // +1 for the &, +1 for the =
  511. q.position -= param.key.length + param.value.length + 2;
  512. }
  513. state.entry.query = state.entry.query.filter((p) => p.id !== param.id);
  514. state.entry.url = url;
  515. console.log($state.snapshot(state.entry.query));
  516. expandUrl();
  517. }
  518. export async function updateQueryParamEnabled(param: QueryParam) {
  519. console.log(param);
  520. console.log($state.snapshot(state.entry.query));
  521. const [newQp, url] = await invoke<[QueryParam, string]>(
  522. "update_query_param_enabled",
  523. {
  524. reqId: state.entry.id,
  525. qpId: param.id,
  526. url: state.entry!!.url,
  527. },
  528. );
  529. console.log("updated QP", newQp, url);
  530. // Param was removed, offset all positions
  531. if (newQp.position == null) {
  532. for (const q of state.entry.query) {
  533. if (q.position == null || q.position < param.position!!) {
  534. continue;
  535. }
  536. // +1 for the &, +1 for the =
  537. q.position -= newQp.key.length + newQp.value.length + 2;
  538. }
  539. }
  540. state.entry!!.url = url;
  541. param.key = newQp.key;
  542. param.value = newQp.value;
  543. param.position = newQp.position;
  544. console.log($state.snapshot(state.entry.query));
  545. expandUrl();
  546. }
  547. export async function updateRequestMethod(method: string) {
  548. await invoke("update_request_method", { id: state.entry.id, method });
  549. state.entry!!.method = method;
  550. console.log(
  551. "update request method:",
  552. $state.snapshot(state.entry.id),
  553. $state.snapshot(state.entry.method),
  554. );
  555. }
  556. export async function expandUrl() {
  557. state.entry!!.expandedUrl = await invoke<string>("expand_url", {
  558. entryId: state.entry!!.id,
  559. envId: state.environment?.id,
  560. url: state.entry!!.url,
  561. });
  562. }
  563. export async function insertEnvVariable(
  564. workspaceId: number,
  565. envId: number,
  566. name: string = "",
  567. value: string = "",
  568. secret: boolean = false,
  569. ) {
  570. const v = await invoke<EnvVariable>("insert_env_var", {
  571. workspaceId,
  572. envId,
  573. name,
  574. value,
  575. secret,
  576. });
  577. state.environment?.variables.push(v);
  578. }
  579. export async function updateEnvVariable(v: EnvVariable) {
  580. if (v.name.length === 0 && v.value.length === 0) {
  581. console.debug("deleting var:", v);
  582. return deleteEnvVariable(v.id);
  583. }
  584. console.debug("updating var:", v);
  585. return invoke("update_env_var", {
  586. id: v.id,
  587. name: v.name,
  588. value: v.value,
  589. secret: v.secret,
  590. });
  591. }
  592. export async function deleteEnvVariable(id: number) {
  593. await invoke("delete_env_var", { id });
  594. state.environment!!.variables = state.environment!!.variables.filter(
  595. (v) => v.id !== id,
  596. );
  597. }
  598. export async function insertHeader(name: string = "", value: string = "") {
  599. const header: RequestHeader = await invoke("insert_header", {
  600. entryId: state.entry!!.id,
  601. insert: { name, value },
  602. });
  603. state.entry!!.headers.push(header);
  604. return header.id;
  605. }
  606. export async function updateHeader(id: number, name: string, value: string) {
  607. await invoke("update_header", {
  608. update: { id, name, value },
  609. });
  610. const header = state.entry!!.headers.find((header) => header.id === id);
  611. header.name = name;
  612. header.value = value;
  613. }
  614. export async function updateHeaderEnabled(id: number, enabled: boolean) {
  615. await invoke("update_header_enabled", { headerId: id, enabled });
  616. const header = state.entry!!.headers.find((header) => header.id === id);
  617. header.enabled = enabled;
  618. }
  619. export async function deleteHeader(id: number) {
  620. await invoke("delete_header", {
  621. headerId: id,
  622. });
  623. state.entry!!.headers = state.entry!!.headers.filter(
  624. (header) => header.id !== id,
  625. );
  626. }
  627. export async function deleteBody() {
  628. if (state.entry!!.body === null) {
  629. console.warn("attempted to delete null body", $state.snapshot(state.entry));
  630. return;
  631. }
  632. await invoke("update_request_body", {
  633. id: state.entry!!.body.id,
  634. body: { Null: null },
  635. });
  636. state.entry.body = null;
  637. console.debug("Deleted request body");
  638. }
  639. export async function updateBodyContent(content: string, ty: string) {
  640. if (state.entry!!.body != null) {
  641. await invoke("update_request_body", {
  642. id: state.entry!!.body.id,
  643. body: { Value: { ty, content } },
  644. });
  645. state.entry!!.body.content = content;
  646. state.entry!!.body.ty = ty;
  647. } else {
  648. const b = await invoke("insert_request_body", {
  649. entryId: state.entry!!.id,
  650. body: { ty, content },
  651. });
  652. state.entry!!.body = b;
  653. }
  654. console.debug("Updated body content to", $state.snapshot(state.entry!!.body));
  655. }
  656. export async function createAuth(type: AuthType) {
  657. const auth = await invoke<Authentication>("insert_auth", {
  658. workspaceId: state.workspace!!.id,
  659. type,
  660. });
  661. console.debug("created auth", auth);
  662. state.auth.unshift(auth);
  663. }
  664. export async function renameAuth(id: number, name: string) {
  665. await invoke<Authentication>("rename_auth", {
  666. id,
  667. name,
  668. });
  669. const auth = state.auth.find((a) => a.id === id);
  670. auth!!.name = name;
  671. }
  672. export async function updateAuthParams(id: number) {
  673. const auth = state.auth.find((a) => a.id === id);
  674. console.debug("updating auth params", $state.snapshot(auth));
  675. if (!auth) {
  676. console.warn("Attempted to update non-existing auth", id);
  677. return;
  678. }
  679. console.log($state.snapshot(auth.params));
  680. await invoke<Authentication>("update_auth", {
  681. id,
  682. params: auth.params,
  683. });
  684. }
  685. export async function deleteAuth(id: number) {
  686. console.debug("deleting auth", id);
  687. await invoke("delete_auth", { id });
  688. state.auth = state.auth.filter((a) => a.id !== id);
  689. }
  690. export async function setEntryAuth(id: number | null, inherit: boolean | null) {
  691. console.debug("setting entry auth to", id, "inheriting:", inherit);
  692. await invoke("set_workspace_entry_auth", {
  693. entryId: state.entry!!.id,
  694. authId: id,
  695. inherit,
  696. });
  697. state.entry!!.auth = id;
  698. state.indexes[state.entry!!.id].auth = id;
  699. if (inherit != null) {
  700. state.entry!!.auth_inherit = inherit;
  701. state.indexes[state.entry!!.id].auth_inherit = inherit;
  702. }
  703. }
  704. type WorkspaceEntryResponse =
  705. | {
  706. type: "Collection";
  707. data: WorkspaceEntryBase;
  708. }
  709. | {
  710. type: "Request";
  711. data: WorkspaceRequestResponse;
  712. };
  713. type WorkspaceRequestResponse = {
  714. entry: WorkspaceEntryBase;
  715. method: string;
  716. url: string;
  717. body: RequestBody | null;
  718. headers: RequestHeader[];
  719. path_params: PathParam[];
  720. query_params: QueryParam[];
  721. };