@hasna/testers 0.0.55 → 0.0.57
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 +5 -4
- package/dist/cli/index.js +89 -9
- package/dist/lib/next-route-inventory.d.ts +1 -1
- package/dist/lib/next-route-inventory.d.ts.map +1 -1
- package/dist/lib/workflow-fanout.d.ts +16 -0
- package/dist/lib/workflow-fanout.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/server/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,9 +39,10 @@ testers workflow create "Projects CRUD" \
|
|
|
39
39
|
|
|
40
40
|
testers workflow fanout --project alumia --workers 6 --url https://preview.example.com
|
|
41
41
|
testers workflow fanout wf_abc,wf_def wf_xyz --workers 12 --url https://preview.example.com --json
|
|
42
|
+
testers workflow fanout --project alumia --tag action-specific --workers 6 --batch-size 12 --batch 1 --url https://preview.example.com
|
|
42
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
|
+
`--workers` is bounded to 1-12 concurrent sandboxes. Use `--batch-size` with a 1-based `--batch` to run large workflow corpora in deterministic waves, or `--offset` for a manual selected-workflow cursor. 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
46
|
|
|
46
47
|
### Next.js Route and Action Inventory
|
|
47
48
|
|
|
@@ -54,7 +55,7 @@ testers inventory next /path/to/app \
|
|
|
54
55
|
--create-action-scenarios \
|
|
55
56
|
--create-workflows \
|
|
56
57
|
--create-action-workflows \
|
|
57
|
-
--action-workflow-grouping
|
|
58
|
+
--action-workflow-grouping action \
|
|
58
59
|
--sandbox-provider e2b \
|
|
59
60
|
--sandbox-sync rsync \
|
|
60
61
|
--sandbox-app-source /path/to/app \
|
|
@@ -63,10 +64,10 @@ testers inventory next /path/to/app \
|
|
|
63
64
|
--sandbox-app-wait-url http://127.0.0.1:3000/health \
|
|
64
65
|
--sandbox-env-optional OPENAI_API_KEY
|
|
65
66
|
|
|
66
|
-
testers workflow fanout --project alumia --tag
|
|
67
|
+
testers workflow fanout --project alumia --tag action-specific --workers 6 --batch-size 12 --batch 1 --url https://preview.example.com --dry-run
|
|
67
68
|
```
|
|
68
69
|
|
|
69
|
-
Use `--action-workflow-grouping route` for route-specific workflows or
|
|
70
|
+
Use `--action-workflow-grouping action` for one workflow per discovered action, `route` for route-specific workflows, or `area-kind` for broader workflows such as commerce buttons or admin API methods. Add the `--sandbox-app-*` flags when the sandbox should rsync, install, start, and test the app source instead of only testing an already-running URL.
|
|
70
71
|
|
|
71
72
|
### Common Flags
|
|
72
73
|
|
package/dist/cli/index.js
CHANGED
|
@@ -59443,7 +59443,7 @@ function scenarioInputForNextRouteAction(item, action, index, projectId) {
|
|
|
59443
59443
|
name: `Next ${label}: ${item.routePath} :: ${action.kind} ${action.label} #${index + 1}`,
|
|
59444
59444
|
description: `Source-discovered ${label} ${index + 1} from ${action.sourceFile}. Verify ${action.kind} "${action.label}" on ${item.routePath}.`,
|
|
59445
59445
|
steps: item.kind === "page" ? pageSteps : apiSteps,
|
|
59446
|
-
tags: actionTagsForRoute(item, action),
|
|
59446
|
+
tags: actionTagsForRoute(item, action, index),
|
|
59447
59447
|
priority: action.destructive ? "critical" : item.priority,
|
|
59448
59448
|
targetPath: item.routePath,
|
|
59449
59449
|
requiresAuth: item.requiresAuth,
|
|
@@ -59564,6 +59564,28 @@ function upsertRouteInventoryActionWorkflows(inventory, options) {
|
|
|
59564
59564
|
}
|
|
59565
59565
|
return workflows;
|
|
59566
59566
|
}
|
|
59567
|
+
if (grouping === "action") {
|
|
59568
|
+
for (const item of inventory.items.filter((route) => route.actions.length > 0)) {
|
|
59569
|
+
item.actions.forEach((action, index) => {
|
|
59570
|
+
const name = `Next action inventory ${item.kind} ${item.routePath} #${index + 1} ${action.kind} ${action.label}`;
|
|
59571
|
+
const scenarioTags = [
|
|
59572
|
+
"next-action",
|
|
59573
|
+
"action-specific",
|
|
59574
|
+
`route:${item.kind}`,
|
|
59575
|
+
`route-path:${item.routePath}`,
|
|
59576
|
+
`action-ordinal:${index + 1}`
|
|
59577
|
+
];
|
|
59578
|
+
workflows.push(upsertTestingWorkflow(existingWorkflows, name, {
|
|
59579
|
+
name,
|
|
59580
|
+
description: `Source-discovered action #${index + 1} coverage for ${item.kind} route ${item.routePath}: ${action.kind} "${action.label}".`,
|
|
59581
|
+
projectId: options.projectId,
|
|
59582
|
+
scenarioFilter: { tags: scenarioTags },
|
|
59583
|
+
execution: workflowExecutionFromOptions(options)
|
|
59584
|
+
}));
|
|
59585
|
+
});
|
|
59586
|
+
}
|
|
59587
|
+
return workflows;
|
|
59588
|
+
}
|
|
59567
59589
|
for (const item of inventory.items.filter((route) => route.actions.length > 0)) {
|
|
59568
59590
|
const name = `Next action inventory ${item.kind} ${item.routePath}`;
|
|
59569
59591
|
const scenarioTags = ["next-action", `route:${item.kind}`, `route-path:${item.routePath}`];
|
|
@@ -59920,12 +59942,14 @@ function tagsForRoute(input) {
|
|
|
59920
59942
|
tags.add("api");
|
|
59921
59943
|
return [...tags];
|
|
59922
59944
|
}
|
|
59923
|
-
function actionTagsForRoute(item, action) {
|
|
59945
|
+
function actionTagsForRoute(item, action, index) {
|
|
59924
59946
|
const tags = new Set([
|
|
59925
59947
|
...item.tags,
|
|
59926
59948
|
"next-action",
|
|
59949
|
+
"action-specific",
|
|
59927
59950
|
`action:${action.kind}`,
|
|
59928
|
-
`route-path:${item.routePath}
|
|
59951
|
+
`route-path:${item.routePath}`,
|
|
59952
|
+
`action-ordinal:${index + 1}`
|
|
59929
59953
|
]);
|
|
59930
59954
|
if (action.destructive)
|
|
59931
59955
|
tags.add("destructive-action");
|
|
@@ -60604,6 +60628,7 @@ var exports_workflow_fanout = {};
|
|
|
60604
60628
|
__export(exports_workflow_fanout, {
|
|
60605
60629
|
runWorkflowFanout: () => runWorkflowFanout,
|
|
60606
60630
|
resolveWorkflowFanoutSelection: () => resolveWorkflowFanoutSelection,
|
|
60631
|
+
resolveWorkflowFanoutBatch: () => resolveWorkflowFanoutBatch,
|
|
60607
60632
|
normalizeFanoutWorkerCount: () => normalizeFanoutWorkerCount,
|
|
60608
60633
|
checkWorkflowFanoutReadiness: () => checkWorkflowFanoutReadiness
|
|
60609
60634
|
});
|
|
@@ -60619,6 +60644,33 @@ function normalizeFanoutWorkerCount(value) {
|
|
|
60619
60644
|
}
|
|
60620
60645
|
return workers;
|
|
60621
60646
|
}
|
|
60647
|
+
function resolveWorkflowFanoutBatch(workflows, options = {}) {
|
|
60648
|
+
const batchSize = normalizeOptionalPositiveInteger(options.batchSize, "workflow fanout batch size");
|
|
60649
|
+
const batch = normalizeOptionalPositiveInteger(options.batch, "workflow fanout batch");
|
|
60650
|
+
const offset = normalizeOptionalNonNegativeInteger(options.offset, "workflow fanout offset");
|
|
60651
|
+
if (batch !== undefined && offset !== undefined) {
|
|
60652
|
+
throw new Error("workflow fanout batch and offset cannot both be set");
|
|
60653
|
+
}
|
|
60654
|
+
if (batch !== undefined && batchSize === undefined) {
|
|
60655
|
+
throw new Error("workflow fanout batch requires batch size");
|
|
60656
|
+
}
|
|
60657
|
+
const resolvedOffset = batch !== undefined && batchSize !== undefined ? (batch - 1) * batchSize : offset ?? 0;
|
|
60658
|
+
const limit = batchSize;
|
|
60659
|
+
const selected = workflows.slice(resolvedOffset, limit === undefined ? undefined : resolvedOffset + limit);
|
|
60660
|
+
if (selected.length === 0) {
|
|
60661
|
+
throw new Error(`No testing workflows matched the fanout batch selection (matched ${workflows.length}, offset ${resolvedOffset})`);
|
|
60662
|
+
}
|
|
60663
|
+
return {
|
|
60664
|
+
workflows: selected,
|
|
60665
|
+
selection: {
|
|
60666
|
+
matched: workflows.length,
|
|
60667
|
+
offset: resolvedOffset,
|
|
60668
|
+
...limit !== undefined ? { limit } : {},
|
|
60669
|
+
...batch !== undefined ? { batch } : {},
|
|
60670
|
+
...batchSize !== undefined ? { batchSize, totalBatches: Math.ceil(workflows.length / batchSize) } : {}
|
|
60671
|
+
}
|
|
60672
|
+
};
|
|
60673
|
+
}
|
|
60622
60674
|
function resolveWorkflowFanoutSelection(options) {
|
|
60623
60675
|
const ids = splitWorkflowIds(options.workflowIds);
|
|
60624
60676
|
const workflows = ids.length > 0 ? ids.map((id) => {
|
|
@@ -60738,7 +60790,8 @@ async function mapWithConcurrency(items, limit, worker) {
|
|
|
60738
60790
|
}
|
|
60739
60791
|
async function runWorkflowFanout(options, dependencies = {}) {
|
|
60740
60792
|
const workers = normalizeFanoutWorkerCount(options.workers);
|
|
60741
|
-
const
|
|
60793
|
+
const matchedWorkflows = resolveWorkflowFanoutSelection(options);
|
|
60794
|
+
const { workflows, selection } = resolveWorkflowFanoutBatch(matchedWorkflows, options);
|
|
60742
60795
|
const {
|
|
60743
60796
|
runTestingWorkflow: runOne = runTestingWorkflow,
|
|
60744
60797
|
preflight: preflightOverride,
|
|
@@ -60759,6 +60812,7 @@ async function runWorkflowFanout(options, dependencies = {}) {
|
|
|
60759
60812
|
return {
|
|
60760
60813
|
status: "failed",
|
|
60761
60814
|
workers,
|
|
60815
|
+
selection,
|
|
60762
60816
|
total: workflows.length,
|
|
60763
60817
|
passed: 0,
|
|
60764
60818
|
failed: workflows.length,
|
|
@@ -60814,6 +60868,7 @@ async function runWorkflowFanout(options, dependencies = {}) {
|
|
|
60814
60868
|
return {
|
|
60815
60869
|
status: dryRun ? "dry-run" : failed > 0 ? "failed" : "passed",
|
|
60816
60870
|
workers,
|
|
60871
|
+
selection,
|
|
60817
60872
|
total: items.length,
|
|
60818
60873
|
passed,
|
|
60819
60874
|
failed,
|
|
@@ -60847,6 +60902,24 @@ function defaultCommandExists(command) {
|
|
|
60847
60902
|
const result = spawnSync2(command, ["--version"], { encoding: "utf8" });
|
|
60848
60903
|
return !result.error && result.status === 0;
|
|
60849
60904
|
}
|
|
60905
|
+
function normalizeOptionalPositiveInteger(value, label) {
|
|
60906
|
+
if (value === undefined)
|
|
60907
|
+
return;
|
|
60908
|
+
const normalized = Math.floor(value);
|
|
60909
|
+
if (!Number.isFinite(normalized) || normalized < 1) {
|
|
60910
|
+
throw new Error(`${label} must be a positive integer`);
|
|
60911
|
+
}
|
|
60912
|
+
return normalized;
|
|
60913
|
+
}
|
|
60914
|
+
function normalizeOptionalNonNegativeInteger(value, label) {
|
|
60915
|
+
if (value === undefined)
|
|
60916
|
+
return;
|
|
60917
|
+
const normalized = Math.floor(value);
|
|
60918
|
+
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
60919
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
60920
|
+
}
|
|
60921
|
+
return normalized;
|
|
60922
|
+
}
|
|
60850
60923
|
function collectMissingSandboxEnvRefs(workflows, env, credentialResolver) {
|
|
60851
60924
|
const requiredMissing = [];
|
|
60852
60925
|
const optionalMissing = [];
|
|
@@ -95551,7 +95624,7 @@ import chalk6 from "chalk";
|
|
|
95551
95624
|
// package.json
|
|
95552
95625
|
var package_default = {
|
|
95553
95626
|
name: "@hasna/testers",
|
|
95554
|
-
version: "0.0.
|
|
95627
|
+
version: "0.0.57",
|
|
95555
95628
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
95556
95629
|
type: "module",
|
|
95557
95630
|
main: "dist/index.js",
|
|
@@ -101176,7 +101249,7 @@ Imported ${imported} scenarios from API spec:`));
|
|
|
101176
101249
|
}
|
|
101177
101250
|
});
|
|
101178
101251
|
var inventoryCmd = program2.command("inventory").description("Discover source-derived app route/action inventories");
|
|
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
|
|
101252
|
+
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, area-kind, or action", "route").option("--workflow-target <target>", "Workflow execution target: local or sandbox", "sandbox").option("--sandbox-provider <provider>", "Sandbox provider for created workflows", "e2b").option("--sandbox-image <image>", "Sandbox image/template for created workflows").option("--sandbox-remote-dir <path>", "Remote working directory for sandbox runs").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-setup-command <command>", "Shell command to run before testers in the sandbox").option("--sandbox-package <spec>", "Package spec to execute in the sandbox", "@hasna/testers").option("--sandbox-env <assignment>", "Sandbox env var; KEY forwards host KEY, KEY=value stores value (repeatable)", (val, acc) => {
|
|
101180
101253
|
acc.push(val);
|
|
101181
101254
|
return acc;
|
|
101182
101255
|
}, []).option("--sandbox-env-optional <name>", "Optional sandbox env var; forwards host NAME only when set (repeatable)", (val, acc) => {
|
|
@@ -101187,8 +101260,8 @@ inventoryCmd.command("next [root]").description("Discover Next.js app routes and
|
|
|
101187
101260
|
const { importNextRouteInventory: importNextRouteInventory2 } = await Promise.resolve().then(() => (init_next_route_inventory(), exports_next_route_inventory));
|
|
101188
101261
|
const projectId = resolveProject2(opts.project) ?? undefined;
|
|
101189
101262
|
const env = parseSandboxEnv(opts.sandboxEnv, opts.sandboxEnvOptional);
|
|
101190
|
-
if (!["route", "area-kind"].includes(opts.actionWorkflowGrouping)) {
|
|
101191
|
-
throw new Error("--action-workflow-grouping must be route
|
|
101263
|
+
if (!["route", "area-kind", "action"].includes(opts.actionWorkflowGrouping)) {
|
|
101264
|
+
throw new Error("--action-workflow-grouping must be route, area-kind, or action");
|
|
101192
101265
|
}
|
|
101193
101266
|
const result = importNextRouteInventory2({
|
|
101194
101267
|
rootDir: root ?? process.cwd(),
|
|
@@ -102336,7 +102409,7 @@ workflowCmd.command("run <id>").description("Run a saved testing workflow").requ
|
|
|
102336
102409
|
workflowCmd.command("fanout [ids...]").description("Run multiple saved sandbox workflows concurrently").requiredOption("-u, --url <url>", "Target URL").option("--project <id>", "Project ID").option("--tag <tag>", "Workflow scenario tag filter (repeatable)", (val, acc) => {
|
|
102337
102410
|
acc.push(val);
|
|
102338
102411
|
return acc;
|
|
102339
|
-
}, []).option("--all", "Include disabled workflows when selecting by project/tag", false).option("--workers <n>", "Concurrent sandboxes, 1-12 (default: 6)", "6").option("-m, --model <model>", "AI model").option("--headed", "Run headed", false).option("--parallel <n>", "Parallel browser workers inside each sandbox").option("--timeout <ms>", "Override workflow timeout").option("--dry-run", "Print resolved sandbox plans without spawning sandboxes", false).option("--json", "Output as JSON", false).action(async (ids, opts) => {
|
|
102412
|
+
}, []).option("--all", "Include disabled workflows when selecting by project/tag", false).option("--workers <n>", "Concurrent sandboxes, 1-12 (default: 6)", "6").option("--batch-size <n>", "Limit this run to a batch of selected workflows").option("--batch <n>", "1-based batch number to run with --batch-size").option("--offset <n>", "0-based selected-workflow offset for staged fanout").option("-m, --model <model>", "AI model").option("--headed", "Run headed", false).option("--parallel <n>", "Parallel browser workers inside each sandbox").option("--timeout <ms>", "Override workflow timeout").option("--dry-run", "Print resolved sandbox plans without spawning sandboxes", false).option("--json", "Output as JSON", false).action(async (ids, opts) => {
|
|
102340
102413
|
try {
|
|
102341
102414
|
const { runWorkflowFanout: runWorkflowFanout2 } = await Promise.resolve().then(() => (init_workflow_fanout(), exports_workflow_fanout));
|
|
102342
102415
|
const result = await runWorkflowFanout2({
|
|
@@ -102345,6 +102418,9 @@ workflowCmd.command("fanout [ids...]").description("Run multiple saved sandbox w
|
|
|
102345
102418
|
tags: opts.tag,
|
|
102346
102419
|
includeDisabled: opts.all,
|
|
102347
102420
|
workers: opts.workers ? parseInt(opts.workers, 10) : undefined,
|
|
102421
|
+
batchSize: opts.batchSize ? parseInt(opts.batchSize, 10) : undefined,
|
|
102422
|
+
batch: opts.batch ? parseInt(opts.batch, 10) : undefined,
|
|
102423
|
+
offset: opts.offset ? parseInt(opts.offset, 10) : undefined,
|
|
102348
102424
|
url: opts.url,
|
|
102349
102425
|
model: opts.model,
|
|
102350
102426
|
headed: opts.headed,
|
|
@@ -102368,6 +102444,10 @@ workflowCmd.command("fanout [ids...]").description("Run multiple saved sandbox w
|
|
|
102368
102444
|
log(` ${chalk6.yellow("warning")} ${check2.message}`);
|
|
102369
102445
|
}
|
|
102370
102446
|
}
|
|
102447
|
+
if (result.selection.matched !== result.total) {
|
|
102448
|
+
const batch = result.selection.batch !== undefined && result.selection.totalBatches !== undefined ? ` batch ${result.selection.batch}/${result.selection.totalBatches}` : "";
|
|
102449
|
+
log(chalk6.dim(`Selected ${result.total}/${result.selection.matched} workflow(s) from offset ${result.selection.offset}${batch}.`));
|
|
102450
|
+
}
|
|
102371
102451
|
const status = result.status === "passed" ? chalk6.green("passed") : chalk6.red("failed");
|
|
102372
102452
|
log(chalk6.bold(`Sandbox workflow fanout ${status}: ${result.passed}/${result.total} passed with ${result.workers} worker(s)`));
|
|
102373
102453
|
for (const item of result.items) {
|
|
@@ -45,7 +45,7 @@ export interface ImportNextRouteInventoryOptions {
|
|
|
45
45
|
createActionScenarios?: boolean;
|
|
46
46
|
createWorkflows?: boolean;
|
|
47
47
|
createActionWorkflows?: boolean;
|
|
48
|
-
actionWorkflowGrouping?: "route" | "area-kind";
|
|
48
|
+
actionWorkflowGrouping?: "route" | "area-kind" | "action";
|
|
49
49
|
workflowTarget?: "local" | "sandbox";
|
|
50
50
|
workflowProvider?: string;
|
|
51
51
|
workflowExecution?: Partial<WorkflowExecutionInput>;
|
|
@@ -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,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,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sBAAsB,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,9 @@ export interface WorkflowFanoutOptions extends WorkflowRunOptions {
|
|
|
6
6
|
tags?: string[];
|
|
7
7
|
includeDisabled?: boolean;
|
|
8
8
|
workers?: number;
|
|
9
|
+
batchSize?: number;
|
|
10
|
+
batch?: number;
|
|
11
|
+
offset?: number;
|
|
9
12
|
}
|
|
10
13
|
export interface WorkflowFanoutItem {
|
|
11
14
|
workflowId: string;
|
|
@@ -22,6 +25,7 @@ export interface WorkflowFanoutItem {
|
|
|
22
25
|
export interface WorkflowFanoutResult {
|
|
23
26
|
status: "passed" | "failed" | "dry-run";
|
|
24
27
|
workers: number;
|
|
28
|
+
selection: WorkflowFanoutSelection;
|
|
25
29
|
total: number;
|
|
26
30
|
passed: number;
|
|
27
31
|
failed: number;
|
|
@@ -48,6 +52,14 @@ export interface WorkflowFanoutPreflightResult {
|
|
|
48
52
|
ok: boolean;
|
|
49
53
|
checks: WorkflowFanoutPreflightCheck[];
|
|
50
54
|
}
|
|
55
|
+
export interface WorkflowFanoutSelection {
|
|
56
|
+
matched: number;
|
|
57
|
+
offset: number;
|
|
58
|
+
limit?: number;
|
|
59
|
+
batch?: number;
|
|
60
|
+
batchSize?: number;
|
|
61
|
+
totalBatches?: number;
|
|
62
|
+
}
|
|
51
63
|
interface WorkflowFanoutPreflightDependencies {
|
|
52
64
|
providerApiKeyResolver?: WorkflowFanoutDependencies["providerApiKeyResolver"];
|
|
53
65
|
commandExists?: WorkflowFanoutDependencies["commandExists"];
|
|
@@ -55,6 +67,10 @@ interface WorkflowFanoutPreflightDependencies {
|
|
|
55
67
|
env?: Record<string, string | undefined>;
|
|
56
68
|
}
|
|
57
69
|
export declare function normalizeFanoutWorkerCount(value: number | undefined): number;
|
|
70
|
+
export declare function resolveWorkflowFanoutBatch(workflows: TestingWorkflow[], options?: Pick<WorkflowFanoutOptions, "batchSize" | "batch" | "offset">): {
|
|
71
|
+
workflows: TestingWorkflow[];
|
|
72
|
+
selection: WorkflowFanoutSelection;
|
|
73
|
+
};
|
|
58
74
|
export declare function resolveWorkflowFanoutSelection(options: Pick<WorkflowFanoutOptions, "workflowIds" | "projectId" | "tags" | "includeDisabled">): TestingWorkflow[];
|
|
59
75
|
export declare function checkWorkflowFanoutReadiness(workflows: TestingWorkflow[], dependencies?: WorkflowFanoutPreflightDependencies): Promise<WorkflowFanoutPreflightResult>;
|
|
60
76
|
export declare function runWorkflowFanout(options: WorkflowFanoutOptions, dependencies?: WorkflowFanoutDependencies): Promise<WorkflowFanoutResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-fanout.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-fanout.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,KAAK,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAEpH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-fanout.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-fanout.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.
|
|
55
|
+
version: "0.0.57",
|
|
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",
|
package/dist/server/index.js
CHANGED
|
@@ -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.
|
|
47093
|
+
version: "0.0.57",
|
|
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",
|