@lark-apaas/fullstack-cli 1.1.64-beta.0 → 1.1.65-alpha.20260902125800

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/index.ts
2
- import fs29 from "fs";
3
- import path25 from "path";
4
- import { fileURLToPath as fileURLToPath5 } from "url";
2
+ import fs31 from "fs";
3
+ import path27 from "path";
4
+ import { fileURLToPath as fileURLToPath6 } from "url";
5
5
  import { config as dotenvConfig } from "dotenv";
6
6
 
7
7
  // src/cli.ts
@@ -2483,12 +2483,36 @@ 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
+ ];
2486
2503
  function buildDefaultRules(opts) {
2487
2504
  const rules = [
2488
- // 1. 派生 scripts 目录(总是覆盖;递归同步,包含 scripts/hooks/run-precommit.js)
2505
+ // 1. 显式同步通用 scripts。禁止目录级 wholesale copy,避免将今后新增的
2506
+ // 冷启动专属文件意外回灌到未入组的存量应用。
2507
+ ...DEFAULT_SYNCED_SCRIPT_FILES.map((file) => ({
2508
+ from: `templates/scripts/${file}`,
2509
+ to: `scripts/${file}`,
2510
+ type: "file",
2511
+ overwrite: true
2512
+ })),
2489
2513
  {
2490
- from: "templates/scripts",
2491
- to: "scripts",
2514
+ from: "templates/scripts/hooks",
2515
+ to: "scripts/hooks",
2492
2516
  type: "directory",
2493
2517
  overwrite: true
2494
2518
  },
@@ -2590,6 +2614,14 @@ function buildDefaultRules(opts) {
2590
2614
  ifStartsWith: "concurrently "
2591
2615
  }
2592
2616
  ];
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
+ }
2593
2625
  if (!opts.disableGenOpenapi) {
2594
2626
  rules.push({
2595
2627
  from: "templates/helper/gen-openapi.ts",
@@ -2766,10 +2798,14 @@ async function run2(options) {
2766
2798
  process.exit(0);
2767
2799
  }
2768
2800
  const stack = resolveStack(userProjectRoot);
2801
+ const startupBundleEnrolled = resolveStartupBundleEnrollment(userPackageJson);
2769
2802
  try {
2770
- console.log(`[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`);
2803
+ console.log(
2804
+ `[fullstack-cli] Starting sync${stack ? ` (stack: ${stack})` : ""}...`
2805
+ );
2771
2806
  const config = genSyncConfig(stack, {
2772
- disableGenOpenapi: options.disableGenOpenapi ?? false
2807
+ disableGenOpenapi: options.disableGenOpenapi ?? false,
2808
+ startupBundleEnrolled
2773
2809
  });
2774
2810
  if (!config || !config.sync) {
2775
2811
  console.warn("[fullstack-cli] No sync configuration found");
@@ -2786,7 +2822,9 @@ async function run2(options) {
2786
2822
  activateGitHooks(userProjectRoot);
2787
2823
  } catch (error) {
2788
2824
  const message = error instanceof Error ? error.message : String(error);
2789
- console.warn(`[fullstack-cli] \u26A0 Failed to activate git hooks: ${message}`);
2825
+ console.warn(
2826
+ `[fullstack-cli] \u26A0 Failed to activate git hooks: ${message}`
2827
+ );
2790
2828
  }
2791
2829
  }
2792
2830
  console.log("[fullstack-cli] Sync completed successfully \u2705");
@@ -2796,6 +2834,19 @@ async function run2(options) {
2796
2834
  process.exit(1);
2797
2835
  }
2798
2836
  }
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
+ }
2799
2850
  function resolveStack(userProjectRoot) {
2800
2851
  const sparkMetaPath = path5.join(userProjectRoot, ".spark", "meta.json");
2801
2852
  if (!fs7.existsSync(sparkMetaPath)) {
@@ -2806,7 +2857,9 @@ function resolveStack(userProjectRoot) {
2806
2857
  return typeof meta.stack === "string" ? meta.stack : void 0;
2807
2858
  } catch (error) {
2808
2859
  const message = error instanceof Error ? error.message : String(error);
2809
- console.warn(`[fullstack-cli] \u26A0 Failed to read .spark/meta.json, fallback to default sync: ${message}`);
2860
+ console.warn(
2861
+ `[fullstack-cli] \u26A0 Failed to read .spark/meta.json, fallback to default sync: ${message}`
2862
+ );
2810
2863
  return void 0;
2811
2864
  }
2812
2865
  }
@@ -2827,7 +2880,12 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2827
2880
  }
2828
2881
  if (rule.type === "add-script") {
2829
2882
  const packageJsonPath = path5.join(userProjectRoot, "package.json");
2830
- addScript(packageJsonPath, rule.name, rule.command, rule.overwrite ?? false);
2883
+ addScript(
2884
+ packageJsonPath,
2885
+ rule.name,
2886
+ rule.command,
2887
+ rule.overwrite ?? false
2888
+ );
2831
2889
  return;
2832
2890
  }
2833
2891
  if (rule.type === "patch-script") {
@@ -2860,7 +2918,12 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2860
2918
  syncDirectory(srcPath, destPath, rule.overwrite ?? true);
2861
2919
  break;
2862
2920
  case "file":
2863
- syncFile(srcPath, destPath, rule.overwrite ?? true, rule.onlyIfExists ?? false);
2921
+ syncFile(
2922
+ srcPath,
2923
+ destPath,
2924
+ rule.overwrite ?? true,
2925
+ rule.onlyIfExists ?? false
2926
+ );
2864
2927
  break;
2865
2928
  case "append":
2866
2929
  appendToFile(srcPath, destPath);
@@ -2869,7 +2932,9 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
2869
2932
  }
2870
2933
  function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2871
2934
  if (onlyIfExists && !fs7.existsSync(dest)) {
2872
- console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`);
2935
+ console.log(
2936
+ `[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, target not exists)`
2937
+ );
2873
2938
  return;
2874
2939
  }
2875
2940
  const destDir = path5.dirname(dest);
@@ -2877,7 +2942,9 @@ function syncFile(src, dest, overwrite = true, onlyIfExists = false) {
2877
2942
  fs7.mkdirSync(destDir, { recursive: true });
2878
2943
  }
2879
2944
  if (fs7.existsSync(dest) && !overwrite) {
2880
- console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`);
2945
+ console.log(
2946
+ `[fullstack-cli] \u25CB ${path5.basename(dest)} (skipped, already exists)`
2947
+ );
2881
2948
  return;
2882
2949
  }
2883
2950
  fs7.copyFileSync(src, dest);
@@ -2904,7 +2971,9 @@ function syncDirectory(src, dest, overwrite = true) {
2904
2971
  }
2905
2972
  });
