@azrico/azcli 1.2.5 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,15 +29,15 @@ With Yarn available on PATH, the equivalent commands are `yarn install --immutab
29
29
  | `package finalize` | Generate publishable manifests in `dist/`, `build/`, or `temp/`. |
30
30
  | `package publish` | Publish the prepared packages. |
31
31
  | `package go` | Prepare, build, finalize, then publish; stop on the first failure. |
32
- | `yarni`, `yarnb`, `yarnt` | Run Yarn install, build, or test. |
33
32
  | `transcripts <claude\|codex>` | Export sessions to Markdown with an `index.json`. |
34
33
  | `optimize-transcripts <claude\|codex>` | Prepare a transcript snapshot and run the selected analysis agent. |
35
34
  | `verify-changes` | Run the checks from `.azcli/verify.json` that match files changed since the base ref. Reports `passed`, `failed`, or `not-run`; exits 1 on failure (`--strict` also on not-run). `--profile`, `--base`, `--json`. |
36
35
  | `worktree start <path>` | Install the worktree's locked dependencies, pick a free port above the configured default, run the dev server detached, and wait for it to answer or exit. Reports URL, pid, compile result, and log file (inside the git dir). Fails at once if the process exits; `--timeout` bounds the wait. `--page`, `--port`, `--skip-install`. |
37
36
  | `worktree status <path>` | Branch tips and dirty files of the worktree and the target, ahead/behind counts, whether the branch is merged, and dependency manifests changed on each side. |
38
37
  | `worktree sync <path> --direction in|out|both` | Merge the target into the worktree branch (`in`) and/or the branch into the target (`out`). Refuses dirty trees; on conflict stops with the merge in progress and writes base/ours/theirs copies under the git dir, flagging whitespace-only conflicts. Never resolves conflicts; exits 1 on conflict. |
38
+ | `browser-check [directory]` | Start the selected project's dev server, wait for readiness, and run its browser-test script with the server origin in `BASE_URL`. `--tags`, `--headed`, `--url`, `--json`. |
39
39
 
40
- Worktree commands read `.azcli/worktree.json` from the worktree, else from `--project`; run `azcli worktree start --help` for the format. All three accept `--target` and `--json`.
40
+ Worktree commands read `.azcli/worktree.json` from the worktree, else from `--project`; run `azcli worktree start --help` for the format. All three accept `--target` and `--json`.
41
41
 
42
42
  Project commands accept `--path` to select a working directory. Build, update, clean, and publish accept `--type`, direct flags such as `--python`, and exclusions such as `--nopython` or `--no-python`. Use `--quiet` to suppress progress. Errors go to stderr and cause a nonzero exit status.
43
43
 
@@ -45,6 +45,49 @@ External tools must already be available on PATH. Commands run with explicit arg
45
45
 
46
46
  Production Composer installation (`up --prod` and WordPress builds) preserves the lockfile and installs with `--no-dev`. Normal Composer updates retain the existing dependency-refresh behavior. Python build/publish expands distribution files explicitly.
47
47
 
48
+ ## Browser checks
49
+
50
+ ```sh
51
+ azcli browser-check ./worktrees/order --tags "@shipping and not @save"
52
+ azcli browser-check --path ./web --url http://localhost:3001 --tags "@shipping" --headed
53
+ ```
54
+
55
+ The directory defaults to the current directory and does not need to be a Git repository. All browser-check configuration lives in `.azcli/browser.json`. Without `--url`, startup installs locked project dependencies, picks a free port above the configured default, and waits for the server. Exited, timed-out, or failed-compilation servers stop the browser checks before the test command runs. Started servers remain running after success or test failure; the command prints their URL, pid, and log location. Logs are written to `.azcli/logs/browser/` in the selected directory; add `.azcli/logs/` to that project's `.gitignore`. `--skip-install`, `--port`, `--page`, and `--timeout` control startup.
56
+
57
+ Use `--url` for an already-running server; this skips installation and startup. Its availability is checked by the project's tests. The server's **origin** (scheme, host, and port, without the page path) is passed as `BASE_URL`. `--headed` sets `HEADED=1`; project browser hooks must read that variable to show the browser.
58
+
59
+ By default, browser checks run `yarn test:browser` in the selected directory. Configure the test command and server together in `.azcli/browser.json` (this example assumes the application's start script reads `PORT`):
60
+
61
+ ```json
62
+ {
63
+ "command": "yarn",
64
+ "args": ["test:browser"],
65
+ "baseUrlEnv": "BASE_URL",
66
+ "report": "cucumber-report.html",
67
+ "install": {
68
+ "command": "yarn",
69
+ "args": ["install", "--immutable"]
70
+ },
71
+ "dev": {
72
+ "command": "yarn",
73
+ "args": ["start"],
74
+ "defaultPort": 3000,
75
+ "portEnv": "PORT",
76
+ "readyPath": "/",
77
+ "timeoutSeconds": 120,
78
+ "pages": { "order": "/order" }
79
+ }
80
+ }
81
+ ```
82
+
83
+ `command` is required when the file exists; `args` defaults to an empty array. Optional `cwd` selects a directory inside the selected project. `report` is relative to that test working directory and must stay inside it. `baseUrlEnv` defaults to `BASE_URL`. Config is read from the selected directory, falling back to `--project`; fallback configuration still runs against the selected directory. Tags are appended as `--tags <expression>` without shell parsing. For npm scripts, use `"command": "npm", "args": ["run", "test:browser", "--"]`.
84
+
85
+ `dev` is required for startup; without a config or `dev`, supply `--url`. `install` is optional and defaults to `yarn install --immutable` at the selected directory. Both accept `command`, `args`, and optional `cwd` relative to that directory. `dev.defaultPort` defaults to 3000; startup picks the first free port above it unless `--port` is supplied. `dev.portEnv` defaults to `PORT`, and `{port}` in dev arguments is replaced with the selected port. `dev.readyPath` defaults to `/`, `timeoutSeconds` to 120, and `binDirs` to `["node_modules/.bin"]`. `--page` selects a named path from `dev.pages`; it affects the reported server URL, while `BASE_URL` remains the origin. Optional `compiledPattern` and `failedPattern` customize regexes matched against the dev log. Worktree configuration is never read by browser-check.
86
+
87
+ The target application owns its `.feature` files, Cucumber step definitions, authentication, fixtures, and cleanup. For example, its `package.json` can define `"test:browser": "cucumber-js"`. Its browser hook should create a Playwright context with `baseURL: process.env.BASE_URL` and launch with `headless: process.env.HEADED !== '1'`. Configure Cucumber's HTML formatter to write the same file named by `report`; azcli reports its path when that file exists after a successful run. Test failures propagate to exit code 1. `--quiet` suppresses progress and subprocess output; `--json` returns server details and captured test output on success, while failures use the normal CLI error handler.
88
+
89
+ `@cucumber/cucumber` (12.x, compatible with Node.js 20) and `@playwright/test` are optional dependencies of azcli. Other commands do not import them, so they work when optional dependencies are omitted. Browser binaries are a separate setup step (`yarn playwright install chromium` in the application). The application's test script and step definitions must have their own resolvable Cucumber/Playwright dependencies, usually declared as its dev dependencies; installing azcli globally does not expose its optional packages to another project's module resolution. No packages or browser binaries are installed automatically by browser-check beyond its configured dependency install.
90
+
48
91
  ## Transcript options
49
92
 
50
93
  Use `--project <text>` repeatedly to filter projects, `--since-days 0` to include all dates, and `--last N` to select the newest N matched sessions across projects. `--list` lists without exporting. Rendering supports `--user-only`, `--no-include-tools`, `--no-include-thinking`, and `--include-subagents`.
@@ -58,7 +101,7 @@ src/
58
101
  cli/ entry point, registration, shared CLI options
59
102
  core/ subprocesses, files, JSON guards, logging, project detection
60
103
  features/
61
- build/ clean/ update/ publish/ package/ yarn/
104
+ build/ clean/ update/ publish/ package/
62
105
  transcripts/ extraction, rendering, types, JSONL readers
63
106
  sources/ Claude and Codex adapters
64
107
  optimize-transcripts/ snapshots, redaction, prompts, agent execution
@@ -14,8 +14,8 @@ const command_6 = __importDefault(require("../features/transcripts/command"));
14
14
  const command_7 = __importDefault(require("../features/optimize-transcripts/command"));
15
15
  const command_8 = __importDefault(require("../features/verify-changes/command"));
16
16
  const command_9 = __importDefault(require("../features/worktree/command"));
17
- const commands_1 = require("../features/yarn/commands");
18
- const commands = [command_1.default, command_2.default, command_3.default, command_4.default, command_5.default, command_6.default, command_7.default, command_8.default, command_9.default, commands_1.installCommand, commands_1.buildCommand, commands_1.testCommand];
17
+ const command_10 = __importDefault(require("../features/browser-check/command"));
18
+ const commands = [command_1.default, command_2.default, command_3.default, command_4.default, command_5.default, command_6.default, command_7.default, command_8.default, command_9.default, command_10.default];
19
19
  function getHelpCommand(command) {
20
20
  const registered = commands.some((module) => [module.command].flat().some((name) => name?.split(' ')[0] === command));
21
21
  return `azcli${registered ? ` ${command}` : ''} --help`;
@@ -1 +1 @@
1
- {"version":3,"file":"create-cli.js","sourceRoot":"","sources":["../../src/cli/create-cli.ts"],"names":[],"mappings":";;;;;;AAAA,wDAAgC;AAChC,wEAA8C;AAC9C,wEAA8C;AAC9C,yEAAgD;AAChD,0EAAkD;AAClD,0EAAmD;AACnD,8EAA0D;AAC1D,uFAAgE;AAChE,iFAA+D;AAC/D,2EAAoD;AACpD,wDAAsF;AAEtF,MAAM,QAAQ,GAAG,CAAC,iBAAK,EAAE,iBAAK,EAAE,iBAAM,EAAE,iBAAO,EAAE,iBAAQ,EAAE,iBAAW,EAAE,iBAAQ,EAAE,iBAAa,EAAE,iBAAQ,EAAE,yBAAc,EAAE,uBAAY,EAAE,sBAAW,CAAC,CAAC;AAEtJ,SAAgB,cAAc,CAAC,OAAoC;IAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC;IACtH,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;AACzD,CAAC;AAHD,wCAGC;AAED,SAAgB,SAAS,CAAC,IAAc;IACvC,OAAO,IAAA,eAAK,EAAC,IAAI,CAAC;SAChB,UAAU,CAAC,OAAO,CAAC;SACnB,KAAK,CAAC,+BAA+B,CAAC;SACtC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,yCAAyC,EAAE,CAAC;SAC5G,OAAO,CAAM,QAAQ,CAAC;SACtB,aAAa,CAAC,CAAC,CAAC;SAChB,MAAM,EAAE;SACR,IAAI,EAAE;SACN,WAAW,CAAC,KAAK,CAAC;SAClB,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAXD,8BAWC"}
1
+ {"version":3,"file":"create-cli.js","sourceRoot":"","sources":["../../src/cli/create-cli.ts"],"names":[],"mappings":";;;;;;AAAA,wDAAgC;AAChC,wEAA8C;AAC9C,wEAA8C;AAC9C,yEAAgD;AAChD,0EAAkD;AAClD,0EAAmD;AACnD,8EAA0D;AAC1D,uFAAgE;AAChE,iFAA+D;AAC/D,2EAAoD;AACpD,iFAA6D;AAE7D,MAAM,QAAQ,GAAG,CAAC,iBAAK,EAAE,iBAAK,EAAE,iBAAM,EAAE,iBAAO,EAAE,iBAAQ,EAAE,iBAAW,EAAE,iBAAQ,EAAE,iBAAa,EAAE,iBAAQ,EAAE,kBAAY,CAAC,CAAC;AAEzH,SAAgB,cAAc,CAAC,OAAoC;IAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC;IACtH,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;AACzD,CAAC;AAHD,wCAGC;AAED,SAAgB,SAAS,CAAC,IAAc;IACvC,OAAO,IAAA,eAAK,EAAC,IAAI,CAAC;SAChB,UAAU,CAAC,OAAO,CAAC;SACnB,KAAK,CAAC,+BAA+B,CAAC;SACtC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,yCAAyC,EAAE,CAAC;SAC5G,OAAO,CAAM,QAAQ,CAAC;SACtB,aAAa,CAAC,CAAC,CAAC;SAChB,MAAM,EAAE;SACR,IAAI,EAAE;SACN,WAAW,CAAC,KAAK,CAAC;SAClB,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAXD,8BAWC"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDevConfig = exports.parseCommandConfig = void 0;
4
+ const json_1 = require("./json");
5
+ function stringArray(value, label) {
6
+ if (!Array.isArray(value) || value.some((item) => typeof item !== 'string'))
7
+ throw new Error(`${label} must be an array of strings`);
8
+ return value;
9
+ }
10
+ function parseCommandConfig(value, label) {
11
+ if (!(0, json_1.isRecord)(value))
12
+ throw new Error(`${label} must be an object`);
13
+ const command = (0, json_1.asString)(value.command);
14
+ if (!command)
15
+ throw new Error(`${label} needs a "command"`);
16
+ return { command, args: value.args === undefined ? [] : stringArray(value.args, `${label} "args"`), cwd: (0, json_1.asString)(value.cwd) };
17
+ }
18
+ exports.parseCommandConfig = parseCommandConfig;
19
+ function positive(value, label, fallback) {
20
+ if (value === undefined)
21
+ return fallback;
22
+ if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0)
23
+ throw new Error(`${label} must be a positive integer`);
24
+ return value;
25
+ }
26
+ function parseDevConfig(value) {
27
+ const base = parseCommandConfig(value, '"dev"');
28
+ const dev = value;
29
+ const pages = {};
30
+ if (dev.pages !== undefined) {
31
+ if (!(0, json_1.isRecord)(dev.pages))
32
+ throw new Error('"dev" "pages" must be an object');
33
+ for (const [name, page] of Object.entries(dev.pages)) {
34
+ if (typeof page !== 'string')
35
+ throw new Error(`"dev" page "${name}" must be a string`);
36
+ pages[name] = page;
37
+ }
38
+ }
39
+ return {
40
+ ...base,
41
+ portEnv: (0, json_1.asString)(dev.portEnv) ?? 'PORT',
42
+ defaultPort: positive(dev.defaultPort, '"dev" "defaultPort"', 3000),
43
+ readyPath: (0, json_1.asString)(dev.readyPath) ?? '/',
44
+ pages,
45
+ timeoutSeconds: positive(dev.timeoutSeconds, '"dev" "timeoutSeconds"', 120),
46
+ binDirs: dev.binDirs === undefined ? ['node_modules/.bin'] : stringArray(dev.binDirs, '"dev" "binDirs"'),
47
+ compiledPattern: (0, json_1.asString)(dev.compiledPattern) ?? 'Compiled (successfully|with warnings)',
48
+ failedPattern: (0, json_1.asString)(dev.failedPattern) ?? 'Failed to compile|Module not found|error TS\\d+',
49
+ };
50
+ }
51
+ exports.parseDevConfig = parseDevConfig;
52
+ //# sourceMappingURL=dev-server-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-server-config.js","sourceRoot":"","sources":["../../src/core/dev-server-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatStart = exports.startDevServer = exports.pickPort = exports.isPortFree = void 0;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_http_1 = __importDefault(require("node:http"));
10
+ const node_net_1 = __importDefault(require("node:net"));
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const files_1 = require("./files");
13
+ const logger_1 = require("./logger");
14
+ const process_1 = require("./process");
15
+ const pollMs = 500;
16
+ const compileGraceMs = 3000;
17
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
18
+ function isPortFree(port) {
19
+ return new Promise((resolve) => {
20
+ const server = node_net_1.default.createServer();
21
+ server.once('error', () => resolve(false));
22
+ server.listen(port, () => server.close(() => resolve(true)));
23
+ });
24
+ }
25
+ exports.isPortFree = isPortFree;
26
+ /** An explicitly requested port must be free; otherwise take the first free port above the default one. */
27
+ async function pickPort(defaultPort, requested) {
28
+ if (requested !== undefined) {
29
+ if (!(await isPortFree(requested)))
30
+ throw new Error(`Port ${requested} is already in use`);
31
+ return requested;
32
+ }
33
+ for (let port = defaultPort + 1; port < defaultPort + 101; port++)
34
+ if (await isPortFree(port))
35
+ return port;
36
+ throw new Error(`No free port found above ${defaultPort}`);
37
+ }
38
+ exports.pickPort = pickPort;
39
+ function probe(url) {
40
+ return new Promise((resolve) => {
41
+ const request = node_http_1.default.get(url, { timeout: 2000 }, (response) => {
42
+ response.resume();
43
+ resolve(true);
44
+ });
45
+ request.once('timeout', () => request.destroy());
46
+ request.once('error', () => resolve(false));
47
+ });
48
+ }
49
+ function killTree(pid) {
50
+ try {
51
+ if (process.platform === 'win32')
52
+ (0, node_child_process_1.spawnSync)('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true });
53
+ else
54
+ process.kill(-pid, 'SIGTERM');
55
+ }
56
+ catch {
57
+ // The process is already gone.
58
+ }
59
+ }
60
+ function compileState(log, dev) {
61
+ if (new RegExp(dev.failedPattern, 'i').test(log))
62
+ return 'failed';
63
+ return new RegExp(dev.compiledPattern, 'i').test(log) ? 'ok' : 'unknown';
64
+ }
65
+ function launchEnvironment(root, cwd, dev, port) {
66
+ const env = { ...process.env, [dev.portEnv]: String(port) };
67
+ const key = Object.keys(env).find((name) => name.toUpperCase() === 'PATH') ?? 'PATH';
68
+ const directories = dev.binDirs.flatMap((dir) => [(0, files_1.resolveInside)(root, dir), (0, files_1.resolveInside)(cwd, dir)]);
69
+ env[key] = [...directories, env[key] ?? ''].join(node_path_1.default.delimiter);
70
+ return env;
71
+ }
72
+ async function startDevServer(options, config, run = process_1.runCommand) {
73
+ const root = node_path_1.default.resolve(options.path);
74
+ if (!node_fs_1.default.existsSync(root))
75
+ throw new Error(`Project not found: ${root}`);
76
+ const dev = config.dev;
77
+ if (!dev)
78
+ throw new Error('Missing dev server configuration.');
79
+ let pagePath = dev.readyPath;
80
+ if (options.page) {
81
+ if (!(options.page in dev.pages))
82
+ throw new Error(`Unknown page "${options.page}". Configured pages: ${Object.keys(dev.pages).join(', ') || 'none'}`);
83
+ pagePath = dev.pages[options.page];
84
+ }
85
+ if (!options.skipInstall) {
86
+ const install = config.install;
87
+ await run(install.command, install.args, { cwd: install.cwd ? (0, files_1.resolveInside)(root, install.cwd) : root, quiet: options.quiet });
88
+ }
89
+ const port = await pickPort(dev.defaultPort, options.port);
90
+ const cwd = dev.cwd ? (0, files_1.resolveInside)(root, dev.cwd) : root;
91
+ const launch = (0, process_1.resolveCommand)(dev.command, dev.args.map((arg) => arg.replaceAll('{port}', String(port))), cwd, launchEnvironment(root, cwd, dev, port));
92
+ const logDirectory = await options.logDirectory();
93
+ node_fs_1.default.mkdirSync(logDirectory, { recursive: true });
94
+ const log = node_path_1.default.join(logDirectory, `dev-${port}.log`);
95
+ const report = (fields) => ({
96
+ url: `http://localhost:${port}${pagePath}`, port, log, ...fields,
97
+ });
98
+ const fd = node_fs_1.default.openSync(log, 'w');
99
+ let exit;
100
+ const child = (0, node_child_process_1.spawn)(launch.command, launch.args, {
101
+ cwd,
102
+ env: launchEnvironment(root, cwd, dev, port),
103
+ detached: true,
104
+ stdio: ['ignore', fd, fd],
105
+ windowsHide: true,
106
+ });
107
+ node_fs_1.default.closeSync(fd);
108
+ child.once('error', (error) => { exit = `could not start ${dev.command}: ${error.message}`; });
109
+ child.once('exit', (code, signal) => { exit = signal ? `terminated by ${signal}` : `exit code ${code}`; });
110
+ const readLog = () => node_fs_1.default.readFileSync(log, 'utf8');
111
+ const tail = () => readLog().split(/\r?\n/).filter(Boolean).slice(-20).join('\n');
112
+ const fail = (state, error) => {
113
+ if (state === 'timeout' && child.pid)
114
+ killTree(child.pid);
115
+ return report({ state, compile: compileState(readLog(), dev), pid: child.pid, error, logTail: tail() });
116
+ };
117
+ const timeoutMs = (options.timeout ?? dev.timeoutSeconds) * 1000;
118
+ const started = Date.now();
119
+ const readyUrl = `http://localhost:${port}${dev.readyPath}`;
120
+ if (!options.quiet)
121
+ (0, logger_1.showMessage)(`Waiting for ${readyUrl} (up to ${timeoutMs / 1000}s)`);
122
+ while (!exit) {
123
+ if (await probe(readyUrl))
124
+ break;
125
+ if (Date.now() - started > timeoutMs)
126
+ return fail('timeout', `no response from ${readyUrl} within ${timeoutMs / 1000}s`);
127
+ await sleep(pollMs);
128
+ }
129
+ if (exit)
130
+ return fail('exited', `dev server stopped: ${exit}`);
131
+ const graceEnd = Date.now() + compileGraceMs;
132
+ while (compileState(readLog(), dev) === 'unknown' && Date.now() < graceEnd && !exit)
133
+ await sleep(200);
134
+ if (exit)
135
+ return fail('exited', `dev server stopped: ${exit}`);
136
+ child.unref();
137
+ return report({ state: 'running', compile: compileState(readLog(), dev), pid: child.pid });
138
+ }
139
+ exports.startDevServer = startDevServer;
140
+ function formatStart(report) {
141
+ const output = [
142
+ `state: ${report.state}${report.error ? ` (${report.error})` : ''}`,
143
+ `url: ${report.url}`,
144
+ `pid: ${report.pid ?? 'none'}`,
145
+ `compile: ${report.compile}`,
146
+ `log: ${report.log}`,
147
+ ];
148
+ if (report.logTail)
149
+ output.push('last log lines:', report.logTail);
150
+ return output.join('\n');
151
+ }
152
+ exports.formatStart = formatStart;
153
+ //# sourceMappingURL=dev-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-server.js","sourceRoot":"","sources":["../../src/core/dev-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.browserCheck = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const files_1 = require("../../core/files");
10
+ const logger_1 = require("../../core/logger");
11
+ const process_1 = require("../../core/process");
12
+ const dev_server_1 = require("../../core/dev-server");
13
+ const config_1 = require("./config");
14
+ async function browserCheck(options, run = process_1.runCommand, start = dev_server_1.startDevServer) {
15
+ if (options.path && options.directory)
16
+ throw new Error('Use either the directory argument or --path, not both.');
17
+ const root = node_path_1.default.resolve(options.path ?? options.directory ?? process.cwd());
18
+ const config = (0, config_1.loadConfig)(root, node_path_1.default.resolve(options.project ?? '.'));
19
+ const cwd = config.cwd && config.cwd !== '.' ? (0, files_1.resolveInside)(root, config.cwd) : root;
20
+ const report = config.report ? (0, files_1.resolveInside)(cwd, config.report) : undefined;
21
+ const quiet = options.quiet || options.json;
22
+ let url = options.url;
23
+ let server;
24
+ if (url !== undefined) {
25
+ try {
26
+ const parsed = new URL(url);
27
+ if (!['http:', 'https:'].includes(parsed.protocol))
28
+ throw new Error('Unsupported protocol');
29
+ url = parsed.origin;
30
+ }
31
+ catch {
32
+ throw new Error('--url must be an absolute HTTP or HTTPS URL.');
33
+ }
34
+ }
35
+ else {
36
+ if (!config.dev)
37
+ throw new Error(`Missing "dev" in ${config_1.configFile}. Configure it or use --url for an existing server.`);
38
+ server = await start({
39
+ path: root, port: options.port,
40
+ page: options.page, timeout: options.timeout, skipInstall: options.skipInstall, quiet,
41
+ logDirectory: () => (0, files_1.resolveInside)(root, '.azcli/logs/browser'),
42
+ }, config, run);
43
+ if (server.state !== 'running' || server.compile === 'failed')
44
+ throw new Error(`Browser checks were not run: server startup failed.\n${(0, dev_server_1.formatStart)(server)}`);
45
+ url = new URL(server.url).origin;
46
+ }
47
+ if (!quiet) {
48
+ if (server)
49
+ (0, logger_1.showMessage)((0, dev_server_1.formatStart)(server), 'text', false);
50
+ (0, logger_1.showMessage)(`Browser checks: ${url}`);
51
+ if (report)
52
+ (0, logger_1.showMessage)(`Report destination: ${report}`);
53
+ }
54
+ const env = { ...process.env };
55
+ // Windows environment variable names are case-insensitive.
56
+ for (const key of Object.keys(env))
57
+ if (key.toUpperCase() === config.baseUrlEnv.toUpperCase())
58
+ delete env[key];
59
+ env[config.baseUrlEnv] = url;
60
+ if (options.headed)
61
+ env.HEADED = '1';
62
+ const result = await run(config.command, [...config.args, ...(options.tags ? ['--tags', options.tags] : [])], { cwd, env, quiet });
63
+ return { url, server, report: report && node_fs_1.default.existsSync(report) ? report : undefined, ...result };
64
+ }
65
+ exports.browserCheck = browserCheck;
66
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/features/browser-check/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const logger_1 = require("../../core/logger");
4
+ const check_1 = require("./check");
5
+ const config_1 = require("./config");
6
+ const command = {
7
+ command: 'browser-check [directory]',
8
+ describe: 'Start a project server and run its Cucumber browser checks',
9
+ builder: (cli) => cli
10
+ .positional('directory', { type: 'string', describe: 'Project directory (default: current directory)' })
11
+ .option('path', { type: 'string', describe: 'Project directory instead of the positional argument' })
12
+ .option('project', { type: 'string', describe: 'Fallback directory for .azcli/browser.json' })
13
+ .option('url', { type: 'string', describe: 'Use this HTTP(S) server instead of installing and starting one' })
14
+ .option('tags', { type: 'string', describe: 'Cucumber tag expression, e.g. "@shipping and not @save"' })
15
+ .option('headed', { type: 'boolean', describe: 'Set HEADED=1 for project browser hooks' })
16
+ .option('port', { type: 'number', describe: 'Server port (default: first free port above the configured default)' })
17
+ .option('page', { type: 'string', describe: 'Page name from dev.pages in .azcli/browser.json' })
18
+ .option('timeout', { type: 'number', describe: 'Seconds to wait for server readiness' })
19
+ .option('skip-install', { type: 'boolean', describe: 'Skip installing project dependencies before startup' })
20
+ .option('json', { type: 'boolean', describe: 'Print the result, server details, and test output as JSON' })
21
+ .check((args) => {
22
+ if (args.port !== undefined && (!Number.isInteger(args.port) || args.port < 1 || args.port > 65535))
23
+ throw new Error('--port must be an integer between 1 and 65535.');
24
+ if (args.timeout !== undefined && (!Number.isFinite(args.timeout) || args.timeout <= 0))
25
+ throw new Error('--timeout must be a positive number.');
26
+ return true;
27
+ })
28
+ .epilog(config_1.configHelp),
29
+ handler: async (args) => {
30
+ const result = await (0, check_1.browserCheck)(args);
31
+ if (args.json)
32
+ console.log(JSON.stringify(result, null, 2));
33
+ else if (!args.quiet)
34
+ (0, logger_1.showMessage)(`Browser checks passed${result.report ? `. Report: ${result.report}` : ''}`);
35
+ },
36
+ };
37
+ exports.default = command;
38
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../../src/features/browser-check/command.ts"],"names":[],"mappings":";;AACA,8CAAgD;AAChD,mCAA4D;AAC5D,qCAAsC;AAEtC,MAAM,OAAO,GAA2C;IACvD,OAAO,EAAE,2BAA2B;IACpC,QAAQ,EAAE,4DAA4D;IACtE,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG;SACnB,UAAU,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,gDAAgD,EAAE,CAAC;SACvG,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,sDAAsD,EAAE,CAAC;SACpG,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,4CAA4C,EAAE,CAAC;SAC7F,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,gEAAgE,EAAE,CAAC;SAC7G,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,yDAAyD,EAAE,CAAC;SACvG,MAAM,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,wCAAwC,EAAE,CAAC;SACzF,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,qEAAqE,EAAE,CAAC;SACnH,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,iDAAiD,EAAE,CAAC;SAC/F,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,sCAAsC,EAAE,CAAC;SACvF,MAAM,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,qDAAqD,EAAE,CAAC;SAC5G,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,2DAA2D,EAAE,CAAC;SAC1G,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACf,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACvK,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACjJ,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;SACD,MAAM,CAAC,mBAAU,CAAC;IACpB,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAY,EAAC,IAAI,CAAC,CAAC;QACxC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;aACvD,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,IAAA,oBAAW,EAAC,wBAAwB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChH,CAAC;CACD,CAAC;AACF,kBAAe,OAAO,CAAC"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadConfig = exports.parseConfig = exports.configHelp = exports.configFile = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const json_1 = require("../../core/json");
10
+ const dev_server_config_1 = require("../../core/dev-server-config");
11
+ exports.configFile = node_path_1.default.join('.azcli', 'browser.json');
12
+ exports.configHelp = `Setup in the target web project:
13
+ 1. yarn/npm add --dev "@cucumber/cucumber@^12" "@playwright/test"
14
+ yarn/npm playwright install chromium
15
+ 2. Add package.json script: "test:browser": "cucumber-js".
16
+ 3. Add .feature files, matching step definitions, and Playwright hooks.
17
+ Configure cucumber.cjs paths/require to load them. Hooks should use
18
+ baseURL: process.env.BASE_URL and headless: process.env.HEADED !== '1'.
19
+ 4. Configure dev in .azcli/browser.json (example below),
20
+ or use --url for an already-running server.
21
+
22
+ Examples:
23
+ azcli browser-check . --tags "@shipping" --headed
24
+ azcli browser-check . --url http://localhost:3001 --tags "@shipping"
25
+
26
+ Config: .azcli/browser.json, read from the selected directory, else --project.
27
+ Without a config, use --url; runs yarn test:browser with BASE_URL set to the server origin.
28
+ Example:
29
+ {
30
+ "command": "yarn",
31
+ "args": ["test:browser"],
32
+ "baseUrlEnv": "BASE_URL",
33
+ "report": "cucumber-report.html",
34
+ "dev": { "command": "yarn", "args": ["start"], "defaultPort": 3000 }
35
+ }
36
+ The dev command must honor PORT (or use {port} in args). dev is required unless
37
+ --url is supplied. dev accepts cwd, portEnv, readyPath, timeoutSeconds, pages,
38
+ binDirs, compiledPattern, and failedPattern. --page selects from dev.pages.
39
+ Optional install: { "command": "yarn", "args": ["install", "--immutable"] }.
40
+ This is also the default install command; --skip-install skips it.
41
+ Optional cwd is relative to the selected directory; report is relative to cwd.
42
+ Both must stay inside those directories. --tags is appended as a literal argument.
43
+ Cucumber must generate the report, e.g. format: ["html:cucumber-report.html"].
44
+ Server logs are written to .azcli/logs/browser/ in the selected directory.
45
+ Started servers remain running after the checks; their pid and log are reported.`;
46
+ function parseConfig(value) {
47
+ if (!(0, json_1.isRecord)(value))
48
+ throw new Error(`${exports.configFile} must contain a JSON object`);
49
+ if (typeof value.command !== 'string' || !value.command.trim())
50
+ throw new Error(`${exports.configFile} needs a "command"`);
51
+ if (value.args !== undefined &&
52
+ (!Array.isArray(value.args) || value.args.some((arg) => typeof arg !== 'string')))
53
+ throw new Error(`${exports.configFile} "args" must be an array of strings`);
54
+ for (const key of ['cwd', 'report']) {
55
+ const field = value[key];
56
+ if (field !== undefined && (typeof field !== 'string' || !field.trim()))
57
+ throw new Error(`${exports.configFile} "${key}" must be a nonempty string`);
58
+ }
59
+ const baseUrlEnv = value.baseUrlEnv ?? 'BASE_URL';
60
+ if (typeof baseUrlEnv !== 'string' || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(baseUrlEnv))
61
+ throw new Error(`${exports.configFile} "baseUrlEnv" must be an environment variable name`);
62
+ return {
63
+ command: value.command,
64
+ args: value.args === undefined ? [] : value.args,
65
+ cwd: value.cwd,
66
+ baseUrlEnv,
67
+ report: value.report,
68
+ install: value.install === undefined ? { command: 'yarn', args: ['install', '--immutable'] } : (0, dev_server_config_1.parseCommandConfig)(value.install, '"install"'),
69
+ dev: value.dev === undefined ? undefined : (0, dev_server_config_1.parseDevConfig)(value.dev),
70
+ };
71
+ }
72
+ exports.parseConfig = parseConfig;
73
+ function loadConfig(...roots) {
74
+ for (const root of roots) {
75
+ const file = node_path_1.default.join(root, exports.configFile);
76
+ if (!node_fs_1.default.existsSync(file))
77
+ continue;
78
+ try {
79
+ return parseConfig(JSON.parse(node_fs_1.default.readFileSync(file, 'utf8')));
80
+ }
81
+ catch (error) {
82
+ throw new Error(`Could not read ${file}: ${error instanceof Error ? error.message : String(error)}`);
83
+ }
84
+ }
85
+ return parseConfig({ command: 'yarn', args: ['test:browser'] });
86
+ }
87
+ exports.loadConfig = loadConfig;
88
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/features/browser-check/config.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAyB;AACzB,0DAA6B;AAC7B,0CAA2C;AAC3C,oEAAgG;AAEnF,QAAA,UAAU,GAAG,mBAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AAUjD,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iFAiCuD,CAAC;AAElF,SAAgB,WAAW,CAAC,KAAc;IACzC,IAAI,CAAC,IAAA,eAAQ,EAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,6BAA6B,CAAC,CAAC;IAClF,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAC7D,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,oBAAoB,CAAC,CAAC;IACpD,IACC,KAAK,CAAC,IAAI,KAAK,SAAS;QACxB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC;QAEjF,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,qCAAqC,CAAC,CAAC;IACrE,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAU,EAAE;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACtE,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,KAAK,GAAG,6BAA6B,CAAC,CAAC;KACrE;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,UAAU,CAAC;IAClD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,oDAAoD,CAAC,CAAC;IACpF,OAAO;QACN,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,KAAK,CAAC,IAAiB;QAC9D,GAAG,EAAE,KAAK,CAAC,GAAyB;QACpC,UAAU;QACV,MAAM,EAAE,KAAK,CAAC,MAA4B;QAC1C,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAA,sCAAkB,EAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC;QAC7I,GAAG,EAAE,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,kCAAc,EAAC,KAAK,CAAC,GAAG,CAAC;KACpE,CAAC;AACH,CAAC;AA1BD,kCA0BC;AAED,SAAgB,UAAU,CAAC,GAAG,KAAe;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACzB,MAAM,IAAI,GAAG,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAU,CAAC,CAAC;QACzC,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACnC,IAAI;YACH,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;SAC9D;QAAC,OAAO,KAAK,EAAE;YACf,MAAM,IAAI,KAAK,CACd,kBAAkB,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACnF,CAAC;SACF;KACD;IACD,OAAO,WAAW,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;AACjE,CAAC;AAbD,gCAaC"}
@@ -7,6 +7,7 @@ exports.loadConfig = exports.parseConfig = exports.configHelp = exports.configFi
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const json_1 = require("../../core/json");
10
+ const dev_server_config_1 = require("../../core/dev-server-config");
10
11
  exports.configFile = node_path_1.default.join('.azcli', 'worktree.json');
