@saptools/cf-inspector 0.8.1 → 0.9.0
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/README.md +29 -0
- package/dist/cli.js +1192 -50
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -152,12 +152,1153 @@ var init_wsTransport = __esm({
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
// src/cli.ts
|
|
155
|
-
import
|
|
155
|
+
import process14 from "process";
|
|
156
156
|
|
|
157
157
|
// src/cli/program.ts
|
|
158
|
+
import process13 from "process";
|
|
159
|
+
|
|
160
|
+
// ../core/src/package-metadata.ts
|
|
158
161
|
import { readFileSync } from "fs";
|
|
159
|
-
import { dirname, join
|
|
162
|
+
import { dirname, join } from "path";
|
|
160
163
|
import { fileURLToPath } from "url";
|
|
164
|
+
|
|
165
|
+
// ../core/src/records.ts
|
|
166
|
+
function isRecord(value) {
|
|
167
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
168
|
+
}
|
|
169
|
+
function errorMessage(error) {
|
|
170
|
+
return error instanceof Error ? error.message : String(error);
|
|
171
|
+
}
|
|
172
|
+
function readString(record, key) {
|
|
173
|
+
const value = record[key];
|
|
174
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ../core/src/package-metadata.ts
|
|
178
|
+
var MAX_WALK_UP = 6;
|
|
179
|
+
function readPackageManifest(directory) {
|
|
180
|
+
let raw;
|
|
181
|
+
try {
|
|
182
|
+
raw = readFileSync(join(directory, "package.json"), "utf8");
|
|
183
|
+
} catch {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
let parsed;
|
|
187
|
+
try {
|
|
188
|
+
parsed = JSON.parse(raw);
|
|
189
|
+
} catch {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!isRecord(parsed)) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const name = readString(parsed, "name");
|
|
196
|
+
const version = readString(parsed, "version");
|
|
197
|
+
if (name === void 0 || version === void 0) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
return { name, version, directory };
|
|
201
|
+
}
|
|
202
|
+
function findPackageMetadata(startDirectory, expectedName) {
|
|
203
|
+
let current = startDirectory;
|
|
204
|
+
for (let depth = 0; depth < MAX_WALK_UP; depth += 1) {
|
|
205
|
+
const manifest = readPackageManifest(current);
|
|
206
|
+
if (manifest?.name === expectedName) {
|
|
207
|
+
return manifest;
|
|
208
|
+
}
|
|
209
|
+
const parent = dirname(current);
|
|
210
|
+
if (parent === current) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
current = parent;
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
function readPackageMetadata(importMetaUrl, expectedName) {
|
|
218
|
+
const startDirectory = dirname(fileURLToPath(importMetaUrl));
|
|
219
|
+
const found = findPackageMetadata(startDirectory, expectedName);
|
|
220
|
+
if (found === void 0) {
|
|
221
|
+
throw new Error(`Cannot find the package.json of ${expectedName} above ${startDirectory}`);
|
|
222
|
+
}
|
|
223
|
+
return found;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ../core/src/saptools-paths.ts
|
|
227
|
+
import { chmodSync, mkdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "fs";
|
|
228
|
+
import { homedir } from "os";
|
|
229
|
+
import { join as join2 } from "path";
|
|
230
|
+
var SAPTOOLS_DIR_NAME = ".saptools";
|
|
231
|
+
var SAPTOOLS_ROOT_ENV = "SAPTOOLS_ROOT";
|
|
232
|
+
function resolveSaptoolsRoot(explicit, env = process.env) {
|
|
233
|
+
if (explicit !== void 0 && explicit.length > 0) {
|
|
234
|
+
return explicit;
|
|
235
|
+
}
|
|
236
|
+
const fromEnv = env[SAPTOOLS_ROOT_ENV];
|
|
237
|
+
if (fromEnv !== void 0 && fromEnv.length > 0) {
|
|
238
|
+
return fromEnv;
|
|
239
|
+
}
|
|
240
|
+
return join2(homedir(), SAPTOOLS_DIR_NAME);
|
|
241
|
+
}
|
|
242
|
+
function ensurePrivateDirectorySync(directory) {
|
|
243
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
244
|
+
try {
|
|
245
|
+
chmodSync(directory, 448);
|
|
246
|
+
} catch {
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function writeFileAtomicSync(path, data, mode = 384) {
|
|
250
|
+
const temp = `${path}.${String(process.pid)}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
251
|
+
try {
|
|
252
|
+
writeFileSync(temp, data, { encoding: "utf8", mode });
|
|
253
|
+
try {
|
|
254
|
+
chmodSync(temp, mode);
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
renameSync(temp, path);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
rmSync(temp, { force: true });
|
|
260
|
+
throw error;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function readJsonFileSync(path) {
|
|
264
|
+
let raw;
|
|
265
|
+
try {
|
|
266
|
+
raw = readFileSync2(path, "utf8");
|
|
267
|
+
} catch {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(raw);
|
|
272
|
+
} catch {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ../core/src/self-update/reexec.ts
|
|
278
|
+
import { spawn } from "child_process";
|
|
279
|
+
import { constants } from "os";
|
|
280
|
+
var REEXEC_MARKER_ENV = "SAPTOOLS_SELF_UPDATE_REEXEC";
|
|
281
|
+
var FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
282
|
+
function nativeExecve() {
|
|
283
|
+
return process.execve?.bind(process);
|
|
284
|
+
}
|
|
285
|
+
function defaultRuntime() {
|
|
286
|
+
return {
|
|
287
|
+
platform: process.platform,
|
|
288
|
+
execve: nativeExecve(),
|
|
289
|
+
spawnImpl: spawn,
|
|
290
|
+
onSignal: (signal, handler) => {
|
|
291
|
+
process.on(signal, handler);
|
|
292
|
+
},
|
|
293
|
+
offSignal: (signal, handler) => {
|
|
294
|
+
process.off(signal, handler);
|
|
295
|
+
},
|
|
296
|
+
kill: (pid, signal) => {
|
|
297
|
+
process.kill(pid, signal);
|
|
298
|
+
},
|
|
299
|
+
exit: (code) => {
|
|
300
|
+
process.exit(code);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function buildReexecArgv(request2) {
|
|
305
|
+
return [...request2.execArgv, request2.binPath, ...request2.args];
|
|
306
|
+
}
|
|
307
|
+
function reexecEnvironment(env) {
|
|
308
|
+
return { ...env, [REEXEC_MARKER_ENV]: "1" };
|
|
309
|
+
}
|
|
310
|
+
function signalExitCode(signal) {
|
|
311
|
+
const number = constants.signals[signal];
|
|
312
|
+
return 128 + number;
|
|
313
|
+
}
|
|
314
|
+
async function reexecProcess(request2, runtimeOverrides = {}) {
|
|
315
|
+
const runtime = { ...defaultRuntime(), ...runtimeOverrides };
|
|
316
|
+
const argv = buildReexecArgv(request2);
|
|
317
|
+
const env = reexecEnvironment(request2.env);
|
|
318
|
+
if (runtime.platform !== "win32" && runtime.execve !== void 0) {
|
|
319
|
+
try {
|
|
320
|
+
runtime.execve(request2.execPath, [request2.execPath, ...argv], env);
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
await new Promise((resolve, reject) => {
|
|
325
|
+
const child = runtime.spawnImpl(request2.execPath, argv, { stdio: "inherit", env });
|
|
326
|
+
const forward = (signal) => {
|
|
327
|
+
child.kill(signal);
|
|
328
|
+
};
|
|
329
|
+
for (const signal of FORWARDED_SIGNALS) {
|
|
330
|
+
runtime.onSignal(signal, forward);
|
|
331
|
+
}
|
|
332
|
+
const detach = () => {
|
|
333
|
+
for (const signal of FORWARDED_SIGNALS) {
|
|
334
|
+
runtime.offSignal(signal, forward);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
child.once("error", (error) => {
|
|
338
|
+
detach();
|
|
339
|
+
reject(error);
|
|
340
|
+
});
|
|
341
|
+
child.once("exit", (code, signal) => {
|
|
342
|
+
detach();
|
|
343
|
+
if (signal === null) {
|
|
344
|
+
runtime.exit(code ?? 1);
|
|
345
|
+
} else {
|
|
346
|
+
runtime.kill(process.pid, signal);
|
|
347
|
+
runtime.exit(signalExitCode(signal));
|
|
348
|
+
}
|
|
349
|
+
resolve();
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ../core/src/self-update/policy.ts
|
|
355
|
+
var POLICY_ENV = "SAPTOOLS_AUTO_UPDATE";
|
|
356
|
+
var INTERVAL_ENV = "SAPTOOLS_UPDATE_INTERVAL_MINUTES";
|
|
357
|
+
var DEBUG_ENV = "SAPTOOLS_UPDATE_DEBUG";
|
|
358
|
+
var DEFAULT_CHECK_INTERVAL_MINUTES = 60;
|
|
359
|
+
var SELF_UPDATE_COMMAND = "self-update";
|
|
360
|
+
function parsePolicy(raw) {
|
|
361
|
+
if (raw === void 0) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const value = raw.trim().toLowerCase();
|
|
365
|
+
if (["on", "1", "true", "yes", "auto", "always"].includes(value)) {
|
|
366
|
+
return "on";
|
|
367
|
+
}
|
|
368
|
+
if (["notify", "check"].includes(value)) {
|
|
369
|
+
return "notify";
|
|
370
|
+
}
|
|
371
|
+
if (["off", "0", "false", "no", "never", "disabled"].includes(value)) {
|
|
372
|
+
return "off";
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
function isTruthyFlag(raw) {
|
|
377
|
+
return raw !== void 0 && !["", "0", "false", "no", "off"].includes(raw.trim().toLowerCase());
|
|
378
|
+
}
|
|
379
|
+
function readExplicitPolicy(env, envPrefix) {
|
|
380
|
+
const perPackage = envPrefix === void 0 ? void 0 : parsePolicy(env[`${envPrefix}_AUTO_UPDATE`]);
|
|
381
|
+
return perPackage ?? parsePolicy(env[POLICY_ENV]);
|
|
382
|
+
}
|
|
383
|
+
function off(reason, explicit) {
|
|
384
|
+
return { policy: "off", reason, explicit };
|
|
385
|
+
}
|
|
386
|
+
function notify(reason, explicit) {
|
|
387
|
+
return { policy: "notify", reason, explicit };
|
|
388
|
+
}
|
|
389
|
+
function locationDecision(location, explicit) {
|
|
390
|
+
switch (location.kind) {
|
|
391
|
+
case "local":
|
|
392
|
+
return off("running from a source checkout or linked package, not a package-manager install", explicit);
|
|
393
|
+
case "npx":
|
|
394
|
+
return off("running through npx or dlx, which resolves the version on its own", explicit);
|
|
395
|
+
case "unknown":
|
|
396
|
+
return notify(`install location not recognized (${location.detail})`, explicit);
|
|
397
|
+
case "npm-global":
|
|
398
|
+
case "pnpm-global":
|
|
399
|
+
case "yarn-global":
|
|
400
|
+
case "bun-global":
|
|
401
|
+
case "volta":
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function resolveUpdatePolicy(input) {
|
|
406
|
+
const { env, location } = input;
|
|
407
|
+
const manual = input.manual === true;
|
|
408
|
+
const explicit = readExplicitPolicy(env, input.envPrefix);
|
|
409
|
+
if (!manual && isTruthyFlag(env[REEXEC_MARKER_ENV])) {
|
|
410
|
+
return off("already re-executed after an update", false);
|
|
411
|
+
}
|
|
412
|
+
const commandPath = input.commandPath ?? "";
|
|
413
|
+
if (!manual && (commandPath === SELF_UPDATE_COMMAND || (input.skipCommands ?? []).includes(commandPath))) {
|
|
414
|
+
return off(`the "${commandPath}" command is excluded from automatic updates`, false);
|
|
415
|
+
}
|
|
416
|
+
if (!manual && explicit === "off") {
|
|
417
|
+
return off(`${POLICY_ENV} is off`, true);
|
|
418
|
+
}
|
|
419
|
+
if (!manual && explicit === void 0) {
|
|
420
|
+
if (isTruthyFlag(env["NO_UPDATE_NOTIFIER"])) {
|
|
421
|
+
return off("NO_UPDATE_NOTIFIER is set", false);
|
|
422
|
+
}
|
|
423
|
+
if (isTruthyFlag(env["CI"])) {
|
|
424
|
+
return off("running in CI", false);
|
|
425
|
+
}
|
|
426
|
+
if (env["NODE_ENV"] === "test") {
|
|
427
|
+
return off("NODE_ENV is test", false);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const byLocation = locationDecision(location, explicit !== void 0);
|
|
431
|
+
if (byLocation !== void 0) {
|
|
432
|
+
return byLocation;
|
|
433
|
+
}
|
|
434
|
+
if (!location.writable) {
|
|
435
|
+
return notify("the install directory is not writable by this user", explicit !== void 0);
|
|
436
|
+
}
|
|
437
|
+
if (manual) {
|
|
438
|
+
return { policy: "on", reason: "requested explicitly", explicit: true };
|
|
439
|
+
}
|
|
440
|
+
return explicit === void 0 ? { policy: "on", reason: "default", explicit: false } : { policy: explicit, reason: `${POLICY_ENV} is ${explicit}`, explicit: true };
|
|
441
|
+
}
|
|
442
|
+
function resolveCheckIntervalMs(env) {
|
|
443
|
+
const raw = env[INTERVAL_ENV];
|
|
444
|
+
if (raw === void 0) {
|
|
445
|
+
return DEFAULT_CHECK_INTERVAL_MINUTES * 6e4;
|
|
446
|
+
}
|
|
447
|
+
const minutes = Number(raw.trim());
|
|
448
|
+
if (!Number.isFinite(minutes) || minutes < 0) {
|
|
449
|
+
return DEFAULT_CHECK_INTERVAL_MINUTES * 6e4;
|
|
450
|
+
}
|
|
451
|
+
return Math.round(minutes * 6e4);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ../core/src/self-update/run.ts
|
|
455
|
+
import { spawn as spawn2 } from "child_process";
|
|
456
|
+
import { homedir as homedir2 } from "os";
|
|
457
|
+
|
|
458
|
+
// ../core/src/self-update/install-location.ts
|
|
459
|
+
import { accessSync, constants as constants2, realpathSync } from "fs";
|
|
460
|
+
import { basename, dirname as dirname2, join as join3, sep } from "path";
|
|
461
|
+
var UPGRADABLE_KINDS = /* @__PURE__ */ new Set(["npm-global", "pnpm-global", "yarn-global", "bun-global", "volta"]);
|
|
462
|
+
function isUpgradableKind(kind) {
|
|
463
|
+
return UPGRADABLE_KINDS.has(kind);
|
|
464
|
+
}
|
|
465
|
+
function defaultIsWritable(path) {
|
|
466
|
+
try {
|
|
467
|
+
accessSync(path, constants2.W_OK);
|
|
468
|
+
return true;
|
|
469
|
+
} catch {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function toPosix(path) {
|
|
474
|
+
return path.split(sep).join("/");
|
|
475
|
+
}
|
|
476
|
+
function isPnpmGlobal(posixDirectory, env) {
|
|
477
|
+
if (posixDirectory.includes("/pnpm/global/")) {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
const home = env["PNPM_HOME"];
|
|
481
|
+
return home !== void 0 && home.length > 0 && posixDirectory.startsWith(`${toPosix(home).replace(/\/+$/, "")}/global/`);
|
|
482
|
+
}
|
|
483
|
+
function nodeModulesRootOf(packageDirectory) {
|
|
484
|
+
const parent = dirname2(packageDirectory);
|
|
485
|
+
if (basename(parent) === "node_modules") {
|
|
486
|
+
return parent;
|
|
487
|
+
}
|
|
488
|
+
const grandparent = dirname2(parent);
|
|
489
|
+
return basename(grandparent) === "node_modules" ? grandparent : void 0;
|
|
490
|
+
}
|
|
491
|
+
function npmPrefixFor(packageDirectory, platform) {
|
|
492
|
+
const nodeModules = nodeModulesRootOf(packageDirectory);
|
|
493
|
+
if (nodeModules === void 0) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const parent = dirname2(nodeModules);
|
|
497
|
+
if (platform === "win32") {
|
|
498
|
+
return basename(parent).toLowerCase() === "npm" ? parent : void 0;
|
|
499
|
+
}
|
|
500
|
+
return basename(parent) === "lib" ? dirname2(parent) : void 0;
|
|
501
|
+
}
|
|
502
|
+
function classifyKind(packageDirectory, platform, env) {
|
|
503
|
+
const posix = toPosix(packageDirectory);
|
|
504
|
+
if (posix.includes("/_npx/") || /\/dlx-[^/]+\//.test(posix) || posix.includes("/pnpm/dlx/") || posix.includes("/.cache/pnpm/dlx")) {
|
|
505
|
+
return "npx";
|
|
506
|
+
}
|
|
507
|
+
if (posix.includes("/.volta/tools/image/packages/")) {
|
|
508
|
+
return "volta";
|
|
509
|
+
}
|
|
510
|
+
if (posix.includes("/.bun/install/global/")) {
|
|
511
|
+
return "bun-global";
|
|
512
|
+
}
|
|
513
|
+
if (posix.includes("/.config/yarn/global/") || posix.includes("/yarn/global/node_modules/")) {
|
|
514
|
+
return "yarn-global";
|
|
515
|
+
}
|
|
516
|
+
if (isPnpmGlobal(posix, env)) {
|
|
517
|
+
return "pnpm-global";
|
|
518
|
+
}
|
|
519
|
+
if (!posix.includes("/node_modules/")) {
|
|
520
|
+
return "local";
|
|
521
|
+
}
|
|
522
|
+
return npmPrefixFor(packageDirectory, platform) === void 0 ? "local" : "npm-global";
|
|
523
|
+
}
|
|
524
|
+
function writableForUpgrade(packageDirectory, prefix, platform, isWritable) {
|
|
525
|
+
const targets = [packageDirectory, dirname2(packageDirectory)];
|
|
526
|
+
const nodeModules = nodeModulesRootOf(packageDirectory);
|
|
527
|
+
if (nodeModules !== void 0) {
|
|
528
|
+
targets.push(nodeModules);
|
|
529
|
+
}
|
|
530
|
+
if (prefix !== void 0) {
|
|
531
|
+
targets.push(platform === "win32" ? prefix : join3(prefix, "bin"));
|
|
532
|
+
}
|
|
533
|
+
return targets.every((target) => isWritable(target));
|
|
534
|
+
}
|
|
535
|
+
function describe(kind, packageDirectory, prefix) {
|
|
536
|
+
if (kind === "npm-global" && prefix !== void 0) {
|
|
537
|
+
return `npm global install under ${prefix}`;
|
|
538
|
+
}
|
|
539
|
+
return `${kind} install at ${packageDirectory}`;
|
|
540
|
+
}
|
|
541
|
+
function unknownLocation(detail) {
|
|
542
|
+
return { kind: "unknown", packageDirectory: void 0, prefix: void 0, writable: false, detail };
|
|
543
|
+
}
|
|
544
|
+
function detectInstallLocation(options) {
|
|
545
|
+
const platform = options.platform ?? process.platform;
|
|
546
|
+
const env = options.env ?? process.env;
|
|
547
|
+
const realpath = options.realpath ?? ((path) => realpathSync(path));
|
|
548
|
+
const isWritable = options.isWritable ?? defaultIsWritable;
|
|
549
|
+
let resolvedBin;
|
|
550
|
+
try {
|
|
551
|
+
resolvedBin = realpath(options.binPath);
|
|
552
|
+
} catch (error) {
|
|
553
|
+
return unknownLocation(`cannot resolve ${options.binPath}: ${errorMessage(error)}`);
|
|
554
|
+
}
|
|
555
|
+
const manifest = findPackageMetadata(dirname2(resolvedBin), options.packageName);
|
|
556
|
+
if (manifest === void 0) {
|
|
557
|
+
return unknownLocation(`no package.json for ${options.packageName} above ${resolvedBin}`);
|
|
558
|
+
}
|
|
559
|
+
const packageDirectory = manifest.directory;
|
|
560
|
+
const kind = classifyKind(packageDirectory, platform, env);
|
|
561
|
+
const prefix = kind === "npm-global" ? npmPrefixFor(packageDirectory, platform) : void 0;
|
|
562
|
+
const writable = isUpgradableKind(kind) && writableForUpgrade(packageDirectory, prefix, platform, isWritable);
|
|
563
|
+
return { kind, packageDirectory, prefix, writable, detail: describe(kind, packageDirectory, prefix) };
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// ../core/src/self-update/installer.ts
|
|
567
|
+
import { existsSync, realpathSync as realpathSync2 } from "fs";
|
|
568
|
+
import { tmpdir } from "os";
|
|
569
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
570
|
+
var DEFAULT_INSTALL_TIMEOUT_MS = 18e4;
|
|
571
|
+
function manualInstallCommand(kind, spec) {
|
|
572
|
+
switch (kind) {
|
|
573
|
+
case "pnpm-global":
|
|
574
|
+
return `pnpm add -g ${spec}`;
|
|
575
|
+
case "yarn-global":
|
|
576
|
+
return `yarn global add ${spec}`;
|
|
577
|
+
case "bun-global":
|
|
578
|
+
return `bun add -g ${spec}`;
|
|
579
|
+
case "volta":
|
|
580
|
+
return `volta install ${spec}`;
|
|
581
|
+
case "npm-global":
|
|
582
|
+
case "npx":
|
|
583
|
+
case "local":
|
|
584
|
+
case "unknown":
|
|
585
|
+
return `npm install -g ${spec}`;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function resolveNpmInvocation(execPath, platform, exists, realpath) {
|
|
589
|
+
const binDirectory = dirname3(execPath);
|
|
590
|
+
if (platform === "win32") {
|
|
591
|
+
const cli = join4(binDirectory, "node_modules", "npm", "bin", "npm-cli.js");
|
|
592
|
+
return exists(cli) ? { file: execPath, leadingArgs: [cli] } : void 0;
|
|
593
|
+
}
|
|
594
|
+
const sibling = join4(binDirectory, "npm");
|
|
595
|
+
if (exists(sibling)) {
|
|
596
|
+
try {
|
|
597
|
+
const target = realpath(sibling);
|
|
598
|
+
return target.endsWith(".js") ? { file: execPath, leadingArgs: [target] } : { file: target, leadingArgs: [] };
|
|
599
|
+
} catch {
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return { file: "npm", leadingArgs: [] };
|
|
603
|
+
}
|
|
604
|
+
function buildNpmCommand(options, spec) {
|
|
605
|
+
if (options.location.prefix === void 0) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const npm = resolveNpmInvocation(
|
|
609
|
+
options.execPath ?? process.execPath,
|
|
610
|
+
options.platform ?? process.platform,
|
|
611
|
+
options.exists ?? existsSync,
|
|
612
|
+
options.realpath ?? ((path) => realpathSync2(path))
|
|
613
|
+
);
|
|
614
|
+
if (npm === void 0) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
file: npm.file,
|
|
619
|
+
args: [
|
|
620
|
+
...npm.leadingArgs,
|
|
621
|
+
"install",
|
|
622
|
+
"--global",
|
|
623
|
+
"--prefix",
|
|
624
|
+
options.location.prefix,
|
|
625
|
+
spec,
|
|
626
|
+
"--registry",
|
|
627
|
+
options.registryUrl,
|
|
628
|
+
"--no-fund",
|
|
629
|
+
"--no-audit",
|
|
630
|
+
"--no-update-notifier",
|
|
631
|
+
"--loglevel=error"
|
|
632
|
+
],
|
|
633
|
+
display: manualInstallCommand("npm-global", spec)
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
function buildInstallCommand(options) {
|
|
637
|
+
const spec = `${options.packageName}@${options.version}`;
|
|
638
|
+
switch (options.location.kind) {
|
|
639
|
+
case "npm-global":
|
|
640
|
+
return buildNpmCommand(options, spec);
|
|
641
|
+
case "pnpm-global":
|
|
642
|
+
return { file: "pnpm", args: ["add", "--global", spec], display: manualInstallCommand("pnpm-global", spec) };
|
|
643
|
+
case "yarn-global":
|
|
644
|
+
return { file: "yarn", args: ["global", "add", spec], display: manualInstallCommand("yarn-global", spec) };
|
|
645
|
+
case "bun-global":
|
|
646
|
+
return { file: "bun", args: ["add", "--global", spec], display: manualInstallCommand("bun-global", spec) };
|
|
647
|
+
case "volta":
|
|
648
|
+
return { file: "volta", args: ["install", spec], display: manualInstallCommand("volta", spec) };
|
|
649
|
+
case "npx":
|
|
650
|
+
case "local":
|
|
651
|
+
case "unknown":
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function lastLine(text) {
|
|
656
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
657
|
+
return lines.at(-1) ?? "";
|
|
658
|
+
}
|
|
659
|
+
function runInstall(command, options) {
|
|
660
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_INSTALL_TIMEOUT_MS;
|
|
661
|
+
const env = { ...options.env, SAPTOOLS_AUTO_UPDATE: "off" };
|
|
662
|
+
delete env["SAP_EMAIL"];
|
|
663
|
+
delete env["SAP_PASSWORD"];
|
|
664
|
+
return new Promise((resolve) => {
|
|
665
|
+
let child;
|
|
666
|
+
try {
|
|
667
|
+
child = options.spawnImpl(command.file, command.args, { cwd: tmpdir(), env, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
668
|
+
} catch (error) {
|
|
669
|
+
resolve({ ok: false, reason: errorMessage(error) });
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
let stderr = "";
|
|
673
|
+
child.stderr?.on("data", (chunk) => {
|
|
674
|
+
stderr = `${stderr}${chunk.toString()}`.slice(-4e3);
|
|
675
|
+
});
|
|
676
|
+
const timer = setTimeout(() => {
|
|
677
|
+
child.kill("SIGKILL");
|
|
678
|
+
}, timeoutMs);
|
|
679
|
+
child.once("error", (error) => {
|
|
680
|
+
clearTimeout(timer);
|
|
681
|
+
resolve({ ok: false, reason: `${command.file}: ${error.message}` });
|
|
682
|
+
});
|
|
683
|
+
child.once("close", (code, signal) => {
|
|
684
|
+
clearTimeout(timer);
|
|
685
|
+
if (code === 0) {
|
|
686
|
+
resolve({ ok: true });
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const detail = lastLine(stderr);
|
|
690
|
+
const suffix = detail.length > 0 ? `: ${detail}` : "";
|
|
691
|
+
resolve({
|
|
692
|
+
ok: false,
|
|
693
|
+
reason: signal === null ? `${command.display} exited with code ${String(code ?? -1)}${suffix}` : `${command.display} was killed by ${signal} after ${String(Math.round(timeoutMs / 1e3))}s`
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// ../core/src/self-update/registry.ts
|
|
700
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
701
|
+
import { join as join5 } from "path";
|
|
702
|
+
|
|
703
|
+
// ../core/src/self-update/semver.ts
|
|
704
|
+
var SEMVER_PATTERN = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
705
|
+
function parseSemver(text) {
|
|
706
|
+
const match = SEMVER_PATTERN.exec(text.trim());
|
|
707
|
+
if (match === null) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const [, major, minor, patch, prerelease] = match;
|
|
711
|
+
if (major === void 0 || minor === void 0 || patch === void 0) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
major: Number(major),
|
|
716
|
+
minor: Number(minor),
|
|
717
|
+
patch: Number(patch),
|
|
718
|
+
prerelease: prerelease === void 0 ? [] : prerelease.split(".")
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function compareIdentifiers(left, right) {
|
|
722
|
+
const leftNumeric = /^\d+$/.test(left);
|
|
723
|
+
const rightNumeric = /^\d+$/.test(right);
|
|
724
|
+
if (leftNumeric && rightNumeric) {
|
|
725
|
+
return Math.sign(Number(left) - Number(right));
|
|
726
|
+
}
|
|
727
|
+
if (leftNumeric) {
|
|
728
|
+
return -1;
|
|
729
|
+
}
|
|
730
|
+
if (rightNumeric) {
|
|
731
|
+
return 1;
|
|
732
|
+
}
|
|
733
|
+
if (left === right) {
|
|
734
|
+
return 0;
|
|
735
|
+
}
|
|
736
|
+
return left < right ? -1 : 1;
|
|
737
|
+
}
|
|
738
|
+
function comparePrerelease(left, right) {
|
|
739
|
+
if (left.length === 0 && right.length === 0) {
|
|
740
|
+
return 0;
|
|
741
|
+
}
|
|
742
|
+
if (left.length === 0) {
|
|
743
|
+
return 1;
|
|
744
|
+
}
|
|
745
|
+
if (right.length === 0) {
|
|
746
|
+
return -1;
|
|
747
|
+
}
|
|
748
|
+
for (const [index, identifier] of left.entries()) {
|
|
749
|
+
const other = right[index];
|
|
750
|
+
if (other === void 0) {
|
|
751
|
+
return 1;
|
|
752
|
+
}
|
|
753
|
+
const result = compareIdentifiers(identifier, other);
|
|
754
|
+
if (result !== 0) {
|
|
755
|
+
return result;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return left.length < right.length ? -1 : 0;
|
|
759
|
+
}
|
|
760
|
+
function compareSemver(left, right) {
|
|
761
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
762
|
+
if (left[key] !== right[key]) {
|
|
763
|
+
return left[key] < right[key] ? -1 : 1;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return comparePrerelease(left.prerelease, right.prerelease);
|
|
767
|
+
}
|
|
768
|
+
function isNewerRelease(candidate, current) {
|
|
769
|
+
const next = parseSemver(candidate);
|
|
770
|
+
const installed = parseSemver(current);
|
|
771
|
+
if (next === void 0 || installed === void 0) {
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
if (next.prerelease.length > 0) {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
return compareSemver(next, installed) > 0;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// ../core/src/self-update/registry.ts
|
|
781
|
+
var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
|
|
782
|
+
var REGISTRY_ENV = "SAPTOOLS_NPM_REGISTRY";
|
|
783
|
+
var DEFAULT_CHECK_TIMEOUT_MS = 2e3;
|
|
784
|
+
function normalizeRegistryUrl(value) {
|
|
785
|
+
if (value === void 0) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
789
|
+
if (!/^https?:\/\/\S+$/i.test(trimmed)) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
return trimmed;
|
|
793
|
+
}
|
|
794
|
+
function registryFromNpmrc(text, key) {
|
|
795
|
+
if (text === void 0) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
for (const line of text.split(/\r?\n/)) {
|
|
799
|
+
const trimmed = line.trim();
|
|
800
|
+
if (trimmed.length === 0 || trimmed.startsWith("#") || trimmed.startsWith(";")) {
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
const separator = trimmed.indexOf("=");
|
|
804
|
+
if (separator === -1 || trimmed.slice(0, separator).trim() !== key) {
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
return trimmed.slice(separator + 1).trim().replace(/^"(.*)"$/, "$1");
|
|
808
|
+
}
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
function readUserNpmrc(homeDirectory) {
|
|
812
|
+
try {
|
|
813
|
+
return readFileSync3(join5(homeDirectory, ".npmrc"), "utf8");
|
|
814
|
+
} catch {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function resolveRegistryUrl(env, userNpmrc) {
|
|
819
|
+
const candidates = [
|
|
820
|
+
env[REGISTRY_ENV],
|
|
821
|
+
env["npm_config_registry"],
|
|
822
|
+
registryFromNpmrc(userNpmrc, "@saptools:registry"),
|
|
823
|
+
registryFromNpmrc(userNpmrc, "registry")
|
|
824
|
+
];
|
|
825
|
+
for (const candidate of candidates) {
|
|
826
|
+
const normalized = normalizeRegistryUrl(candidate);
|
|
827
|
+
if (normalized !== void 0) {
|
|
828
|
+
return normalized;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return DEFAULT_NPM_REGISTRY;
|
|
832
|
+
}
|
|
833
|
+
async function fetchJson(fetchImpl, url, headers, timeoutMs) {
|
|
834
|
+
try {
|
|
835
|
+
const response = await fetchImpl(url, { headers, signal: AbortSignal.timeout(timeoutMs), redirect: "follow" });
|
|
836
|
+
if (!response.ok) {
|
|
837
|
+
return { ok: false, status: response.status, reason: `HTTP ${String(response.status)} from ${url}` };
|
|
838
|
+
}
|
|
839
|
+
const body = await response.json();
|
|
840
|
+
return { ok: true, body };
|
|
841
|
+
} catch (error) {
|
|
842
|
+
return { ok: false, status: void 0, reason: `${errorMessage(error)} (${url})` };
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
function readLatestTag(body, nestedUnderDistTags) {
|
|
846
|
+
if (!isRecord(body)) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const tags = nestedUnderDistTags ? body["dist-tags"] : body;
|
|
850
|
+
if (!isRecord(tags)) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const latest = readString(tags, "latest");
|
|
854
|
+
return latest !== void 0 && parseSemver(latest) !== void 0 ? latest : void 0;
|
|
855
|
+
}
|
|
856
|
+
async function fetchLatestVersion(packageName, registryUrl, options = {}) {
|
|
857
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
858
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS;
|
|
859
|
+
const encoded = encodeURIComponent(packageName);
|
|
860
|
+
const baseHeaders = { "user-agent": options.userAgent ?? "saptools-self-update" };
|
|
861
|
+
const distTags = await fetchJson(fetchImpl, `${registryUrl}/-/package/${encoded}/dist-tags`, { ...baseHeaders, accept: "application/json" }, timeoutMs);
|
|
862
|
+
if (distTags.ok) {
|
|
863
|
+
const latest2 = readLatestTag(distTags.body, false);
|
|
864
|
+
return latest2 === void 0 ? { ok: false, reason: "dist-tags response carries no valid latest version" } : { ok: true, latest: latest2 };
|
|
865
|
+
}
|
|
866
|
+
if (distTags.status === void 0) {
|
|
867
|
+
return { ok: false, reason: distTags.reason };
|
|
868
|
+
}
|
|
869
|
+
const packument = await fetchJson(
|
|
870
|
+
fetchImpl,
|
|
871
|
+
`${registryUrl}/${encoded}`,
|
|
872
|
+
{ ...baseHeaders, accept: "application/vnd.npm.install-v1+json" },
|
|
873
|
+
timeoutMs
|
|
874
|
+
);
|
|
875
|
+
if (!packument.ok) {
|
|
876
|
+
return { ok: false, reason: packument.reason };
|
|
877
|
+
}
|
|
878
|
+
const latest = readLatestTag(packument.body, true);
|
|
879
|
+
return latest === void 0 ? { ok: false, reason: "packument carries no valid latest dist-tag" } : { ok: true, latest };
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// ../core/src/self-update/state.ts
|
|
883
|
+
import { closeSync, openSync, rmSync as rmSync2, statSync, utimesSync, writeSync } from "fs";
|
|
884
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
885
|
+
var EMPTY_UPDATE_STATE = { version: 1 };
|
|
886
|
+
var UPDATES_DIRECTORY_NAME = "updates";
|
|
887
|
+
var DEFAULT_LOCK_STALE_MS = 10 * 6e4;
|
|
888
|
+
var STRING_FIELDS = ["checkedAt", "latest", "lastFailureAt", "lastFailureReason", "notifiedVersion", "notifiedAt"];
|
|
889
|
+
function updateStateFileName(packageName) {
|
|
890
|
+
return `${packageName.replace(/^@/, "").replaceAll("/", "__")}.json`;
|
|
891
|
+
}
|
|
892
|
+
function updateStatePath(saptoolsRoot, packageName) {
|
|
893
|
+
return join6(saptoolsRoot, UPDATES_DIRECTORY_NAME, updateStateFileName(packageName));
|
|
894
|
+
}
|
|
895
|
+
function updateLockPath(statePath) {
|
|
896
|
+
return statePath.replace(/\.json$/, ".lock");
|
|
897
|
+
}
|
|
898
|
+
function readInstallAttempt(value) {
|
|
899
|
+
if (!isRecord(value)) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const version = readString(value, "version");
|
|
903
|
+
const at = readString(value, "at");
|
|
904
|
+
const ok = value["ok"];
|
|
905
|
+
if (version === void 0 || at === void 0 || typeof ok !== "boolean") {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
const reason = readString(value, "reason");
|
|
909
|
+
return reason === void 0 ? { version, at, ok } : { version, at, ok, reason };
|
|
910
|
+
}
|
|
911
|
+
function readUpdateState(path) {
|
|
912
|
+
const parsed = readJsonFileSync(path);
|
|
913
|
+
if (!isRecord(parsed) || parsed["version"] !== 1) {
|
|
914
|
+
return EMPTY_UPDATE_STATE;
|
|
915
|
+
}
|
|
916
|
+
const draft = { version: 1 };
|
|
917
|
+
for (const key of STRING_FIELDS) {
|
|
918
|
+
const value = readString(parsed, key);
|
|
919
|
+
if (value !== void 0) {
|
|
920
|
+
draft[key] = value;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const lastInstall = readInstallAttempt(parsed["lastInstall"]);
|
|
924
|
+
if (lastInstall !== void 0) {
|
|
925
|
+
draft.lastInstall = lastInstall;
|
|
926
|
+
}
|
|
927
|
+
return draft;
|
|
928
|
+
}
|
|
929
|
+
function clearFailure(state) {
|
|
930
|
+
const next = { ...state };
|
|
931
|
+
delete next.lastFailureAt;
|
|
932
|
+
delete next.lastFailureReason;
|
|
933
|
+
return next;
|
|
934
|
+
}
|
|
935
|
+
function writeUpdateState(path, state) {
|
|
936
|
+
ensurePrivateDirectorySync(dirname4(path));
|
|
937
|
+
writeFileAtomicSync(path, `${JSON.stringify(state, null, 2)}
|
|
938
|
+
`, 384);
|
|
939
|
+
}
|
|
940
|
+
function tryCreateLock(lockPath, now) {
|
|
941
|
+
let descriptor;
|
|
942
|
+
try {
|
|
943
|
+
descriptor = openSync(lockPath, "wx", 384);
|
|
944
|
+
} catch {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
writeSync(descriptor, `${JSON.stringify({ pid: process.pid, at: now.toISOString() })}
|
|
949
|
+
`);
|
|
950
|
+
} finally {
|
|
951
|
+
closeSync(descriptor);
|
|
952
|
+
}
|
|
953
|
+
utimesSync(lockPath, now, now);
|
|
954
|
+
return {
|
|
955
|
+
release: () => {
|
|
956
|
+
rmSync2(lockPath, { force: true });
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function isStaleLock(lockPath, now, staleMs) {
|
|
961
|
+
try {
|
|
962
|
+
return now.getTime() - statSync(lockPath).mtimeMs > staleMs;
|
|
963
|
+
} catch {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
function acquireUpdateLock(lockPath, now, staleMs = DEFAULT_LOCK_STALE_MS) {
|
|
968
|
+
ensurePrivateDirectorySync(dirname4(lockPath));
|
|
969
|
+
const lock = tryCreateLock(lockPath, now);
|
|
970
|
+
if (lock !== void 0) {
|
|
971
|
+
return lock;
|
|
972
|
+
}
|
|
973
|
+
if (!isStaleLock(lockPath, now, staleMs)) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
rmSync2(lockPath, { force: true });
|
|
977
|
+
return tryCreateLock(lockPath, now);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// ../core/src/self-update/run.ts
|
|
981
|
+
var FAILURE_BACKOFF_MS = 15 * 6e4;
|
|
982
|
+
var NOTIFY_INTERVAL_MS = 24 * 60 * 6e4;
|
|
983
|
+
var INSTALL_RETRY_BACKOFF_MS = 24 * 60 * 6e4;
|
|
984
|
+
function defaultRuntime2() {
|
|
985
|
+
return {
|
|
986
|
+
env: process.env,
|
|
987
|
+
argv: process.argv,
|
|
988
|
+
execPath: process.execPath,
|
|
989
|
+
execArgv: process.execArgv,
|
|
990
|
+
platform: process.platform,
|
|
991
|
+
homeDirectory: homedir2(),
|
|
992
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
993
|
+
fetchImpl: fetch,
|
|
994
|
+
spawnImpl: spawn2,
|
|
995
|
+
reexecImpl: reexecProcess,
|
|
996
|
+
checkTimeoutMs: DEFAULT_CHECK_TIMEOUT_MS,
|
|
997
|
+
installTimeoutMs: DEFAULT_INSTALL_TIMEOUT_MS
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
function ageMs(isoTimestamp, nowMs) {
|
|
1001
|
+
const then = Date.parse(isoTimestamp);
|
|
1002
|
+
return Number.isNaN(then) || then > nowMs ? Number.POSITIVE_INFINITY : nowMs - then;
|
|
1003
|
+
}
|
|
1004
|
+
function detectLocation(options, runtime, binPath) {
|
|
1005
|
+
return detectInstallLocation({
|
|
1006
|
+
binPath,
|
|
1007
|
+
packageName: options.packageName,
|
|
1008
|
+
platform: runtime.platform,
|
|
1009
|
+
env: runtime.env,
|
|
1010
|
+
...runtime.realpath === void 0 ? {} : { realpath: runtime.realpath },
|
|
1011
|
+
...runtime.isWritable === void 0 ? {} : { isWritable: runtime.isWritable }
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
function createContext(options, runtime) {
|
|
1015
|
+
const { env } = runtime;
|
|
1016
|
+
const binPath = runtime.argv[1] ?? "";
|
|
1017
|
+
const location = detectLocation(options, runtime, binPath);
|
|
1018
|
+
const decision = resolveUpdatePolicy({
|
|
1019
|
+
env,
|
|
1020
|
+
location,
|
|
1021
|
+
skipCommands: options.skipCommands ?? [],
|
|
1022
|
+
manual: options.manual === true,
|
|
1023
|
+
...options.envPrefix === void 0 ? {} : { envPrefix: options.envPrefix },
|
|
1024
|
+
...options.commandPath === void 0 ? {} : { commandPath: options.commandPath }
|
|
1025
|
+
});
|
|
1026
|
+
const statePath = updateStatePath(resolveSaptoolsRoot(options.saptoolsRoot, env), options.packageName);
|
|
1027
|
+
const notice = options.notice ?? ((line) => {
|
|
1028
|
+
process.stderr.write(`${options.binName}: ${line}
|
|
1029
|
+
`);
|
|
1030
|
+
});
|
|
1031
|
+
const debug = isTruthyFlag(env[DEBUG_ENV]) ? (line) => {
|
|
1032
|
+
process.stderr.write(`${options.binName}: [self-update] ${line}
|
|
1033
|
+
`);
|
|
1034
|
+
} : () => {
|
|
1035
|
+
};
|
|
1036
|
+
return {
|
|
1037
|
+
options,
|
|
1038
|
+
runtime,
|
|
1039
|
+
binPath,
|
|
1040
|
+
location,
|
|
1041
|
+
decision,
|
|
1042
|
+
registryUrl: resolveRegistryUrl(env, readUserNpmrc(runtime.homeDirectory)),
|
|
1043
|
+
statePath,
|
|
1044
|
+
intervalMs: resolveCheckIntervalMs(env),
|
|
1045
|
+
// Loaded lazily in resolveLatest: a disabled run must not touch ~/.saptools at all.
|
|
1046
|
+
state: EMPTY_UPDATE_STATE,
|
|
1047
|
+
notice,
|
|
1048
|
+
debug
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function saveState(ctx) {
|
|
1052
|
+
try {
|
|
1053
|
+
writeUpdateState(ctx.statePath, ctx.state);
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
ctx.debug(`cannot write ${ctx.statePath}: ${errorMessage(error)}`);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async function resolveLatest(ctx) {
|
|
1059
|
+
ctx.state = readUpdateState(ctx.statePath);
|
|
1060
|
+
const { state } = ctx;
|
|
1061
|
+
const nowMs = ctx.runtime.now().getTime();
|
|
1062
|
+
if (ctx.options.manual !== true) {
|
|
1063
|
+
if (state.latest !== void 0 && state.checkedAt !== void 0 && ageMs(state.checkedAt, nowMs) < ctx.intervalMs) {
|
|
1064
|
+
ctx.debug(`using cached latest ${state.latest} (checked ${state.checkedAt})`);
|
|
1065
|
+
return state.latest;
|
|
1066
|
+
}
|
|
1067
|
+
if (state.lastFailureAt !== void 0 && ageMs(state.lastFailureAt, nowMs) < FAILURE_BACKOFF_MS) {
|
|
1068
|
+
ctx.debug(`skipping the registry check after a recent failure: ${state.lastFailureReason ?? "unknown"}`);
|
|
1069
|
+
return state.latest;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
const result = await fetchLatestVersion(ctx.options.packageName, ctx.registryUrl, {
|
|
1073
|
+
fetchImpl: ctx.runtime.fetchImpl,
|
|
1074
|
+
timeoutMs: ctx.runtime.checkTimeoutMs,
|
|
1075
|
+
userAgent: `${ctx.options.binName}/${ctx.options.currentVersion} saptools-self-update`
|
|
1076
|
+
});
|
|
1077
|
+
const at = ctx.runtime.now().toISOString();
|
|
1078
|
+
if (result.ok) {
|
|
1079
|
+
ctx.state = clearFailure({ ...ctx.state, checkedAt: at, latest: result.latest });
|
|
1080
|
+
saveState(ctx);
|
|
1081
|
+
return result.latest;
|
|
1082
|
+
}
|
|
1083
|
+
ctx.state = { ...ctx.state, lastFailureAt: at, lastFailureReason: result.reason };
|
|
1084
|
+
saveState(ctx);
|
|
1085
|
+
ctx.debug(`registry check failed: ${result.reason}`);
|
|
1086
|
+
return ctx.state.latest;
|
|
1087
|
+
}
|
|
1088
|
+
function notify2(ctx, latest, reason) {
|
|
1089
|
+
const nowMs = ctx.runtime.now().getTime();
|
|
1090
|
+
const alreadyAnnounced = ctx.state.notifiedVersion === latest && ctx.state.notifiedAt !== void 0 && ageMs(ctx.state.notifiedAt, nowMs) < NOTIFY_INTERVAL_MS;
|
|
1091
|
+
if (!alreadyAnnounced || ctx.options.manual === true) {
|
|
1092
|
+
const command = manualInstallCommand(ctx.location.kind, `${ctx.options.packageName}@${latest}`);
|
|
1093
|
+
ctx.notice(`${latest} is available (installed ${ctx.options.currentVersion}) but was not installed: ${reason}. Run: ${command}`);
|
|
1094
|
+
ctx.state = { ...ctx.state, notifiedVersion: latest, notifiedAt: ctx.runtime.now().toISOString() };
|
|
1095
|
+
saveState(ctx);
|
|
1096
|
+
}
|
|
1097
|
+
return { kind: "notified", latest, reason };
|
|
1098
|
+
}
|
|
1099
|
+
function recentInstallFailure(ctx, latest) {
|
|
1100
|
+
const attempt = ctx.state.lastInstall;
|
|
1101
|
+
if (ctx.options.manual === true || attempt === void 0 || attempt.ok || attempt.version !== latest) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (ageMs(attempt.at, ctx.runtime.now().getTime()) >= INSTALL_RETRY_BACKOFF_MS) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
return `the previous attempt failed (${attempt.reason ?? "unknown reason"})`;
|
|
1108
|
+
}
|
|
1109
|
+
function recordInstall(ctx, latest, ok, reason) {
|
|
1110
|
+
const at = ctx.runtime.now().toISOString();
|
|
1111
|
+
ctx.state = { ...ctx.state, lastInstall: reason === void 0 ? { version: latest, at, ok } : { version: latest, at, ok, reason } };
|
|
1112
|
+
saveState(ctx);
|
|
1113
|
+
}
|
|
1114
|
+
async function installLatest(ctx, latest) {
|
|
1115
|
+
const command = buildInstallCommand({
|
|
1116
|
+
location: ctx.location,
|
|
1117
|
+
packageName: ctx.options.packageName,
|
|
1118
|
+
version: latest,
|
|
1119
|
+
registryUrl: ctx.registryUrl,
|
|
1120
|
+
execPath: ctx.runtime.execPath,
|
|
1121
|
+
platform: ctx.runtime.platform,
|
|
1122
|
+
...ctx.runtime.exists === void 0 ? {} : { exists: ctx.runtime.exists },
|
|
1123
|
+
...ctx.runtime.realpath === void 0 ? {} : { realpath: ctx.runtime.realpath }
|
|
1124
|
+
});
|
|
1125
|
+
if (command === void 0) {
|
|
1126
|
+
return notify2(ctx, latest, "no supported package manager was found for this install");
|
|
1127
|
+
}
|
|
1128
|
+
const lock = acquireUpdateLock(updateLockPath(ctx.statePath), ctx.runtime.now());
|
|
1129
|
+
if (lock === void 0) {
|
|
1130
|
+
ctx.debug("another process holds the update lock");
|
|
1131
|
+
return { kind: "skipped", reason: "another process is installing the update" };
|
|
1132
|
+
}
|
|
1133
|
+
try {
|
|
1134
|
+
ctx.notice(`updating ${ctx.options.currentVersion} -> ${latest} ...`);
|
|
1135
|
+
const result = await runInstall(command, { env: ctx.runtime.env, timeoutMs: ctx.runtime.installTimeoutMs, spawnImpl: ctx.runtime.spawnImpl });
|
|
1136
|
+
const installed = ctx.location.packageDirectory === void 0 ? void 0 : readPackageManifest(ctx.location.packageDirectory)?.version;
|
|
1137
|
+
const failure = result.ok ? installed === latest ? void 0 : `the installed version is ${installed ?? "unreadable"}, not ${latest}` : result.reason;
|
|
1138
|
+
if (failure !== void 0) {
|
|
1139
|
+
recordInstall(ctx, latest, false, failure);
|
|
1140
|
+
ctx.notice(`update to ${latest} failed (${failure}); continuing with ${ctx.options.currentVersion}. Run: ${command.display}`);
|
|
1141
|
+
return { kind: "failed", latest, reason: failure };
|
|
1142
|
+
}
|
|
1143
|
+
recordInstall(ctx, latest, true);
|
|
1144
|
+
} finally {
|
|
1145
|
+
lock.release();
|
|
1146
|
+
}
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
async function upgrade(ctx, latest) {
|
|
1150
|
+
const failed = await installLatest(ctx, latest);
|
|
1151
|
+
if (failed !== void 0) {
|
|
1152
|
+
return failed;
|
|
1153
|
+
}
|
|
1154
|
+
const outcome = { kind: "updated", from: ctx.options.currentVersion, to: latest };
|
|
1155
|
+
if (ctx.options.reexec === false) {
|
|
1156
|
+
ctx.notice(`updated to ${latest}`);
|
|
1157
|
+
return outcome;
|
|
1158
|
+
}
|
|
1159
|
+
ctx.notice(`updated to ${latest}; re-running the command`);
|
|
1160
|
+
try {
|
|
1161
|
+
await ctx.runtime.reexecImpl({
|
|
1162
|
+
execPath: ctx.runtime.execPath,
|
|
1163
|
+
execArgv: ctx.runtime.execArgv,
|
|
1164
|
+
binPath: ctx.binPath,
|
|
1165
|
+
args: ctx.runtime.argv.slice(2),
|
|
1166
|
+
env: ctx.runtime.env
|
|
1167
|
+
});
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
ctx.notice(`could not re-run on ${latest} (${errorMessage(error)}); continuing with the already loaded ${ctx.options.currentVersion}`);
|
|
1170
|
+
}
|
|
1171
|
+
return outcome;
|
|
1172
|
+
}
|
|
1173
|
+
async function runSelfUpdateUnsafe(options, runtime) {
|
|
1174
|
+
const ctx = createContext(options, runtime);
|
|
1175
|
+
if (ctx.decision.policy === "off") {
|
|
1176
|
+
ctx.debug(`off: ${ctx.decision.reason}`);
|
|
1177
|
+
return { kind: "skipped", reason: ctx.decision.reason };
|
|
1178
|
+
}
|
|
1179
|
+
const latest = await resolveLatest(ctx);
|
|
1180
|
+
if (latest === void 0) {
|
|
1181
|
+
return { kind: "skipped", reason: "the latest version is unknown" };
|
|
1182
|
+
}
|
|
1183
|
+
if (!isNewerRelease(latest, options.currentVersion)) {
|
|
1184
|
+
return { kind: "current", latest };
|
|
1185
|
+
}
|
|
1186
|
+
if (ctx.decision.policy === "notify") {
|
|
1187
|
+
return notify2(ctx, latest, ctx.decision.reason);
|
|
1188
|
+
}
|
|
1189
|
+
const backoff = recentInstallFailure(ctx, latest);
|
|
1190
|
+
if (backoff !== void 0) {
|
|
1191
|
+
return notify2(ctx, latest, backoff);
|
|
1192
|
+
}
|
|
1193
|
+
return await upgrade(ctx, latest);
|
|
1194
|
+
}
|
|
1195
|
+
async function runSelfUpdate(options, overrides = {}) {
|
|
1196
|
+
const runtime = { ...defaultRuntime2(), ...overrides };
|
|
1197
|
+
try {
|
|
1198
|
+
return await runSelfUpdateUnsafe(options, runtime);
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
const reason = errorMessage(error);
|
|
1201
|
+
if (isTruthyFlag(runtime.env[DEBUG_ENV])) {
|
|
1202
|
+
process.stderr.write(`${options.binName}: [self-update] ${reason}
|
|
1203
|
+
`);
|
|
1204
|
+
}
|
|
1205
|
+
return { kind: "skipped", reason };
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
async function inspectSelfUpdate(options, overrides = {}) {
|
|
1209
|
+
const runtime = { ...defaultRuntime2(), ...overrides };
|
|
1210
|
+
const ctx = createContext({ ...options, manual: true }, runtime);
|
|
1211
|
+
const result = await fetchLatestVersion(options.packageName, ctx.registryUrl, {
|
|
1212
|
+
fetchImpl: runtime.fetchImpl,
|
|
1213
|
+
timeoutMs: runtime.checkTimeoutMs,
|
|
1214
|
+
userAgent: `${options.binName}/${options.currentVersion} saptools-self-update`
|
|
1215
|
+
});
|
|
1216
|
+
return {
|
|
1217
|
+
packageName: options.packageName,
|
|
1218
|
+
installed: options.currentVersion,
|
|
1219
|
+
location: ctx.location,
|
|
1220
|
+
policy: resolveUpdatePolicy({
|
|
1221
|
+
env: runtime.env,
|
|
1222
|
+
location: ctx.location,
|
|
1223
|
+
...options.envPrefix === void 0 ? {} : { envPrefix: options.envPrefix }
|
|
1224
|
+
}),
|
|
1225
|
+
registryUrl: ctx.registryUrl,
|
|
1226
|
+
statePath: ctx.statePath,
|
|
1227
|
+
latest: result.ok ? result.latest : void 0,
|
|
1228
|
+
checkError: result.ok ? void 0 : result.reason
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// ../core/src/self-update/commander.ts
|
|
1233
|
+
function commandPathOf(command) {
|
|
1234
|
+
const names = [];
|
|
1235
|
+
let current = command;
|
|
1236
|
+
while (current.parent !== null) {
|
|
1237
|
+
names.unshift(current.name());
|
|
1238
|
+
current = current.parent;
|
|
1239
|
+
}
|
|
1240
|
+
return names.join(" ");
|
|
1241
|
+
}
|
|
1242
|
+
function attachSelfUpdate(program, options) {
|
|
1243
|
+
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
1244
|
+
await runSelfUpdate({ ...options, commandPath: commandPathOf(actionCommand) });
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
function formatSelfUpdateStatus(status) {
|
|
1248
|
+
const latest = status.latest ?? (status.checkError === void 0 ? "unknown" : `unknown (${status.checkError})`);
|
|
1249
|
+
const install = status.location.packageDirectory === void 0 ? status.location.detail : `${status.location.kind} ${status.location.packageDirectory}`;
|
|
1250
|
+
return [
|
|
1251
|
+
`package=${status.packageName}`,
|
|
1252
|
+
`installed=${status.installed}`,
|
|
1253
|
+
`latest=${latest}`,
|
|
1254
|
+
`policy=${status.policy.policy} (${status.policy.reason})`,
|
|
1255
|
+
`install=${install}`,
|
|
1256
|
+
`writable=${status.location.writable ? "yes" : "no"}`,
|
|
1257
|
+
`registry=${status.registryUrl}`,
|
|
1258
|
+
`state=${status.statePath}`
|
|
1259
|
+
];
|
|
1260
|
+
}
|
|
1261
|
+
function describeOutcome(outcome) {
|
|
1262
|
+
switch (outcome.kind) {
|
|
1263
|
+
case "current":
|
|
1264
|
+
return `current (${outcome.latest} is the newest release)`;
|
|
1265
|
+
case "updated":
|
|
1266
|
+
return `updated ${outcome.from} -> ${outcome.to}`;
|
|
1267
|
+
case "notified":
|
|
1268
|
+
return `not installed (${outcome.reason})`;
|
|
1269
|
+
case "failed":
|
|
1270
|
+
return `failed (${outcome.reason})`;
|
|
1271
|
+
case "skipped":
|
|
1272
|
+
return `skipped (${outcome.reason})`;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
async function runSelfUpdateCommand(options, checkOnly) {
|
|
1276
|
+
const print = options.print ?? ((line) => {
|
|
1277
|
+
process.stdout.write(`${line}
|
|
1278
|
+
`);
|
|
1279
|
+
});
|
|
1280
|
+
const status = await inspectSelfUpdate(options);
|
|
1281
|
+
for (const line of formatSelfUpdateStatus(status)) {
|
|
1282
|
+
print(line);
|
|
1283
|
+
}
|
|
1284
|
+
if (checkOnly) {
|
|
1285
|
+
const verdict = status.latest === void 0 ? "unknown" : isNewerRelease(status.latest, status.installed) ? "update-available" : "current";
|
|
1286
|
+
print(`result=${verdict}`);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
const outcome = await runSelfUpdate({ ...options, manual: true, reexec: false });
|
|
1290
|
+
print(`result=${describeOutcome(outcome)}`);
|
|
1291
|
+
if (outcome.kind === "failed") {
|
|
1292
|
+
process.exitCode = 1;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function registerSelfUpdateCommand(program, options) {
|
|
1296
|
+
program.command(SELF_UPDATE_COMMAND).description("check npm for a newer release and install it now; every other command does this automatically (at most once an hour)").option("--check", "report whether a newer release exists without installing it").action(async (flags) => {
|
|
1297
|
+
await runSelfUpdateCommand(options, flags.check === true);
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// src/cli/program.ts
|
|
161
1302
|
import { Command } from "commander";
|
|
162
1303
|
|
|
163
1304
|
// src/cli/commands/attach.ts
|
|
@@ -169,7 +1310,7 @@ import { request } from "http";
|
|
|
169
1310
|
import { performance } from "perf_hooks";
|
|
170
1311
|
var InvalidDiscoveryPayloadError = class extends CfInspectorError {
|
|
171
1312
|
};
|
|
172
|
-
async function
|
|
1313
|
+
async function fetchJson2(url, timeoutMs) {
|
|
173
1314
|
const deadline = performance.now() + timeoutMs;
|
|
174
1315
|
let lastError;
|
|
175
1316
|
while (performance.now() < deadline) {
|
|
@@ -296,7 +1437,7 @@ function toInspectorTarget(value, source) {
|
|
|
296
1437
|
}
|
|
297
1438
|
async function discoverInspectorTargets(host, port, timeoutMs) {
|
|
298
1439
|
const url = `http://${host}:${port.toString()}/json/list`;
|
|
299
|
-
const raw = await
|
|
1440
|
+
const raw = await fetchJson2(url, timeoutMs);
|
|
300
1441
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
301
1442
|
throw new CfInspectorError(
|
|
302
1443
|
"INSPECTOR_DISCOVERY_FAILED",
|
|
@@ -316,7 +1457,7 @@ function readVersionField(value, ...keys) {
|
|
|
316
1457
|
}
|
|
317
1458
|
async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
318
1459
|
const url = `http://${host}:${port.toString()}/json/version`;
|
|
319
|
-
const raw = await
|
|
1460
|
+
const raw = await fetchJson2(url, timeoutMs);
|
|
320
1461
|
if (typeof raw !== "object" || raw === null) {
|
|
321
1462
|
throw new CfInspectorError(
|
|
322
1463
|
"INSPECTOR_DISCOVERY_FAILED",
|
|
@@ -1019,7 +2160,7 @@ function asString(value, fallback = "") {
|
|
|
1019
2160
|
function asNumber(value, fallback = 0) {
|
|
1020
2161
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
1021
2162
|
}
|
|
1022
|
-
function
|
|
2163
|
+
function isRecord2(value) {
|
|
1023
2164
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1024
2165
|
}
|
|
1025
2166
|
function nonEmptyString(value) {
|
|
@@ -1036,7 +2177,7 @@ function optionalBoolean(value) {
|
|
|
1036
2177
|
return typeof value === "boolean" ? value : void 0;
|
|
1037
2178
|
}
|
|
1038
2179
|
function toScriptLocation(value) {
|
|
1039
|
-
if (!
|
|
2180
|
+
if (!isRecord2(value)) {
|
|
1040
2181
|
return void 0;
|
|
1041
2182
|
}
|
|
1042
2183
|
const scriptId = nonEmptyString(value["scriptId"]);
|
|
@@ -1057,7 +2198,7 @@ function toResolvedLocations(value) {
|
|
|
1057
2198
|
}
|
|
1058
2199
|
return value.flatMap((entry) => {
|
|
1059
2200
|
const location = toScriptLocation(entry);
|
|
1060
|
-
if (location === void 0 || !
|
|
2201
|
+
if (location === void 0 || !isRecord2(entry)) {
|
|
1061
2202
|
return [];
|
|
1062
2203
|
}
|
|
1063
2204
|
const url = typeof entry["url"] === "string" ? entry["url"] : void 0;
|
|
@@ -1070,7 +2211,7 @@ function toBreakLocations(value) {
|
|
|
1070
2211
|
}
|
|
1071
2212
|
return value.flatMap((entry) => {
|
|
1072
2213
|
const location = toScriptLocation(entry);
|
|
1073
|
-
if (location === void 0 || !
|
|
2214
|
+
if (location === void 0 || !isRecord2(entry)) {
|
|
1074
2215
|
return [];
|
|
1075
2216
|
}
|
|
1076
2217
|
const type = nonEmptyString(entry["type"]);
|
|
@@ -1087,7 +2228,7 @@ function optionalOwnField(key, value) {
|
|
|
1087
2228
|
return Object.hasOwn(value, key) ? { [key]: value[key] } : {};
|
|
1088
2229
|
}
|
|
1089
2230
|
function toRemoteObject(value) {
|
|
1090
|
-
if (!
|
|
2231
|
+
if (!isRecord2(value)) {
|
|
1091
2232
|
return void 0;
|
|
1092
2233
|
}
|
|
1093
2234
|
const type = nonEmptyString(value["type"]);
|
|
@@ -1114,7 +2255,7 @@ function optionalTextField(key, value) {
|
|
|
1114
2255
|
return typeof value === "string" ? { [key]: value } : {};
|
|
1115
2256
|
}
|
|
1116
2257
|
function toScope(value) {
|
|
1117
|
-
if (!
|
|
2258
|
+
if (!isRecord2(value)) {
|
|
1118
2259
|
return void 0;
|
|
1119
2260
|
}
|
|
1120
2261
|
const type = nonEmptyString(value["type"]);
|
|
@@ -1162,7 +2303,7 @@ function toCallFrameMetadata(candidate, location, scripts) {
|
|
|
1162
2303
|
};
|
|
1163
2304
|
}
|
|
1164
2305
|
function toCallFrame(value, scripts) {
|
|
1165
|
-
if (!
|
|
2306
|
+
if (!isRecord2(value)) {
|
|
1166
2307
|
return void 0;
|
|
1167
2308
|
}
|
|
1168
2309
|
const callFrameId = nonEmptyString(value["callFrameId"]);
|
|
@@ -1186,7 +2327,7 @@ function toCallFrames(value, scripts) {
|
|
|
1186
2327
|
}) : [];
|
|
1187
2328
|
}
|
|
1188
2329
|
function toStackTraceId(value) {
|
|
1189
|
-
if (!
|
|
2330
|
+
if (!isRecord2(value)) {
|
|
1190
2331
|
return void 0;
|
|
1191
2332
|
}
|
|
1192
2333
|
const id = nonEmptyString(value["id"]);
|
|
@@ -1197,7 +2338,7 @@ function toStackTraceId(value) {
|
|
|
1197
2338
|
return debuggerId === void 0 ? { id } : { id, debuggerId };
|
|
1198
2339
|
}
|
|
1199
2340
|
function toStackTraceFrame(value) {
|
|
1200
|
-
if (!
|
|
2341
|
+
if (!isRecord2(value)) {
|
|
1201
2342
|
return void 0;
|
|
1202
2343
|
}
|
|
1203
2344
|
const scriptId = nonEmptyString(value["scriptId"]);
|
|
@@ -1213,7 +2354,7 @@ function toStackTraceFrame(value) {
|
|
|
1213
2354
|
};
|
|
1214
2355
|
}
|
|
1215
2356
|
function toStackTrace(value) {
|
|
1216
|
-
if (!
|
|
2357
|
+
if (!isRecord2(value) || !Array.isArray(value["callFrames"])) {
|
|
1217
2358
|
return void 0;
|
|
1218
2359
|
}
|
|
1219
2360
|
const callFrames = value["callFrames"].flatMap((entry) => {
|
|
@@ -1231,7 +2372,7 @@ function toStackTrace(value) {
|
|
|
1231
2372
|
};
|
|
1232
2373
|
}
|
|
1233
2374
|
function toPauseEvent(value, receivedAtMs, scripts) {
|
|
1234
|
-
const params =
|
|
2375
|
+
const params = isRecord2(value) ? value : {};
|
|
1235
2376
|
const asyncStackTrace = toStackTrace(params["asyncStackTrace"]);
|
|
1236
2377
|
const asyncStackTraceId = toStackTraceId(params["asyncStackTraceId"]);
|
|
1237
2378
|
const asyncCallStackTraceId = toStackTraceId(params["asyncCallStackTraceId"]);
|
|
@@ -1247,7 +2388,7 @@ function toPauseEvent(value, receivedAtMs, scripts) {
|
|
|
1247
2388
|
};
|
|
1248
2389
|
}
|
|
1249
2390
|
function toScriptInfo(value) {
|
|
1250
|
-
if (!
|
|
2391
|
+
if (!isRecord2(value)) {
|
|
1251
2392
|
return void 0;
|
|
1252
2393
|
}
|
|
1253
2394
|
const scriptId = nonEmptyString(value["scriptId"]);
|
|
@@ -1789,10 +2930,10 @@ var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
|
1789
2930
|
init_types();
|
|
1790
2931
|
import { execFileSync } from "child_process";
|
|
1791
2932
|
import { createHash, randomUUID } from "crypto";
|
|
1792
|
-
import { constants } from "fs";
|
|
2933
|
+
import { constants as constants3 } from "fs";
|
|
1793
2934
|
import { mkdir, open, readFile, readdir, stat, unlink, writeFile } from "fs/promises";
|
|
1794
|
-
import { homedir } from "os";
|
|
1795
|
-
import { join } from "path";
|
|
2935
|
+
import { homedir as homedir3 } from "os";
|
|
2936
|
+
import { join as join7 } from "path";
|
|
1796
2937
|
var ELECTION_WINDOW_MS = 25;
|
|
1797
2938
|
var LOCK_FILE_SUFFIX = ".lock";
|
|
1798
2939
|
async function acquireDebugSessionLock(target, options = {}) {
|
|
@@ -1804,8 +2945,8 @@ async function acquireDebugSessionLock(target, options = {}) {
|
|
|
1804
2945
|
const targetIdentity = debugTargetIdentity(target);
|
|
1805
2946
|
const key = createHash("sha256").update(targetIdentity).digest("hex");
|
|
1806
2947
|
const lockRoot = options.stateRoot ?? defaultStateRoot();
|
|
1807
|
-
const lockDirectory =
|
|
1808
|
-
const ownPath =
|
|
2948
|
+
const lockDirectory = join7(lockRoot, "cf-inspector", "locks");
|
|
2949
|
+
const ownPath = join7(lockDirectory, `${key}.${pid.toString()}.${token}${LOCK_FILE_SUFFIX}`);
|
|
1809
2950
|
const metadata = {
|
|
1810
2951
|
pid,
|
|
1811
2952
|
...ownerProcessStart === void 0 ? {} : { processStart: ownerProcessStart },
|
|
@@ -1815,7 +2956,7 @@ async function acquireDebugSessionLock(target, options = {}) {
|
|
|
1815
2956
|
target: targetIdentity
|
|
1816
2957
|
};
|
|
1817
2958
|
await mkdir(lockDirectory, { recursive: true, mode: 448 });
|
|
1818
|
-
const handle = await open(ownPath,
|
|
2959
|
+
const handle = await open(ownPath, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
|
|
1819
2960
|
try {
|
|
1820
2961
|
await handle.writeFile(`${JSON.stringify(metadata)}
|
|
1821
2962
|
`, "utf8");
|
|
@@ -1884,7 +3025,7 @@ function debugTargetIdentity(target) {
|
|
|
1884
3025
|
}
|
|
1885
3026
|
function defaultStateRoot() {
|
|
1886
3027
|
const configured = process.env["CF_INSPECTOR_STATE_DIR"]?.trim();
|
|
1887
|
-
return configured === void 0 || configured.length === 0 ?
|
|
3028
|
+
return configured === void 0 || configured.length === 0 ? join7(homedir3(), ".saptools") : configured;
|
|
1888
3029
|
}
|
|
1889
3030
|
async function findLiveContenders(lockDirectory, key, ownPath, isProcessAlive, getProcessStart) {
|
|
1890
3031
|
const prefix = `${key}.`;
|
|
@@ -1894,7 +3035,7 @@ async function findLiveContenders(lockDirectory, key, ownPath, isProcessAlive, g
|
|
|
1894
3035
|
if (!name.startsWith(prefix) || !name.endsWith(LOCK_FILE_SUFFIX)) {
|
|
1895
3036
|
continue;
|
|
1896
3037
|
}
|
|
1897
|
-
const path =
|
|
3038
|
+
const path = join7(lockDirectory, name);
|
|
1898
3039
|
if (path === ownPath) {
|
|
1899
3040
|
continue;
|
|
1900
3041
|
}
|
|
@@ -1925,7 +3066,7 @@ async function findLiveContenders(lockDirectory, key, ownPath, isProcessAlive, g
|
|
|
1925
3066
|
async function readLockMetadata(path) {
|
|
1926
3067
|
try {
|
|
1927
3068
|
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
1928
|
-
if (!
|
|
3069
|
+
if (!isRecord3(parsed)) {
|
|
1929
3070
|
return void 0;
|
|
1930
3071
|
}
|
|
1931
3072
|
const pid = parsed["pid"];
|
|
@@ -2016,7 +3157,7 @@ function normalizeHost(host) {
|
|
|
2016
3157
|
const normalized = host.trim().toLowerCase();
|
|
2017
3158
|
return normalized === "localhost" || normalized === "::1" ? "127.0.0.1" : normalized;
|
|
2018
3159
|
}
|
|
2019
|
-
function
|
|
3160
|
+
function isRecord3(value) {
|
|
2020
3161
|
return typeof value === "object" && value !== null;
|
|
2021
3162
|
}
|
|
2022
3163
|
function isNodeError(error, code) {
|
|
@@ -5404,27 +6545,27 @@ var collectStrings = (value, prev = []) => [
|
|
|
5404
6545
|
value
|
|
5405
6546
|
];
|
|
5406
6547
|
var READY_EVENT_DESCRIPTION = "Emit a versioned breakpoint-armed JSON event on stderr after every current isolate is armed";
|
|
5407
|
-
function
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
const candidate = join2(current, "package.json");
|
|
5411
|
-
try {
|
|
5412
|
-
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
5413
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
5414
|
-
const record = parsed;
|
|
5415
|
-
if (record["name"] === "@saptools/cf-inspector" && typeof record["version"] === "string") {
|
|
5416
|
-
return record["version"];
|
|
5417
|
-
}
|
|
5418
|
-
}
|
|
5419
|
-
} catch {
|
|
5420
|
-
}
|
|
5421
|
-
current = dirname(current);
|
|
6548
|
+
function writeUpdateNotice(argv, line) {
|
|
6549
|
+
if (argv.includes("--emit-ready-event")) {
|
|
6550
|
+
return;
|
|
5422
6551
|
}
|
|
5423
|
-
|
|
6552
|
+
process13.stderr.write(`cf-inspector: ${line}
|
|
6553
|
+
`);
|
|
5424
6554
|
}
|
|
5425
6555
|
async function main(argv) {
|
|
6556
|
+
const { version } = readPackageMetadata(import.meta.url, "@saptools/cf-inspector");
|
|
6557
|
+
const selfUpdate = {
|
|
6558
|
+
packageName: "@saptools/cf-inspector",
|
|
6559
|
+
currentVersion: version,
|
|
6560
|
+
binName: "cf-inspector",
|
|
6561
|
+
envPrefix: "CF_INSPECTOR",
|
|
6562
|
+
notice: (line) => {
|
|
6563
|
+
writeUpdateNotice(argv, line);
|
|
6564
|
+
}
|
|
6565
|
+
};
|
|
5426
6566
|
const program = new Command();
|
|
5427
|
-
program.name("cf-inspector").version(
|
|
6567
|
+
program.name("cf-inspector").version(version).description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
6568
|
+
attachSelfUpdate(program, selfUpdate);
|
|
5428
6569
|
registerSnapshot(program);
|
|
5429
6570
|
registerLog(program);
|
|
5430
6571
|
registerWatch(program);
|
|
@@ -5434,6 +6575,7 @@ async function main(argv) {
|
|
|
5434
6575
|
registerListScripts(program);
|
|
5435
6576
|
registerListTargets(program);
|
|
5436
6577
|
registerAttach(program);
|
|
6578
|
+
registerSelfUpdateCommand(program, selfUpdate);
|
|
5437
6579
|
await program.parseAsync([...argv]);
|
|
5438
6580
|
}
|
|
5439
6581
|
function registerCheckBreakpoint(program) {
|
|
@@ -5517,20 +6659,20 @@ function registerAttach(program) {
|
|
|
5517
6659
|
// src/cli.ts
|
|
5518
6660
|
init_types();
|
|
5519
6661
|
try {
|
|
5520
|
-
await main(
|
|
6662
|
+
await main(process14.argv);
|
|
5521
6663
|
} catch (err) {
|
|
5522
6664
|
if (err instanceof CfInspectorError) {
|
|
5523
|
-
|
|
6665
|
+
process14.stderr.write(`Error [${err.code}]: ${err.message}
|
|
5524
6666
|
`);
|
|
5525
6667
|
if (err.detail !== void 0) {
|
|
5526
|
-
|
|
6668
|
+
process14.stderr.write(` detail: ${err.detail}
|
|
5527
6669
|
`);
|
|
5528
6670
|
}
|
|
5529
|
-
|
|
6671
|
+
process14.exit(1);
|
|
5530
6672
|
}
|
|
5531
6673
|
const message = err instanceof Error ? err.message : String(err);
|
|
5532
|
-
|
|
6674
|
+
process14.stderr.write(`Error: ${message}
|
|
5533
6675
|
`);
|
|
5534
|
-
|
|
6676
|
+
process14.exit(1);
|
|
5535
6677
|
}
|
|
5536
6678
|
//# sourceMappingURL=cli.js.map
|