@testsmith/api-spector 0.2.2 → 0.2.4
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/bin/cli.js +42 -33
- package/out/main/chunks/auth-builder-B7-LgcGr.js +373 -0
- package/out/main/chunks/import-C9qdkBCH.js +154 -0
- package/out/main/chunks/ipc-validate-CscN4HfG.js +94 -0
- package/out/main/chunks/{mock-server-Cx-xG4pJ.js → mock-server-DmdvwCgj.js} +4 -0
- package/out/main/chunks/{request-collection-Dx0ZqB54.js → request-collection-DIsjTggj.js} +25 -349
- package/out/main/chunks/snapshots-C7YbGHM7.js +588 -0
- package/out/main/chunks/soap-handler-Cpj-JwyA.js +326 -0
- package/out/main/contract.js +210 -0
- package/out/main/index.js +398 -592
- package/out/main/mock.js +1 -1
- package/out/main/runner.js +20 -18
- package/out/main/wsdl.js +174 -0
- package/out/preload/index.js +12 -0
- package/out/renderer/assets/{index-C_vjoxxA.js → index-BC1srylp.js} +1374 -647
- package/out/renderer/assets/index-DfkLUeA1.css +2 -0
- package/out/renderer/index.html +2 -2
- package/package.json +6 -1
- package/out/renderer/assets/index-DVaubmCJ.css +0 -2
package/out/main/mock.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const mockServer = require("./chunks/mock-server-
|
|
5
|
+
const mockServer = require("./chunks/mock-server-DmdvwCgj.js");
|
|
6
6
|
require("http");
|
|
7
7
|
require("crypto");
|
|
8
8
|
require("vm");
|
package/out/main/runner.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const undici = require("undici");
|
|
6
|
-
const
|
|
6
|
+
const authBuilder = require("./chunks/auth-builder-B7-LgcGr.js");
|
|
7
|
+
const requestCollection = require("./chunks/request-collection-DIsjTggj.js");
|
|
7
8
|
require("crypto");
|
|
8
9
|
require("dayjs");
|
|
9
10
|
require("vm");
|
|
@@ -11,6 +12,7 @@ require("tv4");
|
|
|
11
12
|
require("jsonpath-plus");
|
|
12
13
|
require("@xmldom/xmldom");
|
|
13
14
|
require("ajv");
|
|
15
|
+
require("./chunks/ipc-validate-CscN4HfG.js");
|
|
14
16
|
function buildJsonReport(results, summary, meta = {}) {
|
|
15
17
|
return JSON.stringify({
|
|
16
18
|
timestamp: meta.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -266,7 +268,7 @@ function buildHtmlReport(results, summary, meta = {}) {
|
|
|
266
268
|
const html = document.documentElement
|
|
267
269
|
const isLight = html.classList.toggle('light')
|
|
268
270
|
document.getElementById('themeBtn').textContent = isLight ? '🌙' : '☀️'
|
|
269
|
-
try { localStorage.setItem('theme', isLight ? 'light' : 'dark') } catch {}
|
|
271
|
+
try { localStorage.setItem('theme', isLight ? 'light' : 'dark') } catch {} /* private mode / quota — non-fatal */
|
|
270
272
|
}
|
|
271
273
|
// Restore saved preference or respect OS preference
|
|
272
274
|
(function() {
|
|
@@ -276,7 +278,7 @@ function buildHtmlReport(results, summary, meta = {}) {
|
|
|
276
278
|
document.documentElement.classList.add('light')
|
|
277
279
|
document.getElementById('themeBtn').textContent = '🌙'
|
|
278
280
|
}
|
|
279
|
-
} catch {}
|
|
281
|
+
} catch {} /* private mode / quota — non-fatal */
|
|
280
282
|
})()
|
|
281
283
|
<\/script>
|
|
282
284
|
</body>
|
|
@@ -417,10 +419,10 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
417
419
|
let updatedCollectionVars = { ...collectionVars };
|
|
418
420
|
let updatedGlobals = { ...globals };
|
|
419
421
|
let preScriptError;
|
|
420
|
-
const dynamicVars = await
|
|
421
|
-
let vars =
|
|
422
|
+
const dynamicVars = await authBuilder.buildDynamicVars();
|
|
423
|
+
let vars = authBuilder.mergeVars(envVars, collectionVars, globals, localVars, dynamicVars);
|
|
422
424
|
if (req.preRequestScript?.trim()) {
|
|
423
|
-
const r = await requestCollection.runScript(
|
|
425
|
+
const r = await requestCollection.runScript(authBuilder.interpolate(req.preRequestScript, vars), {
|
|
424
426
|
envVars: { ...envVars },
|
|
425
427
|
collectionVars: { ...collectionVars },
|
|
426
428
|
globals: { ...globals },
|
|
@@ -436,36 +438,36 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
436
438
|
if (verbose && r.consoleOutput.length) r.consoleOutput.forEach((l) => console.log(color(` [pre] ${l}`, C.gray)));
|
|
437
439
|
if (r.error) console.error(color(` [pre-script error] ${r.error}`, C.red));
|
|
438
440
|
}
|
|
439
|
-
vars =
|
|
440
|
-
const resolvedUrl =
|
|
441
|
+
vars = authBuilder.mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
|
|
442
|
+
const resolvedUrl = authBuilder.buildUrl(req.url, req.params, vars);
|
|
441
443
|
base.resolvedUrl = resolvedUrl;
|
|
442
444
|
const start = Date.now();
|
|
443
445
|
try {
|
|
444
446
|
const authH = {};
|
|
445
447
|
if (req.auth.type === "bearer") {
|
|
446
448
|
let token = req.auth.token ?? "";
|
|
447
|
-
if (!token && req.auth.tokenSecretRef) token = await
|
|
448
|
-
if (token) authH["Authorization"] = `Bearer ${
|
|
449
|
+
if (!token && req.auth.tokenSecretRef) token = await authBuilder.getSecret(req.auth.tokenSecretRef) ?? "";
|
|
450
|
+
if (token) authH["Authorization"] = `Bearer ${authBuilder.interpolate(token, vars)}`;
|
|
449
451
|
}
|
|
450
452
|
const headers = new undici.Headers();
|
|
451
453
|
for (const h of req.headers) {
|
|
452
|
-
if (h.enabled && h.key) headers.set(
|
|
454
|
+
if (h.enabled && h.key) headers.set(authBuilder.interpolate(h.key, vars), authBuilder.interpolate(h.value, vars));
|
|
453
455
|
}
|
|
454
456
|
for (const [k, v] of Object.entries(authH)) headers.set(k, v);
|
|
455
457
|
let body;
|
|
456
458
|
if (req.body.mode === "json" && req.body.json) {
|
|
457
|
-
body =
|
|
459
|
+
body = authBuilder.interpolate(req.body.json, vars);
|
|
458
460
|
if (!headers.has("content-type")) headers.set("Content-Type", "application/json");
|
|
459
461
|
} else if (req.body.mode === "raw" && req.body.raw) {
|
|
460
|
-
body =
|
|
462
|
+
body = authBuilder.interpolate(req.body.raw, vars);
|
|
461
463
|
if (!headers.has("content-type")) headers.set("Content-Type", req.body.rawContentType ?? "text/plain");
|
|
462
464
|
} else if (req.body.mode === "graphql" && req.body.graphql) {
|
|
463
465
|
const gql = req.body.graphql;
|
|
464
|
-
const gqlBody = { query:
|
|
466
|
+
const gqlBody = { query: authBuilder.interpolate(gql.query, vars) };
|
|
465
467
|
const rawVars = gql.variables?.trim();
|
|
466
468
|
if (rawVars) {
|
|
467
469
|
try {
|
|
468
|
-
gqlBody.variables = JSON.parse(
|
|
470
|
+
gqlBody.variables = JSON.parse(authBuilder.interpolate(rawVars, vars));
|
|
469
471
|
} catch {
|
|
470
472
|
}
|
|
471
473
|
}
|
|
@@ -498,7 +500,7 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
498
500
|
let consoleOutput = [];
|
|
499
501
|
let postScriptError;
|
|
500
502
|
if (req.postRequestScript?.trim()) {
|
|
501
|
-
const r = await requestCollection.runScript(
|
|
503
|
+
const r = await requestCollection.runScript(authBuilder.interpolate(req.postRequestScript, vars), {
|
|
502
504
|
envVars: updatedEnvVars,
|
|
503
505
|
collectionVars: updatedCollectionVars,
|
|
504
506
|
globals: updatedGlobals,
|
|
@@ -626,7 +628,7 @@ async function main() {
|
|
|
626
628
|
console.log(color(` Environment: ${env?.name ?? "(none)"}`, C.gray));
|
|
627
629
|
if (filterTags.length) console.log(color(` Tags: ${filterTags.join(", ")}`, C.gray));
|
|
628
630
|
console.log("");
|
|
629
|
-
const envVarsSnapshot = await
|
|
631
|
+
const envVarsSnapshot = await authBuilder.buildEnvVars(env);
|
|
630
632
|
const secretValuesToMask = (env?.variables ?? []).filter((v) => v.secret && v.enabled).map((v) => envVarsSnapshot[v.key]).filter((v) => typeof v === "string" && v.length > 0);
|
|
631
633
|
function redact(s) {
|
|
632
634
|
let out = s;
|
|
@@ -656,7 +658,7 @@ async function main() {
|
|
|
656
658
|
const items = requestCollection.collectTagged(col.rootFolder, col.requests, col.collectionVariables ?? {}, filterTags);
|
|
657
659
|
if (items.length === 0) continue;
|
|
658
660
|
if (!firstColName) firstColName = col.name;
|
|
659
|
-
let runEnvVars = await
|
|
661
|
+
let runEnvVars = await authBuilder.buildEnvVars(env);
|
|
660
662
|
let runGlobals = requestCollection.getGlobals();
|
|
661
663
|
let runCollectionVars = { ...col.collectionVariables ?? {} };
|
|
662
664
|
let runLocalVars = {};
|
package/out/main/wsdl.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
const promises = require("fs/promises");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const https = require("https");
|
|
6
|
+
const http = require("http");
|
|
7
|
+
const soapHandler = require("./chunks/soap-handler-Cpj-JwyA.js");
|
|
8
|
+
const _import = require("./chunks/import-C9qdkBCH.js");
|
|
9
|
+
require("@xmldom/xmldom");
|
|
10
|
+
require("uuid");
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const args = {};
|
|
13
|
+
for (let i = 0; i < argv.length; i++) {
|
|
14
|
+
const arg = argv[i];
|
|
15
|
+
if (arg.startsWith("--")) {
|
|
16
|
+
const key = arg.slice(2);
|
|
17
|
+
const next = argv[i + 1];
|
|
18
|
+
if (!next || next.startsWith("--")) {
|
|
19
|
+
args[key] = true;
|
|
20
|
+
} else {
|
|
21
|
+
args[key] = next;
|
|
22
|
+
i++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
28
|
+
function fetchUrl(url) {
|
|
29
|
+
return new Promise((resolveP, rejectP) => {
|
|
30
|
+
const lib = url.startsWith("https") ? https : http;
|
|
31
|
+
const req = lib.get(url, (res) => {
|
|
32
|
+
const chunks = [];
|
|
33
|
+
res.on("data", (c) => chunks.push(c));
|
|
34
|
+
res.on("end", () => resolveP(Buffer.concat(chunks).toString("utf8")));
|
|
35
|
+
res.on("error", rejectP);
|
|
36
|
+
});
|
|
37
|
+
req.on("error", rejectP);
|
|
38
|
+
req.setTimeout(15e3, () => {
|
|
39
|
+
req.destroy();
|
|
40
|
+
rejectP(new Error("WSDL fetch timed out"));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function resolveWorkspacePath(wsPath) {
|
|
45
|
+
const s = await promises.stat(wsPath);
|
|
46
|
+
if (!s.isDirectory()) return wsPath;
|
|
47
|
+
const entries = await promises.readdir(wsPath);
|
|
48
|
+
const spector = entries.find((e) => e.endsWith(".spector"));
|
|
49
|
+
if (!spector) throw new Error(`No .spector workspace file found in directory: ${wsPath}`);
|
|
50
|
+
return path.join(wsPath, spector);
|
|
51
|
+
}
|
|
52
|
+
async function loadWorkspace(wsPath) {
|
|
53
|
+
const resolved = await resolveWorkspacePath(wsPath);
|
|
54
|
+
const raw = await promises.readFile(resolved, "utf8");
|
|
55
|
+
return { workspace: JSON.parse(raw), dir: path.dirname(path.resolve(resolved)), file: resolved };
|
|
56
|
+
}
|
|
57
|
+
async function ensureDir(dir) {
|
|
58
|
+
await promises.mkdir(dir, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
async function cmdDescribe(args) {
|
|
61
|
+
const url = typeof args["url"] === "string" ? args["url"] : void 0;
|
|
62
|
+
if (!url) {
|
|
63
|
+
console.error(" [error] --url <wsdlUrl> is required");
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
const wsdlText = await fetchUrl(url);
|
|
67
|
+
const parsed = soapHandler.parseWsdl(wsdlText);
|
|
68
|
+
console.log("");
|
|
69
|
+
console.log(` Target namespace: ${parsed.targetNamespace || "(none)"}`);
|
|
70
|
+
if (parsed.endpoints.length) {
|
|
71
|
+
console.log(" Endpoints:");
|
|
72
|
+
for (const e of parsed.endpoints) {
|
|
73
|
+
console.log(` [${e.soapVersion}] ${e.binding} → ${e.address}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
console.log("");
|
|
77
|
+
if (parsed.operations.length === 0) {
|
|
78
|
+
console.log(" No operations found.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
console.log(" Operation Ver SOAPAction");
|
|
82
|
+
console.log(" ───────────────────────────────────── ───── ──────────────────────────────");
|
|
83
|
+
for (const op of parsed.operations) {
|
|
84
|
+
const name = op.name.slice(0, 37).padEnd(37);
|
|
85
|
+
const ver = op.soapVersion.padEnd(5);
|
|
86
|
+
const sa = (op.soapAction ?? "—").slice(0, 30);
|
|
87
|
+
console.log(` ${name} ${ver} ${sa}`);
|
|
88
|
+
}
|
|
89
|
+
console.log("");
|
|
90
|
+
}
|
|
91
|
+
async function loadExistingMockPorts(workspace, dir) {
|
|
92
|
+
const ports = [];
|
|
93
|
+
for (const relPath of workspace.mocks ?? []) {
|
|
94
|
+
try {
|
|
95
|
+
const raw = await promises.readFile(path.join(dir, relPath), "utf8");
|
|
96
|
+
const m = JSON.parse(raw);
|
|
97
|
+
if (typeof m.port === "number") ports.push(m.port);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return ports;
|
|
102
|
+
}
|
|
103
|
+
async function cmdImportCollection(args) {
|
|
104
|
+
const url = typeof args["url"] === "string" ? args["url"] : void 0;
|
|
105
|
+
const wsArg = typeof args["workspace"] === "string" ? args["workspace"] : void 0;
|
|
106
|
+
if (!url) {
|
|
107
|
+
console.error(" [error] --url <wsdlUrl> is required");
|
|
108
|
+
process.exit(2);
|
|
109
|
+
}
|
|
110
|
+
if (!wsArg) {
|
|
111
|
+
console.error(" [error] --workspace <path> is required");
|
|
112
|
+
process.exit(2);
|
|
113
|
+
}
|
|
114
|
+
const wsdlText = await fetchUrl(url);
|
|
115
|
+
const { workspace, dir, file } = await loadWorkspace(wsArg);
|
|
116
|
+
const name = typeof args["name"] === "string" ? args["name"] : void 0;
|
|
117
|
+
const { collection } = _import.importWsdl(wsdlText, { name });
|
|
118
|
+
const relPath = _import.defaultCollectionRelPath(workspace, collection);
|
|
119
|
+
const fullPath = path.resolve(dir, relPath);
|
|
120
|
+
await ensureDir(path.dirname(fullPath));
|
|
121
|
+
await promises.writeFile(fullPath, JSON.stringify(collection, null, 2), "utf8");
|
|
122
|
+
workspace.collections.push(relPath);
|
|
123
|
+
await promises.writeFile(file, JSON.stringify(workspace, null, 2), "utf8");
|
|
124
|
+
console.log(` ✓ Wrote ${relPath} (${Object.keys(collection.requests).length} requests)`);
|
|
125
|
+
}
|
|
126
|
+
async function cmdImportMock(args) {
|
|
127
|
+
const url = typeof args["url"] === "string" ? args["url"] : void 0;
|
|
128
|
+
const wsArg = typeof args["workspace"] === "string" ? args["workspace"] : void 0;
|
|
129
|
+
if (!url) {
|
|
130
|
+
console.error(" [error] --url <wsdlUrl> is required");
|
|
131
|
+
process.exit(2);
|
|
132
|
+
}
|
|
133
|
+
if (!wsArg) {
|
|
134
|
+
console.error(" [error] --workspace <path> is required");
|
|
135
|
+
process.exit(2);
|
|
136
|
+
}
|
|
137
|
+
const wsdlText = await fetchUrl(url);
|
|
138
|
+
const { workspace, dir, file } = await loadWorkspace(wsArg);
|
|
139
|
+
const existingPorts = await loadExistingMockPorts(workspace, dir);
|
|
140
|
+
const name = typeof args["name"] === "string" ? args["name"] : void 0;
|
|
141
|
+
const { mock } = _import.importWsdl(wsdlText, { name, existingMockPorts: existingPorts });
|
|
142
|
+
const relPath = _import.defaultMockRelPath(mock);
|
|
143
|
+
const fullPath = path.resolve(dir, relPath);
|
|
144
|
+
await ensureDir(path.dirname(fullPath));
|
|
145
|
+
await promises.writeFile(fullPath, JSON.stringify(mock, null, 2), "utf8");
|
|
146
|
+
if (!workspace.mocks) workspace.mocks = [];
|
|
147
|
+
workspace.mocks.push(relPath);
|
|
148
|
+
await promises.writeFile(file, JSON.stringify(workspace, null, 2), "utf8");
|
|
149
|
+
console.log(` ✓ Wrote ${relPath} on port ${mock.port} (${mock.routes.length} dispatch route${mock.routes.length === 1 ? "" : "s"})`);
|
|
150
|
+
if (args["start"] === true) {
|
|
151
|
+
console.log(" [note] --start is not supported in CLI mode; launch the mock from the app.");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function main() {
|
|
155
|
+
const [, , sub, ...rest] = process.argv;
|
|
156
|
+
const args = parseArgs(rest);
|
|
157
|
+
if (sub === "describe") return cmdDescribe(args);
|
|
158
|
+
if (sub === "import-collection") return cmdImportCollection(args);
|
|
159
|
+
if (sub === "import-mock") return cmdImportMock(args);
|
|
160
|
+
if (args["help"] || !sub) {
|
|
161
|
+
console.log(`
|
|
162
|
+
api-spector wsdl describe --url <wsdlUrl>
|
|
163
|
+
api-spector wsdl import-collection --workspace <path> --url <wsdlUrl> [--name <label>]
|
|
164
|
+
api-spector wsdl import-mock --workspace <path> --url <wsdlUrl> [--name <label>]
|
|
165
|
+
`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
console.error(` [error] Unknown subcommand "${sub}"`);
|
|
169
|
+
process.exit(2);
|
|
170
|
+
}
|
|
171
|
+
main().catch((e) => {
|
|
172
|
+
console.error(` [error] ${e instanceof Error ? e.message : String(e)}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
});
|
package/out/preload/index.js
CHANGED
|
@@ -11,6 +11,10 @@ const api = {
|
|
|
11
11
|
saveCollection: (relPath, col) => electron.ipcRenderer.invoke("file:saveCollection", relPath, col),
|
|
12
12
|
loadEnvironment: (relPath) => electron.ipcRenderer.invoke("file:loadEnvironment", relPath),
|
|
13
13
|
saveEnvironment: (relPath, env) => electron.ipcRenderer.invoke("file:saveEnvironment", relPath, env),
|
|
14
|
+
/** Idempotent unlink of a workspace-relative file. Used by collection /
|
|
15
|
+
* environment / mock delete flows so the file is removed from disk, not
|
|
16
|
+
* just from the workspace manifest. */
|
|
17
|
+
deleteWorkspaceFile: (relPath) => electron.ipcRenderer.invoke("file:deleteWorkspaceFile", relPath),
|
|
14
18
|
// ─── HTTP execution ────────────────────────────────────────────────────────
|
|
15
19
|
sendRequest: (payload) => electron.ipcRenderer.invoke("request:send", payload),
|
|
16
20
|
// ─── Secrets (encrypted, master-key-based) ───────────────────────────────
|
|
@@ -78,11 +82,18 @@ const api = {
|
|
|
78
82
|
},
|
|
79
83
|
// ─── SOAP / WSDL ──────────────────────────────────────────────────────────
|
|
80
84
|
wsdlFetch: (url, extraHeaders) => electron.ipcRenderer.invoke("wsdl:fetch", url, extraHeaders ?? {}),
|
|
85
|
+
/** Build a Collection + MockServer from a WSDL. Renderer registers the
|
|
86
|
+
* returned objects via the usual loadCollection/loadMock + save flows. */
|
|
87
|
+
wsdlImport: (opts) => electron.ipcRenderer.invoke("wsdl:import", opts),
|
|
81
88
|
// ─── Docs generation ──────────────────────────────────────────────────────
|
|
82
89
|
generateDocs: (payload) => electron.ipcRenderer.invoke("docs:generate", payload),
|
|
83
90
|
// ─── Contract testing ─────────────────────────────────────────────────────
|
|
84
91
|
runContracts: (payload) => electron.ipcRenderer.invoke("contract:run", payload),
|
|
85
92
|
inferContractSchema: (jsonBody) => electron.ipcRenderer.invoke("contract:inferSchema", jsonBody),
|
|
93
|
+
captureContractSnapshot: (opts) => electron.ipcRenderer.invoke("contract:captureSnapshot", opts),
|
|
94
|
+
listContractSnapshots: (registered = []) => electron.ipcRenderer.invoke("contract:listSnapshots", registered),
|
|
95
|
+
loadContractSnapshot: (relPath) => electron.ipcRenderer.invoke("contract:loadSnapshot", relPath),
|
|
96
|
+
deleteContractSnapshot: (relPath) => electron.ipcRenderer.invoke("contract:deleteSnapshot", relPath),
|
|
86
97
|
// ─── Script hooks ─────────────────────────────────────────────────────────
|
|
87
98
|
runScriptHook: (payload) => electron.ipcRenderer.invoke("script:run-hook", payload),
|
|
88
99
|
// ─── Git ──────────────────────────────────────────────────────────────────
|
|
@@ -98,6 +109,7 @@ const api = {
|
|
|
98
109
|
gitLog: (limit) => electron.ipcRenderer.invoke("git:log", limit),
|
|
99
110
|
gitBranches: () => electron.ipcRenderer.invoke("git:branches"),
|
|
100
111
|
gitCheckout: (branch, create) => electron.ipcRenderer.invoke("git:checkout", branch, create),
|
|
112
|
+
gitDeleteBranch: (name, force = false) => electron.ipcRenderer.invoke("git:deleteBranch", name, force),
|
|
101
113
|
gitPull: () => electron.ipcRenderer.invoke("git:pull"),
|
|
102
114
|
gitPush: (setUpstream) => electron.ipcRenderer.invoke("git:push", setUpstream),
|
|
103
115
|
gitRemotes: () => electron.ipcRenderer.invoke("git:remotes"),
|