11
12
  exports.configHelp = `Config file: .azcli/worktree.json (JSON), read from the worktree, else the project. Example:
12
13
 
@@ -39,58 +40,15 @@ Fields:
39
40
  the worktree root and to cwd. Default ["node_modules/.bin"].
40
41
  compiledPattern, failedPattern
41
42
  optional. Regexes matched against the dev log.`;
42
- function stringArray(value, label) {
43
- if (!Array.isArray(value) || value.some((item) => typeof item !== 'string'))
44
- throw new Error(`${label} must be an array of strings`);
45
- return value;
46
- }
47
- function commandConfig(value, label) {
48
- if (!(0, json_1.isRecord)(value))
49
- throw new Error(`${label} must be an object`);
50
- const command = (0, json_1.asString)(value.command);
51
- if (!command)
52
- throw new Error(`${label} needs a "command"`);
53
- return { command, args: value.args === undefined ? [] : stringArray(value.args, `${label} "args"`), cwd: (0, json_1.asString)(value.cwd) };
54
- }
55
- function positive(value, label, fallback) {
56
- if (value === undefined)
57
- return fallback;
58
- if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0)
59
- throw new Error(`${label} must be a positive integer`);
60
- return value;
61
- }
62
43
  function parseConfig(value) {
63
44
  if (!(0, json_1.isRecord)(value))
64
45
  throw new Error(`${exports.configFile} must contain a JSON object`);
65
46
  const config = {
66
47
  target: (0, json_1.asString)(value.target),
67
- install: value.install === undefined ? { command: 'yarn', args: ['install', '--immutable'] } : commandConfig(value.install, '"install"'),
48
+ install: value.install === undefined ? { command: 'yarn', args: ['install', '--immutable'] } : (0, dev_server_config_1.parseCommandConfig)(value.install, '"install"'),
68
49
  };
69
- if (value.dev !== undefined) {
70
- const base = commandConfig(value.dev, '"dev"');
71
- const dev = value.dev;
72
- const pages = {};
73
- if (dev.pages !== undefined) {
74
- if (!(0, json_1.isRecord)(dev.pages))
75
- throw new Error('"dev" "pages" must be an object');
76
- for (const [name, page] of Object.entries(dev.pages)) {
77
- if (typeof page !== 'string')
78
- throw new Error(`"dev" page "${name}" must be a string`);
79
- pages[name] = page;
80
- }
81
- }
82
- config.dev = {
83
- ...base,
84
- portEnv: (0, json_1.asString)(dev.portEnv) ?? 'PORT',
85
- defaultPort: positive(dev.defaultPort, '"dev" "defaultPort"', 3000),
86
- readyPath: (0, json_1.asString)(dev.readyPath) ?? '/',
87
- pages,
88
- timeoutSeconds: positive(dev.timeoutSeconds, '"dev" "timeoutSeconds"', 120),
89
- binDirs: dev.binDirs === undefined ? ['node_modules/.bin'] : stringArray(dev.binDirs, '"dev" "binDirs"'),
90
- compiledPattern: (0, json_1.asString)(dev.compiledPattern) ?? 'Compiled (successfully|with warnings)',
91
- failedPattern: (0, json_1.asString)(dev.failedPattern) ?? 'Failed to compile|Module not found|error TS\\d+',
92
- };
93
- }
50
+ if (value.dev !== undefined)
51
+ config.dev = (0, dev_server_config_1.parseDevConfig)(value.dev);
94
52
  return config;
95
53
  }
