@testsmith/api-spector 0.3.2 → 0.3.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
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const os = require('os')
|
|
4
6
|
const path = require('path')
|
|
5
|
-
const { spawn } = require('child_process')
|
|
7
|
+
const { spawn, spawnSync } = require('child_process')
|
|
6
8
|
|
|
7
9
|
const [, , cmd = 'ui', ...rest] = process.argv
|
|
8
10
|
|
|
@@ -61,50 +63,206 @@ if (!command) {
|
|
|
61
63
|
process.exit(1)
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
// ─── Electron binary self-repair ─────────────────────────────────────────────
|
|
67
|
+
//
|
|
68
|
+
// `require('electron')` throws when the postinstall didn't download the
|
|
69
|
+
// platform binary. On corporate machines the ~100 MB zip often IS fully
|
|
70
|
+
// downloaded into electron's cache — it's the extraction into node_modules
|
|
71
|
+
// that got interrupted (antivirus, killed install, …). In that case we can
|
|
72
|
+
// repair the install ourselves, using the OS's own unzip tooling (which is
|
|
73
|
+
// not affected by whatever broke Node's extractor), and launch anyway.
|
|
74
|
+
|
|
75
|
+
// Mirrors getPlatformPath() in electron's install.js — path.txt must contain
|
|
76
|
+
// exactly this value.
|
|
77
|
+
function electronPlatformPath() {
|
|
78
|
+
switch (process.platform) {
|
|
79
|
+
case 'win32': return 'electron.exe'
|
|
80
|
+
case 'darwin':
|
|
81
|
+
case 'mas': return 'Electron.app/Contents/MacOS/Electron'
|
|
82
|
+
default: return 'electron'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Default cache roots used by @electron/get, per OS. `electron_config_cache`
|
|
87
|
+
// overrides them (same variable electron's own installer respects).
|
|
88
|
+
function electronCacheDirs() {
|
|
89
|
+
if (process.env.electron_config_cache) return [process.env.electron_config_cache]
|
|
90
|
+
const home = os.homedir()
|
|
91
|
+
if (process.platform === 'win32') {
|
|
92
|
+
return [path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'electron', 'Cache')]
|
|
93
|
+
}
|
|
94
|
+
if (process.platform === 'darwin') {
|
|
95
|
+
return [path.join(home, 'Library', 'Caches', 'electron')]
|
|
96
|
+
}
|
|
97
|
+
return [path.join(process.env.XDG_CACHE_HOME || path.join(home, '.cache'), 'electron')]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Find a fully-downloaded electron zip for this version/platform/arch in the
|
|
101
|
+
// cache. Entries live in hash-named subdirectories; a real zip is >20 MB —
|
|
102
|
+
// anything smaller is a truncated download or a proxy's HTML block page.
|
|
103
|
+
function findCachedElectronZip(version) {
|
|
104
|
+
const wanted = `electron-v${version}-${process.platform}-${process.arch}.zip`
|
|
105
|
+
for (const root of electronCacheDirs()) {
|
|
106
|
+
let entries
|
|
107
|
+
try { entries = fs.readdirSync(root) } catch { continue }
|
|
108
|
+
for (const entry of ['', ...entries]) {
|
|
109
|
+
const candidate = path.join(root, entry, wanted)
|
|
110
|
+
try {
|
|
111
|
+
if (fs.statSync(candidate).size > 20 * 1024 * 1024) return candidate
|
|
112
|
+
} catch { /* not there — keep looking */ }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Extract with OS-native tools: PowerShell on Windows, ditto on macOS (it
|
|
119
|
+
// preserves the symlinks inside Electron.app, plain unzip does not), unzip on
|
|
120
|
+
// Linux. Deliberately NOT extract-zip — when we get here, that path already
|
|
121
|
+
// failed once on this machine.
|
|
122
|
+
function extractZipNative(zip, destDir) {
|
|
123
|
+
let r
|
|
124
|
+
if (process.platform === 'win32') {
|
|
125
|
+
r = spawnSync('powershell.exe', [
|
|
126
|
+
'-NoProfile', '-NonInteractive', '-Command',
|
|
127
|
+
`Expand-Archive -LiteralPath "${zip}" -DestinationPath "${destDir}" -Force`,
|
|
128
|
+
], { stdio: 'ignore' })
|
|
129
|
+
} else if (process.platform === 'darwin') {
|
|
130
|
+
r = spawnSync('ditto', ['-x', '-k', zip, destDir], { stdio: 'ignore' })
|
|
131
|
+
} else {
|
|
132
|
+
r = spawnSync('unzip', ['-o', '-q', zip, '-d', destDir], { stdio: 'ignore' })
|
|
133
|
+
}
|
|
134
|
+
return Boolean(r && r.status === 0)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Attempt to rebuild node_modules/electron/dist from a cached zip.
|
|
138
|
+
// Returns 'repaired', 'no-zip', or a { zip } object when extraction failed.
|
|
139
|
+
function tryRepairElectron() {
|
|
140
|
+
let pkgPath
|
|
141
|
+
try { pkgPath = require.resolve('electron/package.json') } catch { return 'no-zip' }
|
|
142
|
+
const electronDir = path.dirname(pkgPath)
|
|
143
|
+
const version = require(pkgPath).version
|
|
144
|
+
const zip = findCachedElectronZip(version)
|
|
145
|
+
if (!zip) return 'no-zip'
|
|
146
|
+
|
|
147
|
+
console.error(` Electron ${version} was already downloaded — repairing the`)
|
|
148
|
+
console.error(' installation from the local cache...')
|
|
149
|
+
const distDir = path.join(electronDir, 'dist')
|
|
150
|
+
try { fs.rmSync(distDir, { recursive: true, force: true }) } catch { /* best effort */ }
|
|
151
|
+
if (!extractZipNative(zip, distDir) || !fs.existsSync(path.join(distDir, electronPlatformPath()))) {
|
|
152
|
+
return { zip }
|
|
153
|
+
}
|
|
154
|
+
fs.writeFileSync(path.join(electronDir, 'path.txt'), electronPlatformPath())
|
|
155
|
+
console.error(' Repaired.')
|
|
156
|
+
console.error('')
|
|
157
|
+
return 'repaired'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Resolve the electron executable, classifying the failure modes.
|
|
161
|
+
function loadElectron() {
|
|
71
162
|
try {
|
|
72
|
-
electron = require('electron')
|
|
163
|
+
const electron = require('electron')
|
|
164
|
+
// path.txt can exist while dist/ is incomplete (interrupted extraction) —
|
|
165
|
+
// require() succeeds but points at a binary that isn't there.
|
|
166
|
+
if (typeof electron === 'string' && !fs.existsSync(electron)) {
|
|
167
|
+
return { status: 'binary-missing' }
|
|
168
|
+
}
|
|
169
|
+
return { status: 'ok', electron }
|
|
73
170
|
} catch (err) {
|
|
74
171
|
const msg = err && err.message ? err.message : String(err)
|
|
75
|
-
|
|
172
|
+
if (/Cannot find module 'electron'/i.test(msg)) return { status: 'not-installed' }
|
|
173
|
+
if (/Electron failed to install correctly/i.test(msg)) return { status: 'binary-missing' }
|
|
174
|
+
return { status: 'error', message: msg }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const TROUBLESHOOTING_URL =
|
|
179
|
+
'https://github.com/testsmith-io/api-spector/blob/main/docs/getting-started/troubleshooting.md'
|
|
180
|
+
|
|
181
|
+
// ui: spawn electron with the app dir
|
|
182
|
+
if (command.runner === 'electron') {
|
|
183
|
+
let loaded = loadElectron()
|
|
184
|
+
let failedZip = null
|
|
185
|
+
|
|
186
|
+
if (loaded.status === 'binary-missing') {
|
|
187
|
+
const repair = tryRepairElectron()
|
|
188
|
+
if (repair === 'repaired') {
|
|
189
|
+
loaded = loadElectron()
|
|
190
|
+
} else if (repair && repair.zip) {
|
|
191
|
+
failedZip = repair.zip
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (loaded.status !== 'ok') {
|
|
76
196
|
console.error('')
|
|
77
197
|
console.error(' API Spector — failed to launch the UI.')
|
|
78
198
|
console.error('')
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
console.error('
|
|
82
|
-
console.error(' download during `npm install` did not complete (often a proxy or')
|
|
83
|
-
console.error(' firewall blocking github.com / electronjs.org).')
|
|
84
|
-
console.error('')
|
|
85
|
-
console.error(' Fix options (try in order):')
|
|
199
|
+
if (loaded.status === 'not-installed') {
|
|
200
|
+
console.error(' The electron package is not installed alongside API Spector.')
|
|
201
|
+
console.error(' Versions 0.3.1 and 0.3.2 shipped without it by mistake.')
|
|
86
202
|
console.error('')
|
|
87
|
-
console.error('
|
|
88
|
-
console.error(' npm install -g @testsmith/api-spector --force')
|
|
203
|
+
console.error(' Fix options:')
|
|
89
204
|
console.error('')
|
|
90
|
-
console.error('
|
|
91
|
-
console.error(' npm
|
|
92
|
-
console.error('
|
|
93
|
-
console.error(' set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/')
|
|
94
|
-
console.error(' npm install -g @testsmith/api-spector --force')
|
|
205
|
+
console.error(' 1. Update API Spector (0.3.3 or later includes electron):')
|
|
206
|
+
console.error(' npm install -D @testsmith/api-spector@latest')
|
|
207
|
+
console.error(' (use -g instead of -D if you installed globally)')
|
|
95
208
|
console.error('')
|
|
96
|
-
console.error('
|
|
97
|
-
console.error(
|
|
98
|
-
|
|
209
|
+
console.error(' 2. Or keep this version and install electron yourself:')
|
|
210
|
+
console.error(' npm install -D electron@31')
|
|
211
|
+
} else if (loaded.status === 'binary-missing') {
|
|
212
|
+
let electronDir = null
|
|
213
|
+
let version = '<version>'
|
|
214
|
+
try {
|
|
215
|
+
const pkgPath = require.resolve('electron/package.json')
|
|
216
|
+
electronDir = path.dirname(pkgPath)
|
|
217
|
+
version = require(pkgPath).version
|
|
218
|
+
} catch { /* keep placeholders */ }
|
|
219
|
+
const zipName = `electron-v${version}-${process.platform}-${process.arch}.zip`
|
|
220
|
+
|
|
221
|
+
if (failedZip) {
|
|
222
|
+
console.error(' Electron\'s binary is missing. A downloaded copy exists at')
|
|
223
|
+
console.error(` ${failedZip}`)
|
|
224
|
+
console.error(' but it could not be extracted — the file may be corrupt (delete')
|
|
225
|
+
console.error(' it and reinstall), or antivirus is blocking the extraction.')
|
|
226
|
+
} else {
|
|
227
|
+
console.error(' Electron is installed, but its platform binary is missing and no')
|
|
228
|
+
console.error(' usable download was found in the local cache. The download during')
|
|
229
|
+
console.error(' `npm install` was probably blocked.')
|
|
230
|
+
console.error('')
|
|
231
|
+
console.error(' Common causes on corporate machines:')
|
|
232
|
+
console.error('')
|
|
233
|
+
console.error(' - Proxy blocks github.com downloads. Note: npm\'s proxy settings')
|
|
234
|
+
console.error(' do NOT apply to electron\'s downloader — it needs:')
|
|
235
|
+
console.error(' ELECTRON_GET_USE_PROXY=1')
|
|
236
|
+
console.error(' GLOBAL_AGENT_HTTPS_PROXY=http://your-proxy:port')
|
|
237
|
+
console.error(' then: npm install -D @testsmith/api-spector --force')
|
|
238
|
+
console.error('')
|
|
239
|
+
console.error(' - TLS-intercepting proxy (certificate errors): point Node at')
|
|
240
|
+
console.error(' your corporate root CA:')
|
|
241
|
+
console.error(' NODE_EXTRA_CA_CERTS=/path/to/corporate-root-ca.pem')
|
|
242
|
+
console.error('')
|
|
243
|
+
console.error(' - ELECTRON_SKIP_BINARY_DOWNLOAD=1 set machine-wide (some IT')
|
|
244
|
+
console.error(' images do this) — unset it and reinstall.')
|
|
245
|
+
}
|
|
99
246
|
console.error('')
|
|
100
|
-
console.error('
|
|
101
|
-
console.error(
|
|
247
|
+
console.error(' Manual fix (works without any of the above): download')
|
|
248
|
+
console.error(` https://github.com/electron/electron/releases/download/v${version}/${zipName}`)
|
|
249
|
+
console.error(' in a browser, extract ALL of it into:')
|
|
250
|
+
console.error(` ${electronDir ? path.join(electronDir, 'dist') : '<node_modules>/electron/dist'}`)
|
|
251
|
+
console.error(` and create a file "path.txt" next to "dist" containing exactly:`)
|
|
252
|
+
console.error(` ${electronPlatformPath()}`)
|
|
102
253
|
} else {
|
|
103
|
-
console.error(` ${
|
|
254
|
+
console.error(` ${loaded.message}`)
|
|
104
255
|
}
|
|
105
256
|
console.error('')
|
|
257
|
+
console.error(' CLI subcommands (run / mock / record / contract / wsdl) do not')
|
|
258
|
+
console.error(' need the UI binary and work even while this is broken.')
|
|
259
|
+
console.error('')
|
|
260
|
+
console.error(` Full troubleshooting guide: ${TROUBLESHOOTING_URL}`)
|
|
261
|
+
console.error('')
|
|
106
262
|
process.exit(1)
|
|
107
263
|
}
|
|
264
|
+
|
|
265
|
+
const electron = loaded.electron
|
|
108
266
|
const appDir = path.join(__dirname, '..')
|
|
109
267
|
// Forward the user's cwd so the main process can decide whether to open a
|
|
110
268
|
// workspace in this folder, or fall through to the welcome screen. Without
|
package/out/main/runner.js
CHANGED
|
@@ -393,7 +393,7 @@ async function main() {
|
|
|
393
393
|
if (envName && !env) {
|
|
394
394
|
console.warn(cliCommon.color(`Warning: environment "${envName}" not found. Running without environment.`, cliCommon.C.yellow));
|
|
395
395
|
}
|
|
396
|
-
const version = `v${"0.3.
|
|
396
|
+
const version = `v${"0.3.4"}`;
|
|
397
397
|
console.log("");
|
|
398
398
|
console.log(cliCommon.color(" API Test Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
|
|
399
399
|
console.log(cliCommon.color(` Workspace: ${wsPath}`, cliCommon.C.gray));
|
|
@@ -70829,7 +70829,7 @@ function App() {
|
|
|
70829
70829
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
|
|
70830
70830
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
|
|
70831
70831
|
"v",
|
|
70832
|
-
"0.3.
|
|
70832
|
+
"0.3.4"
|
|
70833
70833
|
] }),
|
|
70834
70834
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
|
|
70835
70835
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
package/out/renderer/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>API Spector</title>
|
|
8
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-BjjOh-E5.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="./assets/index-CjvjKHtF.css">
|
|
10
10
|
</head>
|
|
11
11
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testsmith/api-spector",
|
|
3
3
|
"productName": "API Spector",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.4",
|
|
5
5
|
"description": "Local-first API testing tool to inspect, test and mock APIs",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -59,7 +59,6 @@
|
|
|
59
59
|
"@vitest/coverage-v8": "^3.2.4",
|
|
60
60
|
"jsdom": "^25.0.1",
|
|
61
61
|
"autoprefixer": "^10.4.27",
|
|
62
|
-
"electron": "~31.7.0",
|
|
63
62
|
"electron-builder": "^26.8.1",
|
|
64
63
|
"electron-vite": "^5.0.0",
|
|
65
64
|
"eslint": "^9.39.4",
|
|
@@ -96,6 +95,9 @@
|
|
|
96
95
|
"ws": "^8.20.0",
|
|
97
96
|
"zustand": "^5.0.12"
|
|
98
97
|
},
|
|
98
|
+
"optionalDependencies": {
|
|
99
|
+
"electron": "~31.7.0"
|
|
100
|
+
},
|
|
99
101
|
"build": {
|
|
100
102
|
"appId": "com.apispector.app",
|
|
101
103
|
"productName": "API Spector",
|
package/readme.md
CHANGED
|
@@ -13,6 +13,9 @@ Local-first API testing tool. Inspect, test and mock APIs. Secrets stay on your
|
|
|
13
13
|
npm install -g @testsmith/api-spector
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
> UI not starting after install (common on corporate machines — proxies, antivirus)?
|
|
17
|
+
> See [Troubleshooting: the UI won't start](docs/getting-started/troubleshooting.md).
|
|
18
|
+
|
|
16
19
|
## Usage
|
|
17
20
|
|
|
18
21
|
### GUI
|