@lark-apaas/fullstack-cli 1.1.64-alpha.20260901133248 → 1.1.64-alpha.20260902011518
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/dist/index.js +2090 -28
- package/package.json +4 -1
- package/templates/scripts/build.sh +0 -12
- package/templates/scripts/dev.js +700 -54
- package/templates/scripts/server-handoff-runtime.mjs +1906 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs31 from "fs";
|
|
3
|
+
import path27 from "path";
|
|
4
4
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
@@ -8260,38 +8260,2100 @@ async function preUploadStatic(options) {
|
|
|
8260
8260
|
}
|
|
8261
8261
|
}
|
|
8262
8262
|
|
|
8263
|
-
// src/commands/build/
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8263
|
+
// src/commands/build/server-startup-bundle.handler.ts
|
|
8264
|
+
import fs30 from "fs";
|
|
8265
|
+
import http from "http";
|
|
8266
|
+
import net from "net";
|
|
8267
|
+
import os3 from "os";
|
|
8268
|
+
import path26 from "path";
|
|
8269
|
+
import { builtinModules, createRequire as createRequire3 } from "module";
|
|
8270
|
+
import { spawn as spawn2 } from "child_process";
|
|
8271
|
+
import { createHash, randomUUID } from "crypto";
|
|
8272
|
+
import { build } from "esbuild";
|
|
8273
|
+
import { parse } from "acorn";
|
|
8274
|
+
import { ancestor as walkAncestor, simple as walkSimple } from "acorn-walk";
|
|
8275
|
+
|
|
8276
|
+
// src/commands/build/server-startup-module-guard.ts
|
|
8277
|
+
import fs29 from "fs";
|
|
8278
|
+
import path25 from "path";
|
|
8279
|
+
var STARTUP_BUNDLE_FILE = "payload/server.bundle.cjs";
|
|
8280
|
+
var STARTUP_ENTRY_FILE = "payload/server.start.cjs";
|
|
8281
|
+
var MODULE_GUARD_FILE = "module-resolution-guard.cjs";
|
|
8282
|
+
var MODULE_GUARD_SOURCE = `'use strict';
|
|
8283
|
+
const fs = require('node:fs');
|
|
8284
|
+
const path = require('node:path');
|
|
8285
|
+
const Module = require('node:module');
|
|
8286
|
+
|
|
8287
|
+
const payloadRoot = fs.realpathSync(__dirname);
|
|
8288
|
+
const payloadRequire = Module.createRequire(path.join(payloadRoot, 'package.json'));
|
|
8289
|
+
const builtins = new Set([
|
|
8290
|
+
...Module.builtinModules,
|
|
8291
|
+
...Module.builtinModules.map(name => 'node:' + name),
|
|
8292
|
+
]);
|
|
8293
|
+
const originalResolveFilename = Module._resolveFilename;
|
|
8294
|
+
|
|
8295
|
+
function isInside(root, candidate) {
|
|
8296
|
+
const relative = path.relative(root, candidate);
|
|
8297
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
8298
|
+
}
|
|
8299
|
+
|
|
8300
|
+
function isBareSpecifier(request) {
|
|
8301
|
+
return typeof request === 'string' &&
|
|
8302
|
+
!request.startsWith('.') &&
|
|
8303
|
+
!path.isAbsolute(request) &&
|
|
8304
|
+
!request.startsWith('file:');
|
|
8305
|
+
}
|
|
8306
|
+
|
|
8307
|
+
function assertPayloadResolution(request, resolved) {
|
|
8308
|
+
if (builtins.has(resolved)) return resolved;
|
|
8309
|
+
const realResolved = typeof resolved === 'string' && fs.existsSync(resolved)
|
|
8310
|
+
? fs.realpathSync(resolved)
|
|
8311
|
+
: resolved;
|
|
8312
|
+
if (typeof realResolved !== 'string' || !isInside(payloadRoot, realResolved)) {
|
|
8313
|
+
const error = new Error('MIAODA_STARTUP_MODULE_OUTSIDE_PAYLOAD:' + request);
|
|
8314
|
+
error.code = 'MIAODA_STARTUP_MODULE_OUTSIDE_PAYLOAD';
|
|
8315
|
+
throw error;
|
|
8316
|
+
}
|
|
8317
|
+
return realResolved;
|
|
8318
|
+
}
|
|
8319
|
+
|
|
8320
|
+
Module._resolveFilename = function miaodaResolveFilename(request, parent, isMain, options) {
|
|
8321
|
+
if (builtins.has(request)) {
|
|
8322
|
+
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
8323
|
+
}
|
|
8324
|
+
const parentFilename = parent && typeof parent.filename === 'string'
|
|
8325
|
+
? parent.filename
|
|
8326
|
+
: '';
|
|
8327
|
+
if (!isInside(payloadRoot, parentFilename)) {
|
|
8328
|
+
if (typeof request === 'string' && path.isAbsolute(request)) {
|
|
8329
|
+
return assertPayloadResolution(
|
|
8330
|
+
request,
|
|
8331
|
+
originalResolveFilename.call(this, request, parent, isMain, options)
|
|
8332
|
+
);
|
|
8333
|
+
}
|
|
8334
|
+
if (!isBareSpecifier(request)) {
|
|
8335
|
+
return assertPayloadResolution(request, undefined);
|
|
8336
|
+
}
|
|
8337
|
+
return assertPayloadResolution(request, payloadRequire.resolve(request));
|
|
8271
8338
|
}
|
|
8339
|
+
return assertPayloadResolution(
|
|
8340
|
+
request,
|
|
8341
|
+
originalResolveFilename.call(this, request, parent, isMain, options)
|
|
8342
|
+
);
|
|
8272
8343
|
};
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8344
|
+
`;
|
|
8345
|
+
function writeServerStartupLauncher(payloadRoot) {
|
|
8346
|
+
fs29.writeFileSync(
|
|
8347
|
+
path25.join(payloadRoot, MODULE_GUARD_FILE),
|
|
8348
|
+
MODULE_GUARD_SOURCE,
|
|
8349
|
+
{
|
|
8350
|
+
mode: 420
|
|
8351
|
+
}
|
|
8352
|
+
);
|
|
8353
|
+
fs29.writeFileSync(
|
|
8354
|
+
path25.join(payloadRoot, path25.basename(STARTUP_ENTRY_FILE)),
|
|
8355
|
+
`require('./${MODULE_GUARD_FILE}');
|
|
8356
|
+
require('./${path25.basename(STARTUP_BUNDLE_FILE)}');
|
|
8357
|
+
`,
|
|
8358
|
+
{ mode: 420 }
|
|
8359
|
+
);
|
|
8360
|
+
}
|
|
8361
|
+
|
|
8362
|
+
// src/commands/build/server-startup-bundle.handler.ts
|
|
8363
|
+
var SCHEMA_VERSION = 4;
|
|
8364
|
+
var BUILDER_POLICY_VERSION = 5;
|
|
8365
|
+
var ARTIFACT_FORMAT = "hybrid-bundle-dynamic-islands";
|
|
8366
|
+
var DEPENDENCY_MODE = "dynamic-islands";
|
|
8367
|
+
var MAX_ISLAND_DISCOVERY_PASSES = 128;
|
|
8368
|
+
var MAX_GENERATION_FILES = 5e4;
|
|
8369
|
+
var MAX_GENERATION_BYTES = 256 * 1024 * 1024;
|
|
8370
|
+
var MAX_GENERATION_FILE_BYTES = 64 * 1024 * 1024;
|
|
8371
|
+
var MAX_FILE_INDEX_BYTES = 16 * 1024 * 1024;
|
|
8372
|
+
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
8373
|
+
var MAX_PROBE_BYTES = 64 * 1024;
|
|
8374
|
+
var MAX_DEPENDENCY_METADATA_BYTES = 64 * 1024 * 1024;
|
|
8375
|
+
var MAX_PACKAGE_MANIFEST_BYTES = 1024 * 1024;
|
|
8376
|
+
var MAX_READINESS_RESPONSE_BYTES = 64 * 1024;
|
|
8377
|
+
var BUILTINS = /* @__PURE__ */ new Set([
|
|
8378
|
+
...builtinModules,
|
|
8379
|
+
...builtinModules.map((name) => `node:${name}`)
|
|
8380
|
+
]);
|
|
8381
|
+
var ROOT_SOURCE_HASH_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8382
|
+
".agents",
|
|
8383
|
+
".claude",
|
|
8384
|
+
".git",
|
|
8385
|
+
".miaoda-cache",
|
|
8386
|
+
".miaoda-runtime",
|
|
8387
|
+
".turbo",
|
|
8388
|
+
"coverage",
|
|
8389
|
+
"dist",
|
|
8390
|
+
"logs"
|
|
8391
|
+
]);
|
|
8392
|
+
var probeIdentities = /* @__PURE__ */ new WeakMap();
|
|
8393
|
+
function sha256(contents) {
|
|
8394
|
+
return createHash("sha256").update(contents).digest("hex");
|
|
8395
|
+
}
|
|
8396
|
+
function stableJson(value) {
|
|
8397
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
8398
|
+
if (value && typeof value === "object") {
|
|
8399
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
8400
|
+
}
|
|
8401
|
+
return JSON.stringify(value);
|
|
8402
|
+
}
|
|
8403
|
+
function isCapabilityReadinessPayload(contents) {
|
|
8404
|
+
if (contents.length > MAX_READINESS_RESPONSE_BYTES) return false;
|
|
8405
|
+
try {
|
|
8406
|
+
const payload = JSON.parse(contents.toString("utf8"));
|
|
8407
|
+
return Boolean(
|
|
8408
|
+
payload && typeof payload === "object" && payload.status_code === "0" && payload.data && typeof payload.data === "object" && !Array.isArray(payload.data) && Array.isArray(payload.data.capabilities)
|
|
8409
|
+
);
|
|
8410
|
+
} catch {
|
|
8411
|
+
return false;
|
|
8412
|
+
}
|
|
8413
|
+
}
|
|
8414
|
+
function isInside(root, candidate) {
|
|
8415
|
+
const relative = path26.relative(root, candidate);
|
|
8416
|
+
return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
|
|
8417
|
+
}
|
|
8418
|
+
function toPosixRelative(root, file) {
|
|
8419
|
+
return path26.relative(root, file).split(path26.sep).join("/");
|
|
8420
|
+
}
|
|
8421
|
+
function assertNoSymlinkComponents(root, candidate, message) {
|
|
8422
|
+
if (!isInside(root, candidate)) throw new Error(`${message}: escaped root`);
|
|
8423
|
+
let current = root;
|
|
8424
|
+
for (const segment of path26.relative(root, candidate).split(path26.sep)) {
|
|
8425
|
+
if (!segment) continue;
|
|
8426
|
+
current = path26.join(current, segment);
|
|
8427
|
+
if (fs30.existsSync(current) && fs30.lstatSync(current).isSymbolicLink()) {
|
|
8428
|
+
throw new Error(message);
|
|
8429
|
+
}
|
|
8430
|
+
}
|
|
8431
|
+
}
|
|
8432
|
+
function walkSourceFiles(root, directory, output) {
|
|
8433
|
+
if (!fs30.existsSync(directory)) return;
|
|
8434
|
+
for (const entry of fs30.readdirSync(directory, { withFileTypes: true })) {
|
|
8435
|
+
if (entry.name === "node_modules" || directory === root && ROOT_SOURCE_HASH_IGNORED_DIRECTORIES.has(entry.name)) {
|
|
8436
|
+
continue;
|
|
8437
|
+
}
|
|
8438
|
+
const candidate = path26.join(directory, entry.name);
|
|
8439
|
+
if (entry.isSymbolicLink()) {
|
|
8440
|
+
throw new Error(
|
|
8441
|
+
`source symlink is unsupported: ${toPosixRelative(root, candidate)}`
|
|
8442
|
+
);
|
|
8443
|
+
}
|
|
8444
|
+
if (entry.isDirectory()) walkSourceFiles(root, candidate, output);
|
|
8445
|
+
else if (entry.isFile()) output.push(candidate);
|
|
8446
|
+
else {
|
|
8447
|
+
throw new Error(
|
|
8448
|
+
`source special file is unsupported: ${toPosixRelative(root, candidate)}`
|
|
8449
|
+
);
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
}
|
|
8453
|
+
function workspaceSourceSha256(projectRoot) {
|
|
8454
|
+
const files = [];
|
|
8455
|
+
walkSourceFiles(projectRoot, projectRoot, files);
|
|
8456
|
+
const hash = createHash("sha256");
|
|
8457
|
+
for (const file of files.sort()) {
|
|
8458
|
+
hash.update(toPosixRelative(projectRoot, file));
|
|
8459
|
+
hash.update("\0");
|
|
8460
|
+
updateHashFromFile(hash, file);
|
|
8461
|
+
hash.update("\0");
|
|
8462
|
+
}
|
|
8463
|
+
return hash.digest("hex");
|
|
8464
|
+
}
|
|
8465
|
+
function updateHashFromFile(hash, file) {
|
|
8466
|
+
const descriptor = fs30.openSync(file, "r");
|
|
8467
|
+
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
8468
|
+
try {
|
|
8469
|
+
const before = fs30.fstatSync(descriptor);
|
|
8470
|
+
let position = 0;
|
|
8471
|
+
while (position < before.size) {
|
|
8472
|
+
const bytesRead = fs30.readSync(
|
|
8473
|
+
descriptor,
|
|
8474
|
+
buffer,
|
|
8475
|
+
0,
|
|
8476
|
+
Math.min(buffer.length, before.size - position),
|
|
8477
|
+
position
|
|
8478
|
+
);
|
|
8479
|
+
if (bytesRead === 0) break;
|
|
8480
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
8481
|
+
position += bytesRead;
|
|
8482
|
+
}
|
|
8483
|
+
const after = fs30.fstatSync(descriptor);
|
|
8484
|
+
if (position !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs) {
|
|
8485
|
+
throw new Error(`source changed while hashing: ${file}`);
|
|
8486
|
+
}
|
|
8487
|
+
} finally {
|
|
8488
|
+
fs30.closeSync(descriptor);
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
function runtimeIdentity() {
|
|
8492
|
+
const report = process.report?.getReport();
|
|
8493
|
+
const header = report?.header;
|
|
8494
|
+
return {
|
|
8495
|
+
nodeVersion: process.versions.node,
|
|
8496
|
+
nodeModulesAbi: process.versions.modules,
|
|
8497
|
+
platform: process.platform,
|
|
8498
|
+
arch: process.arch,
|
|
8499
|
+
libc: typeof header?.glibcVersionRuntime === "string" ? `glibc-${header.glibcVersionRuntime}` : process.platform === "linux" ? "linux-unknown-libc" : "not-applicable"
|
|
8500
|
+
};
|
|
8501
|
+
}
|
|
8502
|
+
function resolveEntry(projectRoot, requested) {
|
|
8503
|
+
const entry = path26.resolve(projectRoot, requested);
|
|
8504
|
+
if (!isInside(projectRoot, entry))
|
|
8505
|
+
throw new Error("compiled entry escaped project root");
|
|
8506
|
+
if (!fs30.existsSync(entry))
|
|
8507
|
+
throw new Error(`compiled entry is not a file: ${entry}`);
|
|
8508
|
+
if (fs30.lstatSync(entry).isSymbolicLink()) {
|
|
8509
|
+
throw new Error("compiled entry symlink is unsupported");
|
|
8510
|
+
}
|
|
8511
|
+
const realEntry = fs30.realpathSync(entry);
|
|
8512
|
+
if (!isInside(projectRoot, realEntry))
|
|
8513
|
+
throw new Error("compiled entry escaped project root");
|
|
8514
|
+
if (!fs30.statSync(realEntry).isFile())
|
|
8515
|
+
throw new Error(`compiled entry is not a file: ${entry}`);
|
|
8516
|
+
return realEntry;
|
|
8517
|
+
}
|
|
8518
|
+
function resolveNestTsconfig(projectRoot) {
|
|
8519
|
+
const nestConfigFile = path26.join(projectRoot, "nest-cli.json");
|
|
8520
|
+
let configured;
|
|
8521
|
+
if (fs30.existsSync(nestConfigFile)) {
|
|
8522
|
+
const nestConfig = JSON.parse(fs30.readFileSync(nestConfigFile, "utf8"));
|
|
8523
|
+
configured = nestConfig.compilerOptions?.tsConfigPath;
|
|
8524
|
+
if (configured !== void 0 && typeof configured !== "string") {
|
|
8525
|
+
throw new Error("nest-cli.json compilerOptions.tsConfigPath is invalid");
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
const candidates = typeof configured === "string" ? [configured] : ["tsconfig.build.json", "tsconfig.node.json", "tsconfig.json"];
|
|
8529
|
+
const selected = candidates.map((candidate) => path26.resolve(projectRoot, candidate)).find((candidate) => fs30.existsSync(candidate));
|
|
8530
|
+
if (!selected) {
|
|
8531
|
+
throw new Error(`Nest TypeScript config missing: ${candidates.join(", ")}`);
|
|
8532
|
+
}
|
|
8533
|
+
if (!isInside(projectRoot, selected))
|
|
8534
|
+
throw new Error("Nest TypeScript config escaped project root");
|
|
8535
|
+
if (fs30.lstatSync(selected).isSymbolicLink())
|
|
8536
|
+
throw new Error("Nest TypeScript config symlink is unsupported");
|
|
8537
|
+
const realConfig = fs30.realpathSync(selected);
|
|
8538
|
+
if (!isInside(projectRoot, realConfig) || !fs30.statSync(realConfig).isFile()) {
|
|
8539
|
+
throw new Error("Nest TypeScript config escaped project root");
|
|
8540
|
+
}
|
|
8541
|
+
return realConfig;
|
|
8542
|
+
}
|
|
8543
|
+
function resolveNestCli(projectRoot) {
|
|
8544
|
+
const projectRequire = createRequire3(path26.join(projectRoot, "package.json"));
|
|
8545
|
+
try {
|
|
8546
|
+
return projectRequire.resolve("@nestjs/cli/bin/nest.js");
|
|
8547
|
+
} catch (directError) {
|
|
8548
|
+
try {
|
|
8549
|
+
const packageFile = projectRequire.resolve("@nestjs/cli/package.json");
|
|
8550
|
+
const packageJson = JSON.parse(fs30.readFileSync(packageFile, "utf8"));
|
|
8551
|
+
const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.nest;
|
|
8552
|
+
if (!relativeBin) throw new Error("@nestjs/cli has no nest binary");
|
|
8553
|
+
const cliFile = path26.resolve(path26.dirname(packageFile), relativeBin);
|
|
8554
|
+
if (!fs30.statSync(cliFile).isFile())
|
|
8555
|
+
throw new Error("@nestjs/cli nest binary is not a file");
|
|
8556
|
+
return cliFile;
|
|
8557
|
+
} catch (packageError) {
|
|
8558
|
+
throw new Error(
|
|
8559
|
+
`project @nestjs/cli is unavailable: ${packageError instanceof Error ? packageError.message : String(packageError)}; direct=${directError instanceof Error ? directError.message : String(directError)}`
|
|
8560
|
+
);
|
|
8561
|
+
}
|
|
8562
|
+
}
|
|
8563
|
+
}
|
|
8564
|
+
function readLinuxProcessIdentity(pid) {
|
|
8565
|
+
if (process.platform !== "linux") return void 0;
|
|
8566
|
+
try {
|
|
8567
|
+
const stat = fs30.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
8568
|
+
const commandEnd = stat.lastIndexOf(")");
|
|
8569
|
+
if (commandEnd < 0) return void 0;
|
|
8570
|
+
const fields = stat.slice(commandEnd + 1).trim().split(/\s+/);
|
|
8571
|
+
return /^\d+$/.test(fields[19] || "") && /^\d+$/.test(fields[2] || "") ? { processStartTicks: fields[19], processGroupId: Number(fields[2]) } : void 0;
|
|
8572
|
+
} catch {
|
|
8573
|
+
return void 0;
|
|
8574
|
+
}
|
|
8575
|
+
}
|
|
8576
|
+
function registerProbe(child) {
|
|
8577
|
+
if (!child.pid) throw new Error("isolated process pid unavailable");
|
|
8578
|
+
const identity = readLinuxProcessIdentity(child.pid);
|
|
8579
|
+
if (process.platform === "linux" && !identity) {
|
|
8580
|
+
child.kill("SIGKILL");
|
|
8581
|
+
throw new Error("isolated process birth identity unavailable");
|
|
8582
|
+
}
|
|
8583
|
+
probeIdentities.set(child, {
|
|
8584
|
+
pid: child.pid,
|
|
8585
|
+
processStartTicks: identity?.processStartTicks
|
|
8586
|
+
});
|
|
8587
|
+
}
|
|
8588
|
+
function probeGroupRunning(child) {
|
|
8589
|
+
const registered = probeIdentities.get(child);
|
|
8590
|
+
if (!registered) return false;
|
|
8591
|
+
if (process.platform === "linux") {
|
|
8592
|
+
const current = readLinuxProcessIdentity(registered.pid);
|
|
8593
|
+
if (current) {
|
|
8594
|
+
if (current.processStartTicks !== registered.processStartTicks || current.processGroupId !== registered.pid) {
|
|
8595
|
+
return false;
|
|
8596
|
+
}
|
|
8597
|
+
} else {
|
|
8598
|
+
try {
|
|
8599
|
+
process.kill(registered.pid, 0);
|
|
8600
|
+
return false;
|
|
8601
|
+
} catch (error) {
|
|
8602
|
+
if (error.code !== "ESRCH") return false;
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
}
|
|
8606
|
+
try {
|
|
8607
|
+
process.kill(-registered.pid, 0);
|
|
8608
|
+
return true;
|
|
8609
|
+
} catch (error) {
|
|
8610
|
+
return error.code === "EPERM";
|
|
8611
|
+
}
|
|
8612
|
+
}
|
|
8613
|
+
async function waitForProbeGroup(child, timeoutMs) {
|
|
8614
|
+
const deadline = Date.now() + timeoutMs;
|
|
8615
|
+
while (probeGroupRunning(child) && Date.now() < deadline) {
|
|
8616
|
+
await new Promise((resolve2) => setTimeout(resolve2, 25));
|
|
8617
|
+
}
|
|
8618
|
+
return !probeGroupRunning(child);
|
|
8619
|
+
}
|
|
8620
|
+
async function stopProbe(child) {
|
|
8621
|
+
const registered = probeIdentities.get(child);
|
|
8622
|
+
if (!registered) return;
|
|
8623
|
+
if (probeGroupRunning(child)) {
|
|
8624
|
+
try {
|
|
8625
|
+
process.kill(-registered.pid, "SIGTERM");
|
|
8626
|
+
} catch {
|
|
8627
|
+
}
|
|
8628
|
+
}
|
|
8629
|
+
if (!await waitForProbeGroup(child, 500)) {
|
|
8630
|
+
if (probeGroupRunning(child)) {
|
|
8631
|
+
try {
|
|
8632
|
+
process.kill(-registered.pid, "SIGKILL");
|
|
8633
|
+
} catch {
|
|
8634
|
+
}
|
|
8635
|
+
}
|
|
8636
|
+
if (!await waitForProbeGroup(child, 500)) {
|
|
8637
|
+
throw new Error(
|
|
8638
|
+
`isolated probe process group did not exit: ${registered.pid}`
|
|
8639
|
+
);
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
8643
|
+
const exited = await new Promise((resolve2) => {
|
|
8644
|
+
let settled = false;
|
|
8645
|
+
let timer;
|
|
8646
|
+
const finish = (result) => {
|
|
8647
|
+
if (settled) return;
|
|
8648
|
+
settled = true;
|
|
8649
|
+
if (timer) clearTimeout(timer);
|
|
8650
|
+
child.off("exit", onExit);
|
|
8651
|
+
child.off("error", onError);
|
|
8652
|
+
resolve2(result);
|
|
8653
|
+
};
|
|
8654
|
+
const onExit = () => finish(true);
|
|
8655
|
+
const onError = () => finish(false);
|
|
8656
|
+
child.once("exit", onExit);
|
|
8657
|
+
child.once("error", onError);
|
|
8658
|
+
timer = setTimeout(() => finish(false), 5e3);
|
|
8659
|
+
if (child.exitCode !== null || child.signalCode !== null) finish(true);
|
|
8279
8660
|
});
|
|
8661
|
+
if (!exited) {
|
|
8662
|
+
throw new Error(
|
|
8663
|
+
`isolated probe child did not publish exit after group cleanup: ${registered.pid}`
|
|
8664
|
+
);
|
|
8665
|
+
}
|
|
8280
8666
|
}
|
|
8281
|
-
}
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8667
|
+
}
|
|
8668
|
+
function collectChildOutput(child, stream) {
|
|
8669
|
+
let output = "";
|
|
8670
|
+
child[stream]?.setEncoding("utf8");
|
|
8671
|
+
child[stream]?.on("data", (chunk) => {
|
|
8672
|
+
output = `${output}${String(chunk)}`.slice(-64 * 1024);
|
|
8673
|
+
});
|
|
8674
|
+
return () => output;
|
|
8675
|
+
}
|
|
8676
|
+
async function waitForCommand(child, timeoutMs, description) {
|
|
8677
|
+
let timer;
|
|
8678
|
+
try {
|
|
8679
|
+
return await new Promise((resolve2, reject) => {
|
|
8680
|
+
let settled = false;
|
|
8681
|
+
const finish = (callback) => {
|
|
8682
|
+
if (settled) return;
|
|
8683
|
+
settled = true;
|
|
8684
|
+
if (timer) clearTimeout(timer);
|
|
8685
|
+
callback();
|
|
8686
|
+
};
|
|
8687
|
+
child.once("error", (error) => finish(() => reject(error)));
|
|
8688
|
+
child.once(
|
|
8689
|
+
"exit",
|
|
8690
|
+
(code, signal) => finish(
|
|
8691
|
+
() => signal ? reject(new Error(`${description} exited by ${signal}`)) : resolve2(code ?? 1)
|
|
8692
|
+
)
|
|
8693
|
+
);
|
|
8694
|
+
timer = setTimeout(
|
|
8695
|
+
() => finish(() => reject(new Error(`${description} timeout`))),
|
|
8696
|
+
timeoutMs
|
|
8697
|
+
);
|
|
8698
|
+
});
|
|
8699
|
+
} finally {
|
|
8700
|
+
if (timer) clearTimeout(timer);
|
|
8701
|
+
}
|
|
8702
|
+
}
|
|
8703
|
+
async function compileNestSource(projectRoot, stagingRoot, timeoutMs, sourceConfig) {
|
|
8704
|
+
const cliFile = resolveNestCli(projectRoot);
|
|
8705
|
+
const compileRoot = path26.join(stagingRoot, ".compiled");
|
|
8706
|
+
const outputRoot = path26.join(compileRoot, "output");
|
|
8707
|
+
fs30.mkdirSync(outputRoot, { recursive: true });
|
|
8708
|
+
const isolatedConfig = path26.join(compileRoot, "tsconfig.json");
|
|
8709
|
+
fs30.writeFileSync(
|
|
8710
|
+
isolatedConfig,
|
|
8711
|
+
`${JSON.stringify(
|
|
8712
|
+
{
|
|
8713
|
+
extends: sourceConfig,
|
|
8714
|
+
compilerOptions: {
|
|
8715
|
+
outDir: outputRoot,
|
|
8716
|
+
incremental: false,
|
|
8717
|
+
composite: false,
|
|
8718
|
+
declaration: false,
|
|
8719
|
+
declarationMap: false,
|
|
8720
|
+
sourceMap: false,
|
|
8721
|
+
inlineSourceMap: false
|
|
8722
|
+
}
|
|
8723
|
+
},
|
|
8724
|
+
null,
|
|
8725
|
+
2
|
|
8726
|
+
)}
|
|
8727
|
+
`,
|
|
8728
|
+
{ mode: 384 }
|
|
8729
|
+
);
|
|
8730
|
+
const child = spawn2(
|
|
8731
|
+
process.execPath,
|
|
8732
|
+
[cliFile, "build", "--path", path26.relative(projectRoot, isolatedConfig)],
|
|
8733
|
+
{
|
|
8734
|
+
cwd: projectRoot,
|
|
8735
|
+
detached: true,
|
|
8736
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
8737
|
+
env: { ...process.env, NODE_ENV: "production" }
|
|
8738
|
+
}
|
|
8739
|
+
);
|
|
8740
|
+
registerProbe(child);
|
|
8741
|
+
const stdout = collectChildOutput(child, "stdout");
|
|
8742
|
+
const stderr = collectChildOutput(child, "stderr");
|
|
8743
|
+
try {
|
|
8744
|
+
const code = await waitForCommand(
|
|
8745
|
+
child,
|
|
8746
|
+
timeoutMs,
|
|
8747
|
+
"isolated Nest compilation"
|
|
8748
|
+
);
|
|
8749
|
+
if (code !== 0) {
|
|
8750
|
+
throw new Error(
|
|
8751
|
+
`isolated Nest compilation failed with exit ${code}: ${stderr().trim() || stdout().trim()}`
|
|
8752
|
+
);
|
|
8753
|
+
}
|
|
8754
|
+
} finally {
|
|
8755
|
+
await stopProbe(child);
|
|
8756
|
+
}
|
|
8757
|
+
const candidates = [
|
|
8758
|
+
path26.join(outputRoot, "server", "main.js"),
|
|
8759
|
+
path26.join(outputRoot, "main.js")
|
|
8760
|
+
].filter((candidate) => fs30.existsSync(candidate));
|
|
8761
|
+
if (candidates.length !== 1) {
|
|
8762
|
+
throw new Error(
|
|
8763
|
+
candidates.length === 0 ? "isolated Nest compiled entry missing; checked server/main.js and main.js" : "isolated Nest compiled entry is ambiguous"
|
|
8764
|
+
);
|
|
8765
|
+
}
|
|
8766
|
+
const entry = fs30.realpathSync(candidates[0]);
|
|
8767
|
+
if (!isInside(fs30.realpathSync(outputRoot), entry) || !fs30.statSync(entry).isFile()) {
|
|
8768
|
+
throw new Error("isolated Nest compiled entry escaped output root");
|
|
8769
|
+
}
|
|
8770
|
+
return { entry, applicationRoot: outputRoot };
|
|
8771
|
+
}
|
|
8772
|
+
function copyRegularFile(source, target) {
|
|
8773
|
+
const stats = fs30.lstatSync(source);
|
|
8774
|
+
if (stats.isSymbolicLink()) {
|
|
8775
|
+
throw new Error(`runtime tree symlink is unsupported: ${source}`);
|
|
8776
|
+
}
|
|
8777
|
+
if (!stats.isFile()) {
|
|
8778
|
+
throw new Error(`runtime tree special file is unsupported: ${source}`);
|
|
8779
|
+
}
|
|
8780
|
+
fs30.mkdirSync(path26.dirname(target), { recursive: true });
|
|
8781
|
+
fs30.copyFileSync(source, target, fs30.constants.COPYFILE_FICLONE);
|
|
8782
|
+
fs30.chmodSync(target, stats.mode & 73 ? 493 : 420);
|
|
8783
|
+
}
|
|
8784
|
+
function copyDirectoryContents(sourceRoot, targetRoot, skipNestedNodeModules, budget, inspectFile) {
|
|
8785
|
+
const visit = (source, target) => {
|
|
8786
|
+
const stats = fs30.lstatSync(source);
|
|
8787
|
+
if (stats.isSymbolicLink()) {
|
|
8788
|
+
throw new Error(`runtime tree symlink is unsupported: ${source}`);
|
|
8789
|
+
}
|
|
8790
|
+
if (stats.isFile()) {
|
|
8791
|
+
if (budget) {
|
|
8792
|
+
reserveGenerationBudget(source, stats.size, budget);
|
|
8793
|
+
}
|
|
8794
|
+
inspectFile?.(source, stats);
|
|
8795
|
+
copyRegularFile(source, target);
|
|
8796
|
+
return;
|
|
8797
|
+
}
|
|
8798
|
+
if (!stats.isDirectory()) {
|
|
8799
|
+
throw new Error(`runtime tree special file is unsupported: ${source}`);
|
|
8800
|
+
}
|
|
8801
|
+
fs30.mkdirSync(target, { recursive: true, mode: 493 });
|
|
8802
|
+
for (const entry of fs30.readdirSync(source, { withFileTypes: true })) {
|
|
8803
|
+
if (skipNestedNodeModules && entry.name === "node_modules") continue;
|
|
8804
|
+
visit(path26.join(source, entry.name), path26.join(target, entry.name));
|
|
8805
|
+
}
|
|
8806
|
+
};
|
|
8807
|
+
visit(sourceRoot, targetRoot);
|
|
8808
|
+
}
|
|
8809
|
+
function reserveGenerationBudget(file, size, budget) {
|
|
8810
|
+
if (size > MAX_GENERATION_FILE_BYTES) {
|
|
8811
|
+
throw new Error(`generation file exceeds 64 MiB: ${file}`);
|
|
8812
|
+
}
|
|
8813
|
+
if (budget.fileCount + 1 > MAX_GENERATION_FILES) {
|
|
8814
|
+
throw new Error(`generation exceeds ${MAX_GENERATION_FILES} files`);
|
|
8815
|
+
}
|
|
8816
|
+
if (budget.totalBytes + size > MAX_GENERATION_BYTES) {
|
|
8817
|
+
throw new Error("generation exceeds 256 MiB");
|
|
8818
|
+
}
|
|
8819
|
+
budget.fileCount += 1;
|
|
8820
|
+
budget.totalBytes += size;
|
|
8821
|
+
}
|
|
8822
|
+
function measurePayload(payloadRoot) {
|
|
8823
|
+
const budget = { fileCount: 0, totalBytes: 0 };
|
|
8824
|
+
const visit = (directory) => {
|
|
8825
|
+
for (const entry of fs30.readdirSync(directory, { withFileTypes: true })) {
|
|
8826
|
+
const candidate = path26.join(directory, entry.name);
|
|
8827
|
+
const stats = fs30.lstatSync(candidate);
|
|
8828
|
+
if (stats.isSymbolicLink()) {
|
|
8829
|
+
throw new Error(`generation symlink is unsupported: ${candidate}`);
|
|
8830
|
+
}
|
|
8831
|
+
if (stats.isDirectory()) visit(candidate);
|
|
8832
|
+
else if (stats.isFile()) {
|
|
8833
|
+
reserveGenerationBudget(candidate, stats.size, budget);
|
|
8834
|
+
} else {
|
|
8835
|
+
throw new Error(`generation special file is unsupported: ${candidate}`);
|
|
8836
|
+
}
|
|
8837
|
+
}
|
|
8838
|
+
};
|
|
8839
|
+
visit(payloadRoot);
|
|
8840
|
+
return budget;
|
|
8841
|
+
}
|
|
8842
|
+
function copyProbeWorkspace(projectRoot, probeProjectRoot) {
|
|
8843
|
+
const files = [];
|
|
8844
|
+
walkSourceFiles(projectRoot, projectRoot, files);
|
|
8845
|
+
const budget = { fileCount: 0, totalBytes: 0 };
|
|
8846
|
+
for (const file of files) {
|
|
8847
|
+
reserveGenerationBudget(file, fs30.lstatSync(file).size, budget);
|
|
8848
|
+
copyRegularFile(
|
|
8849
|
+
file,
|
|
8850
|
+
path26.join(
|
|
8851
|
+
probeProjectRoot,
|
|
8852
|
+
...toPosixRelative(projectRoot, file).split("/")
|
|
8853
|
+
)
|
|
8854
|
+
);
|
|
8855
|
+
}
|
|
8856
|
+
}
|
|
8857
|
+
function readJsonObject(file, description) {
|
|
8858
|
+
const parsed = JSON.parse(
|
|
8859
|
+
readBoundedFile(file, MAX_PACKAGE_MANIFEST_BYTES, description).toString(
|
|
8860
|
+
"utf8"
|
|
8861
|
+
)
|
|
8862
|
+
);
|
|
8863
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8864
|
+
throw new Error(`${description} must be a JSON object`);
|
|
8865
|
+
}
|
|
8866
|
+
return parsed;
|
|
8867
|
+
}
|
|
8868
|
+
function readBoundedFile(file, maxBytes, description) {
|
|
8869
|
+
const stats = fs30.lstatSync(file);
|
|
8870
|
+
if (stats.isSymbolicLink() || !stats.isFile() || stats.size > maxBytes) {
|
|
8871
|
+
throw new Error(`${description} exceeds the supported metadata size`);
|
|
8872
|
+
}
|
|
8873
|
+
const contents = fs30.readFileSync(file);
|
|
8874
|
+
if (contents.length !== stats.size) {
|
|
8875
|
+
throw new Error(`${description} changed while reading`);
|
|
8876
|
+
}
|
|
8877
|
+
return contents;
|
|
8878
|
+
}
|
|
8879
|
+
function lockPackages(lock, description) {
|
|
8880
|
+
if (typeof lock.lockfileVersion !== "number" || lock.lockfileVersion < 2 || !lock.packages || typeof lock.packages !== "object" || Array.isArray(lock.packages)) {
|
|
8881
|
+
throw new Error(`${description} must be an npm lockfile v2 or v3`);
|
|
8882
|
+
}
|
|
8883
|
+
return lock.packages;
|
|
8884
|
+
}
|
|
8885
|
+
function comparableLockRecord(record) {
|
|
8886
|
+
return {
|
|
8887
|
+
integrity: record.integrity,
|
|
8888
|
+
link: record.link,
|
|
8889
|
+
resolved: record.resolved,
|
|
8890
|
+
version: record.version
|
|
8891
|
+
};
|
|
8892
|
+
}
|
|
8893
|
+
function isSafePackageLockPath(relative) {
|
|
8894
|
+
if (!relative.startsWith("node_modules/") || relative.includes("\\") || path26.posix.isAbsolute(relative)) {
|
|
8895
|
+
return false;
|
|
8896
|
+
}
|
|
8897
|
+
const segments = relative.split("/");
|
|
8898
|
+
return segments.every(
|
|
8899
|
+
(segment) => segment && segment !== "." && segment !== ".."
|
|
8900
|
+
);
|
|
8901
|
+
}
|
|
8902
|
+
function memberPropertyName(node) {
|
|
8903
|
+
if (!node || typeof node !== "object") return void 0;
|
|
8904
|
+
const candidate = node;
|
|
8905
|
+
if (candidate.type === "Identifier") return candidate.name;
|
|
8906
|
+
if (candidate.type === "Literal" && typeof candidate.value === "string") {
|
|
8907
|
+
return candidate.value;
|
|
8908
|
+
}
|
|
8909
|
+
return void 0;
|
|
8910
|
+
}
|
|
8911
|
+
function isLiteralString(node) {
|
|
8912
|
+
return Boolean(
|
|
8913
|
+
node && typeof node === "object" && node.type === "Literal" && typeof node.value === "string"
|
|
8914
|
+
);
|
|
8915
|
+
}
|
|
8916
|
+
function isRuntimeTypeScriptSpecifier(node) {
|
|
8917
|
+
return Boolean(
|
|
8918
|
+
isLiteralString(node) && /\.(?:cts|mts|ts|tsx)(?:$|[?#])/.test(
|
|
8919
|
+
String(node.value)
|
|
8920
|
+
)
|
|
8921
|
+
);
|
|
8922
|
+
}
|
|
8923
|
+
function isPackageRelativeDynamicTarget(node) {
|
|
8924
|
+
if (!node || typeof node !== "object") return false;
|
|
8925
|
+
const candidate = node;
|
|
8926
|
+
if (candidate.type === "Literal" && typeof candidate.value === "string") {
|
|
8927
|
+
return candidate.value.startsWith("./");
|
|
8928
|
+
}
|
|
8929
|
+
if (candidate.type === "TemplateLiteral") {
|
|
8930
|
+
const prefix = candidate.quasis?.[0]?.value?.cooked;
|
|
8931
|
+
return typeof prefix === "string" && prefix.startsWith("./");
|
|
8932
|
+
}
|
|
8933
|
+
if (candidate.type === "BinaryExpression" && candidate.operator === "+") {
|
|
8934
|
+
return isPackageRelativeDynamicTarget(candidate.left);
|
|
8935
|
+
}
|
|
8936
|
+
if (candidate.type === "ConditionalExpression") {
|
|
8937
|
+
return isPackageRelativeDynamicTarget(candidate.consequent) && isPackageRelativeDynamicTarget(candidate.alternate);
|
|
8938
|
+
}
|
|
8939
|
+
if (candidate.type === "LogicalExpression" && (candidate.operator === "||" || candidate.operator === "??")) {
|
|
8940
|
+
return isPackageRelativeDynamicTarget(candidate.left) && isPackageRelativeDynamicTarget(candidate.right);
|
|
8941
|
+
}
|
|
8942
|
+
if (candidate.type !== "CallExpression") return false;
|
|
8943
|
+
const callee = candidate.callee;
|
|
8944
|
+
if (callee?.type !== "MemberExpression") return false;
|
|
8945
|
+
const property = memberPropertyName(callee.property);
|
|
8946
|
+
if (property !== "join" && property !== "resolve") return false;
|
|
8947
|
+
const firstArgument = candidate.arguments?.[0];
|
|
8948
|
+
return Boolean(
|
|
8949
|
+
firstArgument && (firstArgument.type === "Identifier" && (firstArgument.name === "__dirname" || firstArgument.name === "__filename") || isPackageRelativeDynamicTarget(firstArgument))
|
|
8950
|
+
);
|
|
8951
|
+
}
|
|
8952
|
+
function isProvablyPackageEscapingPath(node) {
|
|
8953
|
+
if (!node || typeof node !== "object") return false;
|
|
8954
|
+
const candidate = node;
|
|
8955
|
+
if (candidate.type === "BinaryExpression" && candidate.operator === "+") {
|
|
8956
|
+
const left = candidate.left;
|
|
8957
|
+
const right = candidate.right;
|
|
8958
|
+
return Boolean(
|
|
8959
|
+
left?.type === "Identifier" && left.name === "__dirname" && right?.type === "Literal" && typeof right.value === "string" && path26.posix.normalize(path26.posix.join("/package", right.value)).startsWith("/package/") === false
|
|
8960
|
+
);
|
|
8961
|
+
}
|
|
8962
|
+
if (candidate.type !== "CallExpression") return false;
|
|
8963
|
+
const callee = candidate.callee;
|
|
8964
|
+
if (callee?.type !== "MemberExpression" || !["join", "resolve"].includes(memberPropertyName(callee.property) || "")) {
|
|
8965
|
+
return false;
|
|
8966
|
+
}
|
|
8967
|
+
const [base, ...segments] = candidate.arguments || [];
|
|
8968
|
+
if (!base || typeof base !== "object" || base.type !== "Identifier" || base.name !== "__dirname" || segments.some(
|
|
8969
|
+
(segment) => !segment || typeof segment !== "object" || segment.type !== "Literal" || typeof segment.value !== "string"
|
|
8970
|
+
)) {
|
|
8971
|
+
return false;
|
|
8972
|
+
}
|
|
8973
|
+
const normalized = path26.posix.normalize(
|
|
8974
|
+
path26.posix.join(
|
|
8975
|
+
"/package",
|
|
8976
|
+
...segments.map((segment) => String(segment.value))
|
|
8977
|
+
)
|
|
8978
|
+
);
|
|
8979
|
+
return normalized !== "/package" && !normalized.startsWith("/package/");
|
|
8980
|
+
}
|
|
8981
|
+
function dynamicCodeEvaluatorKind(node) {
|
|
8982
|
+
if (!node || typeof node !== "object") return void 0;
|
|
8983
|
+
const candidate = node;
|
|
8984
|
+
if (candidate.type === "Identifier") {
|
|
8985
|
+
if (candidate.name === "eval") return "eval";
|
|
8986
|
+
if (candidate.name === "Function") return "function";
|
|
8987
|
+
return void 0;
|
|
8988
|
+
}
|
|
8989
|
+
if (candidate.type === "SequenceExpression") {
|
|
8990
|
+
for (const expression of candidate.expressions ?? []) {
|
|
8991
|
+
const kind = dynamicCodeEvaluatorKind(expression);
|
|
8992
|
+
if (kind) return kind;
|
|
8993
|
+
}
|
|
8994
|
+
return void 0;
|
|
8995
|
+
}
|
|
8996
|
+
if (candidate.type === "MemberExpression") {
|
|
8997
|
+
const property = memberPropertyName(candidate.property);
|
|
8998
|
+
if (property === "eval") return "eval";
|
|
8999
|
+
if (property === "Function") return "function";
|
|
9000
|
+
}
|
|
9001
|
+
return void 0;
|
|
9002
|
+
}
|
|
9003
|
+
function isLiteralEvalImport(node) {
|
|
9004
|
+
if (node.callee.type !== "Identifier" || node.callee.name !== "eval" || node.arguments.length !== 1 || !isLiteralString(node.arguments[0])) {
|
|
9005
|
+
return false;
|
|
9006
|
+
}
|
|
9007
|
+
const source = String(node.arguments[0].value).trim();
|
|
9008
|
+
const match = source.match(/^import\s*\(\s*(['"])([^'"\\]+)\1\s*\)\s*;?$/);
|
|
9009
|
+
return Boolean(match && isBareSpecifier(match[2]));
|
|
9010
|
+
}
|
|
9011
|
+
function isLiteralEvalWithoutUnboundedModuleLoading(node) {
|
|
9012
|
+
if (node.callee.type !== "Identifier" && node.callee.type !== "SequenceExpression") {
|
|
9013
|
+
return false;
|
|
9014
|
+
}
|
|
9015
|
+
if (dynamicCodeEvaluatorKind(node.callee) !== "eval" || node.arguments.length !== 1 || !isLiteralString(node.arguments[0])) {
|
|
9016
|
+
return false;
|
|
9017
|
+
}
|
|
9018
|
+
const source = String(node.arguments[0].value).trim();
|
|
9019
|
+
return source === "require" || isLiteralEvalImport(node) || !/\b(?:import|require)\b|\b(?:dlopen|_load|_resolveFilename)\b/.test(source);
|
|
9020
|
+
}
|
|
9021
|
+
function isEvalRequireFactory(node) {
|
|
9022
|
+
if (!node || typeof node !== "object") return false;
|
|
9023
|
+
const candidate = node;
|
|
9024
|
+
return Boolean(
|
|
9025
|
+
candidate.type === "CallExpression" && candidate.callee.type === "Identifier" && candidate.callee.name === "eval" && candidate.arguments.length === 1 && isLiteralString(candidate.arguments[0]) && String(candidate.arguments[0].value).trim() === "require"
|
|
9026
|
+
);
|
|
9027
|
+
}
|
|
9028
|
+
function packageLayoutCapabilityReasons(source) {
|
|
9029
|
+
const reasons = /* @__PURE__ */ new Set();
|
|
9030
|
+
if (/\brequire\s*\(\s*['"](?:node:)?fs(?:\/promises)?['"]\s*\)/.test(source) || /\bfrom\s*['"](?:node:)?fs(?:\/promises)?['"]/.test(source) || /\bimport\s*['"](?:node:)?fs(?:\/promises)?['"]/.test(source)) {
|
|
9031
|
+
reasons.add("uses filesystem APIs");
|
|
9032
|
+
}
|
|
9033
|
+
if (/\brequire\s*\(\s*['"](?:node:)?vm['"]\s*\)/.test(source) || /\bfrom\s*['"](?:node:)?vm['"]/.test(source) || /\bimport\s*['"](?:node:)?vm['"]/.test(source)) {
|
|
9034
|
+
reasons.add("uses VM code evaluation");
|
|
9035
|
+
}
|
|
9036
|
+
let ast;
|
|
9037
|
+
try {
|
|
9038
|
+
ast = parse(source, {
|
|
9039
|
+
ecmaVersion: "latest",
|
|
9040
|
+
sourceType: "module",
|
|
9041
|
+
allowHashBang: true,
|
|
9042
|
+
allowReturnOutsideFunction: true
|
|
8288
9043
|
});
|
|
9044
|
+
} catch {
|
|
9045
|
+
try {
|
|
9046
|
+
ast = parse(source, {
|
|
9047
|
+
ecmaVersion: "latest",
|
|
9048
|
+
sourceType: "script",
|
|
9049
|
+
allowHashBang: true,
|
|
9050
|
+
allowReturnOutsideFunction: true
|
|
9051
|
+
});
|
|
9052
|
+
} catch {
|
|
9053
|
+
return [...reasons, "requires a package-local JavaScript loader"];
|
|
9054
|
+
}
|
|
9055
|
+
}
|
|
9056
|
+
const requireAliases = /* @__PURE__ */ new Set(["require"]);
|
|
9057
|
+
walkSimple(ast, {
|
|
9058
|
+
VariableDeclarator(node) {
|
|
9059
|
+
if (node.id.type === "Identifier" && node.init?.type === "Identifier" && requireAliases.has(node.init.name)) {
|
|
9060
|
+
requireAliases.add(node.id.name);
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
});
|
|
9064
|
+
walkSimple(ast, {
|
|
9065
|
+
CallExpression(node) {
|
|
9066
|
+
const callee = node.callee;
|
|
9067
|
+
const evaluatorKind = dynamicCodeEvaluatorKind(callee);
|
|
9068
|
+
if (evaluatorKind === "eval" && !isLiteralEvalWithoutUnboundedModuleLoading(node)) {
|
|
9069
|
+
reasons.add("uses eval-generated code");
|
|
9070
|
+
} else if (evaluatorKind === "function") {
|
|
9071
|
+
reasons.add("uses Function constructor");
|
|
9072
|
+
}
|
|
9073
|
+
if (callee.type === "MemberExpression") {
|
|
9074
|
+
const property = memberPropertyName(callee.property);
|
|
9075
|
+
if (property === "_load" || property === "_resolveFilename") {
|
|
9076
|
+
reasons.add("uses unbounded Node module loader");
|
|
9077
|
+
}
|
|
9078
|
+
if (property === "dlopen" && callee.object.type === "Identifier" && callee.object.name === "process") {
|
|
9079
|
+
reasons.add("uses native dynamic loader");
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
if (isEvalRequireFactory(callee)) {
|
|
9083
|
+
reasons.add("uses unbounded dynamic require");
|
|
9084
|
+
}
|
|
9085
|
+
const directRequire = callee.type === "Identifier" && requireAliases.has(callee.name);
|
|
9086
|
+
const moduleRequire = callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "module" && memberPropertyName(callee.property) === "require";
|
|
9087
|
+
const requireResolve = callee.type === "MemberExpression" && callee.object.type === "Identifier" && callee.object.name === "require" && memberPropertyName(callee.property) === "resolve";
|
|
9088
|
+
if ((directRequire || moduleRequire) && !isLiteralString(node.arguments[0])) {
|
|
9089
|
+
reasons.add(
|
|
9090
|
+
isPackageRelativeDynamicTarget(node.arguments[0]) ? "uses package-relative dynamic require" : "uses unbounded dynamic require"
|
|
9091
|
+
);
|
|
9092
|
+
}
|
|
9093
|
+
if ((directRequire || moduleRequire || requireResolve) && isRuntimeTypeScriptSpecifier(node.arguments[0])) {
|
|
9094
|
+
reasons.add("loads runtime TypeScript");
|
|
9095
|
+
}
|
|
9096
|
+
if (requireResolve) {
|
|
9097
|
+
reasons.add(
|
|
9098
|
+
isLiteralString(node.arguments[0]) || isPackageRelativeDynamicTarget(node.arguments[0]) ? "uses package-relative require.resolve" : "uses unbounded dynamic require.resolve"
|
|
9099
|
+
);
|
|
9100
|
+
}
|
|
9101
|
+
},
|
|
9102
|
+
ImportExpression(node) {
|
|
9103
|
+
if (isRuntimeTypeScriptSpecifier(node.source)) {
|
|
9104
|
+
reasons.add("loads runtime TypeScript");
|
|
9105
|
+
} else if (!isLiteralString(node.source)) {
|
|
9106
|
+
reasons.add(
|
|
9107
|
+
isPackageRelativeDynamicTarget(node.source) ? "uses package-relative dynamic import" : "uses unbounded dynamic import"
|
|
9108
|
+
);
|
|
9109
|
+
}
|
|
9110
|
+
},
|
|
9111
|
+
ImportDeclaration(node) {
|
|
9112
|
+
if (isRuntimeTypeScriptSpecifier(node.source)) {
|
|
9113
|
+
reasons.add("loads runtime TypeScript");
|
|
9114
|
+
}
|
|
9115
|
+
},
|
|
9116
|
+
VariableDeclarator(node) {
|
|
9117
|
+
if (node.id.type !== "ObjectPattern") return;
|
|
9118
|
+
const init2 = node.init;
|
|
9119
|
+
const moduleConstructor = Boolean(
|
|
9120
|
+
init2?.type === "MemberExpression" && memberPropertyName(init2.property) === "constructor" && init2.object?.type === "Identifier" && init2.object.name === "module"
|
|
9121
|
+
);
|
|
9122
|
+
const nodeModuleNamespace = Boolean(
|
|
9123
|
+
init2?.type === "Identifier" && init2.name === "Module"
|
|
9124
|
+
);
|
|
9125
|
+
if (!moduleConstructor && !nodeModuleNamespace) return;
|
|
9126
|
+
for (const property of node.id.properties) {
|
|
9127
|
+
if (property.type !== "Property") continue;
|
|
9128
|
+
const name = memberPropertyName(property.key);
|
|
9129
|
+
if (name === "_load" || name === "_resolveFilename") {
|
|
9130
|
+
reasons.add("uses unbounded Node module loader");
|
|
9131
|
+
}
|
|
9132
|
+
if (name === "dlopen") reasons.add("uses native dynamic loader");
|
|
9133
|
+
}
|
|
9134
|
+
},
|
|
9135
|
+
NewExpression(node) {
|
|
9136
|
+
if (dynamicCodeEvaluatorKind(node.callee) === "function") {
|
|
9137
|
+
reasons.add("uses Function constructor");
|
|
9138
|
+
}
|
|
9139
|
+
},
|
|
9140
|
+
MetaProperty(node) {
|
|
9141
|
+
if (node.meta.name === "import" && node.property.name === "meta") {
|
|
9142
|
+
reasons.add("uses import.meta package location");
|
|
9143
|
+
}
|
|
9144
|
+
}
|
|
9145
|
+
});
|
|
9146
|
+
const fsNamespaces = /* @__PURE__ */ new Set();
|
|
9147
|
+
const fsPathFunctions = /* @__PURE__ */ new Set();
|
|
9148
|
+
const fsPathMethods = /* @__PURE__ */ new Set([
|
|
9149
|
+
"access",
|
|
9150
|
+
"accessSync",
|
|
9151
|
+
"createReadStream",
|
|
9152
|
+
"existsSync",
|
|
9153
|
+
"lstat",
|
|
9154
|
+
"lstatSync",
|
|
9155
|
+
"open",
|
|
9156
|
+
"openSync",
|
|
9157
|
+
"readFile",
|
|
9158
|
+
"readFileSync",
|
|
9159
|
+
"readdir",
|
|
9160
|
+
"readdirSync",
|
|
9161
|
+
"realpath",
|
|
9162
|
+
"realpathSync",
|
|
9163
|
+
"stat",
|
|
9164
|
+
"statSync"
|
|
9165
|
+
]);
|
|
9166
|
+
walkSimple(ast, {
|
|
9167
|
+
ImportDeclaration(node) {
|
|
9168
|
+
if (typeof node.source.value !== "string" || !["fs", "node:fs", "fs/promises", "node:fs/promises"].includes(
|
|
9169
|
+
node.source.value
|
|
9170
|
+
)) {
|
|
9171
|
+
return;
|
|
9172
|
+
}
|
|
9173
|
+
for (const specifier of node.specifiers) {
|
|
9174
|
+
if (specifier.type === "ImportSpecifier") {
|
|
9175
|
+
if (fsPathMethods.has(specifier.imported.name)) {
|
|
9176
|
+
fsPathFunctions.add(specifier.local.name);
|
|
9177
|
+
}
|
|
9178
|
+
} else {
|
|
9179
|
+
fsNamespaces.add(specifier.local.name);
|
|
9180
|
+
}
|
|
9181
|
+
}
|
|
9182
|
+
},
|
|
9183
|
+
VariableDeclarator(node) {
|
|
9184
|
+
const init2 = node.init;
|
|
9185
|
+
const required = init2?.type === "CallExpression" && init2.callee?.type === "Identifier" && init2.callee.name === "require" && isLiteralString(init2.arguments?.[0]) && ["fs", "node:fs", "fs/promises", "node:fs/promises"].includes(
|
|
9186
|
+
String((init2.arguments?.[0]).value)
|
|
9187
|
+
);
|
|
9188
|
+
if (!required) return;
|
|
9189
|
+
if (node.id.type === "Identifier") fsNamespaces.add(node.id.name);
|
|
9190
|
+
if (node.id.type === "ObjectPattern") {
|
|
9191
|
+
for (const property of node.id.properties) {
|
|
9192
|
+
if (property.type === "Property" && property.value.type === "Identifier" && fsPathMethods.has(memberPropertyName(property.key) || "")) {
|
|
9193
|
+
fsPathFunctions.add(property.value.name);
|
|
9194
|
+
}
|
|
9195
|
+
}
|
|
9196
|
+
}
|
|
9197
|
+
}
|
|
9198
|
+
});
|
|
9199
|
+
walkSimple(ast, {
|
|
9200
|
+
CallExpression(node) {
|
|
9201
|
+
const callee = node.callee;
|
|
9202
|
+
let isFsPathCall = callee.type === "Identifier" && fsPathFunctions.has(callee.name);
|
|
9203
|
+
if (callee.type === "MemberExpression") {
|
|
9204
|
+
const method = memberPropertyName(callee.property);
|
|
9205
|
+
const object = callee.object;
|
|
9206
|
+
const namespace = object.type === "Identifier" ? object.name : object.type === "MemberExpression" && memberPropertyName(object.property) === "promises" && object.object.type === "Identifier" ? object.object.name : void 0;
|
|
9207
|
+
isFsPathCall = Boolean(
|
|
9208
|
+
namespace && fsNamespaces.has(namespace) && fsPathMethods.has(method || "")
|
|
9209
|
+
);
|
|
9210
|
+
}
|
|
9211
|
+
if (isFsPathCall && isProvablyPackageEscapingPath(node.arguments[0])) {
|
|
9212
|
+
reasons.add("uses filesystem path outside the proven package boundary");
|
|
9213
|
+
}
|
|
9214
|
+
}
|
|
9215
|
+
});
|
|
9216
|
+
walkAncestor(ast, {
|
|
9217
|
+
Identifier(node, _state, ancestors) {
|
|
9218
|
+
const parent = ancestors.at(-2);
|
|
9219
|
+
const grandparent = ancestors.at(-3);
|
|
9220
|
+
if (node.name === "__dirname" || node.name === "__filename") {
|
|
9221
|
+
if (parent?.type === "MemberExpression" && parent.property === node && !parent.computed) {
|
|
9222
|
+
return;
|
|
9223
|
+
}
|
|
9224
|
+
reasons.add("uses package-relative file location");
|
|
9225
|
+
return;
|
|
9226
|
+
}
|
|
9227
|
+
if (node.name === "createRequire") {
|
|
9228
|
+
reasons.add("uses createRequire");
|
|
9229
|
+
return;
|
|
9230
|
+
}
|
|
9231
|
+
if (node.name !== "require") return;
|
|
9232
|
+
if (parent?.type === "CallExpression" && parent.callee === node) return;
|
|
9233
|
+
if (parent?.type === "UnaryExpression" && parent.operator === "typeof") {
|
|
9234
|
+
return;
|
|
9235
|
+
}
|
|
9236
|
+
if (parent?.type === "MemberExpression" && parent.object === node) {
|
|
9237
|
+
const property = memberPropertyName(parent.property);
|
|
9238
|
+
if (property === "resolve" && grandparent?.type === "CallExpression" && grandparent.callee === parent) {
|
|
9239
|
+
return;
|
|
9240
|
+
}
|
|
9241
|
+
reasons.add(`uses require.${property ?? "unknown"} runtime state`);
|
|
9242
|
+
return;
|
|
9243
|
+
}
|
|
9244
|
+
reasons.add("escapes the require function");
|
|
9245
|
+
},
|
|
9246
|
+
MemberExpression(node, _state, ancestors) {
|
|
9247
|
+
const property = memberPropertyName(node.property);
|
|
9248
|
+
const object = node.object;
|
|
9249
|
+
const moduleConstructor = Boolean(
|
|
9250
|
+
object.type === "MemberExpression" && memberPropertyName(object.property) === "constructor" && object.object?.type === "Identifier" && object.object.name === "module"
|
|
9251
|
+
);
|
|
9252
|
+
if ((property === "_load" || property === "_resolveFilename") && (moduleConstructor || object.type === "Identifier" && object.name === "Module")) {
|
|
9253
|
+
reasons.add("uses unbounded Node module loader");
|
|
9254
|
+
}
|
|
9255
|
+
if (property === "dlopen" && object.type === "Identifier" && object.name === "process") {
|
|
9256
|
+
reasons.add("uses native dynamic loader");
|
|
9257
|
+
}
|
|
9258
|
+
if (node.object.type !== "Identifier" || node.object.name !== "module" || property !== "require") {
|
|
9259
|
+
return;
|
|
9260
|
+
}
|
|
9261
|
+
const parent = ancestors.at(-2);
|
|
9262
|
+
if (parent?.type === "CallExpression" && parent.callee === node) return;
|
|
9263
|
+
reasons.add("escapes module.require");
|
|
9264
|
+
}
|
|
9265
|
+
});
|
|
9266
|
+
return [...reasons].sort();
|
|
9267
|
+
}
|
|
9268
|
+
function createPackageOwnerResolver(projectRoot) {
|
|
9269
|
+
const nodeModulesRoot = path26.join(projectRoot, "node_modules");
|
|
9270
|
+
const cache = /* @__PURE__ */ new Map();
|
|
9271
|
+
return (file) => {
|
|
9272
|
+
const absolute = path26.resolve(file);
|
|
9273
|
+
const cached = cache.get(absolute);
|
|
9274
|
+
if (cached !== void 0 || cache.has(absolute)) return cached;
|
|
9275
|
+
if (!isInside(nodeModulesRoot, absolute)) {
|
|
9276
|
+
cache.set(absolute, void 0);
|
|
9277
|
+
return void 0;
|
|
9278
|
+
}
|
|
9279
|
+
let current = fs30.existsSync(absolute) && fs30.statSync(absolute).isDirectory() ? absolute : path26.dirname(absolute);
|
|
9280
|
+
while (isInside(nodeModulesRoot, current) && current !== nodeModulesRoot) {
|
|
9281
|
+
const manifest = path26.join(current, "package.json");
|
|
9282
|
+
if (fs30.existsSync(manifest) && fs30.statSync(manifest).isFile()) {
|
|
9283
|
+
cache.set(absolute, current);
|
|
9284
|
+
return current;
|
|
9285
|
+
}
|
|
9286
|
+
current = path26.dirname(current);
|
|
9287
|
+
}
|
|
9288
|
+
cache.set(absolute, void 0);
|
|
9289
|
+
return void 0;
|
|
9290
|
+
};
|
|
9291
|
+
}
|
|
9292
|
+
function readPackageIdentity(projectRoot, packageRoot) {
|
|
9293
|
+
const packagePath = toPosixRelative(projectRoot, packageRoot);
|
|
9294
|
+
if (!isSafePackageLockPath(packagePath)) {
|
|
9295
|
+
throw new Error(`unsupported dynamic island path: ${packagePath}`);
|
|
9296
|
+
}
|
|
9297
|
+
const manifest = readJsonObject(
|
|
9298
|
+
path26.join(packageRoot, "package.json"),
|
|
9299
|
+
`dynamic island ${packagePath}`
|
|
9300
|
+
);
|
|
9301
|
+
if (typeof manifest.name !== "string" || !manifest.name) {
|
|
9302
|
+
throw new Error(`dynamic island package name is invalid: ${packagePath}`);
|
|
9303
|
+
}
|
|
9304
|
+
return { packagePath, packageName: manifest.name };
|
|
9305
|
+
}
|
|
9306
|
+
function addDynamicIsland(projectRoot, islands, packageRoot, reasons) {
|
|
9307
|
+
const existing = islands.get(packageRoot);
|
|
9308
|
+
if (existing) {
|
|
9309
|
+
for (const reason of reasons) existing.reasons.add(reason);
|
|
9310
|
+
return;
|
|
9311
|
+
}
|
|
9312
|
+
const identity = readPackageIdentity(projectRoot, packageRoot);
|
|
9313
|
+
islands.set(packageRoot, {
|
|
9314
|
+
packageRoot,
|
|
9315
|
+
...identity,
|
|
9316
|
+
reasons: new Set(reasons)
|
|
9317
|
+
});
|
|
9318
|
+
}
|
|
9319
|
+
function isBareSpecifier(specifier) {
|
|
9320
|
+
return !specifier.startsWith(".") && !specifier.startsWith("/") && !specifier.startsWith("file:");
|
|
9321
|
+
}
|
|
9322
|
+
function isBundleableResolvedFile(file) {
|
|
9323
|
+
const extension = path26.extname(file).toLowerCase();
|
|
9324
|
+
return extension === "" || extension === ".js" || extension === ".cjs" || extension === ".cts" || extension === ".mjs" || extension === ".mts" || extension === ".json" || extension === ".jsx" || extension === ".ts" || extension === ".tsx";
|
|
9325
|
+
}
|
|
9326
|
+
function retryIslandDiscovery(packagePath, reasons) {
|
|
9327
|
+
return {
|
|
9328
|
+
errors: [
|
|
9329
|
+
{
|
|
9330
|
+
text: `retry with dynamic island ${packagePath}: ${reasons.join(", ")}`
|
|
9331
|
+
}
|
|
9332
|
+
]
|
|
9333
|
+
};
|
|
9334
|
+
}
|
|
9335
|
+
function hybridDynamicIslandPlugin(projectRoot, islands) {
|
|
9336
|
+
const packageOwner = createPackageOwnerResolver(projectRoot);
|
|
9337
|
+
const discover = (owner, reasons) => {
|
|
9338
|
+
addDynamicIsland(projectRoot, islands, owner, reasons);
|
|
9339
|
+
return retryIslandDiscovery(toPosixRelative(projectRoot, owner), reasons);
|
|
9340
|
+
};
|
|
9341
|
+
return {
|
|
9342
|
+
name: "miaoda-hybrid-dynamic-islands",
|
|
9343
|
+
setup(esbuild) {
|
|
9344
|
+
esbuild.onResolve({ filter: /.*/ }, async (args) => {
|
|
9345
|
+
const pluginData = args.pluginData;
|
|
9346
|
+
if (pluginData?.miaodaHybridSkip) return void 0;
|
|
9347
|
+
if (BUILTINS.has(args.path)) return { path: args.path, external: true };
|
|
9348
|
+
if (args.kind === "entry-point") return void 0;
|
|
9349
|
+
const importerOwner = args.importer ? packageOwner(args.importer) : void 0;
|
|
9350
|
+
const resolved = await esbuild.resolve(args.path, {
|
|
9351
|
+
importer: args.importer,
|
|
9352
|
+
kind: args.kind,
|
|
9353
|
+
namespace: args.namespace,
|
|
9354
|
+
resolveDir: args.resolveDir,
|
|
9355
|
+
pluginData: { miaodaHybridSkip: true }
|
|
9356
|
+
});
|
|
9357
|
+
if (resolved.errors.length > 0) {
|
|
9358
|
+
return importerOwner ? discover(importerOwner, [`resolves ${args.path} at runtime`]) : { errors: resolved.errors };
|
|
9359
|
+
}
|
|
9360
|
+
if (resolved.external) return resolved;
|
|
9361
|
+
const targetOwner = packageOwner(resolved.path);
|
|
9362
|
+
if (targetOwner && islands.has(targetOwner)) {
|
|
9363
|
+
if (isBareSpecifier(args.path)) {
|
|
9364
|
+
const targetIdentity = readPackageIdentity(
|
|
9365
|
+
projectRoot,
|
|
9366
|
+
targetOwner
|
|
9367
|
+
);
|
|
9368
|
+
if (args.kind === "import-statement" || args.kind === "dynamic-import") {
|
|
9369
|
+
if (importerOwner && importerOwner !== targetOwner) {
|
|
9370
|
+
return discover(importerOwner, [
|
|
9371
|
+
`preserves ESM import conditions for ${targetIdentity.packagePath}`
|
|
9372
|
+
]);
|
|
9373
|
+
}
|
|
9374
|
+
return {
|
|
9375
|
+
errors: [
|
|
9376
|
+
{
|
|
9377
|
+
text: `bundle entry cannot preserve ESM import conditions for dynamic island: ${targetIdentity.packagePath}`
|
|
9378
|
+
}
|
|
9379
|
+
]
|
|
9380
|
+
};
|
|
9381
|
+
}
|
|
9382
|
+
const rootPackagePath = `node_modules/${targetIdentity.packageName}`;
|
|
9383
|
+
if (targetIdentity.packagePath === rootPackagePath) {
|
|
9384
|
+
return { path: args.path, external: true };
|
|
9385
|
+
}
|
|
9386
|
+
if (importerOwner && importerOwner !== targetOwner) {
|
|
9387
|
+
return discover(importerOwner, [
|
|
9388
|
+
`resolves nested dynamic island ${targetIdentity.packagePath}`
|
|
9389
|
+
]);
|
|
9390
|
+
}
|
|
9391
|
+
return {
|
|
9392
|
+
errors: [
|
|
9393
|
+
{
|
|
9394
|
+
text: `bundle entry cannot preserve nested package resolution: ${targetIdentity.packagePath}`
|
|
9395
|
+
}
|
|
9396
|
+
]
|
|
9397
|
+
};
|
|
9398
|
+
}
|
|
9399
|
+
if (importerOwner && importerOwner !== targetOwner) {
|
|
9400
|
+
return discover(importerOwner, [
|
|
9401
|
+
`depends on dynamic island ${toPosixRelative(
|
|
9402
|
+
projectRoot,
|
|
9403
|
+
targetOwner
|
|
9404
|
+
)}`
|
|
9405
|
+
]);
|
|
9406
|
+
}
|
|
9407
|
+
if (!isBareSpecifier(args.path)) {
|
|
9408
|
+
return {
|
|
9409
|
+
errors: [
|
|
9410
|
+
{
|
|
9411
|
+
text: `application relative import crosses into dynamic island: ${args.path}`
|
|
9412
|
+
}
|
|
9413
|
+
]
|
|
9414
|
+
};
|
|
9415
|
+
}
|
|
9416
|
+
return { path: args.path, external: true };
|
|
9417
|
+
}
|
|
9418
|
+
if (targetOwner && resolved.path && !isBundleableResolvedFile(resolved.path)) {
|
|
9419
|
+
return discover(targetOwner, [
|
|
9420
|
+
`loads runtime asset ${path26.extname(resolved.path) || "<no extension>"}`
|
|
9421
|
+
]);
|
|
9422
|
+
}
|
|
9423
|
+
return resolved;
|
|
9424
|
+
});
|
|
9425
|
+
esbuild.onLoad(
|
|
9426
|
+
{ filter: /\.(?:cjs|cts|js|jsx|mjs|mts|ts|tsx)$/ },
|
|
9427
|
+
(args) => {
|
|
9428
|
+
const source = fs30.readFileSync(args.path, "utf8");
|
|
9429
|
+
const reasons = packageLayoutCapabilityReasons(source);
|
|
9430
|
+
if (reasons.length === 0) return void 0;
|
|
9431
|
+
const owner = packageOwner(args.path);
|
|
9432
|
+
if (owner) {
|
|
9433
|
+
if (/\.(?:cts|mts|ts|tsx)$/.test(args.path)) {
|
|
9434
|
+
const identity = readPackageIdentity(projectRoot, owner);
|
|
9435
|
+
return {
|
|
9436
|
+
errors: [
|
|
9437
|
+
{
|
|
9438
|
+
text: `TypeScript dependency requires package layout but is not directly executable: ${identity.packagePath}/${toPosixRelative(owner, args.path)}: ${reasons.join(", ")}`
|
|
9439
|
+
}
|
|
9440
|
+
]
|
|
9441
|
+
};
|
|
9442
|
+
}
|
|
9443
|
+
return discover(owner, reasons);
|
|
9444
|
+
}
|
|
9445
|
+
const applicationReasons = reasons.filter(
|
|
9446
|
+
(reason) => reason !== "uses filesystem APIs" && reason !== "uses filesystem path outside the proven package boundary"
|
|
9447
|
+
);
|
|
9448
|
+
if (applicationReasons.length === 0) return void 0;
|
|
9449
|
+
return {
|
|
9450
|
+
errors: [
|
|
9451
|
+
{
|
|
9452
|
+
text: `application startup code cannot preserve dynamic layout: ${toPosixRelative(
|
|
9453
|
+
projectRoot,
|
|
9454
|
+
args.path
|
|
9455
|
+
)}: ${applicationReasons.join(", ")}`
|
|
9456
|
+
}
|
|
9457
|
+
]
|
|
9458
|
+
};
|
|
9459
|
+
}
|
|
9460
|
+
);
|
|
9461
|
+
}
|
|
9462
|
+
};
|
|
9463
|
+
}
|
|
9464
|
+
async function buildHybridBundle(projectRoot, entryFile, outputFile) {
|
|
9465
|
+
const islands = /* @__PURE__ */ new Map();
|
|
9466
|
+
for (let pass = 0; pass < MAX_ISLAND_DISCOVERY_PASSES; pass += 1) {
|
|
9467
|
+
const before = islands.size;
|
|
9468
|
+
fs30.rmSync(outputFile, { force: true });
|
|
9469
|
+
try {
|
|
9470
|
+
await build({
|
|
9471
|
+
absWorkingDir: projectRoot,
|
|
9472
|
+
entryPoints: [entryFile],
|
|
9473
|
+
outfile: outputFile,
|
|
9474
|
+
bundle: true,
|
|
9475
|
+
platform: "node",
|
|
9476
|
+
format: "cjs",
|
|
9477
|
+
target: `node${process.versions.node.split(".")[0]}`,
|
|
9478
|
+
keepNames: true,
|
|
9479
|
+
legalComments: "none",
|
|
9480
|
+
logLevel: "silent",
|
|
9481
|
+
preserveSymlinks: true,
|
|
9482
|
+
sourcemap: false,
|
|
9483
|
+
plugins: [hybridDynamicIslandPlugin(projectRoot, islands)]
|
|
9484
|
+
});
|
|
9485
|
+
return islands;
|
|
9486
|
+
} catch (error) {
|
|
9487
|
+
if (islands.size > before) continue;
|
|
9488
|
+
throw error;
|
|
9489
|
+
}
|
|
9490
|
+
}
|
|
9491
|
+
throw new Error(
|
|
9492
|
+
`dynamic island discovery exceeded ${MAX_ISLAND_DISCOVERY_PASSES} passes`
|
|
9493
|
+
);
|
|
9494
|
+
}
|
|
9495
|
+
function readDependencyState(projectRoot) {
|
|
9496
|
+
const packageFile = path26.join(projectRoot, "package.json");
|
|
9497
|
+
const lockFile = path26.join(projectRoot, "package-lock.json");
|
|
9498
|
+
const installedLockFile = path26.join(
|
|
9499
|
+
projectRoot,
|
|
9500
|
+
"node_modules",
|
|
9501
|
+
".package-lock.json"
|
|
9502
|
+
);
|
|
9503
|
+
for (const [file, description] of [
|
|
9504
|
+
[packageFile, "package.json"],
|
|
9505
|
+
[lockFile, "package-lock.json"],
|
|
9506
|
+
[installedLockFile, "installed node_modules lockfile"]
|
|
9507
|
+
]) {
|
|
9508
|
+
if (!fs30.existsSync(file) || fs30.lstatSync(file).isSymbolicLink()) {
|
|
9509
|
+
throw new Error(`${description} is unavailable`);
|
|
9510
|
+
}
|
|
9511
|
+
}
|
|
9512
|
+
const packageContents = readBoundedFile(
|
|
9513
|
+
packageFile,
|
|
9514
|
+
MAX_PACKAGE_MANIFEST_BYTES,
|
|
9515
|
+
"package.json"
|
|
9516
|
+
);
|
|
9517
|
+
const lockContents = readBoundedFile(
|
|
9518
|
+
lockFile,
|
|
9519
|
+
MAX_DEPENDENCY_METADATA_BYTES,
|
|
9520
|
+
"package-lock.json"
|
|
9521
|
+
);
|
|
9522
|
+
const installedLockContents = readBoundedFile(
|
|
9523
|
+
installedLockFile,
|
|
9524
|
+
MAX_DEPENDENCY_METADATA_BYTES,
|
|
9525
|
+
"installed node_modules lockfile"
|
|
9526
|
+
);
|
|
9527
|
+
const packageJson = readJsonObject(packageFile, "package.json");
|
|
9528
|
+
const rootLock = JSON.parse(lockContents.toString("utf8"));
|
|
9529
|
+
const installedLock = JSON.parse(
|
|
9530
|
+
installedLockContents.toString("utf8")
|
|
9531
|
+
);
|
|
9532
|
+
const rootPackages = lockPackages(rootLock, "package-lock.json");
|
|
9533
|
+
const installedPackages = lockPackages(
|
|
9534
|
+
installedLock,
|
|
9535
|
+
"installed node_modules lockfile"
|
|
9536
|
+
);
|
|
9537
|
+
const rootRecord = rootPackages[""];
|
|
9538
|
+
if (!rootRecord) throw new Error("package-lock.json root package is missing");
|
|
9539
|
+
for (const field of ["dependencies", "optionalDependencies"]) {
|
|
9540
|
+
const declared = packageJson[field] ?? {};
|
|
9541
|
+
const locked = rootRecord[field] ?? {};
|
|
9542
|
+
if (stableJson(declared) !== stableJson(locked)) {
|
|
9543
|
+
throw new Error(`package.json and package-lock.json ${field} disagree`);
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
let declaredDynamicDependencies;
|
|
9547
|
+
const startupConfig = packageJson.miaodaStartupBundle;
|
|
9548
|
+
if (startupConfig !== void 0) {
|
|
9549
|
+
if (!startupConfig || typeof startupConfig !== "object" || Array.isArray(startupConfig)) {
|
|
9550
|
+
throw new Error("package.json miaodaStartupBundle must be an object");
|
|
9551
|
+
}
|
|
9552
|
+
const configured = startupConfig.dynamicDependencies;
|
|
9553
|
+
if (!Array.isArray(configured) || configured.some((item) => typeof item !== "string")) {
|
|
9554
|
+
throw new Error(
|
|
9555
|
+
"package.json miaodaStartupBundle.dynamicDependencies must be a string array"
|
|
9556
|
+
);
|
|
9557
|
+
}
|
|
9558
|
+
declaredDynamicDependencies = [...new Set(configured)].sort();
|
|
9559
|
+
if (declaredDynamicDependencies.length !== configured.length) {
|
|
9560
|
+
throw new Error(
|
|
9561
|
+
"package.json miaodaStartupBundle.dynamicDependencies contains duplicates"
|
|
9562
|
+
);
|
|
9563
|
+
}
|
|
9564
|
+
const rootRuntimeDependencies = /* @__PURE__ */ new Set([
|
|
9565
|
+
...Object.keys(stringRecord(packageJson.dependencies)),
|
|
9566
|
+
...Object.keys(stringRecord(packageJson.optionalDependencies))
|
|
9567
|
+
]);
|
|
9568
|
+
for (const dependencyName of declaredDynamicDependencies) {
|
|
9569
|
+
packageNameSegments(dependencyName);
|
|
9570
|
+
if (!rootRuntimeDependencies.has(dependencyName)) {
|
|
9571
|
+
throw new Error(
|
|
9572
|
+
`declared startup dynamic dependency is not a root runtime dependency: ${dependencyName}`
|
|
9573
|
+
);
|
|
9574
|
+
}
|
|
9575
|
+
}
|
|
9576
|
+
}
|
|
9577
|
+
return {
|
|
9578
|
+
packageJson,
|
|
9579
|
+
packageContents,
|
|
9580
|
+
lockContents,
|
|
9581
|
+
rootPackages,
|
|
9582
|
+
installedPackages,
|
|
9583
|
+
declaredDynamicDependencies
|
|
9584
|
+
};
|
|
9585
|
+
}
|
|
9586
|
+
function manifestContainsRuntimeTypeScript(value) {
|
|
9587
|
+
if (typeof value === "string") {
|
|
9588
|
+
return /\.(?:cts|mts|ts|tsx)(?:$|[?#])/.test(value);
|
|
9589
|
+
}
|
|
9590
|
+
if (Array.isArray(value))
|
|
9591
|
+
return value.some(manifestContainsRuntimeTypeScript);
|
|
9592
|
+
if (value && typeof value === "object") {
|
|
9593
|
+
return Object.entries(value).some(
|
|
9594
|
+
([condition, target]) => condition !== "types" && !condition.startsWith("types@") && manifestContainsRuntimeTypeScript(target)
|
|
9595
|
+
);
|
|
9596
|
+
}
|
|
9597
|
+
return false;
|
|
9598
|
+
}
|
|
9599
|
+
function isPackageDevelopmentFile(relativeFile) {
|
|
9600
|
+
const segments = relativeFile.split("/");
|
|
9601
|
+
return segments.some(
|
|
9602
|
+
(segment) => /^(?:__tests__|bench(?:mark)?s?|fixtures?|tests?)$/i.test(segment)
|
|
9603
|
+
) || /(?:^|\.)\b(?:spec|test)\.[cm]?[jt]sx?$/.test(path26.basename(relativeFile));
|
|
9604
|
+
}
|
|
9605
|
+
function createMaterializedPackageAuditor(packageRoot, identity) {
|
|
9606
|
+
if (manifestContainsRuntimeTypeScript(identity.manifest.main) || manifestContainsRuntimeTypeScript(identity.manifest.exports) || manifestContainsRuntimeTypeScript(identity.manifest.bin)) {
|
|
9607
|
+
throw new Error(
|
|
9608
|
+
`TypeScript dependency is not directly executable in the startup runtime: ${identity.packagePath}`
|
|
9609
|
+
);
|
|
9610
|
+
}
|
|
9611
|
+
let runtimeTypeScriptFile;
|
|
9612
|
+
let packageRelativeDynamicLoader;
|
|
9613
|
+
return {
|
|
9614
|
+
inspect(candidate) {
|
|
9615
|
+
const name = path26.basename(candidate);
|
|
9616
|
+
const relativeFile = toPosixRelative(packageRoot, candidate);
|
|
9617
|
+
if (isPackageDevelopmentFile(relativeFile)) return;
|
|
9618
|
+
if (/\.(?:cts|mts|ts|tsx)$/.test(name) && !/\.d\.(?:cts|mts|ts)$/.test(name)) {
|
|
9619
|
+
runtimeTypeScriptFile ?? (runtimeTypeScriptFile = relativeFile);
|
|
9620
|
+
return;
|
|
9621
|
+
}
|
|
9622
|
+
if (!/\.(?:cjs|js|jsx|mjs)$/.test(name)) return;
|
|
9623
|
+
const reasons = packageLayoutCapabilityReasons(
|
|
9624
|
+
fs30.readFileSync(candidate, "utf8")
|
|
9625
|
+
);
|
|
9626
|
+
for (const reason of reasons) {
|
|
9627
|
+
if (reason === "loads runtime TypeScript") {
|
|
9628
|
+
throw new Error(
|
|
9629
|
+
`TypeScript dependency is not directly executable in the startup runtime: ${identity.packagePath}/${toPosixRelative(packageRoot, candidate)}`
|
|
9630
|
+
);
|
|
9631
|
+
}
|
|
9632
|
+
if (reason.startsWith("uses package-relative dynamic")) {
|
|
9633
|
+
packageRelativeDynamicLoader ?? (packageRelativeDynamicLoader = relativeFile);
|
|
9634
|
+
}
|
|
9635
|
+
if (reason === "uses filesystem path outside the proven package boundary") {
|
|
9636
|
+
throw new Error(
|
|
9637
|
+
`filesystem access escapes the startup island boundary: ${identity.packagePath}/${toPosixRelative(packageRoot, candidate)}`
|
|
9638
|
+
);
|
|
9639
|
+
}
|
|
9640
|
+
if (reason === "uses unbounded dynamic import" || reason === "uses eval-generated code" || reason === "uses VM code evaluation" || reason === "uses native dynamic loader") {
|
|
9641
|
+
throw new Error(
|
|
9642
|
+
`startup island uses an unsupported executable loader: ${identity.packagePath}/${toPosixRelative(packageRoot, candidate)}: ${reason}`
|
|
9643
|
+
);
|
|
9644
|
+
}
|
|
9645
|
+
}
|
|
9646
|
+
},
|
|
9647
|
+
assertComplete() {
|
|
9648
|
+
if (runtimeTypeScriptFile && packageRelativeDynamicLoader) {
|
|
9649
|
+
throw new Error(
|
|
9650
|
+
`package-relative dynamic loader may select non-executable TypeScript: ${identity.packagePath}/${packageRelativeDynamicLoader} -> ${runtimeTypeScriptFile}`
|
|
9651
|
+
);
|
|
9652
|
+
}
|
|
9653
|
+
}
|
|
9654
|
+
};
|
|
9655
|
+
}
|
|
9656
|
+
function packageNameSegments(name) {
|
|
9657
|
+
const segments = name.split("/");
|
|
9658
|
+
if (name.startsWith("@") && segments.length !== 2 || !name.startsWith("@") && segments.length !== 1 || segments.some((segment) => !segment || segment === "." || segment === "..")) {
|
|
9659
|
+
throw new Error(`unsupported dependency package name: ${name}`);
|
|
9660
|
+
}
|
|
9661
|
+
return segments;
|
|
9662
|
+
}
|
|
9663
|
+
function resolveInstalledDependencyRoot(projectRoot, packageRoot, dependencyName) {
|
|
9664
|
+
const segments = packageNameSegments(dependencyName);
|
|
9665
|
+
let current = packageRoot;
|
|
9666
|
+
while (isInside(projectRoot, current)) {
|
|
9667
|
+
const candidate = path26.join(current, "node_modules", ...segments);
|
|
9668
|
+
if (fs30.existsSync(candidate)) {
|
|
9669
|
+
if (fs30.lstatSync(candidate).isSymbolicLink()) {
|
|
9670
|
+
throw new Error(
|
|
9671
|
+
`linked runtime dependency is unsupported: ${toPosixRelative(
|
|
9672
|
+
projectRoot,
|
|
9673
|
+
candidate
|
|
9674
|
+
)}`
|
|
9675
|
+
);
|
|
9676
|
+
}
|
|
9677
|
+
if (fs30.statSync(candidate).isDirectory() && fs30.existsSync(path26.join(candidate, "package.json"))) {
|
|
9678
|
+
return candidate;
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
const parent = path26.dirname(current);
|
|
9682
|
+
if (parent === current) break;
|
|
9683
|
+
current = parent;
|
|
9684
|
+
}
|
|
9685
|
+
return void 0;
|
|
9686
|
+
}
|
|
9687
|
+
function stringRecord(value) {
|
|
9688
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
9689
|
+
return Object.fromEntries(
|
|
9690
|
+
Object.entries(value).filter(
|
|
9691
|
+
(entry) => typeof entry[1] === "string"
|
|
9692
|
+
)
|
|
9693
|
+
);
|
|
9694
|
+
}
|
|
9695
|
+
function validateInstalledPackage(projectRoot, packageRoot, state) {
|
|
9696
|
+
const { packagePath, packageName } = readPackageIdentity(
|
|
9697
|
+
projectRoot,
|
|
9698
|
+
packageRoot
|
|
9699
|
+
);
|
|
9700
|
+
const locked = state.rootPackages[packagePath];
|
|
9701
|
+
const installed = state.installedPackages[packagePath];
|
|
9702
|
+
if (!locked)
|
|
9703
|
+
throw new Error(`package is absent from lockfile: ${packagePath}`);
|
|
9704
|
+
if (locked.link === true) {
|
|
9705
|
+
throw new Error(`linked runtime package is unsupported: ${packagePath}`);
|
|
9706
|
+
}
|
|
9707
|
+
if (!installed || stableJson(comparableLockRecord(installed)) !== stableJson(comparableLockRecord(locked))) {
|
|
9708
|
+
throw new Error(
|
|
9709
|
+
`installed package does not match lockfile: ${packagePath}`
|
|
9710
|
+
);
|
|
9711
|
+
}
|
|
9712
|
+
const manifest = readJsonObject(
|
|
9713
|
+
path26.join(packageRoot, "package.json"),
|
|
9714
|
+
`installed package ${packagePath}`
|
|
9715
|
+
);
|
|
9716
|
+
if (manifest.name !== packageName) {
|
|
9717
|
+
throw new Error(
|
|
9718
|
+
`installed package name does not match path: ${packagePath}`
|
|
9719
|
+
);
|
|
9720
|
+
}
|
|
9721
|
+
const version = manifest.version;
|
|
9722
|
+
if (typeof version !== "string" || !version) {
|
|
9723
|
+
throw new Error(`installed package version is invalid: ${packagePath}`);
|
|
9724
|
+
}
|
|
9725
|
+
if (typeof locked.version === "string" && locked.version !== version) {
|
|
9726
|
+
throw new Error(
|
|
9727
|
+
`installed package version does not match lockfile: ${packagePath}`
|
|
9728
|
+
);
|
|
9729
|
+
}
|
|
9730
|
+
return { packagePath, packageName, version, manifest };
|
|
9731
|
+
}
|
|
9732
|
+
function materializeDynamicIslands(projectRoot, payloadRoot, islands) {
|
|
9733
|
+
const state = readDependencyState(projectRoot);
|
|
9734
|
+
if (islands.size > 0 && state.declaredDynamicDependencies === void 0) {
|
|
9735
|
+
throw new Error(
|
|
9736
|
+
"startup bundle with dynamic islands requires package.json miaodaStartupBundle.dynamicDependencies (use [] when no non-relative dynamic package is allowed)"
|
|
9737
|
+
);
|
|
9738
|
+
}
|
|
9739
|
+
const queue = [...islands.values()].sort((left, right) => left.packagePath.localeCompare(right.packagePath)).map((island) => island.packageRoot);
|
|
9740
|
+
const dynamicDependencies = [];
|
|
9741
|
+
for (const dependencyName of state.declaredDynamicDependencies ?? []) {
|
|
9742
|
+
const dependencyRoot = resolveInstalledDependencyRoot(
|
|
9743
|
+
projectRoot,
|
|
9744
|
+
projectRoot,
|
|
9745
|
+
dependencyName
|
|
9746
|
+
);
|
|
9747
|
+
if (!dependencyRoot) {
|
|
9748
|
+
throw new Error(
|
|
9749
|
+
`declared startup dynamic dependency is not installed: ${dependencyName}`
|
|
9750
|
+
);
|
|
9751
|
+
}
|
|
9752
|
+
dynamicDependencies.push({
|
|
9753
|
+
specifier: dependencyName,
|
|
9754
|
+
resolvedPackagePath: readPackageIdentity(projectRoot, dependencyRoot).packagePath
|
|
9755
|
+
});
|
|
9756
|
+
queue.push(dependencyRoot);
|
|
9757
|
+
}
|
|
9758
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9759
|
+
const materializedPackages = [];
|
|
9760
|
+
const selectedLockRecords = [];
|
|
9761
|
+
fs30.mkdirSync(payloadRoot, { recursive: true, mode: 493 });
|
|
9762
|
+
fs30.writeFileSync(
|
|
9763
|
+
path26.join(payloadRoot, "package.json"),
|
|
9764
|
+
`${JSON.stringify({ private: true, type: "commonjs" })}
|
|
9765
|
+
`,
|
|
9766
|
+
{ mode: 420 }
|
|
9767
|
+
);
|
|
9768
|
+
const budget = measurePayload(payloadRoot);
|
|
9769
|
+
while (queue.length > 0) {
|
|
9770
|
+
const packageRoot = queue.shift();
|
|
9771
|
+
if (visited.has(packageRoot)) continue;
|
|
9772
|
+
visited.add(packageRoot);
|
|
9773
|
+
const identity = validateInstalledPackage(projectRoot, packageRoot, state);
|
|
9774
|
+
const auditor = createMaterializedPackageAuditor(packageRoot, identity);
|
|
9775
|
+
copyDirectoryContents(
|
|
9776
|
+
packageRoot,
|
|
9777
|
+
path26.join(payloadRoot, ...identity.packagePath.split("/")),
|
|
9778
|
+
true,
|
|
9779
|
+
budget,
|
|
9780
|
+
auditor.inspect
|
|
9781
|
+
);
|
|
9782
|
+
auditor.assertComplete();
|
|
9783
|
+
const island = islands.has(packageRoot);
|
|
9784
|
+
materializedPackages.push({
|
|
9785
|
+
packagePath: identity.packagePath,
|
|
9786
|
+
packageName: identity.packageName,
|
|
9787
|
+
version: identity.version,
|
|
9788
|
+
island
|
|
9789
|
+
});
|
|
9790
|
+
selectedLockRecords.push({
|
|
9791
|
+
path: identity.packagePath,
|
|
9792
|
+
record: comparableLockRecord(state.rootPackages[identity.packagePath])
|
|
9793
|
+
});
|
|
9794
|
+
const dependencies = stringRecord(identity.manifest.dependencies);
|
|
9795
|
+
const optionalDependencies = stringRecord(
|
|
9796
|
+
identity.manifest.optionalDependencies
|
|
9797
|
+
);
|
|
9798
|
+
const peerDependencies = stringRecord(identity.manifest.peerDependencies);
|
|
9799
|
+
const peerMetadata = identity.manifest.peerDependenciesMeta && typeof identity.manifest.peerDependenciesMeta === "object" && !Array.isArray(identity.manifest.peerDependenciesMeta) ? identity.manifest.peerDependenciesMeta : {};
|
|
9800
|
+
for (const dependencyName of [
|
|
9801
|
+
.../* @__PURE__ */ new Set([
|
|
9802
|
+
...Object.keys(dependencies),
|
|
9803
|
+
...Object.keys(optionalDependencies),
|
|
9804
|
+
...Object.keys(peerDependencies)
|
|
9805
|
+
])
|
|
9806
|
+
].sort()) {
|
|
9807
|
+
const dependencyRoot = resolveInstalledDependencyRoot(
|
|
9808
|
+
projectRoot,
|
|
9809
|
+
packageRoot,
|
|
9810
|
+
dependencyName
|
|
9811
|
+
);
|
|
9812
|
+
const peerConfig = peerMetadata[dependencyName];
|
|
9813
|
+
const optionalPeer = Boolean(
|
|
9814
|
+
peerConfig && typeof peerConfig === "object" && peerConfig.optional === true
|
|
9815
|
+
);
|
|
9816
|
+
const optional = Object.hasOwn(optionalDependencies, dependencyName) || optionalPeer;
|
|
9817
|
+
if (!dependencyRoot) {
|
|
9818
|
+
if (optional) continue;
|
|
9819
|
+
throw new Error(
|
|
9820
|
+
`runtime dependency is not installed: ${identity.packagePath} -> ${dependencyName}`
|
|
9821
|
+
);
|
|
9822
|
+
}
|
|
9823
|
+
queue.push(dependencyRoot);
|
|
9824
|
+
}
|
|
9825
|
+
}
|
|
9826
|
+
materializedPackages.sort(
|
|
9827
|
+
(left, right) => left.packagePath.localeCompare(right.packagePath)
|
|
9828
|
+
);
|
|
9829
|
+
selectedLockRecords.sort(
|
|
9830
|
+
(left, right) => left.path.localeCompare(right.path)
|
|
9831
|
+
);
|
|
9832
|
+
return {
|
|
9833
|
+
lockfileSha256: sha256(state.lockContents),
|
|
9834
|
+
dependencyViewSha256: sha256(
|
|
9835
|
+
stableJson({
|
|
9836
|
+
packageJsonSha256: sha256(state.packageContents),
|
|
9837
|
+
lockfileSha256: sha256(state.lockContents),
|
|
9838
|
+
runtime: runtimeIdentity(),
|
|
9839
|
+
selectedLockRecords
|
|
9840
|
+
})
|
|
9841
|
+
),
|
|
9842
|
+
materializedPackages,
|
|
9843
|
+
dynamicDependencies
|
|
9844
|
+
};
|
|
9845
|
+
}
|
|
9846
|
+
function indexPayload(payloadRoot) {
|
|
9847
|
+
const files = [];
|
|
9848
|
+
const budget = { fileCount: 0, totalBytes: 0 };
|
|
9849
|
+
const visit = (directory) => {
|
|
9850
|
+
for (const entry of fs30.readdirSync(directory, { withFileTypes: true })) {
|
|
9851
|
+
const candidate = path26.join(directory, entry.name);
|
|
9852
|
+
if (entry.isSymbolicLink()) {
|
|
9853
|
+
throw new Error(`generation symlink is unsupported: ${candidate}`);
|
|
9854
|
+
}
|
|
9855
|
+
if (entry.isDirectory()) {
|
|
9856
|
+
visit(candidate);
|
|
9857
|
+
continue;
|
|
9858
|
+
}
|
|
9859
|
+
if (!entry.isFile()) {
|
|
9860
|
+
throw new Error(`generation special file is unsupported: ${candidate}`);
|
|
9861
|
+
}
|
|
9862
|
+
const stats = fs30.lstatSync(candidate);
|
|
9863
|
+
if (!stats.isFile()) {
|
|
9864
|
+
throw new Error(`generation special file is unsupported: ${candidate}`);
|
|
9865
|
+
}
|
|
9866
|
+
reserveGenerationBudget(candidate, stats.size, budget);
|
|
9867
|
+
const contents = fs30.readFileSync(candidate);
|
|
9868
|
+
if (contents.length !== stats.size) {
|
|
9869
|
+
throw new Error(
|
|
9870
|
+
`generation file changed while indexing: ${toPosixRelative(payloadRoot, candidate)}`
|
|
9871
|
+
);
|
|
9872
|
+
}
|
|
9873
|
+
files.push({
|
|
9874
|
+
path: `payload/${toPosixRelative(payloadRoot, candidate)}`,
|
|
9875
|
+
sha256: sha256(contents),
|
|
9876
|
+
size: contents.length,
|
|
9877
|
+
mode: stats.mode & 511
|
|
9878
|
+
});
|
|
9879
|
+
}
|
|
9880
|
+
};
|
|
9881
|
+
visit(payloadRoot);
|
|
9882
|
+
files.sort((left, right) => left.path.localeCompare(right.path));
|
|
9883
|
+
return {
|
|
9884
|
+
files,
|
|
9885
|
+
treeSha256: sha256(stableJson(files)),
|
|
9886
|
+
totalBytes: budget.totalBytes
|
|
9887
|
+
};
|
|
9888
|
+
}
|
|
9889
|
+
function reservePort() {
|
|
9890
|
+
return new Promise((resolve2, reject) => {
|
|
9891
|
+
const server = net.createServer();
|
|
9892
|
+
server.once("error", reject);
|
|
9893
|
+
server.listen(0, "127.0.0.1", () => {
|
|
9894
|
+
const address = server.address();
|
|
9895
|
+
if (!address || typeof address === "string") {
|
|
9896
|
+
server.close(
|
|
9897
|
+
() => reject(new Error("isolated probe failed to reserve a port"))
|
|
9898
|
+
);
|
|
9899
|
+
return;
|
|
9900
|
+
}
|
|
9901
|
+
server.close((error) => error ? reject(error) : resolve2(address.port));
|
|
9902
|
+
});
|
|
9903
|
+
});
|
|
9904
|
+
}
|
|
9905
|
+
async function isolatedProbe(projectRoot, payloadRoot, entry, timeoutMs) {
|
|
9906
|
+
const probeRoot = fs30.mkdtempSync(
|
|
9907
|
+
path26.join(os3.tmpdir(), "miaoda-server-generation-probe-")
|
|
9908
|
+
);
|
|
9909
|
+
const probeWorkspaceRoot = path26.join(probeRoot, "workspace");
|
|
9910
|
+
const probeProjectRoot = path26.join(probeWorkspaceRoot, "code");
|
|
9911
|
+
copyProbeWorkspace(projectRoot, probeProjectRoot);
|
|
9912
|
+
const probeGenerationRoot = path26.join(
|
|
9913
|
+
probeProjectRoot,
|
|
9914
|
+
".miaoda-cache",
|
|
9915
|
+
"server",
|
|
9916
|
+
"generations",
|
|
9917
|
+
"probe"
|
|
9918
|
+
);
|
|
9919
|
+
const probePayload = path26.join(probeGenerationRoot, "payload");
|
|
9920
|
+
copyDirectoryContents(payloadRoot, probePayload, false);
|
|
9921
|
+
const probeEntry = path26.join(probeGenerationRoot, ...entry.split("/"));
|
|
9922
|
+
const probeModuleGuard = path26.join(
|
|
9923
|
+
probePayload,
|
|
9924
|
+
"module-resolution-guard.cjs"
|
|
9925
|
+
);
|
|
9926
|
+
const port = await reservePort();
|
|
9927
|
+
const configuredBasePath = process.env.CLIENT_BASE_PATH;
|
|
9928
|
+
const basePath = configuredBasePath?.startsWith("/") && !configuredBasePath.startsWith("//") ? configuredBasePath.replace(/\/+$/, "") : "";
|
|
9929
|
+
const readinessPath = `${basePath}/__innerapi__/capability/list`;
|
|
9930
|
+
const startedAt = Date.now();
|
|
9931
|
+
const child = spawn2(process.execPath, [probeEntry], {
|
|
9932
|
+
cwd: probeProjectRoot,
|
|
9933
|
+
detached: true,
|
|
9934
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9935
|
+
env: {
|
|
9936
|
+
...process.env,
|
|
9937
|
+
NODE_PATH: path26.join(probePayload, "node_modules"),
|
|
9938
|
+
NODE_OPTIONS: `--require=${JSON.stringify(probeModuleGuard)}`,
|
|
9939
|
+
PWD: probeProjectRoot,
|
|
9940
|
+
WORKSPACE_DIR: probeWorkspaceRoot,
|
|
9941
|
+
MIAODA_WORKSPACE_ROOT: probeProjectRoot,
|
|
9942
|
+
NODE_ENV: "development",
|
|
9943
|
+
SERVER_HOST: "127.0.0.1",
|
|
9944
|
+
SERVER_PORT: String(port),
|
|
9945
|
+
MIAODA_SERVER_CACHE_PROBE: "true",
|
|
9946
|
+
DEPRECATED_SKIP_INIT_DB_CONNECTION: process.env.DEPRECATED_SKIP_INIT_DB_CONNECTION ?? "true",
|
|
9947
|
+
FORCE_AUTHN_INNERAPI_DOMAIN: process.env.FORCE_AUTHN_INNERAPI_DOMAIN ?? "http://127.0.0.1",
|
|
9948
|
+
FORCE_AUTHN_ACCESS_KEY: process.env.FORCE_AUTHN_ACCESS_KEY ?? "server-startup-probe",
|
|
9949
|
+
FORCE_AUTHN_ACCESS_SECRET: process.env.FORCE_AUTHN_ACCESS_SECRET ?? "server-startup-probe"
|
|
9950
|
+
}
|
|
9951
|
+
});
|
|
9952
|
+
registerProbe(child);
|
|
9953
|
+
const stdout = collectChildOutput(child, "stdout");
|
|
9954
|
+
const stderr = collectChildOutput(child, "stderr");
|
|
9955
|
+
try {
|
|
9956
|
+
await new Promise((resolve2, reject) => {
|
|
9957
|
+
const deadline = Date.now() + timeoutMs;
|
|
9958
|
+
let settled = false;
|
|
9959
|
+
let retryTimer;
|
|
9960
|
+
const detail = () => `${stderr()}
|
|
9961
|
+
${stdout()}`.trim();
|
|
9962
|
+
const finish = (error) => {
|
|
9963
|
+
if (settled) return;
|
|
9964
|
+
settled = true;
|
|
9965
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
9966
|
+
if (error) reject(error);
|
|
9967
|
+
else resolve2();
|
|
9968
|
+
};
|
|
9969
|
+
const retry = () => {
|
|
9970
|
+
const remaining = deadline - Date.now();
|
|
9971
|
+
if (remaining <= 0) {
|
|
9972
|
+
finish(
|
|
9973
|
+
new Error(
|
|
9974
|
+
`isolated generation readiness timeout${detail() ? `: ${detail()}` : ""}`
|
|
9975
|
+
)
|
|
9976
|
+
);
|
|
9977
|
+
return;
|
|
9978
|
+
}
|
|
9979
|
+
retryTimer = setTimeout(attempt, Math.min(25, remaining));
|
|
9980
|
+
};
|
|
9981
|
+
const attempt = () => {
|
|
9982
|
+
if (settled) return;
|
|
9983
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
9984
|
+
finish(
|
|
9985
|
+
new Error(
|
|
9986
|
+
`isolated generation exited before readiness${detail() ? `: ${detail()}` : ""}`
|
|
9987
|
+
)
|
|
9988
|
+
);
|
|
9989
|
+
return;
|
|
9990
|
+
}
|
|
9991
|
+
const remaining = deadline - Date.now();
|
|
9992
|
+
if (remaining <= 0) return retry();
|
|
9993
|
+
let requestSettled = false;
|
|
9994
|
+
const request = http.get(
|
|
9995
|
+
{ host: "127.0.0.1", port, path: readinessPath },
|
|
9996
|
+
(response) => {
|
|
9997
|
+
if (requestSettled || settled) {
|
|
9998
|
+
response.destroy();
|
|
9999
|
+
return;
|
|
10000
|
+
}
|
|
10001
|
+
if (response.statusCode !== 200) {
|
|
10002
|
+
requestSettled = true;
|
|
10003
|
+
response.resume();
|
|
10004
|
+
retry();
|
|
10005
|
+
return;
|
|
10006
|
+
}
|
|
10007
|
+
const chunks = [];
|
|
10008
|
+
let responseBytes = 0;
|
|
10009
|
+
response.on("data", (chunk) => {
|
|
10010
|
+
if (requestSettled || settled) return;
|
|
10011
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
10012
|
+
responseBytes += buffer.length;
|
|
10013
|
+
if (responseBytes > MAX_READINESS_RESPONSE_BYTES) {
|
|
10014
|
+
requestSettled = true;
|
|
10015
|
+
response.destroy();
|
|
10016
|
+
retry();
|
|
10017
|
+
return;
|
|
10018
|
+
}
|
|
10019
|
+
chunks.push(buffer);
|
|
10020
|
+
});
|
|
10021
|
+
response.once("end", () => {
|
|
10022
|
+
if (requestSettled || settled) return;
|
|
10023
|
+
requestSettled = true;
|
|
10024
|
+
if (isCapabilityReadinessPayload(Buffer.concat(chunks))) finish();
|
|
10025
|
+
else retry();
|
|
10026
|
+
});
|
|
10027
|
+
response.once("error", () => {
|
|
10028
|
+
if (requestSettled || settled) return;
|
|
10029
|
+
requestSettled = true;
|
|
10030
|
+
retry();
|
|
10031
|
+
});
|
|
10032
|
+
}
|
|
10033
|
+
);
|
|
10034
|
+
request.setTimeout(
|
|
10035
|
+
remaining,
|
|
10036
|
+
() => request.destroy(new Error("isolated generation readiness timeout"))
|
|
10037
|
+
);
|
|
10038
|
+
request.once("error", () => {
|
|
10039
|
+
if (requestSettled || settled) return;
|
|
10040
|
+
requestSettled = true;
|
|
10041
|
+
retry();
|
|
10042
|
+
});
|
|
10043
|
+
};
|
|
10044
|
+
attempt();
|
|
10045
|
+
});
|
|
10046
|
+
return Date.now() - startedAt;
|
|
10047
|
+
} finally {
|
|
10048
|
+
await stopProbe(child);
|
|
10049
|
+
fs30.rmSync(probeRoot, { recursive: true, force: true });
|
|
10050
|
+
}
|
|
10051
|
+
}
|
|
10052
|
+
function writeBoundedJson(file, value, maxBytes, description) {
|
|
10053
|
+
const contents = `${JSON.stringify(value, null, 2)}
|
|
10054
|
+
`;
|
|
10055
|
+
if (Buffer.byteLength(contents) > maxBytes) {
|
|
10056
|
+
throw new Error(`${description} exceeds ${maxBytes} bytes`);
|
|
10057
|
+
}
|
|
10058
|
+
fs30.writeFileSync(file, contents, {
|
|
10059
|
+
mode: 384
|
|
10060
|
+
});
|
|
10061
|
+
}
|
|
10062
|
+
function removeLegacyPointer(cacheRoot) {
|
|
10063
|
+
fs30.rmSync(path26.join(cacheRoot, "current.json"), { force: true });
|
|
10064
|
+
}
|
|
10065
|
+
function removeStaleBuildArtifacts(cacheRoot) {
|
|
10066
|
+
for (const entry of fs30.readdirSync(cacheRoot)) {
|
|
10067
|
+
if (!/^\.(?:staging|retired)-[a-z0-9-]+$/.test(entry)) continue;
|
|
10068
|
+
const target = path26.join(cacheRoot, entry);
|
|
10069
|
+
const stats = fs30.lstatSync(target);
|
|
10070
|
+
if (stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
10071
|
+
fs30.rmSync(target, { recursive: true, force: true });
|
|
10072
|
+
} else {
|
|
10073
|
+
fs30.rmSync(target, { force: true });
|
|
10074
|
+
}
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
async function buildServerStartupBundle(options) {
|
|
10078
|
+
const startedAt = Date.now();
|
|
10079
|
+
let stagingRoot;
|
|
10080
|
+
let cacheRoot;
|
|
10081
|
+
try {
|
|
10082
|
+
const projectRoot = fs30.realpathSync(path26.resolve(options.projectRoot));
|
|
10083
|
+
cacheRoot = path26.resolve(
|
|
10084
|
+
projectRoot,
|
|
10085
|
+
options.cacheRoot ?? ".miaoda-cache/server"
|
|
10086
|
+
);
|
|
10087
|
+
if (!isInside(projectRoot, cacheRoot))
|
|
10088
|
+
throw new Error("cache root escaped project root");
|
|
10089
|
+
assertNoSymlinkComponents(
|
|
10090
|
+
projectRoot,
|
|
10091
|
+
cacheRoot,
|
|
10092
|
+
"cache path symlink is unsupported"
|
|
10093
|
+
);
|
|
10094
|
+
fs30.mkdirSync(cacheRoot, { recursive: true, mode: 448 });
|
|
10095
|
+
removeLegacyPointer(cacheRoot);
|
|
10096
|
+
removeStaleBuildArtifacts(cacheRoot);
|
|
10097
|
+
const timeoutMs = options.probeTimeoutMs ?? 1e4;
|
|
10098
|
+
const compileTimeoutMs = options.compileTimeoutMs ?? 12e4;
|
|
10099
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
10100
|
+
throw new Error("probe timeout must be positive");
|
|
10101
|
+
if (!Number.isFinite(compileTimeoutMs) || compileTimeoutMs <= 0)
|
|
10102
|
+
throw new Error("compile timeout must be positive");
|
|
10103
|
+
const sourceSha256 = workspaceSourceSha256(projectRoot);
|
|
10104
|
+
stagingRoot = path26.join(cacheRoot, `.staging-${randomUUID()}`);
|
|
10105
|
+
fs30.mkdirSync(stagingRoot, { recursive: false, mode: 448 });
|
|
10106
|
+
const explicitEntry = options.entry ? resolveEntry(projectRoot, options.entry) : void 0;
|
|
10107
|
+
const distRoot = path26.join(projectRoot, "dist");
|
|
10108
|
+
const compiled = explicitEntry ? {
|
|
10109
|
+
entry: explicitEntry,
|
|
10110
|
+
applicationRoot: isInside(distRoot, explicitEntry) ? distRoot : path26.dirname(explicitEntry)
|
|
10111
|
+
} : await compileNestSource(
|
|
10112
|
+
projectRoot,
|
|
10113
|
+
stagingRoot,
|
|
10114
|
+
compileTimeoutMs,
|
|
10115
|
+
resolveNestTsconfig(projectRoot)
|
|
10116
|
+
);
|
|
10117
|
+
if (workspaceSourceSha256(projectRoot) !== sourceSha256) {
|
|
10118
|
+
throw new Error("source changed during isolated Nest compilation");
|
|
10119
|
+
}
|
|
10120
|
+
const payloadRoot = path26.join(stagingRoot, "payload");
|
|
10121
|
+
fs30.mkdirSync(payloadRoot, { recursive: true, mode: 493 });
|
|
10122
|
+
const entry = STARTUP_ENTRY_FILE;
|
|
10123
|
+
const bundleFile = path26.join(
|
|
10124
|
+
stagingRoot,
|
|
10125
|
+
...STARTUP_BUNDLE_FILE.split("/")
|
|
10126
|
+
);
|
|
10127
|
+
const islands = await buildHybridBundle(
|
|
10128
|
+
projectRoot,
|
|
10129
|
+
compiled.entry,
|
|
10130
|
+
bundleFile
|
|
10131
|
+
);
|
|
10132
|
+
const dynamicIslands = [...islands.values()].map((island) => ({
|
|
10133
|
+
packagePath: island.packagePath,
|
|
10134
|
+
packageName: island.packageName,
|
|
10135
|
+
reasons: [...island.reasons].sort()
|
|
10136
|
+
})).sort((left, right) => left.packagePath.localeCompare(right.packagePath));
|
|
10137
|
+
const {
|
|
10138
|
+
lockfileSha256,
|
|
10139
|
+
dependencyViewSha256,
|
|
10140
|
+
materializedPackages,
|
|
10141
|
+
dynamicDependencies
|
|
10142
|
+
} = materializeDynamicIslands(projectRoot, payloadRoot, islands);
|
|
10143
|
+
writeServerStartupLauncher(payloadRoot);
|
|
10144
|
+
const { files, treeSha256, totalBytes } = indexPayload(payloadRoot);
|
|
10145
|
+
const fileIndexContents = `${JSON.stringify(files, null, 2)}
|
|
10146
|
+
`;
|
|
10147
|
+
if (Buffer.byteLength(fileIndexContents) > MAX_FILE_INDEX_BYTES) {
|
|
10148
|
+
throw new Error("generation file index exceeds 16 MiB");
|
|
10149
|
+
}
|
|
10150
|
+
fs30.writeFileSync(
|
|
10151
|
+
path26.join(stagingRoot, "files.mtree.json"),
|
|
10152
|
+
fileIndexContents,
|
|
10153
|
+
{ mode: 384 }
|
|
10154
|
+
);
|
|
10155
|
+
const runtime = runtimeIdentity();
|
|
10156
|
+
const generationId = sha256(
|
|
10157
|
+
stableJson({
|
|
10158
|
+
artifactFormat: ARTIFACT_FORMAT,
|
|
10159
|
+
builderPolicyVersion: BUILDER_POLICY_VERSION,
|
|
10160
|
+
dependencyMode: DEPENDENCY_MODE,
|
|
10161
|
+
dependencyViewSha256,
|
|
10162
|
+
dynamicDependencies,
|
|
10163
|
+
dynamicIslands,
|
|
10164
|
+
entry,
|
|
10165
|
+
lockfileSha256,
|
|
10166
|
+
materializedPackages,
|
|
10167
|
+
runtime,
|
|
10168
|
+
schemaVersion: SCHEMA_VERSION,
|
|
10169
|
+
sourceSha256,
|
|
10170
|
+
treeSha256
|
|
10171
|
+
})
|
|
10172
|
+
);
|
|
10173
|
+
const startupMs = await isolatedProbe(
|
|
10174
|
+
projectRoot,
|
|
10175
|
+
payloadRoot,
|
|
10176
|
+
entry,
|
|
10177
|
+
timeoutMs
|
|
10178
|
+
);
|
|
10179
|
+
if (workspaceSourceSha256(projectRoot) !== sourceSha256) {
|
|
10180
|
+
throw new Error("source changed while building startup generation");
|
|
10181
|
+
}
|
|
10182
|
+
const manifest = {
|
|
10183
|
+
schemaVersion: SCHEMA_VERSION,
|
|
10184
|
+
builderPolicyVersion: BUILDER_POLICY_VERSION,
|
|
10185
|
+
artifactFormat: ARTIFACT_FORMAT,
|
|
10186
|
+
generationId,
|
|
10187
|
+
entry,
|
|
10188
|
+
sourceSha256,
|
|
10189
|
+
lockfileSha256,
|
|
10190
|
+
dependencyViewSha256,
|
|
10191
|
+
dependencyMode: DEPENDENCY_MODE,
|
|
10192
|
+
dynamicDependencies,
|
|
10193
|
+
dynamicIslands,
|
|
10194
|
+
materializedPackages,
|
|
10195
|
+
runtime,
|
|
10196
|
+
artifact: {
|
|
10197
|
+
fileIndex: "files.mtree.json",
|
|
10198
|
+
fileIndexSha256: sha256(fileIndexContents),
|
|
10199
|
+
treeSha256,
|
|
10200
|
+
fileCount: files.length,
|
|
10201
|
+
totalBytes
|
|
10202
|
+
},
|
|
10203
|
+
isolatedProbe: {
|
|
10204
|
+
success: true,
|
|
10205
|
+
workspaceNodeModulesVisible: false,
|
|
10206
|
+
startupMs
|
|
10207
|
+
}
|
|
10208
|
+
};
|
|
10209
|
+
writeBoundedJson(
|
|
10210
|
+
path26.join(stagingRoot, "manifest.json"),
|
|
10211
|
+
manifest,
|
|
10212
|
+
MAX_MANIFEST_BYTES,
|
|
10213
|
+
"startup generation manifest"
|
|
10214
|
+
);
|
|
10215
|
+
writeBoundedJson(
|
|
10216
|
+
path26.join(stagingRoot, "probe.json"),
|
|
10217
|
+
{
|
|
10218
|
+
schemaVersion: SCHEMA_VERSION,
|
|
10219
|
+
generationId,
|
|
10220
|
+
success: true,
|
|
10221
|
+
workspaceNodeModulesVisible: false,
|
|
10222
|
+
startupMs
|
|
10223
|
+
},
|
|
10224
|
+
MAX_PROBE_BYTES,
|
|
10225
|
+
"startup generation probe"
|
|
10226
|
+
);
|
|
10227
|
+
fs30.rmSync(path26.join(stagingRoot, ".compiled"), {
|
|
10228
|
+
recursive: true,
|
|
10229
|
+
force: true
|
|
10230
|
+
});
|
|
10231
|
+
const generationsRoot = path26.join(cacheRoot, "generations");
|
|
10232
|
+
fs30.mkdirSync(generationsRoot, { recursive: true, mode: 448 });
|
|
10233
|
+
const generationRoot = path26.join(generationsRoot, generationId);
|
|
10234
|
+
const previousGenerations = fs30.readdirSync(generationsRoot);
|
|
10235
|
+
if (previousGenerations.length > 1) {
|
|
10236
|
+
throw new Error("startup generation directory is ambiguous");
|
|
10237
|
+
}
|
|
10238
|
+
const previous = previousGenerations[0];
|
|
10239
|
+
const previousRoot = previous ? path26.join(generationsRoot, previous) : void 0;
|
|
10240
|
+
const retiredRoot = previousRoot ? path26.join(cacheRoot, `.retired-${randomUUID()}`) : void 0;
|
|
10241
|
+
if (previousRoot && retiredRoot) {
|
|
10242
|
+
fs30.renameSync(previousRoot, retiredRoot);
|
|
10243
|
+
}
|
|
10244
|
+
try {
|
|
10245
|
+
fs30.renameSync(stagingRoot, generationRoot);
|
|
10246
|
+
} catch (publishError) {
|
|
10247
|
+
if (previousRoot && retiredRoot && fs30.existsSync(retiredRoot)) {
|
|
10248
|
+
fs30.renameSync(retiredRoot, previousRoot);
|
|
10249
|
+
}
|
|
10250
|
+
throw publishError;
|
|
10251
|
+
}
|
|
10252
|
+
if (previousRoot && retiredRoot) {
|
|
10253
|
+
try {
|
|
10254
|
+
fs30.rmSync(retiredRoot, { recursive: true, force: true });
|
|
10255
|
+
} catch (pruneError) {
|
|
10256
|
+
fs30.rmSync(generationRoot, { recursive: true, force: true });
|
|
10257
|
+
fs30.renameSync(retiredRoot, previousRoot);
|
|
10258
|
+
throw pruneError;
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
10261
|
+
stagingRoot = void 0;
|
|
10262
|
+
return {
|
|
10263
|
+
built: true,
|
|
10264
|
+
generationId,
|
|
10265
|
+
generationRoot,
|
|
10266
|
+
entryFile: path26.join(generationRoot, ...entry.split("/")),
|
|
10267
|
+
manifestFile: path26.join(generationRoot, "manifest.json"),
|
|
10268
|
+
reasons: [],
|
|
10269
|
+
elapsedMs: Date.now() - startedAt
|
|
10270
|
+
};
|
|
10271
|
+
} catch (error) {
|
|
10272
|
+
if (stagingRoot) fs30.rmSync(stagingRoot, { recursive: true, force: true });
|
|
10273
|
+
if (cacheRoot) removeLegacyPointer(cacheRoot);
|
|
10274
|
+
return {
|
|
10275
|
+
built: false,
|
|
10276
|
+
reasons: [error instanceof Error ? error.message : String(error)],
|
|
10277
|
+
elapsedMs: Date.now() - startedAt
|
|
10278
|
+
};
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
|
|
10282
|
+
// src/commands/build/index.ts
|
|
10283
|
+
var getTokenCommand = {
|
|
10284
|
+
name: "get-token",
|
|
10285
|
+
description: "Get artifact upload credential (STI token)",
|
|
10286
|
+
register(program) {
|
|
10287
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").requiredOption("--scene <scene>", "Build scene (pipeline, static)").option("--commit-id <id>", "Git commit ID (required for pipeline scene)").action(
|
|
10288
|
+
async (options) => {
|
|
10289
|
+
await getToken(options);
|
|
10290
|
+
}
|
|
10291
|
+
);
|
|
10292
|
+
}
|
|
10293
|
+
};
|
|
10294
|
+
var uploadStaticCommand = {
|
|
10295
|
+
name: "upload-static",
|
|
10296
|
+
description: "Upload shared/static files to TOS",
|
|
10297
|
+
register(program) {
|
|
10298
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option(
|
|
10299
|
+
"--static-dir <dir>",
|
|
10300
|
+
"Static files directory",
|
|
10301
|
+
UPLOAD_STATIC_DEFAULTS.staticDir
|
|
10302
|
+
).option(
|
|
10303
|
+
"--tosutil-path <path>",
|
|
10304
|
+
"Path to tosutil binary",
|
|
10305
|
+
UPLOAD_STATIC_DEFAULTS.tosutilPath
|
|
10306
|
+
).option(
|
|
10307
|
+
"--endpoint <endpoint>",
|
|
10308
|
+
"TOS endpoint",
|
|
10309
|
+
UPLOAD_STATIC_DEFAULTS.endpoint
|
|
10310
|
+
).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
|
|
10311
|
+
await uploadStatic(options);
|
|
10312
|
+
});
|
|
10313
|
+
}
|
|
10314
|
+
};
|
|
10315
|
+
var preUploadStaticCommand = {
|
|
10316
|
+
name: "pre-upload-static",
|
|
10317
|
+
description: "Get TOS upload info and output as env vars for build.sh eval",
|
|
10318
|
+
register(program) {
|
|
10319
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
|
|
10320
|
+
await preUploadStatic(options);
|
|
10321
|
+
});
|
|
10322
|
+
}
|
|
10323
|
+
};
|
|
10324
|
+
var serverStartupBundleCommand = {
|
|
10325
|
+
name: "server-startup-bundle",
|
|
10326
|
+
description: "Build and isolate-probe a recycle-only NestJS startup generation",
|
|
10327
|
+
register(program) {
|
|
10328
|
+
program.command(this.name).description(this.description).option("--project-root <dir>", "Application project root", process.cwd()).option(
|
|
10329
|
+
"--probe-timeout-ms <ms>",
|
|
10330
|
+
"Isolated availability probe timeout in milliseconds",
|
|
10331
|
+
"10000"
|
|
10332
|
+
).action(
|
|
10333
|
+
async (options) => {
|
|
10334
|
+
const probeTimeoutMs = Number(options.probeTimeoutMs);
|
|
10335
|
+
if (!Number.isFinite(probeTimeoutMs) || probeTimeoutMs <= 0) {
|
|
10336
|
+
throw new Error("--probe-timeout-ms must be a positive number");
|
|
10337
|
+
}
|
|
10338
|
+
const result = await buildServerStartupBundle({
|
|
10339
|
+
projectRoot: options.projectRoot,
|
|
10340
|
+
probeTimeoutMs
|
|
10341
|
+
});
|
|
10342
|
+
console.log(JSON.stringify(result));
|
|
10343
|
+
if (!result.built) process.exitCode = 2;
|
|
10344
|
+
}
|
|
10345
|
+
);
|
|
8289
10346
|
}
|
|
8290
10347
|
};
|
|
8291
10348
|
var buildCommandGroup = {
|
|
8292
10349
|
name: "build",
|
|
8293
10350
|
description: "Build related commands",
|
|
8294
|
-
commands: [
|
|
10351
|
+
commands: [
|
|
10352
|
+
getTokenCommand,
|
|
10353
|
+
uploadStaticCommand,
|
|
10354
|
+
preUploadStaticCommand,
|
|
10355
|
+
serverStartupBundleCommand
|
|
10356
|
+
]
|
|
8295
10357
|
};
|
|
8296
10358
|
|
|
8297
10359
|
// src/commands/index.ts
|
|
@@ -8309,13 +10371,13 @@ var commands = [
|
|
|
8309
10371
|
|
|
8310
10372
|
// src/index.ts
|
|
8311
10373
|
for (const filename of [".env.local", ".env"]) {
|
|
8312
|
-
const envPath =
|
|
8313
|
-
if (
|
|
10374
|
+
const envPath = path27.join(process.cwd(), filename);
|
|
10375
|
+
if (fs31.existsSync(envPath)) {
|
|
8314
10376
|
dotenvConfig({ path: envPath });
|
|
8315
10377
|
}
|
|
8316
10378
|
}
|
|
8317
|
-
var __dirname =
|
|
8318
|
-
var pkg = JSON.parse(
|
|
10379
|
+
var __dirname = path27.dirname(fileURLToPath5(import.meta.url));
|
|
10380
|
+
var pkg = JSON.parse(fs31.readFileSync(path27.join(__dirname, "../package.json"), "utf-8"));
|
|
8319
10381
|
var cli = new FullstackCLI(pkg.version);
|
|
8320
10382
|
cli.useAll(commands);
|
|
8321
10383
|
cli.run();
|