96
54
  exports.parseConfig = parseConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/features/worktree/config.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAyB;AACzB,0DAA6B;AAC7B,0CAAqD;AAExC,QAAA,UAAU,GAAG,mBAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AA6BlD,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iEA8BuC,CAAC;AAElE,SAAS,WAAW,CAAC,KAAc,EAAE,KAAa;IACjD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,8BAA8B,CAAC,CAAC;IACrI,OAAO,KAAiB,CAAC;AAC1B,CAAC;AACD,SAAS,aAAa,CAAC,KAAc,EAAE,KAAa;IACnD,IAAI,CAAC,IAAA,eAAQ,EAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,IAAA,eAAQ,EAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;IAC5D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,SAAS,CAAC,EAAE,GAAG,EAAE,IAAA,eAAQ,EAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AAChI,CAAC;AACD,SAAS,QAAQ,CAAC,KAAc,EAAE,KAAa,EAAE,QAAgB;IAChE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;IAChI,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAgB,WAAW,CAAC,KAAc;IACzC,IAAI,CAAC,IAAA,eAAQ,EAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,6BAA6B,CAAC,CAAC;IAClF,MAAM,MAAM,GAAmB;QAC9B,MAAM,EAAE,IAAA,eAAQ,EAAC,KAAK,CAAC,MAAM,CAAC;QAC9B,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC;KACxI,CAAC;IACF,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE;QAC5B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,KAAK,CAAC,GAA8B,CAAC;QACjD,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE;YAC5B,IAAI,CAAC,IAAA,eAAQ,EAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;YAC7E,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;gBACrD,IAAI,OAAO,IAAI,KAAK,QAAQ;oBAAE,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,oBAAoB,CAAC,CAAC;gBACvF,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;aACnB;SACD;QACD,MAAM,CAAC,GAAG,GAAG;YACZ,GAAG,IAAI;YACP,OAAO,EAAE,IAAA,eAAQ,EAAC,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM;YACxC,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,qBAAqB,EAAE,IAAI,CAAC;YACnE,SAAS,EAAE,IAAA,eAAQ,EAAC,GAAG,CAAC,SAAS,CAAC,IAAI,GAAG;YACzC,KAAK;YACL,cAAc,EAAE,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,wBAAwB,EAAE,GAAG,CAAC;YAC3E,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC;YACxG,eAAe,EAAE,IAAA,eAAQ,EAAC,GAAG,CAAC,eAAe,CAAC,IAAI,uCAAuC;YACzF,aAAa,EAAE,IAAA,eAAQ,EAAC,GAAG,CAAC,aAAa,CAAC,IAAI,iDAAiD;SAC/F,CAAC;KACF;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AA9BD,kCA8BC;AAED,uGAAuG;AACvG,SAAgB,UAAU,CAAC,GAAG,KAAe;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACzB,MAAM,IAAI,GAAG,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAU,CAAC,CAAC;QACzC,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACnC,IAAI,MAAe,CAAC;QACpB,IAAI;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;SACnD;QAAC,OAAO,KAAK,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;SACrG;QACD,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;KAC3B;IACD,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAbD,gCAaC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/features/worktree/config.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAyB;AACzB,0DAA6B;AAC7B,0CAAqD;AACrD,oEAAgG;AAEnF,QAAA,UAAU,GAAG,mBAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAKlD,QAAA,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iEA8BuC,CAAC;AAElE,SAAgB,WAAW,CAAC,KAAc;IACzC,IAAI,CAAC,IAAA,eAAQ,EAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,kBAAU,6BAA6B,CAAC,CAAC;IAClF,MAAM,MAAM,GAAmB;QAC9B,MAAM,EAAE,IAAA,eAAQ,EAAC,KAAK,CAAC,MAAM,CAAC;QAC9B,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAA,sCAAkB,EAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC;KAC7I,CAAC;IACF,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;QAAE,MAAM,CAAC,GAAG,GAAG,IAAA,kCAAc,EAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,MAAM,CAAC;AACf,CAAC;AARD,kCAQC;AAED,uGAAuG;AACvG,SAAgB,UAAU,CAAC,GAAG,KAAe;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACzB,MAAM,IAAI,GAAG,mBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAU,CAAC,CAAC;QACzC,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QACnC,IAAI,MAAe,CAAC;QACpB,IAAI;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;SACnD;QAAC,OAAO,KAAK,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;SACrG;QACD,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;KAC3B;IACD,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAbD,gCAaC"}
