browsermation 0.0.98-beta.1 → 0.0.98-beta.3

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.
Files changed (2) hide show
  1. package/dist/bin/cli.js +963 -251
  2. package/package.json +3 -3
package/dist/bin/cli.js CHANGED
@@ -3638,7 +3638,7 @@ var require_util = __commonJS({
3638
3638
  return path7;
3639
3639
  }
3640
3640
  exports2.normalize = normalize;
3641
- function join9(aRoot, aPath) {
3641
+ function join8(aRoot, aPath) {
3642
3642
  if (aRoot === "") {
3643
3643
  aRoot = ".";
3644
3644
  }
@@ -3670,11 +3670,11 @@ var require_util = __commonJS({
3670
3670
  }
3671
3671
  return joined;
3672
3672
  }
3673
- exports2.join = join9;
3673
+ exports2.join = join8;
3674
3674
  exports2.isAbsolute = function(aPath) {
3675
3675
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
3676
3676
  };
3677
- function relative4(aRoot, aPath) {
3677
+ function relative5(aRoot, aPath) {
3678
3678
  if (aRoot === "") {
3679
3679
  aRoot = ".";
3680
3680
  }
@@ -3693,7 +3693,7 @@ var require_util = __commonJS({
3693
3693
  }
3694
3694
  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
3695
3695
  }
3696
- exports2.relative = relative4;
3696
+ exports2.relative = relative5;
3697
3697
  var supportsNullProto = (function() {
3698
3698
  var obj = /* @__PURE__ */ Object.create(null);
3699
3699
  return !("__proto__" in obj);
@@ -3843,7 +3843,7 @@ var require_util = __commonJS({
3843
3843
  parsed.path = parsed.path.substring(0, index + 1);
3844
3844
  }
3845
3845
  }
3846
- sourceURL = join9(urlGenerate(parsed), sourceURL);
3846
+ sourceURL = join8(urlGenerate(parsed), sourceURL);
3847
3847
  }
3848
3848
  return normalize(sourceURL);
3849
3849
  }
@@ -15075,11 +15075,11 @@ ${lanes.join("\n")}
15075
15075
  return toComponents;
15076
15076
  }
15077
15077
  const components = toComponents.slice(start);
15078
- const relative4 = [];
15078
+ const relative5 = [];
15079
15079
  for (; start < fromComponents.length; start++) {
15080
- relative4.push("..");
15080
+ relative5.push("..");
15081
15081
  }
15082
- return ["", ...relative4, ...components];
15082
+ return ["", ...relative5, ...components];
15083
15083
  }
15084
15084
  function getRelativePathFromDirectory(fromDirectory, to, getCanonicalFileNameOrIgnoreCase) {
15085
15085
  Debug.assert(getRootLength(fromDirectory) > 0 === getRootLength(to) > 0, "Paths must either both be absolute or both be relative");
@@ -53636,11 +53636,11 @@ ${lanes.join("\n")}
53636
53636
  if (i < rootLength) {
53637
53637
  return void 0;
53638
53638
  }
53639
- const sep = directory.lastIndexOf(directorySeparator, i - 1);
53640
- if (sep === -1) {
53639
+ const sep3 = directory.lastIndexOf(directorySeparator, i - 1);
53640
+ if (sep3 === -1) {
53641
53641
  return void 0;
53642
53642
  }
53643
- return directory.substr(0, Math.max(sep, rootLength));
53643
+ return directory.substr(0, Math.max(sep3, rootLength));
53644
53644
  }
53645
53645
  }
53646
53646
  }
@@ -59546,9 +59546,9 @@ ${lanes.join("\n")}
59546
59546
  if (!startsWithDirectory(target, realPathDirectory, getCanonicalFileName)) {
59547
59547
  return;
59548
59548
  }
59549
- const relative4 = getRelativePathFromDirectory(realPathDirectory, target, getCanonicalFileName);
59549
+ const relative5 = getRelativePathFromDirectory(realPathDirectory, target, getCanonicalFileName);
59550
59550
  for (const symlinkDirectory of symlinkDirectories) {
59551
- const option = resolvePath(symlinkDirectory, relative4);
59551
+ const option = resolvePath(symlinkDirectory, relative5);
59552
59552
  const result2 = cb(option, target === referenceRedirect);
59553
59553
  shouldFilterIgnoredPaths = true;
59554
59554
  if (result2) return result2;
@@ -218118,7 +218118,7 @@ var require_path_browserify = __commonJS({
218118
218118
  }
218119
218119
  return res;
218120
218120
  }
218121
- function _format(sep, pathObject) {
218121
+ function _format(sep3, pathObject) {
218122
218122
  var dir = pathObject.dir || pathObject.root;
218123
218123
  var base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
218124
218124
  if (!dir) {
@@ -218127,7 +218127,7 @@ var require_path_browserify = __commonJS({
218127
218127
  if (dir === pathObject.root) {
218128
218128
  return dir + base;
218129
218129
  }
218130
- return dir + sep + base;
218130
+ return dir + sep3 + base;
218131
218131
  }
218132
218132
  var posix = {
218133
218133
  // path.resolve([from ...], to)
@@ -218178,7 +218178,7 @@ var require_path_browserify = __commonJS({
218178
218178
  assertPath(path7);
218179
218179
  return path7.length > 0 && path7.charCodeAt(0) === 47;
218180
218180
  },
218181
- join: function join9() {
218181
+ join: function join8() {
218182
218182
  if (arguments.length === 0)
218183
218183
  return ".";
218184
218184
  var joined;
@@ -218196,7 +218196,7 @@ var require_path_browserify = __commonJS({
218196
218196
  return ".";
218197
218197
  return posix.normalize(joined);
218198
218198
  },
218199
- relative: function relative4(from, to) {
218199
+ relative: function relative5(from, to) {
218200
218200
  assertPath(from);
218201
218201
  assertPath(to);
218202
218202
  if (from === to) return "";
@@ -218265,7 +218265,7 @@ var require_path_browserify = __commonJS({
218265
218265
  _makeLong: function _makeLong(path7) {
218266
218266
  return path7;
218267
218267
  },
218268
- dirname: function dirname2(path7) {
218268
+ dirname: function dirname3(path7) {
218269
218269
  assertPath(path7);
218270
218270
  if (path7.length === 0) return ".";
218271
218271
  var code = path7.charCodeAt(0);
@@ -220696,10 +220696,10 @@ var require_dist2 = __commonJS({
220696
220696
  ignore: ignorePatterns
220697
220697
  };
220698
220698
  }
220699
- function formatPaths(paths, relative4) {
220699
+ function formatPaths(paths, relative5) {
220700
220700
  for (let i = paths.length - 1; i >= 0; i--) {
220701
220701
  const path$2 = paths[i];
220702
- paths[i] = relative4(path$2);
220702
+ paths[i] = relative5(path$2);
220703
220703
  }
220704
220704
  return paths;
220705
220705
  }
@@ -220796,26 +220796,26 @@ var require_dist2 = __commonJS({
220796
220796
  props.root = props.root.replace(BACKSLASHES, "");
220797
220797
  const root = props.root;
220798
220798
  if (options.debug) log("internal properties:", props);
220799
- const relative4 = cwd !== root && !options.absolute && buildRelative(cwd, props.root);
220800
- return [new fdir.fdir(fdirOptions).crawl(root), relative4];
220799
+ const relative5 = cwd !== root && !options.absolute && buildRelative(cwd, props.root);
220800
+ return [new fdir.fdir(fdirOptions).crawl(root), relative5];
220801
220801
  }
220802
220802
  async function glob(patternsOrOptions, options) {
220803
220803
  if (patternsOrOptions && (options === null || options === void 0 ? void 0 : options.patterns)) throw new Error("Cannot pass patterns as both an argument and an option");
220804
220804
  const isModern = isReadonlyArray(patternsOrOptions) || typeof patternsOrOptions === "string";
220805
220805
  const opts = isModern ? options : patternsOrOptions;
220806
220806
  const patterns = isModern ? patternsOrOptions : patternsOrOptions.patterns;
220807
- const [crawler, relative4] = getCrawler(patterns, opts);
220808
- if (!relative4) return crawler.withPromise();
220809
- return formatPaths(await crawler.withPromise(), relative4);
220807
+ const [crawler, relative5] = getCrawler(patterns, opts);
220808
+ if (!relative5) return crawler.withPromise();
220809
+ return formatPaths(await crawler.withPromise(), relative5);
220810
220810
  }
220811
220811
  function globSync(patternsOrOptions, options) {
220812
220812
  if (patternsOrOptions && (options === null || options === void 0 ? void 0 : options.patterns)) throw new Error("Cannot pass patterns as both an argument and an option");
220813
220813
  const isModern = isReadonlyArray(patternsOrOptions) || typeof patternsOrOptions === "string";
220814
220814
  const opts = isModern ? options : patternsOrOptions;
220815
220815
  const patterns = isModern ? patternsOrOptions : patternsOrOptions.patterns;
220816
- const [crawler, relative4] = getCrawler(patterns, opts);
220817
- if (!relative4) return crawler.sync();
220818
- return formatPaths(crawler.sync(), relative4);
220816
+ const [crawler, relative5] = getCrawler(patterns, opts);
220817
+ if (!relative5) return crawler.sync();
220818
+ return formatPaths(crawler.sync(), relative5);
220819
220819
  }
220820
220820
  exports2.convertPathToPattern = convertPathToPattern;
220821
220821
  exports2.escapePath = escapePath;
@@ -246761,8 +246761,8 @@ var require_readdir_glob = __commonJS({
246761
246761
  useStat = true;
246762
246762
  }
246763
246763
  const filename = dir + "/" + name;
246764
- const relative4 = filename.slice(1);
246765
- const absolute = path7 + "/" + relative4;
246764
+ const relative5 = filename.slice(1);
246765
+ const absolute = path7 + "/" + relative5;
246766
246766
  let stats = null;
246767
246767
  if (useStat || followSymlinks) {
246768
246768
  stats = await stat(absolute, followSymlinks);
@@ -246774,12 +246774,12 @@ var require_readdir_glob = __commonJS({
246774
246774
  stats = { isDirectory: () => false };
246775
246775
  }
246776
246776
  if (stats.isDirectory()) {
246777
- if (!shouldSkip(relative4)) {
246778
- yield { relative: relative4, absolute, stats };
246777
+ if (!shouldSkip(relative5)) {
246778
+ yield { relative: relative5, absolute, stats };
246779
246779
  yield* exploreWalkAsync(filename, path7, followSymlinks, useStat, shouldSkip, false);
246780
246780
  }
246781
246781
  } else {
246782
- yield { relative: relative4, absolute, stats };
246782
+ yield { relative: relative5, absolute, stats };
246783
246783
  }
246784
246784
  }
246785
246785
  }
@@ -246849,11 +246849,11 @@ var require_readdir_glob = __commonJS({
246849
246849
  }
246850
246850
  setTimeout(() => this._next(), 0);
246851
246851
  }
246852
- _shouldSkipDirectory(relative4) {
246853
- return this.skipMatchers.some((m) => m.match(relative4));
246852
+ _shouldSkipDirectory(relative5) {
246853
+ return this.skipMatchers.some((m) => m.match(relative5));
246854
246854
  }
246855
- _fileMatches(relative4, isDirectory) {
246856
- const file = relative4 + (isDirectory ? "/" : "");
246855
+ _fileMatches(relative5, isDirectory) {
246856
+ const file = relative5 + (isDirectory ? "/" : "");
246857
246857
  return (this.matchers.length === 0 || this.matchers.some((m) => m.match(file))) && !this.ignoreMatchers.some((m) => m.match(file)) && (!this.options.nodir || !isDirectory);
246858
246858
  }
246859
246859
  _next() {
@@ -246862,16 +246862,16 @@ var require_readdir_glob = __commonJS({
246862
246862
  if (!obj.done) {
246863
246863
  const isDirectory = obj.value.stats.isDirectory();
246864
246864
  if (this._fileMatches(obj.value.relative, isDirectory)) {
246865
- let relative4 = obj.value.relative;
246865
+ let relative5 = obj.value.relative;
246866
246866
  let absolute = obj.value.absolute;
246867
246867
  if (this.options.mark && isDirectory) {
246868
- relative4 += "/";
246868
+ relative5 += "/";
246869
246869
  absolute += "/";
246870
246870
  }
246871
246871
  if (this.options.stat) {
246872
- this.emit("match", { relative: relative4, absolute, stat: obj.value.stats });
246872
+ this.emit("match", { relative: relative5, absolute, stat: obj.value.stats });
246873
246873
  } else {
246874
- this.emit("match", { relative: relative4, absolute });
246874
+ this.emit("match", { relative: relative5, absolute });
246875
246875
  }
246876
246876
  }
246877
246877
  this._next(this.iterator);
@@ -249841,7 +249841,7 @@ var require_BufferList = __commonJS({
249841
249841
  this.head = this.tail = null;
249842
249842
  this.length = 0;
249843
249843
  };
249844
- BufferList.prototype.join = function join9(s) {
249844
+ BufferList.prototype.join = function join8(s) {
249845
249845
  if (this.length === 0) return "";
249846
249846
  var p = this.head;
249847
249847
  var ret = "" + p.data;
@@ -252376,8 +252376,8 @@ var require_primordials = __commonJS({
252376
252376
  ArrayPrototypeIndexOf(self2, el) {
252377
252377
  return self2.indexOf(el);
252378
252378
  },
252379
- ArrayPrototypeJoin(self2, sep) {
252380
- return self2.join(sep);
252379
+ ArrayPrototypeJoin(self2, sep3) {
252380
+ return self2.join(sep3);
252381
252381
  },
252382
252382
  ArrayPrototypeMap(self2, fn) {
252383
252383
  return self2.map(fn);
@@ -264413,7 +264413,7 @@ var require_commonjs7 = __commonJS({
264413
264413
  *
264414
264414
  * @internal
264415
264415
  */
264416
- constructor(cwd = process.cwd(), pathImpl, sep, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS } = {}) {
264416
+ constructor(cwd = process.cwd(), pathImpl, sep3, { nocase, childrenCacheSize = 16 * 1024, fs = defaultFS } = {}) {
264417
264417
  this.#fs = fsFromOption(fs);
264418
264418
  if (cwd instanceof URL || cwd.startsWith("file://")) {
264419
264419
  cwd = (0, node_url_1.fileURLToPath)(cwd);
@@ -264424,7 +264424,7 @@ var require_commonjs7 = __commonJS({
264424
264424
  this.#resolveCache = new ResolveCache();
264425
264425
  this.#resolvePosixCache = new ResolveCache();
264426
264426
  this.#children = new ChildrenCache(childrenCacheSize);
264427
- const split = cwdPath.substring(this.rootPath.length).split(sep);
264427
+ const split = cwdPath.substring(this.rootPath.length).split(sep3);
264428
264428
  if (split.length === 1 && !split[0]) {
264429
264429
  split.pop();
264430
264430
  }
@@ -265267,10 +265267,10 @@ var require_ignore = __commonJS({
265267
265267
  ignored(p) {
265268
265268
  const fullpath = p.fullpath();
265269
265269
  const fullpaths = `${fullpath}/`;
265270
- const relative4 = p.relative() || ".";
265271
- const relatives = `${relative4}/`;
265270
+ const relative5 = p.relative() || ".";
265271
+ const relatives = `${relative5}/`;
265272
265272
  for (const m of this.relative) {
265273
- if (m.match(relative4) || m.match(relatives))
265273
+ if (m.match(relative5) || m.match(relatives))
265274
265274
  return true;
265275
265275
  }
265276
265276
  for (const m of this.absolute) {
@@ -265281,9 +265281,9 @@ var require_ignore = __commonJS({
265281
265281
  }
265282
265282
  childrenIgnored(p) {
265283
265283
  const fullpath = p.fullpath() + "/";
265284
- const relative4 = (p.relative() || ".") + "/";
265284
+ const relative5 = (p.relative() || ".") + "/";
265285
265285
  for (const m of this.relativeChildren) {
265286
- if (m.match(relative4))
265286
+ if (m.match(relative5))
265287
265287
  return true;
265288
265288
  }
265289
265289
  for (const m of this.absoluteChildren) {
@@ -282462,8 +282462,8 @@ var require_follow_redirects = __commonJS({
282462
282462
  }
282463
282463
  return parsed;
282464
282464
  }
282465
- function resolveUrl(relative4, base) {
282466
- return useNativeURL ? new URL2(relative4, base) : parseUrl(url2.resolve(base, relative4));
282465
+ function resolveUrl(relative5, base) {
282466
+ return useNativeURL ? new URL2(relative5, base) : parseUrl(url2.resolve(base, relative5));
282467
282467
  }
282468
282468
  function validateUrl(input) {
282469
282469
  if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -283064,7 +283064,7 @@ var {
283064
283064
  // package.json
283065
283065
  var package_default = {
283066
283066
  name: "browsermation",
283067
- version: "0.0.98-beta.1",
283067
+ version: "0.0.98-beta.3",
283068
283068
  description: "The testing platform for Playwright by Browsermation.",
283069
283069
  main: "./dist/index.js",
283070
283070
  types: "./dist/index.d.ts",
@@ -283075,7 +283075,7 @@ var package_default = {
283075
283075
  "dist"
283076
283076
  ],
283077
283077
  scripts: {
283078
- "build:cli": "esbuild src/bin/cli.ts --bundle --platform=node --external:@browsermation/browser-engine --external:playwright --target=node22 --outfile=dist/bin/cli.js",
283078
+ "build:cli": "esbuild src/bin/cli.ts --bundle --platform=node --external:@browsermation/browser-engine --external:playwright --external:esbuild --target=node22 --outfile=dist/bin/cli.js",
283079
283079
  build: "rm -rf dist && npm run build:cli",
283080
283080
  start: "node dist/bin/cli.js",
283081
283081
  "build:start": "npm run build && npm start",
@@ -283096,11 +283096,11 @@ var package_default = {
283096
283096
  devDependencies: {
283097
283097
  "@types/archiver": "^7.0.0",
283098
283098
  "@types/node": "^25.0.3",
283099
- esbuild: "^0.27.2",
283100
283099
  typescript: "^5.9.3",
283101
283100
  vitest: "^4.0.16"
283102
283101
  },
283103
283102
  dependencies: {
283103
+ esbuild: "^0.27.2",
283104
283104
  "@browsermation/browser-engine": "^0.0.44",
283105
283105
  archiver: "^7.0.1",
283106
283106
  axios: "^1.13.2",
@@ -285760,100 +285760,55 @@ var ASSET_EXTENSIONS = [
285760
285760
  ".mp3",
285761
285761
  ".wav"
285762
285762
  ];
285763
- var ImportExtractor = class {
285764
- project;
285765
- constructor(tsconfigPath) {
285766
- this.project = new import_ts_morph.Project({
285767
- tsConfigFilePath: tsconfigPath,
285768
- skipAddingFilesFromTsConfig: true,
285769
- compilerOptions: {
285770
- allowJs: true,
285771
- checkJs: false
285772
- }
285773
- });
285774
- }
285763
+ var FastImportExtractor = class _FastImportExtractor {
285764
+ static IMPORT_PATTERNS = [
285765
+ // ESM imports: import ... from 'module'
285766
+ /import\s+(?:(?:[\w*{}\s,]+)\s+from\s+)?['"]([^'"]+)['"]/g,
285767
+ // ESM exports: export ... from 'module'
285768
+ /export\s+(?:(?:[\w*{}\s,]+)\s+from\s+)?['"]([^'"]+)['"]/g,
285769
+ // Dynamic imports: import('module')
285770
+ /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
285771
+ // Require: require('module')
285772
+ /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g
285773
+ ];
285775
285774
  extractImports(filePath) {
285776
- let sourceFile;
285775
+ const { readFileSync: readFileSync3 } = require("fs");
285776
+ let content;
285777
285777
  try {
285778
- sourceFile = this.project.addSourceFileAtPath(filePath);
285778
+ content = readFileSync3(filePath, "utf-8");
285779
285779
  } catch {
285780
- sourceFile = this.project.getSourceFile(filePath) || this.project.addSourceFileAtPath(filePath);
285780
+ return { staticImports: [], dynamicImports: [], requireCalls: [], assetImports: [] };
285781
+ }
285782
+ content = this.removeComments(content);
285783
+ const staticImports = [];
285784
+ const dynamicImports = [];
285785
+ const requireCalls = [];
285786
+ for (const match of content.matchAll(_FastImportExtractor.IMPORT_PATTERNS[0])) {
285787
+ if (match[1]) staticImports.push(match[1]);
285788
+ }
285789
+ for (const match of content.matchAll(_FastImportExtractor.IMPORT_PATTERNS[1])) {
285790
+ if (match[1]) staticImports.push(match[1]);
285791
+ }
285792
+ for (const match of content.matchAll(_FastImportExtractor.IMPORT_PATTERNS[2])) {
285793
+ if (match[1]) dynamicImports.push(match[1]);
285794
+ }
285795
+ for (const match of content.matchAll(_FastImportExtractor.IMPORT_PATTERNS[3])) {
285796
+ if (match[1]) requireCalls.push(match[1]);
285781
285797
  }
285782
- const staticImports = this.getStaticImports(sourceFile);
285783
- const dynamicImports = this.getDynamicImports(sourceFile);
285784
- const requireCalls = this.getRequireCalls(sourceFile);
285785
285798
  const allImports = [...staticImports, ...dynamicImports, ...requireCalls];
285786
285799
  return {
285787
285800
  staticImports,
285788
285801
  dynamicImports,
285789
285802
  requireCalls,
285790
- assetImports: this.filterAssetImports(allImports)
285803
+ assetImports: allImports.filter(
285804
+ (imp) => ASSET_EXTENSIONS.some((ext) => imp.toLowerCase().endsWith(ext))
285805
+ )
285791
285806
  };
285792
285807
  }
285793
- getStaticImports(sourceFile) {
285794
- const imports = [];
285795
- for (const imp of sourceFile.getImportDeclarations()) {
285796
- imports.push(imp.getModuleSpecifierValue());
285797
- }
285798
- for (const exp of sourceFile.getExportDeclarations()) {
285799
- const moduleSpecifier = exp.getModuleSpecifierValue();
285800
- if (moduleSpecifier) {
285801
- imports.push(moduleSpecifier);
285802
- }
285803
- }
285804
- return imports;
285805
- }
285806
- getDynamicImports(sourceFile) {
285807
- const imports = [];
285808
- const callExpressions = sourceFile.getDescendantsOfKind(
285809
- import_ts_morph.SyntaxKind.CallExpression
285810
- );
285811
- for (const call of callExpressions) {
285812
- const expression = call.getExpression();
285813
- if (expression.getKind() === import_ts_morph.SyntaxKind.ImportKeyword) {
285814
- const args = call.getArguments();
285815
- if (args.length > 0) {
285816
- const firstArg = args[0];
285817
- if (firstArg.isKind(import_ts_morph.SyntaxKind.StringLiteral)) {
285818
- imports.push(firstArg.getLiteralValue());
285819
- } else if (firstArg.isKind(import_ts_morph.SyntaxKind.NoSubstitutionTemplateLiteral)) {
285820
- imports.push(firstArg.getLiteralValue());
285821
- } else if (firstArg.isKind(import_ts_morph.SyntaxKind.TemplateExpression)) {
285822
- const head = firstArg.getHead().getLiteralValue();
285823
- if (head) {
285824
- console.warn(
285825
- `Dynamic import with template expression found: ${head}... - may not be fully resolved`
285826
- );
285827
- }
285828
- }
285829
- }
285830
- }
285831
- }
285832
- return imports;
285833
- }
285834
- getRequireCalls(sourceFile) {
285835
- const imports = [];
285836
- const callExpressions = sourceFile.getDescendantsOfKind(
285837
- import_ts_morph.SyntaxKind.CallExpression
285838
- );
285839
- for (const call of callExpressions) {
285840
- const expression = call.getExpression();
285841
- if (expression.isKind(import_ts_morph.SyntaxKind.Identifier) && expression.getText() === "require") {
285842
- const args = call.getArguments();
285843
- if (args.length > 0) {
285844
- const firstArg = args[0];
285845
- if (firstArg.isKind(import_ts_morph.SyntaxKind.StringLiteral)) {
285846
- imports.push(firstArg.getLiteralValue());
285847
- }
285848
- }
285849
- }
285850
- }
285851
- return imports;
285852
- }
285853
- filterAssetImports(imports) {
285854
- return imports.filter(
285855
- (imp) => ASSET_EXTENSIONS.some((ext) => imp.toLowerCase().endsWith(ext))
285856
- );
285808
+ removeComments(content) {
285809
+ content = content.replace(/\/\/.*$/gm, "");
285810
+ content = content.replace(/\/\*[\s\S]*?\*\//g, "");
285811
+ return content;
285857
285812
  }
285858
285813
  };
285859
285814
 
@@ -286044,11 +285999,8 @@ var PathResolver = class {
286044
285999
  };
286045
286000
 
286046
286001
  // src/analyzer/dependency-graph.ts
286047
- var EXCLUDED_PACKAGES = [
286048
- "@playwright/test",
286049
- "playwright",
286050
- "playwright-core",
286051
- // Node.js built-in modules
286002
+ var PLAYWRIGHT_PACKAGES = ["@playwright/test", "playwright", "playwright-core"];
286003
+ var NODE_BUILTINS = /* @__PURE__ */ new Set([
286052
286004
  "assert",
286053
286005
  "buffer",
286054
286006
  "child_process",
@@ -286085,30 +286037,57 @@ var EXCLUDED_PACKAGES = [
286085
286037
  "vm",
286086
286038
  "worker_threads",
286087
286039
  "zlib"
286088
- ];
286040
+ ]);
286089
286041
  var DependencyGraphBuilder = class {
286090
286042
  graph = /* @__PURE__ */ new Map();
286091
286043
  visited = /* @__PURE__ */ new Set();
286044
+ skipped = /* @__PURE__ */ new Set();
286092
286045
  nodeModuleDeps = /* @__PURE__ */ new Set();
286093
286046
  assets = /* @__PURE__ */ new Set();
286094
286047
  extractor;
286095
286048
  resolver;
286096
286049
  projectRoot;
286050
+ testDir = "";
286097
286051
  constructor(options) {
286098
286052
  this.projectRoot = options.projectRoot;
286099
- const tsconfigPath = path2.join(options.projectRoot, "tsconfig.json");
286100
- this.extractor = new ImportExtractor(
286101
- require("fs").existsSync(tsconfigPath) ? tsconfigPath : void 0
286102
- );
286053
+ this.extractor = new FastImportExtractor();
286103
286054
  this.resolver = new PathResolver(options.projectRoot);
286104
286055
  }
286105
- async build(entryPoints) {
286056
+ /**
286057
+ * Check if a file is within the test directory (or is a config file)
286058
+ * Files in node_modules are never test-related (they're npm dependencies)
286059
+ */
286060
+ isTestRelatedFile(filePath) {
286061
+ const relativePath = path2.relative(this.projectRoot, filePath);
286062
+ if (relativePath.startsWith("node_modules")) {
286063
+ return false;
286064
+ }
286065
+ if (relativePath.includes("playwright.config") || relativePath === "tsconfig.json") {
286066
+ return true;
286067
+ }
286068
+ const testDirRelative = path2.relative(this.projectRoot, this.testDir);
286069
+ if (testDirRelative === "") {
286070
+ return true;
286071
+ }
286072
+ if (relativePath.startsWith(testDirRelative + path2.sep) || relativePath === testDirRelative) {
286073
+ return true;
286074
+ }
286075
+ return false;
286076
+ }
286077
+ async build(entryPoints, testDir) {
286078
+ this.testDir = testDir;
286079
+ const testDirRelative = path2.relative(this.projectRoot, testDir);
286080
+ console.log(`Test directory: ${testDirRelative || "(project root)"}`);
286106
286081
  const queue = [...entryPoints];
286107
286082
  while (queue.length > 0) {
286108
286083
  const filePath = queue.shift();
286109
286084
  const normalizedPath = path2.resolve(filePath);
286110
286085
  if (this.visited.has(normalizedPath)) continue;
286111
286086
  this.visited.add(normalizedPath);
286087
+ if (!this.isTestRelatedFile(normalizedPath)) {
286088
+ this.skipped.add(path2.relative(this.projectRoot, normalizedPath));
286089
+ continue;
286090
+ }
286112
286091
  const node = this.processFile(normalizedPath);
286113
286092
  if (!node) continue;
286114
286093
  this.graph.set(normalizedPath, node);
@@ -286118,6 +286097,9 @@ var DependencyGraphBuilder = class {
286118
286097
  }
286119
286098
  }
286120
286099
  }
286100
+ if (this.skipped.size > 0) {
286101
+ console.log(`Skipped ${this.skipped.size} app source files (not needed for tests)`);
286102
+ }
286121
286103
  return {
286122
286104
  nodes: this.graph,
286123
286105
  entryPoints,
@@ -286168,6 +286150,9 @@ var DependencyGraphBuilder = class {
286168
286150
  if (moduleSpecifier.startsWith("node:")) {
286169
286151
  return null;
286170
286152
  }
286153
+ if (moduleSpecifier.startsWith(".") || moduleSpecifier.startsWith("/")) {
286154
+ return null;
286155
+ }
286171
286156
  if (moduleSpecifier.startsWith("@")) {
286172
286157
  const parts2 = moduleSpecifier.split("/");
286173
286158
  if (parts2.length >= 2) {
@@ -286176,10 +286161,36 @@ var DependencyGraphBuilder = class {
286176
286161
  return null;
286177
286162
  }
286178
286163
  const parts = moduleSpecifier.split("/");
286179
- return parts[0] || null;
286164
+ const packageName = parts[0] || null;
286165
+ const invalidPackageNames = [
286166
+ "src",
286167
+ "lib",
286168
+ "dist",
286169
+ "build",
286170
+ "app",
286171
+ "pages",
286172
+ "components",
286173
+ "utils",
286174
+ "helpers",
286175
+ "services",
286176
+ "types",
286177
+ "models",
286178
+ "config",
286179
+ "public",
286180
+ "assets",
286181
+ "styles",
286182
+ "tests",
286183
+ "test",
286184
+ "spec",
286185
+ "e2e"
286186
+ ];
286187
+ if (packageName && invalidPackageNames.includes(packageName)) {
286188
+ return null;
286189
+ }
286190
+ return packageName;
286180
286191
  }
286181
286192
  isExcludedPackage(packageName) {
286182
- return EXCLUDED_PACKAGES.includes(packageName);
286193
+ return PLAYWRIGHT_PACKAGES.includes(packageName) || NODE_BUILTINS.has(packageName);
286183
286194
  }
286184
286195
  determineType(filePath) {
286185
286196
  const lowerPath = filePath.toLowerCase();
@@ -286229,8 +286240,19 @@ var PackageGenerator = class {
286229
286240
  if (dep === "@playwright/test" || dep === "playwright" || dep === "playwright-core") {
286230
286241
  continue;
286231
286242
  }
286232
- if (allDeps[dep]) {
286233
- dependencies[dep] = allDeps[dep];
286243
+ const declaredVersion = allDeps[dep];
286244
+ if (declaredVersion?.startsWith("workspace:")) {
286245
+ const version = this.getVersionFromNodeModules(dep);
286246
+ if (version) {
286247
+ dependencies[dep] = `^${version}`;
286248
+ } else {
286249
+ console.warn(
286250
+ `Warning: Could not resolve workspace dependency ${dep}, using "latest"`
286251
+ );
286252
+ dependencies[dep] = "latest";
286253
+ }
286254
+ } else if (declaredVersion) {
286255
+ dependencies[dep] = declaredVersion;
286234
286256
  } else {
286235
286257
  const version = this.getVersionFromNodeModules(dep);
286236
286258
  if (version) {
@@ -286277,6 +286299,19 @@ var PlaywrightConfigParser = class {
286277
286299
  constructor(projectRoot) {
286278
286300
  this.projectRoot = projectRoot;
286279
286301
  }
286302
+ /**
286303
+ * Detect package manager based on lock files
286304
+ * Returns the command prefix to run playwright (e.g., 'yarn', 'pnpm', 'npx')
286305
+ */
286306
+ detectPackageManager() {
286307
+ if ((0, import_node_fs3.existsSync)(path4.join(this.projectRoot, "yarn.lock"))) {
286308
+ return "yarn";
286309
+ }
286310
+ if ((0, import_node_fs3.existsSync)(path4.join(this.projectRoot, "pnpm-lock.yaml"))) {
286311
+ return "pnpm";
286312
+ }
286313
+ return "npx";
286314
+ }
286280
286315
  async findConfig() {
286281
286316
  const candidates = [
286282
286317
  "playwright.config.ts",
@@ -286293,12 +286328,15 @@ var PlaywrightConfigParser = class {
286293
286328
  return null;
286294
286329
  }
286295
286330
  /**
286296
- * Use `npx playwright test --list --reporter=json` to discover test files
286331
+ * Use playwright test --list --reporter=json to discover test files
286297
286332
  * This respects all Playwright config settings (testDir, testMatch, projects, etc.)
286333
+ * Detects package manager (yarn/pnpm/npm) to run the correct command
286334
+ * Returns both test files and the detected testDir from Playwright's config
286298
286335
  */
286299
286336
  async getTestFilesFromPlaywright(project) {
286300
286337
  try {
286301
- let command = "npx playwright test --list --reporter=json";
286338
+ const pm = this.detectPackageManager();
286339
+ let command = `${pm} playwright test --list --reporter=json`;
286302
286340
  if (project) {
286303
286341
  command += ` --project="${project}"`;
286304
286342
  }
@@ -286311,9 +286349,10 @@ var PlaywrightConfigParser = class {
286311
286349
  const parsed = JSON.parse(output);
286312
286350
  const rootDir = parsed.config?.rootDir || this.projectRoot;
286313
286351
  const files = this.extractFilesFromSuites(parsed.suites);
286314
- return [...new Set(files)].map(
286352
+ const absoluteFiles = [...new Set(files)].map(
286315
286353
  (file) => path4.isAbsolute(file) ? file : path4.join(rootDir, file)
286316
286354
  );
286355
+ return { files: absoluteFiles, testDir: rootDir };
286317
286356
  } catch (error2) {
286318
286357
  console.warn(
286319
286358
  "Warning: Could not run playwright test --list, falling back to file scan"
@@ -286321,9 +286360,35 @@ var PlaywrightConfigParser = class {
286321
286360
  if (error2 instanceof Error && error2.message) {
286322
286361
  console.warn(` ${error2.message.split("\n")[0]}`);
286323
286362
  }
286324
- return this.fallbackGetTestFiles();
286363
+ const files = await this.fallbackGetTestFiles();
286364
+ const testDir = this.findCommonParentDir(files);
286365
+ return { files, testDir };
286325
286366
  }
286326
286367
  }
286368
+ /**
286369
+ * Find the common parent directory of all given file paths
286370
+ * Excludes node_modules paths since those are npm dependencies
286371
+ */
286372
+ findCommonParentDir(files) {
286373
+ const projectFiles = files.filter((f) => {
286374
+ const rel = path4.relative(this.projectRoot, f);
286375
+ return !rel.startsWith("node_modules");
286376
+ });
286377
+ if (projectFiles.length === 0) return this.projectRoot;
286378
+ if (projectFiles.length === 1) return path4.dirname(projectFiles[0]);
286379
+ const dirs = projectFiles.map((f) => path4.dirname(f));
286380
+ const parts = dirs.map((d) => d.split(path4.sep));
286381
+ const commonParts = [];
286382
+ for (let i = 0; i < parts[0].length; i++) {
286383
+ const part = parts[0][i];
286384
+ if (parts.every((p) => p[i] === part)) {
286385
+ commonParts.push(part);
286386
+ } else {
286387
+ break;
286388
+ }
286389
+ }
286390
+ return commonParts.join(path4.sep) || this.projectRoot;
286391
+ }
286327
286392
  /**
286328
286393
  * Recursively extract file paths from Playwright's suite structure
286329
286394
  */
@@ -286348,10 +286413,19 @@ var PlaywrightConfigParser = class {
286348
286413
  }
286349
286414
  /**
286350
286415
  * Fallback: scan for test files if playwright --list fails
286416
+ * Only scans e2e-specific directories to avoid picking up unit tests
286351
286417
  */
286352
286418
  async fallbackGetTestFiles() {
286353
- const { readdirSync, statSync } = await import("node:fs");
286354
- const testDirs = ["tests", "test", "e2e", "specs", "__tests__", "."];
286419
+ const { readdirSync, statSync, readFileSync: readFileSync3 } = await import("node:fs");
286420
+ const testDirs = [
286421
+ "e2e",
286422
+ "e2e-playwright",
286423
+ "playwright",
286424
+ "tests/e2e",
286425
+ "test/e2e",
286426
+ "tests",
286427
+ "test"
286428
+ ];
286355
286429
  const files = [];
286356
286430
  const testPatterns = [
286357
286431
  /\.spec\.(ts|js|tsx|jsx)$/,
@@ -286369,7 +286443,9 @@ var PlaywrightConfigParser = class {
286369
286443
  if (stat.isDirectory()) {
286370
286444
  scanDir(fullPath);
286371
286445
  } else if (testPatterns.some((p) => p.test(entry))) {
286372
- files.push(fullPath);
286446
+ if (this.isPlaywrightTest(fullPath, readFileSync3)) {
286447
+ files.push(fullPath);
286448
+ }
286373
286449
  }
286374
286450
  } catch {
286375
286451
  }
@@ -286385,6 +286461,22 @@ var PlaywrightConfigParser = class {
286385
286461
  }
286386
286462
  return [...new Set(files)];
286387
286463
  }
286464
+ /**
286465
+ * Check if a file is likely a Playwright test
286466
+ * Either imports from @playwright/test or is in an e2e-specific directory
286467
+ */
286468
+ isPlaywrightTest(filePath, readFileSync3) {
286469
+ const lowerPath = filePath.toLowerCase();
286470
+ if (lowerPath.includes("/e2e-playwright/") || lowerPath.includes("/e2e/") || lowerPath.includes("/playwright/")) {
286471
+ return true;
286472
+ }
286473
+ try {
286474
+ const content = readFileSync3(filePath, "utf-8");
286475
+ return content.includes("@playwright/test") || content.includes('from "playwright"') || content.includes("from 'playwright'");
286476
+ } catch {
286477
+ return false;
286478
+ }
286479
+ }
286388
286480
  };
286389
286481
 
286390
286482
  // src/analyzer/index.ts
@@ -286398,7 +286490,7 @@ async function analyze(options) {
286398
286490
  );
286399
286491
  }
286400
286492
  console.log(`Found config: ${path5.relative(projectRoot, configPath)}`);
286401
- const testFiles = await configParser.getTestFilesFromPlaywright(options.project);
286493
+ const { files: testFiles, testDir } = await configParser.getTestFilesFromPlaywright(options.project);
286402
286494
  console.log(`Found ${testFiles.length} test files via Playwright`);
286403
286495
  const entryPoints = [configPath, ...testFiles].filter(
286404
286496
  (file, index, self2) => self2.indexOf(file) === index
@@ -286408,7 +286500,7 @@ async function analyze(options) {
286408
286500
  console.warn("Warning: No test files found. Check your Playwright config.");
286409
286501
  }
286410
286502
  const graphBuilder = new DependencyGraphBuilder(options);
286411
- const graph = await graphBuilder.build(entryPoints);
286503
+ const graph = await graphBuilder.build(entryPoints, testDir);
286412
286504
  console.log(`Analyzed ${graph.nodes.size} source files`);
286413
286505
  console.log(`Found ${graph.nodeModuleDeps.size} npm dependencies`);
286414
286506
  console.log(`Found ${graph.assets.size} assets`);
@@ -286443,16 +286535,70 @@ var import_archiver = __toESM(require_archiver());
286443
286535
  var import_fs = require("fs");
286444
286536
  var import_path = require("path");
286445
286537
  var import_stream = require("stream");
286446
- async function zipFilesToBuffer(projectRoot, files, packageJson) {
286538
+ async function zipFilesToBuffer(projectRoot, files, packageJson, npmDeps) {
286447
286539
  const passthrough = new import_stream.PassThrough();
286448
286540
  const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
286541
+ const includedPackages = /* @__PURE__ */ new Set();
286542
+ const skippedPackages = /* @__PURE__ */ new Set();
286543
+ const workspacePackages = /* @__PURE__ */ new Set();
286544
+ const warnings = [];
286449
286545
  for (const file of files) {
286450
286546
  const filePath = (0, import_path.join)(projectRoot, file);
286451
286547
  if ((0, import_fs.existsSync)(filePath)) {
286452
286548
  archive.file(filePath, { name: file });
286453
286549
  }
286454
286550
  }
286455
- archive.append(JSON.stringify(packageJson, null, 2), { name: "package.json" });
286551
+ archive.append(JSON.stringify(packageJson, null, 2), {
286552
+ name: "package.json"
286553
+ });
286554
+ const skipPackages = /* @__PURE__ */ new Set([
286555
+ // Playwright - pre-installed on runner
286556
+ "@playwright/test",
286557
+ "playwright",
286558
+ "playwright-core",
286559
+ // Frameworks - not needed for remote test execution (app already running)
286560
+ "next",
286561
+ "nuxt",
286562
+ "gatsby",
286563
+ "vite",
286564
+ "webpack",
286565
+ "turbopack",
286566
+ // Dev tools - not needed at runtime
286567
+ "eslint",
286568
+ "prettier",
286569
+ "typescript",
286570
+ "ts-node",
286571
+ "tsx",
286572
+ "esbuild",
286573
+ "rollup",
286574
+ "parcel",
286575
+ // Test runners (we use Playwright)
286576
+ "jest",
286577
+ "vitest",
286578
+ "mocha",
286579
+ "jasmine"
286580
+ ]);
286581
+ if (npmDeps && npmDeps.size > 0) {
286582
+ const nodeModulesPath = (0, import_path.join)(projectRoot, "node_modules");
286583
+ if ((0, import_fs.existsSync)(nodeModulesPath)) {
286584
+ for (const dep of npmDeps) {
286585
+ if (skipPackages.has(dep)) {
286586
+ skippedPackages.add(dep);
286587
+ continue;
286588
+ }
286589
+ await addPackageWithDeps(
286590
+ archive,
286591
+ nodeModulesPath,
286592
+ dep,
286593
+ includedPackages,
286594
+ skipPackages,
286595
+ skippedPackages,
286596
+ warnings,
286597
+ workspacePackages
286598
+ );
286599
+ }
286600
+ }
286601
+ }
286456
286602
  archive.finalize();
286457
286603
  archive.pipe(passthrough);
286458
286604
  const chunks = [];
@@ -286466,7 +286612,451 @@ async function zipFilesToBuffer(projectRoot, files, packageJson) {
286466
286612
  }
286467
286613
  writeStream.end();
286468
286614
  console.log(`Created test bundle at ${tmpZipPath}`);
286469
- return Buffer.concat(chunks);
286615
+ return {
286616
+ buffer: Buffer.concat(chunks),
286617
+ includedPackages,
286618
+ skippedPackages,
286619
+ workspacePackages,
286620
+ warnings
286621
+ };
286622
+ }
286623
+ function isWorkspacePackage(packagePath, packageName) {
286624
+ try {
286625
+ const { lstatSync, readFileSync: readFileSync3 } = require("fs");
286626
+ const stats = lstatSync(packagePath);
286627
+ if (stats.isSymbolicLink()) {
286628
+ return true;
286629
+ }
286630
+ const pkgJsonPath = (0, import_path.join)(packagePath, "package.json");
286631
+ if ((0, import_fs.existsSync)(pkgJsonPath)) {
286632
+ const pkgJson = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
286633
+ const version = pkgJson.version || "";
286634
+ if (version === "0.0.0" || version === "^0.0.0" || version.startsWith("0.0.")) {
286635
+ return true;
286636
+ }
286637
+ const main = pkgJson.main || "";
286638
+ if (main.startsWith("src/") && main.endsWith(".ts")) {
286639
+ return true;
286640
+ }
286641
+ if (pkgJson.private === true && !pkgJson.publishConfig) {
286642
+ return true;
286643
+ }
286644
+ }
286645
+ if (packageName.startsWith("@") && packageName.includes("/")) {
286646
+ const scope = packageName.split("/")[0];
286647
+ const nodeModulesPath = (0, import_path.join)(packagePath, "..");
286648
+ const scopePath = (0, import_path.join)(nodeModulesPath, scope);
286649
+ if ((0, import_fs.existsSync)(scopePath)) {
286650
+ try {
286651
+ const { readdirSync } = require("fs");
286652
+ const scopePackages = readdirSync(scopePath);
286653
+ if (scopePackages.length > 5) {
286654
+ const symlinkCount = scopePackages.filter((pkg) => {
286655
+ try {
286656
+ return lstatSync((0, import_path.join)(scopePath, pkg)).isSymbolicLink();
286657
+ } catch {
286658
+ return false;
286659
+ }
286660
+ }).length;
286661
+ if (symlinkCount > 3) {
286662
+ return true;
286663
+ }
286664
+ }
286665
+ } catch {
286666
+ }
286667
+ }
286668
+ }
286669
+ return false;
286670
+ } catch {
286671
+ return false;
286672
+ }
286673
+ }
286674
+ function checkUnbuiltPackage(packagePath, packageName) {
286675
+ const pkgJsonPath = (0, import_path.join)(packagePath, "package.json");
286676
+ if (!(0, import_fs.existsSync)(pkgJsonPath)) {
286677
+ return null;
286678
+ }
286679
+ try {
286680
+ const { readFileSync: readFileSync3, lstatSync } = require("fs");
286681
+ const pkgJson = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
286682
+ const main = pkgJson.main || "";
286683
+ const isSourceMain = main.startsWith("src/") && main.endsWith(".ts");
286684
+ if (!isSourceMain) {
286685
+ return null;
286686
+ }
286687
+ const stats = lstatSync(packagePath);
286688
+ const isSymlink = stats.isSymbolicLink();
286689
+ const distPath = (0, import_path.join)(packagePath, "dist");
286690
+ const hasDistFolder = (0, import_fs.existsSync)(distPath);
286691
+ if (isSourceMain && !hasDistFolder) {
286692
+ const symlinkNote = isSymlink ? " (symlinked monorepo package)" : "";
286693
+ return `Package "${packageName}" has main="${main}" but no dist/ folder${symlinkNote}. This package needs to be built before bundling, or install the published version from npm.`;
286694
+ }
286695
+ } catch {
286696
+ }
286697
+ return null;
286698
+ }
286699
+ async function addWorkspacePackageFiltered(archive, packagePath, packageName) {
286700
+ const { readdirSync, statSync, lstatSync, realpathSync } = require("fs");
286701
+ const excludeDirs = /* @__PURE__ */ new Set([
286702
+ "node_modules",
286703
+ "src",
286704
+ "test",
286705
+ "tests",
286706
+ "__tests__",
286707
+ "__mocks__",
286708
+ "coverage",
286709
+ ".git",
286710
+ ".turbo",
286711
+ ".next",
286712
+ "playwright",
286713
+ "e2e",
286714
+ "cypress"
286715
+ ]);
286716
+ const excludePatterns = [
286717
+ /\.test\.(ts|js|tsx|jsx)$/,
286718
+ /\.spec\.(ts|js|tsx|jsx)$/,
286719
+ /\.stories\.(ts|js|tsx|jsx)$/,
286720
+ /^tsconfig.*\.json$/,
286721
+ /^jest\.config/,
286722
+ /^vitest\.config/,
286723
+ /^\.eslint/,
286724
+ /^\.prettier/
286725
+ ];
286726
+ let realPath = packagePath;
286727
+ try {
286728
+ const stats = lstatSync(packagePath);
286729
+ if (stats.isSymbolicLink()) {
286730
+ realPath = realpathSync(packagePath);
286731
+ }
286732
+ } catch {
286733
+ }
286734
+ const addFilesRecursively = (dir, relativeTo, archivePath) => {
286735
+ try {
286736
+ const entries = readdirSync(dir);
286737
+ for (const entry of entries) {
286738
+ const fullPath = (0, import_path.join)(dir, entry);
286739
+ const relPath = (0, import_path.join)(archivePath, entry);
286740
+ if (excludeDirs.has(entry)) continue;
286741
+ if (excludePatterns.some((p) => p.test(entry))) continue;
286742
+ try {
286743
+ const stat = statSync(fullPath);
286744
+ if (stat.isDirectory()) {
286745
+ addFilesRecursively(fullPath, relativeTo, relPath);
286746
+ } else {
286747
+ archive.file(fullPath, { name: relPath });
286748
+ }
286749
+ } catch {
286750
+ }
286751
+ }
286752
+ } catch {
286753
+ }
286754
+ };
286755
+ addFilesRecursively(realPath, realPath, `node_modules/${packageName}`);
286756
+ }
286757
+ async function addPackageFiltered(archive, packagePath, packageName) {
286758
+ const { readdirSync, statSync } = require("fs");
286759
+ const includeExtensions = /* @__PURE__ */ new Set([
286760
+ ".js",
286761
+ ".cjs",
286762
+ ".mjs",
286763
+ ".json",
286764
+ ".node",
286765
+ ".wasm"
286766
+ ]);
286767
+ const skipDirs = /* @__PURE__ */ new Set([
286768
+ "node_modules",
286769
+ "test",
286770
+ "tests",
286771
+ "__tests__",
286772
+ "__mocks__",
286773
+ "src",
286774
+ "source",
286775
+ "docs",
286776
+ "doc",
286777
+ "documentation",
286778
+ "example",
286779
+ "examples",
286780
+ "benchmark",
286781
+ "benchmarks",
286782
+ ".git",
286783
+ ".github",
286784
+ ".vscode",
286785
+ "coverage",
286786
+ ".nyc_output",
286787
+ "android",
286788
+ "ios"
286789
+ // Native mobile dirs
286790
+ ]);
286791
+ const skipPatterns = [
286792
+ /\.d\.ts$/,
286793
+ /\.d\.mts$/,
286794
+ /\.d\.cts$/,
286795
+ // Type definitions
286796
+ /\.map$/,
286797
+ /\.min\.js\.map$/,
286798
+ // Source maps
286799
+ /\.ts$/,
286800
+ /\.tsx$/,
286801
+ /\.mts$/,
286802
+ /\.cts$/,
286803
+ // TypeScript source
286804
+ /\.md$/,
286805
+ /\.txt$/,
286806
+ /\.rst$/,
286807
+ // Documentation
286808
+ /^LICENSE/,
286809
+ /^LICENCE/,
286810
+ /^README/,
286811
+ /^CHANGELOG/,
286812
+ /^HISTORY/,
286813
+ /^\.eslint/,
286814
+ /^\.prettier/,
286815
+ /^tsconfig/,
286816
+ /^jest\.config/,
286817
+ /^vitest\.config/,
286818
+ /\.test\.js$/,
286819
+ /\.spec\.js$/,
286820
+ /\.test\.cjs$/,
286821
+ /\.spec\.cjs$/,
286822
+ /^Makefile$/,
286823
+ /^Gruntfile/,
286824
+ /^Gulpfile/
286825
+ ];
286826
+ const addFilesRecursively = (dir, archivePath) => {
286827
+ try {
286828
+ const entries = readdirSync(dir);
286829
+ for (const entry of entries) {
286830
+ if (entry.startsWith(".") && entry !== ".bin") continue;
286831
+ if (skipDirs.has(entry)) continue;
286832
+ if (skipPatterns.some((p) => p.test(entry))) continue;
286833
+ const fullPath = (0, import_path.join)(dir, entry);
286834
+ const relPath = (0, import_path.join)(archivePath, entry);
286835
+ try {
286836
+ const stat = statSync(fullPath);
286837
+ if (stat.isDirectory()) {
286838
+ addFilesRecursively(fullPath, relPath);
286839
+ } else {
286840
+ const ext = entry.substring(entry.lastIndexOf("."));
286841
+ if (includeExtensions.has(ext) || entry === "package.json") {
286842
+ archive.file(fullPath, { name: relPath });
286843
+ }
286844
+ }
286845
+ } catch {
286846
+ }
286847
+ }
286848
+ } catch {
286849
+ }
286850
+ };
286851
+ addFilesRecursively(packagePath, `node_modules/${packageName}`);
286852
+ }
286853
+ async function addPackageWithDeps(archive, nodeModulesPath, packageName, includedPackages, skipPackages, skippedPackages, warnings, workspacePackages) {
286854
+ if (skipPackages.has(packageName)) {
286855
+ skippedPackages.add(packageName);
286856
+ return;
286857
+ }
286858
+ if (includedPackages.has(packageName)) {
286859
+ return;
286860
+ }
286861
+ const packagePath = (0, import_path.join)(nodeModulesPath, packageName);
286862
+ if (!(0, import_fs.existsSync)(packagePath)) {
286863
+ return;
286864
+ }
286865
+ const isWorkspace = isWorkspacePackage(packagePath, packageName);
286866
+ if (isWorkspace) {
286867
+ workspacePackages.add(packageName);
286868
+ }
286869
+ const unbuiltWarning = checkUnbuiltPackage(packagePath, packageName);
286870
+ if (unbuiltWarning) {
286871
+ warnings.push(unbuiltWarning);
286872
+ }
286873
+ includedPackages.add(packageName);
286874
+ if (isWorkspace) {
286875
+ await addWorkspacePackageFiltered(archive, packagePath, packageName);
286876
+ } else {
286877
+ archive.directory(packagePath, `node_modules/${packageName}`);
286878
+ }
286879
+ if (isWorkspace) {
286880
+ return;
286881
+ }
286882
+ const pkgJsonPath = (0, import_path.join)(packagePath, "package.json");
286883
+ if ((0, import_fs.existsSync)(pkgJsonPath)) {
286884
+ try {
286885
+ const { readFileSync: readFileSync3 } = await import("fs");
286886
+ const pkgJson = JSON.parse(readFileSync3(pkgJsonPath, "utf-8"));
286887
+ const deps = pkgJson.dependencies || {};
286888
+ for (const dep of Object.keys(deps)) {
286889
+ await addPackageWithDeps(archive, nodeModulesPath, dep, includedPackages, skipPackages, skippedPackages, warnings, workspacePackages);
286890
+ }
286891
+ } catch {
286892
+ }
286893
+ }
286894
+ }
286895
+ async function zipWithEsbuild(projectRoot, files, packageJson, _npmDeps) {
286896
+ const esbuild = await import("esbuild");
286897
+ const os2 = await import("os");
286898
+ const result = {
286899
+ buffer: Buffer.alloc(0),
286900
+ bundledFiles: 0,
286901
+ originalFiles: files.length,
286902
+ errors: [],
286903
+ warnings: []
286904
+ };
286905
+ const tmpDir = (0, import_path.join)(os2.tmpdir(), `browsermation-esbuild-${Date.now()}`);
286906
+ (0, import_fs.mkdirSync)(tmpDir, { recursive: true });
286907
+ try {
286908
+ const tsFiles = files.filter((f) => /\.tsx?$/.test(f) && !/\.d\.ts$/.test(f));
286909
+ const jsFiles = files.filter((f) => /\.(js|jsx|mjs)$/.test(f));
286910
+ const otherFiles = files.filter((f) => !/\.(ts|tsx|js|jsx|mjs)$/.test(f) || /\.d\.ts$/.test(f));
286911
+ const testFiles = tsFiles.filter(
286912
+ (f) => /\.(spec|test|e2e)\.(ts|tsx|js|jsx)$/.test(f)
286913
+ );
286914
+ const testEntryPoints = testFiles.map((f) => (0, import_path.join)(projectRoot, f));
286915
+ const allEntryPoints = tsFiles.map((f) => (0, import_path.join)(projectRoot, f));
286916
+ const preInstalled = ["@playwright/test", "playwright", "playwright-core"];
286917
+ console.log(`Analyzing dependencies from ${testFiles.length} test files...`);
286918
+ const actualDeps = /* @__PURE__ */ new Set();
286919
+ if (testEntryPoints.length > 0) {
286920
+ try {
286921
+ await esbuild.build({
286922
+ entryPoints: testEntryPoints,
286923
+ bundle: true,
286924
+ write: false,
286925
+ outdir: tmpDir,
286926
+ outbase: projectRoot,
286927
+ platform: "node",
286928
+ target: "node18",
286929
+ format: "cjs",
286930
+ metafile: true,
286931
+ logLevel: "silent",
286932
+ external: preInstalled,
286933
+ // Mark all npm packages as external and collect them
286934
+ plugins: [{
286935
+ name: "collect-deps",
286936
+ setup(build) {
286937
+ build.onResolve({ filter: /^[^./]/ }, (args) => {
286938
+ if (args.path.startsWith("node:")) return null;
286939
+ const parts = args.path.split("/");
286940
+ const pkgName = args.path.startsWith("@") ? `${parts[0]}/${parts[1]}` : parts[0];
286941
+ if (!preInstalled.includes(pkgName)) {
286942
+ actualDeps.add(pkgName);
286943
+ }
286944
+ return { path: args.path, external: true };
286945
+ });
286946
+ }
286947
+ }]
286948
+ });
286949
+ } catch (e) {
286950
+ if (e instanceof Error) {
286951
+ result.warnings.push(`esbuild analysis failed, using fallback: ${e.message.slice(0, 80)}`);
286952
+ }
286953
+ const { readFileSync: readFileSync3 } = require("fs");
286954
+ const importRegex = /(?:import|require)\s*\(?['"]([^'"./][^'"]*)['"]/g;
286955
+ for (const file of testEntryPoints) {
286956
+ try {
286957
+ const content = readFileSync3(file, "utf-8");
286958
+ for (const match of content.matchAll(importRegex)) {
286959
+ const parts = match[1].split("/");
286960
+ const pkgName = match[1].startsWith("@") ? `${parts[0]}/${parts[1]}` : parts[0];
286961
+ if (!preInstalled.includes(pkgName) && !pkgName.startsWith("node:")) {
286962
+ actualDeps.add(pkgName);
286963
+ }
286964
+ }
286965
+ } catch {
286966
+ }
286967
+ }
286968
+ }
286969
+ }
286970
+ console.log(`Found ${actualDeps.size} npm dependencies used by tests`);
286971
+ console.log(`Transpiling ${tsFiles.length} TypeScript files...`);
286972
+ if (allEntryPoints.length > 0) {
286973
+ const buildResult = await esbuild.build({
286974
+ entryPoints: allEntryPoints,
286975
+ bundle: false,
286976
+ // Just transpile
286977
+ platform: "node",
286978
+ target: "node18",
286979
+ format: "cjs",
286980
+ outdir: tmpDir,
286981
+ outbase: projectRoot,
286982
+ sourcemap: false,
286983
+ logLevel: "warning"
286984
+ });
286985
+ for (const warning2 of buildResult.warnings) {
286986
+ result.warnings.push(warning2.text);
286987
+ }
286988
+ for (const error2 of buildResult.errors) {
286989
+ result.errors.push(error2.text);
286990
+ }
286991
+ }
286992
+ result.bundledFiles = tsFiles.length;
286993
+ const passthrough = new import_stream.PassThrough();
286994
+ const archive = (0, import_archiver.default)("zip", { zlib: { level: 9 } });
286995
+ for (const file of tsFiles) {
286996
+ const jsFile = file.replace(/\.tsx?$/, ".js");
286997
+ const transpiledPath = (0, import_path.join)(tmpDir, jsFile);
286998
+ if ((0, import_fs.existsSync)(transpiledPath)) {
286999
+ archive.file(transpiledPath, { name: jsFile });
287000
+ }
287001
+ }
287002
+ for (const file of jsFiles) {
287003
+ const filePath = (0, import_path.join)(projectRoot, file);
287004
+ if ((0, import_fs.existsSync)(filePath)) {
287005
+ archive.file(filePath, { name: file });
287006
+ }
287007
+ }
287008
+ for (const file of otherFiles) {
287009
+ const filePath = (0, import_path.join)(projectRoot, file);
287010
+ if ((0, import_fs.existsSync)(filePath)) {
287011
+ archive.file(filePath, { name: file });
287012
+ }
287013
+ }
287014
+ const nodeModulesPath = (0, import_path.join)(projectRoot, "node_modules");
287015
+ const includedPackages = /* @__PURE__ */ new Set();
287016
+ const skippedWorkspacePackages = /* @__PURE__ */ new Set();
287017
+ const depsToInclude = [...actualDeps].filter((d) => !d.startsWith("@types/"));
287018
+ for (const dep of depsToInclude) {
287019
+ const packagePath = (0, import_path.join)(nodeModulesPath, dep);
287020
+ if (!(0, import_fs.existsSync)(packagePath)) continue;
287021
+ if (isWorkspacePackage(packagePath, dep)) {
287022
+ skippedWorkspacePackages.add(dep);
287023
+ } else {
287024
+ includedPackages.add(dep);
287025
+ await addPackageFiltered(archive, packagePath, dep);
287026
+ }
287027
+ }
287028
+ console.log(`Including ${includedPackages.size} npm packages (skipped ${skippedWorkspacePackages.size} workspace packages)`);
287029
+ if (skippedWorkspacePackages.size > 0) {
287030
+ console.log(` Workspace packages (resolved from source): ${[...skippedWorkspacePackages].join(", ")}`);
287031
+ }
287032
+ const minimalPackageJson = {
287033
+ name: packageJson.name,
287034
+ version: packageJson.version,
287035
+ dependencies: Object.fromEntries(
287036
+ [...includedPackages].filter((pkg) => packageJson.dependencies[pkg]).map((pkg) => [pkg, packageJson.dependencies[pkg]])
287037
+ )
287038
+ };
287039
+ archive.append(JSON.stringify(minimalPackageJson, null, 2), { name: "package.json" });
287040
+ archive.finalize();
287041
+ archive.pipe(passthrough);
287042
+ const chunks = [];
287043
+ for await (const chunk of passthrough) {
287044
+ chunks.push(chunk);
287045
+ }
287046
+ result.buffer = Buffer.concat(chunks);
287047
+ } catch (error2) {
287048
+ if (error2 instanceof Error) {
287049
+ result.errors.push(error2.message);
287050
+ } else {
287051
+ result.errors.push(String(error2));
287052
+ }
287053
+ } finally {
287054
+ try {
287055
+ (0, import_fs.rmSync)(tmpDir, { recursive: true, force: true });
287056
+ } catch {
287057
+ }
287058
+ }
287059
+ return result;
286470
287060
  }
286471
287061
 
286472
287062
  // node_modules/axios/lib/helpers/bind.js
@@ -290285,79 +290875,120 @@ async function upload(zipBuffer, shardNumber, totalShards, playwrightConfig, opt
290285
290875
 
290286
290876
  // src/tunnel.ts
290287
290877
  var import_node_child_process2 = require("node:child_process");
290288
- function startTunnel(tunnelProcess, options) {
290289
- const token = getToken();
290290
- if (!token) {
290291
- console.error(
290292
- source_default.red.bold(
290293
- "\u274C Error: Not authenticated. Run 'browsermation login' or set BM_API_TOKEN environment variable."
290294
- )
290295
- );
290296
- process.exit(1);
290297
- }
290298
- if (!options?.port) {
290299
- console.error(
290300
- source_default.red.bold(
290301
- "\u274C Error: Please provide a port to expose via the tunnel using the --port option."
290302
- )
290303
- );
290304
- process.exit(1);
290305
- }
290306
- const defaultDomain = "browsermationtunnel.com";
290307
- const args = [
290308
- "-o",
290309
- "StrictHostKeyChecking=no",
290310
- "-o",
290311
- "UserKnownHostsFile=/dev/null",
290312
- "-R",
290313
- `:80:localhost:${options.port}`,
290314
- `v0@${defaultDomain}`,
290315
- "-p",
290316
- "2200",
290317
- "http",
290318
- "--user",
290319
- token
290320
- ];
290321
- if (options.host) {
290322
- args.push("--host-header-rewrite", options.host);
290323
- }
290324
- tunnelProcess = (0, import_node_child_process2.spawn)("ssh", args);
290325
- process.on("SIGINT", function() {
290326
- console.log("\nGracefully shutting down from SIGINT (Ctrl+C)");
290327
- if (tunnelProcess) {
290328
- console.log("Killing proxy process...");
290329
- tunnelProcess.kill();
290330
- }
290331
- process.exit();
290332
- });
290333
- tunnelProcess?.stdout?.on("data", (data) => {
290334
- if (process.env.BM_DEBUG) {
290335
- console.log(`stdout: ${data}`);
290336
- }
290337
- if (data.toString().includes("RemoteAddress:")) {
290338
- const domain = data.toString().split("\n").map((line) => line.trim()).find((line) => line.startsWith("RemoteAddress:")).replace("RemoteAddress: ", "");
290339
- if (domain) {
290340
- console.log(
290341
- source_default.green.bold(
290342
- `\u2705 Tunnel is running! Access your application at: https://${domain}`
290343
- )
290344
- );
290878
+ function startTunnel(_tunnelProcess, options) {
290879
+ return new Promise((resolve3, reject) => {
290880
+ const token = getToken();
290881
+ if (!token) {
290882
+ const errorMsg = "Not authenticated. Run 'browsermation login' or set BM_API_TOKEN environment variable.";
290883
+ if (options.onError) {
290884
+ options.onError(errorMsg);
290885
+ }
290886
+ if (options.exitOnError !== false) {
290887
+ console.error(source_default.red.bold(`\u274C Error: ${errorMsg}`));
290888
+ process.exit(1);
290345
290889
  }
290890
+ reject(new Error(errorMsg));
290891
+ return;
290346
290892
  }
290347
- });
290348
- tunnelProcess?.stderr?.on("data", (data) => {
290349
- if (data.toString().includes("Permanently added")) {
290893
+ if (!options?.port) {
290894
+ const errorMsg = "Please provide a port to expose via the tunnel using the --port option.";
290895
+ if (options.onError) {
290896
+ options.onError(errorMsg);
290897
+ }
290898
+ if (options.exitOnError !== false) {
290899
+ console.error(source_default.red.bold(`\u274C Error: ${errorMsg}`));
290900
+ process.exit(1);
290901
+ }
290902
+ reject(new Error(errorMsg));
290350
290903
  return;
290351
290904
  }
290352
- if (data.toString().includes("closed by remote host.")) {
290353
- console.error(
290354
- source_default.red.bold(
290355
- "\u274C Error: Please check that your BM_API_TOKEN is correct and has permissions to create tunnels."
290356
- )
290357
- );
290358
- process.exit(1);
290905
+ const defaultDomain = "browsermationtunnel.com";
290906
+ const args = [
290907
+ "-o",
290908
+ "StrictHostKeyChecking=no",
290909
+ "-o",
290910
+ "UserKnownHostsFile=/dev/null",
290911
+ "-R",
290912
+ `:80:localhost:${options.port}`,
290913
+ `v0@${defaultDomain}`,
290914
+ "-p",
290915
+ "2200",
290916
+ "http",
290917
+ "--user",
290918
+ token
290919
+ ];
290920
+ if (options.host) {
290921
+ args.push("--host-header-rewrite", options.host);
290359
290922
  }
290360
- console.error(`stderr: ${data}`);
290923
+ const tunnelProcess = (0, import_node_child_process2.spawn)("ssh", args);
290924
+ process.on("SIGINT", function() {
290925
+ console.log("\nGracefully shutting down from SIGINT (Ctrl+C)");
290926
+ if (tunnelProcess) {
290927
+ console.log("Killing proxy process...");
290928
+ tunnelProcess.kill();
290929
+ }
290930
+ process.exit();
290931
+ });
290932
+ tunnelProcess?.stdout?.on("data", async (data) => {
290933
+ if (process.env.BM_DEBUG) {
290934
+ console.log(`stdout: ${data}`);
290935
+ }
290936
+ if (data.toString().includes("RemoteAddress:")) {
290937
+ const domain = data.toString().split("\n").map((line) => line.trim()).find((line) => line.startsWith("RemoteAddress:"))?.replace("RemoteAddress: ", "");
290938
+ if (domain) {
290939
+ const tunnelUrl = `https://${domain}`;
290940
+ if (options.onStart) {
290941
+ await options.onStart(tunnelUrl);
290942
+ }
290943
+ if (process.env.BM_DEBUG) {
290944
+ console.log(`Tunnel ready: ${tunnelUrl}`);
290945
+ }
290946
+ resolve3({
290947
+ tunnelProcess,
290948
+ tunnelUrl
290949
+ });
290950
+ }
290951
+ }
290952
+ });
290953
+ tunnelProcess?.stderr?.on("data", (data) => {
290954
+ const message = data.toString();
290955
+ if (message.includes("Permanently added")) {
290956
+ return;
290957
+ }
290958
+ if (message.includes("closed by remote host.")) {
290959
+ const errorMsg = "Please check that your BM_API_TOKEN is correct and has permissions to create tunnels.";
290960
+ if (options.onError) {
290961
+ options.onError(errorMsg);
290962
+ }
290963
+ if (options.exitOnError !== false) {
290964
+ console.error(source_default.red.bold(`\u274C Error: ${errorMsg}`));
290965
+ process.exit(1);
290966
+ }
290967
+ reject(new Error(errorMsg));
290968
+ return;
290969
+ }
290970
+ if (options.onError) {
290971
+ options.onError(message);
290972
+ } else {
290973
+ console.error(`stderr: ${data}`);
290974
+ }
290975
+ });
290976
+ tunnelProcess?.on("error", (error2) => {
290977
+ const errorMsg = `Failed to start tunnel: ${error2.message}`;
290978
+ if (options.onError) {
290979
+ options.onError(errorMsg);
290980
+ }
290981
+ reject(new Error(errorMsg));
290982
+ });
290983
+ tunnelProcess?.on("close", (code) => {
290984
+ if (code !== 0 && code !== null) {
290985
+ const errorMsg = `Tunnel process exited with code ${code}`;
290986
+ if (options.onError) {
290987
+ options.onError(errorMsg);
290988
+ }
290989
+ reject(new Error(errorMsg));
290990
+ }
290991
+ });
290361
290992
  });
290362
290993
  }
290363
290994
 
@@ -290375,8 +291006,12 @@ async function runTestCommand(options) {
290375
291006
  }
290376
291007
  let tunnelProcess = null;
290377
291008
  let tunnelUrl;
291009
+ const appUrl = process.env.APP_URL;
290378
291010
  try {
290379
- if (options.tunnel) {
291011
+ if (appUrl) {
291012
+ console.log(source_default.green(`Using APP_URL: ${appUrl}`));
291013
+ tunnelUrl = appUrl;
291014
+ } else if (options.tunnel) {
290380
291015
  const tunnelSpinner = ora("Starting tunnel...").start();
290381
291016
  try {
290382
291017
  const result2 = await startTunnel(null, {
@@ -290402,14 +291037,17 @@ async function runTestCommand(options) {
290402
291037
  console.log(source_default.dim("\u2500".repeat(50)));
290403
291038
  console.log(source_default.bold(`
290404
291039
  \u{1F4C1} Files to bundle (${result.files.length}):`));
291040
+ const isConfigFile = (f) => /^(playwright\.config|tsconfig|jest\.config|vitest\.config)\.(ts|js|mjs|cjs|json)$/.test(
291041
+ f.split("/").pop() || ""
291042
+ );
290405
291043
  const filesByType = {
290406
- config: result.files.filter((f) => f.includes("config")),
291044
+ config: result.files.filter(isConfigFile),
290407
291045
  tests: result.files.filter((f) => /\.(spec|test)\.(ts|js|tsx|jsx)$/.test(f)),
290408
291046
  source: result.files.filter(
290409
- (f) => !f.includes("config") && !/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(f) && /\.(ts|js|tsx|jsx)$/.test(f)
291047
+ (f) => !isConfigFile(f) && !/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(f) && /\.(ts|js|tsx|jsx)$/.test(f)
290410
291048
  ),
290411
291049
  other: result.files.filter(
290412
- (f) => !f.includes("config") && !/\.(spec|test|ts|js|tsx|jsx)/.test(f)
291050
+ (f) => !isConfigFile(f) && !/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(f) && !/\.(ts|js|tsx|jsx)$/.test(f)
290413
291051
  )
290414
291052
  };
290415
291053
  if (filesByType.config.length > 0) {
@@ -290455,16 +291093,80 @@ async function runTestCommand(options) {
290455
291093
  return;
290456
291094
  }
290457
291095
  const zipSpinner = ora("Creating test bundle...").start();
290458
- const zipBuffer = await zipFilesToBuffer(
290459
- projectRoot,
290460
- result.files,
290461
- result.packageJson
290462
- );
290463
- zipSpinner.succeed(
290464
- source_default.green(
290465
- `Bundle created (${(zipBuffer.length / 1024 / 1024).toFixed(2)} MB)`
290466
- )
290467
- );
291096
+ let zipBuffer;
291097
+ let bundleInfo = {};
291098
+ if (options.esbuild) {
291099
+ zipSpinner.text = "Transpiling with esbuild...";
291100
+ const esbuildResult = await zipWithEsbuild(
291101
+ projectRoot,
291102
+ result.files,
291103
+ result.packageJson,
291104
+ result.graph.nodeModuleDeps
291105
+ );
291106
+ zipBuffer = esbuildResult.buffer;
291107
+ bundleInfo.warnings = esbuildResult.warnings;
291108
+ bundleInfo.errors = esbuildResult.errors;
291109
+ if (esbuildResult.errors.length > 0) {
291110
+ zipSpinner.warn(source_default.yellow(`Bundle created with ${esbuildResult.errors.length} errors`));
291111
+ console.log(source_default.red("\nEsbuild errors:"));
291112
+ esbuildResult.errors.forEach((err) => console.log(source_default.red(` ${err}`)));
291113
+ }
291114
+ } else {
291115
+ const zipResult = await zipFilesToBuffer(
291116
+ projectRoot,
291117
+ result.files,
291118
+ result.packageJson,
291119
+ result.graph.nodeModuleDeps
291120
+ );
291121
+ zipBuffer = zipResult.buffer;
291122
+ bundleInfo.workspacePackages = zipResult.workspacePackages;
291123
+ bundleInfo.includedPackages = zipResult.includedPackages;
291124
+ bundleInfo.warnings = zipResult.warnings;
291125
+ }
291126
+ if (process.env.KEEP_ZIP === "true") {
291127
+ const zipPath = path6.join(projectRoot, "browsermation-bundle.zip");
291128
+ (0, import_node_fs5.writeFileSync)(zipPath, zipBuffer);
291129
+ zipSpinner.succeed(
291130
+ source_default.green(
291131
+ `Bundle created (${(zipBuffer.length / 1024 / 1024).toFixed(2)} MB) \u2192 ${zipPath}`
291132
+ )
291133
+ );
291134
+ } else {
291135
+ zipSpinner.succeed(
291136
+ source_default.green(
291137
+ `Bundle created (${(zipBuffer.length / 1024 / 1024).toFixed(2)} MB)${options.esbuild ? " [esbuild]" : ""}`
291138
+ )
291139
+ );
291140
+ }
291141
+ if (!options.esbuild && bundleInfo.includedPackages) {
291142
+ const regularPackages = new Set(
291143
+ [...bundleInfo.includedPackages].filter((p) => !bundleInfo.workspacePackages?.has(p))
291144
+ );
291145
+ if (bundleInfo.workspacePackages && bundleInfo.workspacePackages.size > 0) {
291146
+ console.log(source_default.bold(`
291147
+ \u{1F517} Workspace packages (${bundleInfo.workspacePackages.size}) - bundled without transitive deps:`));
291148
+ const sortedWorkspace = Array.from(bundleInfo.workspacePackages).sort();
291149
+ sortedWorkspace.forEach((pkg) => console.log(source_default.yellow(` ${pkg}`)));
291150
+ }
291151
+ if (regularPackages.size > 0) {
291152
+ console.log(source_default.bold(`
291153
+ \u{1F4E6} Bundled node_modules (${regularPackages.size}):`));
291154
+ const sortedPackages = Array.from(regularPackages).sort();
291155
+ sortedPackages.forEach((pkg) => console.log(source_default.dim(` ${pkg}`)));
291156
+ }
291157
+ }
291158
+ if (bundleInfo.warnings && bundleInfo.warnings.length > 0) {
291159
+ console.log(source_default.bold.yellow(`
291160
+ \u26A0\uFE0F Warnings (${bundleInfo.warnings.length}):`));
291161
+ bundleInfo.warnings.forEach((warning2) => {
291162
+ console.log(source_default.yellow(` ${warning2}`));
291163
+ });
291164
+ if (!options.esbuild) {
291165
+ console.log(source_default.yellow("\n Tests may fail due to unbuilt packages. Consider:"));
291166
+ console.log(source_default.yellow(" 1. Building the packages first (e.g., npm run build in the package directory)"));
291167
+ console.log(source_default.yellow(" 2. Installing published versions from npm instead of monorepo symlinks"));
291168
+ }
291169
+ }
290468
291170
  const uploadSpinner = ora("Uploading to BrowserMation...").start();
290469
291171
  const eventStream = new (await import("stream")).Readable({
290470
291172
  read() {
@@ -290513,8 +291215,18 @@ Error: ${error2.message}`));
290513
291215
  // src/commands/tunnel.ts
290514
291216
  async function runTunnelCommand(options) {
290515
291217
  console.log(source_default.blue(`Starting BrowserMation Tunnel...`));
290516
- let tunnelProcess = null;
290517
- await startTunnel(tunnelProcess, options);
291218
+ const tunnelOptions = {
291219
+ port: options.port || "",
291220
+ host: options.host,
291221
+ onStart: (tunnelUrl) => {
291222
+ console.log(
291223
+ source_default.green.bold(
291224
+ `\u2705 Tunnel is running! Access your application at: ${tunnelUrl}`
291225
+ )
291226
+ );
291227
+ }
291228
+ };
291229
+ await startTunnel(null, tunnelOptions);
290518
291230
  }
290519
291231
 
290520
291232
  // src/commands/runner.ts
@@ -290621,7 +291333,7 @@ program2.command("tunnel").description("Start a BrowserMation tunnel").option("-
290621
291333
  program2.command("runner").description("Start the browser engine server").option("-p, --port <port>").option("-t, --tunnel").action(async (options) => {
290622
291334
  await runRunnerCommand(options);
290623
291335
  });
290624
- program2.command("test").description("Analyze and run Playwright tests on BrowserMation").option("--project <project>", "Run a specific Playwright project").option("-t, --tunnel <port>", "Expose local port via tunnel").option("-p, --proxy", "Use proxy for test execution").option("--dry-run", "Analyze dependencies without uploading").action(async (options) => {
291336
+ program2.command("test").description("Analyze and run Playwright tests on BrowserMation").option("--project <project>", "Run a specific Playwright project").option("-t, --tunnel <port>", "Expose local port via tunnel").option("-p, --proxy", "Use proxy for test execution").option("--dry-run", "Analyze dependencies without uploading").option("--esbuild", "Use esbuild tree-shaking for minimal bundle size (experimental)").action(async (options) => {
290625
291337
  console.log(source_default.blue("Starting BrowserMation Test Runner..."));
290626
291338
  await runTestCommand(options);
290627
291339
  });