@hasna/testers 0.0.52 → 0.0.54

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/README.md CHANGED
@@ -43,6 +43,27 @@ testers workflow fanout wf_abc,wf_def wf_xyz --workers 12 --url https://preview.
43
43
 
44
44
  `--workers` is bounded to 1-12 concurrent sandboxes. Fanout preflights provider credentials, required sandbox environment references, `rsync`, and app source directories before launching workers. Use `--dry-run` to inspect the remote commands, upload plans, and preflight checks without spawning sandboxes.
45
45
 
46
+ ### Next.js Route and Action Inventory
47
+
48
+ For large apps, generate source-derived route coverage from the Next.js app directory. The importer can create route-level scenarios and one scenario per discovered link, button, form, input, or API method, then group those action scenarios into sandbox workflows for fanout.
49
+
50
+ ```bash
51
+ testers inventory next /path/to/app \
52
+ --project alumia \
53
+ --create-scenarios \
54
+ --create-action-scenarios \
55
+ --create-workflows \
56
+ --create-action-workflows \
57
+ --action-workflow-grouping route \
58
+ --sandbox-provider e2b \
59
+ --sandbox-sync rsync \
60
+ --sandbox-env-optional OPENAI_API_KEY
61
+
62
+ testers workflow fanout --project alumia --tag next-action --workers 6 --url https://preview.example.com --dry-run
63
+ ```
64
+
65
+ Use `--action-workflow-grouping route` for route-specific workflows or `--action-workflow-grouping area-kind` for broader workflows such as commerce buttons or admin API methods.
66
+
46
67
  ### Common Flags
47
68
 
48
69
  - `--json --output results.json` — write structured results to a file for downstream tooling.
