@lark-apaas/fullstack-cli 1.1.64-alpha.20260901065806 → 1.1.64-alpha.20260901212526

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