@nathapp/nax 0.62.0-canary.7 → 0.62.0
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/nax.js +384 -126
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -4976,21 +4976,54 @@ Rules:`, STEP3_SHARED_RULES = `- **One test per AC**, named exactly "AC-N: <desc
|
|
|
4976
4976
|
var init_acceptance_builder = () => {};
|
|
4977
4977
|
|
|
4978
4978
|
// src/test-runners/conventions.ts
|
|
4979
|
-
function
|
|
4980
|
-
|
|
4981
|
-
if (lastStar === -1)
|
|
4979
|
+
function globToRegex(pattern) {
|
|
4980
|
+
if (pattern.length === 0)
|
|
4982
4981
|
return null;
|
|
4983
|
-
|
|
4984
|
-
if (suffix.length === 0)
|
|
4982
|
+
if (/^\*+\/?$/.test(pattern))
|
|
4985
4983
|
return null;
|
|
4986
|
-
|
|
4987
|
-
|
|
4984
|
+
let regex = "";
|
|
4985
|
+
let i = 0;
|
|
4986
|
+
while (i < pattern.length) {
|
|
4987
|
+
const c = pattern[i];
|
|
4988
|
+
if (c === "*") {
|
|
4989
|
+
if (pattern[i + 1] === "*") {
|
|
4990
|
+
const beforeSlash = i > 0 && pattern[i - 1] === "/";
|
|
4991
|
+
const afterSlash = pattern[i + 2] === "/";
|
|
4992
|
+
if (beforeSlash && afterSlash) {
|
|
4993
|
+
regex = `${regex.slice(0, -1)}(?:.*\\/)?`;
|
|
4994
|
+
i += 3;
|
|
4995
|
+
} else if (afterSlash) {
|
|
4996
|
+
regex += "(?:.*\\/)?";
|
|
4997
|
+
i += 3;
|
|
4998
|
+
} else {
|
|
4999
|
+
regex += ".*";
|
|
5000
|
+
i += 2;
|
|
5001
|
+
}
|
|
5002
|
+
continue;
|
|
5003
|
+
}
|
|
5004
|
+
regex += "[^/]*";
|
|
5005
|
+
i++;
|
|
5006
|
+
continue;
|
|
5007
|
+
}
|
|
5008
|
+
if (c === "?") {
|
|
5009
|
+
regex += "[^/]";
|
|
5010
|
+
i++;
|
|
5011
|
+
continue;
|
|
5012
|
+
}
|
|
5013
|
+
if (".+^${}()|[]\\".includes(c)) {
|
|
5014
|
+
regex += `\\${c}`;
|
|
5015
|
+
} else {
|
|
5016
|
+
regex += c;
|
|
5017
|
+
}
|
|
5018
|
+
i++;
|
|
5019
|
+
}
|
|
5020
|
+
return new RegExp(`(?:^|/)${regex}$`);
|
|
4988
5021
|
}
|
|
4989
5022
|
function globsToTestRegex(patterns) {
|
|
4990
5023
|
const regexes = [];
|
|
4991
5024
|
const seen = new Set;
|
|
4992
5025
|
for (const pattern of patterns) {
|
|
4993
|
-
const re =
|
|
5026
|
+
const re = globToRegex(pattern);
|
|
4994
5027
|
if (re && !seen.has(re.source)) {
|
|
4995
5028
|
regexes.push(re);
|
|
4996
5029
|
seen.add(re.source);
|
|
@@ -5294,68 +5327,101 @@ var init_file_scan = __esm(() => {
|
|
|
5294
5327
|
};
|
|
5295
5328
|
});
|
|
5296
5329
|
|
|
5297
|
-
// src/test-runners/detect/
|
|
5298
|
-
function
|
|
5299
|
-
return
|
|
5300
|
-
}
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5330
|
+
// src/test-runners/detect/extglob.ts
|
|
5331
|
+
function hasCharRange(pattern) {
|
|
5332
|
+
return /\[[^\]]*-[^\]]*\]/.test(pattern);
|
|
5333
|
+
}
|
|
5334
|
+
function hasUnsupported(pattern) {
|
|
5335
|
+
return pattern.includes("!(") || hasCharRange(pattern);
|
|
5336
|
+
}
|
|
5337
|
+
function expandLeftmost(pattern) {
|
|
5338
|
+
let earliest = null;
|
|
5339
|
+
for (const spec of CONSTRUCTS) {
|
|
5340
|
+
const m = pattern.match(spec.re);
|
|
5341
|
+
if (m?.index !== undefined) {
|
|
5342
|
+
if (earliest === null || m.index < (earliest.match.index ?? Number.POSITIVE_INFINITY)) {
|
|
5343
|
+
earliest = { match: m, spec };
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
if (!earliest)
|
|
5348
|
+
return [pattern];
|
|
5349
|
+
const full = earliest.match[0];
|
|
5350
|
+
const body = earliest.match[1];
|
|
5351
|
+
const start = earliest.match.index;
|
|
5352
|
+
const before = pattern.slice(0, start);
|
|
5353
|
+
const after = pattern.slice(start + full.length);
|
|
5354
|
+
let alternatives = earliest.spec.charClass ? [...body] : body.split(earliest.spec.separator);
|
|
5355
|
+
if (earliest.spec.withEmpty)
|
|
5356
|
+
alternatives = ["", ...alternatives];
|
|
5357
|
+
return alternatives.map((alt) => `${before}${alt}${after}`);
|
|
5358
|
+
}
|
|
5359
|
+
function expandExtglob(pattern) {
|
|
5360
|
+
if (hasUnsupported(pattern))
|
|
5361
|
+
return [pattern];
|
|
5362
|
+
let variants = [pattern];
|
|
5363
|
+
for (let pass = 0;pass < MAX_PASSES; pass++) {
|
|
5364
|
+
const next = [];
|
|
5365
|
+
let changed = false;
|
|
5366
|
+
for (const v of variants) {
|
|
5367
|
+
const expanded = expandLeftmost(v);
|
|
5368
|
+
if (expanded.length > 1 || expanded[0] !== v)
|
|
5369
|
+
changed = true;
|
|
5370
|
+
next.push(...expanded);
|
|
5371
|
+
if (next.length > MAX_VARIANTS)
|
|
5372
|
+
break;
|
|
5314
5373
|
}
|
|
5315
|
-
|
|
5374
|
+
variants = [...new Set(next)];
|
|
5375
|
+
if (!changed)
|
|
5376
|
+
break;
|
|
5377
|
+
if (variants.length > MAX_VARIANTS)
|
|
5378
|
+
break;
|
|
5316
5379
|
}
|
|
5317
|
-
return
|
|
5380
|
+
return variants;
|
|
5318
5381
|
}
|
|
5319
|
-
|
|
5320
|
-
const
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
const
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
return { type: "framework-config", path, patterns: filterExcluded(patterns) };
|
|
5328
|
-
}
|
|
5329
|
-
const pkgPath = `${workdir}/package.json`;
|
|
5330
|
-
const pkgText = await _frameworkConfigDeps.readText(pkgPath);
|
|
5331
|
-
if (pkgText) {
|
|
5332
|
-
try {
|
|
5333
|
-
const pkg = JSON.parse(pkgText);
|
|
5334
|
-
const jestConfig = pkg.jest;
|
|
5335
|
-
if (jestConfig) {
|
|
5336
|
-
const patterns = extractJestPatternsFromObject(jestConfig);
|
|
5337
|
-
if (patterns.length > 0) {
|
|
5338
|
-
return { type: "framework-config", path: `${pkgPath}#jest`, patterns: filterExcluded(patterns) };
|
|
5339
|
-
}
|
|
5382
|
+
function expandExtglobAll(patterns) {
|
|
5383
|
+
const seen = new Set;
|
|
5384
|
+
const result = [];
|
|
5385
|
+
for (const p of patterns) {
|
|
5386
|
+
for (const expanded of expandExtglob(p)) {
|
|
5387
|
+
if (!seen.has(expanded)) {
|
|
5388
|
+
seen.add(expanded);
|
|
5389
|
+
result.push(expanded);
|
|
5340
5390
|
}
|
|
5341
|
-
}
|
|
5342
|
-
}
|
|
5343
|
-
return null;
|
|
5344
|
-
}
|
|
5345
|
-
function extractJestPatterns(text) {
|
|
5346
|
-
const matchMatch = text.match(/testMatch\s*:\s*\[([^\]]+)\]/s);
|
|
5347
|
-
if (matchMatch) {
|
|
5348
|
-
const patterns = extractStringLiterals(matchMatch[1]);
|
|
5349
|
-
if (patterns.length > 0)
|
|
5350
|
-
return patterns;
|
|
5391
|
+
}
|
|
5351
5392
|
}
|
|
5352
|
-
return
|
|
5393
|
+
return result;
|
|
5353
5394
|
}
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5395
|
+
var MAX_VARIANTS = 64, MAX_PASSES = 10, CONSTRUCTS;
|
|
5396
|
+
var init_extglob = __esm(() => {
|
|
5397
|
+
CONSTRUCTS = [
|
|
5398
|
+
{ re: /\{([^{}]+)\}/, withEmpty: false, charClass: false, separator: "," },
|
|
5399
|
+
{ re: /\?\(([^()]*)\)/, withEmpty: true, charClass: false, separator: "|" },
|
|
5400
|
+
{ re: /\*\(([^()]*)\)/, withEmpty: true, charClass: false, separator: "|" },
|
|
5401
|
+
{ re: /\+\(([^()]*)\)/, withEmpty: false, charClass: false, separator: "|" },
|
|
5402
|
+
{ re: /@\(([^()]*)\)/, withEmpty: false, charClass: false, separator: "|" },
|
|
5403
|
+
{ re: /\[([^\]]+)\]/, withEmpty: false, charClass: true, separator: "" }
|
|
5404
|
+
];
|
|
5405
|
+
});
|
|
5406
|
+
|
|
5407
|
+
// src/test-runners/detect/framework-configs-deps.ts
|
|
5408
|
+
var _frameworkConfigDeps;
|
|
5409
|
+
var init_framework_configs_deps = __esm(() => {
|
|
5410
|
+
_frameworkConfigDeps = {
|
|
5411
|
+
readText: async (path) => {
|
|
5412
|
+
const f = Bun.file(path);
|
|
5413
|
+
if (!await f.exists())
|
|
5414
|
+
return null;
|
|
5415
|
+
return f.text();
|
|
5416
|
+
},
|
|
5417
|
+
parseToml: (text) => Bun.TOML.parse(text),
|
|
5418
|
+
parseYaml: (text) => Bun.YAML.parse(text)
|
|
5419
|
+
};
|
|
5420
|
+
});
|
|
5421
|
+
|
|
5422
|
+
// src/test-runners/detect/framework-configs-python.ts
|
|
5423
|
+
function filterExcluded(patterns) {
|
|
5424
|
+
return patterns.filter((p) => !EXCLUDE_DIRS.some((d) => p.includes(`/${d}/`) || p.startsWith(`${d}/`)));
|
|
5359
5425
|
}
|
|
5360
5426
|
async function parsePyprojectToml(workdir) {
|
|
5361
5427
|
const path = `${workdir}/pyproject.toml`;
|
|
@@ -5390,7 +5456,7 @@ async function parsePyprojectToml(workdir) {
|
|
|
5390
5456
|
if (patterns.length === 0) {
|
|
5391
5457
|
patterns.push("test_*.py", "*_test.py");
|
|
5392
5458
|
}
|
|
5393
|
-
return { type: "framework-config", path, patterns: filterExcluded(patterns) };
|
|
5459
|
+
return { type: "framework-config", framework: "pytest", path, patterns: filterExcluded(patterns) };
|
|
5394
5460
|
} catch {
|
|
5395
5461
|
return null;
|
|
5396
5462
|
}
|
|
@@ -5421,10 +5487,94 @@ async function parsePytestIni(workdir) {
|
|
|
5421
5487
|
}
|
|
5422
5488
|
if (patterns.length === 0)
|
|
5423
5489
|
patterns.push("test_*.py", "*_test.py");
|
|
5424
|
-
return { type: "framework-config", path, patterns: filterExcluded(patterns) };
|
|
5490
|
+
return { type: "framework-config", framework: "pytest", path, patterns: filterExcluded(patterns) };
|
|
5425
5491
|
}
|
|
5426
5492
|
return null;
|
|
5427
5493
|
}
|
|
5494
|
+
var EXCLUDE_DIRS;
|
|
5495
|
+
var init_framework_configs_python = __esm(() => {
|
|
5496
|
+
init_framework_configs_deps();
|
|
5497
|
+
EXCLUDE_DIRS = ["node_modules", "dist", "build", ".nax", "coverage", ".git"];
|
|
5498
|
+
});
|
|
5499
|
+
|
|
5500
|
+
// src/test-runners/detect/framework-configs.ts
|
|
5501
|
+
function filterExcluded2(patterns) {
|
|
5502
|
+
return patterns.filter((p) => !EXCLUDE_DIRS2.some((d) => p.includes(`/${d}/`) || p.startsWith(`${d}/`)));
|
|
5503
|
+
}
|
|
5504
|
+
function normalize(patterns) {
|
|
5505
|
+
return filterExcluded2(expandExtglobAll(patterns));
|
|
5506
|
+
}
|
|
5507
|
+
async function parseVitestConfig(workdir) {
|
|
5508
|
+
const candidates = ["vitest.config.ts", "vitest.config.mts", "vitest.config.js", "vitest.config.mjs"];
|
|
5509
|
+
for (const name of candidates) {
|
|
5510
|
+
const path = `${workdir}/${name}`;
|
|
5511
|
+
const text = await _frameworkConfigDeps.readText(path);
|
|
5512
|
+
if (!text)
|
|
5513
|
+
continue;
|
|
5514
|
+
const includeMatch = text.match(/include\s*:\s*\[([^\]]+)\]/s);
|
|
5515
|
+
if (includeMatch) {
|
|
5516
|
+
const patterns = extractStringLiterals(includeMatch[1]);
|
|
5517
|
+
if (patterns.length > 0) {
|
|
5518
|
+
return { type: "framework-config", framework: "vitest", path, patterns: normalize(patterns) };
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
return { type: "framework-config", framework: "vitest", path, patterns: [] };
|
|
5522
|
+
}
|
|
5523
|
+
return null;
|
|
5524
|
+
}
|
|
5525
|
+
async function parseJestConfig(workdir) {
|
|
5526
|
+
const candidates = ["jest.config.ts", "jest.config.js", "jest.config.cjs", "jest.config.mjs", "jest.config.json"];
|
|
5527
|
+
for (const name of candidates) {
|
|
5528
|
+
const path = `${workdir}/${name}`;
|
|
5529
|
+
const text = await _frameworkConfigDeps.readText(path);
|
|
5530
|
+
if (!text)
|
|
5531
|
+
continue;
|
|
5532
|
+
if (name.endsWith(".json")) {
|
|
5533
|
+
try {
|
|
5534
|
+
const config = JSON.parse(text);
|
|
5535
|
+
const patterns2 = extractJestPatternsFromObject(config);
|
|
5536
|
+
return { type: "framework-config", framework: "jest", path, patterns: normalize(patterns2) };
|
|
5537
|
+
} catch {}
|
|
5538
|
+
}
|
|
5539
|
+
const patterns = extractJestPatterns(text);
|
|
5540
|
+
return { type: "framework-config", framework: "jest", path, patterns: normalize(patterns) };
|
|
5541
|
+
}
|
|
5542
|
+
const pkgPath = `${workdir}/package.json`;
|
|
5543
|
+
const pkgText = await _frameworkConfigDeps.readText(pkgPath);
|
|
5544
|
+
if (pkgText) {
|
|
5545
|
+
try {
|
|
5546
|
+
const pkg = JSON.parse(pkgText);
|
|
5547
|
+
const jestConfig = pkg.jest;
|
|
5548
|
+
if (jestConfig) {
|
|
5549
|
+
const patterns = extractJestPatternsFromObject(jestConfig);
|
|
5550
|
+
if (patterns.length > 0) {
|
|
5551
|
+
return {
|
|
5552
|
+
type: "framework-config",
|
|
5553
|
+
framework: "jest",
|
|
5554
|
+
path: `${pkgPath}#jest`,
|
|
5555
|
+
patterns: normalize(patterns)
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
} catch {}
|
|
5560
|
+
}
|
|
5561
|
+
return null;
|
|
5562
|
+
}
|
|
5563
|
+
function extractJestPatterns(text) {
|
|
5564
|
+
const matchMatch = text.match(/testMatch\s*:\s*\[([^\]]+)\]/s);
|
|
5565
|
+
if (matchMatch) {
|
|
5566
|
+
const patterns = extractStringLiterals(matchMatch[1]);
|
|
5567
|
+
if (patterns.length > 0)
|
|
5568
|
+
return patterns;
|
|
5569
|
+
}
|
|
5570
|
+
return [];
|
|
5571
|
+
}
|
|
5572
|
+
function extractJestPatternsFromObject(config) {
|
|
5573
|
+
const testMatch = config.testMatch;
|
|
5574
|
+
if (Array.isArray(testMatch))
|
|
5575
|
+
return testMatch.filter((p) => typeof p === "string");
|
|
5576
|
+
return [];
|
|
5577
|
+
}
|
|
5428
5578
|
async function parseMochaConfig(workdir) {
|
|
5429
5579
|
const candidates = [".mocharc.js", ".mocharc.cjs", ".mocharc.yaml", ".mocharc.yml", ".mocharc.json"];
|
|
5430
5580
|
for (const name of candidates) {
|
|
@@ -5441,15 +5591,16 @@ async function parseMochaConfig(workdir) {
|
|
|
5441
5591
|
} else {
|
|
5442
5592
|
const specMatch = text.match(/spec\s*:\s*['"]([^'"]+)['"]/);
|
|
5443
5593
|
if (specMatch) {
|
|
5444
|
-
return { type: "framework-config", path, patterns: [specMatch[1]] };
|
|
5594
|
+
return { type: "framework-config", framework: "mocha", path, patterns: normalize([specMatch[1]]) };
|
|
5445
5595
|
}
|
|
5446
|
-
|
|
5596
|
+
return { type: "framework-config", framework: "mocha", path, patterns: [] };
|
|
5447
5597
|
}
|
|
5448
5598
|
const spec = config.spec;
|
|
5449
5599
|
const patterns = Array.isArray(spec) ? spec.filter((p) => typeof p === "string") : typeof spec === "string" ? [spec] : [];
|
|
5450
5600
|
if (patterns.length > 0) {
|
|
5451
|
-
return { type: "framework-config", path, patterns:
|
|
5601
|
+
return { type: "framework-config", framework: "mocha", path, patterns: normalize(patterns) };
|
|
5452
5602
|
}
|
|
5603
|
+
return { type: "framework-config", framework: "mocha", path, patterns: [] };
|
|
5453
5604
|
} catch {}
|
|
5454
5605
|
}
|
|
5455
5606
|
return null;
|
|
@@ -5471,9 +5622,9 @@ async function parsePlaywrightConfig(workdir) {
|
|
|
5471
5622
|
patterns.push(...extracted);
|
|
5472
5623
|
}
|
|
5473
5624
|
if (patterns.length > 0) {
|
|
5474
|
-
return { type: "framework-config", path, patterns:
|
|
5625
|
+
return { type: "framework-config", framework: "playwright", path, patterns: normalize(patterns) };
|
|
5475
5626
|
}
|
|
5476
|
-
return { type: "framework-config", path, patterns: [
|
|
5627
|
+
return { type: "framework-config", framework: "playwright", path, patterns: [] };
|
|
5477
5628
|
}
|
|
5478
5629
|
return null;
|
|
5479
5630
|
}
|
|
@@ -5486,9 +5637,14 @@ async function parseCypressConfig(workdir) {
|
|
|
5486
5637
|
continue;
|
|
5487
5638
|
const specMatch = text.match(/specPattern\s*:\s*['"]([^'"]+)['"]/);
|
|
5488
5639
|
if (specMatch) {
|
|
5489
|
-
return { type: "framework-config", path, patterns: [specMatch[1]] };
|
|
5640
|
+
return { type: "framework-config", framework: "cypress", path, patterns: normalize([specMatch[1]]) };
|
|
5490
5641
|
}
|
|
5491
|
-
return {
|
|
5642
|
+
return {
|
|
5643
|
+
type: "framework-config",
|
|
5644
|
+
framework: "cypress",
|
|
5645
|
+
path,
|
|
5646
|
+
patterns: normalize(["cypress/e2e/**/*.cy.{js,ts}"])
|
|
5647
|
+
};
|
|
5492
5648
|
}
|
|
5493
5649
|
return null;
|
|
5494
5650
|
}
|
|
@@ -5503,10 +5659,79 @@ function extractStringLiterals(body) {
|
|
|
5503
5659
|
}
|
|
5504
5660
|
return patterns;
|
|
5505
5661
|
}
|
|
5662
|
+
async function parseViteConfig(workdir) {
|
|
5663
|
+
const candidates = ["vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"];
|
|
5664
|
+
for (const name of candidates) {
|
|
5665
|
+
const path = `${workdir}/${name}`;
|
|
5666
|
+
const text = await _frameworkConfigDeps.readText(path);
|
|
5667
|
+
if (!text)
|
|
5668
|
+
continue;
|
|
5669
|
+
if (!/\btest\s*:\s*\{/.test(text))
|
|
5670
|
+
continue;
|
|
5671
|
+
const testBlock = extractBalancedBlock(text, /\btest\s*:\s*\{/);
|
|
5672
|
+
if (testBlock) {
|
|
5673
|
+
const includeMatch = testBlock.match(/include\s*:\s*\[([^\]]+)\]/s);
|
|
5674
|
+
if (includeMatch) {
|
|
5675
|
+
const patterns = extractStringLiterals(includeMatch[1]);
|
|
5676
|
+
if (patterns.length > 0) {
|
|
5677
|
+
return { type: "framework-config", framework: "vitest", path, patterns: normalize(patterns) };
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
return { type: "framework-config", framework: "vitest", path, patterns: [] };
|
|
5682
|
+
}
|
|
5683
|
+
return null;
|
|
5684
|
+
}
|
|
5685
|
+
async function parseBunfig(workdir) {
|
|
5686
|
+
const path = `${workdir}/bunfig.toml`;
|
|
5687
|
+
const text = await _frameworkConfigDeps.readText(path);
|
|
5688
|
+
if (!text)
|
|
5689
|
+
return null;
|
|
5690
|
+
try {
|
|
5691
|
+
const parsed = _frameworkConfigDeps.parseToml(text);
|
|
5692
|
+
if (!parsed?.test || typeof parsed.test !== "object")
|
|
5693
|
+
return null;
|
|
5694
|
+
} catch {
|
|
5695
|
+
return null;
|
|
5696
|
+
}
|
|
5697
|
+
return {
|
|
5698
|
+
type: "framework-config",
|
|
5699
|
+
framework: "bun",
|
|
5700
|
+
path,
|
|
5701
|
+
patterns: normalize([
|
|
5702
|
+
"**/*.test.{ts,tsx,js,jsx,mjs,cjs}",
|
|
5703
|
+
"**/*_test.{ts,tsx,js,jsx,mjs,cjs}",
|
|
5704
|
+
"**/*.spec.{ts,tsx,js,jsx,mjs,cjs}",
|
|
5705
|
+
"**/*_spec.{ts,tsx,js,jsx,mjs,cjs}"
|
|
5706
|
+
])
|
|
5707
|
+
};
|
|
5708
|
+
}
|
|
5709
|
+
function extractBalancedBlock(text, anchor) {
|
|
5710
|
+
const m = text.match(anchor);
|
|
5711
|
+
if (!m || m.index === undefined)
|
|
5712
|
+
return null;
|
|
5713
|
+
const openIdx = text.indexOf("{", m.index);
|
|
5714
|
+
if (openIdx === -1)
|
|
5715
|
+
return null;
|
|
5716
|
+
let depth = 0;
|
|
5717
|
+
for (let i = openIdx;i < text.length; i++) {
|
|
5718
|
+
const c = text[i];
|
|
5719
|
+
if (c === "{")
|
|
5720
|
+
depth++;
|
|
5721
|
+
else if (c === "}") {
|
|
5722
|
+
depth--;
|
|
5723
|
+
if (depth === 0)
|
|
5724
|
+
return text.slice(openIdx + 1, i);
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
return null;
|
|
5728
|
+
}
|
|
5506
5729
|
async function detectFromFrameworkConfigs(workdir) {
|
|
5507
5730
|
const results = await Promise.all([
|
|
5508
5731
|
parseVitestConfig(workdir),
|
|
5732
|
+
parseViteConfig(workdir),
|
|
5509
5733
|
parseJestConfig(workdir),
|
|
5734
|
+
parseBunfig(workdir),
|
|
5510
5735
|
parsePyprojectToml(workdir),
|
|
5511
5736
|
parsePytestIni(workdir),
|
|
5512
5737
|
parseMochaConfig(workdir),
|
|
@@ -5515,19 +5740,13 @@ async function detectFromFrameworkConfigs(workdir) {
|
|
|
5515
5740
|
]);
|
|
5516
5741
|
return results.filter((r) => r !== null);
|
|
5517
5742
|
}
|
|
5518
|
-
var
|
|
5743
|
+
var EXCLUDE_DIRS2;
|
|
5519
5744
|
var init_framework_configs = __esm(() => {
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
return null;
|
|
5526
|
-
return f.text();
|
|
5527
|
-
},
|
|
5528
|
-
parseToml: (text) => Bun.TOML.parse(text),
|
|
5529
|
-
parseYaml: (text) => Bun.YAML.parse(text)
|
|
5530
|
-
};
|
|
5745
|
+
init_extglob();
|
|
5746
|
+
init_framework_configs_deps();
|
|
5747
|
+
init_framework_configs_python();
|
|
5748
|
+
init_framework_configs_deps();
|
|
5749
|
+
EXCLUDE_DIRS2 = ["node_modules", "dist", "build", ".nax", "coverage", ".git"];
|
|
5531
5750
|
});
|
|
5532
5751
|
|
|
5533
5752
|
// src/test-runners/detect/framework-defaults.ts
|
|
@@ -5535,61 +5754,75 @@ async function detectFromPackageJson(workdir) {
|
|
|
5535
5754
|
const path = `${workdir}/package.json`;
|
|
5536
5755
|
const text = await _frameworkDefaultsDeps.readText(path);
|
|
5537
5756
|
if (!text)
|
|
5538
|
-
return
|
|
5757
|
+
return [];
|
|
5539
5758
|
let pkg;
|
|
5540
5759
|
try {
|
|
5541
5760
|
pkg = JSON.parse(text);
|
|
5542
5761
|
} catch {
|
|
5543
|
-
return
|
|
5762
|
+
return [];
|
|
5544
5763
|
}
|
|
5545
5764
|
const devDeps = pkg.devDependencies ?? {};
|
|
5546
5765
|
const deps = pkg.dependencies ?? {};
|
|
5547
5766
|
const allDeps = { ...deps, ...devDeps };
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5767
|
+
const results = [];
|
|
5768
|
+
for (const { depKey, framework, patterns } of JS_FRAMEWORK_DEFAULTS) {
|
|
5769
|
+
if (depKey in allDeps) {
|
|
5770
|
+
results.push({ type: "manifest", framework, path, patterns: expandExtglobAll(patterns) });
|
|
5551
5771
|
}
|
|
5552
5772
|
}
|
|
5773
|
+
if (results.length > 0)
|
|
5774
|
+
return results;
|
|
5553
5775
|
const scripts = pkg.scripts;
|
|
5554
5776
|
const testScript = typeof scripts?.test === "string" ? scripts.test : "";
|
|
5555
5777
|
if (testScript.includes("bun test")) {
|
|
5556
|
-
return { type: "manifest", path, patterns: BUN_TEST_DEFAULTS };
|
|
5778
|
+
return [{ type: "manifest", framework: "bun", path, patterns: expandExtglobAll(BUN_TEST_DEFAULTS) }];
|
|
5557
5779
|
}
|
|
5558
|
-
return
|
|
5780
|
+
return [];
|
|
5559
5781
|
}
|
|
5560
5782
|
async function detectFromGoMod(workdir) {
|
|
5561
5783
|
const path = `${workdir}/go.mod`;
|
|
5562
5784
|
if (!await _frameworkDefaultsDeps.fileExists(path))
|
|
5563
5785
|
return null;
|
|
5564
|
-
return { type: "manifest", path, patterns: ["**/*_test.go"] };
|
|
5786
|
+
return { type: "manifest", framework: "go", path, patterns: ["**/*_test.go"] };
|
|
5565
5787
|
}
|
|
5566
5788
|
async function detectFromCargoToml(workdir) {
|
|
5567
5789
|
const path = `${workdir}/Cargo.toml`;
|
|
5568
5790
|
if (!await _frameworkDefaultsDeps.fileExists(path))
|
|
5569
5791
|
return null;
|
|
5570
|
-
return { type: "manifest", path, patterns: ["tests/**/*.rs", "src/**/*.rs"] };
|
|
5792
|
+
return { type: "manifest", framework: "rust", path, patterns: ["tests/**/*.rs", "src/**/*.rs"] };
|
|
5571
5793
|
}
|
|
5572
5794
|
async function detectFromPyprojectDeps(workdir) {
|
|
5573
5795
|
const path = `${workdir}/pyproject.toml`;
|
|
5574
5796
|
const text = await _frameworkDefaultsDeps.readText(path);
|
|
5575
5797
|
if (!text)
|
|
5576
5798
|
return null;
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
return
|
|
5799
|
+
const PYTEST_DEP_RE = /(?:^[ \t]*["']?pytest(?![-\w]))|(?:["']pytest(?![-\w])(?:[>=~!<][^"']*)?["'])/m;
|
|
5800
|
+
if (!PYTEST_DEP_RE.test(text))
|
|
5801
|
+
return null;
|
|
5802
|
+
return {
|
|
5803
|
+
type: "manifest",
|
|
5804
|
+
framework: "pytest",
|
|
5805
|
+
path,
|
|
5806
|
+
patterns: ["test_*.py", "*_test.py", "tests/**/*.py"]
|
|
5807
|
+
};
|
|
5581
5808
|
}
|
|
5582
5809
|
async function detectFromFrameworkDefaults(workdir) {
|
|
5583
|
-
const
|
|
5810
|
+
const [pkgJsonSources, goSource, cargoSource, pyprojectSource] = await Promise.all([
|
|
5584
5811
|
detectFromPackageJson(workdir),
|
|
5585
5812
|
detectFromGoMod(workdir),
|
|
5586
5813
|
detectFromCargoToml(workdir),
|
|
5587
5814
|
detectFromPyprojectDeps(workdir)
|
|
5588
5815
|
]);
|
|
5589
|
-
return
|
|
5816
|
+
return [
|
|
5817
|
+
...pkgJsonSources,
|
|
5818
|
+
...goSource ? [goSource] : [],
|
|
5819
|
+
...cargoSource ? [cargoSource] : [],
|
|
5820
|
+
...pyprojectSource ? [pyprojectSource] : []
|
|
5821
|
+
];
|
|
5590
5822
|
}
|
|
5591
5823
|
var _frameworkDefaultsDeps, JS_FRAMEWORK_DEFAULTS, BUN_TEST_DEFAULTS;
|
|
5592
5824
|
var init_framework_defaults = __esm(() => {
|
|
5825
|
+
init_extglob();
|
|
5593
5826
|
_frameworkDefaultsDeps = {
|
|
5594
5827
|
readText: async (path) => {
|
|
5595
5828
|
const f = Bun.file(path);
|
|
@@ -5599,15 +5832,24 @@ var init_framework_defaults = __esm(() => {
|
|
|
5599
5832
|
},
|
|
5600
5833
|
fileExists: async (path) => Bun.file(path).exists()
|
|
5601
5834
|
};
|
|
5602
|
-
JS_FRAMEWORK_DEFAULTS =
|
|
5603
|
-
vitest: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"],
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5835
|
+
JS_FRAMEWORK_DEFAULTS = [
|
|
5836
|
+
{ depKey: "vitest", framework: "vitest", patterns: ["**/*.{test,spec}.?(c|m)[jt]s?(x)"] },
|
|
5837
|
+
{
|
|
5838
|
+
depKey: "jest",
|
|
5839
|
+
framework: "jest",
|
|
5840
|
+
patterns: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"]
|
|
5841
|
+
},
|
|
5842
|
+
{ depKey: "mocha", framework: "mocha", patterns: ["test/**/*.{js,mjs,cjs}", "**/*.spec.{js,ts}"] },
|
|
5843
|
+
{ depKey: "jasmine", framework: "jasmine", patterns: ["spec/**/*.js"] },
|
|
5844
|
+
{ depKey: "@playwright/test", framework: "playwright", patterns: ["**/*.spec.ts", "**/*.spec.js"] },
|
|
5845
|
+
{ depKey: "cypress", framework: "cypress", patterns: ["cypress/e2e/**/*.cy.{js,jsx,ts,tsx}"] }
|
|
5846
|
+
];
|
|
5847
|
+
BUN_TEST_DEFAULTS = [
|
|
5848
|
+
"**/*.test.{ts,tsx,js,jsx,mjs,cjs}",
|
|
5849
|
+
"**/*_test.{ts,tsx,js,jsx,mjs,cjs}",
|
|
5850
|
+
"**/*.spec.{ts,tsx,js,jsx,mjs,cjs}",
|
|
5851
|
+
"**/*_spec.{ts,tsx,js,jsx,mjs,cjs}"
|
|
5852
|
+
];
|
|
5611
5853
|
});
|
|
5612
5854
|
|
|
5613
5855
|
// src/test-runners/detect/index.ts
|
|
@@ -5653,7 +5895,9 @@ async function detectForDirectory(workdir) {
|
|
|
5653
5895
|
const logger = getSafeLogger();
|
|
5654
5896
|
const tier1Sources = await detectFromFrameworkConfigs(workdir);
|
|
5655
5897
|
const tier1Patterns = tier1Sources.flatMap((s) => [...s.patterns]);
|
|
5656
|
-
const
|
|
5898
|
+
const tier1FrameworksWithPatterns = new Set(tier1Sources.filter((s) => s.patterns.length > 0 && s.framework !== undefined).map((s) => s.framework));
|
|
5899
|
+
const tier2SourcesAll = await detectFromFrameworkDefaults(workdir);
|
|
5900
|
+
const tier2Sources = tier2SourcesAll.filter((s) => !s.framework || !tier1FrameworksWithPatterns.has(s.framework));
|
|
5657
5901
|
const tier2Patterns = tier2Sources.flatMap((s) => [...s.patterns]);
|
|
5658
5902
|
const tier3Source = await detectFromFileScan(workdir);
|
|
5659
5903
|
const tier3Patterns = tier3Source ? [...tier3Source.patterns] : [];
|
|
@@ -7191,6 +7435,7 @@ function buildAuditContent(entry, epochMs) {
|
|
|
7191
7435
|
const lines = [
|
|
7192
7436
|
`Timestamp: ${ts}`,
|
|
7193
7437
|
`Session: ${entry.sessionName}`,
|
|
7438
|
+
...entry.recordId ? [`RecordId: ${entry.recordId}`] : [],
|
|
7194
7439
|
...entry.sessionId ? [`SessionId: ${entry.sessionId}`] : [],
|
|
7195
7440
|
`Type: ${typeLabel}`,
|
|
7196
7441
|
`StoryId: ${entry.storyId ?? "(none)"}`,
|
|
@@ -7445,6 +7690,7 @@ class SpawnAcpSession {
|
|
|
7445
7690
|
pidRegistry;
|
|
7446
7691
|
activeProc = null;
|
|
7447
7692
|
id;
|
|
7693
|
+
recordId;
|
|
7448
7694
|
constructor(opts) {
|
|
7449
7695
|
this.agentName = opts.agentName;
|
|
7450
7696
|
this.sessionName = opts.sessionName;
|
|
@@ -7455,6 +7701,7 @@ class SpawnAcpSession {
|
|
|
7455
7701
|
this.env = opts.env;
|
|
7456
7702
|
this.pidRegistry = opts.pidRegistry;
|
|
7457
7703
|
this.id = opts.id;
|
|
7704
|
+
this.recordId = opts.recordId;
|
|
7458
7705
|
}
|
|
7459
7706
|
async prompt(text) {
|
|
7460
7707
|
const cmd = [
|
|
@@ -7602,7 +7849,7 @@ class SpawnAcpSession {
|
|
|
7602
7849
|
await this.trackedSpawn(cmd);
|
|
7603
7850
|
}
|
|
7604
7851
|
}
|
|
7605
|
-
function
|
|
7852
|
+
function parseSessionIds(stdout) {
|
|
7606
7853
|
for (const line of stdout.split(`
|
|
7607
7854
|
`).reverse()) {
|
|
7608
7855
|
const trimmed = line.trim();
|
|
@@ -7610,12 +7857,17 @@ function parseSessionId(stdout) {
|
|
|
7610
7857
|
continue;
|
|
7611
7858
|
try {
|
|
7612
7859
|
const parsed = JSON.parse(trimmed);
|
|
7613
|
-
const
|
|
7614
|
-
|
|
7615
|
-
|
|
7860
|
+
const sessionId = parsed.acpxSessionId;
|
|
7861
|
+
const recordId = parsed.acpxRecordId;
|
|
7862
|
+
if (typeof sessionId === "string" && sessionId.length > 0) {
|
|
7863
|
+
return {
|
|
7864
|
+
sessionId,
|
|
7865
|
+
recordId: typeof recordId === "string" && recordId.length > 0 ? recordId : undefined
|
|
7866
|
+
};
|
|
7867
|
+
}
|
|
7616
7868
|
} catch {}
|
|
7617
7869
|
}
|
|
7618
|
-
return;
|
|
7870
|
+
return { sessionId: undefined, recordId: undefined };
|
|
7619
7871
|
}
|
|
7620
7872
|
|
|
7621
7873
|
class SpawnAcpClient {
|
|
@@ -7672,6 +7924,7 @@ class SpawnAcpClient {
|
|
|
7672
7924
|
if (exitCode !== 0) {
|
|
7673
7925
|
throw new Error(`[acp-adapter] Failed to create session: ${stderr || `exit code ${exitCode}`}`);
|
|
7674
7926
|
}
|
|
7927
|
+
const { sessionId, recordId } = parseSessionIds(stdout);
|
|
7675
7928
|
return new SpawnAcpSession({
|
|
7676
7929
|
agentName: opts.agentName,
|
|
7677
7930
|
sessionName,
|
|
@@ -7681,7 +7934,8 @@ class SpawnAcpClient {
|
|
|
7681
7934
|
permissionMode: opts.permissionMode,
|
|
7682
7935
|
env: this.env,
|
|
7683
7936
|
pidRegistry: this.pidRegistry,
|
|
7684
|
-
id:
|
|
7937
|
+
id: sessionId,
|
|
7938
|
+
recordId
|
|
7685
7939
|
});
|
|
7686
7940
|
}
|
|
7687
7941
|
async loadSession(sessionName, agentName, permissionMode) {
|
|
@@ -7690,6 +7944,7 @@ class SpawnAcpClient {
|
|
|
7690
7944
|
if (exitCode !== 0) {
|
|
7691
7945
|
return null;
|
|
7692
7946
|
}
|
|
7947
|
+
const { sessionId, recordId } = parseSessionIds(stdout);
|
|
7693
7948
|
return new SpawnAcpSession({
|
|
7694
7949
|
agentName,
|
|
7695
7950
|
sessionName,
|
|
@@ -7699,7 +7954,8 @@ class SpawnAcpClient {
|
|
|
7699
7954
|
permissionMode,
|
|
7700
7955
|
env: this.env,
|
|
7701
7956
|
pidRegistry: this.pidRegistry,
|
|
7702
|
-
id:
|
|
7957
|
+
id: sessionId,
|
|
7958
|
+
recordId
|
|
7703
7959
|
});
|
|
7704
7960
|
}
|
|
7705
7961
|
async closeSession(sessionName, agentName) {
|
|
@@ -22981,6 +23237,7 @@ class AcpAgentAdapter {
|
|
|
22981
23237
|
writePromptAudit({
|
|
22982
23238
|
prompt: currentPrompt,
|
|
22983
23239
|
sessionName,
|
|
23240
|
+
recordId: session.recordId,
|
|
22984
23241
|
sessionId: session.id,
|
|
22985
23242
|
workdir: options.workdir,
|
|
22986
23243
|
projectDir: options.projectDir,
|
|
@@ -23135,6 +23392,7 @@ class AcpAgentAdapter {
|
|
|
23135
23392
|
writePromptAudit({
|
|
23136
23393
|
prompt,
|
|
23137
23394
|
sessionName: completeSessionName,
|
|
23395
|
+
recordId: session.recordId,
|
|
23138
23396
|
sessionId: session.id,
|
|
23139
23397
|
workdir: workdir ?? process.cwd(),
|
|
23140
23398
|
auditDir: _completeAuditConfig.agent.promptAudit.dir,
|
|
@@ -23711,7 +23969,7 @@ function migrateLegacyTestPattern(raw, logger) {
|
|
|
23711
23969
|
|
|
23712
23970
|
// src/config/path-security.ts
|
|
23713
23971
|
import { existsSync as existsSync3, lstatSync, realpathSync } from "fs";
|
|
23714
|
-
import { basename, isAbsolute as isAbsolute3, normalize, resolve as resolve2 } from "path";
|
|
23972
|
+
import { basename, isAbsolute as isAbsolute3, normalize as normalize2, resolve as resolve2 } from "path";
|
|
23715
23973
|
function validateDirectory(dirPath, baseDir) {
|
|
23716
23974
|
const resolved = resolve2(dirPath);
|
|
23717
23975
|
if (!existsSync3(resolved)) {
|
|
@@ -23741,8 +23999,8 @@ function validateDirectory(dirPath, baseDir) {
|
|
|
23741
23999
|
return realPath;
|
|
23742
24000
|
}
|
|
23743
24001
|
function isWithinDirectory(targetPath, basePath) {
|
|
23744
|
-
const normalizedTarget =
|
|
23745
|
-
const normalizedBase =
|
|
24002
|
+
const normalizedTarget = normalize2(targetPath);
|
|
24003
|
+
const normalizedBase = normalize2(basePath);
|
|
23746
24004
|
if (!isAbsolute3(normalizedTarget) || !isAbsolute3(normalizedBase)) {
|
|
23747
24005
|
return false;
|
|
23748
24006
|
}
|
|
@@ -38362,14 +38620,14 @@ var init_init_context = __esm(() => {
|
|
|
38362
38620
|
|
|
38363
38621
|
// src/utils/path-security.ts
|
|
38364
38622
|
import { realpathSync as realpathSync3 } from "fs";
|
|
38365
|
-
import { dirname as dirname4, isAbsolute as isAbsolute5, join as join28, normalize as
|
|
38623
|
+
import { dirname as dirname4, isAbsolute as isAbsolute5, join as join28, normalize as normalize3, resolve as resolve7 } from "path";
|
|
38366
38624
|
function safeRealpathForComparison(p) {
|
|
38367
38625
|
try {
|
|
38368
38626
|
return realpathSync3(p);
|
|
38369
38627
|
} catch {
|
|
38370
38628
|
const parent = dirname4(p);
|
|
38371
38629
|
if (parent === p)
|
|
38372
|
-
return
|
|
38630
|
+
return normalize3(p);
|
|
38373
38631
|
const resolvedParent = safeRealpathForComparison(parent);
|
|
38374
38632
|
return join28(resolvedParent, p.split("/").pop() ?? "");
|
|
38375
38633
|
}
|
|
@@ -38380,7 +38638,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
38380
38638
|
}
|
|
38381
38639
|
const resolvedRoots = allowedRoots.map((r) => safeRealpathForComparison(resolve7(r)));
|
|
38382
38640
|
if (isAbsolute5(modulePath)) {
|
|
38383
|
-
const normalized =
|
|
38641
|
+
const normalized = normalize3(modulePath);
|
|
38384
38642
|
const resolved = safeRealpathForComparison(normalized);
|
|
38385
38643
|
const isWithin = resolvedRoots.some((root) => resolved.startsWith(`${root}/`) || resolved === root);
|
|
38386
38644
|
if (isWithin) {
|
|
@@ -39142,7 +39400,7 @@ var package_default;
|
|
|
39142
39400
|
var init_package = __esm(() => {
|
|
39143
39401
|
package_default = {
|
|
39144
39402
|
name: "@nathapp/nax",
|
|
39145
|
-
version: "0.62.0
|
|
39403
|
+
version: "0.62.0",
|
|
39146
39404
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
39147
39405
|
type: "module",
|
|
39148
39406
|
bin: {
|
|
@@ -39222,8 +39480,8 @@ var init_version = __esm(() => {
|
|
|
39222
39480
|
NAX_VERSION = package_default.version;
|
|
39223
39481
|
NAX_COMMIT = (() => {
|
|
39224
39482
|
try {
|
|
39225
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
39226
|
-
return "
|
|
39483
|
+
if (/^[0-9a-f]{6,10}$/.test("2aa5324d"))
|
|
39484
|
+
return "2aa5324d";
|
|
39227
39485
|
} catch {}
|
|
39228
39486
|
try {
|
|
39229
39487
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|