@taodigital/crawler 1.2.0 → 1.2.2

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/job-queue.js CHANGED
@@ -460,25 +460,34 @@ async function getJob(tenantId, jobId) {
460
460
  (tx) => tx.platformJob.findFirst({ where: { id: jobId, tenantId } })
461
461
  );
462
462
  }
463
- async function listJobs(tenantId, projectId) {
464
- return withTenant(tenantId, async (tx) => {
465
- const jobs = await tx.platformJob.findMany({
463
+ async function countJobs(tenantId, projectId) {
464
+ return withTenant(tenantId, (tx) => tx.platformJob.count({ where: { tenantId, projectId } }));
465
+ }
466
+ async function listJobs(tenantId, projectId, { skip = 0, take = 50 } = {}) {
467
+ const jobs = await withTenant(
468
+ tenantId,
469
+ (tx) => tx.platformJob.findMany({
466
470
  where: { tenantId, projectId },
467
471
  orderBy: { queuedAt: "desc" },
468
- take: 50
469
- });
472
+ skip,
473
+ take
474
+ })
475
+ );
476
+ if (jobs.length === 0) return jobs;
477
+ let modelByJobId = /* @__PURE__ */ new Map();
478
+ try {
470
479
  const jobIds = jobs.map((j) => j.id);
471
- if (jobIds.length === 0) return jobs;
472
- const modelRows = await tx.lLMCallLog.groupBy({
480
+ const modelRows = await prismaAdmin.lLMCallLog.groupBy({
473
481
  by: ["jobId"],
474
482
  where: { tenantId, jobId: { in: jobIds } },
475
483
  _max: { model: true, calledAt: true }
476
484
  });
477
- const modelByJobId = new Map(
485
+ modelByJobId = new Map(
478
486
  modelRows.filter((r) => r.jobId && r._max?.model).map((r) => [r.jobId, r._max.model])
479
487
  );
480
- return jobs.map((j) => ({ ...j, llmModel: modelByJobId.get(j.id) ?? null }));
481
- });
488
+ } catch {
489
+ }
490
+ return jobs.map((j) => ({ ...j, llmModel: modelByJobId.get(j.id) ?? null }));
482
491
  }
483
492
  async function cancelJob(tenantId, jobId) {
484
493
  const controller = runningControllers.get(jobId);
@@ -2355,6 +2364,15 @@ async function processMultiRoleCrawlJob(jobId, config) {
2355
2364
  );
2356
2365
  const savedCrawlConfig = proj?.crawlConfig ?? {};
2357
2366
  const credentialProfiles = config.credentialProfiles ?? [];
2367
+ const multiRoleWsProxy = await AppBrain.getTenantProxyConfig(tenantId).catch(() => null);
2368
+ const multiRoleResolvedProxyUrl = (() => {
2369
+ const projectProxy = savedCrawlConfig?.proxyUrl;
2370
+ if (projectProxy === "agent" || multiRoleWsProxy?.mode === "agent") return "agent";
2371
+ if (projectProxy === "auto" || multiRoleWsProxy?.mode === "auto") return "auto";
2372
+ if (projectProxy && projectProxy !== "auto" && projectProxy !== "agent") return projectProxy;
2373
+ if (multiRoleWsProxy?.mode === "custom" && multiRoleWsProxy.proxyUrl) return multiRoleWsProxy.proxyUrl;
2374
+ return void 0;
2375
+ })();
2358
2376
  await updateJobProgress(tenantId, jobId, 10, "Enqueuing primary crawl\u2026");
2359
2377
  const primaryJobId = await enqueueJob("CRAWL", {
2360
2378
  tenantId,
@@ -2366,7 +2384,7 @@ async function processMultiRoleCrawlJob(jobId, config) {
2366
2384
  startUrls: savedCrawlConfig.seedUrls?.length ? savedCrawlConfig.seedUrls : void 0,
2367
2385
  deviceProfiles: savedCrawlConfig.deviceProfiles ?? ["DESKTOP"],
2368
2386
  loginInstructions: savedCrawlConfig.loginInstructions,
2369
- proxyUrl: savedCrawlConfig.proxyUrl,
2387
+ proxyUrl: multiRoleResolvedProxyUrl,
2370
2388
  useSitemap: true,
2371
2389
  sitemapUrl: savedCrawlConfig.sitemapUrl
2372
2390
  });
@@ -2415,7 +2433,7 @@ async function processMultiRoleCrawlJob(jobId, config) {
2415
2433
  storageState: savedCrawlConfig.storageState,
2416
2434
  deviceProfiles: savedCrawlConfig.deviceProfiles ?? ["DESKTOP"],
2417
2435
  loginInstructions: savedCrawlConfig.loginInstructions,
2418
- proxyUrl: savedCrawlConfig.proxyUrl
2436
+ proxyUrl: multiRoleResolvedProxyUrl
2419
2437
  });
2420
2438
  roleJobs.push({ roleName: profile.name, jobId: roleJobId });
2421
2439
  logger.info({ roleJobId, roleName: profile.name, projectId }, "[multi-role-crawl] ROLE_CRAWL enqueued");
@@ -2494,6 +2512,15 @@ async function processRoleCrawlJob(jobId, config) {
2494
2512
  (tx) => tx.project.findUnique({ where: { id: projectId }, select: { crawlConfig: true } })
2495
2513
  );
2496
2514
  const savedCrawlConfig = proj?.crawlConfig ?? {};
2515
+ const roleCrawlWsProxy = await AppBrain.getTenantProxyConfig(tenantId).catch(() => null);
2516
+ const roleCrawlResolvedProxyUrl = (() => {
2517
+ const projectProxy = config.proxyUrl ?? savedCrawlConfig?.proxyUrl;
2518
+ if (projectProxy === "agent" || roleCrawlWsProxy?.mode === "agent") return "agent";
2519
+ if (projectProxy === "auto" || roleCrawlWsProxy?.mode === "auto") return "auto";
2520
+ if (projectProxy && projectProxy !== "auto" && projectProxy !== "agent") return projectProxy;
2521
+ if (roleCrawlWsProxy?.mode === "custom" && roleCrawlWsProxy.proxyUrl) return roleCrawlWsProxy.proxyUrl;
2522
+ return void 0;
2523
+ })();
2497
2524
  const runtimeCreds = config.credentials ? {
2498
2525
  ...config.credentials,
2499
2526
  ...config.credentials.totpSecret ? { getOneTimeCode: async () => generateTOTP(config.credentials.totpSecret) } : {}
@@ -2503,7 +2530,7 @@ async function processRoleCrawlJob(jobId, config) {
2503
2530
  ...config,
2504
2531
  credentials: runtimeCreds,
2505
2532
  storageState: config.storageState ?? savedCrawlConfig.storageState,
2506
- proxyUrl: config.proxyUrl ?? savedCrawlConfig.proxyUrl,
2533
+ proxyUrl: roleCrawlResolvedProxyUrl,
2507
2534
  loginInstructions: config.loginInstructions ?? savedCrawlConfig.loginInstructions,
2508
2535
  abortSignal: controller.signal,
2509
2536
  jobId,
@@ -3024,14 +3051,20 @@ async function processRepoScanJob(jobId, config) {
3024
3051
  const isJavaTestFile = (p) => p.endsWith(".java") && (/[A-Z]\w*Tests?\.java$/.test(p) || /\/tests?\//.test(p) && /[A-Z][a-z]/.test(p.split("/").pop() ?? ""));
3025
3052
  const testGlobs = minimatch ? Array.from(/* @__PURE__ */ new Set([
3026
3053
  testGlob,
3027
- ...testGlob === "**/*.spec.ts" ? ["**/*.test.ts", "**/*.cy.ts"] : [],
3028
- ...testGlob === "**/*.spec.js" ? ["**/*.test.js", "**/*.cy.js"] : []
3054
+ // TS variants: add all common suffixes whenever the primary glob is any *.spec.ts or *.test.ts
3055
+ ...testGlob.endsWith(".spec.ts") ? ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.tsx", "**/*.cy.ts", "**/*.e2e.ts", "**/*.integration.ts", "**/*.smoke.ts", "**/*.steps.ts"] : [],
3056
+ ...testGlob.endsWith(".test.ts") ? ["**/*.spec.ts", "**/*.test.tsx", "**/*.spec.tsx", "**/*.cy.ts", "**/*.e2e.ts", "**/*.integration.ts", "**/*.smoke.ts", "**/*.steps.ts"] : [],
3057
+ // JS variants
3058
+ ...testGlob.endsWith(".spec.js") ? ["**/*.test.js", "**/*.cy.js", "**/*.e2e.js", "**/*.integration.js", "**/*.smoke.js", "**/*.steps.js"] : [],
3059
+ ...testGlob.endsWith(".test.js") ? ["**/*.spec.js", "**/*.cy.js", "**/*.e2e.js", "**/*.integration.js", "**/*.smoke.js", "**/*.steps.js"] : [],
3060
+ // Gherkin feature files are always relevant regardless of primary glob
3061
+ ...framework === "cucumber" || framework === "gherkin" ? ["**/*.feature"] : []
3029
3062
  ])) : [];
3030
3063
  const testPaths = treeResult.paths.filter((p) => {
3031
3064
  if (language === "java") return isJavaTestFile(p);
3032
3065
  if (language === "python") return isPythonTestFile(p);
3033
3066
  if (testGlobs.length) return testGlobs.some((g) => minimatch(p, g, { matchBase: true }));
3034
- return p.endsWith(".spec.ts") || p.endsWith(".spec.js") || p.endsWith(".test.ts") || p.endsWith(".test.js") || p.endsWith(".cy.ts") || p.endsWith(".cy.js") || p.endsWith("_test.py") || p.includes("/test_");
3067
+ return p.endsWith(".spec.ts") || p.endsWith(".spec.js") || p.endsWith(".test.ts") || p.endsWith(".test.js") || p.endsWith(".cy.ts") || p.endsWith(".cy.js") || p.endsWith(".e2e.ts") || p.endsWith(".e2e.js") || p.endsWith(".integration.ts") || p.endsWith(".integration.js") || p.endsWith(".smoke.ts") || p.endsWith(".steps.ts") || p.endsWith(".feature") || p.endsWith("_test.py") || p.includes("/test_");
3035
3068
  }).slice(0, 2e3);
3036
3069
  await updateJobProgress(tenantId, jobId, 40, `Found ${testPaths.length} test files`);
3037
3070
  if (testPaths.length === 0) {
@@ -3299,8 +3332,16 @@ async function processRepoScanJob(jobId, config) {
3299
3332
  try {
3300
3333
  const mainSourcePaths = language === "java" ? treeResult.paths.filter((p) => /^src\/main\/java\//.test(p)).slice(0, 50) : [];
3301
3334
  const testPathSet = new Set(testPaths);
3302
- const sourceFilePaths = language === "typescript" || language === "javascript" ? treeResult.paths.filter((p) => (p.endsWith(".ts") || p.endsWith(".js")) && !testPathSet.has(p) && !p.endsWith(".d.ts") && !p.includes("node_modules/") && !p.includes("dist/") && !p.includes(".config.")).slice(0, 60) : [];
3303
- const allPathsForProfile = [...testPaths, ...mainSourcePaths, ...sourceFilePaths].slice(0, 200);
3335
+ const isFrameworkSupportFile = (p) => /\.(page|fixture|helper|util|utils|data|actions|assertions|locator|locators|steps)\.(ts|js|tsx|jsx)$/.test(p);
3336
+ const isGenericSourceFile = (p) => (p.endsWith(".ts") || p.endsWith(".js") || p.endsWith(".tsx") || p.endsWith(".jsx")) && !p.endsWith(".d.ts") && !p.includes("node_modules/") && !p.includes("dist/") && !p.includes(".next/");
3337
+ const candidateSourcePaths = treeResult.paths.filter(
3338
+ (p) => isGenericSourceFile(p) && !testPathSet.has(p)
3339
+ );
3340
+ const supportFilePaths = candidateSourcePaths.filter(isFrameworkSupportFile).slice(0, 80);
3341
+ const supportPathSet = new Set(supportFilePaths);
3342
+ const otherSourcePaths = candidateSourcePaths.filter((p) => !supportPathSet.has(p)).slice(0, 60);
3343
+ const sourceFilePaths = language === "typescript" || language === "javascript" ? [...supportFilePaths, ...otherSourcePaths] : [];
3344
+ const allPathsForProfile = [...testPaths, ...mainSourcePaths, ...sourceFilePaths].slice(0, 400);
3304
3345
  const freshProfile = await extractStyleProfile(styleSampleFiles, framework, language, tenantId, projectId, jobId, allPathsForProfile, treeResult.paths);
3305
3346
  if (freshProfile) styleProfile = freshProfile;
3306
3347
  } catch {
@@ -3787,6 +3828,7 @@ export {
3787
3828
  claimNextQueuedJob,
3788
3829
  claimSpecificJob,
3789
3830
  classifyFailure,
3831
+ countJobs,
3790
3832
  createTrackedJob,
3791
3833
  dispatchClaimedJob,
3792
3834
  enqueueJob,