@testsmith/api-spector 0.9.0 → 0.9.2

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.
@@ -23,7 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
25
25
  const electron = require("electron");
26
- const handle = require("./chunks/handle-_4XMUlwm.js");
26
+ const handle = require("./chunks/handle-HQp1EXf8.js");
27
27
  const promises = require("node:fs/promises");
28
28
  const path = require("node:path");
29
29
  const crypto = require("node:crypto");
@@ -31,12 +31,12 @@ const JSZip = require("jszip");
31
31
  const simpleGit = require("simple-git");
32
32
  const dataFiles = require("./chunks/data-files-BYqhsoPe.js");
33
33
  const requestCollection = require("./chunks/request-collection-zRRUfg0X.js");
34
- const requestExec = require("./chunks/request-exec-CcF1Fzve.js");
34
+ const requestExec = require("./chunks/request-exec-B8DJ8Id2.js");
35
35
  const ipcValidate = require("./chunks/ipc-validate-sqUFqRmE.js");
36
36
  const uuid = require("uuid");
37
37
  const jsYaml = require("js-yaml");
38
38
  const undici = require("undici");
39
- const soapHandler = require("./chunks/soap-handler-BF5ii0UV.js");
39
+ const soapHandler = require("./chunks/soap-handler-D_Hj9e8S.js");
40
40
  const runConcurrent = require("./chunks/run-concurrent-468tH0AX.js");
41
41
  const flowEngine = require("./chunks/flow-engine-BxwjEy37.js");
42
42
  const mockServer = require("./chunks/mock-server-C4kTFkod.js");
@@ -50,7 +50,7 @@ const os = require("node:os");
50
50
  const grpc = require("@grpc/grpc-js");
51
51
  const protoLoader = require("@grpc/proto-loader");
52
52
  const escape = require("./chunks/escape-DDVAlOvZ.js");
53
- const brokerClient = require("./chunks/broker-client-DqDgA2G6.js");
53
+ const brokerClient = require("./chunks/broker-client-Cl6Fu3oD.js");
54
54
  const recorder = require("./chunks/recorder-BkO8G_mB.js");
55
55
  require("fs/promises");
56
56
  require("tls");
@@ -269,6 +269,62 @@ function defaultWorkspaceName() {
269
269
  }
270
270
  return "my-workspace.spector";
271
271
  }
