@testsmith/api-spector 0.7.2 → 0.7.3

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.
package/out/main/index.js CHANGED
@@ -22,28 +22,29 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
  const electron = require("electron");
25
- const handle = require("./chunks/handle-DDZ37J8G.js");
25
+ const handle = require("./chunks/handle-BY_jaVBT.js");
26
26
  const path = require("node:path");
27
27
  const fs = require("node:fs");
28
28
  const promises = require("node:fs/promises");
29
29
  const crypto = require("node:crypto");
30
30
  const JSZip = require("jszip");
31
31
  const dataFiles = require("./chunks/data-files-BYqhsoPe.js");
32
- const requestExec = require("./chunks/request-exec-DLBjLZe1.js");
32
+ const requestExec = require("./chunks/request-exec-CcLHtkwG.js");
33
33
  const ipcValidate = require("./chunks/ipc-validate-sqUFqRmE.js");
34
34
  const uuid = require("uuid");
35
35
  const jsYaml = require("js-yaml");
36
36
  const undici = require("undici");
37
37
  const requestCollection = require("./chunks/request-collection-KGPuvN7o.js");
38
+ const flowEngine = require("./chunks/flow-engine-DuO1iX7c.js");
38
39
  const mockServer = require("./chunks/mock-server-C4kTFkod.js");
39
40
  const http = require("node:http");
40
41
  const WebSocket = require("ws");
41
42
  const os = require("node:os");
42
43
  const grpc = require("@grpc/grpc-js");
43
44
  const protoLoader = require("@grpc/proto-loader");
44
- const soapHandler = require("./chunks/soap-handler-C_LCcPwE.js");
45
+ const soapHandler = require("./chunks/soap-handler-CkoLJHoI.js");
45
46
  const escape = require("./chunks/escape-DDVAlOvZ.js");
46
- const brokerClient = require("./chunks/broker-client-BSTNMPtT.js");
47
+ const brokerClient = require("./chunks/broker-client-VwFF9sbp.js");
47
48
  const simpleGit = require("simple-git");
48
49
  const recorder = require("./chunks/recorder-BkO8G_mB.js");
49
50
  const electronUpdater = require("electron-updater");
@@ -164,6 +165,7 @@ function readmeContents(workspaceFileName) {
164
165
  `environments/ ← per-env variable files (dev, staging, prod, …)`,
165
166
  `mocks/ ← saved mock servers (optional)`,
166
167
  `contracts/ ← pinned OpenAPI snapshots (optional)`,
168
+ `flows/ ← visual flows chaining saved requests (optional)`,
167
169
  `.gitignore ← excludes secrets, generated docs, run reports`,
168
170
  `.vscode/settings.json ← maps *.spector to JSON for editor highlighting`,
169
171
  "```",
@@ -310,6 +312,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
310
312
  await requestExec.loadGlobals(workspaceDir);
311
313
  await promises.mkdir(path.join(workspaceDir, "collections"), { recursive: true });
312
314
  await promises.mkdir(path.join(workspaceDir, "environments"), { recursive: true });
315
+ await promises.mkdir(path.join(workspaceDir, "flows"), { recursive: true });
313
316
  await ensureGitignore(workspaceDir);
314
317
  await ensureVscodeFileAssociation(workspaceDir);
315
318
  await ensureReadme(workspaceDir, path.basename(result.filePath));
@@ -353,6 +356,17 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
353
356
  await promises.mkdir(path.dirname(fullPath), { recursive: true });
354
357
  await atomicWrite(fullPath, JSON.stringify(env, null, 2));
355
358
  });
359
+ handle.handleIpc(ipc, handle.IPC.file.loadFlow, async (_e, relPath) => {
360
+ if (!workspaceDir) throw new Error("No workspace open");
361
+ const raw = await promises.readFile(path.resolve(workspaceDir, relPath), "utf8");
362
+ return JSON.parse(raw);
363
+ });
364
+ handle.handleIpc(ipc, handle.IPC.file.saveFlow, async (_e, relPath, flow) => {
365
+ if (!workspaceDir) throw new Error("No workspace open");
366
+ const fullPath = path.resolve(workspaceDir, relPath);
367
+ await promises.mkdir(path.dirname(fullPath), { recursive: true });
368
+ await atomicWrite(fullPath, JSON.stringify(flow, null, 2));
369
+ });
356
370
  handle.handleIpc(ipc, handle.IPC.file.deleteWorkspaceFile, async (_e, relPath) => {
357
371
  if (!workspaceDir) throw new Error("No workspace open");
358
372
  const fullPath = path.resolve(workspaceDir, relPath);
@@ -4684,6 +4698,107 @@ function registerRunnerHandler(ipc) {
4684
4698
  return summary;
4685
4699
  });
4686
4700
  }