@@ -3,154 +3,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.formatStart = exports.startWorktree = exports.pickPort = exports.isPortFree = void 0;
7
- const node_child_process_1 = require("node:child_process");
6
+ exports.startWorktree = exports.pickPort = exports.isPortFree = exports.formatStart = void 0;
8
7
  const node_fs_1 = __importDefault(require("node:fs"));
9
- const node_http_1 = __importDefault(require("node:http"));
10
- const node_net_1 = __importDefault(require("node:net"));
11
8
  const node_path_1 = __importDefault(require("node:path"));
12
- const files_1 = require("../../core/files");
13
- const logger_1 = require("../../core/logger");
9
+ const dev_server_1 = require("../../core/dev-server");
14
10
  const process_1 = require("../../core/process");
15
11
  const config_1 = require("./config");
16
12
  const git_1 = require("./git");
17
- const pollMs = 500;
18
- const compileGraceMs = 3000;
19
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
20
- function isPortFree(port) {
21
- return new Promise((resolve) => {
22
- const server = node_net_1.default.createServer();
23
- server.once('error', () => resolve(false));
24
- server.listen(port, () => server.close(() => resolve(true)));
25
- });
26
- }
27
- exports.isPortFree = isPortFree;
28
- /** An explicitly requested port must be free; otherwise take the first free port above the default one. */
29
- async function pickPort(defaultPort, requested) {
30
- if (requested !== undefined) {
31
- if (!(await isPortFree(requested)))
32
- throw new Error(`Port ${requested} is already in use`);
33
- return requested;
34
- }
35
- for (let port = defaultPort + 1; port < defaultPort + 101; port++)
36
- if (await isPortFree(port))
37
- return port;
38
- throw new Error(`No free port found above ${defaultPort}`);
39
- }
40
- exports.pickPort = pickPort;
41
- function probe(url) {
42
- return new Promise((resolve) => {
43
- const request = node_http_1.default.get(url, { timeout: 2000 }, (response) => {
44
- response.resume();
45
- resolve(true);
46
- });
47
- request.once('timeout', () => request.destroy());
48
- request.once('error', () => resolve(false));
49
- });
50
- }
51
- function killTree(pid) {
52
- try {
53
- if (process.platform === 'win32')
54
- (0, node_child_process_1.spawnSync)('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true });
55
- else
56
- process.kill(-pid, 'SIGTERM');
57
- }
58
- catch {
59
- // The process is already gone.
60
- }
61
- }
62
- function compileState(log, dev) {
63
- if (new RegExp(dev.failedPattern, 'i').test(log))
64
- return 'failed';
65
- return new RegExp(dev.compiledPattern, 'i').test(log) ? 'ok' : 'unknown';
66
- }
67
- function launchEnvironment(root, cwd, dev, port) {
68
- const env = { ...process.env, [dev.portEnv]: String(port) };
69
- const key = Object.keys(env).find((name) => name.toUpperCase() === 'PATH') ?? 'PATH';
70
- const directories = dev.binDirs.flatMap((dir) => [(0, files_1.resolveInside)(root, dir), (0, files_1.resolveInside)(cwd, dir)]);
71
- env[key] = [...directories, env[key] ?? ''].join(node_path_1.default.delimiter);
72
- return env;
73
- }
13
+ var dev_server_2 = require("../../core/dev-server");
14
+ Object.defineProperty(exports, "formatStart", { enumerable: true, get: function () { return dev_server_2.formatStart; } });
15
+ Object.defineProperty(exports, "isPortFree", { enumerable: true, get: function () { return dev_server_2.isPortFree; } });
16
+ Object.defineProperty(exports, "pickPort", { enumerable: true, get: function () { return dev_server_2.pickPort; } });
74
17
  async function startWorktree(options, run = process_1.runCommand) {
75
18
  const root = node_path_1.default.resolve(options.path);
76
19
  if (!node_fs_1.default.existsSync(root))
77
20
  throw new Error(`Worktree not found: ${root}`);
78
21
  const config = (0, config_1.loadConfig)(root, node_path_1.default.resolve(options.project ?? '.'));
79
- const dev = config.dev;
80
- if (!dev)
22
+ if (!config.dev)
81
23
  throw new Error('Missing "dev" in .azcli/worktree.json.');
82
- let pagePath = dev.readyPath;
83
- if (options.page) {
84
- if (!(options.page in dev.pages))
85
- throw new Error(`Unknown page "${options.page}". Configured pages: ${Object.keys(dev.pages).join(', ') || 'none'}`);
86
- pagePath = dev.pages[options.page];
87
- }
88
- if (!options.skipInstall) {
89
- const install = config.install;
90
- await run(install.command, install.args, { cwd: install.cwd ? (0, files_1.resolveInside)(root, install.cwd) : root, quiet: options.quiet });
91
- }
92
- const port = await pickPort(dev.defaultPort, options.port);
93
- const cwd = dev.cwd ? (0, files_1.resolveInside)(root, dev.cwd) : root;
94
- const launch = (0, process_1.resolveCommand)(dev.command, dev.args.map((arg) => arg.replaceAll('{port}', String(port))), cwd, launchEnvironment(root, cwd, dev, port));
95
- const logDirectory = node_path_1.default.join(await (0, git_1.gitDir)(run, root), 'azcli');
96
- node_fs_1.default.mkdirSync(logDirectory, { recursive: true });
97
- const log = node_path_1.default.join(logDirectory, `dev-${port}.log`);
98
- const report = (fields) => ({
99
- url: `http://localhost:${port}${pagePath}`, port, log, ...fields,
100
- });
101
- const fd = node_fs_1.default.openSync(log, 'w');
102
- let exit;
103
- const child = (0, node_child_process_1.spawn)(launch.command, launch.args, {
104
- cwd,
105
- env: launchEnvironment(root, cwd, dev, port),
106
- detached: true,
107
- stdio: ['ignore', fd, fd],
108
- windowsHide: true,
109
- });
110
- node_fs_1.default.closeSync(fd);
111
- child.once('error', (error) => { exit = `could not start ${dev.command}: ${error.message}`; });
112
- child.once('exit', (code, signal) => { exit = signal ? `terminated by ${signal}` : `exit code ${code}`; });
113
- const readLog = () => node_fs_1.default.readFileSync(log, 'utf8');
114
- const tail = () => readLog().split(/\r?\n/).filter(Boolean).slice(-20).join('\n');
115
- const fail = (state, error) => {
116
- if (state === 'timeout' && child.pid)
117
- killTree(child.pid);
118
- return report({ state, compile: compileState(readLog(), dev), pid: child.pid, error, logTail: tail() });
119
- };
120
- const timeoutMs = (options.timeout ?? dev.timeoutSeconds) * 1000;
121
- const started = Date.now();
122
- const readyUrl = `http://localhost:${port}${dev.readyPath}`;
123
- if (!options.quiet)
124
- (0, logger_1.showMessage)(`Waiting for ${readyUrl} (up to ${timeoutMs / 1000}s)`);
125
- while (!exit) {
126
- if (await probe(readyUrl))
127
- break;
128
- if (Date.now() - started > timeoutMs)
129
- return fail('timeout', `no response from ${readyUrl} within ${timeoutMs / 1000}s`);
130
- await sleep(pollMs);
131
- }
132
- if (exit)
133
- return fail('exited', `dev server stopped: ${exit}`);
134
- const graceEnd = Date.now() + compileGraceMs;
135
- while (compileState(readLog(), dev) === 'unknown' && Date.now() < graceEnd && !exit)
136
- await sleep(200);
137
- if (exit)
138
- return fail('exited', `dev server stopped: ${exit}`);
139
- child.unref();
140
- return report({ state: 'running', compile: compileState(readLog(), dev), pid: child.pid });
24
+ return (0, dev_server_1.startDevServer)({
25
+ ...options,
26
+ path: root,
27
+ logDirectory: async () => node_path_1.default.join(await (0, git_1.gitDir)(run, root), 'azcli'),
28
+ }, config, run);
141
29
  }
142
30
  exports.startWorktree = startWorktree;
143
- function formatStart(report) {
144
- const output = [
145
- `state: ${report.state}${report.error ? ` (${report.error})` : ''}`,
146
- `url: ${report.url}`,
147
- `pid: ${report.pid ?? 'none'}`,
148
- `compile: ${report.compile}`,
149
- `log: ${report.log}`,
150
- ];
151
- if (report.logTail)
152
- output.push('last log lines:', report.logTail);
153
- return output.join('\n');
154
- }
155
- exports.formatStart = formatStart;
156
31
  //# sourceMappingURL=start.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/features/worktree/start.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../../src/features/worktree/start.ts"],"names":[],"mappings":";;;;;;AAAA,sDAAyB;AACzB,0DAA6B;AAC7B,sDAA2F;AAC3F,gDAA+D;AAC/D,qCAAsC;AACtC,+BAA+B;AAE/B,oDAAuF;AAAjE,yGAAA,WAAW,OAAA;AAAE,wGAAA,UAAU,OAAA;AAAE,sGAAA,QAAQ,OAAA;AAGhD,KAAK,UAAU,aAAa,CAAC,OAAqB,EAAE,MAAqB,oBAAU;IACzF,MAAM,IAAI,GAAG,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,IAAI,EAAE,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC,MAAM,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC3E,OAAO,IAAA,2BAAc,EAAC;QACrB,GAAG,OAAO;QACV,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,mBAAI,CAAC,IAAI,CAAC,MAAM,IAAA,YAAM,EAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC;KACrE,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;AACjB,CAAC;AAVD,sCAUC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azrico/azcli",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "description": "Project build, publishing, dependency, and AI transcript helpers",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cli/index.js",
@@ -35,6 +35,10 @@
35
35
  "yargs": "^17.5.1",
36
36
  "zip-lib": "^0.7.3"
37
37
  },