272
+ const SYNC_SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
273
+ const SYNC_SKIP_FILES = /* @__PURE__ */ new Set(["history.json"]);
274
+ function stripEnvSecrets(text) {
275
+ try {
276
+ const env = JSON.parse(text);
277
+ if (Array.isArray(env.variables)) {
278
+ env.variables = env.variables.map((v) => {
279
+ const rest = { ...v };
280
+ delete rest["secretEncrypted"];
281
+ delete rest["secretSalt"];
282
+ delete rest["secretIv"];
283
+ delete rest["secretHash"];
284
+ if (rest["secret"]) rest["value"] = "";
285
+ return rest;
286
+ });
287
+ }
288
+ return JSON.stringify(env, null, 2);
289
+ } catch {
290
+ return text;
291
+ }
292
+ }
293
+ function mergeEnvSecrets(remoteText, localText) {
294
+ if (!localText) return remoteText;
295
+ try {
296
+ const remote = JSON.parse(remoteText);
297
+ const local = JSON.parse(localText);
298
+ const byKey = /* @__PURE__ */ new Map();
299
+ for (const lv of local.variables ?? []) byKey.set(String(lv["key"]), lv);
300
+ remote.variables = (remote.variables ?? []).map((rv) => {
301
+ const lv = byKey.get(String(rv["key"]));
302
+ if (!lv) return rv;
303
+ const merged = { ...rv };
304
+ for (const f of ["secretEncrypted", "secretSalt", "secretIv", "secretHash"]) {
305
+ if (lv[f] !== void 0) merged[f] = lv[f];
306
+ }
307
+ if (rv["secret"] && lv["value"]) merged["value"] = lv["value"];
308
+ return merged;
309
+ });
310
+ return JSON.stringify(remote, null, 2);
311
+ } catch {
312
+ return remoteText;
313
+ }
314
+ }
315
+ async function walkWorkspaceFiles(dir, base, out) {
316
+ const entries = await promises.readdir(dir, { withFileTypes: true });
317
+ for (const entry of entries) {
318
+ if (entry.isDirectory()) {
319
+ if (SYNC_SKIP_DIRS.has(entry.name)) continue;
320
+ await walkWorkspaceFiles(path.join(dir, entry.name), base, out);
321
+ } else if (entry.isFile()) {
322
+ if (SYNC_SKIP_FILES.has(entry.name)) continue;
323
+ const full = path.join(dir, entry.name);
324
+ out[full.slice(base.length + 1).split(path.sep).join("/")] = await promises.readFile(full, "utf8");
325
+ }
326
+ }
327
+ }
272
328
  function registerFileHandlers(ipc) {
273
329
  handle.handleIpc(ipc, handle.IPC.file.openWorkspace, async () => {
274
330
  const result = await electron.dialog.showOpenDialog({
@@ -383,6 +439,68 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
383
439
  if (err.code !== "ENOENT") throw err;
384
440
  }
385
441
  });
442
+ handle.handleIpc(ipc, handle.IPC.file.serializeWorkspace, async () => {
443
+ if (!workspaceDir || !workspaceFile) throw new Error("No workspace open");
444
+ const manifest = JSON.parse(await promises.readFile(workspaceFile, "utf8"));
445
+ const files = {};
446
+ await walkWorkspaceFiles(workspaceDir, workspaceDir, files);
447
+ const manifestName = path.basename(workspaceFile);
448
+ delete files[manifestName];
449
+ for (const rel of Object.keys(files)) {
450
+ if (rel.startsWith("environments/")) files[rel] = stripEnvSecrets(files[rel]);
451
+ }
452
+ const sharedSettings = { ...manifest.settings ?? {} };
453
+ delete sharedSettings["cloud"];
454
+ delete sharedSettings["sync"];
455
+ return { manifest: { ...manifest, settings: sharedSettings }, manifestName, files };
456
+ });
457
+ handle.handleIpc(ipc, handle.IPC.file.applyWorkspaceSnapshot, async (_e, snap) => {
458
+ if (!workspaceDir || !workspaceFile) throw new Error("No workspace open");
459
+ try {
460
+ const backupDir = path.join(electron.app.getPath("userData"), "sync-backups", path.basename(workspaceDir) + "-" + Date.now());
461
+ await promises.cp(workspaceDir, backupDir, {
462
+ recursive: true,
463
+ filter: (src) => {
464
+ const rel = src.slice(workspaceDir.length);
465
+ return !/[\\/](\.git|node_modules)([\\/]|$)/.test(rel);
466
+ }
467
+ });
468
+ } catch (err) {
469
+ console.warn("sync: workspace backup failed (continuing):", err);
470
+ }
471
+ const guard = path.resolve(workspaceDir) + (process.platform === "win32" ? "\\" : "/");
472
+ let filesWritten = 0;
473
+ for (const [rel, content] of Object.entries(snap.files ?? {})) {
474
+ const full = path.resolve(workspaceDir, rel);
475
+ if (!full.startsWith(guard)) continue;
476
+ let out = content;
477
+ if (rel.startsWith("environments/")) {
478
+ let localText = null;
479
+ try {
480
+ localText = await promises.readFile(full, "utf8");
481
+ } catch {
482
+ }
483
+ out = mergeEnvSecrets(content, localText);
484
+ }
485
+ await promises.mkdir(path.dirname(full), { recursive: true });
486
+ await atomicWrite(full, out);
487
+ filesWritten++;
488
+ }
489
+ const local = JSON.parse(await promises.readFile(workspaceFile, "utf8"));
490
+ const merged = {
491
+ ...local,
492
+ ...snap.manifest,
493
+ settings: { ...snap.manifest.settings ?? {}, ...local.settings ?? {} }
494
+ };
495
+ await atomicWrite(workspaceFile, JSON.stringify(merged, null, 2));
496
+ return {
497
+ ok: true,
498
+ filesWritten,
499
+ collections: Array.isArray(merged.collections) ? merged.collections.length : 0,
500
+ workspace: merged,
501
+ workspacePath: workspaceFile
502
+ };
503
+ });
386
504
  handle.handleIpc(ipc, handle.IPC.file.loadHistory, async () => {
387
505
  if (!workspaceDir) return [];
388
506
  try {
@@ -2677,7 +2795,7 @@ function registerImportHandlers(ipc) {
2677
2795
  if (opts.url?.trim()) {
2678
2796
  const url = opts.url.trim();
2679
2797
  const { validateWsdlFetchUrl } = await Promise.resolve().then(() => require("./chunks/ipc-validate-sqUFqRmE.js"));
2680
- const { fetchUrl } = await Promise.resolve().then(() => require("./chunks/soap-handler-BF5ii0UV.js")).then((n) => n.soapHandler);
2798
+ const { fetchUrl } = await Promise.resolve().then(() => require("./chunks/soap-handler-D_Hj9e8S.js")).then((n) => n.soapHandler);
2681
2799
  validateWsdlFetchUrl(url);
2682
2800
  wsdlText = await fetchUrl(url);
2683
2801
  try {
@@ -7573,6 +7691,30 @@ function registerGitHandlers(ipc) {
7573
7691
  handle.handleIpc(ipc, handle.IPC.git.deleteBranch, async (_e, name, force = false) => {
7574
7692
  await git().deleteLocalBranch(name, force);
7575
7693
  });
7694
+ handle.handleIpc(ipc, handle.IPC.git.stashList, async () => {
7695
+ const raw = await git().raw(["stash", "list", "--format=%gd%gs"]);
7696
+ return raw.split("\n").filter(Boolean).map((line, index) => {
7697
+ const [ref = `stash@{${index}}`, subject = ""] = line.split("");
7698
+ const m = /^(?:WIP on|On) ([^:]+): (.*)$/.exec(subject);
7699
+ return {
7700
+ index,
7701
+ ref,
7702
+ branch: m ? m[1] : "",
7703
+ message: m ? m[2] : subject
7704
+ };
7705
+ });
7706
+ });
7707
+ handle.handleIpc(ipc, handle.IPC.git.stash, async (_e, message) => {
7708
+ const args = ["stash", "push", "-u"];
7709
+ if (message && message.trim()) args.push("-m", message.trim());
7710
+ await git().raw(args);
7711
+ });
7712
+ handle.handleIpc(ipc, handle.IPC.git.stashPop, async (_e, ref) => {
7713
+ await git().raw(["stash", "pop", ...ref ? [ref] : []]);
7714
+ });
7715
+ handle.handleIpc(ipc, handle.IPC.git.stashDrop, async (_e, ref) => {
7716
+ await git().raw(["stash", "drop", ...ref ? [ref] : []]);
7717
+ });
7576
7718
  handle.handleIpc(ipc, handle.IPC.git.fetch, async () => {
7577
7719
  await git().fetch(["--prune"]);
7578
7720
  });
@@ -7921,6 +8063,76 @@ function registerCloudHandlers(ipc) {
7921
8063
  handle.handleIpc(ipc, handle.IPC.cloud.openFlow, async (_e, name) => {
7922
8064
  await electron.shell.openExternal(CLOUD_ENDPOINT + "/flows/" + encodeURIComponent(slugify(name)));
7923
8065
  });
8066
+ const stripRequest = (r) => {
8067
+ const { examples: _examples, ...rest } = r;
8068
+ return rest;
8069
+ };
8070
+ const stripFolder = (f) => {
8071
+ const { dataSet: _d, dataSetRef: _r, ...rest } = f ?? {};
8072
+ const folders = Array.isArray(rest["folders"]) ? rest["folders"].map(stripFolder) : [];
8073
+ return { ...rest, folders };
8074
+ };
8075
+ const stripCollection = (col) => {
8076
+ const { dataSet: _d, dataSetRef: _r, ...rest } = col;
8077
+ const requests = {};
8078
+ for (const [id, req] of Object.entries(col.requests ?? {})) requests[id] = stripRequest(req);
8079
+ return { ...rest, requests, rootFolder: stripFolder(col.rootFolder) };
8080
+ };
8081
+ handle.handleIpc(ipc, handle.IPC.cloud.shareCollection, async (_e, col) => {
8082
+ return cloudFetch("/api/share", "POST", { type: "collection", source_id: col.id, name: col.name, payload: stripCollection(col) });
8083
+ });
8084
+ handle.handleIpc(ipc, handle.IPC.cloud.shareRequest, async (_e, req) => {
8085
+ return cloudFetch("/api/share", "POST", { type: "request", source_id: req.id, name: req.name, payload: stripRequest(req) });
8086
+ });
8087
+ handle.handleIpc(ipc, handle.IPC.cloud.shareFolder, async (_e, arg) => {
8088
+ return cloudFetch("/api/share", "POST", { type: "folder", source_id: arg.sourceId, name: arg.name, payload: stripCollection(arg.payload) });
8089
+ });
8090
+ handle.handleIpc(ipc, handle.IPC.cloud.shareLookup, async (_e, arg) => {
8091
+ const q = new URLSearchParams({ type: arg.type, source_id: arg.sourceId }).toString();
8092
+ return cloudFetch("/api/share-lookup?" + q, "GET");
8093
+ });
8094
+ handle.handleIpc(ipc, handle.IPC.cloud.shareUpdate, async (_e, arg) => {
8095
+ const token = String(arg.token).replace(/[^a-z0-9]/gi, "");
8096
+ return cloudFetch("/api/share/" + encodeURIComponent(token), "PATCH", { expires_days: arg.expiresDays ?? 0 });
8097
+ });
8098
+ handle.handleIpc(ipc, handle.IPC.cloud.shareRevoke, async (_e, token) => {
8099
+ const clean = String(token).replace(/[^a-z0-9]/gi, "");
8100
+ return cloudFetch("/api/share/" + encodeURIComponent(clean), "DELETE");
8101
+ });
8102
+ handle.handleIpc(ipc, handle.IPC.cloud.workspaceList, async () => cloudFetch("/api/workspaces", "GET"));
8103
+ handle.handleIpc(ipc, handle.IPC.cloud.workspaceCreate, async (_e, arg) => cloudFetch("/api/workspaces", "POST", { name: arg.name, payload: arg.payload, author: arg.author }));
8104
+ handle.handleIpc(ipc, handle.IPC.cloud.workspacePull, async (_e, id) => cloudFetch("/api/workspaces/" + encodeURIComponent(String(id)), "GET"));
8105
+ handle.handleIpc(ipc, handle.IPC.cloud.workspaceVersions, async (_e, id) => cloudFetch("/api/workspaces/" + encodeURIComponent(String(id)) + "/versions", "GET"));
8106
+ handle.handleIpc(ipc, handle.IPC.cloud.workspaceRestore, async (_e, arg) => cloudFetch("/api/workspaces/" + encodeURIComponent(String(arg.id)) + "/restore", "POST", { version: arg.version }));
8107
+ handle.handleIpc(ipc, handle.IPC.cloud.workspacePush, async (_e, arg) => {
8108
+ const token = await requestExec.getSecret(CLOUD_TOKEN_REF);
8109
+ if (!token) throw new Error("No cloud API token set. Add one in Settings → Cloud.");
8110
+ const dispatcher = await requestExec.buildDispatcher();
8111
+ const res = await undici.fetch(CLOUD_ENDPOINT + "/api/workspaces/" + encodeURIComponent(String(arg.id)), {
8112
+ method: "PUT",
8113
+ headers: { "Content-Type": "application/json", Accept: "application/json", Authorization: `Bearer ${token}` },
8114
+ body: JSON.stringify({ base_version: arg.baseVersion, payload: arg.payload, author: arg.author }),
8115
+ dispatcher
8116
+ });
8117
+ const json = await res.json().catch(() => ({}));
8118
+ if (res.status === 409) return { conflict: true, currentVersion: json.current_version };
8119
+ if (!res.ok) throw new Error(json.error || json.message || `Push failed (HTTP ${res.status}).`);
8120
+ return { conflict: false, version: json.version };
8121
+ });
8122
+ handle.handleIpc(ipc, handle.IPC.cloud.importShare, async (_e, token) => {
8123
+ const cleaned = String(token).replace(/[^a-z0-9]/gi, "");
8124
+ if (!cleaned) throw new Error("Invalid share link.");
8125
+ const dispatcher = await requestExec.buildDispatcher();
8126
+ const res = await undici.fetch(CLOUD_ENDPOINT + "/api/share/" + encodeURIComponent(cleaned), {
8127
+ method: "GET",
8128
+ headers: { Accept: "application/json" },
8129
+ dispatcher
8130
+ });
8131
+ if (!res.ok) {
8132
+ throw new Error(res.status === 404 || res.status === 410 ? "This share link is invalid or has expired." : `Could not fetch the shared item (HTTP ${res.status}).`);
8133
+ }
8134
+ return res.json();
8135
+ });
7924
8136
  handle.handleIpc(ipc, handle.IPC.cloud.pushMonitor, async (_e, input) => {
7925
8137
  const { request, environment, collectionVars, globals } = input;
7926
8138
  const envVars = await requestExec.buildEnvVars(environment);
@@ -2,7 +2,7 @@
2
2
  "use strict";
3
3
  const promises = require("node:fs/promises");
4
4
  const path = require("node:path");
5
- const requestExec = require("./chunks/request-exec-CcF1Fzve.js");
5
+ const requestExec = require("./chunks/request-exec-B8DJ8Id2.js");
6
6
  const requestCollection = require("./chunks/request-collection-zRRUfg0X.js");
7
7
  const escape = require("./chunks/escape-DDVAlOvZ.js");
8
8
  const runConcurrent = require("./chunks/run-concurrent-468tH0AX.js");
@@ -11,7 +11,7 @@ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
11
11
  require("undici");
12
12
  require("fs/promises");
13
13
  require("tls");
14
- require("./chunks/handle-_4XMUlwm.js");
14
+ require("./chunks/handle-HQp1EXf8.js");
15
15
  require("node:fs");
16
16
  require("node:os");
17
17
  require("node:crypto");
@@ -551,7 +551,7 @@ async function main() {
551
551
  } else if (!envName && workspace.settings?.defaultEnvironment && !env) {
552
552
  console.warn(cliCommon.color(`Warning: default environment "${workspace.settings.defaultEnvironment}" not found. Running without environment.`, cliCommon.C.yellow));
553
553
  }
554
- const version = `v${"0.9.0"}`;
554
+ const version = `v${"0.9.2"}`;
555
555
  console.log("");
556
556
  console.log(cliCommon.color(" API Test Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
557
557
  console.log(cliCommon.color(` Workspace: ${wsPath}`, cliCommon.C.gray));
package/out/main/wsdl.js CHANGED
@@ -4,10 +4,10 @@ const promises = require("node:fs/promises");
4
4
  const path = require("node:path");
5
5
  const https = require("node:https");
6
6
  const http = require("node:http");
7
- const soapHandler = require("./chunks/soap-handler-BF5ii0UV.js");
8
- const _import = require("./chunks/import-ujxXe3Uu.js");
7
+ const soapHandler = require("./chunks/soap-handler-D_Hj9e8S.js");
8
+ const _import = require("./chunks/import-DVpFH9Wb.js");
9
9
  const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
10
- require("./chunks/handle-_4XMUlwm.js");
10
+ require("./chunks/handle-HQp1EXf8.js");
11
11
  require("@xmldom/xmldom");
12
12
  require("uuid");
13
13
  require("./chunks/data-files-BYqhsoPe.js");
@@ -9,6 +9,13 @@ const IPC = {
9
9
  /** Event (main→renderer): a `spector://open-from-git?url=…` deep link was
10
10
  * received; the renderer runs the same open-from-Git flow as the button. */
11
11
  openFromGitDeepLink: "file:openFromGitDeepLink",
12
+ /** Event (main→renderer): a `spector://import?token=…` deep link was
13
+ * received; the renderer imports the shared collection/request. */
14
+ importShareDeepLink: "file:importShareDeepLink",
15
+ /** Serialize the open workspace to a secret-free snapshot for cloud sync. */
16
+ serializeWorkspace: "file:serializeWorkspace",
17
+ /** Apply a pulled snapshot to disk (backs up first; merges local secrets). */
18
+ applyWorkspaceSnapshot: "file:applyWorkspaceSnapshot",
12
19
  getLastWorkspace: "file:getLastWorkspace",
13
20
  getRecentWorkspaces: "file:getRecentWorkspaces",
14
21
  saveWorkspace: "file:saveWorkspace",
@@ -241,6 +248,10 @@ const IPC = {
241
248
  branches: "git:branches",
242
249
  checkout: "git:checkout",
243
250
  deleteBranch: "git:deleteBranch",
251
+ stashList: "git:stashList",
252
+ stash: "git:stash",
253
+ stashPop: "git:stashPop",
254
+ stashDrop: "git:stashDrop",
244
255
  fetch: "git:fetch",
245
256
  pull: "git:pull",
246
257
  push: "git:push",
@@ -309,7 +320,28 @@ const IPC = {
309
320
  /** Open a flow's page in the cloud UI. */
310
321
  openFlow: "cloud:openFlow",
311
322
  /** Open the cloud deployment matrix in the browser. */
312
- openMatrix: "cloud:openMatrix"
323
+ openMatrix: "cloud:openMatrix",
324
+ /** Share a collection as a public read-only link (POST /api/share). */
325
+ shareCollection: "cloud:shareCollection",
326
+ /** Share a single request as a public read-only link (POST /api/share). */
327
+ shareRequest: "cloud:shareRequest",
328
+ /** Share a folder subtree as a public read-only link (POST /api/share). */
329
+ shareFolder: "cloud:shareFolder",
330
+ /** Fetch a shared collection/request by token for import (GET /api/share/{token}). */
331
+ importShare: "cloud:importShare",
332
+ /** Look up an existing share for a local collection/request (GET /api/share-lookup). */
333
+ shareLookup: "cloud:shareLookup",
334
+ /** Restrict a share: set/clear its expiry (PATCH /api/share/{token}). */
335
+ shareUpdate: "cloud:shareUpdate",
336
+ /** Revoke a share link (DELETE /api/share/{token}). */
337
+ shareRevoke: "cloud:shareRevoke",
338
+ /** Cloud-synced workspaces (collaborate without git). */
339
+ workspaceList: "cloud:workspaceList",
340
+ workspaceCreate: "cloud:workspaceCreate",
341
+ workspacePull: "cloud:workspacePull",
342
+ workspacePush: "cloud:workspacePush",
343
+ workspaceVersions: "cloud:workspaceVersions",
344
+ workspaceRestore: "cloud:workspaceRestore"
313
345
  }
314
346
  };
315
347
  const api = {
@@ -325,7 +357,15 @@ const api = {
325
357
  offOpenFromGit: () => {
326
358
  electron.ipcRenderer.removeAllListeners(IPC.file.openFromGitDeepLink);
327
359
  },
360
+ onImportShare: (cb) => {
361
+ electron.ipcRenderer.on(IPC.file.importShareDeepLink, (_e, token) => cb(token));
362
+ },
363
+ offImportShare: () => {
364
+ electron.ipcRenderer.removeAllListeners(IPC.file.importShareDeepLink);
365
+ },
328
366
  saveWorkspace: (ws) => electron.ipcRenderer.invoke(IPC.file.saveWorkspace, ws),
367
+ serializeWorkspace: () => electron.ipcRenderer.invoke(IPC.file.serializeWorkspace),
368
+ applyWorkspaceSnapshot: (snap) => electron.ipcRenderer.invoke(IPC.file.applyWorkspaceSnapshot, snap),
329
369
  newWorkspace: () => electron.ipcRenderer.invoke(IPC.file.newWorkspace),
330
370
  closeWorkspace: () => electron.ipcRenderer.invoke(IPC.file.closeWorkspace),
331
371
  loadCollection: (relPath) => electron.ipcRenderer.invoke(IPC.file.loadCollection, relPath),
@@ -418,6 +458,19 @@ const api = {
418
458
  cloudRunFlow: (name) => electron.ipcRenderer.invoke(IPC.cloud.runFlow, name),
419
459
  cloudGetFlowRun: (arg) => electron.ipcRenderer.invoke(IPC.cloud.getFlowRun, arg),
420
460
  cloudOpenFlow: (name) => electron.ipcRenderer.invoke(IPC.cloud.openFlow, name),
461
+ cloudShareCollection: (collection) => electron.ipcRenderer.invoke(IPC.cloud.shareCollection, collection),
462
+ cloudShareRequest: (request) => electron.ipcRenderer.invoke(IPC.cloud.shareRequest, request),
463
+ cloudShareFolder: (arg) => electron.ipcRenderer.invoke(IPC.cloud.shareFolder, arg),
464
+ cloudImportShare: (token) => electron.ipcRenderer.invoke(IPC.cloud.importShare, token),
465
+ cloudShareLookup: (arg) => electron.ipcRenderer.invoke(IPC.cloud.shareLookup, arg),
466
+ cloudShareUpdate: (arg) => electron.ipcRenderer.invoke(IPC.cloud.shareUpdate, arg),
467
+ cloudShareRevoke: (token) => electron.ipcRenderer.invoke(IPC.cloud.shareRevoke, token),
468
+ cloudWorkspaceList: () => electron.ipcRenderer.invoke(IPC.cloud.workspaceList),
469
+ cloudWorkspaceCreate: (arg) => electron.ipcRenderer.invoke(IPC.cloud.workspaceCreate, arg),
470
+ cloudWorkspacePull: (id) => electron.ipcRenderer.invoke(IPC.cloud.workspacePull, id),
471
+ cloudWorkspacePush: (arg) => electron.ipcRenderer.invoke(IPC.cloud.workspacePush, arg),
472
+ cloudWorkspaceVersions: (id) => electron.ipcRenderer.invoke(IPC.cloud.workspaceVersions, id),
473
+ cloudWorkspaceRestore: (arg) => electron.ipcRenderer.invoke(IPC.cloud.workspaceRestore, arg),
421
474
  // ─── Mock servers ─────────────────────────────────────────────────────────────
422
475
  mockStart: (server) => electron.ipcRenderer.invoke(IPC.mock.start, server),
423
476
  mockStop: (id) => electron.ipcRenderer.invoke(IPC.mock.stop, id),
@@ -566,6 +619,10 @@ const api = {
566
619
  gitBranches: () => electron.ipcRenderer.invoke(IPC.git.branches),
567
620
  gitCheckout: (branch, create) => electron.ipcRenderer.invoke(IPC.git.checkout, branch, create),
568
621
  gitDeleteBranch: (name, force = false) => electron.ipcRenderer.invoke(IPC.git.deleteBranch, name, force),
622
+ gitStashList: () => electron.ipcRenderer.invoke(IPC.git.stashList),
623
+ gitStash: (message) => electron.ipcRenderer.invoke(IPC.git.stash, message),
624
+ gitStashPop: (ref) => electron.ipcRenderer.invoke(IPC.git.stashPop, ref),
625
+ gitStashDrop: (ref) => electron.ipcRenderer.invoke(IPC.git.stashDrop, ref),
569
626
  gitFetch: () => electron.ipcRenderer.invoke(IPC.git.fetch),
570
627
  gitPull: (rebase = false) => electron.ipcRenderer.invoke(IPC.git.pull, rebase),
571
628
  gitPush: (setUpstream) => electron.ipcRenderer.invoke(IPC.git.push, setUpstream),
@@ -1,5 +1,5 @@
1
1
  import { l as linter, E as EditorView, L as LanguageSupport, i as LRLanguage, s as styleTags, m as indentNodeProp, n as delimitedIndent, p as foldNodeProp, q as foldInside, S as StateField, u as LRParser, v as StateEffect, w as tags, a as reactExports, j as jsxRuntimeExports, e as ReactCodeMirror, o as oneDark, d as json } from "./codemirror-BKmwMUlV.js";
2
- import { u as useT, a as useStore, b as useActiveEnvironment, i as isSubscriptionQuery } from "./index-BV1FsSQR.js";
2
+ import { u as useT, a as useStore, b as useActiveEnvironment, i as isSubscriptionQuery } from "./index-C6_D_4og.js";
3
3
  import "./ajv-CJPwFjiP.js";
4
4
  function devAssert(condition, message) {
5
5
  const booleanCondition = Boolean(condition);
@@ -1,5 +1,5 @@
1
1
  import { a as reactExports, j as jsxRuntimeExports, e as ReactCodeMirror, o as oneDark, x as xml } from "./codemirror-BKmwMUlV.js";
2
- import { u as useT, c as btnPrimaryCls, d as commentKeymap } from "./index-BV1FsSQR.js";
2
+ import { u as useT, c as btnPrimaryCls, d as commentKeymap } from "./index-C6_D_4og.js";
3
3
  import "./ajv-CJPwFjiP.js";
4
4
  const SOAP_11_CONTENT_TYPE = "text/xml; charset=utf-8";
5
5
  const SOAP_12_CONTENT_TYPE = "application/soap+xml; charset=utf-8";