2906
2973
  if (count > 0) {
2907
- console.log(`[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`);
2974
+ console.log(
2975
+ `[fullstack-cli] Synced ${count} files to ${path5.basename(dest)}/`
2976
+ );
2908
2977
  }
2909
2978
  }
2910
2979
  function appendToFile(src, dest) {
@@ -2914,7 +2983,9 @@ function appendToFile(src, dest) {
2914
2983
  existingContent = fs7.readFileSync(dest, "utf-8");
2915
2984
  }
2916
2985
  if (existingContent.includes(content.trim())) {
2917
- console.log(`[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`);
2986
+ console.log(
2987
+ `[fullstack-cli] \u25CB ${path5.basename(dest)} (already contains content)`
2988
+ );
2918
2989
  return;
2919
2990
  }
2920
2991
  fs7.appendFileSync(dest, content);
@@ -3000,7 +3071,9 @@ function patchScript(packageJsonPath, name, to, ifStartsWith) {
3000
3071
  console.log(`[fullstack-cli] \u2713 Patched scripts.${name}`);
3001
3072
  } catch (error) {
3002
3073
  const message = error instanceof Error ? error.message : String(error);
3003
- console.warn(`[fullstack-cli] \u26A0 Could not patch scripts.${name}: ${message}`);
3074
+ console.warn(
3075
+ `[fullstack-cli] \u26A0 Could not patch scripts.${name}: ${message}`
3076
+ );
3004
3077
  }
3005
3078
  }
