@hasna/testers 0.0.48 → 0.0.49
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/cli/index.js
CHANGED
|
@@ -59150,6 +59150,297 @@ var init_openapi_import = __esm(() => {
|
|
|
59150
59150
|
init_api_checks();
|
|
59151
59151
|
});
|
|
59152
59152
|
|
|
59153
|
+
// src/lib/next-route-inventory.ts
|
|
59154
|
+
var exports_next_route_inventory = {};
|
|
59155
|
+
__export(exports_next_route_inventory, {
|
|
59156
|
+
scenarioInputForNextRoute: () => scenarioInputForNextRoute,
|
|
59157
|
+
importNextRouteInventory: () => importNextRouteInventory,
|
|
59158
|
+
discoverNextRouteInventory: () => discoverNextRouteInventory
|
|
59159
|
+
});
|
|
59160
|
+
import { existsSync as existsSync18, readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync4 } from "fs";
|
|
59161
|
+
import { basename as basename3, join as join20, relative as relative4, resolve as resolve3 } from "path";
|
|
59162
|
+
function discoverNextRouteInventory(options) {
|
|
59163
|
+
const rootDir = resolve3(options.rootDir);
|
|
59164
|
+
const appDir = resolveAppDir(rootDir, options.appDir);
|
|
59165
|
+
const includePages = options.includePages !== false;
|
|
59166
|
+
const includeApi = options.includeApi !== false;
|
|
59167
|
+
const files = walkRouteFiles(appDir);
|
|
59168
|
+
const items = files.map((file) => routeItemFromFile(rootDir, appDir, file)).filter((item) => Boolean(item)).filter((item) => item.kind === "page" ? includePages : includeApi).sort((a2, b2) => `${a2.kind}:${a2.routePath}:${a2.file}`.localeCompare(`${b2.kind}:${b2.routePath}:${b2.file}`)).slice(0, options.limit);
|
|
59169
|
+
const categories = {};
|
|
59170
|
+
for (const item of items) {
|
|
59171
|
+
categories[item.category] = (categories[item.category] ?? 0) + 1;
|
|
59172
|
+
}
|
|
59173
|
+
return {
|
|
59174
|
+
rootDir,
|
|
59175
|
+
appDir,
|
|
59176
|
+
total: items.length,
|
|
59177
|
+
pages: items.filter((item) => item.kind === "page").length,
|
|
59178
|
+
apiRoutes: items.filter((item) => item.kind === "api").length,
|
|
59179
|
+
dynamic: items.filter((item) => item.dynamic).length,
|
|
59180
|
+
categories,
|
|
59181
|
+
items
|
|
59182
|
+
};
|
|
59183
|
+
}
|
|
59184
|
+
function scenarioInputForNextRoute(item, projectId) {
|
|
59185
|
+
const label = item.kind === "page" ? "page" : "API route";
|
|
59186
|
+
const methodList = item.methods.length > 0 ? item.methods.join(", ") : "discovered methods";
|
|
59187
|
+
const dynamicStep = item.dynamic ? "Substitute dynamic path parameters with valid fixture values from the target org before opening or calling the route." : undefined;
|
|
59188
|
+
const pageSteps = [
|
|
59189
|
+
dynamicStep,
|
|
59190
|
+
`Open the Next.js ${label} ${item.routePath}.`,
|
|
59191
|
+
"Wait for the route to finish loading and verify it does not show a blank shell, framework error page, or unexpected auth loop.",
|
|
59192
|
+
"Exercise visible primary navigation, tabs, filters, dialogs, forms, and safe buttons on this route.",
|
|
59193
|
+
"Verify the route stays within the expected org/workspace context and does not emit console errors."
|
|
59194
|
+
].filter(Boolean);
|
|
59195
|
+
const apiSteps = [
|
|
59196
|
+
dynamicStep,
|
|
59197
|
+
`Call the ${methodList} handler(s) for ${item.routePath} using safe fixture data.`,
|
|
59198
|
+
"Verify expected authentication, authorization, validation, and tenant isolation behavior.",
|
|
59199
|
+
"For mutating methods, use harmless test payloads and confirm the response does not create cross-org side effects.",
|
|
59200
|
+
"Verify response status, JSON shape, and error messages are stable and regression-safe."
|
|
59201
|
+
].filter(Boolean);
|
|
59202
|
+
return {
|
|
59203
|
+
name: `Next ${label}: ${item.routePath}`,
|
|
59204
|
+
description: `Source-discovered ${label} from ${item.file}. Verify route behavior and regressions for ${item.category}.`,
|
|
59205
|
+
steps: item.kind === "page" ? pageSteps : apiSteps,
|
|
59206
|
+
tags: item.tags,
|
|
59207
|
+
priority: item.priority,
|
|
59208
|
+
targetPath: item.routePath,
|
|
59209
|
+
requiresAuth: item.requiresAuth,
|
|
59210
|
+
assertions: item.kind === "page" ? SAFE_PAGE_ASSERTIONS : [],
|
|
59211
|
+
metadata: {
|
|
59212
|
+
source: "next-route-inventory",
|
|
59213
|
+
routeFile: item.file,
|
|
59214
|
+
routeKind: item.kind,
|
|
59215
|
+
category: item.category,
|
|
59216
|
+
methods: item.methods,
|
|
59217
|
+
dynamic: item.dynamic,
|
|
59218
|
+
groups: item.groups
|
|
59219
|
+
},
|
|
59220
|
+
projectId
|
|
59221
|
+
};
|
|
59222
|
+
}
|
|
59223
|
+
function importNextRouteInventory(options) {
|
|
59224
|
+
const inventory = discoverNextRouteInventory(options);
|
|
59225
|
+
let created = 0;
|
|
59226
|
+
let updated = 0;
|
|
59227
|
+
let deduped = 0;
|
|
59228
|
+
const scenarios = [];
|
|
59229
|
+
const workflows = [];
|
|
59230
|
+
if (options.createScenarios) {
|
|
59231
|
+
for (const item of inventory.items) {
|
|
59232
|
+
const result = upsertScenario(scenarioInputForNextRoute(item, options.projectId));
|
|
59233
|
+
scenarios.push(result.scenario);
|
|
59234
|
+
if (result.action === "created")
|
|
59235
|
+
created++;
|
|
59236
|
+
else if (result.action === "updated")
|
|
59237
|
+
updated++;
|
|
59238
|
+
else
|
|
59239
|
+
deduped++;
|
|
59240
|
+
}
|
|
59241
|
+
}
|
|
59242
|
+
if (options.createWorkflows) {
|
|
59243
|
+
workflows.push(...upsertRouteInventoryWorkflows(inventory, options));
|
|
59244
|
+
}
|
|
59245
|
+
return { inventory, created, updated, deduped, scenarios, workflows };
|
|
59246
|
+
}
|
|
59247
|
+
function upsertRouteInventoryWorkflows(inventory, options) {
|
|
59248
|
+
const workflows = [];
|
|
59249
|
+
const categories = Object.keys(inventory.categories).sort();
|
|
59250
|
+
for (const category of categories) {
|
|
59251
|
+
const kinds = new Set(inventory.items.filter((item) => item.category === category).map((item) => item.kind));
|
|
59252
|
+
for (const kind of kinds) {
|
|
59253
|
+
const name = `Next route inventory ${category} ${kind}`;
|
|
59254
|
+
const scenarioTags = ["next-route", `area:${category}`, `route:${kind}`];
|
|
59255
|
+
const execution = {
|
|
59256
|
+
target: options.workflowTarget ?? "sandbox",
|
|
59257
|
+
provider: options.workflowProvider,
|
|
59258
|
+
sandboxCleanup: "delete",
|
|
59259
|
+
sandboxSyncStrategy: "rsync",
|
|
59260
|
+
...options.workflowExecution
|
|
59261
|
+
};
|
|
59262
|
+
const existing = listTestingWorkflows({ projectId: options.projectId, enabled: undefined }).find((workflow) => workflow.name === name);
|
|
59263
|
+
const input = {
|
|
59264
|
+
name,
|
|
59265
|
+
description: `Source-discovered Next.js ${kind} coverage for ${category} routes.`,
|
|
59266
|
+
projectId: options.projectId,
|
|
59267
|
+
scenarioFilter: { tags: scenarioTags },
|
|
59268
|
+
execution
|
|
59269
|
+
};
|
|
59270
|
+
workflows.push(existing ? updateTestingWorkflow(existing.id, input) : createTestingWorkflow(input));
|
|
59271
|
+
}
|
|
59272
|
+
}
|
|
59273
|
+
return workflows;
|
|
59274
|
+
}
|
|
59275
|
+
function resolveAppDir(rootDir, appDir) {
|
|
59276
|
+
const candidates = appDir ? [resolve3(rootDir, appDir)] : [
|
|
59277
|
+
join20(rootDir, "packages", "web", "app"),
|
|
59278
|
+
join20(rootDir, "app"),
|
|
59279
|
+
rootDir
|
|
59280
|
+
];
|
|
59281
|
+
for (const candidate of candidates) {
|
|
59282
|
+
if (existsSync18(candidate) && statSync4(candidate).isDirectory())
|
|
59283
|
+
return candidate;
|
|
59284
|
+
}
|
|
59285
|
+
throw new Error(`Next.js app directory not found under ${rootDir}`);
|
|
59286
|
+
}
|
|
59287
|
+
function walkRouteFiles(appDir) {
|
|
59288
|
+
const files = [];
|
|
59289
|
+
function walk(dir) {
|
|
59290
|
+
for (const entry of readdirSync6(dir)) {
|
|
59291
|
+
if (WALK_EXCLUDES.has(entry))
|
|
59292
|
+
continue;
|
|
59293
|
+
const fullPath = join20(dir, entry);
|
|
59294
|
+
const stat = statSync4(fullPath);
|
|
59295
|
+
if (stat.isDirectory()) {
|
|
59296
|
+
walk(fullPath);
|
|
59297
|
+
} else if (ROUTE_FILE_NAMES.has(entry)) {
|
|
59298
|
+
files.push(fullPath);
|
|
59299
|
+
}
|
|
59300
|
+
}
|
|
59301
|
+
}
|
|
59302
|
+
walk(appDir);
|
|
59303
|
+
return files;
|
|
59304
|
+
}
|
|
59305
|
+
function routeItemFromFile(rootDir, appDir, file) {
|
|
59306
|
+
const fileName = basename3(file);
|
|
59307
|
+
const kind = fileName.startsWith("page.") ? "page" : "api";
|
|
59308
|
+
const relativeFile = relative4(rootDir, file);
|
|
59309
|
+
const appRelative = relative4(appDir, file).split(/[\\/]/);
|
|
59310
|
+
const routeSegments = appRelative.slice(0, -1);
|
|
59311
|
+
const groups = routeSegments.filter((segment) => segment.startsWith("(") && segment.endsWith(")")).map((segment) => segment.slice(1, -1));
|
|
59312
|
+
const pathSegments = routeSegments.filter((segment) => !segment.startsWith("(")).filter((segment) => !segment.startsWith("@")).map(normalizeRouteSegment).filter(Boolean);
|
|
59313
|
+
const routePath = `/${pathSegments.join("/")}`.replace(/\/+/g, "/");
|
|
59314
|
+
const normalizedRoutePath = routePath === "/" ? "/" : routePath.replace(/\/$/, "");
|
|
59315
|
+
const methods = kind === "api" ? extractRouteMethods(file) : [];
|
|
59316
|
+
const category = classifyRoute(normalizedRoutePath, groups, relativeFile);
|
|
59317
|
+
const dynamic = routeSegments.some((segment) => segment.includes("["));
|
|
59318
|
+
const requiresAuth = inferRequiresAuth(normalizedRoutePath, groups, kind);
|
|
59319
|
+
return {
|
|
59320
|
+
kind,
|
|
59321
|
+
routePath: normalizedRoutePath,
|
|
59322
|
+
file: relativeFile,
|
|
59323
|
+
category,
|
|
59324
|
+
groups,
|
|
59325
|
+
methods,
|
|
59326
|
+
dynamic,
|
|
59327
|
+
requiresAuth,
|
|
59328
|
+
tags: tagsForRoute({ kind, routePath: normalizedRoutePath, category, groups, dynamic, requiresAuth }),
|
|
59329
|
+
priority: priorityForRoute(normalizedRoutePath, category, kind)
|
|
59330
|
+
};
|
|
59331
|
+
}
|
|
59332
|
+
function normalizeRouteSegment(segment) {
|
|
59333
|
+
if (segment.startsWith("[[...") && segment.endsWith("]]")) {
|
|
59334
|
+
return `:${segment.slice(5, -2)}*?`;
|
|
59335
|
+
}
|
|
59336
|
+
if (segment.startsWith("[...") && segment.endsWith("]")) {
|
|
59337
|
+
return `:${segment.slice(4, -1)}*`;
|
|
59338
|
+
}
|
|
59339
|
+
if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
59340
|
+
return `:${segment.slice(1, -1)}`;
|
|
59341
|
+
}
|
|
59342
|
+
return segment;
|
|
59343
|
+
}
|
|
59344
|
+
function extractRouteMethods(file) {
|
|
59345
|
+
const source = readFileSync9(file, "utf8");
|
|
59346
|
+
const methods = new Set;
|
|
59347
|
+
const pattern = /\b(?:export\s+)?(?:async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b|\bexport\s+const\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\b/g;
|
|
59348
|
+
for (const match of source.matchAll(pattern)) {
|
|
59349
|
+
const method = match[1] ?? match[2];
|
|
59350
|
+
if (method)
|
|
59351
|
+
methods.add(method);
|
|
59352
|
+
}
|
|
59353
|
+
return [...methods].sort();
|
|
59354
|
+
}
|
|
59355
|
+
function classifyRoute(routePath, groups, file) {
|
|
59356
|
+
const haystack = `${routePath} ${groups.join(" ")} ${file}`.toLowerCase();
|
|
59357
|
+
if (haystack.includes("admin"))
|
|
59358
|
+
return "admin";
|
|
59359
|
+
if (haystack.includes("auth") || routePath.startsWith("/cli/device"))
|
|
59360
|
+
return "auth";
|
|
59361
|
+
if (haystack.includes("ai-runtime") || /\/(chat|sessions|memories|knowledge|learning|copilot|guardrails)\b/.test(routePath))
|
|
59362
|
+
return "ai-runtime";
|
|
59363
|
+
if (haystack.includes("commerce") || /\/(billing|shop|agent-wallet|domains|whois-profiles)\b/.test(routePath))
|
|
59364
|
+
return "commerce";
|
|
59365
|
+
if (haystack.includes("communications") || /\/(telephony|emails)\b/.test(routePath))
|
|
59366
|
+
return "communications";
|
|
59367
|
+
if (haystack.includes("crm") || routePath.includes("/contacts"))
|
|
59368
|
+
return "crm";
|
|
59369
|
+
if (haystack.includes("integrations") || routePath.includes("/connectors"))
|
|
59370
|
+
return "integrations";
|
|
59371
|
+
if (haystack.includes("dashboard") || routePath.includes(":orgSlug"))
|
|
59372
|
+
return "dashboard";
|
|
59373
|
+
if (haystack.includes("public") || haystack.includes("pages"))
|
|
59374
|
+
return "public";
|
|
59375
|
+
if (routePath.startsWith("/api/"))
|
|
59376
|
+
return "api";
|
|
59377
|
+
return "app";
|
|
59378
|
+
}
|
|
59379
|
+
function inferRequiresAuth(routePath, groups, kind) {
|
|
59380
|
+
const haystack = `${routePath} ${groups.join(" ")}`.toLowerCase();
|
|
59381
|
+
if (haystack.includes("auth") || haystack.includes("public") || haystack.includes("webhook"))
|
|
59382
|
+
return false;
|
|
59383
|
+
if (routePath.startsWith("/api/v1/auth/"))
|
|
59384
|
+
return false;
|
|
59385
|
+
if (routePath.startsWith("/api/"))
|
|
59386
|
+
return true;
|
|
59387
|
+
return kind === "page" && (haystack.includes("admin") || haystack.includes("dashboard") || routePath.includes(":orgSlug") || routePath.startsWith("/settings"));
|
|
59388
|
+
}
|
|
59389
|
+
function tagsForRoute(input) {
|
|
59390
|
+
const tags = new Set([
|
|
59391
|
+
"next-route",
|
|
59392
|
+
`route:${input.kind}`,
|
|
59393
|
+
`area:${input.category}`,
|
|
59394
|
+
input.category
|
|
59395
|
+
]);
|
|
59396
|
+
for (const group of input.groups)
|
|
59397
|
+
tags.add(`group:${group}`);
|
|
59398
|
+
if (input.dynamic)
|
|
59399
|
+
tags.add("dynamic-route");
|
|
59400
|
+
if (input.requiresAuth)
|
|
59401
|
+
tags.add("auth-required");
|
|
59402
|
+
if (input.routePath.startsWith("/api/"))
|
|
59403
|
+
tags.add("api");
|
|
59404
|
+
return [...tags];
|
|
59405
|
+
}
|
|
59406
|
+
function priorityForRoute(routePath, category, kind) {
|
|
59407
|
+
if (category === "auth")
|
|
59408
|
+
return "critical";
|
|
59409
|
+
if (category === "commerce" || category === "ai-runtime")
|
|
59410
|
+
return "critical";
|
|
59411
|
+
if (category === "admin" || category === "dashboard")
|
|
59412
|
+
return "high";
|
|
59413
|
+
if (kind === "api")
|
|
59414
|
+
return "high";
|
|
59415
|
+
if (routePath === "/" || category === "public")
|
|
59416
|
+
return "medium";
|
|
59417
|
+
return "medium";
|
|
59418
|
+
}
|
|
59419
|
+
var ROUTE_FILE_NAMES, WALK_EXCLUDES, SAFE_PAGE_ASSERTIONS;
|
|
59420
|
+
var init_next_route_inventory = __esm(() => {
|
|
59421
|
+
init_scenarios();
|
|
59422
|
+
init_workflows();
|
|
59423
|
+
ROUTE_FILE_NAMES = new Set([
|
|
59424
|
+
"page.tsx",
|
|
59425
|
+
"page.ts",
|
|
59426
|
+
"page.jsx",
|
|
59427
|
+
"page.js",
|
|
59428
|
+
"page.mdx",
|
|
59429
|
+
"route.ts",
|
|
59430
|
+
"route.js"
|
|
59431
|
+
]);
|
|
59432
|
+
WALK_EXCLUDES = new Set([
|
|
59433
|
+
".git",
|
|
59434
|
+
".next",
|
|
59435
|
+
".turbo",
|
|
59436
|
+
"node_modules",
|
|
59437
|
+
"dist",
|
|
59438
|
+
"build",
|
|
59439
|
+
"coverage"
|
|
59440
|
+
]);
|
|
59441
|
+
SAFE_PAGE_ASSERTIONS = [{ type: "no_console_errors" }];
|
|
59442
|
+
});
|
|
59443
|
+
|
|
59153
59444
|
// src/lib/generator.ts
|
|
59154
59445
|
var exports_generator = {};
|
|
59155
59446
|
__export(exports_generator, {
|
|
@@ -59448,7 +59739,7 @@ async function recordSession(url, options) {
|
|
|
59448
59739
|
await Promise.race([
|
|
59449
59740
|
page.waitForEvent("close").catch(() => {}),
|
|
59450
59741
|
context.waitForEvent("close").catch(() => {}),
|
|
59451
|
-
new Promise((
|
|
59742
|
+
new Promise((resolve4) => setTimeout(resolve4, timeout))
|
|
59452
59743
|
]);
|
|
59453
59744
|
clearInterval(pollInterval);
|
|
59454
59745
|
try {
|
|
@@ -77016,7 +77307,7 @@ function createProviderToolFactoryWithOutputSchema({
|
|
|
77016
77307
|
supportsDeferredResults
|
|
77017
77308
|
});
|
|
77018
77309
|
}
|
|
77019
|
-
async function
|
|
77310
|
+
async function resolve4(value) {
|
|
77020
77311
|
if (typeof value === "function") {
|
|
77021
77312
|
value = value();
|
|
77022
77313
|
}
|
|
@@ -78703,7 +78994,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78703
78994
|
try {
|
|
78704
78995
|
const { value } = await getFromApi({
|
|
78705
78996
|
url: `${this.config.baseURL}/config`,
|
|
78706
|
-
headers: await
|
|
78997
|
+
headers: await resolve4(this.config.headers()),
|
|
78707
78998
|
successfulResponseHandler: createJsonResponseHandler(gatewayAvailableModelsResponseSchema),
|
|
78708
78999
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
78709
79000
|
errorSchema: exports_external2.any(),
|
|
@@ -78721,7 +79012,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78721
79012
|
const baseUrl = new URL(this.config.baseURL);
|
|
78722
79013
|
const { value } = await getFromApi({
|
|
78723
79014
|
url: `${baseUrl.origin}/v1/credits`,
|
|
78724
|
-
headers: await
|
|
79015
|
+
headers: await resolve4(this.config.headers()),
|
|
78725
79016
|
successfulResponseHandler: createJsonResponseHandler(gatewayCreditsResponseSchema),
|
|
78726
79017
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
78727
79018
|
errorSchema: exports_external2.any(),
|
|
@@ -78767,7 +79058,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78767
79058
|
}
|
|
78768
79059
|
const { value } = await getFromApi({
|
|
78769
79060
|
url: `${baseUrl.origin}/v1/report?${searchParams.toString()}`,
|
|
78770
|
-
headers: await
|
|
79061
|
+
headers: await resolve4(this.config.headers()),
|
|
78771
79062
|
successfulResponseHandler: createJsonResponseHandler(gatewaySpendReportResponseSchema),
|
|
78772
79063
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
78773
79064
|
errorSchema: exports_external2.any(),
|
|
@@ -78789,7 +79080,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78789
79080
|
const baseUrl = new URL(this.config.baseURL);
|
|
78790
79081
|
const { value } = await getFromApi({
|
|
78791
79082
|
url: `${baseUrl.origin}/v1/generation?id=${encodeURIComponent(params.id)}`,
|
|
78792
|
-
headers: await
|
|
79083
|
+
headers: await resolve4(this.config.headers()),
|
|
78793
79084
|
successfulResponseHandler: createJsonResponseHandler(gatewayGenerationInfoResponseSchema),
|
|
78794
79085
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
78795
79086
|
errorSchema: exports_external2.any(),
|
|
@@ -78822,7 +79113,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78822
79113
|
async doGenerate(options) {
|
|
78823
79114
|
const { args, warnings } = await this.getArgs(options);
|
|
78824
79115
|
const { abortSignal } = options;
|
|
78825
|
-
const resolvedHeaders = await
|
|
79116
|
+
const resolvedHeaders = await resolve4(this.config.headers());
|
|
78826
79117
|
try {
|
|
78827
79118
|
const {
|
|
78828
79119
|
responseHeaders,
|
|
@@ -78830,7 +79121,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78830
79121
|
rawValue: rawResponse
|
|
78831
79122
|
} = await postJsonToApi({
|
|
78832
79123
|
url: this.getUrl(),
|
|
78833
|
-
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await
|
|
79124
|
+
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, false), await resolve4(this.config.o11yHeaders)),
|
|
78834
79125
|
body: args,
|
|
78835
79126
|
successfulResponseHandler: createJsonResponseHandler(exports_external2.any()),
|
|
78836
79127
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
@@ -78853,11 +79144,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78853
79144
|
async doStream(options) {
|
|
78854
79145
|
const { args, warnings } = await this.getArgs(options);
|
|
78855
79146
|
const { abortSignal } = options;
|
|
78856
|
-
const resolvedHeaders = await
|
|
79147
|
+
const resolvedHeaders = await resolve4(this.config.headers());
|
|
78857
79148
|
try {
|
|
78858
79149
|
const { value: response, responseHeaders } = await postJsonToApi({
|
|
78859
79150
|
url: this.getUrl(),
|
|
78860
|
-
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await
|
|
79151
|
+
headers: combineHeaders(resolvedHeaders, options.headers, this.getModelConfigHeaders(this.modelId, true), await resolve4(this.config.o11yHeaders)),
|
|
78861
79152
|
body: args,
|
|
78862
79153
|
successfulResponseHandler: createEventSourceResponseHandler(exports_external2.any()),
|
|
78863
79154
|
failedResponseHandler: createJsonErrorResponseHandler({
|
|
@@ -78942,7 +79233,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78942
79233
|
providerOptions
|
|
78943
79234
|
}) {
|
|
78944
79235
|
var _a92;
|
|
78945
|
-
const resolvedHeaders = await
|
|
79236
|
+
const resolvedHeaders = await resolve4(this.config.headers());
|
|
78946
79237
|
try {
|
|
78947
79238
|
const {
|
|
78948
79239
|
responseHeaders,
|
|
@@ -78950,7 +79241,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
78950
79241
|
rawValue
|
|
78951
79242
|
} = await postJsonToApi({
|
|
78952
79243
|
url: this.getUrl(),
|
|
78953
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
79244
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
|
|
78954
79245
|
body: {
|
|
78955
79246
|
values,
|
|
78956
79247
|
...providerOptions ? { providerOptions } : {}
|
|
@@ -79006,7 +79297,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
79006
79297
|
abortSignal
|
|
79007
79298
|
}) {
|
|
79008
79299
|
var _a92, _b92, _c2, _d2;
|
|
79009
|
-
const resolvedHeaders = await
|
|
79300
|
+
const resolvedHeaders = await resolve4(this.config.headers());
|
|
79010
79301
|
try {
|
|
79011
79302
|
const {
|
|
79012
79303
|
responseHeaders,
|
|
@@ -79014,7 +79305,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
79014
79305
|
rawValue
|
|
79015
79306
|
} = await postJsonToApi({
|
|
79016
79307
|
url: this.getUrl(),
|
|
79017
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
79308
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
|
|
79018
79309
|
body: {
|
|
79019
79310
|
prompt,
|
|
79020
79311
|
n: n2,
|
|
@@ -79089,11 +79380,11 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
79089
79380
|
abortSignal
|
|
79090
79381
|
}) {
|
|
79091
79382
|
var _a92;
|
|
79092
|
-
const resolvedHeaders = await
|
|
79383
|
+
const resolvedHeaders = await resolve4(this.config.headers());
|
|
79093
79384
|
try {
|
|
79094
79385
|
const { responseHeaders, value: responseBody } = await postJsonToApi({
|
|
79095
79386
|
url: this.getUrl(),
|
|
79096
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
79387
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders), { accept: "text/event-stream" }),
|
|
79097
79388
|
body: {
|
|
79098
79389
|
prompt,
|
|
79099
79390
|
n: n2,
|
|
@@ -79216,7 +79507,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
79216
79507
|
abortSignal,
|
|
79217
79508
|
providerOptions
|
|
79218
79509
|
}) {
|
|
79219
|
-
const resolvedHeaders = await
|
|
79510
|
+
const resolvedHeaders = await resolve4(this.config.headers());
|
|
79220
79511
|
try {
|
|
79221
79512
|
const {
|
|
79222
79513
|
responseHeaders,
|
|
@@ -79224,7 +79515,7 @@ var import_oidc, import_oidc2, marker17 = "vercel.ai.gateway.error", symbol18, _
|
|
|
79224
79515
|
rawValue
|
|
79225
79516
|
} = await postJsonToApi({
|
|
79226
79517
|
url: this.getUrl(),
|
|
79227
|
-
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await
|
|
79518
|
+
headers: combineHeaders(resolvedHeaders, headers != null ? headers : {}, this.getModelConfigHeaders(), await resolve4(this.config.o11yHeaders)),
|
|
79228
79519
|
body: {
|
|
79229
79520
|
documents,
|
|
79230
79521
|
query,
|
|
@@ -88642,7 +88933,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
|
|
|
88642
88933
|
const schema = asSchema(inputSchema);
|
|
88643
88934
|
return {
|
|
88644
88935
|
name: "object",
|
|
88645
|
-
responseFormat:
|
|
88936
|
+
responseFormat: resolve4(schema.jsonSchema).then((jsonSchema2) => ({
|
|
88646
88937
|
type: "json",
|
|
88647
88938
|
schema: jsonSchema2,
|
|
88648
88939
|
...name21 != null && { name: name21 },
|
|
@@ -88703,7 +88994,7 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
|
|
|
88703
88994
|
const elementSchema = asSchema(inputElementSchema);
|
|
88704
88995
|
return {
|
|
88705
88996
|
name: "array",
|
|
88706
|
-
responseFormat:
|
|
88997
|
+
responseFormat: resolve4(elementSchema.jsonSchema).then((jsonSchema2) => {
|
|
88707
88998
|
const { $schema, ...itemSchema } = jsonSchema2;
|
|
88708
88999
|
return {
|
|
88709
89000
|
type: "json",
|
|
@@ -91600,9 +91891,9 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
|
|
|
91600
91891
|
...options
|
|
91601
91892
|
}) {
|
|
91602
91893
|
var _a21, _b16, _c2, _d2, _e2;
|
|
91603
|
-
const resolvedBody = await
|
|
91604
|
-
const resolvedHeaders = await
|
|
91605
|
-
const resolvedCredentials = await
|
|
91894
|
+
const resolvedBody = await resolve4(this.body);
|
|
91895
|
+
const resolvedHeaders = await resolve4(this.headers);
|
|
91896
|
+
const resolvedCredentials = await resolve4(this.credentials);
|
|
91606
91897
|
const baseHeaders = {
|
|
91607
91898
|
...normalizeHeaders(resolvedHeaders),
|
|
91608
91899
|
...normalizeHeaders(options.headers)
|
|
@@ -91650,9 +91941,9 @@ var import_api2, import_api3, __defProp4, __export4 = (target, all) => {
|
|
|
91650
91941
|
}
|
|
91651
91942
|
async reconnectToStream(options) {
|
|
91652
91943
|
var _a21, _b16, _c2, _d2, _e2;
|
|
91653
|
-
const resolvedBody = await
|
|
91654
|
-
const resolvedHeaders = await
|
|
91655
|
-
const resolvedCredentials = await
|
|
91944
|
+
const resolvedBody = await resolve4(this.body);
|
|
91945
|
+
const resolvedHeaders = await resolve4(this.headers);
|
|
91946
|
+
const resolvedCredentials = await resolve4(this.credentials);
|
|
91656
91947
|
const baseHeaders = {
|
|
91657
91948
|
...normalizeHeaders(resolvedHeaders),
|
|
91658
91949
|
...normalizeHeaders(options.headers)
|
|
@@ -93873,7 +94164,7 @@ __export(exports_session_converter, {
|
|
|
93873
94164
|
convertSessionToScenario: () => convertSessionToScenario,
|
|
93874
94165
|
convertSessionFile: () => convertSessionFile
|
|
93875
94166
|
});
|
|
93876
|
-
import { readFileSync as
|
|
94167
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
93877
94168
|
import { extname } from "path";
|
|
93878
94169
|
function parseRrwebSession(events) {
|
|
93879
94170
|
const result = [];
|
|
@@ -94058,7 +94349,7 @@ ${condensed}`;
|
|
|
94058
94349
|
};
|
|
94059
94350
|
}
|
|
94060
94351
|
async function convertSessionFile(filePath, format, options) {
|
|
94061
|
-
const raw =
|
|
94352
|
+
const raw = readFileSync10(filePath, "utf-8");
|
|
94062
94353
|
let parsed;
|
|
94063
94354
|
try {
|
|
94064
94355
|
parsed = JSON.parse(raw);
|
|
@@ -94092,7 +94383,7 @@ function detectSessionFormat(filePath) {
|
|
|
94092
94383
|
if (ext === ".har")
|
|
94093
94384
|
return "har";
|
|
94094
94385
|
try {
|
|
94095
|
-
const content =
|
|
94386
|
+
const content = readFileSync10(filePath, "utf-8").trim();
|
|
94096
94387
|
const parsed = JSON.parse(content);
|
|
94097
94388
|
if (Array.isArray(parsed) && parsed[0]?.type !== undefined && typeof parsed[0]?.timestamp === "number") {
|
|
94098
94389
|
return "rrweb";
|
|
@@ -94536,7 +94827,7 @@ import chalk6 from "chalk";
|
|
|
94536
94827
|
// package.json
|
|
94537
94828
|
var package_default = {
|
|
94538
94829
|
name: "@hasna/testers",
|
|
94539
|
-
version: "0.0.
|
|
94830
|
+
version: "0.0.49",
|
|
94540
94831
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
94541
94832
|
type: "module",
|
|
94542
94833
|
main: "dist/index.js",
|
|
@@ -94635,9 +94926,9 @@ init_todos_connector();
|
|
|
94635
94926
|
init_browser();
|
|
94636
94927
|
import { render, Box, Text, useInput, useApp } from "ink";
|
|
94637
94928
|
import React, { useState } from "react";
|
|
94638
|
-
import { readFileSync as
|
|
94929
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
94639
94930
|
import { createInterface } from "readline";
|
|
94640
|
-
import { join as
|
|
94931
|
+
import { join as join21, resolve as resolve5 } from "path";
|
|
94641
94932
|
|
|
94642
94933
|
// src/lib/init.ts
|
|
94643
94934
|
init_paths();
|
|
@@ -96723,7 +97014,7 @@ init_ci();
|
|
|
96723
97014
|
init_assertions();
|
|
96724
97015
|
init_paths();
|
|
96725
97016
|
init_sessions();
|
|
96726
|
-
import { existsSync as
|
|
97017
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
|
|
96727
97018
|
|
|
96728
97019
|
// src/lib/repo-discovery.ts
|
|
96729
97020
|
init_paths();
|
|
@@ -97742,18 +98033,18 @@ program2.command("prod-debug <target>").description("Create a safe production de
|
|
|
97742
98033
|
}, config2.prodDebug);
|
|
97743
98034
|
const output = opts.json ? JSON.stringify(plan, null, 2) : formatProdDebugPlan(plan);
|
|
97744
98035
|
if (opts.output) {
|
|
97745
|
-
writeFileSync7(
|
|
98036
|
+
writeFileSync7(resolve5(opts.output), output + `
|
|
97746
98037
|
`);
|
|
97747
98038
|
} else {
|
|
97748
98039
|
log(output);
|
|
97749
98040
|
}
|
|
97750
98041
|
});
|
|
97751
98042
|
var CONFIG_DIR5 = getTestersDir();
|
|
97752
|
-
var CONFIG_PATH4 =
|
|
98043
|
+
var CONFIG_PATH4 = join21(CONFIG_DIR5, "config.json");
|
|
97753
98044
|
function getActiveProject() {
|
|
97754
98045
|
try {
|
|
97755
|
-
if (
|
|
97756
|
-
const raw = JSON.parse(
|
|
98046
|
+
if (existsSync19(CONFIG_PATH4)) {
|
|
98047
|
+
const raw = JSON.parse(readFileSync11(CONFIG_PATH4, "utf-8"));
|
|
97757
98048
|
return raw.activeProject ?? undefined;
|
|
97758
98049
|
}
|
|
97759
98050
|
} catch {}
|
|
@@ -97960,7 +98251,7 @@ program2.command("delete <id>").description("Delete a scenario").option("-y, --y
|
|
|
97960
98251
|
}
|
|
97961
98252
|
if (!opts.yes) {
|
|
97962
98253
|
process.stdout.write(chalk6.yellow(`Delete scenario ${scenario.shortId} "${scenario.name}"? [y/N] `));
|
|
97963
|
-
const answer = await new Promise((
|
|
98254
|
+
const answer = await new Promise((resolve6) => {
|
|
97964
98255
|
let buf = "";
|
|
97965
98256
|
process.stdin.setRawMode?.(true);
|
|
97966
98257
|
process.stdin.resume();
|
|
@@ -97970,7 +98261,7 @@ program2.command("delete <id>").description("Delete a scenario").option("-y, --y
|
|
|
97970
98261
|
process.stdin.pause();
|
|
97971
98262
|
process.stdout.write(`
|
|
97972
98263
|
`);
|
|
97973
|
-
|
|
98264
|
+
resolve6(buf);
|
|
97974
98265
|
});
|
|
97975
98266
|
});
|
|
97976
98267
|
if (answer !== "y" && answer !== "yes") {
|
|
@@ -97999,7 +98290,7 @@ program2.command("remove <id>").alias("uninstall").description("Remove a scenari
|
|
|
97999
98290
|
}
|
|
98000
98291
|
if (!opts.yes) {
|
|
98001
98292
|
process.stdout.write(chalk6.yellow(`Remove scenario ${scenario.shortId} "${scenario.name}"? [y/N] `));
|
|
98002
|
-
const answer = await new Promise((
|
|
98293
|
+
const answer = await new Promise((resolve6) => {
|
|
98003
98294
|
let buf = "";
|
|
98004
98295
|
process.stdin.setRawMode?.(true);
|
|
98005
98296
|
process.stdin.resume();
|
|
@@ -98009,7 +98300,7 @@ program2.command("remove <id>").alias("uninstall").description("Remove a scenari
|
|
|
98009
98300
|
process.stdin.pause();
|
|
98010
98301
|
process.stdout.write(`
|
|
98011
98302
|
`);
|
|
98012
|
-
|
|
98303
|
+
resolve6(buf);
|
|
98013
98304
|
});
|
|
98014
98305
|
});
|
|
98015
98306
|
if (answer !== "y" && answer !== "yes") {
|
|
@@ -98212,7 +98503,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
|
|
|
98212
98503
|
`);
|
|
98213
98504
|
}
|
|
98214
98505
|
};
|
|
98215
|
-
await new Promise((
|
|
98506
|
+
await new Promise((resolve6) => {
|
|
98216
98507
|
const poll = setInterval(() => {
|
|
98217
98508
|
const run3 = getRun(runId);
|
|
98218
98509
|
if (!run3)
|
|
@@ -98220,7 +98511,7 @@ program2.command("run [url] [description]").alias("test").description("Run test
|
|
|
98220
98511
|
renderTable();
|
|
98221
98512
|
if (DONE_STATUSES.has(run3.status)) {
|
|
98222
98513
|
clearInterval(poll);
|
|
98223
|
-
|
|
98514
|
+
resolve6();
|
|
98224
98515
|
}
|
|
98225
98516
|
}, POLL_INTERVAL);
|
|
98226
98517
|
});
|
|
@@ -98615,15 +98906,15 @@ program2.command("screenshots <id>").description("List screenshots for a run or
|
|
|
98615
98906
|
});
|
|
98616
98907
|
program2.command("import <dir>").description("Import markdown test files as scenarios").action((dir) => {
|
|
98617
98908
|
try {
|
|
98618
|
-
const absDir =
|
|
98619
|
-
const files =
|
|
98909
|
+
const absDir = resolve5(dir);
|
|
98910
|
+
const files = readdirSync7(absDir).filter((f2) => f2.endsWith(".md"));
|
|
98620
98911
|
if (files.length === 0) {
|
|
98621
98912
|
log(chalk6.dim("No .md files found in directory."));
|
|
98622
98913
|
return;
|
|
98623
98914
|
}
|
|
98624
98915
|
let imported = 0;
|
|
98625
98916
|
for (const file2 of files) {
|
|
98626
|
-
const content =
|
|
98917
|
+
const content = readFileSync11(join21(absDir, file2), "utf-8");
|
|
98627
98918
|
const lines = content.split(`
|
|
98628
98919
|
`);
|
|
98629
98920
|
let name21 = file2.replace(/\.md$/, "");
|
|
@@ -98679,11 +98970,11 @@ program2.command("export [format]").description("Export scenarios as JSON (defau
|
|
|
98679
98970
|
const outputPath = opts.output ?? "testers-export.json";
|
|
98680
98971
|
const data = JSON.stringify(scenarios, null, 2);
|
|
98681
98972
|
writeFileSync7(outputPath, data, "utf-8");
|
|
98682
|
-
log(chalk6.green(`Exported ${scenarios.length} scenario(s) to ${
|
|
98973
|
+
log(chalk6.green(`Exported ${scenarios.length} scenario(s) to ${resolve5(outputPath)}`));
|
|
98683
98974
|
return;
|
|
98684
98975
|
}
|
|
98685
98976
|
const outputDir = opts.output ?? ".";
|
|
98686
|
-
if (!
|
|
98977
|
+
if (!existsSync19(outputDir)) {
|
|
98687
98978
|
mkdirSync15(outputDir, { recursive: true });
|
|
98688
98979
|
}
|
|
98689
98980
|
for (const s2 of scenarios) {
|
|
@@ -98711,13 +99002,13 @@ program2.command("export [format]").description("Export scenarios as JSON (defau
|
|
|
98711
99002
|
lines.push("");
|
|
98712
99003
|
}
|
|
98713
99004
|
const safeFilename = s2.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
|
|
98714
|
-
const filePath =
|
|
99005
|
+
const filePath = join21(outputDir, `${s2.shortId}-${safeFilename}.md`);
|
|
98715
99006
|
writeFileSync7(filePath, lines.join(`
|
|
98716
99007
|
`), "utf-8");
|
|
98717
99008
|
log(chalk6.dim(` ${s2.shortId}: ${s2.name} \u2192 ${filePath}`));
|
|
98718
99009
|
}
|
|
98719
99010
|
log(chalk6.green(`
|
|
98720
|
-
Exported ${scenarios.length} scenario(s) as markdown to ${
|
|
99011
|
+
Exported ${scenarios.length} scenario(s) as markdown to ${resolve5(outputDir)}`));
|
|
98721
99012
|
} catch (error40) {
|
|
98722
99013
|
logError(chalk6.red(`Error: ${error40 instanceof Error ? error40.message : String(error40)}`));
|
|
98723
99014
|
process.exit(1);
|
|
@@ -98736,7 +99027,7 @@ program2.command("status").description("Show database and auth status").action((
|
|
|
98736
99027
|
try {
|
|
98737
99028
|
const config2 = loadConfig();
|
|
98738
99029
|
const hasApiKey = !!config2.anthropicApiKey || !!process.env["ANTHROPIC_API_KEY"];
|
|
98739
|
-
const dbPath =
|
|
99030
|
+
const dbPath = join21(getTestersDir(), "testers.db");
|
|
98740
99031
|
log("");
|
|
98741
99032
|
log(chalk6.bold(" Open Testers Status"));
|
|
98742
99033
|
log("");
|
|
@@ -98886,13 +99177,13 @@ projectCmd.command("export-open <id>").description("Register a testers project i
|
|
|
98886
99177
|
projectCmd.command("use <name>").description("Set active project (find or create)").option("--json", "Output as JSON", false).action((name21, opts) => {
|
|
98887
99178
|
try {
|
|
98888
99179
|
const project = ensureProject(name21, process.cwd());
|
|
98889
|
-
if (!
|
|
99180
|
+
if (!existsSync19(CONFIG_DIR5)) {
|
|
98890
99181
|
mkdirSync15(CONFIG_DIR5, { recursive: true });
|
|
98891
99182
|
}
|
|
98892
99183
|
let config2 = {};
|
|
98893
|
-
if (
|
|
99184
|
+
if (existsSync19(CONFIG_PATH4)) {
|
|
98894
99185
|
try {
|
|
98895
|
-
config2 = JSON.parse(
|
|
99186
|
+
config2 = JSON.parse(readFileSync11(CONFIG_PATH4, "utf-8"));
|
|
98896
99187
|
} catch {}
|
|
98897
99188
|
}
|
|
98898
99189
|
config2.activeProject = project.id;
|
|
@@ -98910,7 +99201,7 @@ projectCmd.command("use <name>").description("Set active project (find or create
|
|
|
98910
99201
|
var repoCmd = program2.command("repo").description("Discover and run repo-native Playwright tests");
|
|
98911
99202
|
repoCmd.command("discover [path]").alias("scan").description("Discover Playwright tests in a repo").option("--refresh", "Force a fresh scan, ignoring cache", false).option("--json", "Output as JSON", false).option("--base-url <url>", "Override the suggested base URL").action((path, opts) => {
|
|
98912
99203
|
try {
|
|
98913
|
-
const repoPath =
|
|
99204
|
+
const repoPath = resolve5(path ?? process.cwd());
|
|
98914
99205
|
const snapshot = discoverRepo({
|
|
98915
99206
|
repoPath,
|
|
98916
99207
|
refresh: opts.refresh,
|
|
@@ -98976,7 +99267,7 @@ repoCmd.command("discover [path]").alias("scan").description("Discover Playwrigh
|
|
|
98976
99267
|
});
|
|
98977
99268
|
repoCmd.command("prepare [path]").alias("prep").description("Install dependencies and browsers for repo tests").option("--all", "Run all prep steps (install, browsers, build, seed)", false).option("--install", "Install dependencies", false).option("--browsers", "Install Playwright browsers", false).option("--build", "Build the app", false).option("--seed", "Seed the database", false).option("--refresh", "Force fresh discovery scan", false).option("--json", "Output as JSON", false).action((path, opts) => {
|
|
98978
99269
|
try {
|
|
98979
|
-
const repoPath =
|
|
99270
|
+
const repoPath = resolve5(path ?? process.cwd());
|
|
98980
99271
|
const snapshot = discoverRepo({ repoPath, refresh: opts.refresh });
|
|
98981
99272
|
const steps = [];
|
|
98982
99273
|
if (opts.all) {
|
|
@@ -99054,7 +99345,7 @@ repoCmd.command("run [path]").description("Run discovered Playwright tests nativ
|
|
|
99054
99345
|
return acc;
|
|
99055
99346
|
}, []).option("--timeout <ms>", "Timeout per spec file", "300000").option("--url <url>", "Dev server URL").option("--project <id>", "Project ID for result storage").option("--label <text>", "Run label").option("--json", "Output as JSON", false).action(async (path, opts) => {
|
|
99056
99347
|
try {
|
|
99057
|
-
const repoPath =
|
|
99348
|
+
const repoPath = resolve5(path ?? process.cwd());
|
|
99058
99349
|
const snapshot = discoverRepo({
|
|
99059
99350
|
repoPath,
|
|
99060
99351
|
refresh: opts.refresh,
|
|
@@ -99120,7 +99411,7 @@ repoCmd.command("run [path]").description("Run discovered Playwright tests nativ
|
|
|
99120
99411
|
repoCmd.command("cache [path]").description("Manage discovery cache").option("--clear", "Clear discovery cache", false).option("--status", "Show cache status", false).action((path, opts) => {
|
|
99121
99412
|
try {
|
|
99122
99413
|
if (opts.clear) {
|
|
99123
|
-
const repoPath2 = path ?
|
|
99414
|
+
const repoPath2 = path ? resolve5(path) : undefined;
|
|
99124
99415
|
clearDiscoveryCache(repoPath2);
|
|
99125
99416
|
if (repoPath2) {
|
|
99126
99417
|
log(chalk6.green("Discovery cache cleared for this repo."));
|
|
@@ -99130,7 +99421,7 @@ repoCmd.command("cache [path]").description("Manage discovery cache").option("--
|
|
|
99130
99421
|
return;
|
|
99131
99422
|
}
|
|
99132
99423
|
if (opts.status) {
|
|
99133
|
-
const repoPath2 =
|
|
99424
|
+
const repoPath2 = resolve5(path ?? process.cwd());
|
|
99134
99425
|
const info2 = getDiscoveryCacheInfo(repoPath2);
|
|
99135
99426
|
if (!info2) {
|
|
99136
99427
|
log(chalk6.dim("No discovery cache for this repo."));
|
|
@@ -99144,7 +99435,7 @@ repoCmd.command("cache [path]").description("Manage discovery cache").option("--
|
|
|
99144
99435
|
log("");
|
|
99145
99436
|
return;
|
|
99146
99437
|
}
|
|
99147
|
-
const repoPath =
|
|
99438
|
+
const repoPath = resolve5(path ?? process.cwd());
|
|
99148
99439
|
const info = getDiscoveryCacheInfo(repoPath);
|
|
99149
99440
|
if (!info) {
|
|
99150
99441
|
log(chalk6.dim("No discovery cache. Run 'testers repo discover' to create one."));
|
|
@@ -99233,8 +99524,8 @@ sessionCmd.command("show <id>").description("Show details of a recorded session"
|
|
|
99233
99524
|
});
|
|
99234
99525
|
sessionCmd.command("import <file>").description("Import a session JSON file exported from the Chrome extension").action(async (file2) => {
|
|
99235
99526
|
try {
|
|
99236
|
-
const { readFileSync:
|
|
99237
|
-
const raw =
|
|
99527
|
+
const { readFileSync: readFileSync12 } = await import("fs");
|
|
99528
|
+
const raw = readFileSync12(file2, "utf-8");
|
|
99238
99529
|
const data = JSON.parse(raw);
|
|
99239
99530
|
const items = Array.isArray(data) ? data : [data];
|
|
99240
99531
|
const { createSession: createSession2 } = await Promise.resolve().then(() => (init_sessions(), exports_sessions));
|
|
@@ -99474,7 +99765,7 @@ program2.command("daemon").description("Start the scheduler daemon").option("--i
|
|
|
99474
99765
|
} catch (err) {
|
|
99475
99766
|
logError(chalk6.red(`Daemon error: ${err instanceof Error ? err.message : String(err)}`));
|
|
99476
99767
|
}
|
|
99477
|
-
await new Promise((
|
|
99768
|
+
await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
|
|
99478
99769
|
}
|
|
99479
99770
|
};
|
|
99480
99771
|
process.on("SIGINT", () => {
|
|
@@ -99503,9 +99794,9 @@ program2.command("ci [provider]").description("Print or write a CI workflow (def
|
|
|
99503
99794
|
}
|
|
99504
99795
|
const workflow = generateGitHubActionsWorkflow();
|
|
99505
99796
|
if (opts.output) {
|
|
99506
|
-
const outPath =
|
|
99797
|
+
const outPath = resolve5(opts.output);
|
|
99507
99798
|
const outDir = outPath.replace(/\/[^/]*$/, "");
|
|
99508
|
-
if (outDir && !
|
|
99799
|
+
if (outDir && !existsSync19(outDir)) {
|
|
99509
99800
|
mkdirSync15(outDir, { recursive: true });
|
|
99510
99801
|
}
|
|
99511
99802
|
writeFileSync7(outPath, workflow, "utf-8");
|
|
@@ -99539,11 +99830,11 @@ program2.command("init").description("Initialize a new testing project").option(
|
|
|
99539
99830
|
log(` ${chalk6.dim(s2.shortId)} ${s2.name} ${chalk6.dim(`[${s2.tags.join(", ")}]`)}`);
|
|
99540
99831
|
}
|
|
99541
99832
|
if (opts.ci === "github") {
|
|
99542
|
-
const workflowDir =
|
|
99543
|
-
if (!
|
|
99833
|
+
const workflowDir = join21(process.cwd(), ".github", "workflows");
|
|
99834
|
+
if (!existsSync19(workflowDir)) {
|
|
99544
99835
|
mkdirSync15(workflowDir, { recursive: true });
|
|
99545
99836
|
}
|
|
99546
|
-
const workflowPath =
|
|
99837
|
+
const workflowPath = join21(workflowDir, "testers.yml");
|
|
99547
99838
|
writeFileSync7(workflowPath, generateGitHubActionsWorkflow(), "utf-8");
|
|
99548
99839
|
log(` CI: ${chalk6.green("GitHub Actions workflow written to .github/workflows/testers.yml")}`);
|
|
99549
99840
|
} else if (opts.ci) {
|
|
@@ -99553,7 +99844,7 @@ program2.command("init").description("Initialize a new testing project").option(
|
|
|
99553
99844
|
if (opts.yes)
|
|
99554
99845
|
return;
|
|
99555
99846
|
const rl2 = createInterface({ input: process.stdin, output: process.stdout });
|
|
99556
|
-
const ask = (q2) => new Promise((
|
|
99847
|
+
const ask = (q2) => new Promise((resolve6) => rl2.question(q2, resolve6));
|
|
99557
99848
|
try {
|
|
99558
99849
|
const envAnswer = await ask(" Would you like to configure environments? [y/N] ");
|
|
99559
99850
|
if (envAnswer.trim().toLowerCase() === "y") {
|
|
@@ -99737,14 +100028,14 @@ program2.command("quick-qa <url>").alias("quick-check").description("Run a fast
|
|
|
99737
100028
|
wcagLevel
|
|
99738
100029
|
});
|
|
99739
100030
|
if (opts.output) {
|
|
99740
|
-
writeFileSync7(
|
|
100031
|
+
writeFileSync7(resolve5(opts.output), JSON.stringify(result, null, 2));
|
|
99741
100032
|
}
|
|
99742
100033
|
if (opts.json) {
|
|
99743
100034
|
log(JSON.stringify(result, null, 2));
|
|
99744
100035
|
} else {
|
|
99745
100036
|
log(formatQuickQaReport(result));
|
|
99746
100037
|
if (opts.output)
|
|
99747
|
-
log(chalk6.dim(`Wrote JSON results to ${
|
|
100038
|
+
log(chalk6.dim(`Wrote JSON results to ${resolve5(opts.output)}`));
|
|
99748
100039
|
}
|
|
99749
100040
|
process.exit(getQuickQaExitCode(result));
|
|
99750
100041
|
} catch (error40) {
|
|
@@ -99789,7 +100080,7 @@ program2.command("report [run-id]").description("Generate HTML test report or co
|
|
|
99789
100080
|
});
|
|
99790
100081
|
if (opts.output && opts.output !== "report.html") {
|
|
99791
100082
|
writeFileSync7(opts.output, content, "utf-8");
|
|
99792
|
-
const absPath2 =
|
|
100083
|
+
const absPath2 = resolve5(opts.output);
|
|
99793
100084
|
log(chalk6.green(`Compliance report written to ${absPath2}`));
|
|
99794
100085
|
} else {
|
|
99795
100086
|
log(content);
|
|
@@ -99803,7 +100094,7 @@ program2.command("report [run-id]").description("Generate HTML test report or co
|
|
|
99803
100094
|
html = generateHtmlReport(runId);
|
|
99804
100095
|
}
|
|
99805
100096
|
writeFileSync7(opts.output, html, "utf-8");
|
|
99806
|
-
const absPath =
|
|
100097
|
+
const absPath = resolve5(opts.output);
|
|
99807
100098
|
log(chalk6.green(`Report generated: ${absPath}`));
|
|
99808
100099
|
if (opts.open) {
|
|
99809
100100
|
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
@@ -100160,6 +100451,62 @@ Imported ${imported} scenarios from API spec:`));
|
|
|
100160
100451
|
process.exit(1);
|
|
100161
100452
|
}
|
|
100162
100453
|
});
|
|
100454
|
+
var inventoryCmd = program2.command("inventory").description("Discover source-derived app route/action inventories");
|
|
100455
|
+
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) => {
|
|
100456
|
+
acc.push(val);
|
|
100457
|
+
return acc;
|
|
100458
|
+
}, []).option("--timeout <ms>", "Workflow timeout in milliseconds").option("--json", "Output as JSON", false).action(async (root, opts) => {
|
|
100459
|
+
try {
|
|
100460
|
+
const { importNextRouteInventory: importNextRouteInventory2 } = await Promise.resolve().then(() => (init_next_route_inventory(), exports_next_route_inventory));
|
|
100461
|
+
const projectId = resolveProject2(opts.project) ?? undefined;
|
|
100462
|
+
const env = parseSandboxEnv(undefined, opts.sandboxEnvOptional);
|
|
100463
|
+
const result = importNextRouteInventory2({
|
|
100464
|
+
rootDir: root ?? process.cwd(),
|
|
100465
|
+
appDir: opts.appDir,
|
|
100466
|
+
projectId,
|
|
100467
|
+
includePages: opts.pages !== false,
|
|
100468
|
+
includeApi: opts.api !== false,
|
|
100469
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : undefined,
|
|
100470
|
+
createScenarios: opts.createScenarios,
|
|
100471
|
+
createWorkflows: opts.createWorkflows,
|
|
100472
|
+
workflowTarget: opts.workflowTarget,
|
|
100473
|
+
workflowProvider: opts.workflowTarget === "sandbox" ? opts.sandboxProvider : undefined,
|
|
100474
|
+
workflowExecution: {
|
|
100475
|
+
target: opts.workflowTarget,
|
|
100476
|
+
provider: opts.workflowTarget === "sandbox" ? opts.sandboxProvider : undefined,
|
|
100477
|
+
sandboxCleanup: opts.sandboxCleanup,
|
|
100478
|
+
sandboxSyncStrategy: opts.sandboxSync,
|
|
100479
|
+
timeoutMs: opts.timeout ? parseInt(opts.timeout, 10) : undefined,
|
|
100480
|
+
env
|
|
100481
|
+
}
|
|
100482
|
+
});
|
|
100483
|
+
if (opts.json) {
|
|
100484
|
+
log(JSON.stringify(result, null, 2));
|
|
100485
|
+
return;
|
|
100486
|
+
}
|
|
100487
|
+
log("");
|
|
100488
|
+
log(chalk6.bold(" Next.js Route Inventory"));
|
|
100489
|
+
log(chalk6.dim(` Root: ${result.inventory.rootDir}`));
|
|
100490
|
+
log(chalk6.dim(` App: ${result.inventory.appDir}`));
|
|
100491
|
+
log("");
|
|
100492
|
+
log(` Routes: ${chalk6.cyan(String(result.inventory.total))} (${result.inventory.pages} pages, ${result.inventory.apiRoutes} API, ${result.inventory.dynamic} dynamic)`);
|
|
100493
|
+
log(` Scenarios: ${chalk6.green(String(result.created))} created, ${chalk6.yellow(String(result.updated))} updated, ${chalk6.dim(String(result.deduped))} deduped`);
|
|
100494
|
+
log(` Workflows: ${result.workflows.length}`);
|
|
100495
|
+
log("");
|
|
100496
|
+
for (const [category, count] of Object.entries(result.inventory.categories).sort()) {
|
|
100497
|
+
log(` ${category.padEnd(18)} ${count}`);
|
|
100498
|
+
}
|
|
100499
|
+
log("");
|
|
100500
|
+
if (!opts.createScenarios)
|
|
100501
|
+
log(chalk6.dim(" Add --create-scenarios to upsert route scenarios."));
|
|
100502
|
+
if (!opts.createWorkflows)
|
|
100503
|
+
log(chalk6.dim(" Add --create-workflows to upsert grouped workflows."));
|
|
100504
|
+
log("");
|
|
100505
|
+
} catch (error40) {
|
|
100506
|
+
logError(chalk6.red(`Error: ${error40 instanceof Error ? error40.message : String(error40)}`));
|
|
100507
|
+
process.exit(1);
|
|
100508
|
+
}
|
|
100509
|
+
});
|
|
100163
100510
|
program2.command("generate <url>").description("Crawl app and synthesize test scenarios using AI (any provider)").option("--max <n>", "Max scenarios to generate", "10").option("--max-pages <n>", "Max pages to crawl", "10").option("--focus <topic>", "Focus on specific area e.g. 'auth flows', 'checkout'").option("--persona <desc>", "Persona perspective e.g. 'first-time user'").option("--model <model>", "AI model (claude-haiku, gpt-4o-mini, gemini-2.0-flash, etc.)").option("--save", "Persist generated scenarios to DB", false).option("--project <id>", "Project ID").option("--headed", "Run browser in headed mode", false).option("--json", "Output as JSON", false).action(async (url2, opts) => {
|
|
100164
100511
|
try {
|
|
100165
100512
|
const { generateScenarios: generateScenarios2 } = await Promise.resolve().then(() => (init_generator(), exports_generator));
|
|
@@ -100622,7 +100969,7 @@ program2.command("doctor").description("Check system setup and configuration").a
|
|
|
100622
100969
|
log(chalk6.red("\u2717") + " ANTHROPIC_API_KEY is not set (required for AI-powered tests)");
|
|
100623
100970
|
allPassed = false;
|
|
100624
100971
|
}
|
|
100625
|
-
const dbPath =
|
|
100972
|
+
const dbPath = join21(getTestersDir(), "testers.db");
|
|
100626
100973
|
try {
|
|
100627
100974
|
const { Database: Database5 } = await import("bun:sqlite");
|
|
100628
100975
|
const db3 = new Database5(dbPath, { create: true });
|
|
@@ -100676,7 +101023,7 @@ program2.command("serve").description("Start the Open Testers web dashboard").op
|
|
|
100676
101023
|
try {
|
|
100677
101024
|
const port = parseInt(opts.port, 10);
|
|
100678
101025
|
const url2 = `http://localhost:${port}`;
|
|
100679
|
-
const serverBin =
|
|
101026
|
+
const serverBin = join21(resolve5(process.execPath, ".."), "..", "dist", "server", "index.js");
|
|
100680
101027
|
const { join: pathJoin, resolve: pathResolve, dirname: dirname7 } = await import("path");
|
|
100681
101028
|
const { fileURLToPath: fileURLToPath2 } = await import("url");
|
|
100682
101029
|
const serverPath = pathJoin(dirname7(fileURLToPath2(import.meta.url)), "..", "server", "index.js");
|
|
@@ -101442,7 +101789,7 @@ personaCmd.command("delete <id>").description("Delete a persona").option("-y, --
|
|
|
101442
101789
|
}
|
|
101443
101790
|
if (!opts.yes) {
|
|
101444
101791
|
process.stdout.write(chalk6.yellow(`Delete persona ${persona.shortId} "${persona.name}"? [y/N] `));
|
|
101445
|
-
const answer = await new Promise((
|
|
101792
|
+
const answer = await new Promise((resolve6) => {
|
|
101446
101793
|
let buf = "";
|
|
101447
101794
|
process.stdin.setRawMode?.(true);
|
|
101448
101795
|
process.stdin.resume();
|
|
@@ -101452,7 +101799,7 @@ personaCmd.command("delete <id>").description("Delete a persona").option("-y, --
|
|
|
101452
101799
|
process.stdin.pause();
|
|
101453
101800
|
process.stdout.write(`
|
|
101454
101801
|
`);
|
|
101455
|
-
|
|
101802
|
+
resolve6(buf);
|
|
101456
101803
|
});
|
|
101457
101804
|
});
|
|
101458
101805
|
if (answer !== "y" && answer !== "yes") {
|
|
@@ -101613,7 +101960,7 @@ evalCmd.command("rag <url>").description("Run RAG quality evaluation \u2014 fait
|
|
|
101613
101960
|
let ragTestCases = [];
|
|
101614
101961
|
if (opts.docs) {
|
|
101615
101962
|
try {
|
|
101616
|
-
const raw =
|
|
101963
|
+
const raw = readFileSync11(opts.docs, "utf-8");
|
|
101617
101964
|
ragTestCases = JSON.parse(raw);
|
|
101618
101965
|
} catch {
|
|
101619
101966
|
logError(chalk6.red(`Failed to read docs file: ${opts.docs}`));
|
|
@@ -101703,9 +102050,9 @@ Created golden answer check ${chalk6.bold(golden2.shortId)}`));
|
|
|
101703
102050
|
}
|
|
101704
102051
|
const ask = (prompt) => {
|
|
101705
102052
|
const rl2 = createInterface({ input: process.stdin, output: process.stdout });
|
|
101706
|
-
return new Promise((
|
|
102053
|
+
return new Promise((resolve6) => rl2.question(prompt, (ans) => {
|
|
101707
102054
|
rl2.close();
|
|
101708
|
-
|
|
102055
|
+
resolve6(ans.trim());
|
|
101709
102056
|
}));
|
|
101710
102057
|
};
|
|
101711
102058
|
const question = await ask("Question (what this endpoint should answer): ");
|
|
@@ -101866,9 +102213,9 @@ program2.command("run-many <url>").description("Run scenarios \xD7 personas matr
|
|
|
101866
102213
|
});
|
|
101867
102214
|
program2.command("run-script <file>").description("Run a hybrid test script (.ts) that exports an array of HybridScenario objects").option("--url <url>", "Base URL to run against").option("--json", "Output as JSON", false).action(async (file2, opts) => {
|
|
101868
102215
|
try {
|
|
101869
|
-
const { resolve:
|
|
102216
|
+
const { resolve: resolve6 } = await import("path");
|
|
101870
102217
|
const { runHybridScenario: runHybridScenario2 } = await Promise.resolve().then(() => (init_hybrid_runner(), exports_hybrid_runner));
|
|
101871
|
-
const scriptPath =
|
|
102218
|
+
const scriptPath = resolve6(process.cwd(), file2);
|
|
101872
102219
|
const mod = await import(scriptPath);
|
|
101873
102220
|
const scenarios = mod.scenarios ?? mod.default ?? [];
|
|
101874
102221
|
if (!Array.isArray(scenarios) || scenarios.length === 0) {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { CreateScenarioInput, Scenario, ScenarioPriority, TestingWorkflow, WorkflowExecutionInput } from "../types/index.js";
|
|
2
|
+
export type NextRouteKind = "page" | "api";
|
|
3
|
+
export interface NextRouteInventoryItem {
|
|
4
|
+
kind: NextRouteKind;
|
|
5
|
+
routePath: string;
|
|
6
|
+
file: string;
|
|
7
|
+
category: string;
|
|
8
|
+
groups: string[];
|
|
9
|
+
methods: string[];
|
|
10
|
+
dynamic: boolean;
|
|
11
|
+
requiresAuth: boolean;
|
|
12
|
+
tags: string[];
|
|
13
|
+
priority: ScenarioPriority;
|
|
14
|
+
}
|
|
15
|
+
export interface NextRouteInventory {
|
|
16
|
+
rootDir: string;
|
|
17
|
+
appDir: string;
|
|
18
|
+
total: number;
|
|
19
|
+
pages: number;
|
|
20
|
+
apiRoutes: number;
|
|
21
|
+
dynamic: number;
|
|
22
|
+
categories: Record<string, number>;
|
|
23
|
+
items: NextRouteInventoryItem[];
|
|
24
|
+
}
|
|
25
|
+
export interface ImportNextRouteInventoryOptions {
|
|
26
|
+
rootDir: string;
|
|
27
|
+
appDir?: string;
|
|
28
|
+
projectId?: string;
|
|
29
|
+
includePages?: boolean;
|
|
30
|
+
includeApi?: boolean;
|
|
31
|
+
limit?: number;
|
|
32
|
+
createScenarios?: boolean;
|
|
33
|
+
createWorkflows?: boolean;
|
|
34
|
+
workflowTarget?: "local" | "sandbox";
|
|
35
|
+
workflowProvider?: string;
|
|
36
|
+
workflowExecution?: Partial<WorkflowExecutionInput>;
|
|
37
|
+
}
|
|
38
|
+
export interface ImportNextRouteInventoryResult {
|
|
39
|
+
inventory: NextRouteInventory;
|
|
40
|
+
created: number;
|
|
41
|
+
updated: number;
|
|
42
|
+
deduped: number;
|
|
43
|
+
scenarios: Scenario[];
|
|
44
|
+
workflows: TestingWorkflow[];
|
|
45
|
+
}
|
|
46
|
+
export declare function discoverNextRouteInventory(options: {
|
|
47
|
+
rootDir: string;
|
|
48
|
+
appDir?: string;
|
|
49
|
+
includePages?: boolean;
|
|
50
|
+
includeApi?: boolean;
|
|
51
|
+
limit?: number;
|
|
52
|
+
}): NextRouteInventory;
|
|
53
|
+
export declare function scenarioInputForNextRoute(item: NextRouteInventoryItem, projectId?: string): CreateScenarioInput;
|
|
54
|
+
export declare function importNextRouteInventory(options: ImportNextRouteInventoryOptions): ImportNextRouteInventoryResult;
|
|
55
|
+
//# sourceMappingURL=next-route-inventory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-route-inventory.d.ts","sourceRoot":"","sources":["../../src/lib/next-route-inventory.ts"],"names":[],"mappings":"AAIA,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;AAE3C,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,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;AAwBD,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,CA0CrB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,CAuBhC"}
|
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.49",
|
|
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
|
@@ -46937,7 +46937,7 @@ import { join as join14 } from "path";
|
|
|
46937
46937
|
// package.json
|
|
46938
46938
|
var package_default = {
|
|
46939
46939
|
name: "@hasna/testers",
|
|
46940
|
-
version: "0.0.
|
|
46940
|
+
version: "0.0.49",
|
|
46941
46941
|
description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
|
|
46942
46942
|
type: "module",
|
|
46943
46943
|
main: "dist/index.js",
|