@walkeros/cli 4.9.0 → 4.10.0-next-1794522704623
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/CHANGELOG.md +88 -0
- package/dist/cli.js +5018 -2795
- package/dist/examples/flow-complete.json +28 -37
- package/dist/examples/flow-complete.md +4 -10
- package/dist/examples/index.d.ts +6 -40
- package/dist/examples/index.js +35 -73
- package/dist/examples/index.js.map +1 -1
- package/dist/index.d.ts +258 -81
- package/dist/index.js +1539 -545
- package/dist/index.js.map +1 -1
- package/dist/validate.js +58 -1
- package/dist/validate.js.map +1 -1
- package/examples/flow-complete.json +28 -37
- package/examples/flow-complete.md +4 -10
- package/package.json +30 -30
package/dist/index.js
CHANGED
|
@@ -241,6 +241,52 @@ async function resolveLocalPackage(packageName, localPath, configDir, logger) {
|
|
|
241
241
|
`Local package path not found: ${localPath} (resolved to ${absolutePath})`
|
|
242
242
|
);
|
|
243
243
|
}
|
|
244
|
+
var ROOT_COPY_EXCLUDED = [".turbo", ".git"];
|
|
245
|
+
function excludedFromCopy(root, file) {
|
|
246
|
+
const relative2 = path4.relative(root, file);
|
|
247
|
+
if (relative2 === "") return false;
|
|
248
|
+
const parts = relative2.split(path4.sep);
|
|
249
|
+
if (ROOT_COPY_EXCLUDED.includes(parts[0])) return true;
|
|
250
|
+
if (parts.includes("node_modules")) return true;
|
|
251
|
+
const name = parts[parts.length - 1];
|
|
252
|
+
return name === ".env" || name.startsWith(".env.");
|
|
253
|
+
}
|
|
254
|
+
async function copyPackageRoot(source, target) {
|
|
255
|
+
await fs3.copy(source, target, {
|
|
256
|
+
dereference: true,
|
|
257
|
+
filter: (file) => !excludedFromCopy(source, file)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function stringLeaves(value, into = []) {
|
|
261
|
+
if (typeof value === "string") into.push(value);
|
|
262
|
+
else if (value && typeof value === "object")
|
|
263
|
+
for (const inner of Object.values(value)) stringLeaves(inner, into);
|
|
264
|
+
return into;
|
|
265
|
+
}
|
|
266
|
+
var TYPE_CONDITIONS = ["types", "typings"];
|
|
267
|
+
function codeExports(value, into = []) {
|
|
268
|
+
if (typeof value === "string") {
|
|
269
|
+
if (!/\.d\.[cm]?ts$/.test(value) && !value.endsWith(".json"))
|
|
270
|
+
into.push(value);
|
|
271
|
+
} else if (value && typeof value === "object") {
|
|
272
|
+
for (const [key, inner] of Object.entries(value))
|
|
273
|
+
if (!TYPE_CONDITIONS.includes(key)) codeExports(inner, into);
|
|
274
|
+
}
|
|
275
|
+
return into;
|
|
276
|
+
}
|
|
277
|
+
function entriesOutsideDist(manifest) {
|
|
278
|
+
if (!manifest || typeof manifest !== "object") return false;
|
|
279
|
+
const fields = [
|
|
280
|
+
...["main", "module", "bin"].flatMap(
|
|
281
|
+
(field) => stringLeaves(Reflect.get(manifest, field))
|
|
282
|
+
),
|
|
283
|
+
...codeExports(Reflect.get(manifest, "exports"))
|
|
284
|
+
];
|
|
285
|
+
return fields.some((entry) => {
|
|
286
|
+
const relative2 = path4.posix.normalize(entry.replace(/^\.\//, ""));
|
|
287
|
+
return relative2 !== "package.json" && relative2 !== "dist" && !relative2.startsWith("dist/");
|
|
288
|
+
});
|
|
289
|
+
}
|
|
244
290
|
async function copyLocalPackage(localPkg, targetDir, logger) {
|
|
245
291
|
const packageDir = path4.join(targetDir, "node_modules", localPkg.name);
|
|
246
292
|
if (localPkg.type === "file") {
|
|
@@ -258,15 +304,7 @@ async function copyLocalPackage(localPkg, targetDir, logger) {
|
|
|
258
304
|
}
|
|
259
305
|
if (localPkg.type === "directory") {
|
|
260
306
|
await fs3.ensureDir(path4.dirname(packageDir));
|
|
261
|
-
|
|
262
|
-
for (const entry of entries) {
|
|
263
|
-
if (!["node_modules", ".turbo", ".git"].includes(entry)) {
|
|
264
|
-
await fs3.copy(
|
|
265
|
-
path4.join(localPkg.absolutePath, entry),
|
|
266
|
-
path4.join(packageDir, entry)
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
307
|
+
await copyPackageRoot(localPkg.absolutePath, packageDir);
|
|
270
308
|
await fs3.writeJson(path4.join(packageDir, "package.json"), {
|
|
271
309
|
name: localPkg.name,
|
|
272
310
|
main: "./index.ts"
|
|
@@ -281,18 +319,11 @@ async function copyLocalPackage(localPkg, targetDir, logger) {
|
|
|
281
319
|
path4.join(localPkg.absolutePath, "package.json"),
|
|
282
320
|
path4.join(packageDir, "package.json")
|
|
283
321
|
);
|
|
284
|
-
|
|
322
|
+
const manifest = await fs3.readJson(path4.join(localPkg.absolutePath, "package.json")).catch(() => void 0);
|
|
323
|
+
if (localPkg.hasDistFolder && !entriesOutsideDist(manifest)) {
|
|
285
324
|
await fs3.copy(localPkg.distPath, path4.join(packageDir, "dist"));
|
|
286
325
|
} else {
|
|
287
|
-
|
|
288
|
-
for (const entry of entries) {
|
|
289
|
-
if (!["node_modules", ".turbo", ".git"].includes(entry)) {
|
|
290
|
-
await fs3.copy(
|
|
291
|
-
path4.join(localPkg.absolutePath, entry),
|
|
292
|
-
path4.join(packageDir, entry)
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
326
|
+
await copyPackageRoot(localPkg.absolutePath, packageDir);
|
|
296
327
|
}
|
|
297
328
|
logger.info(`\u{1F4E6} Using local: ${localPkg.name} from ${localPkg.absolutePath}`);
|
|
298
329
|
return packageDir;
|
|
@@ -350,11 +381,523 @@ async function readStdinToTempFile(label) {
|
|
|
350
381
|
return tmpPath;
|
|
351
382
|
}
|
|
352
383
|
|
|
384
|
+
// src/lib/credentials.ts
|
|
385
|
+
import {
|
|
386
|
+
closeSync as closeSync2,
|
|
387
|
+
openSync as openSync2,
|
|
388
|
+
readFileSync as readFileSync2,
|
|
389
|
+
readdirSync,
|
|
390
|
+
statSync as statSync2,
|
|
391
|
+
unlinkSync as unlinkSync2,
|
|
392
|
+
writeSync as writeSync2
|
|
393
|
+
} from "fs";
|
|
394
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
395
|
+
import { join as join2 } from "path";
|
|
396
|
+
import { z } from "zod";
|
|
397
|
+
|
|
398
|
+
// src/lib/config-file.ts
|
|
399
|
+
import {
|
|
400
|
+
readFileSync,
|
|
401
|
+
mkdirSync,
|
|
402
|
+
unlinkSync,
|
|
403
|
+
renameSync,
|
|
404
|
+
openSync,
|
|
405
|
+
writeSync,
|
|
406
|
+
fsyncSync,
|
|
407
|
+
closeSync,
|
|
408
|
+
statSync
|
|
409
|
+
} from "fs";
|
|
410
|
+
import { randomBytes } from "crypto";
|
|
411
|
+
import { join } from "path";
|
|
412
|
+
import { homedir } from "os";
|
|
413
|
+
function getConfigDir() {
|
|
414
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
415
|
+
const base = xdgConfig || join(homedir(), ".config");
|
|
416
|
+
return join(base, "walkeros");
|
|
417
|
+
}
|
|
418
|
+
function getConfigPath() {
|
|
419
|
+
return join(getConfigDir(), "config.json");
|
|
420
|
+
}
|
|
421
|
+
function readConfig() {
|
|
422
|
+
const configPath = getConfigPath();
|
|
423
|
+
try {
|
|
424
|
+
const content = readFileSync(configPath, "utf-8");
|
|
425
|
+
return JSON.parse(content);
|
|
426
|
+
} catch {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function ensureConfigDir() {
|
|
431
|
+
const dir = getConfigDir();
|
|
432
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
433
|
+
return dir;
|
|
434
|
+
}
|
|
435
|
+
function writePrivateFile(path23, content) {
|
|
436
|
+
const tempPath = `${path23}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
437
|
+
try {
|
|
438
|
+
const fd = openSync(tempPath, "wx", 384);
|
|
439
|
+
try {
|
|
440
|
+
writeSync(fd, content);
|
|
441
|
+
fsyncSync(fd);
|
|
442
|
+
} finally {
|
|
443
|
+
closeSync(fd);
|
|
444
|
+
}
|
|
445
|
+
renameSync(tempPath, path23);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
try {
|
|
448
|
+
unlinkSync(tempPath);
|
|
449
|
+
} catch {
|
|
450
|
+
}
|
|
451
|
+
throw error;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function replaceConfigFile(config) {
|
|
455
|
+
ensureConfigDir();
|
|
456
|
+
writePrivateFile(getConfigPath(), JSON.stringify(config, null, 2));
|
|
457
|
+
}
|
|
458
|
+
function writeConfig(config) {
|
|
459
|
+
replaceConfigFile({ ...readConfig() ?? {}, ...config });
|
|
460
|
+
}
|
|
461
|
+
function writeTelemetryOnlyConfig(partial) {
|
|
462
|
+
writeConfig(partial);
|
|
463
|
+
}
|
|
464
|
+
function resolveAppUrl() {
|
|
465
|
+
const envUrl = process.env.WALKEROS_APP_URL;
|
|
466
|
+
if (envUrl) return envUrl;
|
|
467
|
+
const config = readConfig();
|
|
468
|
+
if (config?.appUrl) return config.appUrl;
|
|
469
|
+
return "https://app.walkeros.io";
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/lib/credentials.ts
|
|
473
|
+
var LoginSchema = z.object({
|
|
474
|
+
issuer: z.string(),
|
|
475
|
+
client_id: z.string(),
|
|
476
|
+
token_endpoint: z.string(),
|
|
477
|
+
revocation_endpoint: z.string(),
|
|
478
|
+
access_token: z.string(),
|
|
479
|
+
/** Epoch milliseconds. */
|
|
480
|
+
expires_at: z.number(),
|
|
481
|
+
refresh_token: z.string().optional(),
|
|
482
|
+
scope: z.string().optional()
|
|
483
|
+
});
|
|
484
|
+
var CredentialsSchema = z.record(z.string(), z.unknown());
|
|
485
|
+
function getCredentialsPath() {
|
|
486
|
+
return join2(getConfigDir(), "credentials.json");
|
|
487
|
+
}
|
|
488
|
+
function getLockPath() {
|
|
489
|
+
return join2(getConfigDir(), "credentials.lock");
|
|
490
|
+
}
|
|
491
|
+
function readCredentials() {
|
|
492
|
+
let raw;
|
|
493
|
+
try {
|
|
494
|
+
raw = JSON.parse(readFileSync2(getCredentialsPath(), "utf-8"));
|
|
495
|
+
} catch {
|
|
496
|
+
return {};
|
|
497
|
+
}
|
|
498
|
+
const parsed = CredentialsSchema.safeParse(raw);
|
|
499
|
+
if (!parsed.success) return {};
|
|
500
|
+
const logins = {};
|
|
501
|
+
for (const [issuer, value] of Object.entries(parsed.data)) {
|
|
502
|
+
const login = LoginSchema.safeParse(value);
|
|
503
|
+
if (login.success && login.data.issuer === issuer)
|
|
504
|
+
logins[issuer] = login.data;
|
|
505
|
+
}
|
|
506
|
+
return logins;
|
|
507
|
+
}
|
|
508
|
+
function writeCredentials(logins) {
|
|
509
|
+
ensureConfigDir();
|
|
510
|
+
writePrivateFile(getCredentialsPath(), JSON.stringify(logins, null, 2));
|
|
511
|
+
}
|
|
512
|
+
function originOf(url) {
|
|
513
|
+
try {
|
|
514
|
+
return new URL(url).origin;
|
|
515
|
+
} catch {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function loginForAppUrl(logins, appUrl) {
|
|
520
|
+
const origin = originOf(appUrl);
|
|
521
|
+
if (origin === null) return void 0;
|
|
522
|
+
const exact = logins[appUrl.replace(/\/+$/, "")];
|
|
523
|
+
if (exact) return exact;
|
|
524
|
+
return Object.values(logins).find(
|
|
525
|
+
(login) => originOf(login.issuer) === origin
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
var CredentialsBusyError = class extends Error {
|
|
529
|
+
};
|
|
530
|
+
var LOCK_TIMING = {
|
|
531
|
+
waitMs: 15e3,
|
|
532
|
+
staleMs: 3e4,
|
|
533
|
+
pollMs: 100
|
|
534
|
+
};
|
|
535
|
+
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
536
|
+
function isErrnoCode(error, code) {
|
|
537
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
538
|
+
}
|
|
539
|
+
function tryCreateLock(path23, mark) {
|
|
540
|
+
let fd;
|
|
541
|
+
try {
|
|
542
|
+
fd = openSync2(path23, "wx", 384);
|
|
543
|
+
} catch (error) {
|
|
544
|
+
if (isErrnoCode(error, "EEXIST")) return false;
|
|
545
|
+
throw error;
|
|
546
|
+
}
|
|
547
|
+
try {
|
|
548
|
+
writeSync2(fd, mark);
|
|
549
|
+
} finally {
|
|
550
|
+
closeSync2(fd);
|
|
551
|
+
}
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
function readLock(path23) {
|
|
555
|
+
try {
|
|
556
|
+
const ageMs = Date.now() - statSync2(path23).mtimeMs;
|
|
557
|
+
return { mark: readFileSync2(path23, "utf-8"), ageMs };
|
|
558
|
+
} catch {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async function withCredentialsLock(fn, timing = LOCK_TIMING) {
|
|
563
|
+
const dir = ensureConfigDir();
|
|
564
|
+
const path23 = getLockPath();
|
|
565
|
+
const mark = `${process.pid} ${(/* @__PURE__ */ new Date()).toISOString()} ${randomBytes2(8).toString("hex")}`;
|
|
566
|
+
const deadline = Date.now() + timing.waitMs;
|
|
567
|
+
for (; ; ) {
|
|
568
|
+
if (tryCreateLock(path23, mark)) break;
|
|
569
|
+
const held = readLock(path23);
|
|
570
|
+
if (held && held.ageMs > timing.staleMs) {
|
|
571
|
+
const again = readLock(path23);
|
|
572
|
+
if (again && again.mark === held.mark) {
|
|
573
|
+
try {
|
|
574
|
+
unlinkSync2(path23);
|
|
575
|
+
} catch {
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (Date.now() >= deadline)
|
|
581
|
+
throw new CredentialsBusyError(
|
|
582
|
+
`another walkeros command is updating the sign-in in ${dir}: try again`
|
|
583
|
+
);
|
|
584
|
+
await sleep(timing.pollMs);
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
return await fn();
|
|
588
|
+
} finally {
|
|
589
|
+
const held = readLock(path23);
|
|
590
|
+
if (held && held.mark === mark) {
|
|
591
|
+
try {
|
|
592
|
+
unlinkSync2(path23);
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
var PENDING_TTL_MS = 15 * 60 * 1e3;
|
|
599
|
+
var STATE_SHAPE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
600
|
+
var PendingSchema = z.object({
|
|
601
|
+
code_verifier: z.string(),
|
|
602
|
+
state: z.string().regex(STATE_SHAPE),
|
|
603
|
+
redirect_uri: z.string(),
|
|
604
|
+
issuer: z.string(),
|
|
605
|
+
client_id: z.string(),
|
|
606
|
+
token_endpoint: z.string(),
|
|
607
|
+
/** Epoch milliseconds. */
|
|
608
|
+
created_at: z.number()
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// src/core/oauth-login.ts
|
|
612
|
+
import { z as z3 } from "zod";
|
|
613
|
+
|
|
614
|
+
// src/core/app-fetch.ts
|
|
615
|
+
import { z as z2 } from "zod";
|
|
616
|
+
|
|
617
|
+
// src/lib/secure-url.ts
|
|
618
|
+
function isLoopback(hostname) {
|
|
619
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
620
|
+
}
|
|
621
|
+
function requireSecureUrl(url) {
|
|
622
|
+
let parsed;
|
|
623
|
+
try {
|
|
624
|
+
parsed = new URL(url);
|
|
625
|
+
} catch {
|
|
626
|
+
return url;
|
|
627
|
+
}
|
|
628
|
+
if (parsed.protocol !== "http:" || isLoopback(parsed.hostname)) return url;
|
|
629
|
+
throw new Error(
|
|
630
|
+
`Refusing to send walkerOS credentials over plain http to ${url}. Use https, or localhost / 127.0.0.1 / [::1] for local development.`
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
function isSecureSignInUrl(url) {
|
|
634
|
+
let parsed;
|
|
635
|
+
try {
|
|
636
|
+
parsed = new URL(url);
|
|
637
|
+
} catch {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
if (parsed.protocol === "https:") return true;
|
|
641
|
+
return parsed.protocol === "http:" && isLoopback(parsed.hostname);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/core/client-context.ts
|
|
645
|
+
var context;
|
|
646
|
+
function setClientContext(input) {
|
|
647
|
+
const envType = process.env.WALKEROS_CLIENT_TYPE;
|
|
648
|
+
const type = envType ?? input.type ?? "cli";
|
|
649
|
+
context = { type, version: input.version };
|
|
650
|
+
}
|
|
651
|
+
function getClientContext() {
|
|
652
|
+
return context;
|
|
653
|
+
}
|
|
654
|
+
function resetClientContext() {
|
|
655
|
+
context = void 0;
|
|
656
|
+
}
|
|
657
|
+
function clientContextHeaders() {
|
|
658
|
+
if (!context) return {};
|
|
659
|
+
return {
|
|
660
|
+
"User-Agent": `walkeros-${context.type}/${context.version}`,
|
|
661
|
+
"X-WalkerOS-Client": context.type,
|
|
662
|
+
"X-WalkerOS-Client-Version": context.version
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// src/core/app-fetch.ts
|
|
667
|
+
var AppErrorSchema = z2.object({
|
|
668
|
+
error: z2.object({ message: z2.string().min(1) })
|
|
669
|
+
});
|
|
670
|
+
function appErrorMessage(body) {
|
|
671
|
+
const parsed = AppErrorSchema.safeParse(body);
|
|
672
|
+
return parsed.success ? parsed.data.error.message : void 0;
|
|
673
|
+
}
|
|
674
|
+
async function appFetch(path23, init = {}) {
|
|
675
|
+
const url = `${(init.baseUrl ?? resolveAppUrl()).replace(/\/+$/, "")}${path23}`;
|
|
676
|
+
const headers = new Headers(clientContextHeaders());
|
|
677
|
+
for (const [name, value] of Object.entries(init.headers ?? {}))
|
|
678
|
+
headers.set(name, value);
|
|
679
|
+
if (init.token && !headers.has("Authorization"))
|
|
680
|
+
headers.set("Authorization", `Bearer ${init.token}`);
|
|
681
|
+
if (headers.has("Authorization")) requireSecureUrl(url);
|
|
682
|
+
const request = { headers };
|
|
683
|
+
if (init.method && init.method !== "GET") request.method = init.method;
|
|
684
|
+
if (init.body !== void 0) request.body = init.body;
|
|
685
|
+
if (init.signal) request.signal = init.signal;
|
|
686
|
+
if (init.redirect) request.redirect = init.redirect;
|
|
687
|
+
return (init.fetch ?? fetch)(url, request);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// src/core/app-paths.ts
|
|
691
|
+
var APP_PATHS = {
|
|
692
|
+
whoami: "/api/auth/whoami",
|
|
693
|
+
health: "/api/health",
|
|
694
|
+
telemetry: "/api/telemetry",
|
|
695
|
+
openapi: "/api/openapi.json",
|
|
696
|
+
authorizationServer: "/.well-known/oauth-authorization-server",
|
|
697
|
+
cliClient: "/oauth/walkeros-cli"
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
// src/core/oauth-login.ts
|
|
701
|
+
var REQUEST_TIMEOUT_MS = 1e4;
|
|
702
|
+
var FRESH_FOR_MS = 6e4;
|
|
703
|
+
var LoginError = class extends Error {
|
|
704
|
+
};
|
|
705
|
+
var MetadataSchema = z3.object({
|
|
706
|
+
issuer: z3.string(),
|
|
707
|
+
authorization_endpoint: z3.string(),
|
|
708
|
+
token_endpoint: z3.string(),
|
|
709
|
+
revocation_endpoint: z3.string(),
|
|
710
|
+
code_challenge_methods_supported: z3.array(z3.string()).optional()
|
|
711
|
+
});
|
|
712
|
+
var ClientDocumentSchema = z3.object({
|
|
713
|
+
client_id: z3.string(),
|
|
714
|
+
redirect_uris: z3.array(z3.string())
|
|
715
|
+
});
|
|
716
|
+
var TokenSetSchema = z3.object({
|
|
717
|
+
access_token: z3.string().min(1),
|
|
718
|
+
token_type: z3.string().refine((type) => type.toLowerCase() === "bearer"),
|
|
719
|
+
expires_in: z3.number(),
|
|
720
|
+
refresh_token: z3.string().min(1).optional(),
|
|
721
|
+
scope: z3.string().optional()
|
|
722
|
+
});
|
|
723
|
+
var OAuthErrorSchema = z3.object({ error: z3.string() });
|
|
724
|
+
async function postForm(url, form, expectTokens) {
|
|
725
|
+
if (!isSecureSignInUrl(url))
|
|
726
|
+
throw new LoginError(
|
|
727
|
+
`refusing to send a walkerOS sign-in to ${url}: it is neither https nor http on this machine: run walkeros login`
|
|
728
|
+
);
|
|
729
|
+
let response;
|
|
730
|
+
try {
|
|
731
|
+
response = await fetch(url, {
|
|
732
|
+
method: "POST",
|
|
733
|
+
headers: {
|
|
734
|
+
...clientContextHeaders(),
|
|
735
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
736
|
+
Accept: "application/json"
|
|
737
|
+
},
|
|
738
|
+
body: new URLSearchParams(form).toString(),
|
|
739
|
+
redirect: "error",
|
|
740
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
741
|
+
});
|
|
742
|
+
} catch (error2) {
|
|
743
|
+
return {
|
|
744
|
+
kind: "unreachable",
|
|
745
|
+
reason: fetchFailureReason(error2, REQUEST_TIMEOUT_MS)
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
let body;
|
|
749
|
+
try {
|
|
750
|
+
body = await response.json();
|
|
751
|
+
} catch {
|
|
752
|
+
body = void 0;
|
|
753
|
+
}
|
|
754
|
+
if (response.ok) {
|
|
755
|
+
if (!expectTokens) return { kind: "ok" };
|
|
756
|
+
const tokens = TokenSetSchema.safeParse(body);
|
|
757
|
+
return tokens.success ? { kind: "ok", tokens: tokens.data } : { kind: "failed", status: response.status };
|
|
758
|
+
}
|
|
759
|
+
const error = OAuthErrorSchema.safeParse(body);
|
|
760
|
+
return error.success ? { kind: "refused", status: response.status, error: error.data.error } : { kind: "failed", status: response.status };
|
|
761
|
+
}
|
|
762
|
+
function answerText(answer) {
|
|
763
|
+
switch (answer.kind) {
|
|
764
|
+
case "ok":
|
|
765
|
+
return "HTTP 200";
|
|
766
|
+
case "refused":
|
|
767
|
+
return `HTTP ${answer.status} ${answer.error}`;
|
|
768
|
+
case "failed":
|
|
769
|
+
return `HTTP ${answer.status}`;
|
|
770
|
+
case "unreachable":
|
|
771
|
+
return answer.reason;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function isFresh(login, now = Date.now()) {
|
|
775
|
+
return login.expires_at - now > FRESH_FOR_MS;
|
|
776
|
+
}
|
|
777
|
+
function loginOf(endpoints, tokens, now = Date.now()) {
|
|
778
|
+
return {
|
|
779
|
+
issuer: endpoints.issuer,
|
|
780
|
+
client_id: endpoints.clientId,
|
|
781
|
+
token_endpoint: endpoints.tokenEndpoint,
|
|
782
|
+
revocation_endpoint: endpoints.revocationEndpoint,
|
|
783
|
+
access_token: tokens.access_token,
|
|
784
|
+
expires_at: now + tokens.expires_in * 1e3,
|
|
785
|
+
...tokens.refresh_token === void 0 ? {} : { refresh_token: tokens.refresh_token },
|
|
786
|
+
...tokens.scope === void 0 ? {} : { scope: tokens.scope }
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
var SignInError = class extends Error {
|
|
790
|
+
constructor(message, reason) {
|
|
791
|
+
super(message);
|
|
792
|
+
this.reason = reason;
|
|
793
|
+
}
|
|
794
|
+
reason;
|
|
795
|
+
};
|
|
796
|
+
function without(logins, issuer) {
|
|
797
|
+
return Object.fromEntries(
|
|
798
|
+
Object.entries(logins).filter(([key]) => key !== issuer)
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
function endedError(issuer) {
|
|
802
|
+
return new SignInError(
|
|
803
|
+
`your sign-in to ${issuer} ended: run walkeros login`,
|
|
804
|
+
"ended"
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
async function refreshStoredLogin(issuer, options = {}) {
|
|
808
|
+
try {
|
|
809
|
+
return await withCredentialsLock(async () => {
|
|
810
|
+
const logins = readCredentials();
|
|
811
|
+
const login = logins[issuer];
|
|
812
|
+
if (login === void 0) throw endedError(issuer);
|
|
813
|
+
if (login.access_token !== options.rejected && isFresh(login))
|
|
814
|
+
return login;
|
|
815
|
+
const remove = () => writeCredentials(without(logins, issuer));
|
|
816
|
+
if (login.refresh_token === void 0) {
|
|
817
|
+
remove();
|
|
818
|
+
throw endedError(issuer);
|
|
819
|
+
}
|
|
820
|
+
const answer = await postForm(
|
|
821
|
+
login.token_endpoint,
|
|
822
|
+
{
|
|
823
|
+
grant_type: "refresh_token",
|
|
824
|
+
client_id: login.client_id,
|
|
825
|
+
refresh_token: login.refresh_token
|
|
826
|
+
},
|
|
827
|
+
true
|
|
828
|
+
);
|
|
829
|
+
if (answer.kind === "ok" && answer.tokens) {
|
|
830
|
+
const tokens = answer.tokens;
|
|
831
|
+
const refreshed = loginOf(
|
|
832
|
+
{
|
|
833
|
+
issuer: login.issuer,
|
|
834
|
+
clientId: login.client_id,
|
|
835
|
+
tokenEndpoint: login.token_endpoint,
|
|
836
|
+
revocationEndpoint: login.revocation_endpoint
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
...tokens,
|
|
840
|
+
refresh_token: tokens.refresh_token ?? login.refresh_token,
|
|
841
|
+
scope: tokens.scope ?? login.scope
|
|
842
|
+
}
|
|
843
|
+
);
|
|
844
|
+
writeCredentials({ ...logins, [issuer]: refreshed });
|
|
845
|
+
return refreshed;
|
|
846
|
+
}
|
|
847
|
+
if (answer.kind === "refused" && (answer.error === "invalid_grant" || answer.error === "invalid_client")) {
|
|
848
|
+
remove();
|
|
849
|
+
throw endedError(issuer);
|
|
850
|
+
}
|
|
851
|
+
if (answer.kind === "unreachable")
|
|
852
|
+
throw new SignInError(
|
|
853
|
+
`could not reach ${issuer} to renew the sign-in: ${answer.reason}: try again`,
|
|
854
|
+
"unreachable"
|
|
855
|
+
);
|
|
856
|
+
throw new SignInError(
|
|
857
|
+
`${issuer} did not renew the sign-in (${answerText(answer)}): try again, or run walkeros login`,
|
|
858
|
+
"unreachable"
|
|
859
|
+
);
|
|
860
|
+
}, options.timing);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
if (error instanceof SignInError) throw error;
|
|
863
|
+
if (error instanceof LoginError)
|
|
864
|
+
throw new SignInError(error.message, "ended");
|
|
865
|
+
if (error instanceof CredentialsBusyError)
|
|
866
|
+
throw new SignInError(error.message, "busy");
|
|
867
|
+
throw error;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
353
871
|
// src/core/auth.ts
|
|
354
872
|
function resolveAccessToken() {
|
|
355
873
|
const token = process.env.WALKEROS_TOKEN;
|
|
356
874
|
return token ? token : null;
|
|
357
875
|
}
|
|
876
|
+
async function resolveCredential(appUrl = resolveAppUrl()) {
|
|
877
|
+
const env = resolveAccessToken();
|
|
878
|
+
if (env !== null) return { source: "env", token: env };
|
|
879
|
+
const logins = readCredentials();
|
|
880
|
+
const login = loginForAppUrl(logins, appUrl);
|
|
881
|
+
if (login === void 0) {
|
|
882
|
+
const issuers = Object.keys(logins);
|
|
883
|
+
if (issuers.length === 0) return null;
|
|
884
|
+
throw new SignInError(
|
|
885
|
+
`signed in to ${issuers.join(", ")}, not ${appUrl.replace(/\/+$/, "")}: run walkeros login`,
|
|
886
|
+
"elsewhere"
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
const usable = isFresh(login) ? login : await refreshStoredLogin(login.issuer);
|
|
890
|
+
return { source: "login", token: usable.access_token, issuer: usable.issuer };
|
|
891
|
+
}
|
|
892
|
+
async function sendWithCredential(credential, send) {
|
|
893
|
+
const response = await send(credential.token);
|
|
894
|
+
if (response.status !== 401 || credential.source !== "login") return response;
|
|
895
|
+
await response.body?.cancel();
|
|
896
|
+
const renewed = await refreshStoredLogin(credential.issuer, {
|
|
897
|
+
rejected: credential.token
|
|
898
|
+
});
|
|
899
|
+
return send(renewed.access_token);
|
|
900
|
+
}
|
|
358
901
|
|
|
359
902
|
// src/core/event-validation.ts
|
|
360
903
|
import { schemas } from "@walkeros/core/dev";
|
|
@@ -1001,12 +1544,13 @@ function resolveStepPackage(flow, kind, id, logger) {
|
|
|
1001
1544
|
}
|
|
1002
1545
|
|
|
1003
1546
|
// src/core/package-manager.ts
|
|
1547
|
+
import { createHash as createHash3 } from "crypto";
|
|
1004
1548
|
import pacote from "pacote";
|
|
1005
1549
|
import path10 from "path";
|
|
1006
1550
|
import fs10 from "fs-extra";
|
|
1007
1551
|
import { readFile } from "fs/promises";
|
|
1008
1552
|
import os from "os";
|
|
1009
|
-
import
|
|
1553
|
+
import semver3 from "semver";
|
|
1010
1554
|
|
|
1011
1555
|
// src/core/cache-utils.ts
|
|
1012
1556
|
import { getHashServer } from "@walkeros/server-core";
|
|
@@ -1202,6 +1746,246 @@ function assertTransitiveRegistrySpec(check) {
|
|
|
1202
1746
|
);
|
|
1203
1747
|
}
|
|
1204
1748
|
|
|
1749
|
+
// src/core/package-tree.ts
|
|
1750
|
+
import { createHash as createHash2 } from "crypto";
|
|
1751
|
+
import npa2 from "npm-package-arg";
|
|
1752
|
+
import semver2 from "semver";
|
|
1753
|
+
import { z as z4 } from "@walkeros/core/dev";
|
|
1754
|
+
var PACKAGE_TREE_VERSION = 1;
|
|
1755
|
+
var PACKAGE_TREE_MAX_ENTRIES = 5e3;
|
|
1756
|
+
var SHA512_INTEGRITY = /^sha512-[A-Za-z0-9+/]{86}==$/;
|
|
1757
|
+
var PackageTreeEntrySchema = z4.object({
|
|
1758
|
+
name: z4.string().min(1).describe("Registry name of the package"),
|
|
1759
|
+
version: z4.string().min(1).describe("Exact version"),
|
|
1760
|
+
resolved: z4.string().min(1).describe("Tarball URL on the registry"),
|
|
1761
|
+
integrity: z4.string().min(1).describe("sha512 of the tarball bytes"),
|
|
1762
|
+
dependencies: z4.record(z4.string().min(1), z4.string()).optional().describe("Edges followed: dependencies and required peers")
|
|
1763
|
+
}).strict();
|
|
1764
|
+
var PackageTreeSchema = z4.object({
|
|
1765
|
+
version: z4.literal(PACKAGE_TREE_VERSION),
|
|
1766
|
+
registry: z4.string().min(1).describe("Registry every entry comes from"),
|
|
1767
|
+
before: z4.string().nullable().describe("Release-age cutoff the tree was resolved under"),
|
|
1768
|
+
packages: z4.record(z4.string().min(1), PackageTreeEntrySchema).describe("Entries by install path")
|
|
1769
|
+
}).strict().meta({ id: "PackageTree", title: "Recorded package tree" });
|
|
1770
|
+
var PackageTreeError = class extends Error {
|
|
1771
|
+
constructor(problems) {
|
|
1772
|
+
super(`Package tree refused: ${problems.join("; ")}`);
|
|
1773
|
+
this.problems = problems;
|
|
1774
|
+
this.name = "PackageTreeError";
|
|
1775
|
+
}
|
|
1776
|
+
problems;
|
|
1777
|
+
code = "PACKAGE_TREE_INVALID";
|
|
1778
|
+
};
|
|
1779
|
+
function canonicalPackageTree(tree) {
|
|
1780
|
+
return JSON.stringify(sortKeys(tree));
|
|
1781
|
+
}
|
|
1782
|
+
function sortKeys(value) {
|
|
1783
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
1784
|
+
if (typeof value === "object" && value !== null) {
|
|
1785
|
+
const sorted = {};
|
|
1786
|
+
for (const key of Object.keys(value).sort()) {
|
|
1787
|
+
sorted[key] = sortKeys(Reflect.get(value, key));
|
|
1788
|
+
}
|
|
1789
|
+
return sorted;
|
|
1790
|
+
}
|
|
1791
|
+
return value;
|
|
1792
|
+
}
|
|
1793
|
+
function packageTreeDigest(tree) {
|
|
1794
|
+
return createHash2("sha256").update(canonicalPackageTree(tree)).digest("hex");
|
|
1795
|
+
}
|
|
1796
|
+
function normalizeRegistry(registry) {
|
|
1797
|
+
try {
|
|
1798
|
+
const url = new URL(registry);
|
|
1799
|
+
if (!url.pathname.endsWith("/")) url.pathname = `${url.pathname}/`;
|
|
1800
|
+
return url;
|
|
1801
|
+
} catch {
|
|
1802
|
+
return null;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
function isPackageName(name) {
|
|
1806
|
+
try {
|
|
1807
|
+
const parsed = npa2(`${name}@0.0.0`);
|
|
1808
|
+
return parsed.name === name && parsed.type === "version";
|
|
1809
|
+
} catch {
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
function scopeOf(name) {
|
|
1814
|
+
return name.startsWith("@") ? name.slice(0, name.indexOf("/")) : "";
|
|
1815
|
+
}
|
|
1816
|
+
function isExemptName(name, exempt) {
|
|
1817
|
+
return exempt.some((prefix) => name.startsWith(prefix));
|
|
1818
|
+
}
|
|
1819
|
+
function aliasCrossesExemption(installName, realName, exempt) {
|
|
1820
|
+
return isExemptName(installName, exempt) !== isExemptName(realName, exempt);
|
|
1821
|
+
}
|
|
1822
|
+
function edgeTargetName(depName, spec) {
|
|
1823
|
+
try {
|
|
1824
|
+
const parsed = npa2.resolve(depName, spec);
|
|
1825
|
+
if (parsed.type === "alias") return parsed.subSpec.name ?? null;
|
|
1826
|
+
return parsed.name ?? null;
|
|
1827
|
+
} catch {
|
|
1828
|
+
return null;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
function isExactSpec(depName, spec) {
|
|
1832
|
+
try {
|
|
1833
|
+
const parsed = npa2.resolve(depName, spec);
|
|
1834
|
+
if (parsed.type === "alias") return parsed.subSpec.type === "version";
|
|
1835
|
+
return parsed.type === "version";
|
|
1836
|
+
} catch {
|
|
1837
|
+
return false;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
function edgeIsExempt(edge, exempt) {
|
|
1841
|
+
const target = edgeTargetName(edge.name, edge.spec);
|
|
1842
|
+
if (target === null) return false;
|
|
1843
|
+
if (isExemptName(target, exempt)) return true;
|
|
1844
|
+
return edge.parentName !== void 0 && isExemptName(edge.parentName, exempt) && isExactSpec(edge.name, edge.spec);
|
|
1845
|
+
}
|
|
1846
|
+
function tarballPath(registryPath, name, version) {
|
|
1847
|
+
const base = name.startsWith("@") ? name.slice(name.indexOf("/") + 1) : name;
|
|
1848
|
+
return `${registryPath}${name}/-/${base}-${version}.tgz`;
|
|
1849
|
+
}
|
|
1850
|
+
var LAYOUT = /^node_modules\/((?:@[^/]+\/)?[^/@][^/]*)(?:\/node_modules\/((?:@[^/]+\/)?[^/@][^/]*))?$/;
|
|
1851
|
+
function parseInstallPath(path23) {
|
|
1852
|
+
const match = LAYOUT.exec(path23);
|
|
1853
|
+
if (!match) return null;
|
|
1854
|
+
const [, first, second] = match;
|
|
1855
|
+
if (!isPackageName(first)) return null;
|
|
1856
|
+
if (second === void 0) return { name: first };
|
|
1857
|
+
if (!isPackageName(second)) return null;
|
|
1858
|
+
return { name: second, consumer: first };
|
|
1859
|
+
}
|
|
1860
|
+
function lookupPaths(path23, dep) {
|
|
1861
|
+
const at = parseInstallPath(path23);
|
|
1862
|
+
if (!at) return [];
|
|
1863
|
+
return at.consumer === void 0 ? [`node_modules/${at.name}/node_modules/${dep}`, `node_modules/${dep}`] : [
|
|
1864
|
+
`node_modules/${at.consumer}/node_modules/${dep}`,
|
|
1865
|
+
`node_modules/${dep}`
|
|
1866
|
+
];
|
|
1867
|
+
}
|
|
1868
|
+
function packageTreeProblems(value, options) {
|
|
1869
|
+
const parsed = PackageTreeSchema.safeParse(value);
|
|
1870
|
+
if (!parsed.success) {
|
|
1871
|
+
return parsed.error.issues.map(
|
|
1872
|
+
(issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
const tree = parsed.data;
|
|
1876
|
+
const problems = [];
|
|
1877
|
+
const exempt = options.exempt ?? [];
|
|
1878
|
+
const registry = normalizeRegistry(options.registry);
|
|
1879
|
+
const treeRegistry = normalizeRegistry(tree.registry);
|
|
1880
|
+
if (!registry)
|
|
1881
|
+
return [`the build registry is not a URL: ${options.registry}`];
|
|
1882
|
+
if (!treeRegistry || treeRegistry.href !== registry.href) {
|
|
1883
|
+
problems.push(`registry ${tree.registry} is not ${registry.href}`);
|
|
1884
|
+
}
|
|
1885
|
+
const paths = Object.keys(tree.packages);
|
|
1886
|
+
if (paths.length > PACKAGE_TREE_MAX_ENTRIES) {
|
|
1887
|
+
return [
|
|
1888
|
+
`${paths.length} entries, more than ${PACKAGE_TREE_MAX_ENTRIES}`,
|
|
1889
|
+
...problems
|
|
1890
|
+
];
|
|
1891
|
+
}
|
|
1892
|
+
for (const [path23, entry] of Object.entries(tree.packages)) {
|
|
1893
|
+
const at = parseInstallPath(path23);
|
|
1894
|
+
if (!at) {
|
|
1895
|
+
problems.push(
|
|
1896
|
+
`${path23}: not node_modules/<name> or node_modules/<name>/node_modules/<name>`
|
|
1897
|
+
);
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
if (!isPackageName(entry.name)) {
|
|
1901
|
+
problems.push(`${path23}: name ${entry.name} is not a package name`);
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
if (aliasCrossesExemption(at.name, entry.name, exempt)) {
|
|
1905
|
+
problems.push(
|
|
1906
|
+
`${path23}: an alias of ${entry.name} crosses an exempt prefix`
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
if (semver2.valid(entry.version) !== entry.version) {
|
|
1910
|
+
problems.push(`${path23}: version ${entry.version} is not exact`);
|
|
1911
|
+
}
|
|
1912
|
+
if (!SHA512_INTEGRITY.test(entry.integrity)) {
|
|
1913
|
+
problems.push(`${path23}: integrity is not one sha512 value`);
|
|
1914
|
+
}
|
|
1915
|
+
const scoped = options.scopeRegistries?.[scopeOf(entry.name)];
|
|
1916
|
+
const from = scoped ? normalizeRegistry(scoped) : registry;
|
|
1917
|
+
if (!tarballOn(entry.resolved, from, entry.name, entry.version)) {
|
|
1918
|
+
problems.push(
|
|
1919
|
+
`${path23}: resolved ${entry.resolved} is not the ${entry.name}@${entry.version} tarball on ${from?.origin ?? "the registry"}`
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
for (const [dep, spec] of Object.entries(entry.dependencies ?? {})) {
|
|
1923
|
+
const target = edgeTargetName(dep, spec);
|
|
1924
|
+
if (target === null) {
|
|
1925
|
+
problems.push(`${path23}: dependency ${dep}@${spec} is no registry spec`);
|
|
1926
|
+
continue;
|
|
1927
|
+
}
|
|
1928
|
+
if (aliasCrossesExemption(dep, target, exempt)) {
|
|
1929
|
+
problems.push(
|
|
1930
|
+
`${path23}: dependency ${dep}@${spec} crosses an exempt prefix`
|
|
1931
|
+
);
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
const found = lookupPaths(path23, dep).find((p) => tree.packages[p]);
|
|
1935
|
+
if (!found || tree.packages[found].name !== target) {
|
|
1936
|
+
problems.push(
|
|
1937
|
+
`${path23}: dependency ${dep}@${spec} has no ${target} in the tree`
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
for (const [name, spec] of Object.entries(options.declared ?? {})) {
|
|
1943
|
+
const root = tree.packages[`node_modules/${name}`];
|
|
1944
|
+
if (!root || root.name !== name) {
|
|
1945
|
+
problems.push(`declared ${name}@${spec} is not at node_modules/${name}`);
|
|
1946
|
+
} else if (!versionNamedBy(root.version, spec)) {
|
|
1947
|
+
problems.push(`declared ${name}@${spec} is ${root.version} in the tree`);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
return problems;
|
|
1951
|
+
}
|
|
1952
|
+
function assertPackageTree(value, options) {
|
|
1953
|
+
const problems = packageTreeProblems(value, options);
|
|
1954
|
+
if (problems.length > 0) throw new PackageTreeError(problems);
|
|
1955
|
+
return PackageTreeSchema.parse(value);
|
|
1956
|
+
}
|
|
1957
|
+
function tarballOn(resolved, registry, name, version) {
|
|
1958
|
+
if (!registry) return false;
|
|
1959
|
+
let url;
|
|
1960
|
+
try {
|
|
1961
|
+
url = new URL(resolved);
|
|
1962
|
+
} catch {
|
|
1963
|
+
return false;
|
|
1964
|
+
}
|
|
1965
|
+
return url.origin === registry.origin && url.protocol === registry.protocol && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname === tarballPath(registry.pathname, name, version);
|
|
1966
|
+
}
|
|
1967
|
+
function versionNamedBy(version, spec) {
|
|
1968
|
+
if (semver2.valid(spec) !== null) return semver2.eq(spec, version);
|
|
1969
|
+
if (semver2.validRange(spec) !== null) {
|
|
1970
|
+
return semver2.satisfies(version, spec, { includePrerelease: true });
|
|
1971
|
+
}
|
|
1972
|
+
return true;
|
|
1973
|
+
}
|
|
1974
|
+
function treeVersionLines(tree) {
|
|
1975
|
+
return Object.entries(tree.packages).flatMap(([path23, entry]) => {
|
|
1976
|
+
const at = parseInstallPath(path23);
|
|
1977
|
+
return at && at.consumer === void 0 ? [`${at.name}@${entry.version}`] : [];
|
|
1978
|
+
}).sort((a, b) => a.localeCompare(b));
|
|
1979
|
+
}
|
|
1980
|
+
function followedEdges(manifest) {
|
|
1981
|
+
const edges = { ...manifest.dependencies ?? {} };
|
|
1982
|
+
for (const [name, spec] of Object.entries(manifest.peerDependencies ?? {})) {
|
|
1983
|
+
if (manifest.peerDependenciesMeta?.[name]?.optional === true) continue;
|
|
1984
|
+
if (!(name in edges)) edges[name] = spec;
|
|
1985
|
+
}
|
|
1986
|
+
return edges;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1205
1989
|
// src/core/package-manager.ts
|
|
1206
1990
|
var IGNORE_SCRIPTS = { ignoreScripts: true };
|
|
1207
1991
|
var PACOTE_OPTS = {
|
|
@@ -1210,6 +1994,32 @@ var PACOTE_OPTS = {
|
|
|
1210
1994
|
where: void 0,
|
|
1211
1995
|
...IGNORE_SCRIPTS
|
|
1212
1996
|
};
|
|
1997
|
+
var UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
1998
|
+
"E404",
|
|
1999
|
+
"E410",
|
|
2000
|
+
"ETARGET",
|
|
2001
|
+
"EINTEGRITY"
|
|
2002
|
+
]);
|
|
2003
|
+
var PackageUnavailableError = class extends Error {
|
|
2004
|
+
constructor(installPath, entry, reason) {
|
|
2005
|
+
super(
|
|
2006
|
+
`Recorded package ${entry.name}@${entry.version} (${installPath}) could not be installed: ${reason}`
|
|
2007
|
+
);
|
|
2008
|
+
this.installPath = installPath;
|
|
2009
|
+
this.name = "PackageUnavailableError";
|
|
2010
|
+
}
|
|
2011
|
+
installPath;
|
|
2012
|
+
code = "PACKAGE_UNAVAILABLE";
|
|
2013
|
+
};
|
|
2014
|
+
function scopeRegistriesOf(npmConfig) {
|
|
2015
|
+
const scoped = {};
|
|
2016
|
+
for (const [key, value] of Object.entries(npmConfig)) {
|
|
2017
|
+
if (key.startsWith("@") && key.endsWith(":registry") && typeof value === "string") {
|
|
2018
|
+
scoped[key.slice(0, -":registry".length)] = value;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
return scoped;
|
|
2022
|
+
}
|
|
1213
2023
|
async function loadNpmConfigForPacote(projectDir = process.cwd(), homeDir = os.homedir()) {
|
|
1214
2024
|
const merged = {};
|
|
1215
2025
|
const sources = [
|
|
@@ -1334,6 +2144,13 @@ function extractWithResilience(spec, dest, opts, _logger, retryOptions) {
|
|
|
1334
2144
|
retryOptions
|
|
1335
2145
|
);
|
|
1336
2146
|
}
|
|
2147
|
+
function tarballWithResilience(spec, opts, retryOptions) {
|
|
2148
|
+
return withPacoteRetry(
|
|
2149
|
+
(signal) => pacote.tarball(spec, withSignal(opts, signal)),
|
|
2150
|
+
`download ${spec}`,
|
|
2151
|
+
retryOptions
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
1337
2154
|
function manifestWithResilience(spec, opts, _logger, retryOptions) {
|
|
1338
2155
|
return withPacoteRetry(
|
|
1339
2156
|
(signal) => pacote.manifest(spec, withSignal(opts, signal)),
|
|
@@ -1353,9 +2170,10 @@ function getNestedPackageDirectory(baseDir, consumerName, nestedPackageName) {
|
|
|
1353
2170
|
nestedPackageName
|
|
1354
2171
|
);
|
|
1355
2172
|
}
|
|
1356
|
-
async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmConfig = PACOTE_OPTS) {
|
|
2173
|
+
async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmConfig = PACOTE_OPTS, cooldown) {
|
|
1357
2174
|
const allSpecs = /* @__PURE__ */ new Map();
|
|
1358
2175
|
const visited = /* @__PURE__ */ new Set();
|
|
2176
|
+
const exemptEdge = (name, spec, parentName) => cooldown !== void 0 && edgeIsExempt({ name, spec, parentName }, cooldown.exempt);
|
|
1359
2177
|
const directLocalNames = new Set(
|
|
1360
2178
|
packages.filter((p) => p.path).map((p) => p.name)
|
|
1361
2179
|
);
|
|
@@ -1366,7 +2184,7 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1366
2184
|
);
|
|
1367
2185
|
}
|
|
1368
2186
|
}
|
|
1369
|
-
const substituteDep = (depName, depSpec, source, from, optional) => {
|
|
2187
|
+
const substituteDep = (depName, depSpec, source, from, optional, parentName) => {
|
|
1370
2188
|
const overrideSpec = overrides[depName];
|
|
1371
2189
|
if (overrideSpec) {
|
|
1372
2190
|
return {
|
|
@@ -1374,10 +2192,18 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1374
2192
|
spec: overrideSpec,
|
|
1375
2193
|
source: "override",
|
|
1376
2194
|
from: `override (was ${depSpec} from ${from})`,
|
|
1377
|
-
optional
|
|
2195
|
+
optional,
|
|
2196
|
+
exempt: exemptEdge(depName, overrideSpec)
|
|
1378
2197
|
};
|
|
1379
2198
|
}
|
|
1380
|
-
return {
|
|
2199
|
+
return {
|
|
2200
|
+
name: depName,
|
|
2201
|
+
spec: depSpec,
|
|
2202
|
+
source,
|
|
2203
|
+
from,
|
|
2204
|
+
optional,
|
|
2205
|
+
exempt: exemptEdge(depName, depSpec, parentName)
|
|
2206
|
+
};
|
|
1381
2207
|
};
|
|
1382
2208
|
assertRegistrySpecs([
|
|
1383
2209
|
...packages.filter((pkg) => !pkg.path).map((pkg) => ({ name: pkg.name, spec: pkg.version, from: "flow.json" })),
|
|
@@ -1395,7 +2221,8 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1395
2221
|
source: "direct",
|
|
1396
2222
|
from: "flow.json",
|
|
1397
2223
|
optional: false,
|
|
1398
|
-
localPath: pkg.path
|
|
2224
|
+
localPath: pkg.path,
|
|
2225
|
+
exempt: exemptEdge(pkg.name, pkg.version)
|
|
1399
2226
|
}));
|
|
1400
2227
|
while (queue.length > 0) {
|
|
1401
2228
|
const item = queue.shift();
|
|
@@ -1411,6 +2238,7 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1411
2238
|
}
|
|
1412
2239
|
assertTransitiveRegistrySpec(item);
|
|
1413
2240
|
}
|
|
2241
|
+
if (cooldown && !item.localPath) assertAliasKeepsSide(item, cooldown);
|
|
1414
2242
|
if (!allSpecs.has(item.name)) allSpecs.set(item.name, []);
|
|
1415
2243
|
const existing = allSpecs.get(item.name);
|
|
1416
2244
|
if (!existing.some((s) => s.spec === item.spec && s.from === item.from)) {
|
|
@@ -1419,7 +2247,8 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1419
2247
|
source: item.source,
|
|
1420
2248
|
from: item.from,
|
|
1421
2249
|
optional: item.optional,
|
|
1422
|
-
localPath: item.localPath
|
|
2250
|
+
localPath: item.localPath,
|
|
2251
|
+
...cooldown ? { exempt: item.exempt } : {}
|
|
1423
2252
|
});
|
|
1424
2253
|
}
|
|
1425
2254
|
const visitKey = `${item.name}@${item.spec}`;
|
|
@@ -1468,10 +2297,16 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1468
2297
|
try {
|
|
1469
2298
|
manifest = await manifestWithResilience(
|
|
1470
2299
|
`${item.name}@${item.spec}`,
|
|
1471
|
-
npmConfig,
|
|
2300
|
+
cooldownOptions(npmConfig, cooldown, item.exempt),
|
|
1472
2301
|
logger
|
|
1473
2302
|
);
|
|
2303
|
+
if (cooldown && !item.exempt) {
|
|
2304
|
+
assertPublishedBefore(manifest, cooldown, item.from);
|
|
2305
|
+
}
|
|
1474
2306
|
} catch (error) {
|
|
2307
|
+
if (cooldown) {
|
|
2308
|
+
throw cooldownFailure(item.name, item.spec, item.from, cooldown, error);
|
|
2309
|
+
}
|
|
1475
2310
|
logger.warn(
|
|
1476
2311
|
`Failed to fetch manifest for ${item.name}@${item.spec} after retries; its dependencies may be unresolved: ${error instanceof Error ? error.message : String(error)}`
|
|
1477
2312
|
);
|
|
@@ -1481,7 +2316,14 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1481
2316
|
for (const [depName, depSpec] of Object.entries(deps)) {
|
|
1482
2317
|
if (typeof depSpec === "string") {
|
|
1483
2318
|
queue.push(
|
|
1484
|
-
substituteDep(
|
|
2319
|
+
substituteDep(
|
|
2320
|
+
depName,
|
|
2321
|
+
depSpec,
|
|
2322
|
+
"dependency",
|
|
2323
|
+
item.name,
|
|
2324
|
+
false,
|
|
2325
|
+
manifest.name
|
|
2326
|
+
)
|
|
1485
2327
|
);
|
|
1486
2328
|
}
|
|
1487
2329
|
}
|
|
@@ -1496,13 +2338,46 @@ async function collectAllSpecs(packages, logger, configDir, overrides = {}, npmC
|
|
|
1496
2338
|
depSpec,
|
|
1497
2339
|
"peerDependency",
|
|
1498
2340
|
item.name,
|
|
1499
|
-
isOptional
|
|
2341
|
+
isOptional,
|
|
2342
|
+
manifest.name
|
|
1500
2343
|
)
|
|
1501
2344
|
);
|
|
1502
2345
|
}
|
|
1503
2346
|
}
|
|
1504
2347
|
}
|
|
1505
|
-
return allSpecs;
|
|
2348
|
+
return allSpecs;
|
|
2349
|
+
}
|
|
2350
|
+
function cooldownOptions(npmConfig, cooldown, exempt) {
|
|
2351
|
+
return cooldown && !exempt ? { ...npmConfig, before: cooldown.before } : npmConfig;
|
|
2352
|
+
}
|
|
2353
|
+
function assertPublishedBefore(manifest, cooldown, from) {
|
|
2354
|
+
const time = Reflect.get(manifest, "_time");
|
|
2355
|
+
const label = `${manifest.name}@${manifest.version} (from ${from})`;
|
|
2356
|
+
if (typeof time !== "string" || Number.isNaN(Date.parse(time))) {
|
|
2357
|
+
throw new Error(`${label} has no publish time in the registry`);
|
|
2358
|
+
}
|
|
2359
|
+
if (Date.parse(time) > cooldown.before.getTime()) {
|
|
2360
|
+
throw new Error(
|
|
2361
|
+
`${label} was published ${time}, after ${cooldown.before.toISOString()}`
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
function cooldownFailure(name, spec, from, cooldown, error) {
|
|
2366
|
+
if (readErrorCode(error) === "ETARGET") {
|
|
2367
|
+
return new Error(
|
|
2368
|
+
`No version of ${name}@${spec} (from ${from}) was published at or before ${cooldown.before.toISOString()}`
|
|
2369
|
+
);
|
|
2370
|
+
}
|
|
2371
|
+
if (error instanceof Error) return error;
|
|
2372
|
+
return new Error(String(error));
|
|
2373
|
+
}
|
|
2374
|
+
function assertAliasKeepsSide(item, cooldown) {
|
|
2375
|
+
const target = edgeTargetName(item.name, item.spec);
|
|
2376
|
+
if (target !== null && aliasCrossesExemption(item.name, target, cooldown.exempt)) {
|
|
2377
|
+
throw new PackageTreeError([
|
|
2378
|
+
`${item.name}@${item.spec} (from ${item.from}) aliases across an exempt prefix`
|
|
2379
|
+
]);
|
|
2380
|
+
}
|
|
1506
2381
|
}
|
|
1507
2382
|
var SOURCE_PRIORITY = {
|
|
1508
2383
|
direct: 0,
|
|
@@ -1510,21 +2385,26 @@ var SOURCE_PRIORITY = {
|
|
|
1510
2385
|
dependency: 2,
|
|
1511
2386
|
peerDependency: 3
|
|
1512
2387
|
};
|
|
1513
|
-
async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
2388
|
+
async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}, cooldown) {
|
|
1514
2389
|
const topLevel = /* @__PURE__ */ new Map();
|
|
1515
2390
|
const nested = [];
|
|
1516
2391
|
for (const [name, specs] of allSpecs) {
|
|
1517
2392
|
let nestLoser2 = function(spec, alreadyNested2) {
|
|
1518
2393
|
if (spec.localPath) return;
|
|
1519
2394
|
if (alreadyNested2.has(spec.spec)) return;
|
|
1520
|
-
if (
|
|
2395
|
+
if (semver3.valid(spec.spec) === null && semver3.validRange(spec.spec) === null) {
|
|
1521
2396
|
logger.debug(
|
|
1522
2397
|
`Skipping non-semver loser spec for ${name}: ${spec.spec} from ${spec.from}`
|
|
1523
2398
|
);
|
|
1524
2399
|
return;
|
|
1525
2400
|
}
|
|
1526
2401
|
const consumers = activeSpecs.filter((s) => s.spec === spec.spec).map((s) => s.from);
|
|
1527
|
-
nested.push({
|
|
2402
|
+
nested.push({
|
|
2403
|
+
name,
|
|
2404
|
+
version: spec.spec,
|
|
2405
|
+
consumers,
|
|
2406
|
+
...cooldown ? { exempt: specExempt(spec.spec) } : {}
|
|
2407
|
+
});
|
|
1528
2408
|
alreadyNested2.add(spec.spec);
|
|
1529
2409
|
};
|
|
1530
2410
|
var nestLoser = nestLoser2;
|
|
@@ -1537,6 +2417,7 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1537
2417
|
});
|
|
1538
2418
|
continue;
|
|
1539
2419
|
}
|
|
2420
|
+
const specExempt = (spec) => cooldown !== void 0 && specs.filter((s) => s.spec === spec).every((s) => s.exempt === true);
|
|
1540
2421
|
const nonPeerSpecs = specs.filter((s) => s.source !== "peerDependency");
|
|
1541
2422
|
const peerSpecs = specs.filter((s) => s.source === "peerDependency");
|
|
1542
2423
|
let activeSpecs;
|
|
@@ -1554,7 +2435,7 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1554
2435
|
(a, b) => SOURCE_PRIORITY[a.source] - SOURCE_PRIORITY[b.source]
|
|
1555
2436
|
);
|
|
1556
2437
|
const directSpecs = activeSpecs.filter((s) => s.source === "direct");
|
|
1557
|
-
const directExact = directSpecs.find((s) =>
|
|
2438
|
+
const directExact = directSpecs.find((s) => semver3.valid(s.spec) !== null);
|
|
1558
2439
|
let chosenVersion;
|
|
1559
2440
|
const alreadyNested = /* @__PURE__ */ new Set();
|
|
1560
2441
|
if (directExact) {
|
|
@@ -1562,15 +2443,20 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1562
2443
|
} else if (directSpecs.length > 0) {
|
|
1563
2444
|
chosenVersion = directSpecs[0].spec;
|
|
1564
2445
|
} else {
|
|
1565
|
-
const exactVersions = activeSpecs.filter((s) =>
|
|
2446
|
+
const exactVersions = activeSpecs.filter((s) => semver3.valid(s.spec) !== null).map((s) => s.spec);
|
|
1566
2447
|
const uniqueExact = [...new Set(exactVersions)];
|
|
1567
2448
|
if (uniqueExact.length > 1) {
|
|
1568
|
-
const sorted = [...uniqueExact].sort(
|
|
2449
|
+
const sorted = [...uniqueExact].sort(semver3.rcompare);
|
|
1569
2450
|
chosenVersion = sorted[0];
|
|
1570
2451
|
for (let i = 1; i < sorted.length; i++) {
|
|
1571
2452
|
const loserVersion = sorted[i];
|
|
1572
2453
|
const consumers = activeSpecs.filter((s) => s.spec === loserVersion).map((s) => s.from);
|
|
1573
|
-
nested.push({
|
|
2454
|
+
nested.push({
|
|
2455
|
+
name,
|
|
2456
|
+
version: loserVersion,
|
|
2457
|
+
consumers,
|
|
2458
|
+
...cooldown ? { exempt: specExempt(loserVersion) } : {}
|
|
2459
|
+
});
|
|
1574
2460
|
alreadyNested.add(loserVersion);
|
|
1575
2461
|
}
|
|
1576
2462
|
} else if (uniqueExact.length === 1) {
|
|
@@ -1580,17 +2466,25 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1580
2466
|
}
|
|
1581
2467
|
}
|
|
1582
2468
|
let concreteVersion;
|
|
1583
|
-
if (
|
|
2469
|
+
if (semver3.valid(chosenVersion)) {
|
|
1584
2470
|
concreteVersion = chosenVersion;
|
|
1585
2471
|
} else {
|
|
2472
|
+
const askedBy = specs.filter((s) => s.spec === chosenVersion).map((s) => s.from).join(", ");
|
|
1586
2473
|
try {
|
|
2474
|
+
const exempt = specExempt(chosenVersion);
|
|
1587
2475
|
const manifest = await manifestWithResilience(
|
|
1588
2476
|
`${name}@${chosenVersion}`,
|
|
1589
|
-
npmConfig,
|
|
2477
|
+
cooldownOptions(npmConfig, cooldown, exempt),
|
|
1590
2478
|
logger
|
|
1591
2479
|
);
|
|
2480
|
+
if (cooldown && !exempt) {
|
|
2481
|
+
assertPublishedBefore(manifest, cooldown, askedBy);
|
|
2482
|
+
}
|
|
1592
2483
|
concreteVersion = manifest.version;
|
|
1593
2484
|
} catch (err) {
|
|
2485
|
+
if (cooldown) {
|
|
2486
|
+
throw cooldownFailure(name, chosenVersion, askedBy, cooldown, err);
|
|
2487
|
+
}
|
|
1594
2488
|
if (process.env.BUNDLER_STRICT_RANGES === "0") {
|
|
1595
2489
|
logger.warn(
|
|
1596
2490
|
`Could not resolve ${name}@${chosenVersion} for strict validation: ${err instanceof Error ? err.message : String(err)}. Skipping range validation (BUNDLER_STRICT_RANGES=0).`
|
|
@@ -1606,7 +2500,7 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1606
2500
|
for (const spec of specs) {
|
|
1607
2501
|
if (spec.localPath) continue;
|
|
1608
2502
|
if (spec.spec === chosenVersion) continue;
|
|
1609
|
-
const satisfied =
|
|
2503
|
+
const satisfied = semver3.valid(spec.spec) ? spec.spec === concreteVersion : semver3.validRange(spec.spec) ? semver3.satisfies(concreteVersion, spec.spec, {
|
|
1610
2504
|
includePrerelease: true
|
|
1611
2505
|
}) : true;
|
|
1612
2506
|
if (satisfied) continue;
|
|
@@ -1618,7 +2512,11 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1618
2512
|
nestLoser2(spec, alreadyNested);
|
|
1619
2513
|
}
|
|
1620
2514
|
}
|
|
1621
|
-
topLevel.set(name, {
|
|
2515
|
+
topLevel.set(name, {
|
|
2516
|
+
name,
|
|
2517
|
+
version: chosenVersion,
|
|
2518
|
+
...cooldown ? { exempt: specExempt(chosenVersion) } : {}
|
|
2519
|
+
});
|
|
1622
2520
|
}
|
|
1623
2521
|
const consolidatedMap = /* @__PURE__ */ new Map();
|
|
1624
2522
|
for (const entry of nested) {
|
|
@@ -1628,13 +2526,16 @@ async function resolveVersionConflicts(allSpecs, logger, npmConfig = {}) {
|
|
|
1628
2526
|
for (const c of entry.consumers) {
|
|
1629
2527
|
if (!existing.consumers.includes(c)) existing.consumers.push(c);
|
|
1630
2528
|
}
|
|
2529
|
+
if (existing.exempt !== void 0) {
|
|
2530
|
+
existing.exempt = existing.exempt && entry.exempt === true;
|
|
2531
|
+
}
|
|
1631
2532
|
} else {
|
|
1632
2533
|
consolidatedMap.set(key, { ...entry, consumers: [...entry.consumers] });
|
|
1633
2534
|
}
|
|
1634
2535
|
}
|
|
1635
2536
|
return { topLevel, nested: [...consolidatedMap.values()] };
|
|
1636
2537
|
}
|
|
1637
|
-
async function downloadPackagesWithResolution(packages, targetDir, logger, useCache = true, configDir, tmpDir, overrides = {}, npmConfig = PACOTE_OPTS) {
|
|
2538
|
+
async function downloadPackagesWithResolution(packages, targetDir, logger, useCache = true, configDir, tmpDir, overrides = {}, npmConfig = PACOTE_OPTS, cooldown) {
|
|
1638
2539
|
return downloadPackagesImpl(
|
|
1639
2540
|
packages,
|
|
1640
2541
|
targetDir,
|
|
@@ -1643,10 +2544,11 @@ async function downloadPackagesWithResolution(packages, targetDir, logger, useCa
|
|
|
1643
2544
|
configDir,
|
|
1644
2545
|
tmpDir,
|
|
1645
2546
|
overrides,
|
|
1646
|
-
npmConfig
|
|
2547
|
+
npmConfig,
|
|
2548
|
+
cooldown
|
|
1647
2549
|
);
|
|
1648
2550
|
}
|
|
1649
|
-
async function downloadPackagesImpl(packages, targetDir, logger, useCache, configDir, tmpDir, overrides, npmConfig) {
|
|
2551
|
+
async function downloadPackagesImpl(packages, targetDir, logger, useCache, configDir, tmpDir, overrides, npmConfig, cooldown) {
|
|
1650
2552
|
const packagePaths = /* @__PURE__ */ new Map();
|
|
1651
2553
|
const userSpecifiedPackages = new Set(packages.map((p) => p.name));
|
|
1652
2554
|
validateNoDuplicatePackages(packages);
|
|
@@ -1656,11 +2558,30 @@ async function downloadPackagesImpl(packages, targetDir, logger, useCache, confi
|
|
|
1656
2558
|
logger,
|
|
1657
2559
|
configDir,
|
|
1658
2560
|
overrides,
|
|
1659
|
-
npmConfig
|
|
2561
|
+
npmConfig,
|
|
2562
|
+
cooldown
|
|
2563
|
+
);
|
|
2564
|
+
const resolution = await resolveVersionConflicts(
|
|
2565
|
+
allSpecs,
|
|
2566
|
+
logger,
|
|
2567
|
+
npmConfig,
|
|
2568
|
+
cooldown
|
|
1660
2569
|
);
|
|
1661
|
-
const resolution = await resolveVersionConflicts(allSpecs, logger, npmConfig);
|
|
1662
2570
|
const { topLevel, nested } = resolution;
|
|
1663
2571
|
await fs10.ensureDir(targetDir);
|
|
2572
|
+
if (cooldown) {
|
|
2573
|
+
const tree = await installResolvedTree(
|
|
2574
|
+
packages,
|
|
2575
|
+
resolution,
|
|
2576
|
+
targetDir,
|
|
2577
|
+
logger,
|
|
2578
|
+
{ npmConfig, cooldown, tmpDir }
|
|
2579
|
+
);
|
|
2580
|
+
for (const name of topLevel.keys()) {
|
|
2581
|
+
packagePaths.set(name, getPackageDirectory(targetDir, name));
|
|
2582
|
+
}
|
|
2583
|
+
return { packagePaths, resolution, tree };
|
|
2584
|
+
}
|
|
1664
2585
|
const localPackageMap = /* @__PURE__ */ new Map();
|
|
1665
2586
|
for (const pkg of packages) {
|
|
1666
2587
|
if (pkg.path) localPackageMap.set(pkg.name, pkg.path);
|
|
@@ -1721,7 +2642,7 @@ async function downloadPackagesImpl(packages, targetDir, logger, useCache, confi
|
|
|
1721
2642
|
}
|
|
1722
2643
|
for (const nestedPkg of nested) {
|
|
1723
2644
|
let resolvedSpec = `${nestedPkg.name}@${nestedPkg.version}`;
|
|
1724
|
-
if (!
|
|
2645
|
+
if (!semver3.valid(nestedPkg.version)) {
|
|
1725
2646
|
try {
|
|
1726
2647
|
const manifest = await manifestWithResilience(
|
|
1727
2648
|
resolvedSpec,
|
|
@@ -1760,6 +2681,169 @@ async function downloadPackagesImpl(packages, targetDir, logger, useCache, confi
|
|
|
1760
2681
|
}
|
|
1761
2682
|
return { packagePaths, resolution };
|
|
1762
2683
|
}
|
|
2684
|
+
function npmCacheDir(tmpDir) {
|
|
2685
|
+
return process.env.NPM_CACHE_DIR || getTmpPath(tmpDir, "cache", "npm");
|
|
2686
|
+
}
|
|
2687
|
+
function sha512Of(integrity) {
|
|
2688
|
+
return integrity?.split(/\s+/).find((value) => value.startsWith("sha512-")) ?? null;
|
|
2689
|
+
}
|
|
2690
|
+
async function readInstalledManifest(dir) {
|
|
2691
|
+
const manifest = await fs10.readJson(
|
|
2692
|
+
path10.join(dir, "package.json")
|
|
2693
|
+
);
|
|
2694
|
+
return manifest;
|
|
2695
|
+
}
|
|
2696
|
+
async function installResolvedTree(packages, resolution, targetDir, logger, context2) {
|
|
2697
|
+
const { npmConfig, cooldown, tmpDir } = context2;
|
|
2698
|
+
const cacheDir = npmCacheDir(tmpDir);
|
|
2699
|
+
const entries = {};
|
|
2700
|
+
const install = async (installName, spec, exempt, installPath) => {
|
|
2701
|
+
let picked;
|
|
2702
|
+
try {
|
|
2703
|
+
picked = await manifestWithResilience(
|
|
2704
|
+
`${installName}@${spec}`,
|
|
2705
|
+
cooldownOptions(npmConfig, cooldown, exempt),
|
|
2706
|
+
logger
|
|
2707
|
+
);
|
|
2708
|
+
if (!exempt) assertPublishedBefore(picked, cooldown, installPath);
|
|
2709
|
+
} catch (error) {
|
|
2710
|
+
throw cooldownFailure(installName, spec, installPath, cooldown, error);
|
|
2711
|
+
}
|
|
2712
|
+
if (aliasCrossesExemption(installName, picked.name, cooldown.exempt)) {
|
|
2713
|
+
throw new PackageTreeError([
|
|
2714
|
+
`${installPath}: ${installName} holds ${picked.name}, across an exempt prefix`
|
|
2715
|
+
]);
|
|
2716
|
+
}
|
|
2717
|
+
const resolved = picked._resolved;
|
|
2718
|
+
const registryIntegrity = picked._integrity || void 0;
|
|
2719
|
+
const dir = path10.join(targetDir, installPath);
|
|
2720
|
+
await fs10.ensureDir(path10.dirname(dir));
|
|
2721
|
+
const result = await extractWithResilience(
|
|
2722
|
+
resolved,
|
|
2723
|
+
dir,
|
|
2724
|
+
{
|
|
2725
|
+
...npmConfig,
|
|
2726
|
+
cache: cacheDir,
|
|
2727
|
+
...registryIntegrity ? { integrity: registryIntegrity } : {}
|
|
2728
|
+
},
|
|
2729
|
+
logger
|
|
2730
|
+
);
|
|
2731
|
+
let integrity = sha512Of(result.integrity) ?? sha512Of(registryIntegrity);
|
|
2732
|
+
if (integrity === null) {
|
|
2733
|
+
const bytes = await tarballWithResilience(resolved, {
|
|
2734
|
+
...npmConfig,
|
|
2735
|
+
cache: cacheDir,
|
|
2736
|
+
...registryIntegrity ? { integrity: registryIntegrity } : {}
|
|
2737
|
+
});
|
|
2738
|
+
integrity = `sha512-${createHash3("sha512").update(bytes).digest("base64")}`;
|
|
2739
|
+
}
|
|
2740
|
+
const edges = followedEdges(await readInstalledManifest(dir));
|
|
2741
|
+
entries[installPath] = {
|
|
2742
|
+
name: picked.name,
|
|
2743
|
+
version: picked.version,
|
|
2744
|
+
resolved,
|
|
2745
|
+
integrity,
|
|
2746
|
+
...Object.keys(edges).length > 0 ? { dependencies: edges } : {}
|
|
2747
|
+
};
|
|
2748
|
+
logger.debug(
|
|
2749
|
+
`Installed ${picked.name}@${picked.version} at ${installPath}`
|
|
2750
|
+
);
|
|
2751
|
+
};
|
|
2752
|
+
for (const [name, pkg] of resolution.topLevel) {
|
|
2753
|
+
if (pkg.localPath) {
|
|
2754
|
+
throw new PackageTreeError([
|
|
2755
|
+
`${name}: a local package cannot be part of a recorded tree`
|
|
2756
|
+
]);
|
|
2757
|
+
}
|
|
2758
|
+
await install(
|
|
2759
|
+
name,
|
|
2760
|
+
pkg.version,
|
|
2761
|
+
pkg.exempt === true,
|
|
2762
|
+
`node_modules/${name}`
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
for (const nestedPkg of resolution.nested) {
|
|
2766
|
+
for (const consumer of nestedPkg.consumers) {
|
|
2767
|
+
await install(
|
|
2768
|
+
nestedPkg.name,
|
|
2769
|
+
nestedPkg.version,
|
|
2770
|
+
nestedPkg.exempt === true,
|
|
2771
|
+
`node_modules/${consumer}/node_modules/${nestedPkg.name}`
|
|
2772
|
+
);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
const tree = {
|
|
2776
|
+
version: PACKAGE_TREE_VERSION,
|
|
2777
|
+
registry: npmConfig.registry,
|
|
2778
|
+
before: cooldown.before.toISOString(),
|
|
2779
|
+
packages: entries
|
|
2780
|
+
};
|
|
2781
|
+
return assertPackageTree(tree, {
|
|
2782
|
+
registry: npmConfig.registry,
|
|
2783
|
+
scopeRegistries: scopeRegistriesOf(npmConfig),
|
|
2784
|
+
declared: Object.fromEntries(
|
|
2785
|
+
packages.map((pkg) => [pkg.name, pkg.version])
|
|
2786
|
+
),
|
|
2787
|
+
exempt: cooldown.exempt
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
async function installPackageTree(tree, targetDir, logger, context2) {
|
|
2791
|
+
const { npmConfig, tmpDir, declared } = context2;
|
|
2792
|
+
const checked = assertPackageTree(tree, {
|
|
2793
|
+
registry: npmConfig.registry,
|
|
2794
|
+
scopeRegistries: scopeRegistriesOf(npmConfig),
|
|
2795
|
+
declared
|
|
2796
|
+
});
|
|
2797
|
+
const cacheDir = npmCacheDir(tmpDir);
|
|
2798
|
+
const ordered = Object.entries(checked.packages).sort(
|
|
2799
|
+
([a], [b]) => a.split("/").length - b.split("/").length || a.localeCompare(b)
|
|
2800
|
+
);
|
|
2801
|
+
for (const [installPath, entry] of ordered) {
|
|
2802
|
+
const dir = path10.join(targetDir, installPath);
|
|
2803
|
+
await fs10.ensureDir(path10.dirname(dir));
|
|
2804
|
+
try {
|
|
2805
|
+
await extractWithResilience(
|
|
2806
|
+
entry.resolved,
|
|
2807
|
+
dir,
|
|
2808
|
+
{ ...npmConfig, cache: cacheDir, integrity: entry.integrity },
|
|
2809
|
+
logger
|
|
2810
|
+
);
|
|
2811
|
+
} catch (error) {
|
|
2812
|
+
const code = readErrorCode(error);
|
|
2813
|
+
if (code === void 0 || !UNAVAILABLE_CODES.has(code)) throw error;
|
|
2814
|
+
const found = Reflect.get(Object(error), "found");
|
|
2815
|
+
const reason = code === "EINTEGRITY" ? `integrity ${entry.integrity} recorded, ${found === void 0 ? "other bytes" : String(found)} received` : "the registry does not serve it";
|
|
2816
|
+
throw new PackageUnavailableError(installPath, entry, reason);
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
const problems = [];
|
|
2820
|
+
const packagePaths = /* @__PURE__ */ new Map();
|
|
2821
|
+
for (const [installPath, entry] of ordered) {
|
|
2822
|
+
const dir = path10.join(targetDir, installPath);
|
|
2823
|
+
const installed = await readInstalledManifest(dir);
|
|
2824
|
+
if (installed.name !== entry.name || installed.version !== entry.version) {
|
|
2825
|
+
problems.push(
|
|
2826
|
+
`${installPath}: holds ${installed.name}@${installed.version}, recorded ${entry.name}@${entry.version}`
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
for (const [dep, spec] of Object.entries(followedEdges(installed))) {
|
|
2830
|
+
const target = edgeTargetName(dep, spec);
|
|
2831
|
+
const found = lookupPaths(installPath, dep).find(
|
|
2832
|
+
(candidate) => checked.packages[candidate] !== void 0
|
|
2833
|
+
);
|
|
2834
|
+
if (target === null || !found || checked.packages[found].name !== target) {
|
|
2835
|
+
problems.push(
|
|
2836
|
+
`${installPath}: dependency ${dep}@${spec} has no ${target ?? dep} in the tree`
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
const at = parseInstallPath(installPath);
|
|
2841
|
+
if (at && at.consumer === void 0) packagePaths.set(at.name, dir);
|
|
2842
|
+
}
|
|
2843
|
+
if (problems.length > 0) throw new PackageTreeError(problems);
|
|
2844
|
+
logger.debug(`Installed ${ordered.length} packages from the recorded tree`);
|
|
2845
|
+
return packagePaths;
|
|
2846
|
+
}
|
|
1763
2847
|
async function getCachedPackagePath(pkg, tmpDir) {
|
|
1764
2848
|
const cacheDir = getTmpPath(tmpDir, "cache", "packages");
|
|
1765
2849
|
const cacheKey = await getPackageCacheKey(pkg.name, pkg.version);
|
|
@@ -1852,6 +2936,27 @@ function assertDepsTraced(opts) {
|
|
|
1852
2936
|
(dep) => !opts.importedPackages.has(dep) && !isTraced(dep)
|
|
1853
2937
|
);
|
|
1854
2938
|
}
|
|
2939
|
+
async function missingEntry(packageDir) {
|
|
2940
|
+
let manifest;
|
|
2941
|
+
try {
|
|
2942
|
+
manifest = JSON.parse(
|
|
2943
|
+
await fs11.readFile(path11.join(packageDir, "package.json"), "utf8")
|
|
2944
|
+
);
|
|
2945
|
+
} catch {
|
|
2946
|
+
return void 0;
|
|
2947
|
+
}
|
|
2948
|
+
if (!manifest || typeof manifest !== "object") return void 0;
|
|
2949
|
+
const entries = ["main", "module", "exports"].flatMap((field) => stringLeaves(Reflect.get(manifest, field))).filter((entry) => !entry.includes("*") && !entry.endsWith("package.json"));
|
|
2950
|
+
if (entries.length === 0) return void 0;
|
|
2951
|
+
for (const entry of entries) {
|
|
2952
|
+
try {
|
|
2953
|
+
await fs11.access(path11.join(packageDir, entry));
|
|
2954
|
+
return void 0;
|
|
2955
|
+
} catch {
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
return entries[0];
|
|
2959
|
+
}
|
|
1855
2960
|
async function collectImportedPackages(file) {
|
|
1856
2961
|
let result;
|
|
1857
2962
|
try {
|
|
@@ -1923,7 +3028,7 @@ async function walkFiles(root, dir, visit) {
|
|
|
1923
3028
|
// src/commands/bundle/assert-consumer-deps.ts
|
|
1924
3029
|
import fs12 from "fs-extra";
|
|
1925
3030
|
import * as path12 from "path";
|
|
1926
|
-
import
|
|
3031
|
+
import semver4 from "semver";
|
|
1927
3032
|
async function assertConsumerDepsSatisfied(root, logger) {
|
|
1928
3033
|
const consumers = await collectInstalledPackages(
|
|
1929
3034
|
path12.join(root, "node_modules")
|
|
@@ -1944,13 +3049,13 @@ async function assertConsumerDepsSatisfied(root, logger) {
|
|
|
1944
3049
|
);
|
|
1945
3050
|
continue;
|
|
1946
3051
|
}
|
|
1947
|
-
if (
|
|
3052
|
+
if (semver4.validRange(declaredRange) === null && semver4.valid(declaredRange) === null) {
|
|
1948
3053
|
logger.debug(
|
|
1949
3054
|
`assertConsumerDepsSatisfied: skipping non-semver declared range ${depName}@${declaredRange} from ${pkg.name}`
|
|
1950
3055
|
);
|
|
1951
3056
|
continue;
|
|
1952
3057
|
}
|
|
1953
|
-
const satisfied =
|
|
3058
|
+
const satisfied = semver4.valid(declaredRange) ? declaredRange === installed.version : semver4.satisfies(installed.version, declaredRange, {
|
|
1954
3059
|
includePrerelease: true
|
|
1955
3060
|
});
|
|
1956
3061
|
if (!satisfied) {
|
|
@@ -2030,22 +3135,22 @@ function flowNeeds(flow) {
|
|
|
2030
3135
|
...Object.values(flow.destinations ?? {})
|
|
2031
3136
|
];
|
|
2032
3137
|
return {
|
|
2033
|
-
observe:
|
|
3138
|
+
observe: false,
|
|
2034
3139
|
stores: Object.keys(flow.stores ?? {}).length > 0,
|
|
2035
3140
|
state: steps.some(carriesState)
|
|
2036
3141
|
};
|
|
2037
3142
|
}
|
|
2038
|
-
function buildFlagDefines(needs) {
|
|
3143
|
+
function buildFlagDefines(needs, observe) {
|
|
2039
3144
|
if (!needs) {
|
|
2040
3145
|
return {
|
|
2041
|
-
__WALKEROS_OBSERVE__:
|
|
3146
|
+
__WALKEROS_OBSERVE__: String(observe),
|
|
2042
3147
|
__WALKEROS_STORES__: "true",
|
|
2043
3148
|
__WALKEROS_STATE__: "true",
|
|
2044
3149
|
__WALKEROS_VALIDATE__: "true"
|
|
2045
3150
|
};
|
|
2046
3151
|
}
|
|
2047
3152
|
return {
|
|
2048
|
-
__WALKEROS_OBSERVE__: String(
|
|
3153
|
+
__WALKEROS_OBSERVE__: String(observe),
|
|
2049
3154
|
__WALKEROS_STORES__: String(needs.stores),
|
|
2050
3155
|
__WALKEROS_STATE__: String(needs.state),
|
|
2051
3156
|
__WALKEROS_VALIDATE__: "false"
|
|
@@ -2148,21 +3253,21 @@ async function ensureCodeOnDisk(codeContent, compiledCode, tmpDir, inputs) {
|
|
|
2148
3253
|
}
|
|
2149
3254
|
|
|
2150
3255
|
// src/version.ts
|
|
2151
|
-
import { readFileSync } from "fs";
|
|
3256
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2152
3257
|
import { fileURLToPath } from "url";
|
|
2153
|
-
import { dirname as dirname3, join as
|
|
3258
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
2154
3259
|
var versionFilename = fileURLToPath(import.meta.url);
|
|
2155
3260
|
var versionDirname = dirname3(versionFilename);
|
|
2156
3261
|
function findPackageJson() {
|
|
2157
3262
|
const paths = [
|
|
2158
|
-
|
|
3263
|
+
join5(versionDirname, "../package.json"),
|
|
2159
3264
|
// dist/ or src/
|
|
2160
|
-
|
|
3265
|
+
join5(versionDirname, "../../package.json")
|
|
2161
3266
|
// src/core/ (not used, but safe)
|
|
2162
3267
|
];
|
|
2163
3268
|
for (const p of paths) {
|
|
2164
3269
|
try {
|
|
2165
|
-
return
|
|
3270
|
+
return readFileSync3(p, "utf-8");
|
|
2166
3271
|
} catch {
|
|
2167
3272
|
}
|
|
2168
3273
|
}
|
|
@@ -2268,6 +3373,46 @@ async function resolvedVersionLines(topLevel, packagePaths) {
|
|
|
2268
3373
|
}
|
|
2269
3374
|
return lines;
|
|
2270
3375
|
}
|
|
3376
|
+
async function installPackages(packagesArray, targetDir, cacheDir, buildOptions, npmConfig, logger) {
|
|
3377
|
+
const install = buildOptions.packageInstall;
|
|
3378
|
+
if (install && "tree" in install) {
|
|
3379
|
+
const packagePaths2 = await installPackageTree(
|
|
3380
|
+
install.tree,
|
|
3381
|
+
targetDir,
|
|
3382
|
+
logger,
|
|
3383
|
+
{
|
|
3384
|
+
npmConfig,
|
|
3385
|
+
tmpDir: cacheDir,
|
|
3386
|
+
declared: Object.fromEntries(
|
|
3387
|
+
packagesArray.map((pkg) => [pkg.name, pkg.version])
|
|
3388
|
+
)
|
|
3389
|
+
}
|
|
3390
|
+
);
|
|
3391
|
+
return { packagePaths: packagePaths2, versionLines: treeVersionLines(install.tree) };
|
|
3392
|
+
}
|
|
3393
|
+
const { packagePaths, resolution, tree } = await downloadPackagesWithResolution(
|
|
3394
|
+
packagesArray,
|
|
3395
|
+
targetDir,
|
|
3396
|
+
logger,
|
|
3397
|
+
// A recorded tree is built from what was extracted, never a cache copy.
|
|
3398
|
+
install ? false : buildOptions.cache,
|
|
3399
|
+
buildOptions.configDir,
|
|
3400
|
+
// For resolving relative local paths
|
|
3401
|
+
cacheDir,
|
|
3402
|
+
buildOptions.overrides,
|
|
3403
|
+
npmConfig,
|
|
3404
|
+
install?.resolve
|
|
3405
|
+
);
|
|
3406
|
+
if (install) {
|
|
3407
|
+
if (!tree) throw new Error("A resolve under a cutoff returned no tree");
|
|
3408
|
+
install.onTree(tree);
|
|
3409
|
+
return { packagePaths, versionLines: treeVersionLines(tree) };
|
|
3410
|
+
}
|
|
3411
|
+
return {
|
|
3412
|
+
packagePaths,
|
|
3413
|
+
versionLines: await resolvedVersionLines(resolution.topLevel, packagePaths)
|
|
3414
|
+
};
|
|
3415
|
+
}
|
|
2271
3416
|
async function bundleCore(flowSettings, buildOptions, logger, showStats = false, provenance) {
|
|
2272
3417
|
const bundleStartTime = Date.now();
|
|
2273
3418
|
const CACHE_DIR = buildOptions.tempDir || getTmpPath();
|
|
@@ -2296,16 +3441,13 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false,
|
|
|
2296
3441
|
// Pass local path if defined
|
|
2297
3442
|
})
|
|
2298
3443
|
);
|
|
2299
|
-
const { packagePaths,
|
|
3444
|
+
const { packagePaths, versionLines } = await installPackages(
|
|
2300
3445
|
packagesArray,
|
|
2301
3446
|
TEMP_DIR,
|
|
2302
|
-
logger,
|
|
2303
|
-
buildOptions.cache,
|
|
2304
|
-
buildOptions.configDir,
|
|
2305
|
-
// For resolving relative local paths
|
|
2306
3447
|
CACHE_DIR,
|
|
2307
|
-
buildOptions
|
|
2308
|
-
npmConfig
|
|
3448
|
+
buildOptions,
|
|
3449
|
+
npmConfig,
|
|
3450
|
+
logger
|
|
2309
3451
|
);
|
|
2310
3452
|
const warnedBundleFieldPackages = /* @__PURE__ */ new Set();
|
|
2311
3453
|
for (const [pkgName, pkgPath] of packagePaths.entries()) {
|
|
@@ -2344,10 +3486,7 @@ async function bundleCore(flowSettings, buildOptions, logger, showStats = false,
|
|
|
2344
3486
|
if (process.env.BUNDLER_STRICT_RANGES !== "0") {
|
|
2345
3487
|
await assertConsumerDepsSatisfied(TEMP_DIR, logger);
|
|
2346
3488
|
}
|
|
2347
|
-
const sortedVersions =
|
|
2348
|
-
resolutionResult.topLevel,
|
|
2349
|
-
packagePaths
|
|
2350
|
-
);
|
|
3489
|
+
const sortedVersions = versionLines;
|
|
2351
3490
|
const versionsHash = await getHashServer3(sortedVersions.join("\n"), 12);
|
|
2352
3491
|
applyCollectorDefaults(flowSettings);
|
|
2353
3492
|
if (provenance) {
|
|
@@ -2497,8 +3636,7 @@ ${dataDeclaration}${needs}`;
|
|
|
2497
3636
|
const stage2Entry = (buildOptions.platform || "node") === "browser" ? generateWebEntry(stage1Path, dataPayload, {
|
|
2498
3637
|
windowCollector: buildOptions.windowCollector,
|
|
2499
3638
|
windowElb: buildOptions.windowElb,
|
|
2500
|
-
platform: buildOptions.platform
|
|
2501
|
-
observe: readObserveConnect(flowSettings, logger)
|
|
3639
|
+
platform: buildOptions.platform
|
|
2502
3640
|
}) : generateServerEntry(stage1Path, dataPayload);
|
|
2503
3641
|
const stage2EntryPath = path14.join(TEMP_DIR, "stage2.mjs");
|
|
2504
3642
|
await fs14.writeFile(stage2EntryPath, stage2Entry);
|
|
@@ -2525,7 +3663,7 @@ ${dataDeclaration}${needs}`;
|
|
|
2525
3663
|
stage2Options.define = {
|
|
2526
3664
|
"process.env.NODE_ENV": '"production"',
|
|
2527
3665
|
global: "globalThis",
|
|
2528
|
-
...buildFlagDefines(flowNeeds(flowSettings))
|
|
3666
|
+
...buildFlagDefines(flowNeeds(flowSettings), false)
|
|
2529
3667
|
};
|
|
2530
3668
|
stage2Options.target = resolveTarget(buildOptions);
|
|
2531
3669
|
} else {
|
|
@@ -2742,8 +3880,11 @@ async function runNftServerPath(outputPath, flowSettings, buildOptions, tempDir,
|
|
|
2742
3880
|
importedPackages: await collectImportedPackages(outputPath)
|
|
2743
3881
|
});
|
|
2744
3882
|
for (const name of unused) {
|
|
3883
|
+
const entry = await missingEntry(
|
|
3884
|
+
path14.join(tempDir, "node_modules", name)
|
|
3885
|
+
);
|
|
2745
3886
|
logger.warn(
|
|
2746
|
-
`Package ${name} is declared in bundle.packages but nothing imports it; it is not in the bundle. Remove it from bundle.packages if unused.`
|
|
3887
|
+
entry ? `Package ${name} is declared in bundle.packages, but its installed copy has no ${entry}; it is not in the bundle.` : `Package ${name} is declared in bundle.packages but nothing imports it; it is not in the bundle. Remove it from bundle.packages if unused.`
|
|
2747
3888
|
);
|
|
2748
3889
|
}
|
|
2749
3890
|
}
|
|
@@ -3197,66 +4338,12 @@ ${HOST_LOGGER_MERGE}
|
|
|
3197
4338
|
|
|
3198
4339
|
const result = await startFlow(config);
|
|
3199
4340
|
|
|
3200
|
-
// Telemetry observer installation: the host (pipeline.ts) builds the
|
|
3201
|
-
// observer functions via createTelemetryObserver + createBatchedPoster
|
|
3202
|
-
// and forwards them through context. Added to collector.observers so the
|
|
3203
|
-
// runtime self-emission loop drives them.
|
|
3204
|
-
if (context.observers) {
|
|
3205
|
-
for (const observer of context.observers) {
|
|
3206
|
-
result.collector.observers.add(observer);
|
|
3207
|
-
}
|
|
3208
|
-
}
|
|
3209
|
-
|
|
3210
|
-
// The host forwards the level supplier beside the observers; the collector
|
|
3211
|
-
// capture path reads it to decide destination call capture at trace.
|
|
3212
|
-
if (context.observeLevel) result.collector.observeLevel = context.observeLevel;
|
|
3213
|
-
|
|
3214
4341
|
const httpSource = Object.values(result.collector.sources || {})
|
|
3215
4342
|
.find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
|
|
3216
4343
|
|
|
3217
4344
|
return { ...result, httpHandler: httpSource ? httpSource.httpHandler : undefined };
|
|
3218
4345
|
}`;
|
|
3219
4346
|
}
|
|
3220
|
-
function readObserveConnect(flowSettings, logger) {
|
|
3221
|
-
const observe = flowSettings.config?.observe;
|
|
3222
|
-
if (!observe) return void 0;
|
|
3223
|
-
const { url, binding, level, sample } = observe;
|
|
3224
|
-
if (url === void 0 && binding === void 0) return void 0;
|
|
3225
|
-
const trimmedUrl = typeof url === "string" ? url.trim() : "";
|
|
3226
|
-
const trimmedBinding = typeof binding === "string" ? binding.trim() : "";
|
|
3227
|
-
if (trimmedUrl !== "" && trimmedBinding !== "") {
|
|
3228
|
-
return {
|
|
3229
|
-
url: trimmedUrl,
|
|
3230
|
-
binding: trimmedBinding,
|
|
3231
|
-
// Same JSON-borne prudence as the pair: only known-shaped public
|
|
3232
|
-
// values reach the emitted literal.
|
|
3233
|
-
...level === "off" || level === "standard" || level === "trace" ? { level } : {},
|
|
3234
|
-
// Sample is a fraction in [0, 1] per the Flow.Observe contract; the
|
|
3235
|
-
// range test also drops NaN and the infinities.
|
|
3236
|
-
...typeof sample === "number" && sample >= 0 && sample <= 1 ? { sample } : {}
|
|
3237
|
-
};
|
|
3238
|
-
}
|
|
3239
|
-
const missing = [
|
|
3240
|
-
...trimmedUrl === "" ? ["url"] : [],
|
|
3241
|
-
...trimmedBinding === "" ? ["binding"] : []
|
|
3242
|
-
].join(" and ");
|
|
3243
|
-
logger?.warn(
|
|
3244
|
-
`config.observe has no effect: missing or empty ${missing}. Observe connect wiring was skipped; set both config.observe.url and config.observe.binding to non-empty public values.`
|
|
3245
|
-
);
|
|
3246
|
-
return void 0;
|
|
3247
|
-
}
|
|
3248
|
-
var OBSERVE_SLOT_IMPORT = "import { pageObserveSlot } from '@walkeros/web-core';\n";
|
|
3249
|
-
function emitObserveAssignment(observe) {
|
|
3250
|
-
const literal = {
|
|
3251
|
-
url: observe.url,
|
|
3252
|
-
binding: observe.binding,
|
|
3253
|
-
...observe.flowId !== void 0 ? { flowId: observe.flowId } : {},
|
|
3254
|
-
...observe.level !== void 0 ? { level: observe.level } : {},
|
|
3255
|
-
...observe.sample !== void 0 ? { sample: observe.sample } : {}
|
|
3256
|
-
};
|
|
3257
|
-
return `
|
|
3258
|
-
config.observe = { ...${JSON.stringify(literal)}, slot: pageObserveSlot() };`;
|
|
3259
|
-
}
|
|
3260
4347
|
var MOIN_IMPORT = "import { moin } from '@walkeros/web-core';\n";
|
|
3261
4348
|
function emitMoinCall(target) {
|
|
3262
4349
|
if (!target)
|
|
@@ -3305,16 +4392,14 @@ function generateWebEntry(stage1Path, dataPayload, options = {}) {
|
|
|
3305
4392
|
}
|
|
3306
4393
|
}` : "";
|
|
3307
4394
|
const stage1Specifier = toFileImportSpecifier(stage1Path);
|
|
3308
|
-
const observeImport = options.observe ? OBSERVE_SLOT_IMPORT : "";
|
|
3309
|
-
const observeBlock = options.observe ? emitObserveAssignment(options.observe) : "";
|
|
3310
4395
|
const loaderImport = platform === "browser" ? MOIN_IMPORT : "";
|
|
3311
4396
|
const loaderCall = platform === "browser" ? emitMoinCall() : "";
|
|
3312
|
-
return `${loaderImport}
|
|
4397
|
+
return `${loaderImport}import { startFlow, wireConfig } from '${stage1Specifier}';
|
|
3313
4398
|
|
|
3314
4399
|
const __configData = ${dataPayload};
|
|
3315
4400
|
|
|
3316
4401
|
(async () => {${loaderCall}
|
|
3317
|
-
const config = wireConfig(__configData);${envBlock}
|
|
4402
|
+
const config = wireConfig(__configData);${envBlock}
|
|
3318
4403
|
const { collector, elb } = await startFlow(config);${assignmentCode}
|
|
3319
4404
|
})();`;
|
|
3320
4405
|
}
|
|
@@ -3326,39 +4411,10 @@ function generateWrapEntry(stage1Path, options = {}) {
|
|
|
3326
4411
|
);
|
|
3327
4412
|
}
|
|
3328
4413
|
const assignmentCode = assignments.length > 0 ? "\n" + assignments.join("\n") : "";
|
|
3329
|
-
if (options.preview !== void 0 && options.previewGrantTargets !== void 0) {
|
|
3330
|
-
throw new Error(
|
|
3331
|
-
"generateWrapEntry: `preview` (host activator) and `previewGrantTargets` (preview-artifact injection) are mutually exclusive. A host bundle activates a preview; an artifact injects its grant. Pass at most one."
|
|
3332
|
-
);
|
|
3333
|
-
}
|
|
3334
4414
|
const preview = options.preview;
|
|
3335
4415
|
const previewEnabled = !!preview?.enabled;
|
|
3336
4416
|
const previewImport = previewEnabled ? `import { browserSwapActivator } from '@walkeros/web-core';
|
|
3337
4417
|
` : "";
|
|
3338
|
-
const grantTargets = options.previewGrantTargets ?? [];
|
|
3339
|
-
const previewGrantBlock = grantTargets.length > 0 ? `
|
|
3340
|
-
// --- Preview grant injection ---
|
|
3341
|
-
try {
|
|
3342
|
-
const __previewGrant =
|
|
3343
|
-
typeof localStorage !== 'undefined'
|
|
3344
|
-
? localStorage.getItem('elbPreviewSession')
|
|
3345
|
-
: null;
|
|
3346
|
-
if (__previewGrant && config.destinations) {
|
|
3347
|
-
${grantTargets.map((key) => {
|
|
3348
|
-
const ref = `config.destinations[${singleQuoteLiteral(key)}]`;
|
|
3349
|
-
return ` if (${ref}) {
|
|
3350
|
-
const __dest = ${ref};
|
|
3351
|
-
const __cfg = __dest.config || (__dest.config = {});
|
|
3352
|
-
const __settings = __cfg.settings || (__cfg.settings = {});
|
|
3353
|
-
const __headers = __settings.headers || (__settings.headers = {});
|
|
3354
|
-
__headers['X-Walkeros-Preview'] = __previewGrant;
|
|
3355
|
-
}`;
|
|
3356
|
-
}).join("\n")}
|
|
3357
|
-
}
|
|
3358
|
-
} catch (__err) {
|
|
3359
|
-
// localStorage can throw (Safari private mode); boot without the grant.
|
|
3360
|
-
}
|
|
3361
|
-
// --- End preview grant injection ---` : "";
|
|
3362
4418
|
const previewBlock = previewEnabled && preview ? `
|
|
3363
4419
|
// --- Preview activation ---
|
|
3364
4420
|
if (await browserSwapActivator(${JSON.stringify({
|
|
@@ -3382,21 +4438,24 @@ ${grantTargets.map((key) => {
|
|
|
3382
4438
|
env.document = env.document ?? (typeof document !== 'undefined' ? document : undefined);
|
|
3383
4439
|
}
|
|
3384
4440
|
}` : "";
|
|
4441
|
+
const observe = options.observe && {
|
|
4442
|
+
preview: {
|
|
4443
|
+
url: options.observe.preview.url,
|
|
4444
|
+
token: options.observe.preview.token
|
|
4445
|
+
}
|
|
4446
|
+
};
|
|
4447
|
+
const observeBlock = observe ? `
|
|
4448
|
+
config.observe = ${JSON.stringify(observe)};` : "";
|
|
3385
4449
|
const stage1Specifier = toFileImportSpecifier(stage1Path);
|
|
3386
|
-
const observeImport = options.observe ? OBSERVE_SLOT_IMPORT : "";
|
|
3387
|
-
const observeBlock = options.observe ? emitObserveAssignment(options.observe) : "";
|
|
3388
4450
|
const loaderImport = platform === "browser" ? MOIN_IMPORT : "";
|
|
3389
4451
|
const loaderCall = platform === "browser" ? emitMoinCall(options.moin) : "";
|
|
3390
|
-
return `${loaderImport}${previewImport}
|
|
4452
|
+
return `${loaderImport}${previewImport}import { startFlow, wireConfig, __configData } from '${stage1Specifier}';
|
|
3391
4453
|
|
|
3392
4454
|
(async () => {${loaderCall}${previewBlock}
|
|
3393
|
-
const config = wireConfig(__configData);${envBlock}${
|
|
4455
|
+
const config = wireConfig(__configData);${envBlock}${observeBlock}
|
|
3394
4456
|
const { collector, elb } = await startFlow(config);${assignmentCode}
|
|
3395
4457
|
})();`;
|
|
3396
4458
|
}
|
|
3397
|
-
function singleQuoteLiteral(value) {
|
|
3398
|
-
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
3399
|
-
}
|
|
3400
4459
|
function generateWrapEntryServer(stage1Path) {
|
|
3401
4460
|
const stage1Specifier = toFileImportSpecifier(stage1Path);
|
|
3402
4461
|
return `import { startFlow, wireConfig, __configData } from '${stage1Specifier}';
|
|
@@ -3416,20 +4475,6 @@ ${HOST_LOGGER_MERGE}
|
|
|
3416
4475
|
|
|
3417
4476
|
const result = await startFlow(config);
|
|
3418
4477
|
|
|
3419
|
-
// Telemetry observer installation: the host (pipeline.ts) builds the
|
|
3420
|
-
// observer functions via createTelemetryObserver + createBatchedPoster
|
|
3421
|
-
// and forwards them through context. Added to collector.observers so the
|
|
3422
|
-
// runtime self-emission loop drives them.
|
|
3423
|
-
if (context.observers) {
|
|
3424
|
-
for (const observer of context.observers) {
|
|
3425
|
-
result.collector.observers.add(observer);
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
// The host forwards the level supplier beside the observers; the collector
|
|
3430
|
-
// capture path reads it to decide destination call capture at trace.
|
|
3431
|
-
if (context.observeLevel) result.collector.observeLevel = context.observeLevel;
|
|
3432
|
-
|
|
3433
4478
|
const httpSource = Object.values(result.collector.sources || {})
|
|
3434
4479
|
.find(s => 'httpHandler' in s && typeof s.httpHandler === 'function');
|
|
3435
4480
|
|
|
@@ -4077,6 +5122,7 @@ function applyOverrides(config, overrides) {
|
|
|
4077
5122
|
}
|
|
4078
5123
|
}
|
|
4079
5124
|
config.run = true;
|
|
5125
|
+
delete config.observe;
|
|
4080
5126
|
if (overrides.transformerMocks) {
|
|
4081
5127
|
const transformers = config.transformers;
|
|
4082
5128
|
if (transformers) {
|
|
@@ -5311,9 +6357,11 @@ function collectFailedDestinations(collector) {
|
|
|
5311
6357
|
}
|
|
5312
6358
|
function buildFailureSummary(failedIds, collector) {
|
|
5313
6359
|
const view = collector !== null && typeof collector === "object" ? collector : {};
|
|
5314
|
-
const dests = view.destinations ?? {};
|
|
6360
|
+
const dests = Object.entries(view.destinations ?? {});
|
|
5315
6361
|
const labels = failedIds.map((id) => {
|
|
5316
|
-
const type = dests
|
|
6362
|
+
const type = dests.find(
|
|
6363
|
+
([key, dest]) => (dest.config?.id || key) === id
|
|
6364
|
+
)?.[1].type;
|
|
5317
6365
|
return type ? `${id} (${type})` : id;
|
|
5318
6366
|
});
|
|
5319
6367
|
const noun = failedIds.length === 1 ? "destination" : "destinations";
|
|
@@ -6515,7 +7563,7 @@ async function readStepPackageInfo(resolved, options) {
|
|
|
6515
7563
|
}
|
|
6516
7564
|
|
|
6517
7565
|
// src/commands/validate/validators/settings.ts
|
|
6518
|
-
var CLIENT_HEADER = "walkeros-cli/4.
|
|
7566
|
+
var CLIENT_HEADER = "walkeros-cli/4.10.0-next-1794522704623";
|
|
6519
7567
|
var LOGGER = createLogger();
|
|
6520
7568
|
function createSettingsContext(configDir) {
|
|
6521
7569
|
return { configDir, cache: /* @__PURE__ */ new Map(), compiled: /* @__PURE__ */ new Map() };
|
|
@@ -7988,7 +9036,7 @@ function exitCodeOf(result, strict) {
|
|
|
7988
9036
|
}
|
|
7989
9037
|
|
|
7990
9038
|
// src/commands/auth/index.ts
|
|
7991
|
-
import { z as
|
|
9039
|
+
import { z as z6 } from "zod";
|
|
7992
9040
|
|
|
7993
9041
|
// src/core/api-error.ts
|
|
7994
9042
|
var EXIT_RETRYABLE = 75;
|
|
@@ -8017,191 +9065,57 @@ var ApiError = class extends Error {
|
|
|
8017
9065
|
this.client = options?.client;
|
|
8018
9066
|
this.upgrade = options?.upgrade;
|
|
8019
9067
|
this.docs = options?.docs;
|
|
8020
|
-
}
|
|
8021
|
-
};
|
|
8022
|
-
function machineReadableErrorLine(err) {
|
|
8023
|
-
const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
|
|
8024
|
-
const retryable = err instanceof ApiError ? err.retryable === true : false;
|
|
8025
|
-
const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
|
|
8026
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
8027
|
-
return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
|
|
8028
|
-
}
|
|
8029
|
-
function handleCliError(err) {
|
|
8030
|
-
console.error(machineReadableErrorLine(err));
|
|
8031
|
-
if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
|
|
8032
|
-
console.error(`
|
|
8033
|
-
${err.message}
|
|
8034
|
-
`);
|
|
8035
|
-
if (err.upgrade) console.error(` Upgrade: ${err.upgrade}`);
|
|
8036
|
-
if (err.docs) console.error(` Docs: ${err.docs}
|
|
8037
|
-
`);
|
|
8038
|
-
process.exit(2);
|
|
8039
|
-
}
|
|
8040
|
-
if (err instanceof ApiError && err.retryable) {
|
|
8041
|
-
const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
|
|
8042
|
-
console.error(`${err.message}${hint}`);
|
|
8043
|
-
process.exit(EXIT_RETRYABLE);
|
|
8044
|
-
}
|
|
8045
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
8046
|
-
process.exit(1);
|
|
8047
|
-
}
|
|
8048
|
-
|
|
8049
|
-
// src/core/app-fetch.ts
|
|
8050
|
-
import { z } from "zod";
|
|
8051
|
-
|
|
8052
|
-
// src/lib/config-file.ts
|
|
8053
|
-
import {
|
|
8054
|
-
readFileSync as readFileSync3,
|
|
8055
|
-
writeFileSync,
|
|
8056
|
-
mkdirSync,
|
|
8057
|
-
unlinkSync,
|
|
8058
|
-
chmodSync,
|
|
8059
|
-
renameSync
|
|
8060
|
-
} from "fs";
|
|
8061
|
-
import { randomBytes } from "crypto";
|
|
8062
|
-
import { join as join5 } from "path";
|
|
8063
|
-
import { homedir } from "os";
|
|
8064
|
-
function getConfigDir() {
|
|
8065
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
8066
|
-
const base = xdgConfig || join5(homedir(), ".config");
|
|
8067
|
-
return join5(base, "walkeros");
|
|
8068
|
-
}
|
|
8069
|
-
function getConfigPath() {
|
|
8070
|
-
return join5(getConfigDir(), "config.json");
|
|
8071
|
-
}
|
|
8072
|
-
function readConfig() {
|
|
8073
|
-
const configPath = getConfigPath();
|
|
8074
|
-
try {
|
|
8075
|
-
const content = readFileSync3(configPath, "utf-8");
|
|
8076
|
-
return JSON.parse(content);
|
|
8077
|
-
} catch {
|
|
8078
|
-
return null;
|
|
8079
|
-
}
|
|
8080
|
-
}
|
|
8081
|
-
function replaceConfigFile(config) {
|
|
8082
|
-
const dir = getConfigDir();
|
|
8083
|
-
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
8084
|
-
const configPath = getConfigPath();
|
|
8085
|
-
const tempPath = `${configPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
8086
|
-
try {
|
|
8087
|
-
writeFileSync(tempPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
8088
|
-
chmodSync(tempPath, 384);
|
|
8089
|
-
renameSync(tempPath, configPath);
|
|
8090
|
-
} catch (error) {
|
|
8091
|
-
try {
|
|
8092
|
-
unlinkSync(tempPath);
|
|
8093
|
-
} catch {
|
|
8094
|
-
}
|
|
8095
|
-
throw error;
|
|
8096
|
-
}
|
|
8097
|
-
}
|
|
8098
|
-
function writeConfig(config) {
|
|
8099
|
-
replaceConfigFile({ ...readConfig() ?? {}, ...config });
|
|
8100
|
-
}
|
|
8101
|
-
function writeTelemetryOnlyConfig(partial) {
|
|
8102
|
-
writeConfig(partial);
|
|
8103
|
-
}
|
|
8104
|
-
function resolveAppUrl() {
|
|
8105
|
-
const envUrl = process.env.WALKEROS_APP_URL;
|
|
8106
|
-
if (envUrl) return envUrl;
|
|
8107
|
-
const config = readConfig();
|
|
8108
|
-
if (config?.appUrl) return config.appUrl;
|
|
8109
|
-
return "https://app.walkeros.io";
|
|
8110
|
-
}
|
|
8111
|
-
|
|
8112
|
-
// src/lib/secure-url.ts
|
|
8113
|
-
function isLoopback(hostname) {
|
|
8114
|
-
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
8115
|
-
}
|
|
8116
|
-
function requireSecureUrl(url) {
|
|
8117
|
-
let parsed;
|
|
8118
|
-
try {
|
|
8119
|
-
parsed = new URL(url);
|
|
8120
|
-
} catch {
|
|
8121
|
-
return url;
|
|
8122
|
-
}
|
|
8123
|
-
if (parsed.protocol !== "http:" || isLoopback(parsed.hostname)) return url;
|
|
8124
|
-
throw new Error(
|
|
8125
|
-
`Refusing to send walkerOS credentials over plain http to ${url}. Use https, or localhost / 127.0.0.1 / [::1] for local development.`
|
|
8126
|
-
);
|
|
8127
|
-
}
|
|
8128
|
-
|
|
8129
|
-
// src/core/client-context.ts
|
|
8130
|
-
var context;
|
|
8131
|
-
function setClientContext(input) {
|
|
8132
|
-
const envType = process.env.WALKEROS_CLIENT_TYPE;
|
|
8133
|
-
const type = envType ?? input.type ?? "cli";
|
|
8134
|
-
context = { type, version: input.version };
|
|
8135
|
-
}
|
|
8136
|
-
function getClientContext() {
|
|
8137
|
-
return context;
|
|
8138
|
-
}
|
|
8139
|
-
function resetClientContext() {
|
|
8140
|
-
context = void 0;
|
|
8141
|
-
}
|
|
8142
|
-
function clientContextHeaders() {
|
|
8143
|
-
if (!context) return {};
|
|
8144
|
-
return {
|
|
8145
|
-
"User-Agent": `walkeros-${context.type}/${context.version}`,
|
|
8146
|
-
"X-WalkerOS-Client": context.type,
|
|
8147
|
-
"X-WalkerOS-Client-Version": context.version
|
|
8148
|
-
};
|
|
8149
|
-
}
|
|
8150
|
-
|
|
8151
|
-
// src/core/app-fetch.ts
|
|
8152
|
-
var AppErrorSchema = z.object({
|
|
8153
|
-
error: z.object({ message: z.string().min(1) })
|
|
8154
|
-
});
|
|
8155
|
-
function appErrorMessage(body) {
|
|
8156
|
-
const parsed = AppErrorSchema.safeParse(body);
|
|
8157
|
-
return parsed.success ? parsed.data.error.message : void 0;
|
|
9068
|
+
}
|
|
9069
|
+
};
|
|
9070
|
+
function machineReadableErrorLine(err) {
|
|
9071
|
+
const code = err instanceof ApiError ? err.code ?? "UNKNOWN" : "UNKNOWN";
|
|
9072
|
+
const retryable = err instanceof ApiError ? err.retryable === true : false;
|
|
9073
|
+
const retryAfter = err instanceof ApiError && err.retryAfterSeconds !== void 0 ? ` retryAfter=${err.retryAfterSeconds}` : "";
|
|
9074
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9075
|
+
return `error: code=${code} retryable=${retryable}${retryAfter} message=${message}`;
|
|
8158
9076
|
}
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
if (
|
|
8171
|
-
|
|
8172
|
-
|
|
9077
|
+
function handleCliError(err) {
|
|
9078
|
+
console.error(machineReadableErrorLine(err));
|
|
9079
|
+
if (err instanceof ApiError && err.code === "CLIENT_OUTDATED") {
|
|
9080
|
+
console.error(`
|
|
9081
|
+
${err.message}
|
|
9082
|
+
`);
|
|
9083
|
+
if (err.upgrade) console.error(` Upgrade: ${err.upgrade}`);
|
|
9084
|
+
if (err.docs) console.error(` Docs: ${err.docs}
|
|
9085
|
+
`);
|
|
9086
|
+
process.exit(2);
|
|
9087
|
+
}
|
|
9088
|
+
if (err instanceof ApiError && err.retryable) {
|
|
9089
|
+
const hint = err.retryAfterSeconds !== void 0 ? ` Retry after ${err.retryAfterSeconds}s.` : " This is temporary, retry shortly.";
|
|
9090
|
+
console.error(`${err.message}${hint}`);
|
|
9091
|
+
process.exit(EXIT_RETRYABLE);
|
|
9092
|
+
}
|
|
9093
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
9094
|
+
process.exit(1);
|
|
8173
9095
|
}
|
|
8174
9096
|
|
|
8175
|
-
// src/core/app-paths.ts
|
|
8176
|
-
var APP_PATHS = {
|
|
8177
|
-
whoami: "/api/auth/whoami",
|
|
8178
|
-
health: "/api/health",
|
|
8179
|
-
telemetry: "/api/telemetry",
|
|
8180
|
-
openapi: "/api/openapi.json"
|
|
8181
|
-
};
|
|
8182
|
-
|
|
8183
9097
|
// src/commands/api/index.ts
|
|
8184
9098
|
import { once } from "events";
|
|
8185
|
-
import { readFile as
|
|
8186
|
-
import { z as
|
|
8187
|
-
var NOT_SIGNED_IN = "
|
|
9099
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
9100
|
+
import { z as z5 } from "zod";
|
|
9101
|
+
var NOT_SIGNED_IN = "not signed in: run walkeros login, or set WALKEROS_TOKEN to a token from Account > Automation tokens in the walkerOS app";
|
|
8188
9102
|
var ERROR_BODY_LIMIT = 64 * 1024;
|
|
8189
|
-
var DocumentSchema =
|
|
8190
|
-
paths:
|
|
8191
|
-
components:
|
|
9103
|
+
var DocumentSchema = z5.object({
|
|
9104
|
+
paths: z5.record(z5.string(), z5.record(z5.string(), z5.unknown())),
|
|
9105
|
+
components: z5.record(z5.string(), z5.record(z5.string(), z5.unknown())).optional()
|
|
8192
9106
|
});
|
|
8193
|
-
var OperationSchema =
|
|
8194
|
-
summary:
|
|
8195
|
-
description:
|
|
8196
|
-
parameters:
|
|
8197
|
-
requestBody:
|
|
8198
|
-
responses:
|
|
9107
|
+
var OperationSchema = z5.object({
|
|
9108
|
+
summary: z5.string().optional(),
|
|
9109
|
+
description: z5.string().optional(),
|
|
9110
|
+
parameters: z5.array(z5.unknown()).optional(),
|
|
9111
|
+
requestBody: z5.unknown().optional(),
|
|
9112
|
+
responses: z5.record(z5.string(), z5.unknown()).optional()
|
|
8199
9113
|
});
|
|
8200
|
-
var OptionsSchema =
|
|
8201
|
-
data:
|
|
8202
|
-
header:
|
|
8203
|
-
project:
|
|
8204
|
-
yes:
|
|
9114
|
+
var OptionsSchema = z5.object({
|
|
9115
|
+
data: z5.string().optional(),
|
|
9116
|
+
header: z5.array(z5.string()).default([]),
|
|
9117
|
+
project: z5.string().optional(),
|
|
9118
|
+
yes: z5.boolean().optional()
|
|
8205
9119
|
});
|
|
8206
9120
|
|
|
8207
9121
|
// src/commands/auth/index.ts
|
|
@@ -8212,34 +9126,44 @@ async function readJson2(response) {
|
|
|
8212
9126
|
return null;
|
|
8213
9127
|
}
|
|
8214
9128
|
}
|
|
8215
|
-
async function
|
|
8216
|
-
const
|
|
8217
|
-
if (!
|
|
8218
|
-
const response = await
|
|
9129
|
+
async function identify() {
|
|
9130
|
+
const credential = await resolveCredential();
|
|
9131
|
+
if (!credential) throw new Error(NOT_SIGNED_IN);
|
|
9132
|
+
const response = await sendWithCredential(
|
|
9133
|
+
credential,
|
|
9134
|
+
(token) => appFetch(APP_PATHS.whoami, { token })
|
|
9135
|
+
);
|
|
8219
9136
|
const body = await readJson2(response);
|
|
8220
9137
|
if (!response.ok)
|
|
8221
9138
|
throw new Error(appErrorMessage(body) ?? `HTTP ${response.status}`);
|
|
8222
|
-
return body;
|
|
9139
|
+
return { body, credential };
|
|
9140
|
+
}
|
|
9141
|
+
async function whoami() {
|
|
9142
|
+
return (await identify()).body;
|
|
8223
9143
|
}
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
9144
|
+
function credentialSourceLine(credential) {
|
|
9145
|
+
return credential.source === "env" ? "Credential: WALKEROS_TOKEN" : `Credential: signed in to ${credential.issuer}`;
|
|
9146
|
+
}
|
|
9147
|
+
var IdentitySchema = z6.object({
|
|
9148
|
+
email: z6.string().optional(),
|
|
9149
|
+
userId: z6.string().optional()
|
|
8227
9150
|
});
|
|
8228
9151
|
async function whoamiCommand(options) {
|
|
8229
9152
|
const logger = createCLILogger(options);
|
|
8230
9153
|
try {
|
|
8231
|
-
const result = await
|
|
9154
|
+
const { body: result, credential } = await identify();
|
|
8232
9155
|
if (options.json) {
|
|
8233
9156
|
await writeResult(JSON.stringify(result, null, 2), options);
|
|
8234
9157
|
} else {
|
|
8235
9158
|
const identity = IdentitySchema.safeParse(result);
|
|
8236
9159
|
if (!identity.success) {
|
|
8237
9160
|
logger.info(JSON.stringify(result));
|
|
8238
|
-
|
|
9161
|
+
} else {
|
|
9162
|
+
const data = identity.data;
|
|
9163
|
+
if (data.email) logger.info(data.email);
|
|
9164
|
+
if (data.userId) logger.info(`User: ${data.userId}`);
|
|
8239
9165
|
}
|
|
8240
|
-
|
|
8241
|
-
if (data.email) logger.info(data.email);
|
|
8242
|
-
if (data.userId) logger.info(`User: ${data.userId}`);
|
|
9166
|
+
logger.info(credentialSourceLine(credential));
|
|
8243
9167
|
}
|
|
8244
9168
|
} catch (error) {
|
|
8245
9169
|
handleCliError(error);
|
|
@@ -8321,6 +9245,188 @@ import * as path21 from "path";
|
|
|
8321
9245
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8322
9246
|
import fs21 from "fs-extra";
|
|
8323
9247
|
import * as esbuild3 from "esbuild";
|
|
9248
|
+
|
|
9249
|
+
// src/schemas/build-manifest.ts
|
|
9250
|
+
import { z as z7 } from "@walkeros/core/dev";
|
|
9251
|
+
var BUILD_MANIFEST_VERSION = 1;
|
|
9252
|
+
var HttpUrlSchema = z7.url({ protocol: /^https?$/ });
|
|
9253
|
+
var BuildWrapObserveSchema = z7.object({
|
|
9254
|
+
preview: z7.object({ url: HttpUrlSchema, token: z7.string().min(1) }).strict()
|
|
9255
|
+
}).strict();
|
|
9256
|
+
var OutputNameSchema = z7.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "Must be a bare file name").describe("Output file name; its extension picks the format (.tar.gz packs)");
|
|
9257
|
+
var BUNDLE_TARGET_NAMES = [
|
|
9258
|
+
"cdn",
|
|
9259
|
+
"cdn-skeleton",
|
|
9260
|
+
"runner",
|
|
9261
|
+
"simulate",
|
|
9262
|
+
"push"
|
|
9263
|
+
];
|
|
9264
|
+
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
9265
|
+
"content-type",
|
|
9266
|
+
"content-length",
|
|
9267
|
+
"host"
|
|
9268
|
+
]);
|
|
9269
|
+
var OutputFields = {
|
|
9270
|
+
outputName: OutputNameSchema,
|
|
9271
|
+
putUrl: HttpUrlSchema.describe("Presigned PUT for exactly this output"),
|
|
9272
|
+
contentType: z7.string().min(1).optional().describe("Content-Type sent with the PUT (must match a signed header)"),
|
|
9273
|
+
headers: z7.record(z7.string().min(1), z7.string()).refine(
|
|
9274
|
+
(headers) => Object.keys(headers).every(
|
|
9275
|
+
(name) => !RESERVED_HEADERS.has(name.toLowerCase())
|
|
9276
|
+
),
|
|
9277
|
+
"Content-Type, Content-Length and Host are set by the CLI"
|
|
9278
|
+
).optional().describe(
|
|
9279
|
+
"Extra headers sent with the PUT (for headers a presign signs); never logged"
|
|
9280
|
+
)
|
|
9281
|
+
};
|
|
9282
|
+
var BuildBundleArtifactSchema = z7.object({
|
|
9283
|
+
target: z7.enum(BUNDLE_TARGET_NAMES).describe("Named bundle target"),
|
|
9284
|
+
...OutputFields
|
|
9285
|
+
}).strict();
|
|
9286
|
+
var BuildWrapOptionsSchema = z7.object({
|
|
9287
|
+
windowCollector: z7.string().min(1).optional(),
|
|
9288
|
+
windowElb: z7.string().min(1).optional(),
|
|
9289
|
+
preview: z7.object({
|
|
9290
|
+
enabled: z7.boolean(),
|
|
9291
|
+
keyring: z7.array(
|
|
9292
|
+
z7.object({ kid: z7.string(), spki: z7.string() }).strict()
|
|
9293
|
+
),
|
|
9294
|
+
iss: z7.string(),
|
|
9295
|
+
pb: z7.string().optional(),
|
|
9296
|
+
acceptForeign: z7.boolean().optional(),
|
|
9297
|
+
demoAllowlist: z7.array(z7.string()).optional(),
|
|
9298
|
+
previewOrigin: z7.string()
|
|
9299
|
+
}).strict().optional(),
|
|
9300
|
+
moin: z7.object({ app: z7.string(), base: z7.string() }).strict().optional().describe("Tag Mode loader target; absent, the production app"),
|
|
9301
|
+
observe: BuildWrapObserveSchema.optional().describe(
|
|
9302
|
+
"Browser only: Observe preview reporting, the preview route and its token. It replaces the flow's own collector.observe; a browser build never carries usage"
|
|
9303
|
+
),
|
|
9304
|
+
minify: z7.boolean().optional(),
|
|
9305
|
+
minifyOptions: z7.object({
|
|
9306
|
+
identifiers: z7.boolean().optional(),
|
|
9307
|
+
syntax: z7.boolean().optional(),
|
|
9308
|
+
whitespace: z7.boolean().optional(),
|
|
9309
|
+
keepNames: z7.boolean().optional(),
|
|
9310
|
+
legalComments: z7.enum(["none", "inline", "eof", "linked", "external"]).optional()
|
|
9311
|
+
}).strict().optional(),
|
|
9312
|
+
target: z7.string().min(1).optional().describe("esbuild target")
|
|
9313
|
+
}).strict().describe("Options forwarded to wrapSkeleton");
|
|
9314
|
+
var OBSERVE_NOT_ON_NODE = "observe is a browser wrap option";
|
|
9315
|
+
var OBSERVE_NOT_WITH_PREVIEW = "observe is refused beside preview, which makes a host build";
|
|
9316
|
+
var BuildWrapArtifactSchema = z7.object({
|
|
9317
|
+
target: z7.literal("wrap"),
|
|
9318
|
+
platform: z7.enum(["browser", "node"]),
|
|
9319
|
+
skeleton: z7.union([
|
|
9320
|
+
z7.object({ url: HttpUrlSchema }).strict(),
|
|
9321
|
+
z7.object({ artifact: OutputNameSchema }).strict()
|
|
9322
|
+
]).describe(
|
|
9323
|
+
"The skeleton to wrap: a GET URL, or the outputName of an earlier bundle artifact in this manifest"
|
|
9324
|
+
),
|
|
9325
|
+
options: BuildWrapOptionsSchema.optional(),
|
|
9326
|
+
...OutputFields
|
|
9327
|
+
}).strict().refine(
|
|
9328
|
+
(artifact) => artifact.platform === "browser" || artifact.options?.observe === void 0,
|
|
9329
|
+
{ path: ["options", "observe"], message: OBSERVE_NOT_ON_NODE }
|
|
9330
|
+
).refine(
|
|
9331
|
+
(artifact) => artifact.options?.observe === void 0 || artifact.options.preview === void 0,
|
|
9332
|
+
{ path: ["options", "observe"], message: OBSERVE_NOT_WITH_PREVIEW }
|
|
9333
|
+
);
|
|
9334
|
+
var BuildArtifactSchema = z7.union([
|
|
9335
|
+
BuildBundleArtifactSchema,
|
|
9336
|
+
BuildWrapArtifactSchema
|
|
9337
|
+
]);
|
|
9338
|
+
var BuildPackagesSchema = z7.union([
|
|
9339
|
+
z7.object({
|
|
9340
|
+
tree: PackageTreeSchema.describe(
|
|
9341
|
+
"Install exactly this tree for every bundle artifact"
|
|
9342
|
+
)
|
|
9343
|
+
}).strict(),
|
|
9344
|
+
z7.object({
|
|
9345
|
+
resolve: z7.object({
|
|
9346
|
+
before: z7.iso.datetime({ offset: true }).describe("Only versions published at or before this time"),
|
|
9347
|
+
exempt: z7.array(z7.string().min(1)).describe(
|
|
9348
|
+
"Name prefixes the cutoff skips, with the exact versions packages under them declare"
|
|
9349
|
+
)
|
|
9350
|
+
}).strict()
|
|
9351
|
+
}).strict()
|
|
9352
|
+
]).describe("How bundle artifacts install their packages");
|
|
9353
|
+
var BuildManifestSchema = z7.object({
|
|
9354
|
+
version: z7.literal(BUILD_MANIFEST_VERSION),
|
|
9355
|
+
toolchain: z7.string().min(1).describe("Exact @walkeros/cli version; any other version refuses"),
|
|
9356
|
+
flowConfig: z7.record(z7.string(), z7.unknown()).optional().describe("Flow.Json to bundle; required when any bundle artifact is"),
|
|
9357
|
+
flowName: z7.string().min(1).optional(),
|
|
9358
|
+
buildEnv: z7.record(z7.string(), z7.string()).optional().describe(
|
|
9359
|
+
"Base env for web $env, beneath config.bundle.env. Never the CLI process's own env"
|
|
9360
|
+
),
|
|
9361
|
+
artifacts: z7.array(BuildArtifactSchema).min(1),
|
|
9362
|
+
resultPutUrl: HttpUrlSchema.describe("Presigned PUT for the result JSON"),
|
|
9363
|
+
packages: BuildPackagesSchema.optional()
|
|
9364
|
+
}).loose().superRefine((manifest, ctx) => {
|
|
9365
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9366
|
+
const bundled = /* @__PURE__ */ new Set();
|
|
9367
|
+
manifest.artifacts.forEach((artifact, index) => {
|
|
9368
|
+
if (seen.has(artifact.outputName)) {
|
|
9369
|
+
ctx.addIssue({
|
|
9370
|
+
code: "custom",
|
|
9371
|
+
path: ["artifacts", index, "outputName"],
|
|
9372
|
+
message: `Duplicate outputName "${artifact.outputName}"`
|
|
9373
|
+
});
|
|
9374
|
+
}
|
|
9375
|
+
seen.add(artifact.outputName);
|
|
9376
|
+
if (artifact.target !== "wrap") {
|
|
9377
|
+
bundled.add(artifact.outputName);
|
|
9378
|
+
if (!manifest.flowConfig) {
|
|
9379
|
+
ctx.addIssue({
|
|
9380
|
+
code: "custom",
|
|
9381
|
+
path: ["flowConfig"],
|
|
9382
|
+
message: "flowConfig is required for bundle artifacts"
|
|
9383
|
+
});
|
|
9384
|
+
}
|
|
9385
|
+
} else if ("artifact" in artifact.skeleton && !bundled.has(artifact.skeleton.artifact)) {
|
|
9386
|
+
ctx.addIssue({
|
|
9387
|
+
code: "custom",
|
|
9388
|
+
path: ["artifacts", index, "skeleton", "artifact"],
|
|
9389
|
+
message: `No earlier bundle artifact named "${artifact.skeleton.artifact}"`
|
|
9390
|
+
});
|
|
9391
|
+
}
|
|
9392
|
+
});
|
|
9393
|
+
}).meta({ id: "BuildManifest", title: "walkeros bundle --manifest input" });
|
|
9394
|
+
var BUILD_ERROR_CODES = [
|
|
9395
|
+
"INVALID_MANIFEST",
|
|
9396
|
+
"TOOLCHAIN_MISMATCH",
|
|
9397
|
+
"INVALID_CONFIG",
|
|
9398
|
+
"MISSING_BUILD_ENV",
|
|
9399
|
+
"WEB_SECRET_REF",
|
|
9400
|
+
"UNSUPPORTED_PACKAGE_SPEC",
|
|
9401
|
+
"LOCAL_PATH_NOT_ALLOWED",
|
|
9402
|
+
"BUILD_FAILED",
|
|
9403
|
+
"PACKAGE_UNAVAILABLE",
|
|
9404
|
+
"PACKAGE_TREE_INVALID",
|
|
9405
|
+
"UPLOAD_FAILED"
|
|
9406
|
+
];
|
|
9407
|
+
var BuildResultSchema = z7.object({
|
|
9408
|
+
ok: z7.boolean(),
|
|
9409
|
+
toolchain: z7.string().describe("The CLI version that ran the build"),
|
|
9410
|
+
artifacts: z7.array(
|
|
9411
|
+
z7.object({
|
|
9412
|
+
target: z7.string(),
|
|
9413
|
+
outputName: z7.string(),
|
|
9414
|
+
bytes: z7.number().int().nonnegative(),
|
|
9415
|
+
sha256: z7.string()
|
|
9416
|
+
})
|
|
9417
|
+
).describe("Outputs PUT successfully, in manifest order"),
|
|
9418
|
+
packages: z7.object({
|
|
9419
|
+
digest: z7.string().describe("sha256 of the canonical tree"),
|
|
9420
|
+
tree: PackageTreeSchema
|
|
9421
|
+
}).optional().describe("The tree the bundle artifacts installed, when asked for"),
|
|
9422
|
+
error: z7.object({
|
|
9423
|
+
code: z7.enum(BUILD_ERROR_CODES),
|
|
9424
|
+
message: z7.string(),
|
|
9425
|
+
outputName: z7.string().optional().describe("The artifact being built when the error occurred")
|
|
9426
|
+
}).optional()
|
|
9427
|
+
}).meta({ id: "BuildResult", title: "walkeros bundle --manifest result" });
|
|
9428
|
+
|
|
9429
|
+
// src/commands/bundle/wrap.ts
|
|
8324
9430
|
function extractDevExternals(skeletonText) {
|
|
8325
9431
|
const pattern = /import\(\s*['"]([^'"]+\/dev)['"]\s*\)/g;
|
|
8326
9432
|
const found = /* @__PURE__ */ new Set();
|
|
@@ -8343,6 +9449,15 @@ function getNodeResolutionPaths() {
|
|
|
8343
9449
|
}
|
|
8344
9450
|
return candidates;
|
|
8345
9451
|
}
|
|
9452
|
+
function observeProblem(observe, wrap) {
|
|
9453
|
+
if (wrap.platform !== "browser") return OBSERVE_NOT_ON_NODE;
|
|
9454
|
+
if (wrap.preview) return OBSERVE_NOT_WITH_PREVIEW;
|
|
9455
|
+
const parsed = BuildWrapObserveSchema.safeParse(observe);
|
|
9456
|
+
if (parsed.success) return void 0;
|
|
9457
|
+
const issue = parsed.error.issues[0];
|
|
9458
|
+
const at = ["observe", ...(issue?.path ?? []).map(String)].join(".");
|
|
9459
|
+
return `Invalid ${at}: ${issue?.message ?? "not an observe option"}`;
|
|
9460
|
+
}
|
|
8346
9461
|
async function wrapSkeleton(options) {
|
|
8347
9462
|
const {
|
|
8348
9463
|
skeletonPath,
|
|
@@ -8354,16 +9469,18 @@ async function wrapSkeleton(options) {
|
|
|
8354
9469
|
minify = true,
|
|
8355
9470
|
minifyOptions
|
|
8356
9471
|
} = options;
|
|
8357
|
-
if (options.preview && options.previewGrantTargets) {
|
|
8358
|
-
throw new Error(
|
|
8359
|
-
"wrapSkeleton: `preview` (host activator) and `previewGrantTargets` (preview-artifact injection) are mutually exclusive. Pass at most one."
|
|
8360
|
-
);
|
|
8361
|
-
}
|
|
8362
9472
|
if (options.preview && (typeof options.preview.previewOrigin !== "string" || !/^[a-z0-9.-]+$/.test(options.preview.previewOrigin))) {
|
|
8363
9473
|
throw new Error(
|
|
8364
9474
|
`Invalid previewOrigin "${options.preview.previewOrigin}". Must be a bare hostname matching /^[a-z0-9.-]+$/.`
|
|
8365
9475
|
);
|
|
8366
9476
|
}
|
|
9477
|
+
if (options.observe !== void 0) {
|
|
9478
|
+
const problem = observeProblem(options.observe, {
|
|
9479
|
+
platform,
|
|
9480
|
+
preview: options.preview !== void 0
|
|
9481
|
+
});
|
|
9482
|
+
if (problem) throw new Error(problem);
|
|
9483
|
+
}
|
|
8367
9484
|
if (!await fs21.pathExists(skeletonPath)) {
|
|
8368
9485
|
throw new Error(`wrapSkeleton: skeleton not found at ${skeletonPath}`);
|
|
8369
9486
|
}
|
|
@@ -8374,9 +9491,8 @@ async function wrapSkeleton(options) {
|
|
|
8374
9491
|
...windowCollector ? { windowCollector } : {},
|
|
8375
9492
|
...windowElb ? { windowElb } : {},
|
|
8376
9493
|
...options.preview ? { preview: options.preview } : {},
|
|
8377
|
-
...options.previewGrantTargets ? { previewGrantTargets: options.previewGrantTargets } : {},
|
|
8378
|
-
platform,
|
|
8379
9494
|
...options.observe ? { observe: options.observe } : {},
|
|
9495
|
+
platform,
|
|
8380
9496
|
...options.moin ? { moin: options.moin } : {}
|
|
8381
9497
|
}) : generateWrapEntryServer(absoluteSkeletonPath);
|
|
8382
9498
|
const entryDir = await tmpRunDir("wrap");
|
|
@@ -8411,15 +9527,12 @@ async function wrapSkeleton(options) {
|
|
|
8411
9527
|
if (platform === "browser") {
|
|
8412
9528
|
esbuildOptions.format = "iife";
|
|
8413
9529
|
esbuildOptions.plugins = [cliPackagesPlugin()];
|
|
8414
|
-
const needs = readNeedsMarker(skeletonText);
|
|
8415
9530
|
esbuildOptions.define = {
|
|
8416
9531
|
"process.env.NODE_ENV": '"production"',
|
|
8417
9532
|
global: "globalThis",
|
|
8418
9533
|
...buildFlagDefines(
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
observe: needs.observe || options.observe !== void 0
|
|
8422
|
-
}
|
|
9534
|
+
readNeedsMarker(skeletonText),
|
|
9535
|
+
options.observe !== void 0
|
|
8423
9536
|
)
|
|
8424
9537
|
};
|
|
8425
9538
|
esbuildOptions.external = devExternals;
|
|
@@ -8444,158 +9557,6 @@ import path22 from "path";
|
|
|
8444
9557
|
import fs22 from "fs-extra";
|
|
8445
9558
|
import { FlowReferenceError as FlowReferenceError2 } from "@walkeros/core";
|
|
8446
9559
|
|
|
8447
|
-
// src/schemas/build-manifest.ts
|
|
8448
|
-
import { z as z4 } from "@walkeros/core/dev";
|
|
8449
|
-
var BUILD_MANIFEST_VERSION = 1;
|
|
8450
|
-
var HttpUrlSchema = z4.url({ protocol: /^https?$/ });
|
|
8451
|
-
var OutputNameSchema = z4.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "Must be a bare file name").describe("Output file name; its extension picks the format (.tar.gz packs)");
|
|
8452
|
-
var BUNDLE_TARGET_NAMES = [
|
|
8453
|
-
"cdn",
|
|
8454
|
-
"cdn-skeleton",
|
|
8455
|
-
"runner",
|
|
8456
|
-
"simulate",
|
|
8457
|
-
"push"
|
|
8458
|
-
];
|
|
8459
|
-
var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
8460
|
-
"content-type",
|
|
8461
|
-
"content-length",
|
|
8462
|
-
"host"
|
|
8463
|
-
]);
|
|
8464
|
-
var OutputFields = {
|
|
8465
|
-
outputName: OutputNameSchema,
|
|
8466
|
-
putUrl: HttpUrlSchema.describe("Presigned PUT for exactly this output"),
|
|
8467
|
-
contentType: z4.string().min(1).optional().describe("Content-Type sent with the PUT (must match a signed header)"),
|
|
8468
|
-
headers: z4.record(z4.string().min(1), z4.string()).refine(
|
|
8469
|
-
(headers) => Object.keys(headers).every(
|
|
8470
|
-
(name) => !RESERVED_HEADERS.has(name.toLowerCase())
|
|
8471
|
-
),
|
|
8472
|
-
"Content-Type, Content-Length and Host are set by the CLI"
|
|
8473
|
-
).optional().describe(
|
|
8474
|
-
"Extra headers sent with the PUT (for headers a presign signs); never logged"
|
|
8475
|
-
)
|
|
8476
|
-
};
|
|
8477
|
-
var BuildBundleArtifactSchema = z4.object({
|
|
8478
|
-
target: z4.enum(BUNDLE_TARGET_NAMES).describe("Named bundle target"),
|
|
8479
|
-
...OutputFields
|
|
8480
|
-
}).strict();
|
|
8481
|
-
var BuildWrapOptionsSchema = z4.object({
|
|
8482
|
-
windowCollector: z4.string().min(1).optional(),
|
|
8483
|
-
windowElb: z4.string().min(1).optional(),
|
|
8484
|
-
preview: z4.object({
|
|
8485
|
-
enabled: z4.boolean(),
|
|
8486
|
-
keyring: z4.array(
|
|
8487
|
-
z4.object({ kid: z4.string(), spki: z4.string() }).strict()
|
|
8488
|
-
),
|
|
8489
|
-
iss: z4.string(),
|
|
8490
|
-
pb: z4.string().optional(),
|
|
8491
|
-
acceptForeign: z4.boolean().optional(),
|
|
8492
|
-
demoAllowlist: z4.array(z4.string()).optional(),
|
|
8493
|
-
previewOrigin: z4.string()
|
|
8494
|
-
}).strict().optional(),
|
|
8495
|
-
previewGrantTargets: z4.array(z4.string()).optional(),
|
|
8496
|
-
observe: z4.object({
|
|
8497
|
-
url: z4.string(),
|
|
8498
|
-
binding: z4.string(),
|
|
8499
|
-
flowId: z4.string().optional(),
|
|
8500
|
-
level: z4.enum(["off", "standard", "trace"]).optional(),
|
|
8501
|
-
sample: z4.number().min(0).max(1).optional()
|
|
8502
|
-
}).strict().optional(),
|
|
8503
|
-
moin: z4.object({ app: z4.string(), base: z4.string() }).strict().optional().describe("Tag Mode loader target; absent, the production app"),
|
|
8504
|
-
minify: z4.boolean().optional(),
|
|
8505
|
-
minifyOptions: z4.object({
|
|
8506
|
-
identifiers: z4.boolean().optional(),
|
|
8507
|
-
syntax: z4.boolean().optional(),
|
|
8508
|
-
whitespace: z4.boolean().optional(),
|
|
8509
|
-
keepNames: z4.boolean().optional(),
|
|
8510
|
-
legalComments: z4.enum(["none", "inline", "eof", "linked", "external"]).optional()
|
|
8511
|
-
}).strict().optional(),
|
|
8512
|
-
target: z4.string().min(1).optional().describe("esbuild target")
|
|
8513
|
-
}).strict().describe("Options forwarded to wrapSkeleton");
|
|
8514
|
-
var BuildWrapArtifactSchema = z4.object({
|
|
8515
|
-
target: z4.literal("wrap"),
|
|
8516
|
-
platform: z4.enum(["browser", "node"]),
|
|
8517
|
-
skeleton: z4.union([
|
|
8518
|
-
z4.object({ url: HttpUrlSchema }).strict(),
|
|
8519
|
-
z4.object({ artifact: OutputNameSchema }).strict()
|
|
8520
|
-
]).describe(
|
|
8521
|
-
"The skeleton to wrap: a GET URL, or the outputName of an earlier bundle artifact in this manifest"
|
|
8522
|
-
),
|
|
8523
|
-
options: BuildWrapOptionsSchema.optional(),
|
|
8524
|
-
...OutputFields
|
|
8525
|
-
}).strict();
|
|
8526
|
-
var BuildArtifactSchema = z4.union([
|
|
8527
|
-
BuildBundleArtifactSchema,
|
|
8528
|
-
BuildWrapArtifactSchema
|
|
8529
|
-
]);
|
|
8530
|
-
var BuildManifestSchema = z4.object({
|
|
8531
|
-
version: z4.literal(BUILD_MANIFEST_VERSION),
|
|
8532
|
-
toolchain: z4.string().min(1).describe("Exact @walkeros/cli version; any other version refuses"),
|
|
8533
|
-
flowConfig: z4.record(z4.string(), z4.unknown()).optional().describe("Flow.Json to bundle; required when any bundle artifact is"),
|
|
8534
|
-
flowName: z4.string().min(1).optional(),
|
|
8535
|
-
buildEnv: z4.record(z4.string(), z4.string()).optional().describe(
|
|
8536
|
-
"Base env for web $env, beneath config.bundle.env. Never the CLI process's own env"
|
|
8537
|
-
),
|
|
8538
|
-
artifacts: z4.array(BuildArtifactSchema).min(1),
|
|
8539
|
-
resultPutUrl: HttpUrlSchema.describe("Presigned PUT for the result JSON")
|
|
8540
|
-
}).loose().superRefine((manifest, ctx) => {
|
|
8541
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8542
|
-
const bundled = /* @__PURE__ */ new Set();
|
|
8543
|
-
manifest.artifacts.forEach((artifact, index) => {
|
|
8544
|
-
if (seen.has(artifact.outputName)) {
|
|
8545
|
-
ctx.addIssue({
|
|
8546
|
-
code: "custom",
|
|
8547
|
-
path: ["artifacts", index, "outputName"],
|
|
8548
|
-
message: `Duplicate outputName "${artifact.outputName}"`
|
|
8549
|
-
});
|
|
8550
|
-
}
|
|
8551
|
-
seen.add(artifact.outputName);
|
|
8552
|
-
if (artifact.target !== "wrap") {
|
|
8553
|
-
bundled.add(artifact.outputName);
|
|
8554
|
-
if (!manifest.flowConfig) {
|
|
8555
|
-
ctx.addIssue({
|
|
8556
|
-
code: "custom",
|
|
8557
|
-
path: ["flowConfig"],
|
|
8558
|
-
message: "flowConfig is required for bundle artifacts"
|
|
8559
|
-
});
|
|
8560
|
-
}
|
|
8561
|
-
} else if ("artifact" in artifact.skeleton && !bundled.has(artifact.skeleton.artifact)) {
|
|
8562
|
-
ctx.addIssue({
|
|
8563
|
-
code: "custom",
|
|
8564
|
-
path: ["artifacts", index, "skeleton", "artifact"],
|
|
8565
|
-
message: `No earlier bundle artifact named "${artifact.skeleton.artifact}"`
|
|
8566
|
-
});
|
|
8567
|
-
}
|
|
8568
|
-
});
|
|
8569
|
-
}).meta({ id: "BuildManifest", title: "walkeros bundle --manifest input" });
|
|
8570
|
-
var BUILD_ERROR_CODES = [
|
|
8571
|
-
"INVALID_MANIFEST",
|
|
8572
|
-
"TOOLCHAIN_MISMATCH",
|
|
8573
|
-
"INVALID_CONFIG",
|
|
8574
|
-
"MISSING_BUILD_ENV",
|
|
8575
|
-
"WEB_SECRET_REF",
|
|
8576
|
-
"UNSUPPORTED_PACKAGE_SPEC",
|
|
8577
|
-
"LOCAL_PATH_NOT_ALLOWED",
|
|
8578
|
-
"BUILD_FAILED",
|
|
8579
|
-
"UPLOAD_FAILED"
|
|
8580
|
-
];
|
|
8581
|
-
var BuildResultSchema = z4.object({
|
|
8582
|
-
ok: z4.boolean(),
|
|
8583
|
-
toolchain: z4.string().describe("The CLI version that ran the build"),
|
|
8584
|
-
artifacts: z4.array(
|
|
8585
|
-
z4.object({
|
|
8586
|
-
target: z4.string(),
|
|
8587
|
-
outputName: z4.string(),
|
|
8588
|
-
bytes: z4.number().int().nonnegative(),
|
|
8589
|
-
sha256: z4.string()
|
|
8590
|
-
})
|
|
8591
|
-
).describe("Outputs PUT successfully, in manifest order"),
|
|
8592
|
-
error: z4.object({
|
|
8593
|
-
code: z4.enum(BUILD_ERROR_CODES),
|
|
8594
|
-
message: z4.string(),
|
|
8595
|
-
outputName: z4.string().optional().describe("The artifact being built when the error occurred")
|
|
8596
|
-
}).optional()
|
|
8597
|
-
}).meta({ id: "BuildResult", title: "walkeros bundle --manifest result" });
|
|
8598
|
-
|
|
8599
9560
|
// src/commands/bundle/fetch-retry.ts
|
|
8600
9561
|
var BACKOFF_MS = [1e3, 3e3];
|
|
8601
9562
|
function defaultSleep(ms) {
|
|
@@ -8625,7 +9586,7 @@ function describeFetchError(failure, timeoutMs) {
|
|
|
8625
9586
|
return "fetch failed";
|
|
8626
9587
|
}
|
|
8627
9588
|
async function fetchWithRetry(url, init, options) {
|
|
8628
|
-
const { label, timeoutMs, warn, sleep = defaultSleep } = options;
|
|
9589
|
+
const { label, timeoutMs, warn, sleep: sleep2 = defaultSleep } = options;
|
|
8629
9590
|
const maxAttempts = BACKOFF_MS.length + 1;
|
|
8630
9591
|
for (let attempt = 1; ; attempt++) {
|
|
8631
9592
|
let failure;
|
|
@@ -8652,7 +9613,7 @@ async function fetchWithRetry(url, init, options) {
|
|
|
8652
9613
|
warn?.(
|
|
8653
9614
|
`${label} attempt ${attempt}/${maxAttempts} failed: ${reason}, retrying in ${delay2 / 1e3}s`
|
|
8654
9615
|
);
|
|
8655
|
-
await
|
|
9616
|
+
await sleep2(delay2);
|
|
8656
9617
|
}
|
|
8657
9618
|
}
|
|
8658
9619
|
|
|
@@ -8710,6 +9671,8 @@ function classify(error, phase) {
|
|
|
8710
9671
|
if (error instanceof UnsupportedPackageSpecError) {
|
|
8711
9672
|
return "UNSUPPORTED_PACKAGE_SPEC";
|
|
8712
9673
|
}
|
|
9674
|
+
if (error instanceof PackageTreeError) return "PACKAGE_TREE_INVALID";
|
|
9675
|
+
if (error instanceof PackageUnavailableError) return "PACKAGE_UNAVAILABLE";
|
|
8713
9676
|
return phase === "config" ? "INVALID_CONFIG" : "BUILD_FAILED";
|
|
8714
9677
|
}
|
|
8715
9678
|
function isHttpUrl(source) {
|
|
@@ -8764,7 +9727,7 @@ async function download(url, dest, retry) {
|
|
|
8764
9727
|
}
|
|
8765
9728
|
await fs22.writeFile(dest, Buffer.from(await response.arrayBuffer()));
|
|
8766
9729
|
}
|
|
8767
|
-
async function buildArtifact(manifest, configPath, artifact, workDir, index, outputs, retry) {
|
|
9730
|
+
async function buildArtifact(manifest, configPath, artifact, workDir, index, outputs, retry, packageInstall) {
|
|
8768
9731
|
const output = path22.join(workDir, "out", artifact.outputName);
|
|
8769
9732
|
await fs22.ensureDir(path22.dirname(output));
|
|
8770
9733
|
if (artifact.target === "wrap") {
|
|
@@ -8800,7 +9763,11 @@ async function buildArtifact(manifest, configPath, artifact, workDir, index, out
|
|
|
8800
9763
|
cache: false,
|
|
8801
9764
|
// The base env is the manifest's, NEVER this process's own.
|
|
8802
9765
|
buildEnv: manifest.buildEnv ?? {},
|
|
8803
|
-
buildOverrides: {
|
|
9766
|
+
buildOverrides: {
|
|
9767
|
+
output,
|
|
9768
|
+
tempDir: scratch,
|
|
9769
|
+
...packageInstall ? { packageInstall } : {}
|
|
9770
|
+
}
|
|
8804
9771
|
});
|
|
8805
9772
|
return output;
|
|
8806
9773
|
}
|
|
@@ -8858,6 +9825,18 @@ async function runBuildManifest(source, retry = {}) {
|
|
|
8858
9825
|
}
|
|
8859
9826
|
}
|
|
8860
9827
|
const outputs = /* @__PURE__ */ new Map();
|
|
9828
|
+
let tree = manifest.packages && "tree" in manifest.packages ? manifest.packages.tree : void 0;
|
|
9829
|
+
const resolve5 = manifest.packages && "resolve" in manifest.packages ? manifest.packages.resolve : void 0;
|
|
9830
|
+
const installFor = () => {
|
|
9831
|
+
if (tree) return { tree };
|
|
9832
|
+
if (!resolve5) return void 0;
|
|
9833
|
+
return {
|
|
9834
|
+
resolve: { before: new Date(resolve5.before), exempt: resolve5.exempt },
|
|
9835
|
+
onTree: (installed) => {
|
|
9836
|
+
tree = installed;
|
|
9837
|
+
}
|
|
9838
|
+
};
|
|
9839
|
+
};
|
|
8861
9840
|
for (const [index, artifact] of manifest.artifacts.entries()) {
|
|
8862
9841
|
try {
|
|
8863
9842
|
const output = await buildArtifact(
|
|
@@ -8867,7 +9846,8 @@ async function runBuildManifest(source, retry = {}) {
|
|
|
8867
9846
|
workDir,
|
|
8868
9847
|
index,
|
|
8869
9848
|
outputs,
|
|
8870
|
-
retry
|
|
9849
|
+
retry,
|
|
9850
|
+
artifact.target === "wrap" ? void 0 : installFor()
|
|
8871
9851
|
);
|
|
8872
9852
|
outputs.set(artifact.outputName, output);
|
|
8873
9853
|
const content = await fs22.readFile(output);
|
|
@@ -8896,6 +9876,9 @@ async function runBuildManifest(source, retry = {}) {
|
|
|
8896
9876
|
};
|
|
8897
9877
|
}
|
|
8898
9878
|
}
|
|
9879
|
+
if (manifest.packages && tree) {
|
|
9880
|
+
result.packages = { digest: packageTreeDigest(tree), tree };
|
|
9881
|
+
}
|
|
8899
9882
|
result.ok = true;
|
|
8900
9883
|
return { result, reported: await report(manifest.resultPutUrl, result) };
|
|
8901
9884
|
} finally {
|
|
@@ -9129,30 +10112,41 @@ export {
|
|
|
9129
10112
|
BuildArtifactSchema,
|
|
9130
10113
|
BuildBundleArtifactSchema,
|
|
9131
10114
|
BuildManifestSchema,
|
|
10115
|
+
BuildPackagesSchema,
|
|
9132
10116
|
BuildResultSchema,
|
|
9133
10117
|
BuildWrapArtifactSchema,
|
|
9134
10118
|
BuildWrapOptionsSchema,
|
|
10119
|
+
PACKAGE_TREE_MAX_ENTRIES,
|
|
10120
|
+
PACKAGE_TREE_VERSION,
|
|
10121
|
+
PackageTreeError,
|
|
10122
|
+
PackageTreeSchema,
|
|
9135
10123
|
VALIDATE_CODES,
|
|
9136
10124
|
VERSION,
|
|
9137
10125
|
appErrorMessage,
|
|
9138
10126
|
appFetch,
|
|
10127
|
+
assertPackageTree,
|
|
9139
10128
|
buildDataPayload,
|
|
9140
10129
|
bundle,
|
|
9141
10130
|
bundleCommand,
|
|
10131
|
+
canonicalPackageTree,
|
|
9142
10132
|
classifyStepProperties,
|
|
9143
10133
|
clientContextHeaders,
|
|
9144
10134
|
collectKnownSecrets,
|
|
9145
10135
|
containsCodeMarkers,
|
|
10136
|
+
edgeIsExempt,
|
|
9146
10137
|
formatPushResult,
|
|
9147
10138
|
getClientContext,
|
|
9148
10139
|
loadConfig,
|
|
9149
10140
|
loadJsonConfig,
|
|
10141
|
+
packageTreeDigest,
|
|
10142
|
+
packageTreeProblems,
|
|
9150
10143
|
push,
|
|
9151
10144
|
pushCommand,
|
|
9152
10145
|
readConfig,
|
|
9153
10146
|
resetClientContext,
|
|
9154
10147
|
resolveAccessToken,
|
|
9155
10148
|
resolveAppUrl,
|
|
10149
|
+
resolveCredential,
|
|
9156
10150
|
resolveExportName,
|
|
9157
10151
|
resolveManifestSource,
|
|
9158
10152
|
runBuildManifest,
|