3006
3079
  function addLineToFile(filePath, line) {
@@ -3298,8 +3371,11 @@ function getCliVersion() {
3298
3371
  }
3299
3372
 
3300
3373
  // src/commands/upgrade/get-upgrade-files.ts
3301
- function getUpgradeFilesToStage(disableGenOpenapi = true) {
3302
- const syncConfig = genSyncConfig(void 0, { disableGenOpenapi });
3374
+ function getUpgradeFilesToStage(disableGenOpenapi = true, startupBundleEnrolled = false) {
3375
+ const syncConfig = genSyncConfig(void 0, {
3376
+ disableGenOpenapi,
3377
+ startupBundleEnrolled
3378
+ });
3303
3379
  const filesToStage = /* @__PURE__ */ new Set();
3304
3380
  syncConfig.sync.forEach((rule) => {
3305
3381
  if (rule.type === "file" || rule.type === "directory" || rule.type === "merge-json") {
@@ -3333,10 +3409,15 @@ async function run3(options = {}) {
3333
3409
  if (shouldCommit) {
3334
3410
  console.log("[fullstack-cli] Step 3/3: Committing changes...");
3335
3411
  const version = getCliVersion();
3336
- const filesToStage = getUpgradeFilesToStage(options.disableGenOpenapi ?? true);
3412
+ const filesToStage = getUpgradeFilesToStage(
3413
+ options.disableGenOpenapi ?? true,
3414
+ hasStartupBundleEnrollment(readPackageJson(userProjectRoot))
3415
+ );
3337
3416
  autoCommitUpgradeChanges(version, userProjectRoot, filesToStage);
3338
3417
  } else {
3339
- console.log("[fullstack-cli] Step 3/3: Skipping commit (--no-commit flag)");
3418
+ console.log(
3419
+ "[fullstack-cli] Step 3/3: Skipping commit (--no-commit flag)"
3420
+ );
3340
3421
  }
3341
3422
  console.log("[fullstack-cli] Upgrade completed successfully \u2705");
3342
3423
  } catch (error) {
@@ -8260,38 +8341,2533 @@ async function preUploadStatic(options) {
8260
8341
  }
8261
8342
  }
8262
8343
 
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);
8270
- });
8344
+ // src/commands/build/server-startup-bundle.handler.ts
8345
+ import fs30 from "fs";
8346
+ import http from "http";
8347
+ import net from "net";
8348
+ import os3 from "os";
8349
+ import path26 from "path";
8350
+ import { createRequire as createRequire4 } from "module";
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);
8271
8407
  }
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
- });
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}`);
8280
8414
  }
8281
- };
8282
- var preUploadStaticCommand = {
8283
- name: "pre-upload-static",
8284
- description: "Get TOS upload info and output as env vars for build.sh eval",
8285
- register(program) {
8286
- program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
8287
- await preUploadStatic(options);
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
8288
8518
  });
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
+ }
9060
+ return node.arguments.some(
9061
+ (argument) => evaluateLayoutPath(sourceFile, argument, analysis) !== void 0
9062
+ ) ? { kind: "dynamic" } : void 0;
9063
+ }
9064
+ function classifyLayoutPath(sourceFile, node, analysis) {
9065
+ const evaluated = evaluateLayoutPath(sourceFile, node, analysis);
9066
+ if (!evaluated) {
9067
+ return containsRawLayoutPrimitive(node) ? { kind: "unsafe" } : { kind: "none" };
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
+ });
10827
+ }
10828
+ };
10829
+ var preUploadStaticCommand = {
10830
+ name: "pre-upload-static",
10831
+ description: "Get TOS upload info and output as env vars for build.sh eval",
10832
+ register(program) {
10833
+ program.command(this.name).description(this.description).requiredOption("--app-id <id>", "Application ID").action(async (options) => {
10834
+ await preUploadStatic(options);
10835
+ });
10836
+ }
10837
+ };
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
+ );
8289
10860
  }
8290
10861
  };
8291
10862
  var buildCommandGroup = {
8292
10863
  name: "build",
8293
10864
  description: "Build related commands",
8294
- commands: [getTokenCommand, uploadStaticCommand, preUploadStaticCommand]
10865
+ commands: [
10866
+ getTokenCommand,
10867
+ uploadStaticCommand,
10868
+ preUploadStaticCommand,
10869
+ serverStartupBundleCommand
10870
+ ]
8295
10871
  };
8296
10872
 
8297
10873
  // src/commands/index.ts
@@ -8309,13 +10885,13 @@ var commands = [
8309
10885
 
8310
10886
  // src/index.ts
8311
10887
  for (const filename of [".env.local", ".env"]) {
8312
- const envPath = path25.join(process.cwd(), filename);
8313
- if (fs29.existsSync(envPath)) {
10888
+ const envPath = path27.join(process.cwd(), filename);
10889
+ if (fs31.existsSync(envPath)) {
8314
10890
  dotenvConfig({ path: envPath });
8315
10891
  }
8316
10892
  }
8317
- var __dirname = path25.dirname(fileURLToPath5(import.meta.url));
8318
- var pkg = JSON.parse(fs29.readFileSync(path25.join(__dirname, "../package.json"), "utf-8"));
10893
+ var __dirname = path27.dirname(fileURLToPath6(import.meta.url));
10894
+ var pkg = JSON.parse(fs31.readFileSync(path27.join(__dirname, "../package.json"), "utf-8"));
8319
10895
  var cli = new FullstackCLI(pkg.version);
8320
10896
  cli.useAll(commands);
8321
10897
  cli.run();