@detiq/crawler 1.1.22 → 1.1.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@detiq/crawler",
3
- "version": "1.1.22",
3
+ "version": "1.1.23",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
package/src/job-queue.ts CHANGED
@@ -372,10 +372,15 @@ export function startJobWorkerLoop(opts?: { pollIntervalMs?: number; concurrency
372
372
  logger.error({ err: String(err) }, '[job-worker] stale-job sweep failed');
373
373
  }
374
374
  try {
375
- for (let i = 0; i < concurrency; i++) {
376
- const job = await claimNextQueuedJob();
377
- if (!job) break;
378
- void dispatchClaimedJob(job); // not awaited — runs concurrently while the loop keeps polling
375
+ const pauseRow = await prismaAdmin.systemConfig.findUnique({ where: { key: 'queue.paused' } });
376
+ if (pauseRow?.value === 'true') {
377
+ logger.info('[job-worker] queue is paused — skipping claim');
378
+ } else {
379
+ for (let i = 0; i < concurrency; i++) {
380
+ const job = await claimNextQueuedJob();
381
+ if (!job) break;
382
+ void dispatchClaimedJob(job); // not awaited — runs concurrently while the loop keeps polling
383
+ }
379
384
  }
380
385
  } catch (err) {
381
386
  logger.error({ err: String(err) }, '[job-worker] claim loop failed');
@@ -2389,10 +2394,35 @@ async function processCaptureStepsJob(jobId: string, config: any) {
2389
2394
  if (!tc) throw new Error('Test case not found');
2390
2395
 
2391
2396
  const rawSteps = Array.isArray(tc.steps) ? tc.steps : [];
2392
- const stepTexts = rawSteps.map(captureStepText).filter(Boolean);
2397
+ if (rawSteps.length === 0) throw new Error('Test case has no steps to capture');
2398
+
2399
+ // Resolve env tokens from testDataInline so {{fze.env.URL}} → concrete URL before execution plan
2400
+ const _tdi = ((tc as any).testDataInline ?? {}) as Record<string, unknown>;
2401
+ const _tdiStartUrl = typeof _tdi['startUrl'] === 'string' && (_tdi['startUrl'] as string).startsWith('http')
2402
+ ? (_tdi['startUrl'] as string) : '';
2403
+ const _recordedUrls = Array.isArray(_tdi['recordedUrls']) ? (_tdi['recordedUrls'] as unknown[]).map(String) : [];
2404
+ const _recordedStartUrl = _recordedUrls.find((u) => u.startsWith('http')) ?? '';
2405
+
2406
+ const baseUrl = config.baseUrl || _tdiStartUrl || _recordedStartUrl || tc?.screen?.project?.appUrl || crawlCfg?.appUrl || '';
2407
+
2408
+ // Build env token → concrete value map from testDataInline for step text substitution
2409
+ const _envTokenMap: Record<string, string> = {};
2410
+ for (const [k, v] of Object.entries(_tdi)) {
2411
+ if (typeof v === 'string' && v.startsWith('http')) _envTokenMap[k] = v;
2412
+ }
2413
+ if (_recordedStartUrl && !_envTokenMap['startUrl']) _envTokenMap['startUrl'] = _recordedStartUrl;
2414
+
2415
+ // Replace {{fze.env.X}} and [env:X] tokens in step texts with concrete values from testDataInline.
2416
+ // IMPORTANT: only substitute if we have a concrete value for that key.
2417
+ // Fall back to [key] placeholder — never fall back to baseUrl (would inject URL into credential fields).
2418
+ const resolveStepText = (text: string): string =>
2419
+ text
2420
+ .replace(/\{\{\s*fze\.env\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g, (_m, k) => _envTokenMap[k] ?? `[${k}]`)
2421
+ .replace(/\[env:([A-Za-z_][A-Za-z0-9_]*)\]/g, (_m, k) => _envTokenMap[k] ?? `[${k}]`);
2422
+
2423
+ const stepTexts = rawSteps.map(captureStepText).filter(Boolean).map(resolveStepText);
2393
2424
  if (stepTexts.length === 0) throw new Error('Test case has no steps to capture');
2394
2425
 
2395
- const baseUrl = config.baseUrl || tc?.screen?.project?.appUrl || crawlCfg?.appUrl || '';
2396
2426
  let plan = await generateExecutionPlan(tenantId, { ...tc, steps: stepTexts }, baseUrl).catch(() => []);
2397
2427
  plan = Array.isArray(plan)
2398
2428
  ? plan.filter(Boolean).map((step: any, idx: number) => ({ ...step, stepNumber: idx + 1 }))