38
+ "optionalDependencies": {
39
+ "@cucumber/cucumber": "^12.9.0",
40
+ "@playwright/test": "^1.64.1"
41
+ },
38
42
  "packageManager": "yarn@4.6.0",
39
43
  "engines": {
40
44
  "node": ">=20"
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.testCommand = exports.buildCommand = exports.installCommand = void 0;
4
- const process_1 = require("../../core/process");
5
- function yarnCommand(command, describe, args) {
6
- return {
7
- command,
8
- describe,
9
- builder: (cli) => cli.option('path', { type: 'string', describe: 'Project directory' }),
10
- handler: async (options) => {
11
- await (0, process_1.runCommand)('yarn', args, { cwd: options.path, quiet: options.quiet });
12
- },
13
- };
14
- }
15
- exports.installCommand = yarnCommand('yarni', 'Install Yarn dependencies', ['install']);
16
- exports.buildCommand = yarnCommand('yarnb', 'Run the Yarn build', ['run', 'build']);
17
- exports.testCommand = yarnCommand('yarnt', 'Run the Yarn tests', ['run', 'test']);
18
- //# sourceMappingURL=commands.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../../src/features/yarn/commands.ts"],"names":[],"mappings":";;;AACA,gDAAgD;AAGhD,SAAS,WAAW,CAAC,OAAe,EAAE,QAAgB,EAAE,IAAc;IACrE,OAAO;QACN,OAAO;QACP,QAAQ;QACR,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;QACvF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAC1B,MAAM,IAAA,oBAAU,EAAC,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7E,CAAC;KACD,CAAC;AACH,CAAC;AAEY,QAAA,cAAc,GAAG,WAAW,CAAC,OAAO,EAAE,2BAA2B,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;AAChF,QAAA,YAAY,GAAG,WAAW,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5E,QAAA,WAAW,GAAG,WAAW,CAAC,OAAO,EAAE,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC"}