@lark-apaas/fullstack-cli 1.1.65-alpha.20260902125800 → 1.1.65
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 +41 -2617
- package/package.json +1 -4
- package/templates/scripts/dev.js +54 -700
- package/templates/scripts/server-handoff-runtime.mjs +0 -1904
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { fileURLToPath as
|
|
2
|
+
import fs29 from "fs";
|
|
3
|
+
import path25 from "path";
|
|
4
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5
5
|
import { config as dotenvConfig } from "dotenv";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
@@ -2483,36 +2483,12 @@ import fs7 from "fs";
|
|
|
2483
2483
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2484
2484
|
|
|
2485
2485
|
// src/config/sync.ts
|
|
2486
|
-
function hasStartupBundleEnrollment(packageJson) {
|
|
2487
|
-
if (!packageJson || typeof packageJson !== "object" || Array.isArray(packageJson)) {
|
|
2488
|
-
return false;
|
|
2489
|
-
}
|
|
2490
|
-
const config = packageJson.miaodaStartupBundle;
|
|
2491
|
-
return Boolean(
|
|
2492
|
-
config && typeof config === "object" && !Array.isArray(config) && config.schemaVersion === 1
|
|
2493
|
-
);
|
|
2494
|
-
}
|
|
2495
|
-
var DEFAULT_SYNCED_SCRIPT_FILES = [
|
|
2496
|
-
"build.sh",
|
|
2497
|
-
"dev-local.js",
|
|
2498
|
-
"dev.sh",
|
|
2499
|
-
"lint.js",
|
|
2500
|
-
"prune-smart.js",
|
|
2501
|
-
"run.sh"
|
|
2502
|
-
];
|
|
2503
2486
|
function buildDefaultRules(opts) {
|
|
2504
2487
|
const rules = [
|
|
2505
|
-
// 1.
|
|
2506
|
-
// 冷启动专属文件意外回灌到未入组的存量应用。
|
|
2507
|
-
...DEFAULT_SYNCED_SCRIPT_FILES.map((file) => ({
|
|
2508
|
-
from: `templates/scripts/${file}`,
|
|
2509
|
-
to: `scripts/${file}`,
|
|
2510
|
-
type: "file",
|
|
2511
|
-
overwrite: true
|
|
2512
|
-
})),
|
|
2488
|
+
// 1. 派生 scripts 目录(总是覆盖;递归同步,包含 scripts/hooks/run-precommit.js)
|
|
2513
2489
|
{
|
|
2514
|
-
from: "templates/scripts
|
|
2515
|
-
to: "scripts
|
|
2490
|
+
from: "templates/scripts",
|
|
2491
|
+
to: "scripts",
|
|
2516
2492
|
type: "directory",
|
|
2517
2493
|
overwrite: true
|
|
2518
2494
|
},
|
|
@@ -2614,14 +2590,6 @@ function buildDefaultRules(opts) {
|
|
|
2614
2590
|
ifStartsWith: "concurrently "
|
|
2615
2591
|
}
|
|
2616
2592
|
];
|
|
2617
|
-
if (opts.startupBundleEnrolled) {
|
|
2618
|
-
rules.unshift({
|
|
2619
|
-
from: "templates/scripts/dev.js",
|
|
2620
|
-
to: "scripts/dev.js",
|
|
2621
|
-
type: "file",
|
|
2622
|
-
overwrite: true
|
|
2623
|
-
});
|
|
2624
|
-
}
|
|
2625
2593
|
if (!opts.disableGenOpenapi) {
|
|
2626
2594
|
rules.push({
|
|
2627
2595
|
from: "templates/helper/gen-openapi.ts",
|
|
@@ -2798,14 +2766,10 @@ async function run2(options) {
|
|
|
2798
2766
|
process.exit(0);
|
|
2799
2767
|
}
|
|
2800
2768
|
const stack = resolveStack(userProjectRoot);
|
|
2801
|
-
const startupBundleEnrolled = resolveStartupBundleEnrollment(userPackageJson);
|
|
2802
2769
|
try {
|
|
2803
|
-
console.log(
|
|
2804
|
-
`[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`
|
|
2805
|
-
);
|
|
2770
|
+
console.log(`[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`);
|
|
2806
2771
|
const config = genSyncConfig(stack, {
|
|
2807
|
-
disableGenOpenapi: options.disableGenOpenapi ?? false
|
|
2808
|
-
startupBundleEnrolled
|
|
2772
|
+
disableGenOpenapi: options.disableGenOpenapi ?? false
|
|
2809
2773
|
});
|
|
2810
2774
|
if (!config || !config.sync) {
|
|
2811
2775
|
console.warn("[fullstack-cli] No sync configuration found");
|
|
@@ -2822,9 +2786,7 @@ async function run2(options) {
|
|
|
2822
2786
|
activateGitHooks(userProjectRoot);
|
|
2823
2787
|
} catch (error) {
|
|
2824
2788
|
const message = error instanceof Error ? error.message : String(error);
|
|
2825
|
-
console.warn(
|
|
2826
|
-
`[fullstack-cli] \u26A0 Failed to activate git hooks: ${message}`
|
|
2827
|
-
);
|
|
2789
|
+
console.warn(`[fullstack-cli] \u26A0 Failed to activate git hooks: ${message}`);
|
|
2828
2790
|
}
|
|
2829
2791
|
}
|
|
2830
2792
|
console.log("[fullstack-cli] Sync completed successfully \u2705");
|
|
@@ -2834,19 +2796,6 @@ async function run2(options) {
|
|
|
2834
2796
|
process.exit(1);
|
|
2835
2797
|
}
|
|
2836
2798
|
}
|
|
2837
|
-
function resolveStartupBundleEnrollment(packageJsonPath) {
|
|
2838
|
-
try {
|
|
2839
|
-
return hasStartupBundleEnrollment(
|
|
2840
|
-
JSON.parse(fs7.readFileSync(packageJsonPath, "utf-8"))
|
|
2841
|
-
);
|
|
2842
|
-
} catch (error) {
|
|
2843
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2844
|
-
console.warn(
|
|
2845
|
-
`[fullstack-cli] \u26A0 Failed to inspect startup bundle enrollment, using legacy sync: ${message}`
|
|
2846
|
-
);
|
|
2847
|
-
return false;
|
|
2848
|
-
}
|
|
2849
|
-
}
|
|
2850
2799
|
function resolveStack(userProjectRoot) {
|
|
2851
2800
|
const sparkMetaPath = path5.join(userProjectRoot, ".spark", "meta.json");
|
|
2852
2801
|
if (!fs7.existsSync(sparkMetaPath)) {
|
|
@@ -2857,9 +2806,7 @@ function resolveStack(userProjectRoot) {
|
|
|
2857
2806
|
return typeof meta.stack === "string" ? meta.stack : void 0;
|
|
2858
2807
|
} catch (error) {
|
|
2859
2808
|
const message = error instanceof Error ? error.message : String(error);
|
|
2860
|
-
console.warn(
|
|
2861
|
-
`[fullstack-cli] \u26A0 Failed to read .spark/meta.json, fallback to default sync: ${message}`
|
|
2862
|
-
);
|
|
2809
|
+
console.warn(`[fullstack-cli] \u26A0 Failed to read .spark/meta.json, fallback to default sync: ${message}`);
|
|
2863
2810
|
return void 0;
|
|
2864
2811
|
}
|
|
2865
2812
|
}
|
|
@@ -2880,12 +2827,7 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2880
2827
|
}
|
|
2881
2828
|
if (rule.type === "add-script") {
|
|
2882
2829
|
const packageJsonPath = path5.join(userProjectRoot, "package.json");
|
|
2883
|
-
addScript(
|
|
2884
|
-
packageJsonPath,
|
|
2885
|
-
rule.name,
|
|
2886
|
-
rule.command,
|
|
2887
|
-
rule.overwrite ?? false
|
|
2888
|
-
);
|
|
2830
|
+
addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
|
|
2889
2831
|
return;
|
|
2890
2832
|
}
|
|
2891
2833
|
if (rule.type === "patch-script") {
|
|
@@ -2918,12 +2860,7 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2918
2860
|
syncDirectory(srcPath, destPath, rule.overwrite ?? true);
|
|
2919
2861
|
break;
|
|
2920
2862
|
case "file":
|
|
2921
|
-
syncFile(
|
|
2922
|
-
srcPath,
|
|
2923
|
-
destPath,
|
|
2924
|
-
rule.overwrite ?? true,
|
|
2925
|
-
rule.onlyIfExists ?? false
|
|
2926
|
-
);
|
|
2863
|
+
syncFile(srcPath, destPath, rule.overwrite ?? true, rule.onlyIfExists ?? false);
|
|
2927
2864
|
break;
|
|
2928
2865
|
case "append":
|
|
2929
2866
|
appendToFile(srcPath, destPath);
|
|
@@ -2932,9 +2869,7 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2932
2869
|
}
|
|
2933
2870
|
function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
|
|
2934
2871
|
if (onlyIfExists && !fs7.existsSync(dest)) {
|
|
2935
|
-
console.log(
|
|
2936
|
-
`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`
|
|
2937
|
-
);
|
|
2872
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`);
|
|
2938
2873
|
return;
|
|
2939
2874
|
}
|
|
2940
2875
|
const destDir = path5.dirname(dest);
|
|
@@ -2942,9 +2877,7 @@ function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
|
|
|
2942
2877
|
fs7.mkdirSync(destDir, { recursive: true });
|
|
2943
2878
|
}
|
|
2944
2879
|
if (fs7.existsSync(dest) && !overwrite) {
|
|
2945
|
-
console.log(
|
|
2946
|
-
`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`
|
|
2947
|
-
);
|
|
2880
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
|
|
2948
2881
|
return;
|
|
2949
2882
|
}
|
|
2950
2883
|
fs7.copyFileSync(src, dest);
|
|
@@ -2971,9 +2904,7 @@ function syncDirectory(src, dest, overwrite = true) {
|
|
|
2971
2904
|
}
|
|
2972
2905
|
});
|
|
2973
2906
|
if (count > 0) {
|
|
2974
|
-
console.log(
|
|
2975
|
-
`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`
|
|
2976
|
-
);
|
|
2907
|
+
console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
|
|
2977
2908
|
}
|
|
2978
2909
|
}
|
|
2979
2910
|
function appendToFile(src, dest) {
|
|
@@ -2983,9 +2914,7 @@ function appendToFile(src, dest) {
|
|
|
2983
2914
|
existingContent = fs7.readFileSync(dest, "utf-8");
|
|
2984
2915
|
}
|
|
2985
2916
|
if (existingContent.includes(content.trim())) {
|
|
2986
|
-
console.log(
|
|
2987
|
-
`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`
|
|
2988
|
-
);
|
|
2917
|
+
console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
|
|
2989
2918
|
return;
|
|
2990
2919
|
}
|
|
2991
2920
|
fs7.appendFileSync(dest, content);
|
|
@@ -3071,9 +3000,7 @@ function patchScript(packageJsonPath, name, to, ifStartsWith) {
|
|
|
3071
3000
|
console.log(`[fullstack-cli] \u2713 Patched scripts.${name}`);
|
|
3072
3001
|
} catch (error) {
|
|
3073
3002
|
const message = error instanceof Error ? error.message : String(error);
|
|
3074
|
-
console.warn(
|
|
3075
|
-
`[fullstack-cli] \u26A0 Could not patch scripts.${name}: ${message}`
|
|
3076
|
-
);
|
|
3003
|
+
console.warn(`[fullstack-cli] \u26A0 Could not patch scripts.${name}: ${message}`);
|
|
3077
3004
|
}
|
|
3078
3005
|
}
|
|
3079
3006
|
function addLineToFile(filePath, line) {
|
|
@@ -3371,11 +3298,8 @@ function getCliVersion() {
|
|
|
3371
3298
|
}
|
|
3372
3299
|
|
|
3373
3300
|
// src/commands/upgrade/get-upgrade-files.ts
|
|
3374
|
-
function getUpgradeFilesToStage(disableGenOpenapi = true
|
|
3375
|
-
const syncConfig = genSyncConfig(void 0, {
|
|
3376
|
-
disableGenOpenapi,
|
|
3377
|
-
startupBundleEnrolled
|
|
3378
|
-
});
|
|
3301
|
+
function getUpgradeFilesToStage(disableGenOpenapi = true) {
|
|
3302
|
+
const syncConfig = genSyncConfig(void 0, { disableGenOpenapi });
|
|
3379
3303
|
const filesToStage = /* @__PURE__ */ new Set();
|
|
3380
3304
|
syncConfig.sync.forEach((rule) => {
|
|
3381
3305
|
if (rule.type === "file" || rule.type === "directory" || rule.type === "merge-json") {
|
|
@@ -3409,15 +3333,10 @@ async function run3(options = {}) {
|
|
|
3409
3333
|
if (shouldCommit) {
|
|
3410
3334
|
console.log("[fullstack-cli] Step 3/3: Committing changes...");
|
|
3411
3335
|
const version = getCliVersion();
|
|
3412
|
-
const filesToStage = getUpgradeFilesToStage(
|
|
3413
|
-
options.disableGenOpenapi ?? true,
|
|
3414
|
-
hasStartupBundleEnrollment(readPackageJson(userProjectRoot))
|
|
3415
|
-
);
|
|
3336
|
+
const filesToStage = getUpgradeFilesToStage(options.disableGenOpenapi ?? true);
|
|
3416
3337
|
autoCommitUpgradeChanges(version, userProjectRoot, filesToStage);
|
|
3417
3338
|
} else {
|
|
3418
|
-
console.log(
|
|
3419
|
-
"[fullstack-cli] Step 3/3: Skipping commit (--no-commit flag)"
|
|
3420
|
-
);
|
|
3339
|
+
console.log("[fullstack-cli] Step 3/3: Skipping commit (--no-commit flag)");
|
|
3421
3340
|
}
|
|
3422
3341
|
console.log("[fullstack-cli] Upgrade completed successfully \u2705");
|
|
3423
3342
|
} catch (error) {
|
|
@@ -8341,2489 +8260,23 @@ async function preUploadStatic(options) {
|
|
|
8341
8260
|
}
|
|
8342
8261
|
}
|
|
8343
8262
|
|
|
8344
|
-
// src/commands/build/
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
import { spawn as spawn2 } from "child_process";
|
|
8352
|
-
import { createHash, randomUUID } from "crypto";
|
|
8353
|
-
|
|
8354
|
-
// src/commands/build/server-startup-static-bundle.ts
|
|
8355
|
-
import fs29 from "fs";
|
|
8356
|
-
import path25 from "path";
|
|
8357
|
-
import { builtinModules, createRequire as createRequire3 } from "module";
|
|
8358
|
-
import { fileURLToPath as fileURLToPath5, pathToFileURL } from "url";
|
|
8359
|
-
import { build } from "esbuild";
|
|
8360
|
-
import { parse } from "acorn";
|
|
8361
|
-
import { simple as walkSimple } from "acorn-walk";
|
|
8362
|
-
var ACTION_PLUGIN_LOADER_PACKAGE = "@lark-apaas/nestjs-capability";
|
|
8363
|
-
var ENROLLMENT_SCHEMA_VERSION = 1;
|
|
8364
|
-
var MAX_PACKAGE_JSON_BYTES = 1024 * 1024;
|
|
8365
|
-
var BUILTINS = /* @__PURE__ */ new Set([
|
|
8366
|
-
...builtinModules,
|
|
8367
|
-
...builtinModules.map((name) => `node:${name}`)
|
|
8368
|
-
]);
|
|
8369
|
-
function isInside(root, candidate) {
|
|
8370
|
-
const relative = path25.relative(root, candidate);
|
|
8371
|
-
return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
|
|
8372
|
-
}
|
|
8373
|
-
function readBounded(file, maximum, description) {
|
|
8374
|
-
if (!fs29.existsSync(file)) throw new Error(`${description} is unavailable`);
|
|
8375
|
-
const stats = fs29.lstatSync(file);
|
|
8376
|
-
if (stats.isSymbolicLink() || !stats.isFile() || stats.size > maximum) {
|
|
8377
|
-
throw new Error(`${description} exceeds the supported metadata size`);
|
|
8378
|
-
}
|
|
8379
|
-
const contents = fs29.readFileSync(file);
|
|
8380
|
-
if (contents.length !== stats.size) {
|
|
8381
|
-
throw new Error(`${description} changed while reading`);
|
|
8382
|
-
}
|
|
8383
|
-
return contents;
|
|
8384
|
-
}
|
|
8385
|
-
function stringRecord(value) {
|
|
8386
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
8387
|
-
return Object.fromEntries(
|
|
8388
|
-
Object.entries(value).filter(
|
|
8389
|
-
(entry) => typeof entry[1] === "string"
|
|
8390
|
-
)
|
|
8391
|
-
);
|
|
8392
|
-
}
|
|
8393
|
-
function packageNameIsValid(name) {
|
|
8394
|
-
return /^(?:@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._-]*)$/i.test(
|
|
8395
|
-
name
|
|
8396
|
-
);
|
|
8397
|
-
}
|
|
8398
|
-
function findPackageRoot(projectRoot, entry) {
|
|
8399
|
-
const nodeModulesRoot = path25.join(projectRoot, "node_modules");
|
|
8400
|
-
let current = path25.dirname(entry);
|
|
8401
|
-
while (isInside(nodeModulesRoot, current) && current !== nodeModulesRoot) {
|
|
8402
|
-
const packageFile = path25.join(current, "package.json");
|
|
8403
|
-
if (fs29.existsSync(packageFile) && fs29.statSync(packageFile).isFile()) {
|
|
8404
|
-
return current;
|
|
8405
|
-
}
|
|
8406
|
-
current = path25.dirname(current);
|
|
8407
|
-
}
|
|
8408
|
-
return void 0;
|
|
8409
|
-
}
|
|
8410
|
-
function assertSafePackageRoot(projectRoot, packageRoot) {
|
|
8411
|
-
const nodeModulesRoot = path25.join(projectRoot, "node_modules");
|
|
8412
|
-
if (!isInside(nodeModulesRoot, packageRoot)) {
|
|
8413
|
-
throw new Error(`package escaped project node_modules: ${packageRoot}`);
|
|
8414
|
-
}
|
|
8415
|
-
let current = projectRoot;
|
|
8416
|
-
for (const segment of path25.relative(projectRoot, packageRoot).split(path25.sep)) {
|
|
8417
|
-
if (!segment) continue;
|
|
8418
|
-
current = path25.join(current, segment);
|
|
8419
|
-
if (fs29.lstatSync(current).isSymbolicLink()) {
|
|
8420
|
-
throw new Error(`package symlink is unsupported: ${packageRoot}`);
|
|
8421
|
-
}
|
|
8422
|
-
}
|
|
8423
|
-
}
|
|
8424
|
-
function resolveActionPlugins(projectRoot, configured) {
|
|
8425
|
-
const projectRequire = createRequire3(path25.join(projectRoot, "package.json"));
|
|
8426
|
-
return Object.entries(configured).sort(([left], [right]) => left.localeCompare(right)).map(([specifier, version]) => {
|
|
8427
|
-
if (!packageNameIsValid(specifier) || !version) {
|
|
8428
|
-
throw new Error(
|
|
8429
|
-
`Action Plugin has an invalid configured version: ${specifier}`
|
|
8430
|
-
);
|
|
8431
|
-
}
|
|
8432
|
-
let entry;
|
|
8433
|
-
try {
|
|
8434
|
-
entry = projectRequire.resolve(specifier);
|
|
8435
|
-
} catch (error) {
|
|
8436
|
-
throw new Error(
|
|
8437
|
-
`Action Plugin cannot be statically resolved: ${specifier}: ${error instanceof Error ? error.message : String(error)}`
|
|
8438
|
-
);
|
|
8439
|
-
}
|
|
8440
|
-
const packageRoot = findPackageRoot(projectRoot, entry);
|
|
8441
|
-
if (!packageRoot) {
|
|
8442
|
-
throw new Error(`Action Plugin package root not found: ${specifier}`);
|
|
8443
|
-
}
|
|
8444
|
-
assertSafePackageRoot(projectRoot, packageRoot);
|
|
8445
|
-
const packageContents = readBounded(
|
|
8446
|
-
path25.join(packageRoot, "package.json"),
|
|
8447
|
-
MAX_PACKAGE_JSON_BYTES,
|
|
8448
|
-
`Action Plugin package.json ${specifier}`
|
|
8449
|
-
);
|
|
8450
|
-
const packageJson = JSON.parse(packageContents.toString("utf8"));
|
|
8451
|
-
if (packageJson.name !== specifier || packageJson.version !== version) {
|
|
8452
|
-
throw new Error(
|
|
8453
|
-
`Action Plugin identity mismatch: ${specifier}@${version}`
|
|
8454
|
-
);
|
|
8455
|
-
}
|
|
8456
|
-
const manifestContents = readBounded(
|
|
8457
|
-
path25.join(packageRoot, "manifest.json"),
|
|
8458
|
-
MAX_PACKAGE_JSON_BYTES,
|
|
8459
|
-
`Action Plugin manifest ${specifier}`
|
|
8460
|
-
);
|
|
8461
|
-
const manifest = JSON.parse(manifestContents.toString("utf8"));
|
|
8462
|
-
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
8463
|
-
throw new Error(
|
|
8464
|
-
`Action Plugin manifest must be an object: ${specifier}`
|
|
8465
|
-
);
|
|
8466
|
-
}
|
|
8467
|
-
const manifestObject = manifest;
|
|
8468
|
-
if (manifestObject.name !== void 0 && manifestObject.name !== specifier || manifestObject.version !== void 0 && manifestObject.version !== version) {
|
|
8469
|
-
throw new Error(
|
|
8470
|
-
`Action Plugin manifest identity mismatch: ${specifier}`
|
|
8471
|
-
);
|
|
8472
|
-
}
|
|
8473
|
-
return {
|
|
8474
|
-
specifier,
|
|
8475
|
-
version,
|
|
8476
|
-
entry,
|
|
8477
|
-
packageRoot,
|
|
8478
|
-
manifest: manifestObject
|
|
8479
|
-
};
|
|
8480
|
-
});
|
|
8481
|
-
}
|
|
8482
|
-
function readDependencyState(projectRoot) {
|
|
8483
|
-
const packageFile = path25.join(projectRoot, "package.json");
|
|
8484
|
-
const packageContents = readBounded(
|
|
8485
|
-
packageFile,
|
|
8486
|
-
MAX_PACKAGE_JSON_BYTES,
|
|
8487
|
-
"package.json"
|
|
8488
|
-
);
|
|
8489
|
-
const packageJson = JSON.parse(packageContents.toString("utf8"));
|
|
8490
|
-
if (!packageJson || typeof packageJson !== "object" || Array.isArray(packageJson)) {
|
|
8491
|
-
throw new Error("package.json must contain an object");
|
|
8492
|
-
}
|
|
8493
|
-
const packageObject = packageJson;
|
|
8494
|
-
const enrollment = packageObject.miaodaStartupBundle;
|
|
8495
|
-
if (!enrollment || typeof enrollment !== "object" || Array.isArray(enrollment) || enrollment.schemaVersion !== ENROLLMENT_SCHEMA_VERSION) {
|
|
8496
|
-
throw new Error(
|
|
8497
|
-
`package.json miaodaStartupBundle.schemaVersion must be ${ENROLLMENT_SCHEMA_VERSION}`
|
|
8498
|
-
);
|
|
8499
|
-
}
|
|
8500
|
-
const configuredPlugins = packageObject.actionPlugins;
|
|
8501
|
-
if (configuredPlugins !== void 0 && (!configuredPlugins || typeof configuredPlugins !== "object" || Array.isArray(configuredPlugins))) {
|
|
8502
|
-
throw new Error("package.json actionPlugins must be an object");
|
|
8503
|
-
}
|
|
8504
|
-
const actionPlugins = stringRecord(configuredPlugins);
|
|
8505
|
-
if (configuredPlugins && Object.keys(actionPlugins).length !== Object.keys(configuredPlugins).length) {
|
|
8506
|
-
throw new Error("package.json actionPlugins versions must be strings");
|
|
8507
|
-
}
|
|
8508
|
-
return {
|
|
8509
|
-
plugins: resolveActionPlugins(projectRoot, actionPlugins)
|
|
8510
|
-
};
|
|
8511
|
-
}
|
|
8512
|
-
function parseJavaScript(source) {
|
|
8513
|
-
try {
|
|
8514
|
-
return parse(source, {
|
|
8515
|
-
ecmaVersion: "latest",
|
|
8516
|
-
sourceType: "module",
|
|
8517
|
-
allowHashBang: true
|
|
8263
|
+
// src/commands/build/index.ts
|
|
8264
|
+
var getTokenCommand = {
|
|
8265
|
+
name: "get-token",
|
|
8266
|
+
description: "Get artifact upload credential (STI token)",
|
|
8267
|
+
register(program) {
|
|
8268
|
+
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(async (options) => {
|
|
8269
|
+
await getToken(options);
|
|
8518
8270
|
});
|
|
8519
|
-
} catch {
|
|
8520
|
-
try {
|
|
8521
|
-
return parse(source, {
|
|
8522
|
-
ecmaVersion: "latest",
|
|
8523
|
-
sourceType: "script",
|
|
8524
|
-
allowHashBang: true,
|
|
8525
|
-
allowReturnOutsideFunction: true
|
|
8526
|
-
});
|
|
8527
|
-
} catch {
|
|
8528
|
-
return void 0;
|
|
8529
|
-
}
|
|
8530
|
-
}
|
|
8531
|
-
}
|
|
8532
|
-
function staticPropertyName(node) {
|
|
8533
|
-
if (node?.type !== "MemberExpression") return void 0;
|
|
8534
|
-
if (!node.computed && node.property?.type === "Identifier") {
|
|
8535
|
-
return node.property.name;
|
|
8536
|
-
}
|
|
8537
|
-
if (node.computed && node.property?.type === "Literal" && typeof node.property.value === "string") {
|
|
8538
|
-
return node.property.value;
|
|
8539
|
-
}
|
|
8540
|
-
return void 0;
|
|
8541
|
-
}
|
|
8542
|
-
function staticString(node, bindings) {
|
|
8543
|
-
if (!node) return void 0;
|
|
8544
|
-
if (node.type === "Literal" && typeof node.value === "string")
|
|
8545
|
-
return node.value;
|
|
8546
|
-
if (node.type === "Identifier") return bindings.get(node.name);
|
|
8547
|
-
if (node.type === "TemplateLiteral") {
|
|
8548
|
-
let value = "";
|
|
8549
|
-
for (let index = 0; index < node.quasis.length; index += 1) {
|
|
8550
|
-
value += node.quasis[index]?.value?.cooked ?? "";
|
|
8551
|
-
if (index < node.expressions.length) {
|
|
8552
|
-
const expression = staticString(node.expressions[index], bindings);
|
|
8553
|
-
if (expression === void 0) return void 0;
|
|
8554
|
-
value += expression;
|
|
8555
|
-
}
|
|
8556
|
-
}
|
|
8557
|
-
return value;
|
|
8558
|
-
}
|
|
8559
|
-
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
8560
|
-
const left = staticString(node.left, bindings);
|
|
8561
|
-
const right = staticString(node.right, bindings);
|
|
8562
|
-
return left === void 0 || right === void 0 ? void 0 : left + right;
|
|
8563
|
-
}
|
|
8564
|
-
return void 0;
|
|
8565
|
-
}
|
|
8566
|
-
function recordBindingPattern(node, counts) {
|
|
8567
|
-
if (!node) return;
|
|
8568
|
-
if (node.type === "Identifier") {
|
|
8569
|
-
counts.set(node.name, (counts.get(node.name) ?? 0) + 1);
|
|
8570
|
-
return;
|
|
8571
|
-
}
|
|
8572
|
-
if (node.type === "RestElement") {
|
|
8573
|
-
recordBindingPattern(node.argument, counts);
|
|
8574
|
-
return;
|
|
8575
|
-
}
|
|
8576
|
-
if (node.type === "AssignmentPattern") {
|
|
8577
|
-
recordBindingPattern(node.left, counts);
|
|
8578
|
-
return;
|
|
8579
|
-
}
|
|
8580
|
-
if (node.type === "ArrayPattern") {
|
|
8581
|
-
for (const element of node.elements ?? [])
|
|
8582
|
-
recordBindingPattern(element, counts);
|
|
8583
|
-
return;
|
|
8584
|
-
}
|
|
8585
|
-
if (node.type === "ObjectPattern") {
|
|
8586
|
-
for (const property of node.properties ?? []) {
|
|
8587
|
-
recordBindingPattern(
|
|
8588
|
-
property.type === "RestElement" ? property.argument : property.value,
|
|
8589
|
-
counts
|
|
8590
|
-
);
|
|
8591
|
-
}
|
|
8592
|
-
}
|
|
8593
|
-
}
|
|
8594
|
-
function declaredBindingCounts(ast) {
|
|
8595
|
-
const counts = /* @__PURE__ */ new Map();
|
|
8596
|
-
walkSimple(ast, {
|
|
8597
|
-
VariableDeclarator(node) {
|
|
8598
|
-
recordBindingPattern(node.id, counts);
|
|
8599
|
-
},
|
|
8600
|
-
FunctionDeclaration(node) {
|
|
8601
|
-
recordBindingPattern(node.id, counts);
|
|
8602
|
-
for (const parameter of node.params ?? [])
|
|
8603
|
-
recordBindingPattern(parameter, counts);
|
|
8604
|
-
},
|
|
8605
|
-
FunctionExpression(node) {
|
|
8606
|
-
recordBindingPattern(node.id, counts);
|
|
8607
|
-
for (const parameter of node.params ?? [])
|
|
8608
|
-
recordBindingPattern(parameter, counts);
|
|
8609
|
-
},
|
|
8610
|
-
ArrowFunctionExpression(node) {
|
|
8611
|
-
for (const parameter of node.params ?? [])
|
|
8612
|
-
recordBindingPattern(parameter, counts);
|
|
8613
|
-
},
|
|
8614
|
-
ClassDeclaration(node) {
|
|
8615
|
-
recordBindingPattern(node.id, counts);
|
|
8616
|
-
},
|
|
8617
|
-
ClassExpression(node) {
|
|
8618
|
-
recordBindingPattern(node.id, counts);
|
|
8619
|
-
},
|
|
8620
|
-
ImportDeclaration(node) {
|
|
8621
|
-
for (const specifier of node.specifiers ?? []) {
|
|
8622
|
-
recordBindingPattern(specifier.local, counts);
|
|
8623
|
-
}
|
|
8624
|
-
},
|
|
8625
|
-
CatchClause(node) {
|
|
8626
|
-
recordBindingPattern(node.param, counts);
|
|
8627
|
-
},
|
|
8628
|
-
AssignmentExpression(node) {
|
|
8629
|
-
recordBindingPattern(node.left, counts);
|
|
8630
|
-
},
|
|
8631
|
-
UpdateExpression(node) {
|
|
8632
|
-
recordBindingPattern(node.argument, counts);
|
|
8633
|
-
}
|
|
8634
|
-
});
|
|
8635
|
-
return counts;
|
|
8636
|
-
}
|
|
8637
|
-
function collectStaticBindings(ast, counts) {
|
|
8638
|
-
const declarations = [];
|
|
8639
|
-
for (const statement of ast.body ?? []) {
|
|
8640
|
-
const declaration = statement.type === "VariableDeclaration" ? statement : statement.type === "ExportNamedDeclaration" && statement.declaration?.type === "VariableDeclaration" ? statement.declaration : void 0;
|
|
8641
|
-
if (declaration?.kind !== "const") continue;
|
|
8642
|
-
for (const item of declaration.declarations ?? []) {
|
|
8643
|
-
if (item.id?.type === "Identifier" && item.init && counts.get(item.id.name) === 1) {
|
|
8644
|
-
declarations.push({ name: item.id.name, value: item.init });
|
|
8645
|
-
}
|
|
8646
|
-
}
|
|
8647
|
-
}
|
|
8648
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
8649
|
-
for (let pass = 0; pass <= declarations.length; pass += 1) {
|
|
8650
|
-
let changed = false;
|
|
8651
|
-
for (const declaration of declarations) {
|
|
8652
|
-
if (bindings.has(declaration.name)) continue;
|
|
8653
|
-
const value = staticString(declaration.value, bindings);
|
|
8654
|
-
if (value !== void 0) {
|
|
8655
|
-
bindings.set(declaration.name, value);
|
|
8656
|
-
changed = true;
|
|
8657
|
-
}
|
|
8658
|
-
}
|
|
8659
|
-
if (!changed) break;
|
|
8660
|
-
}
|
|
8661
|
-
return bindings;
|
|
8662
|
-
}
|
|
8663
|
-
function runtimeBuiltinModule(specifier) {
|
|
8664
|
-
if (typeof specifier !== "string") return void 0;
|
|
8665
|
-
const normalized = specifier.startsWith("node:") ? specifier.slice("node:".length) : specifier;
|
|
8666
|
-
return [
|
|
8667
|
-
"child_process",
|
|
8668
|
-
"fs",
|
|
8669
|
-
"fs/promises",
|
|
8670
|
-
"path",
|
|
8671
|
-
"url",
|
|
8672
|
-
"worker_threads"
|
|
8673
|
-
].includes(normalized) ? normalized : void 0;
|
|
8674
|
-
}
|
|
8675
|
-
function literalString(node) {
|
|
8676
|
-
return node?.type === "Literal" && typeof node.value === "string" ? node.value : void 0;
|
|
8677
|
-
}
|
|
8678
|
-
function builtinRequire(node, bindingCounts) {
|
|
8679
|
-
if (node?.type !== "CallExpression" || node.callee?.type !== "Identifier" || node.callee.name !== "require" || bindingCounts.has("require") || node.arguments.length !== 1) {
|
|
8680
|
-
return void 0;
|
|
8681
|
-
}
|
|
8682
|
-
const module = runtimeBuiltinModule(literalString(node.arguments[0]));
|
|
8683
|
-
return module ? { module, members: [] } : void 0;
|
|
8684
|
-
}
|
|
8685
|
-
function recordBuiltinPattern(pattern, origin, counts, bindings) {
|
|
8686
|
-
if (!pattern) return;
|
|
8687
|
-
if (pattern.type === "Identifier") {
|
|
8688
|
-
if (counts.get(pattern.name) === 1) bindings.set(pattern.name, origin);
|
|
8689
|
-
return;
|
|
8690
|
-
}
|
|
8691
|
-
if (pattern.type === "AssignmentPattern") {
|
|
8692
|
-
recordBuiltinPattern(pattern.left, origin, counts, bindings);
|
|
8693
|
-
return;
|
|
8694
|
-
}
|
|
8695
|
-
if (pattern.type !== "ObjectPattern") return;
|
|
8696
|
-
for (const property of pattern.properties ?? []) {
|
|
8697
|
-
if (property.type !== "Property") continue;
|
|
8698
|
-
const member = property.key?.type === "Identifier" ? property.key.name : literalString(property.key);
|
|
8699
|
-
if (!member) continue;
|
|
8700
|
-
recordBuiltinPattern(
|
|
8701
|
-
property.value,
|
|
8702
|
-
{ module: origin.module, members: [...origin.members, member] },
|
|
8703
|
-
counts,
|
|
8704
|
-
bindings
|
|
8705
|
-
);
|
|
8706
|
-
}
|
|
8707
|
-
}
|
|
8708
|
-
function collectBuiltinBindings(ast, counts) {
|
|
8709
|
-
const bindings = /* @__PURE__ */ new Map();
|
|
8710
|
-
for (const statement of ast.body ?? []) {
|
|
8711
|
-
if (statement.type === "ImportDeclaration") {
|
|
8712
|
-
const module = runtimeBuiltinModule(literalString(statement.source));
|
|
8713
|
-
if (!module) continue;
|
|
8714
|
-
for (const specifier of statement.specifiers ?? []) {
|
|
8715
|
-
if (specifier.local?.type !== "Identifier") continue;
|
|
8716
|
-
const origin = specifier.type === "ImportSpecifier" ? {
|
|
8717
|
-
module,
|
|
8718
|
-
members: [
|
|
8719
|
-
specifier.imported?.type === "Identifier" ? specifier.imported.name : literalString(specifier.imported) ?? ""
|
|
8720
|
-
]
|
|
8721
|
-
} : { module, members: [] };
|
|
8722
|
-
if (origin.members.every(Boolean) && counts.get(specifier.local.name) === 1) {
|
|
8723
|
-
bindings.set(specifier.local.name, origin);
|
|
8724
|
-
}
|
|
8725
|
-
}
|
|
8726
|
-
continue;
|
|
8727
|
-
}
|
|
8728
|
-
const declaration = statement.type === "VariableDeclaration" ? statement : statement.type === "ExportNamedDeclaration" && statement.declaration?.type === "VariableDeclaration" ? statement.declaration : void 0;
|
|
8729
|
-
if (!declaration) continue;
|
|
8730
|
-
for (const item of declaration.declarations ?? []) {
|
|
8731
|
-
let origin = builtinRequire(item.init, counts);
|
|
8732
|
-
if (!origin && item.init?.type === "MemberExpression" && staticPropertyName(item.init)) {
|
|
8733
|
-
const required = builtinRequire(item.init.object, counts);
|
|
8734
|
-
if (required) {
|
|
8735
|
-
origin = {
|
|
8736
|
-
module: required.module,
|
|
8737
|
-
members: [...required.members, staticPropertyName(item.init)]
|
|
8738
|
-
};
|
|
8739
|
-
}
|
|
8740
|
-
}
|
|
8741
|
-
if (origin) recordBuiltinPattern(item.id, origin, counts, bindings);
|
|
8742
|
-
}
|
|
8743
|
-
}
|
|
8744
|
-
return bindings;
|
|
8745
|
-
}
|
|
8746
|
-
function analyzeSource(ast) {
|
|
8747
|
-
const bindingCounts = declaredBindingCounts(ast);
|
|
8748
|
-
return {
|
|
8749
|
-
bindingCounts,
|
|
8750
|
-
builtinBindings: collectBuiltinBindings(ast, bindingCounts),
|
|
8751
|
-
strings: collectStaticBindings(ast, bindingCounts)
|
|
8752
|
-
};
|
|
8753
|
-
}
|
|
8754
|
-
function unwrapZeroSequence(node) {
|
|
8755
|
-
if (node?.type === "SequenceExpression" && node.expressions.length === 2 && node.expressions[0]?.type === "Literal" && node.expressions[0].value === 0) {
|
|
8756
|
-
return node.expressions[1];
|
|
8757
|
-
}
|
|
8758
|
-
return node;
|
|
8759
|
-
}
|
|
8760
|
-
function builtinReference(node, analysis) {
|
|
8761
|
-
const target = unwrapZeroSequence(node);
|
|
8762
|
-
if (!target) return void 0;
|
|
8763
|
-
if (target.type === "Identifier") {
|
|
8764
|
-
return analysis.builtinBindings.get(target.name);
|
|
8765
|
-
}
|
|
8766
|
-
if (target.type !== "MemberExpression")
|
|
8767
|
-
return builtinRequire(target, analysis.bindingCounts);
|
|
8768
|
-
const member = staticPropertyName(target);
|
|
8769
|
-
if (!member) return void 0;
|
|
8770
|
-
const parent = builtinReference(target.object, analysis);
|
|
8771
|
-
return parent ? { module: parent.module, members: [...parent.members, member] } : void 0;
|
|
8772
|
-
}
|
|
8773
|
-
function builtinMethod(node, analysis, module) {
|
|
8774
|
-
const reference = builtinReference(node, analysis);
|
|
8775
|
-
if (!reference || reference.module !== module) return void 0;
|
|
8776
|
-
return reference.members.length === 1 ? reference.members[0] : void 0;
|
|
8777
|
-
}
|
|
8778
|
-
function fsMethod(node, analysis) {
|
|
8779
|
-
const reference = builtinReference(node, analysis);
|
|
8780
|
-
if (!reference) return void 0;
|
|
8781
|
-
if (reference.module === "fs/promises" && reference.members.length === 1) {
|
|
8782
|
-
return reference.members[0];
|
|
8783
|
-
}
|
|
8784
|
-
if (reference.module !== "fs") return void 0;
|
|
8785
|
-
if (reference.members.length === 1) return reference.members[0];
|
|
8786
|
-
return reference.members.length === 2 && reference.members[0] === "promises" ? reference.members[1] : void 0;
|
|
8787
|
-
}
|
|
8788
|
-
function isDirectRequire(node, analysis) {
|
|
8789
|
-
return isUnshadowedIdentifier(node, "require", analysis);
|
|
8790
|
-
}
|
|
8791
|
-
function isRequireResolve(node, analysis) {
|
|
8792
|
-
return node?.type === "MemberExpression" && staticPropertyName(node) === "resolve" && isDirectRequire(node.object, analysis);
|
|
8793
|
-
}
|
|
8794
|
-
function isLayoutHelperCall(node, analysis) {
|
|
8795
|
-
const pathMethod = builtinMethod(node, analysis, "path");
|
|
8796
|
-
const urlMethod = builtinMethod(node, analysis, "url");
|
|
8797
|
-
return pathMethod === "dirname" || pathMethod === "join" || pathMethod === "resolve" || urlMethod === "fileURLToPath";
|
|
8798
|
-
}
|
|
8799
|
-
function staticModuleReference(sourceFile, node, analysis) {
|
|
8800
|
-
const specifier = staticString(node, analysis.strings);
|
|
8801
|
-
if (specifier !== void 0) return specifier;
|
|
8802
|
-
if (isUrlObjectExpression(node, analysis) && isImportMetaUrl(node?.arguments[1])) {
|
|
8803
|
-
const relative = staticString(node.arguments[0], analysis.strings);
|
|
8804
|
-
if (relative !== void 0) {
|
|
8805
|
-
const moduleUrl = new URL(relative, pathToFileURL(sourceFile));
|
|
8806
|
-
if (moduleUrl.search || moduleUrl.hash) {
|
|
8807
|
-
throw new Error(
|
|
8808
|
-
`module URL search or hash is unsupported in a static startup bundle: ${sourceFile}`
|
|
8809
|
-
);
|
|
8810
|
-
}
|
|
8811
|
-
}
|
|
8812
|
-
}
|
|
8813
|
-
const layout = evaluateLayoutPath(sourceFile, node, analysis);
|
|
8814
|
-
return layout?.kind === "static" && layout.anchor === "directory" ? layout.value : void 0;
|
|
8815
|
-
}
|
|
8816
|
-
function isUrlObjectExpression(node, analysis) {
|
|
8817
|
-
return Boolean(
|
|
8818
|
-
node?.type === "NewExpression" && (isUnshadowedIdentifier(node.callee, "URL", analysis) || builtinMethod(node.callee, analysis, "url") === "URL")
|
|
8819
|
-
);
|
|
8820
|
-
}
|
|
8821
|
-
function staticModuleReferencePlugin() {
|
|
8822
|
-
return {
|
|
8823
|
-
name: "miaoda-static-module-references",
|
|
8824
|
-
setup(esbuild) {
|
|
8825
|
-
esbuild.onLoad({ filter: /\.(?:cjs|mjs|js)$/ }, (args) => {
|
|
8826
|
-
const source = fs29.readFileSync(args.path, "utf8");
|
|
8827
|
-
const ast = parseJavaScript(source);
|
|
8828
|
-
if (!ast) return void 0;
|
|
8829
|
-
const analysis = analyzeSource(ast);
|
|
8830
|
-
const replacements = [];
|
|
8831
|
-
const rewriteCode = (node, value) => {
|
|
8832
|
-
if (!node) return;
|
|
8833
|
-
replacements.push({
|
|
8834
|
-
start: node.start,
|
|
8835
|
-
end: node.end,
|
|
8836
|
-
value
|
|
8837
|
-
});
|
|
8838
|
-
};
|
|
8839
|
-
const rewriteString = (node, value) => {
|
|
8840
|
-
if (node?.type === "Literal" && node.value === value) return;
|
|
8841
|
-
rewriteCode(node, JSON.stringify(value));
|
|
8842
|
-
};
|
|
8843
|
-
walkSimple(ast, {
|
|
8844
|
-
CallExpression(node) {
|
|
8845
|
-
const directRequire = isDirectRequire(node.callee, analysis);
|
|
8846
|
-
const requireResolve = isRequireResolve(node.callee, analysis);
|
|
8847
|
-
if (directRequire || requireResolve) {
|
|
8848
|
-
const argument2 = node.arguments[0];
|
|
8849
|
-
if (directRequire && isUrlObjectExpression(argument2, analysis)) {
|
|
8850
|
-
throw new Error(
|
|
8851
|
-
`require(URL) is unsupported in a static startup bundle: ${args.path}`
|
|
8852
|
-
);
|
|
8853
|
-
}
|
|
8854
|
-
const specifier = staticModuleReference(
|
|
8855
|
-
args.path,
|
|
8856
|
-
argument2,
|
|
8857
|
-
analysis
|
|
8858
|
-
);
|
|
8859
|
-
if (specifier === void 0) return;
|
|
8860
|
-
if (requireResolve) {
|
|
8861
|
-
throw new Error(
|
|
8862
|
-
`require.resolve is unsupported in a static startup bundle: ${args.path}`
|
|
8863
|
-
);
|
|
8864
|
-
}
|
|
8865
|
-
rewriteString(argument2, specifier);
|
|
8866
|
-
return;
|
|
8867
|
-
}
|
|
8868
|
-
const method = fsMethod(node.callee, analysis);
|
|
8869
|
-
if (!method || !COPYABLE_FS_METHODS.has(method) || !readOptionsAreSafe(method, node, analysis) || !canRewritePayloadPath(analysis)) {
|
|
8870
|
-
return;
|
|
8871
|
-
}
|
|
8872
|
-
const argument = node.arguments[0];
|
|
8873
|
-
const layout = classifyLayoutPath(args.path, argument, analysis);
|
|
8874
|
-
if (layout.kind !== "static") return;
|
|
8875
|
-
rewriteCode(
|
|
8876
|
-
argument,
|
|
8877
|
-
`require("node:path").join(__dirname, ${JSON.stringify(
|
|
8878
|
-
layout.asset.destination
|
|
8879
|
-
)})`
|
|
8880
|
-
);
|
|
8881
|
-
},
|
|
8882
|
-
ImportExpression(node) {
|
|
8883
|
-
const specifier = staticModuleReference(
|
|
8884
|
-
args.path,
|
|
8885
|
-
node.source,
|
|
8886
|
-
analysis
|
|
8887
|
-
);
|
|
8888
|
-
if (specifier !== void 0) rewriteString(node.source, specifier);
|
|
8889
|
-
}
|
|
8890
|
-
});
|
|
8891
|
-
if (replacements.length === 0) return void 0;
|
|
8892
|
-
let contents = source;
|
|
8893
|
-
for (const replacement of replacements.sort(
|
|
8894
|
-
(left, right) => right.start - left.start
|
|
8895
|
-
)) {
|
|
8896
|
-
contents = contents.slice(0, replacement.start) + replacement.value + contents.slice(replacement.end);
|
|
8897
|
-
}
|
|
8898
|
-
return { contents, loader: "js", resolveDir: path25.dirname(args.path) };
|
|
8899
|
-
});
|
|
8900
|
-
}
|
|
8901
|
-
};
|
|
8902
|
-
}
|
|
8903
|
-
function isImportMetaUrl(node) {
|
|
8904
|
-
return Boolean(
|
|
8905
|
-
node?.type === "MemberExpression" && staticPropertyName(node) === "url" && node.object?.type === "MetaProperty" && node.object.meta?.name === "import" && node.object.property?.name === "meta"
|
|
8906
|
-
);
|
|
8907
|
-
}
|
|
8908
|
-
function isUnshadowedIdentifier(node, name, analysis) {
|
|
8909
|
-
return node?.type === "Identifier" && node.name === name && !analysis.bindingCounts.has(name);
|
|
8910
|
-
}
|
|
8911
|
-
function containsRawLayoutPrimitive(node) {
|
|
8912
|
-
if (!node) return false;
|
|
8913
|
-
if (node.type === "Identifier" && (node.name === "__dirname" || node.name === "__filename") || isImportMetaUrl(node)) {
|
|
8914
|
-
return true;
|
|
8915
|
-
}
|
|
8916
|
-
if (node.type === "BinaryExpression" || node.type === "LogicalExpression") {
|
|
8917
|
-
return containsRawLayoutPrimitive(node.left) || containsRawLayoutPrimitive(node.right);
|
|
8918
|
-
}
|
|
8919
|
-
if (node.type === "ConditionalExpression") {
|
|
8920
|
-
return containsRawLayoutPrimitive(node.test) || containsRawLayoutPrimitive(node.consequent) || containsRawLayoutPrimitive(node.alternate);
|
|
8921
|
-
}
|
|
8922
|
-
if (node.type === "TemplateLiteral") {
|
|
8923
|
-
return node.expressions.some(containsRawLayoutPrimitive);
|
|
8924
|
-
}
|
|
8925
|
-
if (node.type === "MemberExpression") {
|
|
8926
|
-
return containsRawLayoutPrimitive(node.object) || node.computed && containsRawLayoutPrimitive(node.property);
|
|
8927
|
-
}
|
|
8928
|
-
if (node.type === "CallExpression" || node.type === "NewExpression") {
|
|
8929
|
-
return containsRawLayoutPrimitive(node.callee) || node.arguments.some(containsRawLayoutPrimitive);
|
|
8930
|
-
}
|
|
8931
|
-
if (node.type === "ChainExpression") {
|
|
8932
|
-
return containsRawLayoutPrimitive(node.expression);
|
|
8933
|
-
}
|
|
8934
|
-
return false;
|
|
8935
|
-
}
|
|
8936
|
-
function canRewritePayloadPath(analysis) {
|
|
8937
|
-
return !analysis.bindingCounts.has("require") && !analysis.bindingCounts.has("__dirname");
|
|
8938
|
-
}
|
|
8939
|
-
function canonicalAssetPath(value) {
|
|
8940
|
-
const normalized = value.split(path25.sep).join("/").replace(/^\.\//, "");
|
|
8941
|
-
if (!normalized || normalized === "." || normalized !== path25.posix.normalize(normalized) || normalized.startsWith("../") || path25.posix.isAbsolute(normalized) || normalized.includes("\\")) {
|
|
8942
|
-
return void 0;
|
|
8943
|
-
}
|
|
8944
|
-
return normalized;
|
|
8945
|
-
}
|
|
8946
|
-
function evaluateLayoutPath(sourceFile, node, analysis) {
|
|
8947
|
-
if (!node) return void 0;
|
|
8948
|
-
if (isUnshadowedIdentifier(node, "__dirname", analysis)) {
|
|
8949
|
-
return {
|
|
8950
|
-
kind: "static",
|
|
8951
|
-
value: path25.dirname(sourceFile),
|
|
8952
|
-
anchor: "directory"
|
|
8953
|
-
};
|
|
8954
|
-
}
|
|
8955
|
-
if (isUnshadowedIdentifier(node, "__filename", analysis) || isImportMetaUrl(node)) {
|
|
8956
|
-
return { kind: "static", value: sourceFile, anchor: "file" };
|
|
8957
|
-
}
|
|
8958
|
-
if (node.type === "NewExpression" && (isUnshadowedIdentifier(node.callee, "URL", analysis) || builtinMethod(node.callee, analysis, "url") === "URL") && isImportMetaUrl(node.arguments[1])) {
|
|
8959
|
-
const relative = staticString(node.arguments[0], analysis.strings);
|
|
8960
|
-
if (relative === void 0) return { kind: "dynamic" };
|
|
8961
|
-
try {
|
|
8962
|
-
const value = fileURLToPath5(new URL(relative, pathToFileURL(sourceFile)));
|
|
8963
|
-
return {
|
|
8964
|
-
kind: "static",
|
|
8965
|
-
value,
|
|
8966
|
-
anchor: path25.resolve(value) === path25.resolve(sourceFile) ? "file" : "directory"
|
|
8967
|
-
};
|
|
8968
|
-
} catch {
|
|
8969
|
-
throw new Error(`static runtime URL is unsupported: ${sourceFile}`);
|
|
8970
|
-
}
|
|
8971
|
-
}
|
|
8972
|
-
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
8973
|
-
const leftLayout = evaluateLayoutPath(sourceFile, node.left, analysis);
|
|
8974
|
-
const rightLayout = evaluateLayoutPath(sourceFile, node.right, analysis);
|
|
8975
|
-
if (leftLayout?.kind === "dynamic" || rightLayout?.kind === "dynamic") {
|
|
8976
|
-
return { kind: "dynamic" };
|
|
8977
|
-
}
|
|
8978
|
-
if (!leftLayout && !rightLayout) return void 0;
|
|
8979
|
-
const left = leftLayout?.value ?? staticString(node.left, analysis.strings);
|
|
8980
|
-
const right = rightLayout?.value ?? staticString(node.right, analysis.strings);
|
|
8981
|
-
if (left === void 0 || right === void 0) {
|
|
8982
|
-
return { kind: "dynamic" };
|
|
8983
|
-
}
|
|
8984
|
-
return {
|
|
8985
|
-
kind: "static",
|
|
8986
|
-
value: left + right,
|
|
8987
|
-
anchor: leftLayout?.anchor === "file" || rightLayout?.anchor === "file" ? "file" : "directory"
|
|
8988
|
-
};
|
|
8989
|
-
}
|
|
8990
|
-
if (node.type === "TemplateLiteral") {
|
|
8991
|
-
let value = node.quasis[0]?.value?.cooked ?? "";
|
|
8992
|
-
let hasLayout = false;
|
|
8993
|
-
let anchor = "directory";
|
|
8994
|
-
let dynamic = false;
|
|
8995
|
-
for (let index = 0; index < node.expressions.length; index += 1) {
|
|
8996
|
-
const expression = node.expressions[index];
|
|
8997
|
-
const layout = evaluateLayoutPath(sourceFile, expression, analysis);
|
|
8998
|
-
if (layout?.kind === "dynamic") {
|
|
8999
|
-
dynamic = true;
|
|
9000
|
-
} else if (layout) {
|
|
9001
|
-
hasLayout = true;
|
|
9002
|
-
if (layout.anchor === "file") anchor = "file";
|
|
9003
|
-
value += layout.value;
|
|
9004
|
-
} else {
|
|
9005
|
-
const plain = staticString(expression, analysis.strings);
|
|
9006
|
-
if (plain === void 0) dynamic = true;
|
|
9007
|
-
else value += plain;
|
|
9008
|
-
}
|
|
9009
|
-
value += node.quasis[index + 1]?.value?.cooked ?? "";
|
|
9010
|
-
}
|
|
9011
|
-
if (!hasLayout) return void 0;
|
|
9012
|
-
return dynamic ? { kind: "dynamic" } : { kind: "static", value, anchor };
|
|
9013
|
-
}
|
|
9014
|
-
if (node.type !== "CallExpression") {
|
|
9015
|
-
return void 0;
|
|
9016
|
-
}
|
|
9017
|
-
const urlMethod = builtinMethod(node.callee, analysis, "url");
|
|
9018
|
-
if (urlMethod === "fileURLToPath") {
|
|
9019
|
-
return evaluateLayoutPath(sourceFile, node.arguments[0], analysis);
|
|
9020
|
-
}
|
|
9021
|
-
const pathMethod = builtinMethod(node.callee, analysis, "path");
|
|
9022
|
-
if (pathMethod === "dirname") {
|
|
9023
|
-
const target = evaluateLayoutPath(sourceFile, node.arguments[0], analysis);
|
|
9024
|
-
if (!target || target.kind === "dynamic") return target;
|
|
9025
|
-
return {
|
|
9026
|
-
kind: "static",
|
|
9027
|
-
value: path25.dirname(target.value),
|
|
9028
|
-
anchor: "directory"
|
|
9029
|
-
};
|
|
9030
|
-
}
|
|
9031
|
-
if (pathMethod === "join" || pathMethod === "resolve") {
|
|
9032
|
-
const values = [];
|
|
9033
|
-
let hasLayout = false;
|
|
9034
|
-
let anchor = "directory";
|
|
9035
|
-
let hasUnknownSegment = false;
|
|
9036
|
-
for (const argument of node.arguments) {
|
|
9037
|
-
const layout = evaluateLayoutPath(sourceFile, argument, analysis);
|
|
9038
|
-
if (layout?.kind === "dynamic") return layout;
|
|
9039
|
-
if (layout) {
|
|
9040
|
-
hasLayout = true;
|
|
9041
|
-
if (layout.anchor === "file") anchor = "file";
|
|
9042
|
-
values.push(layout.value);
|
|
9043
|
-
continue;
|
|
9044
|
-
}
|
|
9045
|
-
const value = staticString(argument, analysis.strings);
|
|
9046
|
-
if (value === void 0) {
|
|
9047
|
-
hasUnknownSegment = true;
|
|
9048
|
-
continue;
|
|
9049
|
-
}
|
|
9050
|
-
values.push(value);
|
|
9051
|
-
}
|
|
9052
|
-
if (!hasLayout) return void 0;
|
|
9053
|
-
if (hasUnknownSegment) return { kind: "dynamic" };
|
|
9054
|
-
return {
|
|
9055
|
-
kind: "static",
|
|
9056
|
-
value: pathMethod === "resolve" ? path25.resolve(...values) : path25.join(...values),
|
|
9057
|
-
anchor
|
|
9058
|
-
};
|
|
9059
8271
|
}
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
}
|
|
9069
|
-
if (evaluated.kind === "dynamic" || evaluated.anchor === "file") {
|
|
9070
|
-
return { kind: "unsafe" };
|
|
9071
|
-
}
|
|
9072
|
-
const sourceDirectory = path25.dirname(sourceFile);
|
|
9073
|
-
const relative = path25.relative(sourceDirectory, evaluated.value);
|
|
9074
|
-
const destination = canonicalAssetPath(relative);
|
|
9075
|
-
if (!destination) {
|
|
9076
|
-
throw new Error(
|
|
9077
|
-
`static runtime asset must be a strict descendant: ${sourceFile}`
|
|
9078
|
-
);
|
|
9079
|
-
}
|
|
9080
|
-
return {
|
|
9081
|
-
kind: "static",
|
|
9082
|
-
asset: { source: evaluated.value, destination }
|
|
9083
|
-
};
|
|
9084
|
-
}
|
|
9085
|
-
var COPYABLE_FS_METHODS = /* @__PURE__ */ new Set([
|
|
9086
|
-
"readFile",
|
|
9087
|
-
"readFileSync",
|
|
9088
|
-
"createReadStream",
|
|
9089
|
-
"readdir",
|
|
9090
|
-
"readdirSync",
|
|
9091
|
-
"opendir",
|
|
9092
|
-
"opendirSync",
|
|
9093
|
-
"exists",
|
|
9094
|
-
"existsSync"
|
|
9095
|
-
]);
|
|
9096
|
-
var NON_COPYABLE_FS_METHODS = /* @__PURE__ */ new Set([
|
|
9097
|
-
"access",
|
|
9098
|
-
"accessSync",
|
|
9099
|
-
"appendFile",
|
|
9100
|
-
"appendFileSync",
|
|
9101
|
-
"chmod",
|
|
9102
|
-
"chmodSync",
|
|
9103
|
-
"chown",
|
|
9104
|
-
"chownSync",
|
|
9105
|
-
"copyFile",
|
|
9106
|
-
"copyFileSync",
|
|
9107
|
-
"cp",
|
|
9108
|
-
"cpSync",
|
|
9109
|
-
"createWriteStream",
|
|
9110
|
-
"glob",
|
|
9111
|
-
"globSync",
|
|
9112
|
-
"link",
|
|
9113
|
-
"linkSync",
|
|
9114
|
-
"lstat",
|
|
9115
|
-
"lstatSync",
|
|
9116
|
-
"mkdir",
|
|
9117
|
-
"mkdirSync",
|
|
9118
|
-
"open",
|
|
9119
|
-
"openSync",
|
|
9120
|
-
"readlink",
|
|
9121
|
-
"readlinkSync",
|
|
9122
|
-
"realpath",
|
|
9123
|
-
"realpathSync",
|
|
9124
|
-
"rename",
|
|
9125
|
-
"renameSync",
|
|
9126
|
-
"rm",
|
|
9127
|
-
"rmSync",
|
|
9128
|
-
"rmdir",
|
|
9129
|
-
"rmdirSync",
|
|
9130
|
-
"stat",
|
|
9131
|
-
"statSync",
|
|
9132
|
-
"statfs",
|
|
9133
|
-
"statfsSync",
|
|
9134
|
-
"symlink",
|
|
9135
|
-
"symlinkSync",
|
|
9136
|
-
"truncate",
|
|
9137
|
-
"truncateSync",
|
|
9138
|
-
"unlink",
|
|
9139
|
-
"unlinkSync",
|
|
9140
|
-
"unwatchFile",
|
|
9141
|
-
"utimes",
|
|
9142
|
-
"utimesSync",
|
|
9143
|
-
"watch",
|
|
9144
|
-
"watchFile",
|
|
9145
|
-
"writeFile",
|
|
9146
|
-
"writeFileSync"
|
|
9147
|
-
]);
|
|
9148
|
-
function isFsPathMethod(method) {
|
|
9149
|
-
return Boolean(
|
|
9150
|
-
method && (COPYABLE_FS_METHODS.has(method) || NON_COPYABLE_FS_METHODS.has(method))
|
|
9151
|
-
);
|
|
9152
|
-
}
|
|
9153
|
-
function readOptionsAreSafe(method, call, analysis) {
|
|
9154
|
-
if (!["readFile", "readFileSync", "createReadStream"].includes(method)) {
|
|
9155
|
-
return true;
|
|
9156
|
-
}
|
|
9157
|
-
const options = call.arguments[1];
|
|
9158
|
-
if (!options) return true;
|
|
9159
|
-
if (method === "readFile" && ["ArrowFunctionExpression", "FunctionExpression"].includes(options.type)) {
|
|
9160
|
-
return true;
|
|
9161
|
-
}
|
|
9162
|
-
if (options.type === "Identifier" && options.name === "undefined" && !analysis.bindingCounts.has("undefined")) {
|
|
9163
|
-
return true;
|
|
9164
|
-
}
|
|
9165
|
-
if (options.type === "Literal") {
|
|
9166
|
-
return options.value === null || typeof options.value === "string";
|
|
9167
|
-
}
|
|
9168
|
-
if (options.type !== "ObjectExpression") return false;
|
|
9169
|
-
const accessFlagKey = method === "createReadStream" ? "flags" : "flag";
|
|
9170
|
-
for (const property of options.properties ?? []) {
|
|
9171
|
-
if (property.type !== "Property" || property.kind !== "init") return false;
|
|
9172
|
-
const key = !property.computed && property.key?.type === "Identifier" ? property.key.name : literalString(property.key);
|
|
9173
|
-
if (!key) return false;
|
|
9174
|
-
const stringValue = staticString(property.value, analysis.strings);
|
|
9175
|
-
const literalValue = property.value?.type === "Literal" && (property.value.value === null || ["boolean", "number", "string"].includes(typeof property.value.value));
|
|
9176
|
-
if (key === accessFlagKey) {
|
|
9177
|
-
if (stringValue !== "r" && stringValue !== "rs") return false;
|
|
9178
|
-
} else if (stringValue === void 0 && !literalValue) {
|
|
9179
|
-
return false;
|
|
9180
|
-
}
|
|
9181
|
-
}
|
|
9182
|
-
return true;
|
|
9183
|
-
}
|
|
9184
|
-
function reserveAssetBudget(file, size, budget, limits) {
|
|
9185
|
-
if (size > limits.maxFileBytes) {
|
|
9186
|
-
throw new Error(`generation file exceeds 64 MiB: ${file}`);
|
|
9187
|
-
}
|
|
9188
|
-
if (budget.fileCount + 1 > limits.maxFiles) {
|
|
9189
|
-
throw new Error(`generation exceeds ${limits.maxFiles} files`);
|
|
9190
|
-
}
|
|
9191
|
-
if (budget.totalBytes + size > limits.maxTotalBytes) {
|
|
9192
|
-
throw new Error("generation exceeds 256 MiB");
|
|
9193
|
-
}
|
|
9194
|
-
budget.fileCount += 1;
|
|
9195
|
-
budget.totalBytes += size;
|
|
9196
|
-
}
|
|
9197
|
-
function assertSafeAssetPath(projectRoot, source) {
|
|
9198
|
-
if (!isInside(projectRoot, source)) {
|
|
9199
|
-
throw new Error(`static runtime asset escaped project: ${source}`);
|
|
9200
|
-
}
|
|
9201
|
-
let current = projectRoot;
|
|
9202
|
-
for (const segment of path25.relative(projectRoot, source).split(path25.sep)) {
|
|
9203
|
-
if (!segment) continue;
|
|
9204
|
-
current = path25.join(current, segment);
|
|
9205
|
-
if (fs29.lstatSync(current).isSymbolicLink()) {
|
|
9206
|
-
throw new Error(`static runtime asset symlink is unsupported: ${source}`);
|
|
9207
|
-
}
|
|
9208
|
-
}
|
|
9209
|
-
}
|
|
9210
|
-
function normalizeAssetCandidates(candidates) {
|
|
9211
|
-
const byDestination = /* @__PURE__ */ new Map();
|
|
9212
|
-
for (const candidate of candidates) {
|
|
9213
|
-
const source = path25.resolve(candidate.source);
|
|
9214
|
-
const previous = byDestination.get(candidate.destination);
|
|
9215
|
-
if (previous && previous !== source) {
|
|
9216
|
-
throw new Error(
|
|
9217
|
-
`static runtime asset collision: ${candidate.destination}`
|
|
9218
|
-
);
|
|
9219
|
-
}
|
|
9220
|
-
byDestination.set(candidate.destination, source);
|
|
9221
|
-
}
|
|
9222
|
-
const normalized = [...byDestination].map(([destination, source]) => ({
|
|
9223
|
-
destination,
|
|
9224
|
-
source
|
|
9225
|
-
}));
|
|
9226
|
-
normalized.sort((left, right) => {
|
|
9227
|
-
const depth = left.destination.split("/").length - right.destination.split("/").length;
|
|
9228
|
-
return depth || left.destination.localeCompare(right.destination);
|
|
9229
|
-
});
|
|
9230
|
-
const accepted = /* @__PURE__ */ new Map();
|
|
9231
|
-
for (const candidate of normalized) {
|
|
9232
|
-
const segments = candidate.destination.split("/");
|
|
9233
|
-
for (let length = segments.length - 1; length > 0; length -= 1) {
|
|
9234
|
-
const ancestor = segments.slice(0, length).join("/");
|
|
9235
|
-
const ancestorSource = accepted.get(ancestor);
|
|
9236
|
-
if (!ancestorSource) continue;
|
|
9237
|
-
const expected = path25.resolve(ancestorSource, ...segments.slice(length));
|
|
9238
|
-
if (expected !== candidate.source) {
|
|
9239
|
-
throw new Error(
|
|
9240
|
-
`static runtime asset tree collision: ${candidate.destination}`
|
|
9241
|
-
);
|
|
9242
|
-
}
|
|
9243
|
-
break;
|
|
9244
|
-
}
|
|
9245
|
-
accepted.set(candidate.destination, candidate.source);
|
|
9246
|
-
}
|
|
9247
|
-
return normalized;
|
|
9248
|
-
}
|
|
9249
|
-
function seedPayloadBudget(payloadRoot, limits, budget) {
|
|
9250
|
-
const files = /* @__PURE__ */ new Set();
|
|
9251
|
-
const visit = (current) => {
|
|
9252
|
-
for (const entry of fs29.readdirSync(current, { withFileTypes: true })) {
|
|
9253
|
-
const target = path25.join(current, entry.name);
|
|
9254
|
-
if (entry.isSymbolicLink()) {
|
|
9255
|
-
throw new Error(`generation symlink is unsupported: ${target}`);
|
|
9256
|
-
}
|
|
9257
|
-
if (entry.isDirectory()) {
|
|
9258
|
-
visit(target);
|
|
9259
|
-
continue;
|
|
9260
|
-
}
|
|
9261
|
-
if (!entry.isFile()) {
|
|
9262
|
-
throw new Error(`generation special file is unsupported: ${target}`);
|
|
9263
|
-
}
|
|
9264
|
-
const stats = fs29.lstatSync(target);
|
|
9265
|
-
if (!stats.isFile()) {
|
|
9266
|
-
throw new Error(`generation special file is unsupported: ${target}`);
|
|
9267
|
-
}
|
|
9268
|
-
reserveAssetBudget(target, stats.size, budget, limits);
|
|
9269
|
-
files.add(path25.relative(payloadRoot, target).split(path25.sep).join("/"));
|
|
9270
|
-
}
|
|
9271
|
-
};
|
|
9272
|
-
visit(payloadRoot);
|
|
9273
|
-
return files;
|
|
9274
|
-
}
|
|
9275
|
-
function planAssetFiles(projectRoot, candidates, reserved, limits, budget) {
|
|
9276
|
-
const planned = /* @__PURE__ */ new Map();
|
|
9277
|
-
const visit = (source, destination) => {
|
|
9278
|
-
if (!fs29.existsSync(source)) {
|
|
9279
|
-
throw new Error(`static runtime asset is unavailable: ${source}`);
|
|
9280
|
-
}
|
|
9281
|
-
assertSafeAssetPath(projectRoot, source);
|
|
9282
|
-
const reservedCollision = [...reserved].find(
|
|
9283
|
-
(item) => item === destination || item.startsWith(`${destination}/`) || destination.startsWith(`${item}/`)
|
|
9284
|
-
);
|
|
9285
|
-
if (reservedCollision) {
|
|
9286
|
-
throw new Error(
|
|
9287
|
-
`static runtime asset collides with bundle output: ${reservedCollision}`
|
|
9288
|
-
);
|
|
9289
|
-
}
|
|
9290
|
-
const stats = fs29.lstatSync(source);
|
|
9291
|
-
if (stats.isDirectory()) {
|
|
9292
|
-
const entries = fs29.readdirSync(source).sort();
|
|
9293
|
-
if (entries.length === 0) {
|
|
9294
|
-
throw new Error(
|
|
9295
|
-
`static runtime asset empty directory is unsupported: ${source}`
|
|
9296
|
-
);
|
|
9297
|
-
}
|
|
9298
|
-
for (const entry of entries) {
|
|
9299
|
-
visit(path25.join(source, entry), path25.posix.join(destination, entry));
|
|
9300
|
-
}
|
|
9301
|
-
return;
|
|
9302
|
-
}
|
|
9303
|
-
if (!stats.isFile()) {
|
|
9304
|
-
throw new Error(`static runtime asset type is unsupported: ${source}`);
|
|
9305
|
-
}
|
|
9306
|
-
const previous = planned.get(destination);
|
|
9307
|
-
if (previous && previous.source !== source) {
|
|
9308
|
-
throw new Error(`static runtime asset collision: ${destination}`);
|
|
9309
|
-
}
|
|
9310
|
-
if (previous) return;
|
|
9311
|
-
reserveAssetBudget(source, stats.size, budget, limits);
|
|
9312
|
-
planned.set(destination, {
|
|
9313
|
-
destination,
|
|
9314
|
-
mode: stats.mode & 73 ? 493 : 420,
|
|
9315
|
-
size: stats.size,
|
|
9316
|
-
source
|
|
9317
|
-
});
|
|
9318
|
-
};
|
|
9319
|
-
for (const candidate of candidates) {
|
|
9320
|
-
visit(candidate.source, candidate.destination);
|
|
9321
|
-
}
|
|
9322
|
-
return [...planned.values()].sort(
|
|
9323
|
-
(left, right) => left.destination.localeCompare(right.destination)
|
|
9324
|
-
);
|
|
9325
|
-
}
|
|
9326
|
-
function copyPlannedAssets(projectRoot, assets, destinationRoot) {
|
|
9327
|
-
for (const asset of assets) {
|
|
9328
|
-
assertSafeAssetPath(projectRoot, asset.source);
|
|
9329
|
-
const stats = fs29.lstatSync(asset.source);
|
|
9330
|
-
if (stats.isSymbolicLink() || !stats.isFile() || stats.size !== asset.size) {
|
|
9331
|
-
throw new Error(
|
|
9332
|
-
`static runtime asset changed before copy: ${asset.source}`
|
|
9333
|
-
);
|
|
9334
|
-
}
|
|
9335
|
-
const contents = fs29.readFileSync(asset.source);
|
|
9336
|
-
if (contents.length !== asset.size) {
|
|
9337
|
-
throw new Error(
|
|
9338
|
-
`static runtime asset changed while reading: ${asset.source}`
|
|
9339
|
-
);
|
|
9340
|
-
}
|
|
9341
|
-
const target = path25.join(destinationRoot, ...asset.destination.split("/"));
|
|
9342
|
-
fs29.mkdirSync(path25.dirname(target), { recursive: true });
|
|
9343
|
-
fs29.writeFileSync(target, contents, { mode: asset.mode });
|
|
9344
|
-
}
|
|
9345
|
-
}
|
|
9346
|
-
function materializeStaticAssets(projectRoot, metafile, payloadRoot, limits) {
|
|
9347
|
-
const budget = { fileCount: 0, totalBytes: 0 };
|
|
9348
|
-
const reserved = seedPayloadBudget(payloadRoot, limits, budget);
|
|
9349
|
-
const candidates = [];
|
|
9350
|
-
for (const input of Object.keys(metafile.inputs).sort()) {
|
|
9351
|
-
if (!/\.(?:cjs|mjs|js)$/.test(input)) continue;
|
|
9352
|
-
const sourceFile = path25.isAbsolute(input) ? input : path25.resolve(projectRoot, input);
|
|
9353
|
-
if (!fs29.existsSync(sourceFile) || !fs29.statSync(sourceFile).isFile())
|
|
9354
|
-
continue;
|
|
9355
|
-
const source = fs29.readFileSync(sourceFile, "utf8");
|
|
9356
|
-
const ast = parseJavaScript(source);
|
|
9357
|
-
if (!ast) continue;
|
|
9358
|
-
const analysis = analyzeSource(ast);
|
|
9359
|
-
const classify = (node) => classifyLayoutPath(sourceFile, node, analysis);
|
|
9360
|
-
walkSimple(ast, {
|
|
9361
|
-
CallExpression(node) {
|
|
9362
|
-
const method = fsMethod(node.callee, analysis);
|
|
9363
|
-
const forkMethod = builtinMethod(
|
|
9364
|
-
node.callee,
|
|
9365
|
-
analysis,
|
|
9366
|
-
"child_process"
|
|
9367
|
-
);
|
|
9368
|
-
if (forkMethod === "fork") {
|
|
9369
|
-
const target = node.arguments[0];
|
|
9370
|
-
const layout = classify(target);
|
|
9371
|
-
if (staticString(target, analysis.strings) !== void 0 || layout.kind === "static") {
|
|
9372
|
-
throw new Error(
|
|
9373
|
-
`static child runtime entry is unsupported: ${sourceFile}`
|
|
9374
|
-
);
|
|
9375
|
-
}
|
|
9376
|
-
if (layout.kind === "unsafe") {
|
|
9377
|
-
throw new Error(
|
|
9378
|
-
`child runtime entry has an unknown bundle-relative path: ${sourceFile}`
|
|
9379
|
-
);
|
|
9380
|
-
}
|
|
9381
|
-
return;
|
|
9382
|
-
}
|
|
9383
|
-
if (method && COPYABLE_FS_METHODS.has(method)) {
|
|
9384
|
-
const layout = classify(node.arguments[0]);
|
|
9385
|
-
if (layout.kind === "none") return;
|
|
9386
|
-
if (layout.kind === "unsafe") {
|
|
9387
|
-
throw new Error(
|
|
9388
|
-
`runtime asset has an unknown bundle-relative path: ${sourceFile}`
|
|
9389
|
-
);
|
|
9390
|
-
}
|
|
9391
|
-
if (!readOptionsAreSafe(method, node, analysis)) {
|
|
9392
|
-
throw new Error(
|
|
9393
|
-
`runtime filesystem read options are not statically read-only: ${sourceFile}`
|
|
9394
|
-
);
|
|
9395
|
-
}
|
|
9396
|
-
if (!canRewritePayloadPath(analysis)) {
|
|
9397
|
-
throw new Error(
|
|
9398
|
-
`runtime asset path cannot be rewritten without capturing a local binding: ${sourceFile}`
|
|
9399
|
-
);
|
|
9400
|
-
}
|
|
9401
|
-
candidates.push(layout.asset);
|
|
9402
|
-
return;
|
|
9403
|
-
}
|
|
9404
|
-
if (method && NON_COPYABLE_FS_METHODS.has(method)) {
|
|
9405
|
-
if (node.arguments.some((argument) => classify(argument).kind !== "none")) {
|
|
9406
|
-
throw new Error(
|
|
9407
|
-
`runtime filesystem operation cannot preserve a bundle-relative path: ${sourceFile}`
|
|
9408
|
-
);
|
|
9409
|
-
}
|
|
9410
|
-
return;
|
|
9411
|
-
}
|
|
9412
|
-
if (isDirectRequire(node.callee, analysis) || isRequireResolve(node.callee, analysis) || isLayoutHelperCall(node.callee, analysis)) {
|
|
9413
|
-
return;
|
|
9414
|
-
}
|
|
9415
|
-
if (node.arguments.some((argument) => classify(argument).kind !== "none")) {
|
|
9416
|
-
throw new Error(
|
|
9417
|
-
`unknown runtime call observes a bundle-relative path: ${sourceFile}`
|
|
9418
|
-
);
|
|
9419
|
-
}
|
|
9420
|
-
},
|
|
9421
|
-
NewExpression(node) {
|
|
9422
|
-
const workerMethod = builtinMethod(
|
|
9423
|
-
node.callee,
|
|
9424
|
-
analysis,
|
|
9425
|
-
"worker_threads"
|
|
9426
|
-
);
|
|
9427
|
-
const isWorker = workerMethod === "Worker";
|
|
9428
|
-
const isSharedWorker = isUnshadowedIdentifier(
|
|
9429
|
-
node.callee,
|
|
9430
|
-
"SharedWorker",
|
|
9431
|
-
analysis
|
|
9432
|
-
);
|
|
9433
|
-
const isUrl = isUnshadowedIdentifier(node.callee, "URL", analysis) || builtinMethod(node.callee, analysis, "url") === "URL";
|
|
9434
|
-
if (isUrl) return;
|
|
9435
|
-
if (!isWorker && !isSharedWorker) {
|
|
9436
|
-
if (node.arguments.some((argument) => classify(argument).kind !== "none")) {
|
|
9437
|
-
throw new Error(
|
|
9438
|
-
`unknown runtime constructor observes a bundle-relative path: ${sourceFile}`
|
|
9439
|
-
);
|
|
9440
|
-
}
|
|
9441
|
-
return;
|
|
9442
|
-
}
|
|
9443
|
-
const options = node.arguments[1];
|
|
9444
|
-
const evaluatesSource = Boolean(
|
|
9445
|
-
isWorker && options?.type === "ObjectExpression" && options.properties?.some(
|
|
9446
|
-
(property) => property.type === "Property" && (property.key?.type === "Identifier" && property.key.name === "eval" || property.key?.type === "Literal" && property.key.value === "eval") && property.value?.type === "Literal" && property.value.value === true
|
|
9447
|
-
)
|
|
9448
|
-
);
|
|
9449
|
-
if (evaluatesSource) return;
|
|
9450
|
-
const target = node.arguments[0];
|
|
9451
|
-
const layout = classify(target);
|
|
9452
|
-
if (staticString(target, analysis.strings) !== void 0 || layout.kind === "static") {
|
|
9453
|
-
throw new Error(
|
|
9454
|
-
`static child runtime entry is unsupported: ${sourceFile}`
|
|
9455
|
-
);
|
|
9456
|
-
}
|
|
9457
|
-
if (layout.kind === "unsafe") {
|
|
9458
|
-
throw new Error(
|
|
9459
|
-
`child runtime entry has an unknown bundle-relative path: ${sourceFile}`
|
|
9460
|
-
);
|
|
9461
|
-
}
|
|
9462
|
-
}
|
|
9463
|
-
});
|
|
9464
|
-
}
|
|
9465
|
-
const normalized = normalizeAssetCandidates(candidates);
|
|
9466
|
-
const assets = planAssetFiles(
|
|
9467
|
-
projectRoot,
|
|
9468
|
-
normalized,
|
|
9469
|
-
reserved,
|
|
9470
|
-
limits,
|
|
9471
|
-
budget
|
|
9472
|
-
);
|
|
9473
|
-
copyPlannedAssets(projectRoot, assets, payloadRoot);
|
|
9474
|
-
}
|
|
9475
|
-
function isBareSpecifier(specifier) {
|
|
9476
|
-
return !specifier.startsWith(".") && !specifier.startsWith("/") && !specifier.startsWith("file:");
|
|
9477
|
-
}
|
|
9478
|
-
function expressionSignalsRelativeSpecifier(node, analysis) {
|
|
9479
|
-
const known = staticString(node, analysis.strings);
|
|
9480
|
-
if (known !== void 0)
|
|
9481
|
-
return known.startsWith(".") || known.startsWith("/");
|
|
9482
|
-
if (node?.type === "TemplateLiteral") {
|
|
9483
|
-
return (node.quasis[0]?.value?.cooked ?? "").startsWith(".");
|
|
9484
|
-
}
|
|
9485
|
-
if (node?.type === "BinaryExpression" && node.operator === "+") {
|
|
9486
|
-
return expressionSignalsRelativeSpecifier(node.left, analysis) || expressionSignalsRelativeSpecifier(node.right, analysis);
|
|
9487
|
-
}
|
|
9488
|
-
if (node?.type === "CallExpression" && builtinMethod(node.callee, analysis, "path") === "join") {
|
|
9489
|
-
const prefix = [];
|
|
9490
|
-
for (const argument of node.arguments) {
|
|
9491
|
-
const value = staticString(argument, analysis.strings);
|
|
9492
|
-
if (value === void 0) break;
|
|
9493
|
-
prefix.push(value);
|
|
9494
|
-
}
|
|
9495
|
-
if (prefix.length > 0) {
|
|
9496
|
-
const normalized = path25.join(...prefix);
|
|
9497
|
-
return normalized === "." || normalized === ".." || normalized.startsWith(`.${path25.sep}`) || normalized.startsWith(`..${path25.sep}`) || path25.isAbsolute(normalized);
|
|
9498
|
-
}
|
|
9499
|
-
}
|
|
9500
|
-
return false;
|
|
9501
|
-
}
|
|
9502
|
-
function hasUnknownBundleRelativeReference(file) {
|
|
9503
|
-
if (!/\.(?:cjs|mjs|js)$/.test(file) || !fs29.existsSync(file)) return false;
|
|
9504
|
-
const source = fs29.readFileSync(file, "utf8");
|
|
9505
|
-
const ast = parseJavaScript(source);
|
|
9506
|
-
if (!ast) return false;
|
|
9507
|
-
const analysis = analyzeSource(ast);
|
|
9508
|
-
let unsafe = false;
|
|
9509
|
-
walkSimple(ast, {
|
|
9510
|
-
CallExpression(node) {
|
|
9511
|
-
const directRequire = isDirectRequire(node.callee, analysis);
|
|
9512
|
-
const requireResolve = isRequireResolve(node.callee, analysis);
|
|
9513
|
-
if (directRequire || requireResolve) {
|
|
9514
|
-
const layout = classifyLayoutPath(file, node.arguments[0], analysis);
|
|
9515
|
-
const staticTarget = staticModuleReference(
|
|
9516
|
-
file,
|
|
9517
|
-
node.arguments[0],
|
|
9518
|
-
analysis
|
|
9519
|
-
);
|
|
9520
|
-
if (staticTarget === void 0 && (layout.kind === "unsafe" || expressionSignalsRelativeSpecifier(node.arguments[0], analysis))) {
|
|
9521
|
-
unsafe = true;
|
|
9522
|
-
}
|
|
9523
|
-
return;
|
|
9524
|
-
}
|
|
9525
|
-
const method = fsMethod(node.callee, analysis);
|
|
9526
|
-
if (isFsPathMethod(method)) {
|
|
9527
|
-
const layouts = node.arguments.map(
|
|
9528
|
-
(argument) => classifyLayoutPath(file, argument, analysis)
|
|
9529
|
-
);
|
|
9530
|
-
const pathLayout = layouts[0] ?? { kind: "none" };
|
|
9531
|
-
if (pathLayout.kind === "unsafe" || method !== void 0 && COPYABLE_FS_METHODS.has(method) && pathLayout.kind === "static" && (!readOptionsAreSafe(method, node, analysis) || !canRewritePayloadPath(analysis)) || method !== void 0 && NON_COPYABLE_FS_METHODS.has(method) && layouts.some((layout) => layout.kind !== "none")) {
|
|
9532
|
-
unsafe = true;
|
|
9533
|
-
}
|
|
9534
|
-
return;
|
|
9535
|
-
}
|
|
9536
|
-
if (isLayoutHelperCall(node.callee, analysis)) return;
|
|
9537
|
-
if (node.arguments.some(
|
|
9538
|
-
(argument) => classifyLayoutPath(file, argument, analysis).kind !== "none"
|
|
9539
|
-
)) {
|
|
9540
|
-
unsafe = true;
|
|
9541
|
-
}
|
|
9542
|
-
},
|
|
9543
|
-
ImportExpression(node) {
|
|
9544
|
-
const layout = classifyLayoutPath(file, node.source, analysis);
|
|
9545
|
-
const staticTarget = staticModuleReference(file, node.source, analysis);
|
|
9546
|
-
if (staticTarget === void 0 && (layout.kind === "unsafe" || expressionSignalsRelativeSpecifier(node.source, analysis))) {
|
|
9547
|
-
unsafe = true;
|
|
9548
|
-
}
|
|
9549
|
-
}
|
|
9550
|
-
});
|
|
9551
|
-
return unsafe;
|
|
9552
|
-
}
|
|
9553
|
-
function hasUnbundleableNativeEdge(file) {
|
|
9554
|
-
if (!/\.(?:cjs|mjs|js)$/.test(file) || !fs29.existsSync(file)) return false;
|
|
9555
|
-
const source = fs29.readFileSync(file, "utf8");
|
|
9556
|
-
const ast = parseJavaScript(source);
|
|
9557
|
-
if (!ast) return false;
|
|
9558
|
-
const analysis = analyzeSource(ast);
|
|
9559
|
-
let nativeEdge = false;
|
|
9560
|
-
walkSimple(ast, {
|
|
9561
|
-
CallExpression(node) {
|
|
9562
|
-
if (!isDirectRequire(node.callee, analysis)) {
|
|
9563
|
-
return;
|
|
9564
|
-
}
|
|
9565
|
-
const specifier = staticString(node.arguments[0], analysis.strings);
|
|
9566
|
-
if (specifier?.endsWith(".node")) nativeEdge = true;
|
|
9567
|
-
}
|
|
9568
|
-
});
|
|
9569
|
-
return nativeEdge;
|
|
9570
|
-
}
|
|
9571
|
-
function runtimeBoundaryPlugin(projectRoot) {
|
|
9572
|
-
const decisions = /* @__PURE__ */ new Map();
|
|
9573
|
-
const realProjectRoot = fs29.realpathSync(projectRoot);
|
|
9574
|
-
const resolutionError = (candidate) => {
|
|
9575
|
-
if (!isInside(projectRoot, candidate)) {
|
|
9576
|
-
return `runtime dependency resolved outside project root: ${candidate}`;
|
|
9577
|
-
}
|
|
9578
|
-
let current = projectRoot;
|
|
9579
|
-
try {
|
|
9580
|
-
for (const segment of path25.relative(projectRoot, candidate).split(path25.sep)) {
|
|
9581
|
-
if (!segment) continue;
|
|
9582
|
-
current = path25.join(current, segment);
|
|
9583
|
-
if (fs29.lstatSync(current).isSymbolicLink()) {
|
|
9584
|
-
return `runtime dependency symlink is unsupported: ${candidate}`;
|
|
9585
|
-
}
|
|
9586
|
-
}
|
|
9587
|
-
const realCandidate = fs29.realpathSync(candidate);
|
|
9588
|
-
if (!isInside(realProjectRoot, realCandidate)) {
|
|
9589
|
-
return `runtime dependency resolved outside project root: ${candidate}`;
|
|
9590
|
-
}
|
|
9591
|
-
} catch (error) {
|
|
9592
|
-
return `runtime dependency path is unavailable: ${candidate}: ${error instanceof Error ? error.message : String(error)}`;
|
|
9593
|
-
}
|
|
9594
|
-
return void 0;
|
|
9595
|
-
};
|
|
9596
|
-
return {
|
|
9597
|
-
name: "miaoda-runtime-owner-boundary",
|
|
9598
|
-
setup(esbuild) {
|
|
9599
|
-
esbuild.onResolve({ filter: /.*/ }, async (args) => {
|
|
9600
|
-
const pluginData = args.pluginData;
|
|
9601
|
-
if (pluginData?.miaodaBoundarySkip || args.kind === "entry-point")
|
|
9602
|
-
return void 0;
|
|
9603
|
-
if (BUILTINS.has(args.path)) return { path: args.path, external: true };
|
|
9604
|
-
const importerOwner = args.importer ? findPackageRoot(projectRoot, args.importer) : void 0;
|
|
9605
|
-
const resolved = await esbuild.resolve(args.path, {
|
|
9606
|
-
importer: args.importer,
|
|
9607
|
-
kind: args.kind,
|
|
9608
|
-
namespace: args.namespace,
|
|
9609
|
-
resolveDir: isBareSpecifier(args.path) && !importerOwner ? projectRoot : args.resolveDir,
|
|
9610
|
-
pluginData: { miaodaBoundarySkip: true }
|
|
9611
|
-
});
|
|
9612
|
-
if (resolved.errors.length > 0) {
|
|
9613
|
-
return isBareSpecifier(args.path) ? { path: args.path, external: true } : resolved;
|
|
9614
|
-
}
|
|
9615
|
-
if (resolved.external) return resolved;
|
|
9616
|
-
const unsafeResolution = resolutionError(resolved.path);
|
|
9617
|
-
if (unsafeResolution) {
|
|
9618
|
-
return {
|
|
9619
|
-
errors: [
|
|
9620
|
-
{
|
|
9621
|
-
text: `${unsafeResolution}: ${args.path}`
|
|
9622
|
-
}
|
|
9623
|
-
]
|
|
9624
|
-
};
|
|
9625
|
-
}
|
|
9626
|
-
const owner = findPackageRoot(projectRoot, resolved.path);
|
|
9627
|
-
if (!owner) {
|
|
9628
|
-
if (hasUnknownBundleRelativeReference(resolved.path)) {
|
|
9629
|
-
return {
|
|
9630
|
-
errors: [
|
|
9631
|
-
{
|
|
9632
|
-
text: `application runtime has an unknown bundle-relative reference: ${resolved.path}`
|
|
9633
|
-
}
|
|
9634
|
-
]
|
|
9635
|
-
};
|
|
9636
|
-
}
|
|
9637
|
-
return resolved;
|
|
9638
|
-
}
|
|
9639
|
-
const decisionKey = `${owner}\0${resolved.path}`;
|
|
9640
|
-
let external = decisions.get(decisionKey);
|
|
9641
|
-
if (external === void 0) {
|
|
9642
|
-
external = hasUnknownBundleRelativeReference(resolved.path) || hasUnbundleableNativeEdge(resolved.path);
|
|
9643
|
-
decisions.set(decisionKey, external);
|
|
9644
|
-
}
|
|
9645
|
-
if (!external) return resolved;
|
|
9646
|
-
if (!isBareSpecifier(args.path)) {
|
|
9647
|
-
return {
|
|
9648
|
-
errors: [
|
|
9649
|
-
{
|
|
9650
|
-
text: `runtime owner with an unknown bundle-relative reference must be imported by package name: ${args.path}`
|
|
9651
|
-
}
|
|
9652
|
-
]
|
|
9653
|
-
};
|
|
9654
|
-
}
|
|
9655
|
-
if (importerOwner) {
|
|
9656
|
-
const rootResolved = await esbuild.resolve(args.path, {
|
|
9657
|
-
kind: args.kind,
|
|
9658
|
-
namespace: "file",
|
|
9659
|
-
resolveDir: projectRoot,
|
|
9660
|
-
pluginData: { miaodaBoundarySkip: true }
|
|
9661
|
-
});
|
|
9662
|
-
let resolvesToSameRootPackage = false;
|
|
9663
|
-
if (rootResolved.errors.length === 0 && !rootResolved.external) {
|
|
9664
|
-
try {
|
|
9665
|
-
resolvesToSameRootPackage = fs29.realpathSync(rootResolved.path) === fs29.realpathSync(resolved.path);
|
|
9666
|
-
} catch {
|
|
9667
|
-
resolvesToSameRootPackage = false;
|
|
9668
|
-
}
|
|
9669
|
-
}
|
|
9670
|
-
if (!resolvesToSameRootPackage) {
|
|
9671
|
-
return {
|
|
9672
|
-
errors: [
|
|
9673
|
-
{
|
|
9674
|
-
text: `runtime owner with an unknown bundle-relative reference must resolve to the same package from the bundle root: ${args.path}`
|
|
9675
|
-
}
|
|
9676
|
-
]
|
|
9677
|
-
};
|
|
9678
|
-
}
|
|
9679
|
-
}
|
|
9680
|
-
return { path: args.path, external: true };
|
|
9681
|
-
});
|
|
9682
|
-
}
|
|
9683
|
-
};
|
|
9684
|
-
}
|
|
9685
|
-
function staticActionPluginRegistry(projectRoot, plugins) {
|
|
9686
|
-
if (plugins.length === 0) {
|
|
9687
|
-
return { name: "miaoda-static-action-plugin-registry", setup() {
|
|
9688
|
-
} };
|
|
9689
|
-
}
|
|
9690
|
-
const projectRequire = createRequire3(path25.join(projectRoot, "package.json"));
|
|
9691
|
-
const capabilityEntry = projectRequire.resolve(ACTION_PLUGIN_LOADER_PACKAGE);
|
|
9692
|
-
const imports = plugins.map(
|
|
9693
|
-
(plugin, index) => `const __miaodaPlugin${index} = require(${JSON.stringify(plugin.entry)});`
|
|
9694
|
-
);
|
|
9695
|
-
const registrations = plugins.map(
|
|
9696
|
-
(plugin, index) => `{ pluginKey: ${JSON.stringify(plugin.specifier)}, manifest: ${JSON.stringify(
|
|
9697
|
-
plugin.manifest
|
|
9698
|
-
)}, module: __miaodaPlugin${index} }`
|
|
9699
|
-
);
|
|
9700
|
-
const contents = [
|
|
9701
|
-
`const __miaodaCapability = require(${JSON.stringify(capabilityEntry)});`,
|
|
9702
|
-
"module.exports = __miaodaCapability;",
|
|
9703
|
-
...imports,
|
|
9704
|
-
`const __miaodaRegistrations = [${registrations.join(",")}];`,
|
|
9705
|
-
"if (typeof __miaodaCapability.registerStaticActionPlugins !== 'function') throw new Error('MIAODA_STATIC_ACTION_PLUGIN_REGISTRY_UNSUPPORTED');",
|
|
9706
|
-
"__miaodaCapability.registerStaticActionPlugins(__miaodaRegistrations);"
|
|
9707
|
-
].join("\n");
|
|
9708
|
-
return {
|
|
9709
|
-
name: "miaoda-static-action-plugin-registry",
|
|
9710
|
-
setup(esbuild) {
|
|
9711
|
-
esbuild.onResolve({ filter: /^@lark-apaas\/nestjs-capability$/ }, () => ({
|
|
9712
|
-
path: ACTION_PLUGIN_LOADER_PACKAGE,
|
|
9713
|
-
namespace: "miaoda-static-action-plugin-registry"
|
|
9714
|
-
}));
|
|
9715
|
-
esbuild.onLoad(
|
|
9716
|
-
{ filter: /.*/, namespace: "miaoda-static-action-plugin-registry" },
|
|
9717
|
-
() => ({ contents, loader: "js", resolveDir: projectRoot })
|
|
9718
|
-
);
|
|
9719
|
-
}
|
|
9720
|
-
};
|
|
9721
|
-
}
|
|
9722
|
-
function startupEntryPlugin(entryFile, hasActionPlugins) {
|
|
9723
|
-
const contents = hasActionPlugins ? [
|
|
9724
|
-
`require(${JSON.stringify(ACTION_PLUGIN_LOADER_PACKAGE)});`,
|
|
9725
|
-
`require(${JSON.stringify(entryFile)});`
|
|
9726
|
-
].join("\n") : `require(${JSON.stringify(entryFile)});`;
|
|
9727
|
-
return {
|
|
9728
|
-
name: "miaoda-startup-entry",
|
|
9729
|
-
setup(esbuild) {
|
|
9730
|
-
esbuild.onResolve({ filter: /^miaoda:startup-entry$/ }, () => ({
|
|
9731
|
-
path: "miaoda:startup-entry",
|
|
9732
|
-
namespace: "miaoda-startup-entry"
|
|
9733
|
-
}));
|
|
9734
|
-
esbuild.onLoad(
|
|
9735
|
-
{ filter: /.*/, namespace: "miaoda-startup-entry" },
|
|
9736
|
-
() => ({ contents, loader: "js", resolveDir: path25.dirname(entryFile) })
|
|
9737
|
-
);
|
|
9738
|
-
}
|
|
9739
|
-
};
|
|
9740
|
-
}
|
|
9741
|
-
async function buildStaticStartupPayload(options) {
|
|
9742
|
-
const state = readDependencyState(options.projectRoot);
|
|
9743
|
-
fs29.rmSync(options.outputFile, { force: true });
|
|
9744
|
-
const result = await build({
|
|
9745
|
-
absWorkingDir: options.projectRoot,
|
|
9746
|
-
entryPoints: ["miaoda:startup-entry"],
|
|
9747
|
-
outfile: options.outputFile,
|
|
9748
|
-
bundle: true,
|
|
9749
|
-
platform: "node",
|
|
9750
|
-
format: "cjs",
|
|
9751
|
-
target: `node${process.versions.node.split(".")[0]}`,
|
|
9752
|
-
conditions: ["miaoda-startup"],
|
|
9753
|
-
treeShaking: true,
|
|
9754
|
-
keepNames: true,
|
|
9755
|
-
legalComments: "none",
|
|
9756
|
-
logLevel: "silent",
|
|
9757
|
-
preserveSymlinks: true,
|
|
9758
|
-
sourcemap: false,
|
|
9759
|
-
metafile: true,
|
|
9760
|
-
define: {
|
|
9761
|
-
"import.meta.url": "__miaoda_startup_import_meta_url__"
|
|
9762
|
-
},
|
|
9763
|
-
banner: {
|
|
9764
|
-
js: 'const __miaoda_startup_import_meta_url__ = require("node:url").pathToFileURL(__filename).href;'
|
|
9765
|
-
},
|
|
9766
|
-
plugins: [
|
|
9767
|
-
startupEntryPlugin(options.entryFile, state.plugins.length > 0),
|
|
9768
|
-
staticActionPluginRegistry(options.projectRoot, state.plugins),
|
|
9769
|
-
staticModuleReferencePlugin(),
|
|
9770
|
-
runtimeBoundaryPlugin(options.projectRoot)
|
|
9771
|
-
]
|
|
9772
|
-
});
|
|
9773
|
-
if (!result.metafile) throw new Error("esbuild metafile missing");
|
|
9774
|
-
const nativeInputs = Object.keys(result.metafile.inputs).filter(
|
|
9775
|
-
(input) => input.endsWith(".node")
|
|
9776
|
-
);
|
|
9777
|
-
if (nativeInputs.length > 0) {
|
|
9778
|
-
throw new Error(
|
|
9779
|
-
`native addons are unsupported: ${nativeInputs.join(", ")}`
|
|
9780
|
-
);
|
|
9781
|
-
}
|
|
9782
|
-
materializeStaticAssets(
|
|
9783
|
-
options.projectRoot,
|
|
9784
|
-
result.metafile,
|
|
9785
|
-
options.payloadRoot,
|
|
9786
|
-
options.limits
|
|
9787
|
-
);
|
|
9788
|
-
}
|
|
9789
|
-
|
|
9790
|
-
// src/commands/build/server-startup-bundle.handler.ts
|
|
9791
|
-
var SCHEMA_VERSION = 7;
|
|
9792
|
-
var STARTUP_BUNDLE_ENROLLMENT_SCHEMA_VERSION = 1;
|
|
9793
|
-
var STARTUP_BUNDLE_FILE = "payload/server.bundle.cjs";
|
|
9794
|
-
var MAX_GENERATION_FILES = 5e4;
|
|
9795
|
-
var MAX_GENERATION_BYTES = 256 * 1024 * 1024;
|
|
9796
|
-
var MAX_GENERATION_FILE_BYTES = 64 * 1024 * 1024;
|
|
9797
|
-
var MAX_FILE_INDEX_BYTES = 16 * 1024 * 1024;
|
|
9798
|
-
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
9799
|
-
var MAX_PACKAGE_MANIFEST_BYTES = 1024 * 1024;
|
|
9800
|
-
var MAX_READINESS_RESPONSE_BYTES = 64 * 1024;
|
|
9801
|
-
var ROOT_SOURCE_HASH_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
9802
|
-
".agents",
|
|
9803
|
-
".claude",
|
|
9804
|
-
".git",
|
|
9805
|
-
".miaoda-cache",
|
|
9806
|
-
".miaoda-runtime",
|
|
9807
|
-
".turbo",
|
|
9808
|
-
"coverage",
|
|
9809
|
-
"dist",
|
|
9810
|
-
"logs"
|
|
9811
|
-
]);
|
|
9812
|
-
var probeIdentities = /* @__PURE__ */ new WeakMap();
|
|
9813
|
-
function sha256(contents) {
|
|
9814
|
-
return createHash("sha256").update(contents).digest("hex");
|
|
9815
|
-
}
|
|
9816
|
-
function stableJson(value) {
|
|
9817
|
-
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
9818
|
-
if (value && typeof value === "object") {
|
|
9819
|
-
return `{${Object.entries(value).sort(([left], [right]) => compareCodeUnits(left, right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
9820
|
-
}
|
|
9821
|
-
return JSON.stringify(value);
|
|
9822
|
-
}
|
|
9823
|
-
function compareCodeUnits(left, right) {
|
|
9824
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
9825
|
-
}
|
|
9826
|
-
function isCapabilityReadinessPayload(contents, expectedCapabilityIds = []) {
|
|
9827
|
-
if (contents.length > MAX_READINESS_RESPONSE_BYTES) return false;
|
|
9828
|
-
try {
|
|
9829
|
-
const payload = JSON.parse(contents.toString("utf8"));
|
|
9830
|
-
if (!(payload && typeof payload === "object" && payload.status_code === "0" && payload.data && typeof payload.data === "object" && !Array.isArray(payload.data) && Array.isArray(payload.data.capabilities))) {
|
|
9831
|
-
return false;
|
|
9832
|
-
}
|
|
9833
|
-
const actualIds = new Set(
|
|
9834
|
-
(payload.data.capabilities ?? []).map(
|
|
9835
|
-
(capability) => capability && typeof capability === "object" ? capability.id : void 0
|
|
9836
|
-
).filter((id) => typeof id === "string" && id.length > 0)
|
|
9837
|
-
);
|
|
9838
|
-
return expectedCapabilityIds.every((id) => actualIds.has(id));
|
|
9839
|
-
} catch {
|
|
9840
|
-
return false;
|
|
9841
|
-
}
|
|
9842
|
-
}
|
|
9843
|
-
function resolveExpectedCapabilityIds(projectRoot) {
|
|
9844
|
-
const capabilitiesRoot = path26.join(projectRoot, "server", "capabilities");
|
|
9845
|
-
if (!fs30.existsSync(capabilitiesRoot)) return [];
|
|
9846
|
-
const rootStats = fs30.lstatSync(capabilitiesRoot);
|
|
9847
|
-
if (rootStats.isSymbolicLink() || !rootStats.isDirectory()) {
|
|
9848
|
-
throw new Error("workspace capability directory is unsafe");
|
|
9849
|
-
}
|
|
9850
|
-
const realRoot = fs30.realpathSync(capabilitiesRoot);
|
|
9851
|
-
if (!isInside2(projectRoot, realRoot)) {
|
|
9852
|
-
throw new Error("workspace capability directory escaped project root");
|
|
9853
|
-
}
|
|
9854
|
-
const ids = /* @__PURE__ */ new Set();
|
|
9855
|
-
for (const entry of fs30.readdirSync(realRoot, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
9856
|
-
if (!entry.name.endsWith(".json") || entry.name === "capabilities.json") {
|
|
9857
|
-
continue;
|
|
9858
|
-
}
|
|
9859
|
-
const file = path26.join(realRoot, entry.name);
|
|
9860
|
-
if (entry.isSymbolicLink() || !entry.isFile()) {
|
|
9861
|
-
throw new Error(`workspace capability file is unsafe: ${entry.name}`);
|
|
9862
|
-
}
|
|
9863
|
-
const config = readJsonObject(file, `workspace capability ${entry.name}`);
|
|
9864
|
-
if (typeof config.id !== "string" || config.id.length === 0 || config.id.length > 256) {
|
|
9865
|
-
throw new Error(
|
|
9866
|
-
`workspace capability file has no valid id: ${entry.name}`
|
|
9867
|
-
);
|
|
9868
|
-
}
|
|
9869
|
-
if (ids.has(config.id)) {
|
|
9870
|
-
throw new Error(`workspace capability id is duplicated: ${config.id}`);
|
|
9871
|
-
}
|
|
9872
|
-
ids.add(config.id);
|
|
9873
|
-
}
|
|
9874
|
-
return [...ids].sort();
|
|
9875
|
-
}
|
|
9876
|
-
function isInside2(root, candidate) {
|
|
9877
|
-
const relative = path26.relative(root, candidate);
|
|
9878
|
-
return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
|
|
9879
|
-
}
|
|
9880
|
-
function toPosixRelative(root, file) {
|
|
9881
|
-
return path26.relative(root, file).split(path26.sep).join("/");
|
|
9882
|
-
}
|
|
9883
|
-
function assertNoSymlinkComponents(root, candidate, message) {
|
|
9884
|
-
if (!isInside2(root, candidate)) throw new Error(`${message}: escaped root`);
|
|
9885
|
-
let current = root;
|
|
9886
|
-
for (const segment of path26.relative(root, candidate).split(path26.sep)) {
|
|
9887
|
-
if (!segment) continue;
|
|
9888
|
-
current = path26.join(current, segment);
|
|
9889
|
-
if (fs30.existsSync(current) && fs30.lstatSync(current).isSymbolicLink()) {
|
|
9890
|
-
throw new Error(message);
|
|
9891
|
-
}
|
|
9892
|
-
}
|
|
9893
|
-
}
|
|
9894
|
-
function walkSourceFiles(root, directory, output) {
|
|
9895
|
-
if (!fs30.existsSync(directory)) return;
|
|
9896
|
-
for (const entry of fs30.readdirSync(directory, { withFileTypes: true })) {
|
|
9897
|
-
if (entry.name === "node_modules" || directory === root && ROOT_SOURCE_HASH_IGNORED_DIRECTORIES.has(entry.name)) {
|
|
9898
|
-
continue;
|
|
9899
|
-
}
|
|
9900
|
-
const candidate = path26.join(directory, entry.name);
|
|
9901
|
-
if (entry.isSymbolicLink()) {
|
|
9902
|
-
throw new Error(
|
|
9903
|
-
`source symlink is unsupported: ${toPosixRelative(root, candidate)}`
|
|
9904
|
-
);
|
|
9905
|
-
}
|
|
9906
|
-
if (entry.isDirectory()) walkSourceFiles(root, candidate, output);
|
|
9907
|
-
else if (entry.isFile()) output.push(candidate);
|
|
9908
|
-
else {
|
|
9909
|
-
throw new Error(
|
|
9910
|
-
`source special file is unsupported: ${toPosixRelative(root, candidate)}`
|
|
9911
|
-
);
|
|
9912
|
-
}
|
|
9913
|
-
}
|
|
9914
|
-
}
|
|
9915
|
-
function workspaceSourceSha256(projectRoot) {
|
|
9916
|
-
const files = [];
|
|
9917
|
-
walkSourceFiles(projectRoot, projectRoot, files);
|
|
9918
|
-
const hash = createHash("sha256");
|
|
9919
|
-
for (const file of files.sort()) {
|
|
9920
|
-
hash.update(toPosixRelative(projectRoot, file));
|
|
9921
|
-
hash.update("\0");
|
|
9922
|
-
updateHashFromFile(hash, file);
|
|
9923
|
-
hash.update("\0");
|
|
9924
|
-
}
|
|
9925
|
-
return hash.digest("hex");
|
|
9926
|
-
}
|
|
9927
|
-
function updateHashFromFile(hash, file) {
|
|
9928
|
-
const descriptor = fs30.openSync(file, "r");
|
|
9929
|
-
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
9930
|
-
try {
|
|
9931
|
-
const before = fs30.fstatSync(descriptor);
|
|
9932
|
-
let position = 0;
|
|
9933
|
-
while (position < before.size) {
|
|
9934
|
-
const bytesRead = fs30.readSync(
|
|
9935
|
-
descriptor,
|
|
9936
|
-
buffer,
|
|
9937
|
-
0,
|
|
9938
|
-
Math.min(buffer.length, before.size - position),
|
|
9939
|
-
position
|
|
9940
|
-
);
|
|
9941
|
-
if (bytesRead === 0) break;
|
|
9942
|
-
hash.update(buffer.subarray(0, bytesRead));
|
|
9943
|
-
position += bytesRead;
|
|
9944
|
-
}
|
|
9945
|
-
const after = fs30.fstatSync(descriptor);
|
|
9946
|
-
if (position !== before.size || after.size !== before.size || after.mtimeMs !== before.mtimeMs) {
|
|
9947
|
-
throw new Error(`source changed while hashing: ${file}`);
|
|
9948
|
-
}
|
|
9949
|
-
} finally {
|
|
9950
|
-
fs30.closeSync(descriptor);
|
|
9951
|
-
}
|
|
9952
|
-
}
|
|
9953
|
-
function runtimeIdentity() {
|
|
9954
|
-
const report = process.report?.getReport();
|
|
9955
|
-
const header = report?.header;
|
|
9956
|
-
return {
|
|
9957
|
-
nodeVersion: process.versions.node,
|
|
9958
|
-
nodeModulesAbi: process.versions.modules,
|
|
9959
|
-
platform: process.platform,
|
|
9960
|
-
arch: process.arch,
|
|
9961
|
-
libc: typeof header?.glibcVersionRuntime === "string" ? `glibc-${header.glibcVersionRuntime}` : process.platform === "linux" ? "linux-unknown-libc" : "not-applicable"
|
|
9962
|
-
};
|
|
9963
|
-
}
|
|
9964
|
-
function resolveEntry(projectRoot, requested) {
|
|
9965
|
-
const entry = path26.resolve(projectRoot, requested);
|
|
9966
|
-
if (!isInside2(projectRoot, entry))
|
|
9967
|
-
throw new Error("compiled entry escaped project root");
|
|
9968
|
-
if (!fs30.existsSync(entry))
|
|
9969
|
-
throw new Error(`compiled entry is not a file: ${entry}`);
|
|
9970
|
-
if (fs30.lstatSync(entry).isSymbolicLink()) {
|
|
9971
|
-
throw new Error("compiled entry symlink is unsupported");
|
|
9972
|
-
}
|
|
9973
|
-
const realEntry = fs30.realpathSync(entry);
|
|
9974
|
-
if (!isInside2(projectRoot, realEntry))
|
|
9975
|
-
throw new Error("compiled entry escaped project root");
|
|
9976
|
-
if (!fs30.statSync(realEntry).isFile())
|
|
9977
|
-
throw new Error(`compiled entry is not a file: ${entry}`);
|
|
9978
|
-
return realEntry;
|
|
9979
|
-
}
|
|
9980
|
-
function resolveNestTsconfig(projectRoot) {
|
|
9981
|
-
const nestConfigFile = path26.join(projectRoot, "nest-cli.json");
|
|
9982
|
-
let configured;
|
|
9983
|
-
if (fs30.existsSync(nestConfigFile)) {
|
|
9984
|
-
const nestConfig = JSON.parse(fs30.readFileSync(nestConfigFile, "utf8"));
|
|
9985
|
-
configured = nestConfig.compilerOptions?.tsConfigPath;
|
|
9986
|
-
if (configured !== void 0 && typeof configured !== "string") {
|
|
9987
|
-
throw new Error("nest-cli.json compilerOptions.tsConfigPath is invalid");
|
|
9988
|
-
}
|
|
9989
|
-
}
|
|
9990
|
-
const candidates = typeof configured === "string" ? [configured] : ["tsconfig.build.json", "tsconfig.node.json", "tsconfig.json"];
|
|
9991
|
-
const selected = candidates.map((candidate) => path26.resolve(projectRoot, candidate)).find((candidate) => fs30.existsSync(candidate));
|
|
9992
|
-
if (!selected) {
|
|
9993
|
-
throw new Error(`Nest TypeScript config missing: ${candidates.join(", ")}`);
|
|
9994
|
-
}
|
|
9995
|
-
if (!isInside2(projectRoot, selected))
|
|
9996
|
-
throw new Error("Nest TypeScript config escaped project root");
|
|
9997
|
-
if (fs30.lstatSync(selected).isSymbolicLink())
|
|
9998
|
-
throw new Error("Nest TypeScript config symlink is unsupported");
|
|
9999
|
-
const realConfig = fs30.realpathSync(selected);
|
|
10000
|
-
if (!isInside2(projectRoot, realConfig) || !fs30.statSync(realConfig).isFile()) {
|
|
10001
|
-
throw new Error("Nest TypeScript config escaped project root");
|
|
10002
|
-
}
|
|
10003
|
-
return realConfig;
|
|
10004
|
-
}
|
|
10005
|
-
function resolveNestCli(projectRoot) {
|
|
10006
|
-
const projectRequire = createRequire4(path26.join(projectRoot, "package.json"));
|
|
10007
|
-
try {
|
|
10008
|
-
return projectRequire.resolve("@nestjs/cli/bin/nest.js");
|
|
10009
|
-
} catch (directError) {
|
|
10010
|
-
try {
|
|
10011
|
-
const packageFile = projectRequire.resolve("@nestjs/cli/package.json");
|
|
10012
|
-
const packageJson = JSON.parse(fs30.readFileSync(packageFile, "utf8"));
|
|
10013
|
-
const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.nest;
|
|
10014
|
-
if (!relativeBin) throw new Error("@nestjs/cli has no nest binary");
|
|
10015
|
-
const cliFile = path26.resolve(path26.dirname(packageFile), relativeBin);
|
|
10016
|
-
if (!fs30.statSync(cliFile).isFile())
|
|
10017
|
-
throw new Error("@nestjs/cli nest binary is not a file");
|
|
10018
|
-
return cliFile;
|
|
10019
|
-
} catch (packageError) {
|
|
10020
|
-
throw new Error(
|
|
10021
|
-
`project @nestjs/cli is unavailable: ${packageError instanceof Error ? packageError.message : String(packageError)}; direct=${directError instanceof Error ? directError.message : String(directError)}`
|
|
10022
|
-
);
|
|
10023
|
-
}
|
|
10024
|
-
}
|
|
10025
|
-
}
|
|
10026
|
-
function readLinuxProcessIdentity(pid) {
|
|
10027
|
-
if (process.platform !== "linux") return void 0;
|
|
10028
|
-
try {
|
|
10029
|
-
const stat = fs30.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
10030
|
-
const commandEnd = stat.lastIndexOf(")");
|
|
10031
|
-
if (commandEnd < 0) return void 0;
|
|
10032
|
-
const fields = stat.slice(commandEnd + 1).trim().split(/\s+/);
|
|
10033
|
-
return /^\d+$/.test(fields[19] || "") && /^\d+$/.test(fields[2] || "") ? { processStartTicks: fields[19], processGroupId: Number(fields[2]) } : void 0;
|
|
10034
|
-
} catch {
|
|
10035
|
-
return void 0;
|
|
10036
|
-
}
|
|
10037
|
-
}
|
|
10038
|
-
function registerProbe(child) {
|
|
10039
|
-
if (!child.pid) throw new Error("isolated process pid unavailable");
|
|
10040
|
-
const identity = readLinuxProcessIdentity(child.pid);
|
|
10041
|
-
if (process.platform === "linux" && !identity) {
|
|
10042
|
-
child.kill("SIGKILL");
|
|
10043
|
-
throw new Error("isolated process birth identity unavailable");
|
|
10044
|
-
}
|
|
10045
|
-
probeIdentities.set(child, {
|
|
10046
|
-
pid: child.pid,
|
|
10047
|
-
processStartTicks: identity?.processStartTicks
|
|
10048
|
-
});
|
|
10049
|
-
}
|
|
10050
|
-
function probeGroupRunning(child) {
|
|
10051
|
-
const registered = probeIdentities.get(child);
|
|
10052
|
-
if (!registered) return false;
|
|
10053
|
-
if (process.platform === "linux") {
|
|
10054
|
-
const current = readLinuxProcessIdentity(registered.pid);
|
|
10055
|
-
if (current) {
|
|
10056
|
-
if (current.processStartTicks !== registered.processStartTicks || current.processGroupId !== registered.pid) {
|
|
10057
|
-
return false;
|
|
10058
|
-
}
|
|
10059
|
-
} else {
|
|
10060
|
-
try {
|
|
10061
|
-
process.kill(registered.pid, 0);
|
|
10062
|
-
return false;
|
|
10063
|
-
} catch (error) {
|
|
10064
|
-
if (error.code !== "ESRCH") return false;
|
|
10065
|
-
}
|
|
10066
|
-
}
|
|
10067
|
-
}
|
|
10068
|
-
try {
|
|
10069
|
-
process.kill(-registered.pid, 0);
|
|
10070
|
-
return true;
|
|
10071
|
-
} catch (error) {
|
|
10072
|
-
return error.code === "EPERM";
|
|
10073
|
-
}
|
|
10074
|
-
}
|
|
10075
|
-
async function waitForProbeGroup(child, timeoutMs) {
|
|
10076
|
-
const deadline = Date.now() + timeoutMs;
|
|
10077
|
-
while (probeGroupRunning(child) && Date.now() < deadline) {
|
|
10078
|
-
await new Promise((resolve2) => setTimeout(resolve2, 25));
|
|
10079
|
-
}
|
|
10080
|
-
return !probeGroupRunning(child);
|
|
10081
|
-
}
|
|
10082
|
-
async function stopProbe(child) {
|
|
10083
|
-
const registered = probeIdentities.get(child);
|
|
10084
|
-
if (!registered) return;
|
|
10085
|
-
if (probeGroupRunning(child)) {
|
|
10086
|
-
try {
|
|
10087
|
-
process.kill(-registered.pid, "SIGTERM");
|
|
10088
|
-
} catch {
|
|
10089
|
-
}
|
|
10090
|
-
}
|
|
10091
|
-
if (!await waitForProbeGroup(child, 500)) {
|
|
10092
|
-
if (probeGroupRunning(child)) {
|
|
10093
|
-
try {
|
|
10094
|
-
process.kill(-registered.pid, "SIGKILL");
|
|
10095
|
-
} catch {
|
|
10096
|
-
}
|
|
10097
|
-
}
|
|
10098
|
-
if (!await waitForProbeGroup(child, 500)) {
|
|
10099
|
-
throw new Error(
|
|
10100
|
-
`isolated probe process group did not exit: ${registered.pid}`
|
|
10101
|
-
);
|
|
10102
|
-
}
|
|
10103
|
-
}
|
|
10104
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
10105
|
-
const exited = await new Promise((resolve2) => {
|
|
10106
|
-
let settled = false;
|
|
10107
|
-
let timer;
|
|
10108
|
-
const finish = (result) => {
|
|
10109
|
-
if (settled) return;
|
|
10110
|
-
settled = true;
|
|
10111
|
-
if (timer) clearTimeout(timer);
|
|
10112
|
-
child.off("exit", onExit);
|
|
10113
|
-
child.off("error", onError);
|
|
10114
|
-
resolve2(result);
|
|
10115
|
-
};
|
|
10116
|
-
const onExit = () => finish(true);
|
|
10117
|
-
const onError = () => finish(false);
|
|
10118
|
-
child.once("exit", onExit);
|
|
10119
|
-
child.once("error", onError);
|
|
10120
|
-
timer = setTimeout(() => finish(false), 5e3);
|
|
10121
|
-
if (child.exitCode !== null || child.signalCode !== null) finish(true);
|
|
10122
|
-
});
|
|
10123
|
-
if (!exited) {
|
|
10124
|
-
throw new Error(
|
|
10125
|
-
`isolated probe child did not publish exit after group cleanup: ${registered.pid}`
|
|
10126
|
-
);
|
|
10127
|
-
}
|
|
10128
|
-
}
|
|
10129
|
-
}
|
|
10130
|
-
function collectChildOutput(child, stream) {
|
|
10131
|
-
let output = "";
|
|
10132
|
-
child[stream]?.setEncoding("utf8");
|
|
10133
|
-
child[stream]?.on("data", (chunk) => {
|
|
10134
|
-
output = `${output}${String(chunk)}`.slice(-64 * 1024);
|
|
10135
|
-
});
|
|
10136
|
-
return () => output;
|
|
10137
|
-
}
|
|
10138
|
-
async function waitForCommand(child, timeoutMs, description) {
|
|
10139
|
-
let timer;
|
|
10140
|
-
try {
|
|
10141
|
-
return await new Promise((resolve2, reject) => {
|
|
10142
|
-
let settled = false;
|
|
10143
|
-
const finish = (callback) => {
|
|
10144
|
-
if (settled) return;
|
|
10145
|
-
settled = true;
|
|
10146
|
-
if (timer) clearTimeout(timer);
|
|
10147
|
-
callback();
|
|
10148
|
-
};
|
|
10149
|
-
child.once("error", (error) => finish(() => reject(error)));
|
|
10150
|
-
child.once(
|
|
10151
|
-
"exit",
|
|
10152
|
-
(code, signal) => finish(
|
|
10153
|
-
() => signal ? reject(new Error(`${description} exited by ${signal}`)) : resolve2(code ?? 1)
|
|
10154
|
-
)
|
|
10155
|
-
);
|
|
10156
|
-
timer = setTimeout(
|
|
10157
|
-
() => finish(() => reject(new Error(`${description} timeout`))),
|
|
10158
|
-
timeoutMs
|
|
10159
|
-
);
|
|
10160
|
-
});
|
|
10161
|
-
} finally {
|
|
10162
|
-
if (timer) clearTimeout(timer);
|
|
10163
|
-
}
|
|
10164
|
-
}
|
|
10165
|
-
async function compileNestSource(projectRoot, stagingRoot, timeoutMs, sourceConfig) {
|
|
10166
|
-
const cliFile = resolveNestCli(projectRoot);
|
|
10167
|
-
const compileRoot = path26.join(stagingRoot, ".compiled");
|
|
10168
|
-
const outputRoot = path26.join(compileRoot, "output");
|
|
10169
|
-
fs30.mkdirSync(outputRoot, { recursive: true });
|
|
10170
|
-
const isolatedConfig = path26.join(compileRoot, "tsconfig.json");
|
|
10171
|
-
fs30.writeFileSync(
|
|
10172
|
-
isolatedConfig,
|
|
10173
|
-
`${JSON.stringify(
|
|
10174
|
-
{
|
|
10175
|
-
extends: sourceConfig,
|
|
10176
|
-
compilerOptions: {
|
|
10177
|
-
outDir: outputRoot,
|
|
10178
|
-
incremental: false,
|
|
10179
|
-
composite: false,
|
|
10180
|
-
declaration: false,
|
|
10181
|
-
declarationMap: false,
|
|
10182
|
-
sourceMap: false,
|
|
10183
|
-
inlineSourceMap: false
|
|
10184
|
-
}
|
|
10185
|
-
},
|
|
10186
|
-
null,
|
|
10187
|
-
2
|
|
10188
|
-
)}
|
|
10189
|
-
`,
|
|
10190
|
-
{ mode: 384 }
|
|
10191
|
-
);
|
|
10192
|
-
const child = spawn2(
|
|
10193
|
-
process.execPath,
|
|
10194
|
-
[cliFile, "build", "--path", path26.relative(projectRoot, isolatedConfig)],
|
|
10195
|
-
{
|
|
10196
|
-
cwd: projectRoot,
|
|
10197
|
-
detached: true,
|
|
10198
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
10199
|
-
env: { ...process.env, NODE_ENV: "production" }
|
|
10200
|
-
}
|
|
10201
|
-
);
|
|
10202
|
-
registerProbe(child);
|
|
10203
|
-
const stdout = collectChildOutput(child, "stdout");
|
|
10204
|
-
const stderr = collectChildOutput(child, "stderr");
|
|
10205
|
-
try {
|
|
10206
|
-
const code = await waitForCommand(
|
|
10207
|
-
child,
|
|
10208
|
-
timeoutMs,
|
|
10209
|
-
"isolated Nest compilation"
|
|
10210
|
-
);
|
|
10211
|
-
if (code !== 0) {
|
|
10212
|
-
throw new Error(
|
|
10213
|
-
`isolated Nest compilation failed with exit ${code}: ${stderr().trim() || stdout().trim()}`
|
|
10214
|
-
);
|
|
10215
|
-
}
|
|
10216
|
-
} finally {
|
|
10217
|
-
await stopProbe(child);
|
|
10218
|
-
}
|
|
10219
|
-
const candidates = [
|
|
10220
|
-
path26.join(outputRoot, "server", "main.js"),
|
|
10221
|
-
path26.join(outputRoot, "main.js")
|
|
10222
|
-
].filter((candidate) => fs30.existsSync(candidate));
|
|
10223
|
-
if (candidates.length !== 1) {
|
|
10224
|
-
throw new Error(
|
|
10225
|
-
candidates.length === 0 ? "isolated Nest compiled entry missing; checked server/main.js and main.js" : "isolated Nest compiled entry is ambiguous"
|
|
10226
|
-
);
|
|
10227
|
-
}
|
|
10228
|
-
const entry = fs30.realpathSync(candidates[0]);
|
|
10229
|
-
if (!isInside2(fs30.realpathSync(outputRoot), entry) || !fs30.statSync(entry).isFile()) {
|
|
10230
|
-
throw new Error("isolated Nest compiled entry escaped output root");
|
|
10231
|
-
}
|
|
10232
|
-
return { entry, applicationRoot: outputRoot };
|
|
10233
|
-
}
|
|
10234
|
-
function copyRegularFile(source, target) {
|
|
10235
|
-
const stats = fs30.lstatSync(source);
|
|
10236
|
-
if (stats.isSymbolicLink()) {
|
|
10237
|
-
throw new Error(`runtime tree symlink is unsupported: ${source}`);
|
|
10238
|
-
}
|
|
10239
|
-
if (!stats.isFile()) {
|
|
10240
|
-
throw new Error(`runtime tree special file is unsupported: ${source}`);
|
|
10241
|
-
}
|
|
10242
|
-
fs30.mkdirSync(path26.dirname(target), { recursive: true });
|
|
10243
|
-
fs30.copyFileSync(source, target, fs30.constants.COPYFILE_FICLONE);
|
|
10244
|
-
fs30.chmodSync(target, stats.mode & 73 ? 493 : 420);
|
|
10245
|
-
}
|
|
10246
|
-
function copyDirectoryContents(sourceRoot, targetRoot) {
|
|
10247
|
-
const visit = (source, target) => {
|
|
10248
|
-
const stats = fs30.lstatSync(source);
|
|
10249
|
-
if (stats.isSymbolicLink()) {
|
|
10250
|
-
throw new Error(`runtime tree symlink is unsupported: ${source}`);
|
|
10251
|
-
}
|
|
10252
|
-
if (stats.isFile()) {
|
|
10253
|
-
copyRegularFile(source, target);
|
|
10254
|
-
return;
|
|
10255
|
-
}
|
|
10256
|
-
if (!stats.isDirectory()) {
|
|
10257
|
-
throw new Error(`runtime tree special file is unsupported: ${source}`);
|
|
10258
|
-
}
|
|
10259
|
-
fs30.mkdirSync(target, { recursive: true, mode: 493 });
|
|
10260
|
-
for (const entry of fs30.readdirSync(source, { withFileTypes: true })) {
|
|
10261
|
-
if (entry.name === "node_modules") {
|
|
10262
|
-
throw new Error("startup payload must not contain node_modules");
|
|
10263
|
-
}
|
|
10264
|
-
visit(path26.join(source, entry.name), path26.join(target, entry.name));
|
|
10265
|
-
}
|
|
10266
|
-
};
|
|
10267
|
-
visit(sourceRoot, targetRoot);
|
|
10268
|
-
}
|
|
10269
|
-
function reserveGenerationBudget(file, size, budget) {
|
|
10270
|
-
if (size > MAX_GENERATION_FILE_BYTES) {
|
|
10271
|
-
throw new Error(`generation file exceeds 64 MiB: ${file}`);
|
|
10272
|
-
}
|
|
10273
|
-
if (budget.fileCount + 1 > MAX_GENERATION_FILES) {
|
|
10274
|
-
throw new Error(`generation exceeds ${MAX_GENERATION_FILES} files`);
|
|
10275
|
-
}
|
|
10276
|
-
if (budget.totalBytes + size > MAX_GENERATION_BYTES) {
|
|
10277
|
-
throw new Error("generation exceeds 256 MiB");
|
|
10278
|
-
}
|
|
10279
|
-
budget.fileCount += 1;
|
|
10280
|
-
budget.totalBytes += size;
|
|
10281
|
-
}
|
|
10282
|
-
function copyProbeWorkspace(projectRoot, probeProjectRoot) {
|
|
10283
|
-
const files = [];
|
|
10284
|
-
walkSourceFiles(projectRoot, projectRoot, files);
|
|
10285
|
-
const budget = { fileCount: 0, totalBytes: 0 };
|
|
10286
|
-
for (const file of files) {
|
|
10287
|
-
reserveGenerationBudget(file, fs30.lstatSync(file).size, budget);
|
|
10288
|
-
copyRegularFile(
|
|
10289
|
-
file,
|
|
10290
|
-
path26.join(
|
|
10291
|
-
probeProjectRoot,
|
|
10292
|
-
...toPosixRelative(projectRoot, file).split("/")
|
|
10293
|
-
)
|
|
10294
|
-
);
|
|
10295
|
-
}
|
|
10296
|
-
}
|
|
10297
|
-
function readJsonObject(file, description) {
|
|
10298
|
-
const parsed = JSON.parse(
|
|
10299
|
-
readBoundedFile(file, MAX_PACKAGE_MANIFEST_BYTES, description).toString(
|
|
10300
|
-
"utf8"
|
|
10301
|
-
)
|
|
10302
|
-
);
|
|
10303
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
10304
|
-
throw new Error(`${description} must be a JSON object`);
|
|
10305
|
-
}
|
|
10306
|
-
return parsed;
|
|
10307
|
-
}
|
|
10308
|
-
function readBoundedFile(file, maxBytes, description) {
|
|
10309
|
-
const stats = fs30.lstatSync(file);
|
|
10310
|
-
if (stats.isSymbolicLink() || !stats.isFile() || stats.size > maxBytes) {
|
|
10311
|
-
throw new Error(`${description} exceeds the supported metadata size`);
|
|
10312
|
-
}
|
|
10313
|
-
const contents = fs30.readFileSync(file);
|
|
10314
|
-
if (contents.length !== stats.size) {
|
|
10315
|
-
throw new Error(`${description} changed while reading`);
|
|
10316
|
-
}
|
|
10317
|
-
return contents;
|
|
10318
|
-
}
|
|
10319
|
-
function assertStartupBundleEnrollment(projectRoot) {
|
|
10320
|
-
const packageFile = path26.join(projectRoot, "package.json");
|
|
10321
|
-
if (!fs30.existsSync(packageFile) || fs30.lstatSync(packageFile).isSymbolicLink()) {
|
|
10322
|
-
throw new Error("package.json is unavailable");
|
|
10323
|
-
}
|
|
10324
|
-
const packageJson = readJsonObject(packageFile, "package.json");
|
|
10325
|
-
const enrollment = packageJson.miaodaStartupBundle;
|
|
10326
|
-
if (!enrollment || typeof enrollment !== "object" || Array.isArray(enrollment) || enrollment.schemaVersion !== STARTUP_BUNDLE_ENROLLMENT_SCHEMA_VERSION) {
|
|
10327
|
-
throw new Error(
|
|
10328
|
-
`package.json miaodaStartupBundle.schemaVersion must be ${STARTUP_BUNDLE_ENROLLMENT_SCHEMA_VERSION}`
|
|
10329
|
-
);
|
|
10330
|
-
}
|
|
10331
|
-
}
|
|
10332
|
-
function indexPayload(payloadRoot) {
|
|
10333
|
-
const files = [];
|
|
10334
|
-
const budget = { fileCount: 0, totalBytes: 0 };
|
|
10335
|
-
const visit = (directory) => {
|
|
10336
|
-
for (const entry of fs30.readdirSync(directory, { withFileTypes: true })) {
|
|
10337
|
-
const candidate = path26.join(directory, entry.name);
|
|
10338
|
-
if (entry.isSymbolicLink()) {
|
|
10339
|
-
throw new Error(`generation symlink is unsupported: ${candidate}`);
|
|
10340
|
-
}
|
|
10341
|
-
if (entry.isDirectory()) {
|
|
10342
|
-
visit(candidate);
|
|
10343
|
-
continue;
|
|
10344
|
-
}
|
|
10345
|
-
if (!entry.isFile()) {
|
|
10346
|
-
throw new Error(`generation special file is unsupported: ${candidate}`);
|
|
10347
|
-
}
|
|
10348
|
-
const stats = fs30.lstatSync(candidate);
|
|
10349
|
-
if (!stats.isFile()) {
|
|
10350
|
-
throw new Error(`generation special file is unsupported: ${candidate}`);
|
|
10351
|
-
}
|
|
10352
|
-
reserveGenerationBudget(candidate, stats.size, budget);
|
|
10353
|
-
const contents = fs30.readFileSync(candidate);
|
|
10354
|
-
if (contents.length !== stats.size) {
|
|
10355
|
-
throw new Error(
|
|
10356
|
-
`generation file changed while indexing: ${toPosixRelative(payloadRoot, candidate)}`
|
|
10357
|
-
);
|
|
10358
|
-
}
|
|
10359
|
-
files.push({
|
|
10360
|
-
path: `payload/${toPosixRelative(payloadRoot, candidate)}`,
|
|
10361
|
-
sha256: sha256(contents),
|
|
10362
|
-
size: contents.length,
|
|
10363
|
-
mode: stats.mode & 511
|
|
10364
|
-
});
|
|
10365
|
-
}
|
|
10366
|
-
};
|
|
10367
|
-
visit(payloadRoot);
|
|
10368
|
-
files.sort((left, right) => compareCodeUnits(left.path, right.path));
|
|
10369
|
-
return files;
|
|
10370
|
-
}
|
|
10371
|
-
function reservePort() {
|
|
10372
|
-
return new Promise((resolve2, reject) => {
|
|
10373
|
-
const server = net.createServer();
|
|
10374
|
-
server.once("error", reject);
|
|
10375
|
-
server.listen(0, "127.0.0.1", () => {
|
|
10376
|
-
const address = server.address();
|
|
10377
|
-
if (!address || typeof address === "string") {
|
|
10378
|
-
server.close(
|
|
10379
|
-
() => reject(new Error("isolated probe failed to reserve a port"))
|
|
10380
|
-
);
|
|
10381
|
-
return;
|
|
10382
|
-
}
|
|
10383
|
-
server.close((error) => error ? reject(error) : resolve2(address.port));
|
|
10384
|
-
});
|
|
10385
|
-
});
|
|
10386
|
-
}
|
|
10387
|
-
async function isolatedProbe(projectRoot, payloadRoot, entry, timeoutMs, expectedCapabilityIds) {
|
|
10388
|
-
const probeRoot = fs30.mkdtempSync(
|
|
10389
|
-
path26.join(os3.tmpdir(), "miaoda-server-generation-probe-")
|
|
10390
|
-
);
|
|
10391
|
-
const probeWorkspaceRoot = path26.join(probeRoot, "workspace");
|
|
10392
|
-
const probeProjectRoot = path26.join(probeWorkspaceRoot, "code");
|
|
10393
|
-
copyProbeWorkspace(projectRoot, probeProjectRoot);
|
|
10394
|
-
const probeGenerationRoot = path26.join(
|
|
10395
|
-
probeProjectRoot,
|
|
10396
|
-
".miaoda-cache",
|
|
10397
|
-
"server",
|
|
10398
|
-
"generations",
|
|
10399
|
-
"probe"
|
|
10400
|
-
);
|
|
10401
|
-
const probePayload = path26.join(probeGenerationRoot, "payload");
|
|
10402
|
-
copyDirectoryContents(payloadRoot, probePayload);
|
|
10403
|
-
const probeEntry = path26.join(probeGenerationRoot, ...entry.split("/"));
|
|
10404
|
-
const probeResolutionGuard = path26.join(
|
|
10405
|
-
probeRoot,
|
|
10406
|
-
"probe-module-resolution-guard.cjs"
|
|
10407
|
-
);
|
|
10408
|
-
const probeEsmResolutionLoader = path26.join(
|
|
10409
|
-
probeRoot,
|
|
10410
|
-
"probe-module-resolution-loader.mjs"
|
|
10411
|
-
);
|
|
10412
|
-
const allowedPayloadRoot = fs30.realpathSync(probePayload);
|
|
10413
|
-
fs30.writeFileSync(
|
|
10414
|
-
probeResolutionGuard,
|
|
10415
|
-
`'use strict';
|
|
10416
|
-
const fs = require('node:fs');
|
|
10417
|
-
const Module = require('node:module');
|
|
10418
|
-
const path = require('node:path');
|
|
10419
|
-
const allowedRoot = ${JSON.stringify(allowedPayloadRoot)};
|
|
10420
|
-
const builtins = new Set(Module.builtinModules.flatMap(name => [name, 'node:' + name]));
|
|
10421
|
-
const originalResolveFilename = Module._resolveFilename;
|
|
10422
|
-
Module._resolveFilename = function miaodaProbeResolve(request, parent, isMain, options) {
|
|
10423
|
-
const resolved = originalResolveFilename.call(this, request, parent, isMain, options);
|
|
10424
|
-
if (builtins.has(request)) return resolved;
|
|
10425
|
-
if (typeof resolved !== 'string' || !path.isAbsolute(resolved)) {
|
|
10426
|
-
const error = new Error('MIAODA_ISOLATED_PROBE_MODULE_REJECTED:' + request + ':' + resolved);
|
|
10427
|
-
error.code = 'MODULE_NOT_FOUND';
|
|
10428
|
-
throw error;
|
|
10429
|
-
}
|
|
10430
|
-
const realResolved = fs.realpathSync(resolved);
|
|
10431
|
-
const relative = path.relative(allowedRoot, realResolved);
|
|
10432
|
-
if (relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))) {
|
|
10433
|
-
return resolved;
|
|
10434
|
-
}
|
|
10435
|
-
const error = new Error('MIAODA_ISOLATED_PROBE_MODULE_REJECTED:' + request + ':' + realResolved);
|
|
10436
|
-
error.code = 'MODULE_NOT_FOUND';
|
|
10437
|
-
throw error;
|
|
10438
|
-
};
|
|
10439
|
-
`,
|
|
10440
|
-
{ mode: 384 }
|
|
10441
|
-
);
|
|
10442
|
-
fs30.writeFileSync(
|
|
10443
|
-
probeEsmResolutionLoader,
|
|
10444
|
-
`import fs from 'node:fs';
|
|
10445
|
-
import path from 'node:path';
|
|
10446
|
-
import { fileURLToPath } from 'node:url';
|
|
10447
|
-
|
|
10448
|
-
const allowedRoot = ${JSON.stringify(allowedPayloadRoot)};
|
|
10449
|
-
|
|
10450
|
-
export async function resolve(specifier, context, nextResolve) {
|
|
10451
|
-
const resolved = await nextResolve(specifier, context);
|
|
10452
|
-
if (resolved.url.startsWith('node:')) return resolved;
|
|
10453
|
-
if (!resolved.url.startsWith('file:')) {
|
|
10454
|
-
throw new Error('MIAODA_ISOLATED_PROBE_ESM_REJECTED:' + specifier + ':' + resolved.url);
|
|
10455
|
-
}
|
|
10456
|
-
const realResolved = fs.realpathSync(fileURLToPath(resolved.url));
|
|
10457
|
-
const relative = path.relative(allowedRoot, realResolved);
|
|
10458
|
-
if (relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))) {
|
|
10459
|
-
return resolved;
|
|
10460
|
-
}
|
|
10461
|
-
const error = new Error('MIAODA_ISOLATED_PROBE_ESM_REJECTED:' + specifier + ':' + resolved.url);
|
|
10462
|
-
error.code = 'ERR_MODULE_NOT_FOUND';
|
|
10463
|
-
throw error;
|
|
10464
|
-
}
|
|
10465
|
-
`,
|
|
10466
|
-
{ mode: 384 }
|
|
10467
|
-
);
|
|
10468
|
-
const port = await reservePort();
|
|
10469
|
-
const configuredBasePath = process.env.CLIENT_BASE_PATH;
|
|
10470
|
-
const basePath = configuredBasePath?.startsWith("/") && !configuredBasePath.startsWith("//") ? configuredBasePath.replace(/\/+$/, "") : "";
|
|
10471
|
-
const readinessPath = `${basePath}/__innerapi__/capability/list`;
|
|
10472
|
-
const probeNodeOptions = [
|
|
10473
|
-
`--require=${JSON.stringify(probeResolutionGuard)}`,
|
|
10474
|
-
`--experimental-loader=${JSON.stringify(probeEsmResolutionLoader)}`
|
|
10475
|
-
].join(" ");
|
|
10476
|
-
const child = spawn2(process.execPath, [probeEntry], {
|
|
10477
|
-
cwd: probeProjectRoot,
|
|
10478
|
-
detached: true,
|
|
10479
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
10480
|
-
env: {
|
|
10481
|
-
...process.env,
|
|
10482
|
-
NODE_PATH: "",
|
|
10483
|
-
NODE_OPTIONS: probeNodeOptions,
|
|
10484
|
-
PWD: probeProjectRoot,
|
|
10485
|
-
WORKSPACE_DIR: probeWorkspaceRoot,
|
|
10486
|
-
MIAODA_WORKSPACE_ROOT: probeProjectRoot,
|
|
10487
|
-
NODE_ENV: "development",
|
|
10488
|
-
SERVER_HOST: "127.0.0.1",
|
|
10489
|
-
SERVER_PORT: String(port),
|
|
10490
|
-
DEPRECATED_SKIP_INIT_DB_CONNECTION: process.env.DEPRECATED_SKIP_INIT_DB_CONNECTION ?? "true",
|
|
10491
|
-
FORCE_AUTHN_INNERAPI_DOMAIN: process.env.FORCE_AUTHN_INNERAPI_DOMAIN ?? "http://127.0.0.1",
|
|
10492
|
-
FORCE_AUTHN_ACCESS_KEY: process.env.FORCE_AUTHN_ACCESS_KEY ?? "server-startup-probe",
|
|
10493
|
-
FORCE_AUTHN_ACCESS_SECRET: process.env.FORCE_AUTHN_ACCESS_SECRET ?? "server-startup-probe"
|
|
10494
|
-
}
|
|
10495
|
-
});
|
|
10496
|
-
registerProbe(child);
|
|
10497
|
-
const stdout = collectChildOutput(child, "stdout");
|
|
10498
|
-
const stderr = collectChildOutput(child, "stderr");
|
|
10499
|
-
try {
|
|
10500
|
-
await new Promise((resolve2, reject) => {
|
|
10501
|
-
const deadline = Date.now() + timeoutMs;
|
|
10502
|
-
let settled = false;
|
|
10503
|
-
let retryTimer;
|
|
10504
|
-
const detail = () => `${stderr()}
|
|
10505
|
-
${stdout()}`.trim();
|
|
10506
|
-
const finish = (error) => {
|
|
10507
|
-
if (settled) return;
|
|
10508
|
-
settled = true;
|
|
10509
|
-
if (retryTimer) clearTimeout(retryTimer);
|
|
10510
|
-
if (error) reject(error);
|
|
10511
|
-
else resolve2();
|
|
10512
|
-
};
|
|
10513
|
-
const retry = () => {
|
|
10514
|
-
const remaining = deadline - Date.now();
|
|
10515
|
-
if (remaining <= 0) {
|
|
10516
|
-
finish(
|
|
10517
|
-
new Error(
|
|
10518
|
-
`isolated generation readiness timeout${detail() ? `: ${detail()}` : ""}`
|
|
10519
|
-
)
|
|
10520
|
-
);
|
|
10521
|
-
return;
|
|
10522
|
-
}
|
|
10523
|
-
retryTimer = setTimeout(attempt, Math.min(25, remaining));
|
|
10524
|
-
};
|
|
10525
|
-
const attempt = () => {
|
|
10526
|
-
if (settled) return;
|
|
10527
|
-
if (child.exitCode !== null || child.signalCode !== null) {
|
|
10528
|
-
finish(
|
|
10529
|
-
new Error(
|
|
10530
|
-
`isolated generation exited before readiness${detail() ? `: ${detail()}` : ""}`
|
|
10531
|
-
)
|
|
10532
|
-
);
|
|
10533
|
-
return;
|
|
10534
|
-
}
|
|
10535
|
-
const remaining = deadline - Date.now();
|
|
10536
|
-
if (remaining <= 0) return retry();
|
|
10537
|
-
let requestSettled = false;
|
|
10538
|
-
const request = http.get(
|
|
10539
|
-
{ host: "127.0.0.1", port, path: readinessPath },
|
|
10540
|
-
(response) => {
|
|
10541
|
-
if (requestSettled || settled) {
|
|
10542
|
-
response.destroy();
|
|
10543
|
-
return;
|
|
10544
|
-
}
|
|
10545
|
-
if (response.statusCode !== 200) {
|
|
10546
|
-
requestSettled = true;
|
|
10547
|
-
response.resume();
|
|
10548
|
-
retry();
|
|
10549
|
-
return;
|
|
10550
|
-
}
|
|
10551
|
-
const chunks = [];
|
|
10552
|
-
let responseBytes = 0;
|
|
10553
|
-
response.on("data", (chunk) => {
|
|
10554
|
-
if (requestSettled || settled) return;
|
|
10555
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
10556
|
-
responseBytes += buffer.length;
|
|
10557
|
-
if (responseBytes > MAX_READINESS_RESPONSE_BYTES) {
|
|
10558
|
-
requestSettled = true;
|
|
10559
|
-
response.destroy();
|
|
10560
|
-
retry();
|
|
10561
|
-
return;
|
|
10562
|
-
}
|
|
10563
|
-
chunks.push(buffer);
|
|
10564
|
-
});
|
|
10565
|
-
response.once("end", () => {
|
|
10566
|
-
if (requestSettled || settled) return;
|
|
10567
|
-
requestSettled = true;
|
|
10568
|
-
if (isCapabilityReadinessPayload(
|
|
10569
|
-
Buffer.concat(chunks),
|
|
10570
|
-
expectedCapabilityIds
|
|
10571
|
-
))
|
|
10572
|
-
finish();
|
|
10573
|
-
else retry();
|
|
10574
|
-
});
|
|
10575
|
-
response.once("error", () => {
|
|
10576
|
-
if (requestSettled || settled) return;
|
|
10577
|
-
requestSettled = true;
|
|
10578
|
-
retry();
|
|
10579
|
-
});
|
|
10580
|
-
}
|
|
10581
|
-
);
|
|
10582
|
-
request.setTimeout(
|
|
10583
|
-
remaining,
|
|
10584
|
-
() => request.destroy(new Error("isolated generation readiness timeout"))
|
|
10585
|
-
);
|
|
10586
|
-
request.once("error", () => {
|
|
10587
|
-
if (requestSettled || settled) return;
|
|
10588
|
-
requestSettled = true;
|
|
10589
|
-
retry();
|
|
10590
|
-
});
|
|
10591
|
-
};
|
|
10592
|
-
attempt();
|
|
10593
|
-
});
|
|
10594
|
-
} finally {
|
|
10595
|
-
await stopProbe(child);
|
|
10596
|
-
fs30.rmSync(probeRoot, { recursive: true, force: true });
|
|
10597
|
-
}
|
|
10598
|
-
}
|
|
10599
|
-
function writeBoundedJson(file, value, maxBytes, description) {
|
|
10600
|
-
const contents = `${JSON.stringify(value, null, 2)}
|
|
10601
|
-
`;
|
|
10602
|
-
if (Buffer.byteLength(contents) > maxBytes) {
|
|
10603
|
-
throw new Error(`${description} exceeds ${maxBytes} bytes`);
|
|
10604
|
-
}
|
|
10605
|
-
fs30.writeFileSync(file, contents, {
|
|
10606
|
-
mode: 384
|
|
10607
|
-
});
|
|
10608
|
-
}
|
|
10609
|
-
function removeLegacyPointer(cacheRoot) {
|
|
10610
|
-
fs30.rmSync(path26.join(cacheRoot, "current.json"), { force: true });
|
|
10611
|
-
}
|
|
10612
|
-
function removeLegacyDependencyArtifacts(cacheRoot) {
|
|
10613
|
-
for (const name of ["node_modules", "dependencies.json"]) {
|
|
10614
|
-
const target = path26.join(cacheRoot, name);
|
|
10615
|
-
if (!fs30.existsSync(target)) continue;
|
|
10616
|
-
const stats = fs30.lstatSync(target);
|
|
10617
|
-
if (stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
10618
|
-
fs30.rmSync(target, { recursive: true, force: true });
|
|
10619
|
-
} else {
|
|
10620
|
-
fs30.rmSync(target, { force: true });
|
|
10621
|
-
}
|
|
10622
|
-
}
|
|
10623
|
-
}
|
|
10624
|
-
function removeStaleBuildArtifacts(cacheRoot) {
|
|
10625
|
-
for (const entry of fs30.readdirSync(cacheRoot)) {
|
|
10626
|
-
if (!/^\.(?:staging|retired)-[a-z0-9-]+$/.test(entry)) continue;
|
|
10627
|
-
const target = path26.join(cacheRoot, entry);
|
|
10628
|
-
const stats = fs30.lstatSync(target);
|
|
10629
|
-
if (stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
10630
|
-
fs30.rmSync(target, { recursive: true, force: true });
|
|
10631
|
-
} else {
|
|
10632
|
-
fs30.rmSync(target, { force: true });
|
|
10633
|
-
}
|
|
10634
|
-
}
|
|
10635
|
-
}
|
|
10636
|
-
async function buildServerStartupBundle(options) {
|
|
10637
|
-
const startedAt = Date.now();
|
|
10638
|
-
let stagingRoot;
|
|
10639
|
-
let cacheRoot;
|
|
10640
|
-
try {
|
|
10641
|
-
const projectRoot = fs30.realpathSync(path26.resolve(options.projectRoot));
|
|
10642
|
-
assertStartupBundleEnrollment(projectRoot);
|
|
10643
|
-
cacheRoot = path26.resolve(
|
|
10644
|
-
projectRoot,
|
|
10645
|
-
options.cacheRoot ?? ".miaoda-cache/server"
|
|
10646
|
-
);
|
|
10647
|
-
if (!isInside2(projectRoot, cacheRoot))
|
|
10648
|
-
throw new Error("cache root escaped project root");
|
|
10649
|
-
assertNoSymlinkComponents(
|
|
10650
|
-
projectRoot,
|
|
10651
|
-
cacheRoot,
|
|
10652
|
-
"cache path symlink is unsupported"
|
|
10653
|
-
);
|
|
10654
|
-
fs30.mkdirSync(cacheRoot, { recursive: true, mode: 448 });
|
|
10655
|
-
removeLegacyPointer(cacheRoot);
|
|
10656
|
-
removeLegacyDependencyArtifacts(cacheRoot);
|
|
10657
|
-
removeStaleBuildArtifacts(cacheRoot);
|
|
10658
|
-
const timeoutMs = options.probeTimeoutMs ?? 1e4;
|
|
10659
|
-
const compileTimeoutMs = options.compileTimeoutMs ?? 12e4;
|
|
10660
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
10661
|
-
throw new Error("probe timeout must be positive");
|
|
10662
|
-
if (!Number.isFinite(compileTimeoutMs) || compileTimeoutMs <= 0)
|
|
10663
|
-
throw new Error("compile timeout must be positive");
|
|
10664
|
-
const expectedCapabilityIds = resolveExpectedCapabilityIds(projectRoot);
|
|
10665
|
-
const sourceSha256 = workspaceSourceSha256(projectRoot);
|
|
10666
|
-
stagingRoot = path26.join(cacheRoot, `.staging-${randomUUID()}`);
|
|
10667
|
-
fs30.mkdirSync(stagingRoot, { recursive: false, mode: 448 });
|
|
10668
|
-
const explicitEntry = options.entry ? resolveEntry(projectRoot, options.entry) : void 0;
|
|
10669
|
-
const distRoot = path26.join(projectRoot, "dist");
|
|
10670
|
-
const compiled = explicitEntry ? {
|
|
10671
|
-
entry: explicitEntry,
|
|
10672
|
-
applicationRoot: isInside2(distRoot, explicitEntry) ? distRoot : path26.dirname(explicitEntry)
|
|
10673
|
-
} : await compileNestSource(
|
|
10674
|
-
projectRoot,
|
|
10675
|
-
stagingRoot,
|
|
10676
|
-
compileTimeoutMs,
|
|
10677
|
-
resolveNestTsconfig(projectRoot)
|
|
10678
|
-
);
|
|
10679
|
-
if (workspaceSourceSha256(projectRoot) !== sourceSha256) {
|
|
10680
|
-
throw new Error("source changed during isolated Nest compilation");
|
|
10681
|
-
}
|
|
10682
|
-
const payloadRoot = path26.join(stagingRoot, "payload");
|
|
10683
|
-
fs30.mkdirSync(payloadRoot, { recursive: true, mode: 493 });
|
|
10684
|
-
const entry = STARTUP_BUNDLE_FILE;
|
|
10685
|
-
const bundleFile = path26.join(
|
|
10686
|
-
stagingRoot,
|
|
10687
|
-
...STARTUP_BUNDLE_FILE.split("/")
|
|
10688
|
-
);
|
|
10689
|
-
await buildStaticStartupPayload({
|
|
10690
|
-
projectRoot,
|
|
10691
|
-
entryFile: compiled.entry,
|
|
10692
|
-
outputFile: bundleFile,
|
|
10693
|
-
payloadRoot,
|
|
10694
|
-
limits: {
|
|
10695
|
-
maxFileBytes: MAX_GENERATION_FILE_BYTES,
|
|
10696
|
-
maxFiles: MAX_GENERATION_FILES,
|
|
10697
|
-
maxTotalBytes: MAX_GENERATION_BYTES
|
|
10698
|
-
}
|
|
10699
|
-
});
|
|
10700
|
-
const files = indexPayload(payloadRoot);
|
|
10701
|
-
const fileIndexContents = `${JSON.stringify(files, null, 2)}
|
|
10702
|
-
`;
|
|
10703
|
-
if (Buffer.byteLength(fileIndexContents) > MAX_FILE_INDEX_BYTES) {
|
|
10704
|
-
throw new Error("generation file index exceeds 16 MiB");
|
|
10705
|
-
}
|
|
10706
|
-
fs30.writeFileSync(
|
|
10707
|
-
path26.join(stagingRoot, "files.mtree.json"),
|
|
10708
|
-
fileIndexContents,
|
|
10709
|
-
{ mode: 384 }
|
|
10710
|
-
);
|
|
10711
|
-
const runtime = runtimeIdentity();
|
|
10712
|
-
const fileIndexSha256 = sha256(fileIndexContents);
|
|
10713
|
-
const identity = {
|
|
10714
|
-
schemaVersion: SCHEMA_VERSION,
|
|
10715
|
-
sourceSha256,
|
|
10716
|
-
expectedCapabilityIds,
|
|
10717
|
-
runtime,
|
|
10718
|
-
fileIndexSha256
|
|
10719
|
-
};
|
|
10720
|
-
const generationId = sha256(stableJson(identity));
|
|
10721
|
-
await isolatedProbe(
|
|
10722
|
-
projectRoot,
|
|
10723
|
-
payloadRoot,
|
|
10724
|
-
entry,
|
|
10725
|
-
timeoutMs,
|
|
10726
|
-
expectedCapabilityIds
|
|
10727
|
-
);
|
|
10728
|
-
if (workspaceSourceSha256(projectRoot) !== sourceSha256) {
|
|
10729
|
-
throw new Error("source changed while building startup generation");
|
|
10730
|
-
}
|
|
10731
|
-
const manifest = {
|
|
10732
|
-
generationId,
|
|
10733
|
-
...identity
|
|
10734
|
-
};
|
|
10735
|
-
writeBoundedJson(
|
|
10736
|
-
path26.join(stagingRoot, "manifest.json"),
|
|
10737
|
-
manifest,
|
|
10738
|
-
MAX_MANIFEST_BYTES,
|
|
10739
|
-
"startup generation manifest"
|
|
10740
|
-
);
|
|
10741
|
-
fs30.rmSync(path26.join(stagingRoot, ".compiled"), {
|
|
10742
|
-
recursive: true,
|
|
10743
|
-
force: true
|
|
10744
|
-
});
|
|
10745
|
-
const generationsRoot = path26.join(cacheRoot, "generations");
|
|
10746
|
-
fs30.mkdirSync(generationsRoot, { recursive: true, mode: 448 });
|
|
10747
|
-
const generationRoot = path26.join(generationsRoot, generationId);
|
|
10748
|
-
const previousGenerations = fs30.readdirSync(generationsRoot);
|
|
10749
|
-
if (previousGenerations.length > 1) {
|
|
10750
|
-
throw new Error("startup generation directory is ambiguous");
|
|
10751
|
-
}
|
|
10752
|
-
const previous = previousGenerations[0];
|
|
10753
|
-
const previousRoot = previous ? path26.join(generationsRoot, previous) : void 0;
|
|
10754
|
-
const retiredRoot = previousRoot ? path26.join(cacheRoot, `.retired-${randomUUID()}`) : void 0;
|
|
10755
|
-
if (previousRoot && retiredRoot) {
|
|
10756
|
-
fs30.renameSync(previousRoot, retiredRoot);
|
|
10757
|
-
}
|
|
10758
|
-
try {
|
|
10759
|
-
fs30.renameSync(stagingRoot, generationRoot);
|
|
10760
|
-
} catch (publishError) {
|
|
10761
|
-
if (previousRoot && retiredRoot && fs30.existsSync(retiredRoot)) {
|
|
10762
|
-
fs30.renameSync(retiredRoot, previousRoot);
|
|
10763
|
-
}
|
|
10764
|
-
throw publishError;
|
|
10765
|
-
}
|
|
10766
|
-
if (previousRoot && retiredRoot) {
|
|
10767
|
-
try {
|
|
10768
|
-
fs30.rmSync(retiredRoot, { recursive: true, force: true });
|
|
10769
|
-
} catch (pruneError) {
|
|
10770
|
-
fs30.rmSync(generationRoot, { recursive: true, force: true });
|
|
10771
|
-
fs30.renameSync(retiredRoot, previousRoot);
|
|
10772
|
-
throw pruneError;
|
|
10773
|
-
}
|
|
10774
|
-
}
|
|
10775
|
-
stagingRoot = void 0;
|
|
10776
|
-
return {
|
|
10777
|
-
built: true,
|
|
10778
|
-
generationId,
|
|
10779
|
-
generationRoot,
|
|
10780
|
-
entryFile: path26.join(generationRoot, ...entry.split("/")),
|
|
10781
|
-
manifestFile: path26.join(generationRoot, "manifest.json"),
|
|
10782
|
-
reasons: [],
|
|
10783
|
-
elapsedMs: Date.now() - startedAt
|
|
10784
|
-
};
|
|
10785
|
-
} catch (error) {
|
|
10786
|
-
if (stagingRoot) fs30.rmSync(stagingRoot, { recursive: true, force: true });
|
|
10787
|
-
if (cacheRoot) removeLegacyPointer(cacheRoot);
|
|
10788
|
-
return {
|
|
10789
|
-
built: false,
|
|
10790
|
-
reasons: [error instanceof Error ? error.message : String(error)],
|
|
10791
|
-
elapsedMs: Date.now() - startedAt
|
|
10792
|
-
};
|
|
10793
|
-
}
|
|
10794
|
-
}
|
|
10795
|
-
|
|
10796
|
-
// src/commands/build/index.ts
|
|
10797
|
-
var getTokenCommand = {
|
|
10798
|
-
name: "get-token",
|
|
10799
|
-
description: "Get artifact upload credential (STI token)",
|
|
10800
|
-
register(program) {
|
|
10801
|
-
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(
|
|
10802
|
-
async (options) => {
|
|
10803
|
-
await getToken(options);
|
|
10804
|
-
}
|
|
10805
|
-
);
|
|
10806
|
-
}
|
|
10807
|
-
};
|
|
10808
|
-
var uploadStaticCommand = {
|
|
10809
|
-
name: "upload-static",
|
|
10810
|
-
description: "Upload shared/static files to TOS",
|
|
10811
|
-
register(program) {
|
|
10812
|
-
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option(
|
|
10813
|
-
"--static-dir <dir>",
|
|
10814
|
-
"Static files directory",
|
|
10815
|
-
UPLOAD_STATIC_DEFAULTS.staticDir
|
|
10816
|
-
).option(
|
|
10817
|
-
"--tosutil-path <path>",
|
|
10818
|
-
"Path to tosutil binary",
|
|
10819
|
-
UPLOAD_STATIC_DEFAULTS.tosutilPath
|
|
10820
|
-
).option(
|
|
10821
|
-
"--endpoint <endpoint>",
|
|
10822
|
-
"TOS endpoint",
|
|
10823
|
-
UPLOAD_STATIC_DEFAULTS.endpoint
|
|
10824
|
-
).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
|
|
10825
|
-
await uploadStatic(options);
|
|
10826
|
-
});
|
|
8272
|
+
};
|
|
8273
|
+
var uploadStaticCommand = {
|
|
8274
|
+
name: "upload-static",
|
|
8275
|
+
description: "Upload shared/static files to TOS",
|
|
8276
|
+
register(program) {
|
|
8277
|
+
program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").option("--static-dir <dir>", "Static files directory", UPLOAD_STATIC_DEFAULTS.staticDir).option("--tosutil-path <path>", "Path to tosutil binary", UPLOAD_STATIC_DEFAULTS.tosutilPath).option("--endpoint <endpoint>", "TOS endpoint", UPLOAD_STATIC_DEFAULTS.endpoint).option("--region <region>", "TOS region", UPLOAD_STATIC_DEFAULTS.region).action(async (options) => {
|
|
8278
|
+
await uploadStatic(options);
|
|
8279
|
+
});
|
|
10827
8280
|
}
|
|
10828
8281
|
};
|
|
10829
8282
|
var preUploadStaticCommand = {
|
|
@@ -10835,39 +8288,10 @@ var preUploadStaticCommand = {
|
|
|
10835
8288
|
});
|
|
10836
8289
|
}
|
|
10837
8290
|
};
|
|
10838
|
-
var serverStartupBundleCommand = {
|
|
10839
|
-
name: "server-startup-bundle",
|
|
10840
|
-
description: "Build and isolate-probe a recycle-only NestJS startup generation",
|
|
10841
|
-
register(program) {
|
|
10842
|
-
program.command(this.name).description(this.description).option("--project-root <dir>", "Application project root", process.cwd()).option(
|
|
10843
|
-
"--probe-timeout-ms <ms>",
|
|
10844
|
-
"Isolated availability probe timeout in milliseconds",
|
|
10845
|
-
"10000"
|
|
10846
|
-
).action(
|
|
10847
|
-
async (options) => {
|
|
10848
|
-
const probeTimeoutMs = Number(options.probeTimeoutMs);
|
|
10849
|
-
if (!Number.isFinite(probeTimeoutMs) || probeTimeoutMs <= 0) {
|
|
10850
|
-
throw new Error("--probe-timeout-ms must be a positive number");
|
|
10851
|
-
}
|
|
10852
|
-
const result = await buildServerStartupBundle({
|
|
10853
|
-
projectRoot: options.projectRoot,
|
|
10854
|
-
probeTimeoutMs
|
|
10855
|
-
});
|
|
10856
|
-
console.log(JSON.stringify(result));
|
|
10857
|
-
if (!result.built) process.exitCode = 2;
|
|
10858
|
-
}
|
|
10859
|
-
);
|
|
10860
|
-
}
|
|
10861
|
-
};
|
|
10862
8291
|
var buildCommandGroup = {
|
|
10863
8292
|
name: "build",
|
|
10864
8293
|
description: "Build related commands",
|
|
10865
|
-
commands: [
|
|
10866
|
-
getTokenCommand,
|
|
10867
|
-
uploadStaticCommand,
|
|
10868
|
-
preUploadStaticCommand,
|
|
10869
|
-
serverStartupBundleCommand
|
|
10870
|
-
]
|
|
8294
|
+
commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
|
|
10871
8295
|
};
|
|
10872
8296
|
|
|
10873
8297
|
// src/commands/index.ts
|
|
@@ -10885,13 +8309,13 @@ var commands = [
|
|
|
10885
8309
|
|
|
10886
8310
|
// src/index.ts
|
|
10887
8311
|
for (const filename of [".env.local", ".env"]) {
|
|
10888
|
-
const envPath =
|
|
10889
|
-
if (
|
|
8312
|
+
const envPath = path25.join(process.cwd(), filename);
|
|
8313
|
+
if (fs29.existsSync(envPath)) {
|
|
10890
8314
|
dotenvConfig({ path: envPath });
|
|
10891
8315
|
}
|
|
10892
8316
|
}
|
|
10893
|
-
var __dirname =
|
|
10894
|
-
var pkg = JSON.parse(
|
|
8317
|
+
var __dirname = path25.dirname(fileURLToPath5(import.meta.url));
|
|
8318
|
+
var pkg = JSON.parse(fs29.readFileSync(path25.join(__dirname, "../package.json"), "utf-8"));
|
|
10895
8319
|
var cli = new FullstackCLI(pkg.version);
|
|
10896
8320
|
cli.useAll(commands);
|
|
10897
8321
|
cli.run();
|