package/dist/cli/index.js CHANGED
@@ -59331,6 +59331,7 @@ var init_openapi_import = __esm(() => {
59331
59331
  // src/lib/next-route-inventory.ts
59332
59332
  var exports_next_route_inventory = {};
59333
59333
  __export(exports_next_route_inventory, {
59334
+ scenarioInputsForNextRouteActions: () => scenarioInputsForNextRouteActions,
59334
59335
  scenarioInputForNextRoute: () => scenarioInputForNextRoute,
59335
59336
  importNextRouteInventory: () => importNextRouteInventory,
59336
59337
  discoverNextRouteInventory: () => discoverNextRouteInventory
@@ -59355,6 +59356,7 @@ function discoverNextRouteInventory(options) {
59355
59356
  pages: items.filter((item) => item.kind === "page").length,
59356
59357
  apiRoutes: items.filter((item) => item.kind === "api").length,
59357
59358
  dynamic: items.filter((item) => item.dynamic).length,
59359
+ actions: items.reduce((sum, item) => sum + item.actions.length, 0),
59358
59360
  categories,
59359
59361
  items
59360
59362
  };
@@ -59410,12 +59412,69 @@ function scenarioInputForNextRoute(item, projectId) {
59410
59412
  projectId
59411
59413
  };
59412
59414
  }
59415
+ function scenarioInputsForNextRouteActions(item, projectId) {
59416
+ return item.actions.map((action, index) => scenarioInputForNextRouteAction(item, action, index, projectId));
59417
+ }
59418
+ function scenarioInputForNextRouteAction(item, action, index, projectId) {
59419
+ const label = item.kind === "page" ? "page action" : "API action";
59420
+ const fixtureStep = item.fixtureParams.length > 0 ? `Bind dynamic fixture values for ${item.fixtureParams.map((name) => `:${name}`).join(", ")} before exercising this action.` : undefined;
59421
+ const dynamicStep = item.dynamic ? "Substitute dynamic path parameters with valid fixture values from the target org before opening or calling the route." : undefined;
59422
+ const destructiveGuard = action.destructive ? "If the action reaches a destructive confirmation or mutating final step, verify the warning/cancel path and stop before confirming." : undefined;
59423
+ const pageSteps = [
59424
+ fixtureStep,
59425
+ dynamicStep,
59426
+ `Open the Next.js page ${item.routePath}.`,
59427
+ "Wait for the route to finish loading and verify it does not show a blank shell, framework error page, or unexpected auth loop.",
59428
+ `Locate source-discovered ${action.kind} "${action.label}" from ${action.sourceFile}.`,
59429
+ formatActionStep(action),
59430
+ destructiveGuard,
59431
+ action.kind === "input" ? "Fill the input with safe test data and verify the UI accepts, validates, or rejects it predictably." : "Verify the action produces the expected navigation, modal, toast, table change, validation state, or disabled state.",
59432
+ "Verify the route stays within the expected org/workspace context and does not emit console errors."
59433
+ ].filter(Boolean);
59434
+ const apiSteps = [
59435
+ fixtureStep,
59436
+ dynamicStep,
59437
+ `Call ${action.label} ${item.routePath} using safe fixture data.`,
59438
+ action.destructive ? "Use a harmless dry-run/no-op fixture when available; otherwise verify authentication, authorization, validation, or confirmation blocks without creating destructive side effects." : "Verify the method accepts valid safe input and rejects invalid input with a stable response shape.",
59439
+ "Verify expected authentication, authorization, validation, and tenant isolation behavior.",
59440
+ "Verify response status, JSON shape, and error messages are stable and regression-safe."
59441
+ ].filter(Boolean);
59442
+ return {
59443
+ name: `Next ${label}: ${item.routePath} :: ${action.kind} ${action.label} #${index + 1}`,
59444
+ description: `Source-discovered ${label} ${index + 1} from ${action.sourceFile}. Verify ${action.kind} "${action.label}" on ${item.routePath}.`,
59445
+ steps: item.kind === "page" ? pageSteps : apiSteps,
59446
+ tags: actionTagsForRoute(item, action),
59447
+ priority: action.destructive ? "critical" : item.priority,
59448
+ targetPath: item.routePath,
59449
+ requiresAuth: item.requiresAuth,
59450
+ assertions: item.kind === "page" ? SAFE_PAGE_ASSERTIONS : [],
59451
+ metadata: {
59452
+ source: "next-route-action-inventory",
59453
+ routeFile: item.file,
59454
+ routeKind: item.kind,
59455
+ routePath: item.routePath,
59456
+ category: item.category,
59457
+ methods: item.methods,
59458
+ dynamic: item.dynamic,
59459
+ fixtureParams: item.fixtureParams,
59460
+ actionIndex: index,
59461
+ action,
59462
+ groups: item.groups
59463
+ },
59464
+ parameters: item.fixtureParams.length > 0 ? {
59465
+ routeFixtures: defaultRouteFixturesForParams(item.fixtureParams),
59466
+ routeFixtureParams: item.fixtureParams
59467
+ } : undefined,
59468
+ projectId
59469
+ };
59470
+ }
59413
59471
  function importNextRouteInventory(options) {
59414
59472
  const inventory = discoverNextRouteInventory(options);
59415
59473
  let created = 0;
59416
59474
  let updated = 0;
59417
59475
  let deduped = 0;
59418
59476
  const scenarios = [];
59477
+ const actionScenarios = [];
59419
59478
  const workflows = [];
59420
59479
  if (options.createScenarios) {
59421
59480
  for (const item of inventory.items) {
@@ -59429,10 +59488,28 @@ function importNextRouteInventory(options) {
59429
59488
  deduped++;
59430
59489
  }
59431
59490
  }
59491
+ if (options.createActionScenarios) {
59492
+ for (const item of inventory.items) {
59493
+ for (const input of scenarioInputsForNextRouteActions(item, options.projectId)) {
59494
+ const result = upsertScenario(input);
59495
+ scenarios.push(result.scenario);
59496
+ actionScenarios.push(result.scenario);
59497
+ if (result.action === "created")
59498
+ created++;
59499
+ else if (result.action === "updated")
59500
+ updated++;
59501
+ else
59502
+ deduped++;
59503
+ }
59504
+ }
59505
+ }
59432
59506
  if (options.createWorkflows) {
59433
59507
  workflows.push(...upsertRouteInventoryWorkflows(inventory, options));
59434
59508
  }
59435
- return { inventory, created, updated, deduped, scenarios, workflows };
59509
+ if (options.createActionWorkflows) {
59510
+ workflows.push(...upsertRouteInventoryActionWorkflows(inventory, options));
59511
+ }
59512
+ return { inventory, created, updated, deduped, scenarios, actionScenarios, workflows };
59436
59513
  }
59437
59514
  function upsertRouteInventoryWorkflows(inventory, options) {
59438
59515
  const workflows = [];
@@ -59462,6 +59539,57 @@ function upsertRouteInventoryWorkflows(inventory, options) {
59462
59539
  }
59463
59540
  return workflows;
59464
59541
  }
59542
+ function upsertRouteInventoryActionWorkflows(inventory, options) {
59543
+ const workflows = [];
59544
+ const grouping = options.actionWorkflowGrouping ?? "route";
59545
+ const existingWorkflows = listTestingWorkflows({ projectId: options.projectId, enabled: undefined });
59546
+ if (grouping === "area-kind") {
59547
+ const keys = new Set;
59548
+ for (const item of inventory.items) {
59549
+ for (const action of item.actions) {
59550
+ keys.add(`${item.category}|${action.kind}`);
59551
+ }
59552
+ }
59553
+ for (const key of [...keys].sort()) {
59554
+ const [category, actionKind] = key.split("|");
59555
+ const name = `Next action inventory ${category} ${actionKind}`;
59556
+ const scenarioTags = ["next-action", `area:${category}`, `action:${actionKind}`];
59557
+ workflows.push(upsertTestingWorkflow(existingWorkflows, name, {
59558
+ name,
59559
+ description: `Source-discovered ${actionKind} action coverage for ${category} routes.`,
59560
+ projectId: options.projectId,
59561
+ scenarioFilter: { tags: scenarioTags },
59562
+ execution: workflowExecutionFromOptions(options)
59563
+ }));
59564
+ }
59565
+ return workflows;
59566
+ }
59567
+ for (const item of inventory.items.filter((route) => route.actions.length > 0)) {
59568
+ const name = `Next action inventory ${item.kind} ${item.routePath}`;
59569
+ const scenarioTags = ["next-action", `route:${item.kind}`, `route-path:${item.routePath}`];
59570
+ workflows.push(upsertTestingWorkflow(existingWorkflows, name, {
59571
+ name,
59572
+ description: `Source-discovered action coverage for ${item.kind} route ${item.routePath}.`,
59573
+ projectId: options.projectId,
59574
+ scenarioFilter: { tags: scenarioTags },
59575
+ execution: workflowExecutionFromOptions(options)
59576
+ }));
59577
+ }
59578
+ return workflows;
59579
+ }
59580
+ function workflowExecutionFromOptions(options) {
59581
+ return {
59582
+ target: options.workflowTarget ?? "sandbox",
59583
+ provider: options.workflowProvider,
59584
+ sandboxCleanup: "delete",
59585
+ sandboxSyncStrategy: "rsync",
59586
+ ...options.workflowExecution
59587
+ };
59588
+ }
59589
+ function upsertTestingWorkflow(existingWorkflows, name, input) {
59590
+ const existing = existingWorkflows.find((workflow) => workflow.name === name);
59591
+ return existing ? updateTestingWorkflow(existing.id, input) : createTestingWorkflow(input);
59592
+ }
59465
59593
  function resolveAppDir(rootDir, appDir) {
59466
59594
  const candidates = appDir ? [resolve3(rootDir, appDir)] : [
59467
59595
  join20(rootDir, "packages", "web", "app"),
@@ -59792,6 +59920,19 @@ function tagsForRoute(input) {
59792
59920
  tags.add("api");
59793
59921
  return [...tags];
59794
59922
  }
59923
+ function actionTagsForRoute(item, action) {
59924
+ const tags = new Set([
59925
+ ...item.tags,
59926
+ "next-action",
59927
+ `action:${action.kind}`,
59928
+ `route-path:${item.routePath}`
59929
+ ]);
59930
+ if (action.destructive)
59931
+ tags.add("destructive-action");
59932
+ if (action.requiresFixture)
59933
+ tags.add("fixture-required");
59934
+ return [...tags];
59935
+ }
59795
59936
  function priorityForRoute(routePath, category, kind) {
59796
59937
  if (category === "auth")
59797
59938
  return "critical";
@@ -95410,7 +95551,7 @@ import chalk6 from "chalk";
95410
95551
  // package.json
95411
95552
  var package_default = {
95412
95553
  name: "@hasna/testers",
95413
- version: "0.0.52",
95554
+ version: "0.0.54",
95414
95555
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
95415
95556
  type: "module",
95416
95557
  main: "dist/index.js",
@@ -101035,7 +101176,7 @@ Imported ${imported} scenarios from API spec:`));
101035
101176
  }
101036
101177
  });
101037
101178
  var inventoryCmd = program2.command("inventory").description("Discover source-derived app route/action inventories");
101038
- inventoryCmd.command("next [root]").description("Discover Next.js app routes and optionally import route coverage scenarios").option("--app-dir <path>", "Next.js app directory relative to root (default: packages/web/app or app)").option("--project <id>", "Project ID").option("--no-pages", "Do not include page.tsx/page.ts routes").option("--no-api", "Do not include route.ts/route.js API routes").option("--limit <n>", "Limit discovered routes").option("--create-scenarios", "Upsert source-derived route coverage scenarios", false).option("--create-workflows", "Upsert grouped workflows by area and route kind", false).option("--workflow-target <target>", "Workflow execution target: local or sandbox", "sandbox").option("--sandbox-provider <provider>", "Sandbox provider for created workflows", "e2b").option("--sandbox-cleanup <mode>", "Sandbox cleanup mode: delete, stop, or keep", "delete").option("--sandbox-sync <strategy>", "Sandbox upload sync strategy: rsync or archive", "rsync").option("--sandbox-env-optional <name>", "Optional sandbox env var; forwards host NAME only when set (repeatable)", (val, acc) => {
101179
+ inventoryCmd.command("next [root]").description("Discover Next.js app routes and optionally import route coverage scenarios").option("--app-dir <path>", "Next.js app directory relative to root (default: packages/web/app or app)").option("--project <id>", "Project ID").option("--no-pages", "Do not include page.tsx/page.ts routes").option("--no-api", "Do not include route.ts/route.js API routes").option("--limit <n>", "Limit discovered routes").option("--create-scenarios", "Upsert source-derived route coverage scenarios", false).option("--create-action-scenarios", "Upsert one source-derived scenario per discovered page/API action", false).option("--create-workflows", "Upsert grouped workflows by area and route kind", false).option("--create-action-workflows", "Upsert action-focused workflows for discovered action scenarios", false).option("--action-workflow-grouping <mode>", "Action workflow grouping: route or area-kind", "route").option("--workflow-target <target>", "Workflow execution target: local or sandbox", "sandbox").option("--sandbox-provider <provider>", "Sandbox provider for created workflows", "e2b").option("--sandbox-cleanup <mode>", "Sandbox cleanup mode: delete, stop, or keep", "delete").option("--sandbox-sync <strategy>", "Sandbox upload sync strategy: rsync or archive", "rsync").option("--sandbox-env-optional <name>", "Optional sandbox env var; forwards host NAME only when set (repeatable)", (val, acc) => {
101039
101180
  acc.push(val);
101040
101181
  return acc;
101041
101182
  }, []).option("--timeout <ms>", "Workflow timeout in milliseconds").option("--json", "Output as JSON", false).action(async (root, opts) => {
@@ -101043,6 +101184,9 @@ inventoryCmd.command("next [root]").description("Discover Next.js app routes and
101043
101184
  const { importNextRouteInventory: importNextRouteInventory2 } = await Promise.resolve().then(() => (init_next_route_inventory(), exports_next_route_inventory));
101044
101185
  const projectId = resolveProject2(opts.project) ?? undefined;
101045
101186
  const env = parseSandboxEnv(undefined, opts.sandboxEnvOptional);
101187
+ if (!["route", "area-kind"].includes(opts.actionWorkflowGrouping)) {
101188
+ throw new Error("--action-workflow-grouping must be route or area-kind");
101189
+ }
101046
101190
  const result = importNextRouteInventory2({
101047
101191
  rootDir: root ?? process.cwd(),
101048
101192
  appDir: opts.appDir,
@@ -101051,7 +101195,10 @@ inventoryCmd.command("next [root]").description("Discover Next.js app routes and
101051
101195
  includeApi: opts.api !== false,
101052
101196
  limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
101053
101197
  createScenarios: opts.createScenarios,
101198
+ createActionScenarios: opts.createActionScenarios,
101054
101199
  createWorkflows: opts.createWorkflows,
101200
+ createActionWorkflows: opts.createActionWorkflows,
101201
+ actionWorkflowGrouping: opts.actionWorkflowGrouping,
101055
101202
  workflowTarget: opts.workflowTarget,
101056
101203
  workflowProvider: opts.workflowTarget === "sandbox" ? opts.sandboxProvider : undefined,
101057
101204
  workflowExecution: {
@@ -101073,6 +101220,7 @@ inventoryCmd.command("next [root]").description("Discover Next.js app routes and
101073
101220
  log(chalk6.dim(` App: ${result.inventory.appDir}`));
101074
101221
  log("");
101075
101222
  log(` Routes: ${chalk6.cyan(String(result.inventory.total))} (${result.inventory.pages} pages, ${result.inventory.apiRoutes} API, ${result.inventory.dynamic} dynamic)`);
101223
+ log(` Actions: ${chalk6.cyan(String(result.inventory.actions))}`);
101076
101224
  log(` Scenarios: ${chalk6.green(String(result.created))} created, ${chalk6.yellow(String(result.updated))} updated, ${chalk6.dim(String(result.deduped))} deduped`);
101077
101225
  log(` Workflows: ${result.workflows.length}`);
101078
101226
  log("");
@@ -101082,8 +101230,12 @@ inventoryCmd.command("next [root]").description("Discover Next.js app routes and
101082
101230
  log("");
101083
101231
  if (!opts.createScenarios)
101084
101232
  log(chalk6.dim(" Add --create-scenarios to upsert route scenarios."));
101233
+ if (!opts.createActionScenarios)
101234
+ log(chalk6.dim(" Add --create-action-scenarios to upsert one scenario per discovered action."));
101085
101235
  if (!opts.createWorkflows)
101086
101236
  log(chalk6.dim(" Add --create-workflows to upsert grouped workflows."));
101237
+ if (!opts.createActionWorkflows)
101238
+ log(chalk6.dim(" Add --create-action-workflows to upsert action-focused workflows."));
101087
101239
  log("");
101088
101240
  } catch (error40) {
101089
101241
  logError(chalk6.red(`Error: ${error40 instanceof Error ? error40.message : String(error40)}`));
@@ -30,6 +30,7 @@ export interface NextRouteInventory {
30
30
  pages: number;
31
31
  apiRoutes: number;
32
32
  dynamic: number;
33
+ actions: number;
33
34
  categories: Record<string, number>;
34
35
  items: NextRouteInventoryItem[];
35
36
  }
@@ -41,7 +42,10 @@ export interface ImportNextRouteInventoryOptions {
41
42
  includeApi?: boolean;
42
43
  limit?: number;
43
44
  createScenarios?: boolean;
45
+ createActionScenarios?: boolean;
44
46
  createWorkflows?: boolean;
47
+ createActionWorkflows?: boolean;
48
+ actionWorkflowGrouping?: "route" | "area-kind";
45
49
  workflowTarget?: "local" | "sandbox";
46
50
  workflowProvider?: string;
47
51
  workflowExecution?: Partial<WorkflowExecutionInput>;
@@ -52,6 +56,7 @@ export interface ImportNextRouteInventoryResult {
52
56
  updated: number;
53
57
  deduped: number;
54
58
  scenarios: Scenario[];
59
+ actionScenarios: Scenario[];
55
60
  workflows: TestingWorkflow[];
56
61
  }
57
62
  export declare function discoverNextRouteInventory(options: {
@@ -62,5 +67,6 @@ export declare function discoverNextRouteInventory(options: {
62
67
  limit?: number;
63
68
  }): NextRouteInventory;
64
69
  export declare function scenarioInputForNextRoute(item: NextRouteInventoryItem, projectId?: string): CreateScenarioInput;
70
+ export declare function scenarioInputsForNextRouteActions(item: NextRouteInventoryItem, projectId?: string): CreateScenarioInput[];
65
71
  export declare function importNextRouteInventory(options: ImportNextRouteInventoryOptions): ImportNextRouteInventoryResult;
66
72
  //# sourceMappingURL=next-route-inventory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEV,mBAAmB,EACnB,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,CAAC;AAC3C,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,CAAC;AAEtF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,sBAAsB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B;AAkCD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,kBAAkB,CA4BrB;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,sBAAsB,EAC5B,SAAS,CAAC,EAAE,MAAM,GACjB,mBAAmB,CA8DrB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,CAuBhC"}
1
+ {"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@hasna/testers",
55
- version: "0.0.52",
55
+ version: "0.0.54",
56
56
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
57
57
  type: "module",
58
58
  main: "dist/index.js",
@@ -47090,7 +47090,7 @@ import { join as join14 } from "path";
47090
47090
  // package.json
47091
47091
  var package_default = {
47092
47092
  name: "@hasna/testers",
47093
- version: "0.0.52",
47093
+ version: "0.0.54",
47094
47094
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
47095
47095
  type: "module",
47096
47096
  main: "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.52",
3
+ "version": "0.0.54",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",