4701
+ function registerFlowHandler(ipc) {
4702
+ handle.handleIpc(ipc, handle.IPC.flow.run, async (event, payload) => {
4703
+ const { collections, environment, globals: payloadGlobals, proxy, tls, piiMaskPatterns = [] } = payload;
4704
+ const flow = flowEngine.normalizeFlow(payload.flow);
4705
+ const flows = (payload.flows ?? []).map(flowEngine.normalizeFlow);
4706
+ const envVars = await requestExec.buildEnvVars(environment);
4707
+ const globals = { ...payloadGlobals, ...requestExec.getGlobals() };
4708
+ const dispatcher = await requestExec.buildDispatcher(proxy, tls);
4709
+ const colById = new Map(collections.map((c) => [c.id, c]));
4710
+ const deps = {
4711
+ runRequest: async (ref, input) => {
4712
+ const col = colById.get(ref.collectionId);
4713
+ const original = col?.requests[ref.requestId];
4714
+ if (!col || !original) return null;
4715
+ const req = JSON.parse(JSON.stringify(original));
4716
+ const inherited = requestCollection.resolveInheritedAuthAndHeaders(original.id, col);
4717
+ if (!requestCollection.authIsConfigured(req.auth) && inherited.auth && inherited.auth.type !== "none") {
4718
+ req.auth = inherited.auth;
4719
+ }
4720
+ const inheritedHeaders = inherited.headers.filter((h) => h.enabled && h.key);
4721
+ if (inheritedHeaders.length) req.headers = [...inheritedHeaders, ...req.headers ?? []];
4722
+ const { result, updatedEnvVars, updatedCollectionVars, updatedGlobals, updatedLocalVars } = await requestExec.executeRunnerRequest({
4723
+ req,
4724
+ collectionVars: { ...col.collectionVariables ?? {}, ...input.collectionVars },
4725
+ envVars: input.envVars,
4726
+ globals: input.globals,
4727
+ localVars: input.localVars,
4728
+ dispatcher,
4729
+ // Flows chain on the real response (e.g. extracting an access_token
4730
+ // the next block sends as a Bearer), so the body must NOT be PII-
4731
+ // masked here — masking would feed downstream blocks a sentinel.
4732
+ piiMaskPatterns: [],
4733
+ proxy,
4734
+ tls
4735
+ });
4736
+ const rr = result.receivedResponse;
4737
+ const response = rr ? { status: rr.status, statusText: rr.statusText, headers: rr.headers, body: rr.body, bodySize: rr.body?.length ?? 0, durationMs: result.durationMs ?? 0 } : null;
4738
+ return {
4739
+ envVars: updatedEnvVars,
4740
+ collectionVars: updatedCollectionVars,
4741
+ globals: updatedGlobals,
4742
+ localVars: updatedLocalVars,
4743
+ response,
4744
+ status: result.status,
4745
+ httpStatus: result.httpStatus,
4746
+ durationMs: result.durationMs,
4747
+ error: result.error,
4748
+ testResults: result.testResults,
4749
+ name: result.name,
4750
+ method: result.method,
4751
+ resolvedUrl: result.resolvedUrl
4752
+ };
4753
+ },
4754
+ runScript: async (code, input) => {
4755
+ const out = await requestExec.runScript(code, {
4756
+ envVars: input.envVars,
4757
+ collectionVars: input.collectionVars,
4758
+ globals: input.globals,
4759
+ localVars: input.localVars,
4760
+ response: input.response ?? void 0,
4761
+ piiMaskPatterns,
4762
+ data: input.data
4763
+ });
4764
+ return {
4765
+ envVars: out.updatedEnvVars,
4766
+ collectionVars: out.updatedCollectionVars,
4767
+ globals: out.updatedGlobals,
4768
+ localVars: out.updatedLocalVars,
4769
+ testResults: out.testResults,
4770
+ consoleOutput: out.consoleOutput,
4771
+ error: out.error
4772
+ };
4773
+ },
4774
+ evalExpression: async (code, input) => {
4775
+ const out = await requestExec.evaluateExpression(code, {
4776
+ envVars: input.envVars,
4777
+ collectionVars: input.collectionVars,
4778
+ globals: input.globals,
4779
+ localVars: input.localVars,
4780
+ response: input.response ?? void 0,
4781
+ piiMaskPatterns,
4782
+ data: input.data
4783
+ });
4784
+ return {
4785
+ envVars: out.updatedEnvVars,
4786
+ collectionVars: out.updatedCollectionVars,
4787
+ globals: out.updatedGlobals,
4788
+ localVars: out.updatedLocalVars,
4789
+ value: out.value,
4790
+ consoleOutput: out.consoleOutput,
4791
+ error: out.error
4792
+ };
4793
+ },
4794
+ resolveFlow: (flowId) => flows.find((f) => f.id === flowId) ?? null,
4795
+ sleep: (ms) => new Promise((resolve2) => setTimeout(resolve2, ms)),
4796
+ interpolate: (str, vars) => requestExec.interpolate(str, vars),
4797
+ onEvent: (evt) => event.sender.send(handle.IPC.flow.progress, evt)
4798
+ };
4799
+ return flowEngine.runFlow(flow, deps, { envVars, globals });
4800
+ });
4801
+ }
4687
4802
  function registerMockHandlers(ipc) {
4688
4803
  handle.handleIpc(ipc, handle.IPC.mock.start, async (e, server) => {
4689
4804
  mockServer.setHitCallback((hit) => e.sender.send(handle.IPC.mock.hit, hit));
@@ -5741,6 +5856,29 @@ function registerRecordHandlers(ipc, getWebContents) {
5741
5856
  return recorder.entriesToMockServer(entries, upstream, name, port);
5742
5857
  });
5743
5858
  }
5859
+ async function buildFlowDefinition(input) {
5860
+ const envVars = await requestExec.buildEnvVars(input.environment);
5861
+ const colById = new Map(input.collections.map((c) => [c.id, c]));
5862
+ const collectionVars = {};
5863
+ for (const c of input.collections) Object.assign(collectionVars, c.collectionVariables ?? {});
5864
+ const requests = {};
5865
+ for (const f of [input.flow, ...input.flows ?? []]) {
5866
+ for (const node of f.nodes ?? []) {
5867
+ const ref = node.config?.ref;
5868
+ if (node.type !== "request" || !ref) continue;
5869
+ const col = colById.get(ref.collectionId);
5870
+ const req = col?.requests[ref.requestId];
5871
+ if (!col || !req) continue;
5872
+ const cloned = JSON.parse(JSON.stringify(req));
5873
+ const inherited = requestCollection.resolveInheritedAuthAndHeaders(req.id, col);
5874
+ if (!requestCollection.authIsConfigured(cloned.auth) && inherited.auth && inherited.auth.type !== "none") cloned.auth = inherited.auth;
5875
+ const inhHeaders = inherited.headers.filter((h) => h.enabled && h.key);
5876
+ if (inhHeaders.length) cloned.headers = [...inhHeaders, ...cloned.headers ?? []];
5877
+ requests[`${ref.collectionId}:${ref.requestId}`] = cloned;
5878
+ }
5879
+ }
5880
+ return { flow: input.flow, flows: input.flows ?? [], requests, envVars, collectionVars, globals: input.globals ?? {} };
5881
+ }
5744
5882
  const CLOUD_TOKEN_REF = "cloud:token";
5745
5883
  const CLOUD_ENDPOINT = brokerClient.resolveCloudEndpoint();
5746
5884
  async function cloudFetch(path2, method, body) {
@@ -5912,6 +6050,23 @@ function registerCloudHandlers(ipc) {
5912
6050
  handle.handleIpc(ipc, handle.IPC.cloud.openMatrix, async () => {
5913
6051
  await electron.shell.openExternal(CLOUD_ENDPOINT + "/matrix");
5914
6052
  });
6053
+ handle.handleIpc(ipc, handle.IPC.cloud.pushFlow, async (_e, input) => {
6054
+ const definition = await buildFlowDefinition(input);
6055
+ return cloudFetch("/api/flows", "POST", {
6056
+ name: input.flow.name,
6057
+ slug: slugify(input.flow.name),
6058
+ definition
6059
+ });
6060
+ });
6061
+ handle.handleIpc(ipc, handle.IPC.cloud.runFlow, async (_e, name) => {
6062
+ return cloudFetch("/api/flows/" + encodeURIComponent(slugify(name)) + "/run", "POST");
6063
+ });
6064
+ handle.handleIpc(ipc, handle.IPC.cloud.getFlowRun, async (_e, arg) => {
6065
+ return cloudFetch(`/api/flows/${encodeURIComponent(slugify(arg.name))}/runs/${encodeURIComponent(String(arg.runId))}`, "GET");
6066
+ });
6067
+ handle.handleIpc(ipc, handle.IPC.cloud.openFlow, async (_e, name) => {
6068
+ await electron.shell.openExternal(CLOUD_ENDPOINT + "/flows/" + encodeURIComponent(slugify(name)));
6069
+ });
5915
6070
  handle.handleIpc(ipc, handle.IPC.cloud.pushMonitor, async (_e, input) => {
5916
6071
  const { request, environment, collectionVars, globals } = input;
5917
6072
  const envVars = await requestExec.buildEnvVars(environment);
@@ -5943,7 +6098,7 @@ function isNewer(a, b) {
5943
6098
  return a2 > b2;
5944
6099
  }
5945
6100
  async function checkForUpdate() {
5946
- const current = "0.7.2";
6101
+ const current = "0.7.3";
5947
6102
  try {
5948
6103
  const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(4e3) });
5949
6104
  if (!res.ok) return null;
@@ -6156,6 +6311,7 @@ electron.app.whenReady().then(async () => {
6156
6311
  registerImportHandlers(electron.ipcMain);
6157
6312
  registerGenerateHandlers(electron.ipcMain);
6158
6313
  registerRunnerHandler(electron.ipcMain);
6314
+ registerFlowHandler(electron.ipcMain);
6159
6315
  registerMockHandlers(electron.ipcMain);
6160
6316
  registerOAuth2Handlers(electron.ipcMain);
6161
6317
  registerVaultHandlers(electron.ipcMain);
package/out/main/lib.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const mockServer = require("./chunks/mock-server-C4kTFkod.js");
4
- const requestExec = require("./chunks/request-exec-DLBjLZe1.js");
4
+ const requestExec = require("./chunks/request-exec-CcLHtkwG.js");
5
+ const flowEngine = require("./chunks/flow-engine-DuO1iX7c.js");
5
6
  require("node:http");
6
7
  require("node:crypto");
7
8
  require("node:vm");
@@ -10,7 +11,7 @@ require("@xmldom/xmldom");
10
11
  require("undici");
11
12
  require("fs/promises");
12
13
  require("tls");
13
- require("./chunks/handle-DDZ37J8G.js");
14
+ require("./chunks/handle-BY_jaVBT.js");
14
15
  require("node:fs");
15
16
  require("node:os");
16
17
  require("node:path");
@@ -18,6 +19,60 @@ require("node:fs/promises");
18
19
  require("tv4");
19
20
  require("jsonpath-plus");
20
21
  require("ajv");
22
+ const refKey = (ref) => `${ref.collectionId}:${ref.requestId}`;
23
+ async function runFlowHeadless(input) {
24
+ const flow = flowEngine.normalizeFlow(input.flow);
25
+ const flows = (input.flows ?? []).map(flowEngine.normalizeFlow);
26
+ const requests = input.requests ?? {};
27
+ const dispatcher = await requestExec.buildDispatcher(input.proxy, input.tls);
28
+ const deps = {
29
+ runRequest: async (ref, i) => {
30
+ const original = requests[refKey(ref)];
31
+ if (!original) return null;
32
+ const req = JSON.parse(JSON.stringify(original));
33
+ const { result, updatedEnvVars, updatedCollectionVars, updatedGlobals, updatedLocalVars } = await requestExec.executeRunnerRequest({
34
+ req,
35
+ collectionVars: i.collectionVars,
36
+ envVars: i.envVars,
37
+ globals: i.globals,
38
+ localVars: i.localVars,
39
+ dispatcher,
40
+ piiMaskPatterns: [],
41
+ proxy: input.proxy,
42
+ tls: input.tls
43
+ });
44
+ const rr = result.receivedResponse;
45
+ const response = rr ? { status: rr.status, statusText: rr.statusText, headers: rr.headers, body: rr.body, bodySize: rr.body?.length ?? 0, durationMs: result.durationMs ?? 0 } : null;
46
+ return {
47
+ envVars: updatedEnvVars,
48
+ collectionVars: updatedCollectionVars,
49
+ globals: updatedGlobals,
50
+ localVars: updatedLocalVars,
51
+ response,
52
+ status: result.status,
53
+ httpStatus: result.httpStatus,
54
+ durationMs: result.durationMs,
55
+ error: result.error,
56
+ testResults: result.testResults,
57
+ name: result.name,
58
+ method: result.method,
59
+ resolvedUrl: result.resolvedUrl
60
+ };
61
+ },
62
+ runScript: async (code, i) => {
63
+ const o = await requestExec.runScript(code, { envVars: i.envVars, collectionVars: i.collectionVars, globals: i.globals, localVars: i.localVars, response: i.response ?? void 0, data: i.data, piiMaskPatterns: [] });
64
+ return { envVars: o.updatedEnvVars, collectionVars: o.updatedCollectionVars, globals: o.updatedGlobals, localVars: o.updatedLocalVars, testResults: o.testResults, consoleOutput: o.consoleOutput, error: o.error };
65
+ },
66
+ evalExpression: async (code, i) => {
67
+ const o = await requestExec.evaluateExpression(code, { envVars: i.envVars, collectionVars: i.collectionVars, globals: i.globals, localVars: i.localVars, response: i.response ?? void 0, data: i.data, piiMaskPatterns: [] });
68
+ return { envVars: o.updatedEnvVars, collectionVars: o.updatedCollectionVars, globals: o.updatedGlobals, localVars: o.updatedLocalVars, value: o.value, consoleOutput: o.consoleOutput, error: o.error };
69
+ },
70
+ resolveFlow: (id) => flows.find((f) => f.id === id) ?? null,
71
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
72
+ onEvent: input.onEvent
73
+ };
74
+ return flowEngine.runFlow(flow, deps, { envVars: input.envVars, collectionVars: input.collectionVars, globals: input.globals });
75
+ }
21
76
  exports.findRoute = mockServer.findRoute;
22
77
  exports.getRunningIds = mockServer.getRunningIds;
23
78
  exports.handleRequest = mockServer.handleRequest;
@@ -43,3 +98,7 @@ exports.registeredSchemes = requestExec.registeredSchemes;
43
98
  exports.resolveExternalSecret = requestExec.resolveExternalSecret;
44
99
  exports.setSecretsConfig = requestExec.setSecretsConfig;
45
100
  exports.syntheticHttpFailure = requestExec.syntheticHttpFailure;
101
+ exports.BLOCK_SPECS = flowEngine.BLOCK_SPECS;
102
+ exports.normalizeFlow = flowEngine.normalizeFlow;
103
+ exports.runFlow = flowEngine.runFlow;
104
+ exports.runFlowHeadless = runFlowHeadless;
package/out/main/mock.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  const mockServer = require("./chunks/mock-server-C4kTFkod.js");
4
- const cliCommon = require("./chunks/cli-common-Cq_cphpZ.js");
4
+ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
5
5
  require("node:http");
6
6
  require("node:crypto");
7
7
  require("node:vm");
@@ -3,7 +3,7 @@
3
3
  const promises = require("node:fs/promises");
4
4
  const path = require("node:path");
5
5
  const recorder = require("./chunks/recorder-BkO8G_mB.js");
6
- const cliCommon = require("./chunks/cli-common-Cq_cphpZ.js");
6
+ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
7
7
  require("node:http");
8
8
  require("node:crypto");
9
9
  require("undici");
@@ -2,15 +2,15 @@
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-DLBjLZe1.js");
5
+ const requestExec = require("./chunks/request-exec-CcLHtkwG.js");
6
6
  const escape = require("./chunks/escape-DDVAlOvZ.js");
7
7
  const requestCollection = require("./chunks/request-collection-KGPuvN7o.js");
8
8
  const environments = require("./chunks/environments-iM3SUM-4.js");
9
- const cliCommon = require("./chunks/cli-common-Cq_cphpZ.js");
9
+ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
10
10
  require("undici");
11
11
  require("fs/promises");
12
12
  require("tls");
13
- require("./chunks/handle-DDZ37J8G.js");
13
+ require("./chunks/handle-BY_jaVBT.js");
14
14
  require("node:fs");
15
15
  require("node:os");
16
16
  require("node:crypto");
@@ -495,7 +495,7 @@ async function main() {
495
495
  } else if (!envName && workspace.settings?.defaultEnvironment && !env) {
496
496
  console.warn(cliCommon.color(`Warning: default environment "${workspace.settings.defaultEnvironment}" not found. Running without environment.`, cliCommon.C.yellow));
497
497
  }
498
- const version = `v${"0.7.2"}`;
498
+ const version = `v${"0.7.3"}`;
499
499
  console.log("");
500
500
  console.log(cliCommon.color(" API Test Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
501
501
  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-C_LCcPwE.js");
8
- const _import = require("./chunks/import-CBAklepT.js");
9
- const cliCommon = require("./chunks/cli-common-Cq_cphpZ.js");
10
- require("./chunks/handle-DDZ37J8G.js");
7
+ const soapHandler = require("./chunks/soap-handler-CkoLJHoI.js");
8
+ const _import = require("./chunks/import-DR_r1DUP.js");
9
+ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
10
+ require("./chunks/handle-BY_jaVBT.js");
11
11
  require("@xmldom/xmldom");
12
12
  require("uuid");
13
13
  require("./chunks/data-files-BYqhsoPe.js");
@@ -21,6 +21,8 @@ const IPC = {
21
21
  deleteWorkspaceFile: "file:deleteWorkspaceFile",
22
22
  saveMock: "file:saveMock",
23
23
  loadMock: "file:loadMock",
24
+ saveFlow: "file:saveFlow",
25
+ loadFlow: "file:loadFlow",
24
26
  loadHistory: "file:loadHistory",
25
27
  saveHistory: "file:saveHistory"
26
28
  },
@@ -59,6 +61,12 @@ const IPC = {
59
61
  /** Event: per-request progress pushed from main during a run. */
60
62
  progress: "runner:progress"
61
63
  },
64
+ // ─── Flow runner ───────────────────────────────────────────────────────────
65
+ flow: {
66
+ run: "flow:run",
67
+ /** Event: per-block progress pushed from main during a flow run. */
68
+ progress: "flow:progress"
69
+ },
62
70
  // ─── Run results export ────────────────────────────────────────────────────
63
71
  results: {
64
72
  save: "results:save"
@@ -227,6 +235,14 @@ const IPC = {
227
235
  pushDesignContract: "cloud:pushDesignContract",
228
236
  /** Publish a provider OpenAPI spec (PUT /api/provider-contracts). */
229
237
  pushSpec: "cloud:pushSpec",
238
+ /** Upload a self-contained flow definition (POST /api/flows). */
239
+ pushFlow: "cloud:pushFlow",
240
+ /** Trigger a server-side run of an uploaded flow (POST /api/flows/{slug}/run). */
241
+ runFlow: "cloud:runFlow",
242
+ /** Poll a flow run's status/result (GET /api/flows/{slug}/runs/{id}). */
243
+ getFlowRun: "cloud:getFlowRun",
244
+ /** Open a flow's page in the cloud UI. */
245
+ openFlow: "cloud:openFlow",
230
246
  /** Open the cloud deployment matrix in the browser. */
231
247
  openMatrix: "cloud:openMatrix"
232
248
  }
@@ -282,6 +298,14 @@ const api = {
282
298
  electron.ipcRenderer.removeAllListeners(IPC.runner.progress);
283
299
  },
284
300
  saveResults: (content, defaultName) => electron.ipcRenderer.invoke(IPC.results.save, content, defaultName),
301
+ // ─── Flow runner ──────────────────────────────────────────────────────────
302
+ runFlow: (payload) => electron.ipcRenderer.invoke(IPC.flow.run, payload),
303
+ onFlowProgress: (cb) => {
304
+ electron.ipcRenderer.on(IPC.flow.progress, (_e, evt) => cb(evt));
305
+ },
306
+ offFlowProgress: () => {
307
+ electron.ipcRenderer.removeAllListeners(IPC.flow.progress);
308
+ },
285
309
  // ─── Import ────────────────────────────────────────────────────────────────
286
310
  importPostman: () => electron.ipcRenderer.invoke(IPC.import.postman),
287
311
  importOpenApi: () => electron.ipcRenderer.invoke(IPC.import.openapi),
@@ -313,6 +337,10 @@ const api = {
313
337
  cloudPushDesignContract: (input) => electron.ipcRenderer.invoke(IPC.cloud.pushDesignContract, input),
314
338
  cloudPushSpec: (input) => electron.ipcRenderer.invoke(IPC.cloud.pushSpec, input),
315
339
  cloudOpenMatrix: () => electron.ipcRenderer.invoke(IPC.cloud.openMatrix),
340
+ cloudPushFlow: (input) => electron.ipcRenderer.invoke(IPC.cloud.pushFlow, input),
341
+ cloudRunFlow: (name) => electron.ipcRenderer.invoke(IPC.cloud.runFlow, name),
342
+ cloudGetFlowRun: (arg) => electron.ipcRenderer.invoke(IPC.cloud.getFlowRun, arg),
343
+ cloudOpenFlow: (name) => electron.ipcRenderer.invoke(IPC.cloud.openFlow, name),
316
344
  // ─── Mock servers ─────────────────────────────────────────────────────────────
317
345
  mockStart: (server) => electron.ipcRenderer.invoke(IPC.mock.start, server),
318
346
  mockStop: (id) => electron.ipcRenderer.invoke(IPC.mock.stop, id),
@@ -320,6 +348,8 @@ const api = {
320
348
  mockRunningIds: () => electron.ipcRenderer.invoke(IPC.mock.runningIds),
321
349
  saveMock: (relPath, server) => electron.ipcRenderer.invoke(IPC.file.saveMock, relPath, server),
322
350
  loadMock: (relPath) => electron.ipcRenderer.invoke(IPC.file.loadMock, relPath),
351
+ saveFlow: (relPath, flow) => electron.ipcRenderer.invoke(IPC.file.saveFlow, relPath, flow),
352
+ loadFlow: (relPath) => electron.ipcRenderer.invoke(IPC.file.loadFlow, relPath),
323
353
  mockUpdateRoutes: (id, routes) => electron.ipcRenderer.invoke(IPC.mock.updateRoutes, id, routes),
324
354
  onMockHit: (cb) => {
325
355
  electron.ipcRenderer.on(IPC.mock.hit, (_e, hit) => cb(hit));
@@ -1,5 +1,5 @@
1
1
  import { l as linter, E as EditorView, L as LanguageSupport, e as LRLanguage, s as styleTags, i as indentNodeProp, f as delimitedIndent, m as foldNodeProp, n as foldInside, S as StateField, p as LRParser, q as StateEffect, u as tags, r as reactExports, j as jsxRuntimeExports, c as ReactCodeMirror, o as oneDark, b as json } from "./codemirror-F6m3Hm1M.js";
2
- import { u as useT, a as useStore, b as useActiveEnvironment } from "./index-LLqTi-5a.js";
2
+ import { u as useT, a as useStore, b as useActiveEnvironment } from "./index-aKbixApk.js";
3
3
  import "./ajv-2MAg6kGF.js";
4
4
  function devAssert(condition, message) {
5
5
  const booleanCondition = Boolean(condition);
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, c as ReactCodeMirror, o as oneDark, x as xml } from "./codemirror-F6m3Hm1M.js";
2
- import { u as useT, c as btnPrimaryCls, d as commentKeymap } from "./index-LLqTi-5a.js";
2
+ import { u as useT, c as btnPrimaryCls, d as commentKeymap } from "./index-aKbixApk.js";
3
3
  import "./ajv-2MAg6kGF.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";