@qawolf/cli 1.3.0 → 1.4.0
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.js +711 -187
- package/package.json +4 -3
- package/skills/qawolf-cli/SKILL.md +30 -15
package/dist/cli.js
CHANGED
|
@@ -8514,7 +8514,7 @@ var require_BufferList = __commonJS((exports, module) => {
|
|
|
8514
8514
|
this.head = this.tail = null;
|
|
8515
8515
|
this.length = 0;
|
|
8516
8516
|
};
|
|
8517
|
-
BufferList.prototype.join = function
|
|
8517
|
+
BufferList.prototype.join = function join33(s3) {
|
|
8518
8518
|
if (this.length === 0)
|
|
8519
8519
|
return "";
|
|
8520
8520
|
var p3 = this.head;
|
|
@@ -23070,7 +23070,7 @@ var require_BufferList2 = __commonJS((exports, module) => {
|
|
|
23070
23070
|
this.head = this.tail = null;
|
|
23071
23071
|
this.length = 0;
|
|
23072
23072
|
};
|
|
23073
|
-
BufferList.prototype.join = function
|
|
23073
|
+
BufferList.prototype.join = function join33(s3) {
|
|
23074
23074
|
if (this.length === 0)
|
|
23075
23075
|
return "";
|
|
23076
23076
|
var p3 = this.head;
|
|
@@ -155181,7 +155181,7 @@ var authMessages = {
|
|
|
155181
155181
|
rejected403: (noun) => `QA Wolf API rejected the${noun ? ` ${noun}` : ""} request (HTTP 403). Check that your API key has access to this environment.`,
|
|
155182
155182
|
notFound404: (noun) => `QA Wolf API could not find ${noun ? `${noun} for that environment` : "that environment"} (HTTP 404). Check the --env value.`,
|
|
155183
155183
|
failedWithStatus: (status, noun) => `QA Wolf API${noun ? ` ${noun}` : ""} request failed (HTTP ${status}).`,
|
|
155184
|
-
networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and
|
|
155184
|
+
networkUnreachable: (baseUrl, noun) => `Could not reach the QA Wolf API at ${baseUrl}${noun ? ` to fetch ${noun}` : ""}. Check your network connection and QAWOLF_HOST_URL.`,
|
|
155185
155185
|
unexpectedResponse: (noun) => `Unexpected${noun ? ` ${noun}` : ""} response from the QA Wolf API.`
|
|
155186
155186
|
},
|
|
155187
155187
|
bundle: {
|
|
@@ -155626,6 +155626,15 @@ function makeDefaultFs() {
|
|
|
155626
155626
|
};
|
|
155627
155627
|
}
|
|
155628
155628
|
|
|
155629
|
+
// src/shell/resolveHostUrl.ts
|
|
155630
|
+
var defaultHostUrl = "https://app.qawolf.com";
|
|
155631
|
+
function resolveHostUrl(env2) {
|
|
155632
|
+
const configuredHostUrl = env2["QAWOLF_HOST_URL"]?.trim();
|
|
155633
|
+
if (!configuredHostUrl)
|
|
155634
|
+
return defaultHostUrl;
|
|
155635
|
+
return configuredHostUrl.replace(/\/+$/, "");
|
|
155636
|
+
}
|
|
155637
|
+
|
|
155629
155638
|
// src/domains/auth/resolve.ts
|
|
155630
155639
|
import { Entry as Entry3 } from "@napi-rs/keyring";
|
|
155631
155640
|
|
|
@@ -173377,7 +173386,7 @@ function buildBaseContext(command, signals) {
|
|
|
173377
173386
|
logPath: defaultLogPath(),
|
|
173378
173387
|
...verboseWrite ? { verboseWrite } : {}
|
|
173379
173388
|
});
|
|
173380
|
-
const apiBaseUrl = env2
|
|
173389
|
+
const apiBaseUrl = resolveHostUrl(env2);
|
|
173381
173390
|
return {
|
|
173382
173391
|
ctx: {
|
|
173383
173392
|
ui: createUI(outputMode, {
|
|
@@ -173589,7 +173598,7 @@ function registerAuthCommand(program2, signals) {
|
|
|
173589
173598
|
// package.json
|
|
173590
173599
|
var package_default = {
|
|
173591
173600
|
name: "@qawolf/cli",
|
|
173592
|
-
version: "1.
|
|
173601
|
+
version: "1.4.0",
|
|
173593
173602
|
description: "Run and manage QA Wolf flows from the terminal, CI, or an AI agent",
|
|
173594
173603
|
keywords: [
|
|
173595
173604
|
"automation",
|
|
@@ -173649,16 +173658,17 @@ var package_default = {
|
|
|
173649
173658
|
"@napi-rs/keyring": "1.3.0",
|
|
173650
173659
|
"@oxc-node/core": "0.1.0",
|
|
173651
173660
|
"@playwright/test": "1.60.0",
|
|
173652
|
-
"@qawolf/api-contracts": "0.
|
|
173661
|
+
"@qawolf/api-contracts": "0.11.0",
|
|
173653
173662
|
"@qawolf/emails": "1.1.1",
|
|
173654
173663
|
"@qawolf/flow-targets": "1.0.0",
|
|
173655
|
-
"@qawolf/flows": "0.1.
|
|
173664
|
+
"@qawolf/flows": "0.1.4",
|
|
173656
173665
|
"@qawolf/testkit": "1.1.1",
|
|
173657
173666
|
appium: "2.11.3",
|
|
173658
173667
|
"appium-uiautomator2-driver": "3.7.0",
|
|
173659
173668
|
"appium-xcuitest-driver": "9.10.5",
|
|
173660
173669
|
commander: "14.0.3",
|
|
173661
173670
|
"env-paths": "4.0.0",
|
|
173671
|
+
"expect-webdriverio": "5.6.5",
|
|
173662
173672
|
picomatch: "4.0.4",
|
|
173663
173673
|
pino: "10.3.1",
|
|
173664
173674
|
playwright: "1.58.2",
|
|
@@ -175132,13 +175142,14 @@ import { createHash } from "node:crypto";
|
|
|
175132
175142
|
import { join as join12, resolve as resolve3 } from "node:path";
|
|
175133
175143
|
|
|
175134
175144
|
// src/generated/dependencyVersions.ts
|
|
175135
|
-
var flowsVersion = "0.1.
|
|
175145
|
+
var flowsVersion = "0.1.4";
|
|
175136
175146
|
var playwrightVersion = "1.58.2";
|
|
175137
175147
|
var emailsVersion = "1.1.1";
|
|
175138
175148
|
var testkitVersion = "1.1.1";
|
|
175139
175149
|
var appiumVersion = "2.11.3";
|
|
175140
175150
|
var appiumXcuitestDriverVersion = "9.10.5";
|
|
175141
175151
|
var appiumUiautomator2DriverVersion = "3.7.0";
|
|
175152
|
+
var expectWebdriverioVersion = "5.6.5";
|
|
175142
175153
|
|
|
175143
175154
|
// src/domains/runtimeEnv/pinnedPackages.ts
|
|
175144
175155
|
var pinnedPackages = [
|
|
@@ -175151,7 +175162,8 @@ var pinnedPackages = [
|
|
|
175151
175162
|
{
|
|
175152
175163
|
name: "appium-uiautomator2-driver",
|
|
175153
175164
|
version: appiumUiautomator2DriverVersion
|
|
175154
|
-
}
|
|
175165
|
+
},
|
|
175166
|
+
{ name: "expect-webdriverio", version: expectWebdriverioVersion }
|
|
175155
175167
|
];
|
|
175156
175168
|
|
|
175157
175169
|
// src/domains/runtimeEnv/managedEnvDir.ts
|
|
@@ -175664,9 +175676,125 @@ var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
|
175664
175676
|
|
|
175665
175677
|
// node_modules/@qawolf/api-contracts/dist/v1/ids.js
|
|
175666
175678
|
var defaultIdSchemas = {
|
|
175679
|
+
automation: exports_external.string().min(1).describe("The id of the automation."),
|
|
175667
175680
|
environment: exports_external.string().min(1).describe("The id of the environment."),
|
|
175668
175681
|
flow: exports_external.string().min(1).describe("The id of the flow."),
|
|
175669
|
-
|
|
175682
|
+
issue: exports_external.string().min(1).describe("The id of the issue."),
|
|
175683
|
+
run: exports_external.string().min(1).describe("The id of the run."),
|
|
175684
|
+
workspace: exports_external.string().min(1).describe("The id of the workspace.")
|
|
175685
|
+
};
|
|
175686
|
+
|
|
175687
|
+
// node_modules/@qawolf/api-contracts/dist/v1/resource.js
|
|
175688
|
+
function resource(fields, { urlFieldDescription = "Absolute URL." } = {}) {
|
|
175689
|
+
return exports_external.object({
|
|
175690
|
+
...fields,
|
|
175691
|
+
url: exports_external.url().describe(urlFieldDescription)
|
|
175692
|
+
});
|
|
175693
|
+
}
|
|
175694
|
+
|
|
175695
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/flowSelection.js
|
|
175696
|
+
var makeFlowSelectionSchema = (ids) => exports_external.object({
|
|
175697
|
+
flowIds: exports_external.array(ids.flow).default([]),
|
|
175698
|
+
tagNames: exports_external.array(exports_external.string().min(1)).default([])
|
|
175699
|
+
}).refine((selection) => selection.flowIds.length + selection.tagNames.length > 0, {
|
|
175700
|
+
message: "At least one flow or tag is required."
|
|
175701
|
+
});
|
|
175702
|
+
|
|
175703
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/index.js
|
|
175704
|
+
var makeCreateRunContract = (ids) => {
|
|
175705
|
+
const input = exports_external.object({
|
|
175706
|
+
environmentId: ids.environment,
|
|
175707
|
+
environmentVariables: exports_external.record(exports_external.string(), exports_external.string()).optional(),
|
|
175708
|
+
ignoreRules: exports_external.boolean().default(false),
|
|
175709
|
+
pullRequestNumber: exports_external.number().int().positive().describe("The number of the pull request to associate with the run.").optional(),
|
|
175710
|
+
repository: exports_external.string().min(1).describe('The owner/name of the repository, e.g. "acme/web".').optional()
|
|
175711
|
+
}).and(makeFlowSelectionSchema(ids));
|
|
175712
|
+
const output = resource({ runId: ids.run }, { urlFieldDescription: "Absolute URL of the run page." });
|
|
175713
|
+
return {
|
|
175714
|
+
description: "Create a run for the selected flows and/or tags in an environment.",
|
|
175715
|
+
input,
|
|
175716
|
+
kind: "write",
|
|
175717
|
+
name: "run.create",
|
|
175718
|
+
output
|
|
175719
|
+
};
|
|
175720
|
+
};
|
|
175721
|
+
|
|
175722
|
+
// node_modules/@qawolf/api-contracts/dist/v1/automate/index.js
|
|
175723
|
+
var makeAutomateContract = (ids) => {
|
|
175724
|
+
const input = exports_external.object({
|
|
175725
|
+
environmentId: ids.environment,
|
|
175726
|
+
flowIds: exports_external.array(ids.flow).min(1).max(300).refine((flowIds) => new Set(flowIds).size === flowIds.length, "flowIds must be unique").describe("Draft flows the automation builds."),
|
|
175727
|
+
patch: exports_external.string().min(1).max(1e6).optional().describe("Unified diff applied to the automation's working branch before it runs.")
|
|
175728
|
+
});
|
|
175729
|
+
const output = resource({ automationId: ids.automation }, { urlFieldDescription: "Absolute URL of the automation review page." });
|
|
175730
|
+
return {
|
|
175731
|
+
description: "Request an automation that builds the selected draft flows in an environment.",
|
|
175732
|
+
input,
|
|
175733
|
+
kind: "write",
|
|
175734
|
+
name: "automate",
|
|
175735
|
+
output
|
|
175736
|
+
};
|
|
175737
|
+
};
|
|
175738
|
+
|
|
175739
|
+
// node_modules/@qawolf/api-contracts/dist/v1/environment/index.js
|
|
175740
|
+
var makeEnvironmentResource = (environmentId) => resource({
|
|
175741
|
+
environmentId,
|
|
175742
|
+
flowCodeBranch: exports_external.discriminatedUnion("syncStatus", [
|
|
175743
|
+
exports_external.object({
|
|
175744
|
+
lastSyncedCommitHash: exports_external.string().describe("Hash of the latest reconciled commit."),
|
|
175745
|
+
name: exports_external.string().describe("The name of the environment's flow code branch."),
|
|
175746
|
+
syncStatus: exports_external.literal("reconciled").describe("The branch has reconciled at least one commit.")
|
|
175747
|
+
}),
|
|
175748
|
+
exports_external.object({
|
|
175749
|
+
name: exports_external.string().describe("The name of the environment's flow code branch."),
|
|
175750
|
+
syncStatus: exports_external.literal("not-reconciled").describe("The branch has not reconciled any commit yet.")
|
|
175751
|
+
})
|
|
175752
|
+
]).optional().describe("The environment's flow code branch and its git reconciliation state, if it has one."),
|
|
175753
|
+
kind: exports_external.enum(["static", "preview"]).describe("Whether the environment is static or a preview."),
|
|
175754
|
+
name: exports_external.string(),
|
|
175755
|
+
status: exports_external.enum(["blocked", "needs-investigation", "ready", "running"]).describe("Standing health across the environment's runs: blocked (open blocking bugs), needs-investigation (failures awaiting triage), running (flows executing), ready (none of the above — the greenlit state)."),
|
|
175756
|
+
terminatedAt: exports_external.iso.datetime().optional().describe("When the environment was terminated, if it has been.")
|
|
175757
|
+
}, { urlFieldDescription: "Absolute URL of the environment page." });
|
|
175758
|
+
var makeGetEnvironmentContract = (ids) => {
|
|
175759
|
+
const input = exports_external.object({
|
|
175760
|
+
environmentId: ids.environment
|
|
175761
|
+
});
|
|
175762
|
+
const output = makeEnvironmentResource(ids.environment);
|
|
175763
|
+
return {
|
|
175764
|
+
description: "Read a single environment's name, kind, health status, and termination state.",
|
|
175765
|
+
input,
|
|
175766
|
+
kind: "read",
|
|
175767
|
+
name: "environment.get",
|
|
175768
|
+
output
|
|
175769
|
+
};
|
|
175770
|
+
};
|
|
175771
|
+
var makeListEnvironmentVariableNamesContract = (ids) => {
|
|
175772
|
+
const input = exports_external.object({
|
|
175773
|
+
environmentId: ids.environment
|
|
175774
|
+
});
|
|
175775
|
+
const output = exports_external.object({
|
|
175776
|
+
variableNames: exports_external.array(exports_external.string()).describe("Names of the environment's variables, sorted alphabetically. Values are never returned.")
|
|
175777
|
+
});
|
|
175778
|
+
return {
|
|
175779
|
+
description: "List the names of an environment's variables, sorted. Names only — values never leave the server.",
|
|
175780
|
+
input,
|
|
175781
|
+
kind: "read",
|
|
175782
|
+
name: "environment.listVariableNames",
|
|
175783
|
+
output
|
|
175784
|
+
};
|
|
175785
|
+
};
|
|
175786
|
+
var makeCreateEnvironmentContract = (ids) => {
|
|
175787
|
+
const input = exports_external.object({
|
|
175788
|
+
name: exports_external.string().min(1).max(255).regex(/\S/, "Environment name cannot be blank.").describe("Display name of the new environment.")
|
|
175789
|
+
});
|
|
175790
|
+
const output = makeEnvironmentResource(ids.environment);
|
|
175791
|
+
return {
|
|
175792
|
+
description: "Create an environment on the caller's team and return it in the environment.get shape.",
|
|
175793
|
+
input,
|
|
175794
|
+
kind: "write",
|
|
175795
|
+
name: "environment.create",
|
|
175796
|
+
output
|
|
175797
|
+
};
|
|
175670
175798
|
};
|
|
175671
175799
|
|
|
175672
175800
|
// node_modules/@qawolf/api-contracts/dist/v1/flow/index.js
|
|
@@ -175677,12 +175805,14 @@ var makeListFlowsContract = (ids) => {
|
|
|
175677
175805
|
});
|
|
175678
175806
|
const executionTarget = exports_external.union([exports_external.string().min(1), exports_external.record(exports_external.string(), exports_external.unknown())]).describe("The target the flow executes against, as a preset or object.");
|
|
175679
175807
|
const output = exports_external.object({
|
|
175680
|
-
flows: exports_external.array(
|
|
175808
|
+
flows: exports_external.array(resource({
|
|
175681
175809
|
executionTarget,
|
|
175682
175810
|
flowId: ids.flow,
|
|
175683
175811
|
name: exports_external.string(),
|
|
175684
175812
|
path: exports_external.string().describe("The flow file path, relative to the repo."),
|
|
175685
175813
|
tags: exports_external.array(exports_external.string())
|
|
175814
|
+
}, {
|
|
175815
|
+
urlFieldDescription: "Absolute URL of the flow page in its environment."
|
|
175686
175816
|
}))
|
|
175687
175817
|
});
|
|
175688
175818
|
return {
|
|
@@ -175694,33 +175824,229 @@ var makeListFlowsContract = (ids) => {
|
|
|
175694
175824
|
};
|
|
175695
175825
|
};
|
|
175696
175826
|
|
|
175697
|
-
// node_modules/@qawolf/api-contracts/dist/v1/
|
|
175698
|
-
var
|
|
175699
|
-
|
|
175827
|
+
// node_modules/@qawolf/api-contracts/dist/v1/pagination.js
|
|
175828
|
+
var makePaginationInputFields = (limits) => ({
|
|
175829
|
+
cursor: exports_external.string().min(1).optional().describe("Opaque cursor copied from the previous response's nextCursor. Omit to fetch the first page."),
|
|
175830
|
+
limit: exports_external.number().int().min(1).max(limits.maxLimit).default(limits.defaultLimit).describe(`Maximum rows per page. Defaults to ${limits.defaultLimit}, capped at ${limits.maxLimit}.`)
|
|
175831
|
+
});
|
|
175832
|
+
var nextCursorSchema = exports_external.string().min(1).optional().describe("Repeat the request with this value as cursor to fetch the next page. Absent when there are no further results.");
|
|
175833
|
+
|
|
175834
|
+
// node_modules/@qawolf/api-contracts/dist/v1/issue/index.js
|
|
175835
|
+
var issuePrioritySchema = exports_external.enum([
|
|
175836
|
+
"unprioritized",
|
|
175837
|
+
"low",
|
|
175838
|
+
"medium",
|
|
175839
|
+
"high",
|
|
175840
|
+
"urgent"
|
|
175841
|
+
]);
|
|
175842
|
+
var issueStatusSchema = exports_external.enum([
|
|
175843
|
+
"pending",
|
|
175844
|
+
"inProgress",
|
|
175845
|
+
"paused",
|
|
175846
|
+
"resolved",
|
|
175847
|
+
"canceled",
|
|
175848
|
+
"archived"
|
|
175849
|
+
]);
|
|
175850
|
+
var publicIssueTypeSchema = exports_external.enum(["bug", "coverageRequest", "maintenance"]);
|
|
175851
|
+
var openIssueStatuses = ["pending", "inProgress", "paused"];
|
|
175852
|
+
var makeIssueResourceSchema = (ids) => resource({
|
|
175853
|
+
createdAt: exports_external.coerce.date().describe("When the issue was created."),
|
|
175854
|
+
description: exports_external.string().describe("The issue description in Markdown."),
|
|
175855
|
+
issueId: ids.issue,
|
|
175856
|
+
name: exports_external.string(),
|
|
175857
|
+
number: exports_external.number().int(),
|
|
175858
|
+
priority: issuePrioritySchema,
|
|
175859
|
+
reproductions: exports_external.array(exports_external.object({
|
|
175700
175860
|
environmentId: ids.environment,
|
|
175701
|
-
|
|
175702
|
-
|
|
175703
|
-
|
|
175861
|
+
firstReproducedInRunId: ids.run,
|
|
175862
|
+
flowId: ids.flow,
|
|
175863
|
+
isActive: exports_external.boolean(),
|
|
175864
|
+
lastReproducedInRunId: ids.run
|
|
175865
|
+
})).describe("The environments, flows, and runs where the issue was reproduced."),
|
|
175866
|
+
status: issueStatusSchema,
|
|
175867
|
+
type: publicIssueTypeSchema
|
|
175868
|
+
}, { urlFieldDescription: "Absolute URL of the issue page." });
|
|
175869
|
+
var makeFindIssuesContract = (ids) => {
|
|
175870
|
+
const input = exports_external.object({
|
|
175871
|
+
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 100 }),
|
|
175872
|
+
statuses: exports_external.array(issueStatusSchema).min(1).default([...openIssueStatuses]).describe("Issue statuses to include. Defaults to pending, in-progress, and paused issues."),
|
|
175873
|
+
type: publicIssueTypeSchema.describe("The issue type to include: bug report, maintenance report, or coverage request.")
|
|
175874
|
+
});
|
|
175875
|
+
const output = exports_external.object({
|
|
175876
|
+
issues: exports_external.array(makeIssueResourceSchema(ids)).describe("The team's bug reports, maintenance reports, or coverage requests, newest first."),
|
|
175877
|
+
nextCursor: nextCursorSchema
|
|
175878
|
+
});
|
|
175879
|
+
return {
|
|
175880
|
+
description: "List the team's bug reports, maintenance reports, or coverage requests, newest first.",
|
|
175881
|
+
input,
|
|
175882
|
+
kind: "read",
|
|
175883
|
+
name: "issue.find",
|
|
175884
|
+
output
|
|
175885
|
+
};
|
|
175886
|
+
};
|
|
175887
|
+
var makeGetIssueContract = (ids) => {
|
|
175888
|
+
const input = exports_external.object({
|
|
175889
|
+
issueId: ids.issue
|
|
175890
|
+
});
|
|
175891
|
+
const output = exports_external.object({
|
|
175892
|
+
issue: makeIssueResourceSchema(ids)
|
|
175704
175893
|
});
|
|
175705
|
-
const output = exports_external.object({ runId: ids.run });
|
|
175706
175894
|
return {
|
|
175707
|
-
description: "
|
|
175895
|
+
description: "Get an issue by id.",
|
|
175896
|
+
input,
|
|
175897
|
+
kind: "read",
|
|
175898
|
+
name: "issue.get",
|
|
175899
|
+
output
|
|
175900
|
+
};
|
|
175901
|
+
};
|
|
175902
|
+
|
|
175903
|
+
// node_modules/@qawolf/api-contracts/dist/v1/issue/create.js
|
|
175904
|
+
var makeCreateIssueContract = (ids) => {
|
|
175905
|
+
const commonFields = {
|
|
175906
|
+
description: exports_external.string().optional().describe("The issue description as plain text; each line becomes a paragraph. Markdown is not parsed."),
|
|
175907
|
+
name: exports_external.string().trim().min(1).max(255),
|
|
175908
|
+
priority: issuePrioritySchema.optional().describe('Defaults to "unprioritized".')
|
|
175909
|
+
};
|
|
175910
|
+
const input = exports_external.discriminatedUnion("type", [
|
|
175911
|
+
exports_external.object({
|
|
175912
|
+
...commonFields,
|
|
175913
|
+
type: exports_external.literal("bug")
|
|
175914
|
+
}),
|
|
175915
|
+
exports_external.object({
|
|
175916
|
+
...commonFields,
|
|
175917
|
+
estimatedDueDate: exports_external.iso.date().optional().describe("The calendar date the coverage is estimated to be complete, as YYYY-MM-DD."),
|
|
175918
|
+
type: exports_external.literal("coverageRequest")
|
|
175919
|
+
})
|
|
175920
|
+
]);
|
|
175921
|
+
const output = exports_external.object({
|
|
175922
|
+
issue: makeIssueResourceSchema(ids)
|
|
175923
|
+
});
|
|
175924
|
+
return {
|
|
175925
|
+
description: "Create a bug or coverage request issue for the caller's team. " + "Maintenance issues cannot be created through the public API.",
|
|
175708
175926
|
input,
|
|
175709
175927
|
kind: "write",
|
|
175710
|
-
name: "
|
|
175928
|
+
name: "issue.create",
|
|
175711
175929
|
output
|
|
175712
175930
|
};
|
|
175713
175931
|
};
|
|
175714
175932
|
|
|
175715
|
-
// node_modules/@qawolf/api-contracts/dist/v1/
|
|
175716
|
-
var
|
|
175717
|
-
|
|
175718
|
-
|
|
175719
|
-
|
|
175720
|
-
|
|
175721
|
-
|
|
175722
|
-
|
|
175933
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/summary.js
|
|
175934
|
+
var makeRunSummaryFields = (ids) => ({
|
|
175935
|
+
completedAt: exports_external.iso.datetime().optional().describe("When the run finished executing. Absent while queued or running, and also absent for a terminal run that never completed execution (e.g. every flow was canceled or skipped)."),
|
|
175936
|
+
createdAt: exports_external.iso.datetime(),
|
|
175937
|
+
git: exports_external.object({
|
|
175938
|
+
branch: exports_external.string().optional(),
|
|
175939
|
+
commitMessage: exports_external.string().optional(),
|
|
175940
|
+
commitSha: exports_external.string().optional(),
|
|
175941
|
+
commitUrl: exports_external.string().optional().describe("Link to the commit on the code host.")
|
|
175942
|
+
}).describe("The branch and commit under test. The fields are present when a deploy notification started the run, and absent for runs started another way, for example manually or with run.create."),
|
|
175943
|
+
runId: ids.run,
|
|
175944
|
+
status: exports_external.enum(["queued", "running", "passed", "failed", "canceled"]).describe("Whole-run status. Terminal statuses are passed, failed, and canceled; poll until one is reached.")
|
|
175945
|
+
});
|
|
175946
|
+
|
|
175947
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/find.js
|
|
175948
|
+
var makeFindRunsContract = (ids) => {
|
|
175949
|
+
const input = exports_external.object({
|
|
175950
|
+
createdAfter: exports_external.iso.datetime({ offset: true }).optional().describe("Only include runs created after this instant, e.g. the runs since a deploy."),
|
|
175951
|
+
...makePaginationInputFields({ defaultLimit: 20, maxLimit: 100 }),
|
|
175952
|
+
environmentId: ids.environment
|
|
175953
|
+
});
|
|
175954
|
+
const output = exports_external.object({
|
|
175955
|
+
nextCursor: nextCursorSchema,
|
|
175956
|
+
runs: exports_external.array(resource(makeRunSummaryFields(ids), {
|
|
175957
|
+
urlFieldDescription: "Absolute URL of the run page."
|
|
175958
|
+
})).describe("The environment's runs, newest first. Per-flow results are available via run.get.")
|
|
175959
|
+
});
|
|
175960
|
+
return {
|
|
175961
|
+
description: "List an environment's recent runs, newest first.",
|
|
175962
|
+
input,
|
|
175963
|
+
kind: "read",
|
|
175964
|
+
name: "run.find",
|
|
175965
|
+
output
|
|
175966
|
+
};
|
|
175967
|
+
};
|
|
175968
|
+
|
|
175969
|
+
// node_modules/@qawolf/api-contracts/dist/v1/run/get.js
|
|
175970
|
+
var makeGetRunContract = (ids) => {
|
|
175971
|
+
const input = exports_external.object({
|
|
175972
|
+
runId: ids.run
|
|
175973
|
+
});
|
|
175974
|
+
const failedFlow = exports_external.object({
|
|
175975
|
+
failure: exports_external.object({
|
|
175976
|
+
error: exports_external.string()
|
|
175977
|
+
}),
|
|
175978
|
+
flowId: ids.flow,
|
|
175979
|
+
name: exports_external.string(),
|
|
175980
|
+
status: exports_external.literal("failed")
|
|
175981
|
+
});
|
|
175982
|
+
const nonFailedFlow = exports_external.object({
|
|
175983
|
+
flowId: ids.flow,
|
|
175984
|
+
name: exports_external.string(),
|
|
175985
|
+
status: exports_external.enum(["queued", "running", "passed", "canceled"])
|
|
175986
|
+
});
|
|
175987
|
+
const flows = exports_external.array(exports_external.discriminatedUnion("status", [failedFlow, nonFailedFlow])).describe("The run's flows, ordered alphabetically by name.");
|
|
175988
|
+
const output = resource({
|
|
175989
|
+
...makeRunSummaryFields(ids),
|
|
175990
|
+
flows
|
|
175991
|
+
}, { urlFieldDescription: "Absolute URL of the run page." });
|
|
175992
|
+
return {
|
|
175993
|
+
description: "Get a run's status, per-flow results, and links.",
|
|
175994
|
+
input,
|
|
175995
|
+
kind: "read",
|
|
175996
|
+
name: "run.get",
|
|
175997
|
+
output
|
|
175998
|
+
};
|
|
175999
|
+
};
|
|
176000
|
+
|
|
176001
|
+
// node_modules/@qawolf/api-contracts/dist/v1/tag/index.js
|
|
176002
|
+
var makeTagResourceSchema = () => resource({
|
|
176003
|
+
color: exports_external.string().regex(/^#[0-9a-fA-F]{6}$/).describe("The tag's display color as a hex code, assigned by QA Wolf."),
|
|
176004
|
+
name: exports_external.string()
|
|
176005
|
+
}, {
|
|
176006
|
+
urlFieldDescription: "Absolute URL of the team's tag settings page."
|
|
175723
176007
|
});
|
|
176008
|
+
var makeCreateTagContract = () => {
|
|
176009
|
+
const input = exports_external.object({
|
|
176010
|
+
name: exports_external.string().trim().min(1).max(255).refine((name) => !name.includes(","), "Tag names cannot include commas.")
|
|
176011
|
+
});
|
|
176012
|
+
const output = makeTagResourceSchema();
|
|
176013
|
+
return {
|
|
176014
|
+
description: "Create a tag on the caller's team. Tags select flows in run.create.",
|
|
176015
|
+
input,
|
|
176016
|
+
kind: "write",
|
|
176017
|
+
name: "tag.create",
|
|
176018
|
+
output
|
|
176019
|
+
};
|
|
176020
|
+
};
|
|
176021
|
+
|
|
176022
|
+
// node_modules/@qawolf/api-contracts/dist/v1/index.js
|
|
176023
|
+
var makeContractsV1 = (ids) => {
|
|
176024
|
+
const resolvedIds = { ...defaultIdSchemas, ...ids };
|
|
176025
|
+
return {
|
|
176026
|
+
automate: makeAutomateContract(resolvedIds),
|
|
176027
|
+
environment: {
|
|
176028
|
+
create: makeCreateEnvironmentContract(resolvedIds),
|
|
176029
|
+
get: makeGetEnvironmentContract(resolvedIds),
|
|
176030
|
+
listVariableNames: makeListEnvironmentVariableNamesContract(resolvedIds)
|
|
176031
|
+
},
|
|
176032
|
+
flow: {
|
|
176033
|
+
list: makeListFlowsContract(resolvedIds)
|
|
176034
|
+
},
|
|
176035
|
+
issue: {
|
|
176036
|
+
create: makeCreateIssueContract(resolvedIds),
|
|
176037
|
+
find: makeFindIssuesContract(resolvedIds),
|
|
176038
|
+
get: makeGetIssueContract(resolvedIds)
|
|
176039
|
+
},
|
|
176040
|
+
run: {
|
|
176041
|
+
create: makeCreateRunContract(resolvedIds),
|
|
176042
|
+
find: makeFindRunsContract(resolvedIds),
|
|
176043
|
+
get: makeGetRunContract(resolvedIds)
|
|
176044
|
+
},
|
|
176045
|
+
tag: {
|
|
176046
|
+
create: makeCreateTagContract()
|
|
176047
|
+
}
|
|
176048
|
+
};
|
|
176049
|
+
};
|
|
175724
176050
|
var publicContractsV1 = makeContractsV1(defaultIdSchemas);
|
|
175725
176051
|
|
|
175726
176052
|
// src/domains/flows/listRemote.ts
|
|
@@ -179728,8 +180054,7 @@ async function dispatchFlow({
|
|
|
179728
180054
|
run = { ...run, manifest: stamp };
|
|
179729
180055
|
const durationMs = deps.now() - flowStart;
|
|
179730
180056
|
const outcome = run.passed ? "pass" : "fail";
|
|
179731
|
-
const
|
|
179732
|
-
const attempt = pluralize(attempts, "attempt");
|
|
180057
|
+
const attempt = pluralize(run.attempts, "attempt");
|
|
179733
180058
|
deps.logger?.info(`${outcome}: ${flow.name} (${durationMs}ms, ${attempt})`);
|
|
179734
180059
|
return { run, durationMs };
|
|
179735
180060
|
}
|
|
@@ -180059,7 +180384,7 @@ async function defaultRunWebFlowDeps(cwd = process.cwd(), signals) {
|
|
|
180059
180384
|
|
|
180060
180385
|
// src/domains/runtimeEnv/prepareRunDir.ts
|
|
180061
180386
|
import { createHash as createHash3 } from "node:crypto";
|
|
180062
|
-
import { basename as basename4, dirname as dirname10, join as
|
|
180387
|
+
import { basename as basename4, dirname as dirname10, join as join29, resolve as resolve8, sep as sep4 } from "node:path";
|
|
180063
180388
|
|
|
180064
180389
|
// src/shell/copyDir.ts
|
|
180065
180390
|
import { cp, readdir as readdir2 } from "node:fs/promises";
|
|
@@ -180100,6 +180425,9 @@ async function readPackageJson(pkgPath, fs3) {
|
|
|
180100
180425
|
}
|
|
180101
180426
|
|
|
180102
180427
|
// src/domains/runtimeEnv/innerHop.ts
|
|
180428
|
+
import { join as join27 } from "node:path";
|
|
180429
|
+
|
|
180430
|
+
// src/domains/runtimeEnv/linkPinnedPackages.ts
|
|
180103
180431
|
import { dirname as dirname8, join as join26 } from "node:path";
|
|
180104
180432
|
|
|
180105
180433
|
// src/domains/runtimeEnv/symlinkDir.ts
|
|
@@ -180109,25 +180437,35 @@ async function createDirSymlink(source, target) {
|
|
|
180109
180437
|
await symlink(source, target, linkType);
|
|
180110
180438
|
}
|
|
180111
180439
|
|
|
180112
|
-
// src/domains/runtimeEnv/
|
|
180113
|
-
async function
|
|
180114
|
-
const { depsRoot,
|
|
180440
|
+
// src/domains/runtimeEnv/linkPinnedPackages.ts
|
|
180441
|
+
async function linkPinnedPackages(args) {
|
|
180442
|
+
const { depsRoot, nodeModulesDir, fs: fs3 } = args;
|
|
180115
180443
|
const managedModules = join26(depsRoot, "node_modules");
|
|
180116
|
-
|
|
180117
|
-
await fs3.mkdir(innerModules, { recursive: true });
|
|
180444
|
+
await fs3.mkdir(nodeModulesDir, { recursive: true });
|
|
180118
180445
|
for (const { name } of pinnedPackages) {
|
|
180119
180446
|
const segments = name.split("/");
|
|
180120
|
-
const target = join26(
|
|
180447
|
+
const target = join26(nodeModulesDir, ...segments);
|
|
180121
180448
|
if (segments.length > 1) {
|
|
180122
180449
|
await fs3.mkdir(dirname8(target), { recursive: true });
|
|
180123
180450
|
}
|
|
180451
|
+
await fs3.rm(target, { recursive: true, force: true });
|
|
180124
180452
|
await createDirSymlink(join26(managedModules, ...segments), target);
|
|
180125
180453
|
}
|
|
180126
180454
|
}
|
|
180127
180455
|
|
|
180456
|
+
// src/domains/runtimeEnv/innerHop.ts
|
|
180457
|
+
async function populateInnerHop(args) {
|
|
180458
|
+
const { depsRoot, execDir, fs: fs3 } = args;
|
|
180459
|
+
await linkPinnedPackages({
|
|
180460
|
+
depsRoot,
|
|
180461
|
+
nodeModulesDir: join27(execDir, "node_modules"),
|
|
180462
|
+
fs: fs3
|
|
180463
|
+
});
|
|
180464
|
+
}
|
|
180465
|
+
|
|
180128
180466
|
// src/domains/runtimeEnv/outerHop.ts
|
|
180129
180467
|
import { lstat } from "node:fs/promises";
|
|
180130
|
-
import { dirname as dirname9, join as
|
|
180468
|
+
import { dirname as dirname9, join as join28 } from "node:path";
|
|
180131
180469
|
var executorPackageNames = new Set(pinnedPackages.map((p3) => p3.name));
|
|
180132
180470
|
async function populateOuterHop(args) {
|
|
180133
180471
|
const { projectDir, runDir, fs: fs3 } = args;
|
|
@@ -180137,23 +180475,28 @@ async function populateOuterHop(args) {
|
|
|
180137
180475
|
const depNames = Object.keys(installableDeps);
|
|
180138
180476
|
const { satisfying, rejected } = await findSatisfyingNodeModulesDir(projectDir, depNames, fs3);
|
|
180139
180477
|
if (satisfying !== undefined) {
|
|
180140
|
-
await createDirSymlink(satisfying,
|
|
180478
|
+
await createDirSymlink(satisfying, join28(runDir, "node_modules"));
|
|
180141
180479
|
return { mode: "symlink", nodeModulesDir: satisfying };
|
|
180142
180480
|
}
|
|
180143
180481
|
if (depNames.length === 0)
|
|
180144
180482
|
return { mode: "none" };
|
|
180145
180483
|
args.onInstallStart?.(depNames.length);
|
|
180146
180484
|
await installOuterDeps(runDir, installableDeps, fs3, args.install ?? spawnNpmInstall);
|
|
180485
|
+
await linkPinnedPackages({
|
|
180486
|
+
depsRoot: args.depsRoot,
|
|
180487
|
+
nodeModulesDir: join28(runDir, "node_modules"),
|
|
180488
|
+
fs: fs3
|
|
180489
|
+
});
|
|
180147
180490
|
return { mode: "install", depCount: depNames.length, rejected };
|
|
180148
180491
|
}
|
|
180149
180492
|
async function findSatisfyingNodeModulesDir(startDir, depNames, fs3) {
|
|
180150
180493
|
const rejected = [];
|
|
180151
180494
|
let dir = startDir;
|
|
180152
180495
|
while (true) {
|
|
180153
|
-
const candidate =
|
|
180496
|
+
const candidate = join28(dir, "node_modules");
|
|
180154
180497
|
const stats = await lstatSafe(candidate);
|
|
180155
180498
|
if (stats !== undefined && stats.isDirectory() && !stats.isSymbolicLink()) {
|
|
180156
|
-
const missing = depNames.filter((name) => !fs3.existsSync(
|
|
180499
|
+
const missing = depNames.filter((name) => !fs3.existsSync(join28(candidate, name, "package.json")));
|
|
180157
180500
|
if (missing.length === 0)
|
|
180158
180501
|
return { satisfying: candidate, rejected };
|
|
180159
180502
|
rejected.push({ dir: candidate, missing });
|
|
@@ -180174,7 +180517,7 @@ async function lstatSafe(path3) {
|
|
|
180174
180517
|
async function readInstallableDeps(projectDir, fs3) {
|
|
180175
180518
|
let content;
|
|
180176
180519
|
try {
|
|
180177
|
-
content = await fs3.readFile(
|
|
180520
|
+
content = await fs3.readFile(join28(projectDir, "package.json"));
|
|
180178
180521
|
} catch {
|
|
180179
180522
|
return {};
|
|
180180
180523
|
}
|
|
@@ -180192,8 +180535,8 @@ async function readInstallableDeps(projectDir, fs3) {
|
|
|
180192
180535
|
return Object.fromEntries(Object.entries(rawDeps).filter(([name]) => !executorPackageNames.has(name)).filter((entry) => typeof entry[1] === "string"));
|
|
180193
180536
|
}
|
|
180194
180537
|
async function installOuterDeps(runDir, deps, fs3, install) {
|
|
180195
|
-
await fs3.writeFile(
|
|
180196
|
-
await fs3.writeFile(
|
|
180538
|
+
await fs3.writeFile(join28(runDir, "package.json"), JSON.stringify({ name: "qawolf-run", private: true, dependencies: deps }, undefined, 2));
|
|
180539
|
+
await fs3.writeFile(join28(runDir, ".npmrc"), `@qawolf:registry=https://registry.npmjs.org/
|
|
180197
180540
|
`);
|
|
180198
180541
|
const result = await install(runDir);
|
|
180199
180542
|
if (result.exitCode !== 0) {
|
|
@@ -180207,10 +180550,10 @@ async function prepareRunDir(args) {
|
|
|
180207
180550
|
const { files, projectDir, depsRoot, runRoot } = args;
|
|
180208
180551
|
const fs3 = args.fs ?? makeDefaultFs();
|
|
180209
180552
|
const runId = buildRunId(projectDir, files);
|
|
180210
|
-
const runDir =
|
|
180553
|
+
const runDir = join29(runRoot, runId);
|
|
180211
180554
|
await fs3.rm(runDir, { recursive: true, force: true });
|
|
180212
180555
|
await fs3.mkdir(runDir, { recursive: true });
|
|
180213
|
-
const execDir =
|
|
180556
|
+
const execDir = join29(runDir, "exec");
|
|
180214
180557
|
await fs3.mkdir(execDir, { recursive: true });
|
|
180215
180558
|
await populateInnerHop({ depsRoot, execDir, fs: fs3 });
|
|
180216
180559
|
const stagedFiles = await stageFlowFiles({ files, projectDir, execDir, fs: fs3 });
|
|
@@ -180220,6 +180563,7 @@ async function prepareRunDir(args) {
|
|
|
180220
180563
|
const outerHop = await populateOuterHop({
|
|
180221
180564
|
projectDir,
|
|
180222
180565
|
runDir,
|
|
180566
|
+
depsRoot,
|
|
180223
180567
|
fs: fs3,
|
|
180224
180568
|
...args.onInstallStart !== undefined ? { onInstallStart: args.onInstallStart } : {}
|
|
180225
180569
|
});
|
|
@@ -180247,37 +180591,37 @@ async function stageFlowFiles(args) {
|
|
|
180247
180591
|
if (files.length > 1) {
|
|
180248
180592
|
return Promise.all(files.map(async (f3) => {
|
|
180249
180593
|
const dirHash = createHash3("sha256").update(dirname10(f3)).digest("hex").slice(0, 8);
|
|
180250
|
-
const subDir =
|
|
180594
|
+
const subDir = join29(execDir, dirHash);
|
|
180251
180595
|
await fs3.mkdir(subDir, { recursive: true });
|
|
180252
|
-
const dest =
|
|
180596
|
+
const dest = join29(subDir, basename4(f3));
|
|
180253
180597
|
await fs3.copyFile(f3, dest);
|
|
180254
180598
|
return dest;
|
|
180255
180599
|
}));
|
|
180256
180600
|
}
|
|
180257
|
-
await Promise.all(files.map((f3) => fs3.copyFile(f3,
|
|
180258
|
-
return files.map((f3) =>
|
|
180601
|
+
await Promise.all(files.map((f3) => fs3.copyFile(f3, join29(execDir, basename4(f3)))));
|
|
180602
|
+
return files.map((f3) => join29(execDir, basename4(f3)));
|
|
180259
180603
|
}
|
|
180260
180604
|
function remapPath(file2, projectDir, execDir) {
|
|
180261
180605
|
if (file2 === projectDir)
|
|
180262
180606
|
return execDir;
|
|
180263
180607
|
if (file2.startsWith(projectDir + sep4)) {
|
|
180264
|
-
return
|
|
180608
|
+
return join29(execDir, file2.slice(projectDir.length + 1));
|
|
180265
180609
|
}
|
|
180266
180610
|
return file2;
|
|
180267
180611
|
}
|
|
180268
180612
|
|
|
180269
180613
|
// src/shell/testkit.ts
|
|
180270
|
-
import { join as
|
|
180614
|
+
import { join as join30 } from "node:path";
|
|
180271
180615
|
function notAvailableLocally(name) {
|
|
180272
180616
|
throw new Error(runnerMessages.notAvailableLocally(name));
|
|
180273
180617
|
}
|
|
180274
180618
|
async function loadSdkDeps(cwd) {
|
|
180275
|
-
const testkitDir =
|
|
180619
|
+
const testkitDir = join30(cwd, "node_modules", "@qawolf", "testkit");
|
|
180276
180620
|
try {
|
|
180277
180621
|
const clientMod = await import(resolveFromEnvDir(cwd, "@qawolf/testkit/client"));
|
|
180278
180622
|
if (typeof clientMod.createTestkitClient !== "function")
|
|
180279
180623
|
throw new Error("createTestkitClient is not a function");
|
|
180280
|
-
const scopeMod = await import(
|
|
180624
|
+
const scopeMod = await import(join30(testkitDir, "dist", "clientScope.js"));
|
|
180281
180625
|
if (typeof scopeMod.configureTestkitClient !== "function")
|
|
180282
180626
|
throw new Error("configureTestkitClient is not a function");
|
|
180283
180627
|
return {
|
|
@@ -180334,9 +180678,9 @@ async function registerLazyEmailsClient(getInbox, cwd, deps) {
|
|
|
180334
180678
|
}
|
|
180335
180679
|
|
|
180336
180680
|
// src/commands/flows/envVarDeps.ts
|
|
180337
|
-
import { join as
|
|
180681
|
+
import { join as join31 } from "node:path";
|
|
180338
180682
|
function envFilePath(envDir) {
|
|
180339
|
-
return
|
|
180683
|
+
return join31(envDir, ".env");
|
|
180340
180684
|
}
|
|
180341
180685
|
async function readEnv(envDir, fs3) {
|
|
180342
180686
|
try {
|
|
@@ -180445,7 +180789,7 @@ async function createFlowRuntimeDeps({
|
|
|
180445
180789
|
}
|
|
180446
180790
|
|
|
180447
180791
|
// src/shell/appium/createAppiumServer.ts
|
|
180448
|
-
import { join as
|
|
180792
|
+
import { join as join32 } from "node:path";
|
|
180449
180793
|
|
|
180450
180794
|
// src/shell/appium/spawnAppium.ts
|
|
180451
180795
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -180520,7 +180864,7 @@ async function createAppiumServer(envDir, signals, params) {
|
|
|
180520
180864
|
const spawnFn = params?.deps?.spawn ?? defaultSpawnAppium;
|
|
180521
180865
|
const findFreePortFn = params?.deps?.findFreePort ?? findFreePort;
|
|
180522
180866
|
const resolveAppiumBinFn = params?.deps?.resolveAppiumBin ?? resolveAppiumBin;
|
|
180523
|
-
const appiumHome2 = params?.options?.appiumHome ??
|
|
180867
|
+
const appiumHome2 = params?.options?.appiumHome ?? join32(envPaths("qawolf").data, "appium");
|
|
180524
180868
|
const timeoutMs3 = params?.options?.startTimeoutMs ?? defaultStartTimeoutMs;
|
|
180525
180869
|
const bin = resolveAppiumBinFn(envDir);
|
|
180526
180870
|
const port = await findFreePortFn();
|
|
@@ -180892,7 +181236,7 @@ async function installBrowserList(ctx, browsers, deps) {
|
|
|
180892
181236
|
await ctx.ui.withProgress(browsers.map((browser) => ({
|
|
180893
181237
|
message: installMessages.installingBrowser(browser),
|
|
180894
181238
|
task: async () => {
|
|
180895
|
-
const args = buildArgs(browser, deps.platform);
|
|
181239
|
+
const args = buildArgs(browser, deps.platform, deps.browserDeps);
|
|
180896
181240
|
const result = await deps.spawn(deps.playwrightCliPath, args);
|
|
180897
181241
|
if (result.exitCode !== 0) {
|
|
180898
181242
|
throw new Error(formatError2(browser, result));
|
|
@@ -180912,6 +181256,7 @@ async function installBrowsers(ctx, pattern, deps) {
|
|
|
180912
181256
|
await installBrowserList(ctx, browsers, {
|
|
180913
181257
|
spawn: deps.spawn,
|
|
180914
181258
|
platform: deps.platform,
|
|
181259
|
+
browserDeps: deps.browserDeps,
|
|
180915
181260
|
playwrightCliPath
|
|
180916
181261
|
});
|
|
180917
181262
|
}
|
|
@@ -180926,8 +181271,8 @@ async function collectBrowsers(files, peekFlowMeta) {
|
|
|
180926
181271
|
}
|
|
180927
181272
|
return [...seen].sort();
|
|
180928
181273
|
}
|
|
180929
|
-
function buildArgs(browser, platform) {
|
|
180930
|
-
return platform === "linux" ? ["install", "--with-deps", browser] : ["install", browser];
|
|
181274
|
+
function buildArgs(browser, platform, browserDeps) {
|
|
181275
|
+
return platform === "linux" && browserDeps ? ["install", "--with-deps", browser] : ["install", browser];
|
|
180931
181276
|
}
|
|
180932
181277
|
function formatError2(browser, result) {
|
|
180933
181278
|
if (result.exitCode < 0) {
|
|
@@ -180939,7 +181284,7 @@ function formatError2(browser, result) {
|
|
|
180939
181284
|
}
|
|
180940
181285
|
|
|
180941
181286
|
// src/domains/runner/runAndroidFlow.ts
|
|
180942
|
-
import
|
|
181287
|
+
import path12 from "node:path";
|
|
180943
181288
|
|
|
180944
181289
|
// src/shell/appium/createAndroidLaunchContext.ts
|
|
180945
181290
|
import { writeFile } from "node:fs/promises";
|
|
@@ -181285,66 +181630,6 @@ function createRunner({
|
|
|
181285
181630
|
}
|
|
181286
181631
|
|
|
181287
181632
|
// src/domains/runner/runWebFlowUtils.ts
|
|
181288
|
-
import crypto from "node:crypto";
|
|
181289
|
-
import os6 from "node:os";
|
|
181290
|
-
import path12 from "node:path";
|
|
181291
|
-
function createLaunch({
|
|
181292
|
-
browsers,
|
|
181293
|
-
contextSetup,
|
|
181294
|
-
launchBrowserOpts,
|
|
181295
|
-
signals,
|
|
181296
|
-
timeout,
|
|
181297
|
-
traceMode = "off",
|
|
181298
|
-
tracePath
|
|
181299
|
-
}) {
|
|
181300
|
-
const openBrowsers = [];
|
|
181301
|
-
const openContexts = [];
|
|
181302
|
-
const tracingEnabled = traceMode !== "off";
|
|
181303
|
-
let closed = false;
|
|
181304
|
-
const closeAll = async () => {
|
|
181305
|
-
if (closed)
|
|
181306
|
-
return;
|
|
181307
|
-
closed = true;
|
|
181308
|
-
if (tracingEnabled && tracePath !== undefined) {
|
|
181309
|
-
await Promise.allSettled(openContexts.map((c2) => c2.tracing.stop({ path: tracePath })));
|
|
181310
|
-
}
|
|
181311
|
-
await Promise.allSettled(openContexts.map((c2) => c2.close()));
|
|
181312
|
-
await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
|
|
181313
|
-
};
|
|
181314
|
-
const unregister = signals.register(closeAll);
|
|
181315
|
-
const trackContext = async (ctx) => {
|
|
181316
|
-
openContexts.push(ctx);
|
|
181317
|
-
if (tracingEnabled) {
|
|
181318
|
-
await ctx.tracing.start({ screenshots: true, snapshots: true });
|
|
181319
|
-
}
|
|
181320
|
-
};
|
|
181321
|
-
const launch = async (launchOpts) => {
|
|
181322
|
-
const browserName = normalizeBrowserName(launchOpts?.browser);
|
|
181323
|
-
const bt2 = browsers[browserName];
|
|
181324
|
-
if (launchOpts?.persistentContext === true) {
|
|
181325
|
-
const userDataDir = launchOpts.userDataDir ?? path12.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
|
|
181326
|
-
const context2 = await bt2.launchPersistentContext(userDataDir, {
|
|
181327
|
-
...launchBrowserOpts,
|
|
181328
|
-
...contextSetup
|
|
181329
|
-
});
|
|
181330
|
-
context2.setDefaultTimeout(timeout);
|
|
181331
|
-
await trackContext(context2);
|
|
181332
|
-
const page = await context2.newPage();
|
|
181333
|
-
return { browserType: browserName, context: context2, page };
|
|
181334
|
-
}
|
|
181335
|
-
const browser = await bt2.launch(launchBrowserOpts);
|
|
181336
|
-
openBrowsers.push(browser);
|
|
181337
|
-
const context = await browser.newContext(contextSetup);
|
|
181338
|
-
context.setDefaultTimeout(timeout);
|
|
181339
|
-
await trackContext(context);
|
|
181340
|
-
return { browser, browserType: browserName, context };
|
|
181341
|
-
};
|
|
181342
|
-
const cleanup = async () => {
|
|
181343
|
-
unregister();
|
|
181344
|
-
await closeAll();
|
|
181345
|
-
};
|
|
181346
|
-
return { launch, cleanup };
|
|
181347
|
-
}
|
|
181348
181633
|
function normalizeBrowserName(browser) {
|
|
181349
181634
|
if (browser === "chrome" || browser === "msedge")
|
|
181350
181635
|
return "chromium";
|
|
@@ -181384,7 +181669,7 @@ async function runAndroidFlow({
|
|
|
181384
181669
|
throw new Error("runAndroidFlow: legacy flow functions are not supported; use flow() from @qawolf/flows/android");
|
|
181385
181670
|
}
|
|
181386
181671
|
} catch (err) {
|
|
181387
|
-
const flowName2 =
|
|
181672
|
+
const flowName2 = path12.basename(flowPath, path12.extname(flowPath));
|
|
181388
181673
|
return {
|
|
181389
181674
|
passed: false,
|
|
181390
181675
|
testCounts: { passed: 0, total: 0 },
|
|
@@ -181452,13 +181737,13 @@ async function runAndroidFlow({
|
|
|
181452
181737
|
}
|
|
181453
181738
|
|
|
181454
181739
|
// src/domains/runner/runWebFlow.ts
|
|
181455
|
-
import
|
|
181740
|
+
import path17 from "node:path";
|
|
181456
181741
|
|
|
181457
181742
|
// src/domains/runner/initFlowRuntime.ts
|
|
181458
|
-
import
|
|
181743
|
+
import path13 from "node:path";
|
|
181459
181744
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
181460
181745
|
async function runnerPathInDir(dir, fs9) {
|
|
181461
|
-
const pkgPath =
|
|
181746
|
+
const pkgPath = path13.join(dir, "node_modules", "@qawolf", "flows", "package.json");
|
|
181462
181747
|
try {
|
|
181463
181748
|
const pkg = JSON.parse(await fs9.readFile(pkgPath));
|
|
181464
181749
|
const entry = pkg.exports?.["./_runner"];
|
|
@@ -181466,7 +181751,7 @@ async function runnerPathInDir(dir, fs9) {
|
|
|
181466
181751
|
if (typeof importPath !== "string") {
|
|
181467
181752
|
throw new Error(`@qawolf/flows at ${pkgPath} does not export "./_runner" with an "import" condition`);
|
|
181468
181753
|
}
|
|
181469
|
-
return
|
|
181754
|
+
return path13.resolve(path13.dirname(pkgPath), importPath);
|
|
181470
181755
|
} catch (err) {
|
|
181471
181756
|
if (!isNoEntError(err))
|
|
181472
181757
|
throw err;
|
|
@@ -181474,12 +181759,12 @@ async function runnerPathInDir(dir, fs9) {
|
|
|
181474
181759
|
}
|
|
181475
181760
|
}
|
|
181476
181761
|
async function findFlowsRunnerPath(flowPath, fs9) {
|
|
181477
|
-
let dir =
|
|
181762
|
+
let dir = path13.dirname(flowPath);
|
|
181478
181763
|
while (true) {
|
|
181479
181764
|
const result = await runnerPathInDir(dir, fs9);
|
|
181480
181765
|
if (result !== undefined)
|
|
181481
181766
|
return result;
|
|
181482
|
-
const parent2 =
|
|
181767
|
+
const parent2 = path13.dirname(dir);
|
|
181483
181768
|
if (parent2 === dir)
|
|
181484
181769
|
throw new Error(`@qawolf/flows not found in node_modules above: ${flowPath}`);
|
|
181485
181770
|
dir = parent2;
|
|
@@ -181511,7 +181796,7 @@ async function doInit(flowPath, timeout, fs9, depsRoot) {
|
|
|
181511
181796
|
});
|
|
181512
181797
|
}
|
|
181513
181798
|
function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
|
|
181514
|
-
const startDir =
|
|
181799
|
+
const startDir = path13.dirname(flowPath);
|
|
181515
181800
|
let p3 = initCache.get(startDir);
|
|
181516
181801
|
if (!p3) {
|
|
181517
181802
|
p3 = doInit(flowPath, options.timeout, fs9, options.depsRoot);
|
|
@@ -181520,13 +181805,137 @@ function initFlowRuntime(flowPath, options, fs9 = makeDefaultFs()) {
|
|
|
181520
181805
|
return p3;
|
|
181521
181806
|
}
|
|
181522
181807
|
|
|
181523
|
-
// src/domains/runner/
|
|
181808
|
+
// src/domains/runner/createLaunch.ts
|
|
181809
|
+
import crypto from "node:crypto";
|
|
181810
|
+
import os6 from "node:os";
|
|
181524
181811
|
import path15 from "node:path";
|
|
181812
|
+
|
|
181813
|
+
// src/domains/runner/web/artifactPaths.ts
|
|
181814
|
+
import path14 from "node:path";
|
|
181815
|
+
function indexedArtifactPath(basePath, index2) {
|
|
181816
|
+
if (index2 === 0)
|
|
181817
|
+
return basePath;
|
|
181818
|
+
const ext = path14.extname(basePath);
|
|
181819
|
+
const base = path14.basename(basePath, ext);
|
|
181820
|
+
return path14.join(path14.dirname(basePath), `${base}-${index2 + 1}${ext}`);
|
|
181821
|
+
}
|
|
181822
|
+
function createContextArtifacts(contextSetup, tracePath) {
|
|
181823
|
+
let contextIndex = 0;
|
|
181824
|
+
const harPaths = [];
|
|
181825
|
+
const tracePaths = [];
|
|
181826
|
+
const nextSetup = (overrides = {}) => {
|
|
181827
|
+
const index2 = contextIndex;
|
|
181828
|
+
contextIndex += 1;
|
|
181829
|
+
const setup = { ...contextSetup, ...overrides };
|
|
181830
|
+
if (contextSetup.recordHar !== undefined && overrides.recordHar === undefined) {
|
|
181831
|
+
setup.recordHar = {
|
|
181832
|
+
...contextSetup.recordHar,
|
|
181833
|
+
path: indexedArtifactPath(contextSetup.recordHar.path, index2)
|
|
181834
|
+
};
|
|
181835
|
+
}
|
|
181836
|
+
if (setup.recordHar !== undefined)
|
|
181837
|
+
harPaths.push(setup.recordHar.path);
|
|
181838
|
+
return { setup, index: index2 };
|
|
181839
|
+
};
|
|
181840
|
+
const nextTracePath = (index2) => {
|
|
181841
|
+
if (tracePath === undefined)
|
|
181842
|
+
return;
|
|
181843
|
+
const contextTracePath = indexedArtifactPath(tracePath, index2);
|
|
181844
|
+
tracePaths.push(contextTracePath);
|
|
181845
|
+
return contextTracePath;
|
|
181846
|
+
};
|
|
181847
|
+
const artifactPaths = () => ({
|
|
181848
|
+
harPaths: [...harPaths],
|
|
181849
|
+
tracePaths: [...tracePaths]
|
|
181850
|
+
});
|
|
181851
|
+
return { nextSetup, nextTracePath, artifactPaths };
|
|
181852
|
+
}
|
|
181853
|
+
|
|
181854
|
+
// src/domains/runner/createLaunch.ts
|
|
181855
|
+
function createLaunch({
|
|
181856
|
+
browsers,
|
|
181857
|
+
contextSetup,
|
|
181858
|
+
launchBrowserOpts,
|
|
181859
|
+
signals,
|
|
181860
|
+
timeout,
|
|
181861
|
+
traceMode = "off",
|
|
181862
|
+
tracePath
|
|
181863
|
+
}) {
|
|
181864
|
+
const openBrowsers = [];
|
|
181865
|
+
const openContexts = [];
|
|
181866
|
+
const traceByContext = new Map;
|
|
181867
|
+
const tracingEnabled = traceMode !== "off";
|
|
181868
|
+
const { nextSetup, nextTracePath, artifactPaths } = createContextArtifacts(contextSetup, tracePath);
|
|
181869
|
+
let closePromise;
|
|
181870
|
+
const closeAll = () => {
|
|
181871
|
+
closePromise ??= (async () => {
|
|
181872
|
+
if (tracingEnabled) {
|
|
181873
|
+
await Promise.allSettled(openContexts.map((c2) => {
|
|
181874
|
+
const contextTracePath = traceByContext.get(c2);
|
|
181875
|
+
return contextTracePath === undefined ? Promise.resolve() : c2.tracing.stop({ path: contextTracePath });
|
|
181876
|
+
}));
|
|
181877
|
+
}
|
|
181878
|
+
await Promise.allSettled(openContexts.map((c2) => c2.close()));
|
|
181879
|
+
await Promise.allSettled(openBrowsers.map((b4) => b4.close()));
|
|
181880
|
+
})();
|
|
181881
|
+
return closePromise;
|
|
181882
|
+
};
|
|
181883
|
+
const unregister = signals.register(closeAll);
|
|
181884
|
+
const trackContext = async (ctx, index2) => {
|
|
181885
|
+
openContexts.push(ctx);
|
|
181886
|
+
if (tracingEnabled) {
|
|
181887
|
+
const contextTracePath = nextTracePath(index2);
|
|
181888
|
+
if (contextTracePath !== undefined) {
|
|
181889
|
+
traceByContext.set(ctx, contextTracePath);
|
|
181890
|
+
}
|
|
181891
|
+
await ctx.tracing.start({ screenshots: true, snapshots: true });
|
|
181892
|
+
}
|
|
181893
|
+
};
|
|
181894
|
+
const instrumentBrowser = (browser) => {
|
|
181895
|
+
const originalNewContext = browser.newContext.bind(browser);
|
|
181896
|
+
browser.newContext = async (opts) => {
|
|
181897
|
+
const { setup, index: index2 } = nextSetup(opts);
|
|
181898
|
+
const context = await originalNewContext(setup);
|
|
181899
|
+
context.setDefaultTimeout(timeout);
|
|
181900
|
+
await trackContext(context, index2);
|
|
181901
|
+
return context;
|
|
181902
|
+
};
|
|
181903
|
+
};
|
|
181904
|
+
const launch = async (launchOpts) => {
|
|
181905
|
+
const browserName = normalizeBrowserName(launchOpts?.browser);
|
|
181906
|
+
const bt2 = browsers[browserName];
|
|
181907
|
+
if (launchOpts?.persistentContext === true) {
|
|
181908
|
+
const userDataDir = launchOpts.userDataDir ?? path15.join(os6.tmpdir(), `qawolf-${crypto.randomUUID()}`);
|
|
181909
|
+
const { setup, index: index2 } = nextSetup();
|
|
181910
|
+
const context2 = await bt2.launchPersistentContext(userDataDir, {
|
|
181911
|
+
...launchBrowserOpts,
|
|
181912
|
+
...setup
|
|
181913
|
+
});
|
|
181914
|
+
context2.setDefaultTimeout(timeout);
|
|
181915
|
+
await trackContext(context2, index2);
|
|
181916
|
+
const page = await context2.newPage();
|
|
181917
|
+
return { browserType: browserName, context: context2, page };
|
|
181918
|
+
}
|
|
181919
|
+
const browser = await bt2.launch(launchBrowserOpts);
|
|
181920
|
+
openBrowsers.push(browser);
|
|
181921
|
+
instrumentBrowser(browser);
|
|
181922
|
+
const context = await browser.newContext({});
|
|
181923
|
+
return { browser, browserType: browserName, context };
|
|
181924
|
+
};
|
|
181925
|
+
const cleanup = async () => {
|
|
181926
|
+
unregister();
|
|
181927
|
+
await closeAll();
|
|
181928
|
+
};
|
|
181929
|
+
return { launch, cleanup, artifactPaths };
|
|
181930
|
+
}
|
|
181931
|
+
|
|
181932
|
+
// src/domains/runner/web/contextSetup.ts
|
|
181933
|
+
import path16 from "node:path";
|
|
181525
181934
|
function harFlowPath(outputDir, flowName) {
|
|
181526
|
-
return
|
|
181935
|
+
return path16.join(outputDir, "har", `${flowName}.har`);
|
|
181527
181936
|
}
|
|
181528
181937
|
function traceFlowPath(outputDir, flowName) {
|
|
181529
|
-
return
|
|
181938
|
+
return path16.join(outputDir, "trace", `${flowName}.zip`);
|
|
181530
181939
|
}
|
|
181531
181940
|
function buildHarContextOpts(harPath, harContent) {
|
|
181532
181941
|
return {
|
|
@@ -181542,7 +181951,7 @@ async function initHar(fs9, options, flowName) {
|
|
|
181542
181951
|
if (harMode === "off")
|
|
181543
181952
|
return { harMode, harPath: undefined };
|
|
181544
181953
|
const harPath = harFlowPath(options.outputDir, flowName);
|
|
181545
|
-
await fs9.mkdir(
|
|
181954
|
+
await fs9.mkdir(path16.dirname(harPath), { recursive: true });
|
|
181546
181955
|
return { harMode, harPath };
|
|
181547
181956
|
}
|
|
181548
181957
|
async function initTrace(fs9, options, flowName) {
|
|
@@ -181550,7 +181959,7 @@ async function initTrace(fs9, options, flowName) {
|
|
|
181550
181959
|
if (traceMode === "off")
|
|
181551
181960
|
return { traceMode, tracePath: undefined };
|
|
181552
181961
|
const tracePath = traceFlowPath(options.outputDir, flowName);
|
|
181553
|
-
await fs9.mkdir(
|
|
181962
|
+
await fs9.mkdir(path16.dirname(tracePath), { recursive: true });
|
|
181554
181963
|
return { traceMode, tracePath };
|
|
181555
181964
|
}
|
|
181556
181965
|
async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
|
|
@@ -181563,7 +181972,7 @@ async function maybeCleanupTrace(fs9, tracePath, passed, traceMode, warn) {
|
|
|
181563
181972
|
}
|
|
181564
181973
|
}
|
|
181565
181974
|
function buildContextSetup(videoSize, options, harPath) {
|
|
181566
|
-
const videosDir = options.artifactDir ??
|
|
181975
|
+
const videosDir = options.artifactDir ?? path16.join(options.outputDir, "videos");
|
|
181567
181976
|
const harOpts = harPath !== undefined ? buildHarContextOpts(harPath, options.harContent ?? "omit") : {};
|
|
181568
181977
|
return options.video !== "off" ? {
|
|
181569
181978
|
viewport: videoSize,
|
|
@@ -181599,7 +182008,7 @@ async function runWebFlow({
|
|
|
181599
182008
|
depsRoot: deps.depsRoot
|
|
181600
182009
|
});
|
|
181601
182010
|
} catch (err) {
|
|
181602
|
-
const flowName2 =
|
|
182011
|
+
const flowName2 = path17.basename(flowPath, path17.extname(flowPath));
|
|
181603
182012
|
return {
|
|
181604
182013
|
passed: false,
|
|
181605
182014
|
testCounts: { passed: 0, total: 0 },
|
|
@@ -181608,7 +182017,7 @@ async function runWebFlow({
|
|
|
181608
182017
|
};
|
|
181609
182018
|
}
|
|
181610
182019
|
const isLegacy = typeof exported === "function";
|
|
181611
|
-
const flowName = isLegacy ?
|
|
182020
|
+
const flowName = isLegacy ? path17.basename(flowPath, path17.extname(flowPath)) : exported.name;
|
|
181612
182021
|
const runFn = isLegacy ? exported : exported.run;
|
|
181613
182022
|
const { harMode, harPath } = await initHar(deps.fs, options, flowName);
|
|
181614
182023
|
const { traceMode, tracePath } = await initTrace(deps.fs, options, flowName);
|
|
@@ -181619,7 +182028,7 @@ async function runWebFlow({
|
|
|
181619
182028
|
slowMo: options.slowMo,
|
|
181620
182029
|
...options.executablePath !== undefined ? { executablePath: options.executablePath } : {}
|
|
181621
182030
|
};
|
|
181622
|
-
const { launch, cleanup } = createLaunch({
|
|
182031
|
+
const { launch, cleanup, artifactPaths } = createLaunch({
|
|
181623
182032
|
browsers: {
|
|
181624
182033
|
chromium: deps.chromium,
|
|
181625
182034
|
firefox: deps.firefox,
|
|
@@ -181661,25 +182070,26 @@ async function runWebFlow({
|
|
|
181661
182070
|
} finally {
|
|
181662
182071
|
await cleanup();
|
|
181663
182072
|
const warn = (message) => deps.logger?.warn(message);
|
|
181664
|
-
|
|
181665
|
-
|
|
182073
|
+
const assigned = artifactPaths();
|
|
182074
|
+
for (const contextHarPath of assigned.harPaths) {
|
|
182075
|
+
await maybeCleanupHar(deps.fs, contextHarPath, passed, harMode, warn);
|
|
181666
182076
|
}
|
|
181667
|
-
|
|
181668
|
-
await maybeCleanupTrace(deps.fs,
|
|
182077
|
+
for (const contextTracePath of assigned.tracePaths) {
|
|
182078
|
+
await maybeCleanupTrace(deps.fs, contextTracePath, passed, traceMode, warn);
|
|
181669
182079
|
}
|
|
181670
182080
|
}
|
|
181671
182081
|
}
|
|
181672
182082
|
|
|
181673
182083
|
// src/shell/embeddedWorkerCli.ts
|
|
181674
182084
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
181675
|
-
import { basename as basename6, join as
|
|
182085
|
+
import { basename as basename6, join as join33 } from "node:path";
|
|
181676
182086
|
var embeddedCliPathEnv = "QAWOLF_EMBEDDED_CLI_PATH";
|
|
181677
182087
|
function extractEmbeddedWorkerCli() {
|
|
181678
182088
|
const assetPath = process.env[embeddedCliPathEnv];
|
|
181679
182089
|
if (assetPath === undefined || assetPath === "")
|
|
181680
182090
|
return;
|
|
181681
|
-
const dir =
|
|
181682
|
-
const dest =
|
|
182091
|
+
const dir = join33(getDataDir(), "worker");
|
|
182092
|
+
const dest = join33(dir, basename6(assetPath));
|
|
181683
182093
|
if (existsSync3(dest))
|
|
181684
182094
|
return dest;
|
|
181685
182095
|
mkdirSync3(dir, { recursive: true });
|
|
@@ -181982,7 +182392,7 @@ function createCompositeReporter(reporters) {
|
|
|
181982
182392
|
};
|
|
181983
182393
|
}
|
|
181984
182394
|
// src/shell/reporter/createJUnitReporter.ts
|
|
181985
|
-
import { join as
|
|
182395
|
+
import { join as join34 } from "node:path";
|
|
181986
182396
|
|
|
181987
182397
|
// src/shell/reporter/junitXml.ts
|
|
181988
182398
|
function escapeXml(str) {
|
|
@@ -182021,7 +182431,7 @@ ${suites}`;
|
|
|
182021
182431
|
|
|
182022
182432
|
// src/shell/reporter/createJUnitReporter.ts
|
|
182023
182433
|
function resolveJUnitOutputPath(junit, outputDir) {
|
|
182024
|
-
return typeof junit === "string" && junit.trim() !== "" ? junit :
|
|
182434
|
+
return typeof junit === "string" && junit.trim() !== "" ? junit : join34(outputDir, "junit-report.xml");
|
|
182025
182435
|
}
|
|
182026
182436
|
function createJUnitReporter(deps) {
|
|
182027
182437
|
const flows = [];
|
|
@@ -182084,6 +182494,7 @@ function buildFlowsRunDeps(args) {
|
|
|
182084
182494
|
installBrowsers: (innerCtx, browsers) => installBrowserList(innerCtx, browsers, {
|
|
182085
182495
|
spawn: defaultSpawn,
|
|
182086
182496
|
platform: process.platform,
|
|
182497
|
+
browserDeps: flags.browserDeps,
|
|
182087
182498
|
playwrightCliPath: resolvePlaywrightCli(resolvedDir, process.platform)
|
|
182088
182499
|
}),
|
|
182089
182500
|
runWebFlow,
|
|
@@ -182108,11 +182519,11 @@ function buildFlowsRunDeps(args) {
|
|
|
182108
182519
|
|
|
182109
182520
|
// src/commands/flows/loadEnvFile.ts
|
|
182110
182521
|
import { readFile } from "node:fs/promises";
|
|
182111
|
-
import { join as
|
|
182522
|
+
import { join as join35 } from "node:path";
|
|
182112
182523
|
async function loadEnvFile(envDir) {
|
|
182113
182524
|
let content;
|
|
182114
182525
|
try {
|
|
182115
|
-
content = await readFile(
|
|
182526
|
+
content = await readFile(join35(envDir, ".env"), "utf8");
|
|
182116
182527
|
} catch (err) {
|
|
182117
182528
|
if (isNoEntError(err))
|
|
182118
182529
|
return;
|
|
@@ -182212,7 +182623,7 @@ async function handleFlowsRun(ctx, pattern, flags, deps) {
|
|
|
182212
182623
|
}
|
|
182213
182624
|
|
|
182214
182625
|
// src/commands/flows/hybridRunDefaults.ts
|
|
182215
|
-
import { join as
|
|
182626
|
+
import { join as join36, resolve as resolve9 } from "node:path";
|
|
182216
182627
|
function makeDefaultHybridDeps(fs9) {
|
|
182217
182628
|
return {
|
|
182218
182629
|
expandPatterns: (patterns, cwd, logger) => expandPatterns(patterns, cwd, logger, fs9),
|
|
@@ -182231,7 +182642,7 @@ async function handleHybridFlowsRun(ctx, pattern, flags, deps) {
|
|
|
182231
182642
|
if (validation !== "ok") {
|
|
182232
182643
|
return { error: validation.error, exitCode: 2 };
|
|
182233
182644
|
}
|
|
182234
|
-
const envDir = resolve9(
|
|
182645
|
+
const envDir = resolve9(join36(".qawolf", flags.env));
|
|
182235
182646
|
const patternArgs = buildPatternArgs(pattern);
|
|
182236
182647
|
const globFlows = () => resolvedDeps.expandPatterns(patternArgs, envDir, ctx.log("flows"));
|
|
182237
182648
|
let files = await globFlows();
|
|
@@ -182268,7 +182679,7 @@ Examples:
|
|
|
182268
182679
|
function registerFlowsRunCommand(flows, signals) {
|
|
182269
182680
|
declareCommandKind(flows.command("run [pattern]"), "local", {
|
|
182270
182681
|
kindNote: "read with --env"
|
|
182271
|
-
}).description("Run flows matching [pattern], or every flow when omitted; with --env, pull missing flows from that QA Wolf environment").option("--retries <n>", "Retry each failing flow up to this many times", parseInteger("--retries", { min: 0 }), 0).option("--bail", "Stop the run after the first failure", false).option("--workers <n>", "Parallel worker count for web flows", parseInteger("--workers", { min: 1 }), 1).option("--timeout <ms>", "Default timeout for actions and assertions, in milliseconds", parseInteger("--timeout", { min: 0 }), 30000).option("--video <mode>", "Record video: on | off | retain-on-failure", parseEnum("--video", videoModes), videoDefault).option("--trace <mode>", "Record Playwright trace: on | off | retain-on-failure", parseEnum("--trace", traceModes), traceDefault).option("--har <mode>", "Record HAR network log: on | off | retain-on-failure", parseEnum("--har", harModes), harDefault).option("--har-content <mode>", "HAR response bodies: omit | full (full uses more memory)", parseEnum("--har-content", harContentModes), harContentDefault).option("--output-dir <path>", "Directory for run artifacts (videos, traces, HAR)", defaultOutputDir).option("--junit [path]", "Write a JUnit XML report (default: <output-dir>/junit-report.xml)").option("--headed", "Show the browser window instead of headless", false).option("--env <env>", "Pull and run a flow from this environment (UUID or slug) if not cached locally").option("--deps <dir>", "Use this prepared dependency directory instead of auto-installing the runtime; or set QAWOLF_RUNTIME_DIR to relocate the managed runtime").addHelpText("after", runExamples).action((pattern, opts, command) => {
|
|
182682
|
+
}).description("Run flows matching [pattern], or every flow when omitted; with --env, pull missing flows from that QA Wolf environment").option("--retries <n>", "Retry each failing flow up to this many times", parseInteger("--retries", { min: 0 }), 0).option("--bail", "Stop the run after the first failure", false).option("--workers <n>", "Parallel worker count for web flows", parseInteger("--workers", { min: 1 }), 1).option("--timeout <ms>", "Default timeout for actions and assertions, in milliseconds", parseInteger("--timeout", { min: 0 }), 30000).option("--video <mode>", "Record video: on | off | retain-on-failure", parseEnum("--video", videoModes), videoDefault).option("--trace <mode>", "Record Playwright trace: on | off | retain-on-failure", parseEnum("--trace", traceModes), traceDefault).option("--har <mode>", "Record HAR network log: on | off | retain-on-failure", parseEnum("--har", harModes), harDefault).option("--har-content <mode>", "HAR response bodies: omit | full (full uses more memory)", parseEnum("--har-content", harContentModes), harContentDefault).option("--output-dir <path>", "Directory for run artifacts (videos, traces, HAR)", defaultOutputDir).option("--junit [path]", "Write a JUnit XML report (default: <output-dir>/junit-report.xml)").option("--headed", "Show the browser window instead of headless", false).option("--env <env>", "Pull and run a flow from this environment (UUID or slug) if not cached locally").option("--deps <dir>", "Use this prepared dependency directory instead of auto-installing the runtime; or set QAWOLF_RUNTIME_DIR to relocate the managed runtime").option("--no-browser-deps", "Skip installing OS-level browser dependencies (Linux --with-deps, which needs root); requires the system libraries to already be present").addHelpText("after", runExamples).action((pattern, opts, command) => {
|
|
182272
182683
|
if (opts.env !== undefined) {
|
|
182273
182684
|
const hybridFlags = { ...opts, env: opts.env };
|
|
182274
182685
|
return withAuthContext(signals, (ctx) => handleHybridFlowsRun(ctx, pattern, hybridFlags))(opts, command);
|
|
@@ -182305,7 +182716,7 @@ async function runWorker(signals) {
|
|
|
182305
182716
|
throw new Error("worker subprocess currently supports web flows only");
|
|
182306
182717
|
await configureTestkit(input.resolvedDir);
|
|
182307
182718
|
const fs9 = makeDefaultFs();
|
|
182308
|
-
const apiBaseUrl = process.env
|
|
182719
|
+
const apiBaseUrl = resolveHostUrl(process.env);
|
|
182309
182720
|
const flowRuntimeDeps = await createFlowRuntimeDeps({
|
|
182310
182721
|
envDir: input.resolvedDir,
|
|
182311
182722
|
ctx: { apiBaseUrl, configDir: getConfigDir(), fs: fs9 }
|
|
@@ -182385,7 +182796,7 @@ function registerFlowsCommand(program2, signals) {
|
|
|
182385
182796
|
}
|
|
182386
182797
|
|
|
182387
182798
|
// src/domains/init/init.ts
|
|
182388
|
-
import { dirname as dirname13, join as
|
|
182799
|
+
import { dirname as dirname13, join as join37, relative as relative7 } from "node:path";
|
|
182389
182800
|
|
|
182390
182801
|
// src/domains/init/templates.ts
|
|
182391
182802
|
var qawolfConfigTs = `export default {
|
|
@@ -182423,9 +182834,9 @@ function makeDefaultInitDeps() {
|
|
|
182423
182834
|
async function handleInit(ctx, opts, deps = makeDefaultInitDeps()) {
|
|
182424
182835
|
ctx.ui.gap();
|
|
182425
182836
|
ctx.ui.intro(initMessages.title);
|
|
182426
|
-
const configPath =
|
|
182427
|
-
const flowPath =
|
|
182428
|
-
const gitignorePath =
|
|
182837
|
+
const configPath = join37(deps.cwd, "qawolf.config.ts");
|
|
182838
|
+
const flowPath = join37(deps.cwd, "src", "flows", "example.flow.ts");
|
|
182839
|
+
const gitignorePath = join37(deps.cwd, ".qawolf", ".gitignore");
|
|
182429
182840
|
await writeWithPrompt(ctx, configPath, qawolfConfigTs, opts.yes, deps);
|
|
182430
182841
|
await writeWithPrompt(ctx, flowPath, exampleFlowTs, opts.yes, deps);
|
|
182431
182842
|
await writeWithPrompt(ctx, gitignorePath, qawolfGitignore, opts.yes, deps);
|
|
@@ -182449,7 +182860,7 @@ async function writeWithPrompt(ctx, filePath, content, yes, deps) {
|
|
|
182449
182860
|
ctx.ui.step(initMessages.createdFile(relPath));
|
|
182450
182861
|
}
|
|
182451
182862
|
async function ensurePackageJson(ctx, yes, deps) {
|
|
182452
|
-
const pkgPath =
|
|
182863
|
+
const pkgPath = join37(deps.cwd, "package.json");
|
|
182453
182864
|
if (!await deps.fs.pathExists(pkgPath)) {
|
|
182454
182865
|
const confirmed2 = await ctx.ui.confirm(initMessages.createPackageJsonPrompt, { yes });
|
|
182455
182866
|
if (!confirmed2.ok || !confirmed2.value) {
|
|
@@ -182508,19 +182919,19 @@ Examples:
|
|
|
182508
182919
|
}
|
|
182509
182920
|
|
|
182510
182921
|
// src/commands/install/android.ts
|
|
182511
|
-
import { join as
|
|
182922
|
+
import { join as join40 } from "node:path";
|
|
182512
182923
|
|
|
182513
182924
|
// src/domains/install/android/index.ts
|
|
182514
182925
|
import { parseExecutionTarget as parseExecutionTarget4 } from "@qawolf/flow-targets";
|
|
182515
182926
|
|
|
182516
182927
|
// src/domains/install/android/avd.ts
|
|
182517
|
-
import { join as
|
|
182928
|
+
import { join as join38 } from "node:path";
|
|
182518
182929
|
async function installAvds(ctx, specs, deps) {
|
|
182519
182930
|
const versionResult = await deps.spawn(deps.sdkManagerPath, ["--version"]);
|
|
182520
182931
|
if (versionResult.exitCode !== 0) {
|
|
182521
182932
|
throw new Error(installMessages.android.sdkmanagerNotFound(deps.sdkManagerPath));
|
|
182522
182933
|
}
|
|
182523
|
-
const licenseFile =
|
|
182934
|
+
const licenseFile = join38(deps.androidHome, "licenses", "android-sdk-license");
|
|
182524
182935
|
if (deps.checkExists(licenseFile)) {
|
|
182525
182936
|
ctx.ui.info(installMessages.android.licensesAlreadyAccepted);
|
|
182526
182937
|
} else {
|
|
@@ -182534,7 +182945,7 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182534
182945
|
for (const spec of specs) {
|
|
182535
182946
|
if (installedImages.has(spec.systemImage))
|
|
182536
182947
|
continue;
|
|
182537
|
-
const systemImg =
|
|
182948
|
+
const systemImg = join38(deps.androidHome, ...spec.systemImage.split(";"), "system.img");
|
|
182538
182949
|
if (deps.checkExists(systemImg)) {
|
|
182539
182950
|
ctx.ui.info(installMessages.android.systemImageAlreadyInstalled(spec.systemImage));
|
|
182540
182951
|
installedImages.add(spec.systemImage);
|
|
@@ -182582,9 +182993,9 @@ async function installAvds(ctx, specs, deps) {
|
|
|
182582
182993
|
}
|
|
182583
182994
|
|
|
182584
182995
|
// src/domains/install/android/driver.ts
|
|
182585
|
-
import { join as
|
|
182996
|
+
import { join as join39 } from "node:path";
|
|
182586
182997
|
var appiumEnv = {
|
|
182587
|
-
APPIUM_HOME:
|
|
182998
|
+
APPIUM_HOME: join39(envPaths("qawolf").data, "appium")
|
|
182588
182999
|
};
|
|
182589
183000
|
async function installUiautomator2Driver(ctx, deps) {
|
|
182590
183001
|
const listResult = await deps.spawn(deps.appiumBinPath, ["driver", "list", "--installed"], { env: appiumEnv });
|
|
@@ -182671,8 +183082,8 @@ async function handleInstallAndroid(ctx, pattern, envDir) {
|
|
|
182671
183082
|
arch: process.arch,
|
|
182672
183083
|
androidHome: androidHome2,
|
|
182673
183084
|
checkExists: (path9) => fs9.existsSync(path9),
|
|
182674
|
-
sdkManagerPath:
|
|
182675
|
-
avdManagerPath:
|
|
183085
|
+
sdkManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "sdkmanager"),
|
|
183086
|
+
avdManagerPath: join40(androidHome2, "cmdline-tools", "latest", "bin", "avdmanager"),
|
|
182676
183087
|
expandPatterns: (patterns, cwd) => expandPatterns(patterns, cwd ?? process.cwd(), undefined, fs9),
|
|
182677
183088
|
peekFlowMeta: makePeekFlowMeta(fs9),
|
|
182678
183089
|
resolveDepsRoot: async (files) => envDir ?? (await resolveDepsRoot({ files, fs: fs9 })).depsRoot,
|
|
@@ -182681,13 +183092,15 @@ async function handleInstallAndroid(ctx, pattern, envDir) {
|
|
|
182681
183092
|
}
|
|
182682
183093
|
|
|
182683
183094
|
// src/commands/install/browsers.ts
|
|
182684
|
-
async function handleInstallBrowsers(ctx, pattern,
|
|
183095
|
+
async function handleInstallBrowsers(ctx, pattern, options) {
|
|
182685
183096
|
const cwd = process.cwd();
|
|
182686
183097
|
const { fs: fs9 } = ctx;
|
|
183098
|
+
const { envDir, browserDeps } = options;
|
|
182687
183099
|
return installBrowsers(ctx, pattern, {
|
|
182688
183100
|
cwd,
|
|
182689
183101
|
spawn: defaultSpawn,
|
|
182690
183102
|
platform: process.platform,
|
|
183103
|
+
browserDeps,
|
|
182691
183104
|
expandPatterns: (patterns, dir) => expandPatterns(patterns, dir ?? cwd, undefined, fs9),
|
|
182692
183105
|
peekFlowMeta: makePeekFlowMeta(fs9),
|
|
182693
183106
|
resolvePlaywrightCliPath: async (files) => {
|
|
@@ -182752,18 +183165,27 @@ async function installAll(ctx, pattern, deps) {
|
|
|
182752
183165
|
}
|
|
182753
183166
|
return firstError;
|
|
182754
183167
|
}
|
|
182755
|
-
async function handleInstall(ctx, pattern) {
|
|
183168
|
+
async function handleInstall(ctx, pattern, options) {
|
|
182756
183169
|
const { fs: fs9 } = ctx;
|
|
182757
183170
|
return installAll(ctx, pattern, {
|
|
182758
183171
|
cwd: process.cwd(),
|
|
182759
183172
|
expandPatterns: (patterns, cwd) => expandPatterns(patterns, cwd ?? process.cwd(), undefined, fs9),
|
|
182760
183173
|
peekFlowMeta: makePeekFlowMeta(fs9),
|
|
182761
183174
|
resolveDepsRoot: (files) => resolveDepsRoot({ files, fs: fs9 }),
|
|
182762
|
-
installBrowsers: handleInstallBrowsers,
|
|
183175
|
+
installBrowsers: (innerCtx, innerPattern, envDir) => handleInstallBrowsers(innerCtx, innerPattern, {
|
|
183176
|
+
envDir,
|
|
183177
|
+
browserDeps: options.browserDeps
|
|
183178
|
+
}),
|
|
182763
183179
|
installAndroid: handleInstallAndroid
|
|
182764
183180
|
});
|
|
182765
183181
|
}
|
|
182766
183182
|
|
|
183183
|
+
// src/commands/install/browserDepsFlag.ts
|
|
183184
|
+
function mergedBrowserDeps(local, command) {
|
|
183185
|
+
const parent2 = command.parent?.opts().browserDeps;
|
|
183186
|
+
return local && parent2 !== false;
|
|
183187
|
+
}
|
|
183188
|
+
|
|
182767
183189
|
// src/commands/install/clear.ts
|
|
182768
183190
|
async function handleInstallClear(ctx, opts) {
|
|
182769
183191
|
const dir = managedEnvBaseDir();
|
|
@@ -182791,18 +183213,22 @@ async function handleInstallClear(ctx, opts) {
|
|
|
182791
183213
|
}
|
|
182792
183214
|
|
|
182793
183215
|
// src/commands/install/index.ts
|
|
183216
|
+
var noBrowserDepsDescription = "Skip installing OS-level browser dependencies (Linux --with-deps, which needs root); requires the system libraries to already be present";
|
|
182794
183217
|
function registerInstallCommand(program2, signals) {
|
|
182795
|
-
const install = declareCommandKind(program2.command("install"), "local").description("Install every runtime dependency the project's flows need").argument("[pattern]", "Glob limiting which flows determine required dependencies").addHelpText("after", `
|
|
183218
|
+
const install = declareCommandKind(program2.command("install"), "local").description("Install every runtime dependency the project's flows need").argument("[pattern]", "Glob limiting which flows determine required dependencies").option("--no-browser-deps", noBrowserDepsDescription).addHelpText("after", `
|
|
182796
183219
|
Examples:
|
|
182797
183220
|
$ qawolf install
|
|
182798
183221
|
$ qawolf install "flows/checkout/**"`).action((pattern, opts, command) => {
|
|
182799
|
-
return withContext(signals, (ctx) => handleInstall(ctx, pattern))(opts, command);
|
|
183222
|
+
return withContext(signals, (ctx) => handleInstall(ctx, pattern, { browserDeps: opts.browserDeps }))(opts, command);
|
|
182800
183223
|
});
|
|
182801
|
-
declareCommandKind(install.command("browsers [pattern]"), "local").description("Install Playwright browsers used by the project's web flows").addHelpText("after", `
|
|
183224
|
+
declareCommandKind(install.command("browsers [pattern]"), "local").description("Install Playwright browsers used by the project's web flows").option("--no-browser-deps", noBrowserDepsDescription).addHelpText("after", `
|
|
182802
183225
|
Examples:
|
|
182803
183226
|
$ qawolf install browsers
|
|
182804
183227
|
$ qawolf install browsers "flows/web/**"`).action((pattern, opts, command) => {
|
|
182805
|
-
return withContext(signals, (ctx) => handleInstallBrowsers(ctx, pattern
|
|
183228
|
+
return withContext(signals, (ctx) => handleInstallBrowsers(ctx, pattern, {
|
|
183229
|
+
envDir: undefined,
|
|
183230
|
+
browserDeps: mergedBrowserDeps(opts.browserDeps, command)
|
|
183231
|
+
}))(opts, command);
|
|
182806
183232
|
});
|
|
182807
183233
|
declareCommandKind(install.command("android [pattern]"), "local").description("Install Android system images, AVDs, and the Appium driver used by the project's Android flows").addHelpText("after", `
|
|
182808
183234
|
Examples:
|
|
@@ -182818,8 +183244,7 @@ Examples:
|
|
|
182818
183244
|
});
|
|
182819
183245
|
}
|
|
182820
183246
|
|
|
182821
|
-
// src/core/publicApi/
|
|
182822
|
-
var kebabCase = (field) => field.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
183247
|
+
// src/core/publicApi/flagKind.ts
|
|
182823
183248
|
function flagKind(schema) {
|
|
182824
183249
|
if (schema.type === "string")
|
|
182825
183250
|
return { ok: true, kind: "string" };
|
|
@@ -182854,6 +183279,110 @@ function flagKind(schema) {
|
|
|
182854
183279
|
reason: `type ${schema.type ?? "unknown"} cannot be expressed as a flag`
|
|
182855
183280
|
};
|
|
182856
183281
|
}
|
|
183282
|
+
|
|
183283
|
+
// src/core/publicApi/objectShape.ts
|
|
183284
|
+
var asObjectShape = (schema) => schema.type === "object" && schema.properties ? { properties: schema.properties, required: new Set(schema.required) } : undefined;
|
|
183285
|
+
function mergeIntersection(members) {
|
|
183286
|
+
const shape = { properties: {}, required: new Set };
|
|
183287
|
+
for (const member of members) {
|
|
183288
|
+
const memberShape = asObjectShape(member);
|
|
183289
|
+
if (!memberShape) {
|
|
183290
|
+
return {
|
|
183291
|
+
ok: false,
|
|
183292
|
+
field: "",
|
|
183293
|
+
reason: "intersection members must be object schemas"
|
|
183294
|
+
};
|
|
183295
|
+
}
|
|
183296
|
+
for (const [field, fieldSchema] of Object.entries(memberShape.properties)) {
|
|
183297
|
+
if (field in shape.properties) {
|
|
183298
|
+
return {
|
|
183299
|
+
ok: false,
|
|
183300
|
+
field,
|
|
183301
|
+
reason: "field appears in multiple intersection members"
|
|
183302
|
+
};
|
|
183303
|
+
}
|
|
183304
|
+
shape.properties[field] = fieldSchema;
|
|
183305
|
+
if (memberShape.required.has(field))
|
|
183306
|
+
shape.required.add(field);
|
|
183307
|
+
}
|
|
183308
|
+
}
|
|
183309
|
+
return { ok: true, shape };
|
|
183310
|
+
}
|
|
183311
|
+
function mergeUnion(branches) {
|
|
183312
|
+
const shapes = [];
|
|
183313
|
+
for (const branch of branches) {
|
|
183314
|
+
const branchShape = asObjectShape(branch);
|
|
183315
|
+
if (!branchShape) {
|
|
183316
|
+
return {
|
|
183317
|
+
ok: false,
|
|
183318
|
+
field: "",
|
|
183319
|
+
reason: "union branches must be object schemas"
|
|
183320
|
+
};
|
|
183321
|
+
}
|
|
183322
|
+
shapes.push(branchShape);
|
|
183323
|
+
}
|
|
183324
|
+
const shape = { properties: {}, required: new Set };
|
|
183325
|
+
for (const branchShape of shapes) {
|
|
183326
|
+
for (const [field, fieldSchema] of Object.entries(branchShape.properties)) {
|
|
183327
|
+
const existing = shape.properties[field];
|
|
183328
|
+
if (!existing) {
|
|
183329
|
+
shape.properties[field] = { ...fieldSchema };
|
|
183330
|
+
continue;
|
|
183331
|
+
}
|
|
183332
|
+
const kinds2 = [flagKind(existing), flagKind(fieldSchema)];
|
|
183333
|
+
if (kinds2[0]?.ok && kinds2[1]?.ok && kinds2[0].kind !== kinds2[1].kind) {
|
|
183334
|
+
return {
|
|
183335
|
+
ok: false,
|
|
183336
|
+
field,
|
|
183337
|
+
reason: "field has conflicting types across union branches"
|
|
183338
|
+
};
|
|
183339
|
+
}
|
|
183340
|
+
if (!existing.description && fieldSchema.description) {
|
|
183341
|
+
existing.description = fieldSchema.description;
|
|
183342
|
+
}
|
|
183343
|
+
}
|
|
183344
|
+
}
|
|
183345
|
+
const discriminator = Object.keys(shape.properties).find((field) => {
|
|
183346
|
+
const values = shapes.map((branch) => branch.properties[field]?.const);
|
|
183347
|
+
return values.every((value) => typeof value === "string") && new Set(values).size === shapes.length && shapes.every((branch) => branch.required.has(field));
|
|
183348
|
+
});
|
|
183349
|
+
if (discriminator === undefined) {
|
|
183350
|
+
return {
|
|
183351
|
+
ok: false,
|
|
183352
|
+
field: "",
|
|
183353
|
+
reason: "union branches must share a literal discriminator field"
|
|
183354
|
+
};
|
|
183355
|
+
}
|
|
183356
|
+
for (const [field, fieldSchema] of Object.entries(shape.properties)) {
|
|
183357
|
+
const occurrences = shapes.filter((branch) => (field in branch.properties));
|
|
183358
|
+
if (occurrences.length === shapes.length && occurrences.every((branch) => branch.required.has(field))) {
|
|
183359
|
+
shape.required.add(field);
|
|
183360
|
+
}
|
|
183361
|
+
if (field === discriminator) {
|
|
183362
|
+
const values = shapes.map((branch) => branch.properties[field]?.const);
|
|
183363
|
+
fieldSchema.description = `One of: ${values.join(", ")}`;
|
|
183364
|
+
}
|
|
183365
|
+
}
|
|
183366
|
+
return { ok: true, shape };
|
|
183367
|
+
}
|
|
183368
|
+
function toObjectShape(schema) {
|
|
183369
|
+
if (schema.allOf)
|
|
183370
|
+
return mergeIntersection(schema.allOf);
|
|
183371
|
+
const branches = schema.oneOf ?? schema.anyOf;
|
|
183372
|
+
if (branches)
|
|
183373
|
+
return mergeUnion(branches);
|
|
183374
|
+
const shape = asObjectShape(schema);
|
|
183375
|
+
if (shape)
|
|
183376
|
+
return { ok: true, shape };
|
|
183377
|
+
return {
|
|
183378
|
+
ok: false,
|
|
183379
|
+
field: "",
|
|
183380
|
+
reason: "contract input must be an object schema"
|
|
183381
|
+
};
|
|
183382
|
+
}
|
|
183383
|
+
|
|
183384
|
+
// src/core/publicApi/flagSpecs.ts
|
|
183385
|
+
var kebabCase = (field) => field.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
182857
183386
|
function flagUsage(field, kind) {
|
|
182858
183387
|
const name = `--${kebabCase(field)}`;
|
|
182859
183388
|
if (kind === "boolean")
|
|
@@ -182868,16 +183397,11 @@ function buildFlagSpecs(inputSchema) {
|
|
|
182868
183397
|
const jsonSchema = exports_external.toJSONSchema(inputSchema, {
|
|
182869
183398
|
io: "input"
|
|
182870
183399
|
});
|
|
182871
|
-
|
|
182872
|
-
|
|
182873
|
-
|
|
182874
|
-
field: "",
|
|
182875
|
-
reason: "contract input must be an object schema"
|
|
182876
|
-
};
|
|
182877
|
-
}
|
|
182878
|
-
const required2 = new Set(jsonSchema.required ?? []);
|
|
183400
|
+
const result = toObjectShape(jsonSchema);
|
|
183401
|
+
if (!result.ok)
|
|
183402
|
+
return result;
|
|
182879
183403
|
const flags = [];
|
|
182880
|
-
for (const [field, fieldSchema] of Object.entries(
|
|
183404
|
+
for (const [field, fieldSchema] of Object.entries(result.shape.properties)) {
|
|
182881
183405
|
const kind = flagKind(fieldSchema);
|
|
182882
183406
|
if (!kind.ok)
|
|
182883
183407
|
return { ok: false, field, reason: kind.reason };
|
|
@@ -182885,7 +183409,7 @@ function buildFlagSpecs(inputSchema) {
|
|
|
182885
183409
|
field,
|
|
182886
183410
|
flag: flagUsage(field, kind.kind),
|
|
182887
183411
|
description: fieldSchema.description ?? "",
|
|
182888
|
-
required:
|
|
183412
|
+
required: result.shape.required.has(field),
|
|
182889
183413
|
kind: kind.kind
|
|
182890
183414
|
});
|
|
182891
183415
|
}
|
|
@@ -183063,4 +183587,4 @@ createProgram({ signals }).parseAsync().catch(() => {
|
|
|
183063
183587
|
process.exitCode = 1;
|
|
183064
183588
|
}).finally(() => flushAndExit(typeof process.exitCode === "number" ? process.exitCode : 0));
|
|
183065
183589
|
|
|
183066
|
-
//# debugId=
|
|
183590
|
+
//# debugId=F2AE77060354D60864756E2164756E21
|