@warlock.js/core 5.33.0 → 5.34.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/CHANGELOG.md +26 -46
- package/esm/cli/commands/check/check-command.action.mjs +2 -1
- package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
- package/esm/cli/commands/check.command.mjs +2 -2
- package/esm/cli/commands/check.command.mjs.map +1 -1
- package/esm/cli/commands/e2e.command.mjs +63 -0
- package/esm/cli/commands/e2e.command.mjs.map +1 -0
- package/esm/cli/commands/load-e2e-spec.mjs +47 -0
- package/esm/cli/commands/load-e2e-spec.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +2 -0
- package/esm/cli/framework-cli-commands.mjs.map +1 -1
- package/esm/dev-server/health-checker/checkers/oxlint-health-checker.mjs +1 -1
- package/esm/dev-server/health-checker/default-health-checkers.mjs +1 -6
- package/esm/dev-server/health-checker/default-health-checkers.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +0 -2
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts.map +1 -1
- package/esm/dev-server/health-checker/file-health-result.d.mts +1 -1
- package/esm/dev-server/health-checker/file-health-result.mjs.map +1 -1
- package/esm/e2e/browser.d.mts +4 -0
- package/esm/e2e/browser.d.mts.map +1 -0
- package/esm/e2e/browser.mjs +88 -0
- package/esm/e2e/browser.mjs.map +1 -0
- package/esm/e2e/cdp.d.mts +152 -0
- package/esm/e2e/cdp.d.mts.map +1 -0
- package/esm/e2e/cdp.mjs +106 -0
- package/esm/e2e/cdp.mjs.map +1 -0
- package/esm/e2e/fixture.mjs +195 -0
- package/esm/e2e/fixture.mjs.map +1 -0
- package/esm/e2e/index.d.mts +5 -0
- package/esm/e2e/index.mjs +6 -0
- package/esm/e2e/owned-process.mjs +193 -0
- package/esm/e2e/owned-process.mjs.map +1 -0
- package/esm/e2e/page.d.mts +64 -0
- package/esm/e2e/page.d.mts.map +1 -0
- package/esm/e2e/page.mjs +376 -0
- package/esm/e2e/page.mjs.map +1 -0
- package/esm/e2e/run-e2e.d.mts +17 -0
- package/esm/e2e/run-e2e.d.mts.map +1 -0
- package/esm/e2e/run-e2e.mjs +143 -0
- package/esm/e2e/run-e2e.mjs.map +1 -0
- package/esm/e2e/types.d.mts +123 -0
- package/esm/e2e/types.d.mts.map +1 -0
- package/esm/e2e/types.mjs +24 -0
- package/esm/e2e/types.mjs.map +1 -0
- package/esm/secrets/errors.d.mts +1 -1
- package/esm/secrets/errors.d.mts.map +1 -1
- package/esm/secrets/errors.mjs +2 -0
- package/esm/secrets/errors.mjs.map +1 -1
- package/esm/secrets/resolve-driver.mjs +14 -0
- package/esm/secrets/resolve-driver.mjs.map +1 -0
- package/esm/secrets/secret-store.d.mts.map +1 -1
- package/esm/secrets/secret-store.mjs +4 -1
- package/esm/secrets/secret-store.mjs.map +1 -1
- package/esm/secrets/secrets.d.mts.map +1 -1
- package/esm/secrets/secrets.mjs +10 -1
- package/esm/secrets/secrets.mjs.map +1 -1
- package/esm/secrets/types.d.mts +4 -0
- package/esm/secrets/types.d.mts.map +1 -1
- package/esm/tooling/check/lint-step.d.mts +3 -3
- package/esm/tooling/check/lint-step.d.mts.map +1 -1
- package/esm/tooling/check/lint-step.mjs +3 -47
- package/esm/tooling/check/lint-step.mjs.map +1 -1
- package/esm/tooling/check/run-check.d.mts +2 -2
- package/esm/tooling/check/run-check.mjs +4 -2
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/problem.d.mts +1 -1
- package/esm/tooling/problem.d.mts.map +1 -1
- package/esm/tooling/problem.mjs.map +1 -1
- package/esm/updater/package-manager.mjs +5 -1
- package/esm/updater/package-manager.mjs.map +1 -1
- package/esm/upgrade/codemods/index.mjs +2 -1
- package/esm/upgrade/codemods/index.mjs.map +1 -1
- package/esm/upgrade/codemods/oxc-toolchain.mjs +59 -10
- package/esm/upgrade/codemods/oxc-toolchain.mjs.map +1 -1
- package/esm/upgrade/codemods/oxlint-rule-schemas.mjs +6 -0
- package/esm/upgrade/codemods/oxlint-rule-schemas.mjs.map +1 -0
- package/esm/upgrade/codemods/port-eslint-rules.mjs +343 -0
- package/esm/upgrade/codemods/port-eslint-rules.mjs.map +1 -0
- package/esm/upgrade/codemods/toolchain-templates.mjs +1 -1
- package/esm/upgrade/codemods/toolchain-templates.mjs.map +1 -1
- package/esm/upgrade/codemods/validate-oxlint-rule.mjs +60 -0
- package/esm/upgrade/codemods/validate-oxlint-rule.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +14 -7
- package/esm/upgrade/notes.mjs.map +1 -1
- package/esm/warlock-config/types.d.mts +0 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/llms-full.txt +243 -41
- package/llms.txt +2 -1
- package/package.json +19 -20
- package/skills/create-module/SKILL.md +2 -2
- package/skills/run-app/SKILL.md +3 -3
- package/skills/store-secrets/SKILL.md +40 -1
- package/skills/test-browser/SKILL.md +119 -0
- package/skills/upgrade-from-5-29/SKILL.md +70 -29
- package/skills/warlock-check/SKILL.md +3 -3
- package/skills/warlock-doctor/SKILL.md +2 -3
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs +0 -152
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.mjs.map +0 -1
- package/esm/dev-server/health-checker/eslint-support.mjs +0 -27
- package/esm/dev-server/health-checker/eslint-support.mjs.map +0 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.d.mts +0 -1
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs +0 -183
- package/esm/dev-server/health-checker/workers/eslint-health.worker.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
> ⚠ **Versioning: `@warlock.js/*` does not follow SemVer strictly — breaking changes may ship in a minor.** This is a deliberate decision, not an oversight: the framework is pre-adoption and the cost of a major per behaviour fix currently outweighs the benefit. **Pin an exact version or a tilde range (`~4.13.0`) if you need to opt into changes rather than receive them.** Every breaking change is marked **BREAKING** in its entry and summarised in an _Upgrading_ section at the top of the release. **This policy will change once the framework has consumers beyond its author.**
|
|
8
8
|
|
|
9
|
+
## 5.34.0 - 2026-10-19
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Select an isolated in-memory secrets backend through `SECRETS_DRIVER=memory` or `secrets.driver`, including built-server test deployments and explicit `SecretStore` options. Configuration overrides the environment; unknown drivers fail closed.
|
|
14
|
+
- Refuse the memory driver under `NODE_ENV=production` unless `allowMemoryInProduction: true` is explicitly configured; preserve log masking and namespace isolation without keychain/environment/file access.
|
|
15
|
+
- Add `warlock e2e` and the isolated `@warlock.js/core/e2e` Node subpath: owned app/build fixtures and browser profiles, direct CDP with native WebSocket, stable hit-tested clicks, editable text and navigation waits, responsive screenshots and overflow assertions, console/network allowlists, per-run evidence and PID-verified process-group cleanup. Include fake-CDP coverage, real-browser integration specs and a complete browser-testing guide/skill.
|
|
16
|
+
- `oxc-toolchain-removal` runs on `warlock upgrade --from 5.33 --to 5.34.0`, reusing the preserved 5.33 `oxc-toolchain` migration. It ports literal folder restrictions, native TypeScript rule equivalents, unused-import checks, globals and ignores; unsupported rules/options report their source and file scope without executing config. Native type-aware equivalents enable the optional app engine automatically.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **BREAKING** Migrated template configs enable Oxlint `correctness: error`; stale literal `healthCheckers.eslint` options are removed.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Detect a real lockfile beside `pnpm-workspace.yaml` before treating the directory as pnpm. npm apps with only `package-lock.json` now receive the correct install hint.
|
|
25
|
+
- Validate Postman draft-04 schemas with an explicit development-only Ajv adapter instead of resolving ESLint's private dependency. Generated source formatting checks use Oxfmt.
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
- **BREAKING** Remove the ESLint development checker, CLI selection, checker config option and production dependency. `warlock check --lint eslint` now fails with the 5.34.0 removal and Oxlint/Oxfmt migration guidance.
|
|
30
|
+
|
|
31
|
+
### Upgrading
|
|
32
|
+
|
|
33
|
+
- Run `npx warlock upgrade --from 5.33 --to 5.34.0 --dry-run`, review the findings, apply the migration and run `npm install`. Remove remaining custom ESLint checkers and formatting scripts after porting them. Existing Oxc rules are retained on conflict. Additional correctness diagnostics must be fixed in newly migrated apps. See `upgrade-from-5-29`, section “5.33 to 5.34 toolchain removal”.
|
|
34
|
+
|
|
9
35
|
## 5.33.0 - 2026-10-19
|
|
10
36
|
|
|
11
37
|
### Added
|
|
@@ -344,18 +370,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
344
370
|
|
|
345
371
|
- `request.input(key)` now sees a route param set with `Request.setParam()`. Before, it updated only `request.params`, so in a multi-site app (where the site dispatcher sets each page param) every dynamic segment read as `undefined` through `input()`, `only()` and `all()`. Params keep their precedence over query and body.
|
|
346
372
|
|
|
347
|
-
## 5.23.2 - 2026-09-26
|
|
348
|
-
|
|
349
|
-
### Changed
|
|
350
|
-
|
|
351
|
-
- Lockstep patch release; package APIs are unchanged.
|
|
352
|
-
|
|
353
|
-
## 5.23.1 - 2026-09-26
|
|
354
|
-
|
|
355
|
-
### Changed
|
|
356
|
-
|
|
357
|
-
- Lockstep patch release; package APIs are unchanged.
|
|
358
|
-
|
|
359
373
|
## 5.23.0 - 2026-09-25
|
|
360
374
|
|
|
361
375
|
### Added
|
|
@@ -370,12 +384,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
370
384
|
- `warlock add` installs with the project's package manager: the nearest `packageManager` field, then a `pnpm-workspace.yaml`, then the nearest lockfile, searched upward so an app inside a workspace uses the workspace's manager.
|
|
371
385
|
- `uniqueExceptCurrentUser` / `uniqueExceptCurrentId` / `existsExceptCurrentUser` / `existsExceptCurrentId` on `StringValidator` now return `this`, so `Infer<>` keeps the concrete output type.
|
|
372
386
|
|
|
373
|
-
## 5.22.1 - 2026-09-25
|
|
374
|
-
|
|
375
|
-
### Changed
|
|
376
|
-
|
|
377
|
-
- Lockstep patch release; package APIs are unchanged.
|
|
378
|
-
|
|
379
387
|
## 5.22.0 - 2026-09-25
|
|
380
388
|
|
|
381
389
|
### Fixed
|
|
@@ -415,10 +423,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
415
423
|
registration for a `GET` route at the same path. GET-only routes retain
|
|
416
424
|
Fastify's implicit HEAD behavior.
|
|
417
425
|
|
|
418
|
-
### Changed
|
|
419
|
-
|
|
420
|
-
- Lockstep release maintenance and dependency refresh.
|
|
421
|
-
|
|
422
426
|
## 5.20.0 - 2026-09-24
|
|
423
427
|
|
|
424
428
|
### Added
|
|
@@ -444,12 +448,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
444
448
|
|
|
445
449
|
- The repository cache is also cleared after the DB transaction commits (cascade `afterCommit`), fixing stale reads when a concurrent read re-cached the old row between the model event and `COMMIT`.
|
|
446
450
|
|
|
447
|
-
## 5.19.1 - 2026-09-23
|
|
448
|
-
|
|
449
|
-
### Changed
|
|
450
|
-
|
|
451
|
-
- Lockstep patch release aligning `@warlock.js/core` with the 5.19.1 family; no Core runtime API change.
|
|
452
|
-
|
|
453
451
|
## 5.19.0 - 2026-09-23
|
|
454
452
|
|
|
455
453
|
### Added
|
|
@@ -584,14 +582,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
584
582
|
|
|
585
583
|
- Backend file changes are picked up sooner in `warlock dev` (event debounce 150ms → 50ms).
|
|
586
584
|
|
|
587
|
-
## 5.10.0 - 2026-09-14
|
|
588
|
-
|
|
589
|
-
_Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.10.0._
|
|
590
|
-
|
|
591
|
-
## 5.9.0 - 2026-09-13
|
|
592
|
-
|
|
593
|
-
_Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.9.0._
|
|
594
|
-
|
|
595
585
|
## 5.8.0 - 2026-09-13
|
|
596
586
|
|
|
597
587
|
### Added
|
|
@@ -911,16 +901,6 @@ _Released in lockstep with the `@warlock.js/*` family; no package-specific chang
|
|
|
911
901
|
growing switch.
|
|
912
902
|
- Fixed 7 mojibake checkmarks in CLI output.
|
|
913
903
|
|
|
914
|
-
## 5.0.2 - 2026-08-25
|
|
915
|
-
|
|
916
|
-
No changes to `@warlock.js/core`. Released in lockstep with the `@warlock.js/web` SSR
|
|
917
|
-
fix (`ssr.noExternal`) — see that package's changelog.
|
|
918
|
-
|
|
919
|
-
## 5.0.1 - 2026-08-25
|
|
920
|
-
|
|
921
|
-
No changes to `@warlock.js/core`. Released in lockstep with the `create-warlock` vite
|
|
922
|
-
resolution pin and the `@warlock.js/web` peer narrowing — see those packages' changelogs.
|
|
923
|
-
|
|
924
904
|
## 5.0.0 - 2026-08-25
|
|
925
905
|
|
|
926
906
|
### Upgrading
|
|
@@ -31,7 +31,8 @@ function parseSkippedSteps(value) {
|
|
|
31
31
|
*/
|
|
32
32
|
async function checkCommandAction({ options }) {
|
|
33
33
|
const lint = stringOption(options.lint);
|
|
34
|
-
if (lint
|
|
34
|
+
if (lint === "eslint") throw new Error("ESLint support was removed in 5.34.0. Oxlint/Oxfmt is the default. Run npx warlock upgrade --from 5.33 --to 5.34.0.");
|
|
35
|
+
if (lint !== void 0 && lint !== "oxlint") throw new Error("Unknown lint tool. Use --lint oxlint.");
|
|
35
36
|
const base = stringOption(options.base);
|
|
36
37
|
const changed = options.changed === true || base !== void 0;
|
|
37
38
|
const project = stringOption(options.project);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-command.action.mjs","names":[],"sources":["../../../../../../../../../../packages/core/src/cli/commands/check/check-command.action.ts"],"sourcesContent":["import type { CommandActionData } from \"../../../commands/types\";\nimport { CHECK_STEPS, type CheckStepName } from \"../../../tooling/check/check-report.types\";\nimport { runCheck } from \"../../../tooling/check/run-check\";\nimport { flushAndExit } from \"../../flush-and-exit\";\nimport { childTypecheckRunner } from \"./child-typecheck\";\nimport { formatCheckSummary, printProblems } from \"./format-problems\";\n\nfunction stringOption(value: string | number | boolean | undefined): string | undefined {\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction isStepName(value: string): value is CheckStepName {\n return (CHECK_STEPS as readonly string[]).includes(value);\n}\n\n/** Parse `--skip typecheck,lint`; throws on an unknown step name. */\nexport function parseSkippedSteps(value: string | undefined): CheckStepName[] {\n if (!value) return [];\n\n const names = value\n .split(\",\")\n .map((name) => name.trim())\n .filter((name) => name.length > 0);\n const unknown = names.filter((name) => !isStepName(name));\n\n if (unknown.length > 0) {\n throw new Error(\n `Unknown check step(s): ${unknown.join(\", \")}. Valid steps: ${CHECK_STEPS.join(\", \")}.`,\n );\n }\n\n return names.filter(isStepName);\n}\n\n/**\n * Action behind `warlock check`.\n *\n * Runs {@link runCheck} with the typecheck in a child `warlock typecheck`\n * process (so a heap overflow is reported, not fatal), prints the problems and\n * the step summary — or, with `--json`, exactly the report — and exits with\n * the report's code: 0 clean, 1 your code, 2 this machine.\n *\n * @param data Parsed CLI args: `changed`, `base`, `skip`, `project`, `json`.\n */\nexport async function checkCommandAction({ options }: CommandActionData): Promise<void> {\n const lint = stringOption(options.lint);\n if (lint
|
|
1
|
+
{"version":3,"file":"check-command.action.mjs","names":[],"sources":["../../../../../../../../../../packages/core/src/cli/commands/check/check-command.action.ts"],"sourcesContent":["import type { CommandActionData } from \"../../../commands/types\";\nimport { CHECK_STEPS, type CheckStepName } from \"../../../tooling/check/check-report.types\";\nimport { runCheck } from \"../../../tooling/check/run-check\";\nimport { flushAndExit } from \"../../flush-and-exit\";\nimport { childTypecheckRunner } from \"./child-typecheck\";\nimport { formatCheckSummary, printProblems } from \"./format-problems\";\n\nfunction stringOption(value: string | number | boolean | undefined): string | undefined {\n return typeof value === \"string\" && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction isStepName(value: string): value is CheckStepName {\n return (CHECK_STEPS as readonly string[]).includes(value);\n}\n\n/** Parse `--skip typecheck,lint`; throws on an unknown step name. */\nexport function parseSkippedSteps(value: string | undefined): CheckStepName[] {\n if (!value) return [];\n\n const names = value\n .split(\",\")\n .map((name) => name.trim())\n .filter((name) => name.length > 0);\n const unknown = names.filter((name) => !isStepName(name));\n\n if (unknown.length > 0) {\n throw new Error(\n `Unknown check step(s): ${unknown.join(\", \")}. Valid steps: ${CHECK_STEPS.join(\", \")}.`,\n );\n }\n\n return names.filter(isStepName);\n}\n\n/**\n * Action behind `warlock check`.\n *\n * Runs {@link runCheck} with the typecheck in a child `warlock typecheck`\n * process (so a heap overflow is reported, not fatal), prints the problems and\n * the step summary — or, with `--json`, exactly the report — and exits with\n * the report's code: 0 clean, 1 your code, 2 this machine.\n *\n * @param data Parsed CLI args: `changed`, `base`, `skip`, `project`, `json`.\n */\nexport async function checkCommandAction({ options }: CommandActionData): Promise<void> {\n const lint = stringOption(options.lint);\n if (lint === \"eslint\") {\n throw new Error(\n \"ESLint support was removed in 5.34.0. Oxlint/Oxfmt is the default. Run npx warlock upgrade --from 5.33 --to 5.34.0.\",\n );\n }\n if (lint !== undefined && lint !== \"oxlint\") {\n throw new Error(\"Unknown lint tool. Use --lint oxlint.\");\n }\n const base = stringOption(options.base);\n const changed = options.changed === true || base !== undefined;\n const project = stringOption(options.project);\n const skip = parseSkippedSteps(stringOption(options.skip));\n const cliEntry = process.argv[1];\n const flags = [...(changed ? [\"--changed\"] : []), ...(base ? [\"--base\", base] : [])];\n\n const report = await runCheck({\n lint,\n changed,\n base,\n skip,\n project,\n ...(cliEntry ? { typecheck: childTypecheckRunner(cliEntry, flags) } : {}),\n });\n\n if (options.json === true) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printProblems(report.problems);\n\n if (report.problems.length > 0) console.log(\"\");\n\n for (const line of formatCheckSummary(report)) {\n console.log(line);\n }\n }\n\n if (report.exitCode !== 0) {\n await flushAndExit(report.exitCode);\n }\n}\n"],"mappings":";;;;;;;AAOA,SAAS,aAAa,OAAkE;CACtF,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI;AAC/E;AAEA,SAAS,WAAW,OAAuC;CACzD,OAAQ,YAAkC,SAAS,KAAK;AAC1D;;AAGA,SAAgB,kBAAkB,OAA4C;CAC5E,IAAI,CAAC,OAAO,OAAO,CAAC;CAEpB,MAAM,QAAQ,MACX,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,CAAC;CACnC,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,WAAW,IAAI,CAAC;CAExD,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,0BAA0B,QAAQ,KAAK,IAAI,EAAE,iBAAiB,YAAY,KAAK,IAAI,EAAE,EACvF;CAGF,OAAO,MAAM,OAAO,UAAU;AAChC;;;;;;;;;;;AAYA,eAAsB,mBAAmB,EAAE,WAA6C;CACtF,MAAM,OAAO,aAAa,QAAQ,IAAI;CACtC,IAAI,SAAS,UACX,MAAM,IAAI,MACR,qHACF;CAEF,IAAI,SAAS,UAAa,SAAS,UACjC,MAAM,IAAI,MAAM,uCAAuC;CAEzD,MAAM,OAAO,aAAa,QAAQ,IAAI;CACtC,MAAM,UAAU,QAAQ,YAAY,QAAQ,SAAS;CACrD,MAAM,UAAU,aAAa,QAAQ,OAAO;CAC5C,MAAM,OAAO,kBAAkB,aAAa,QAAQ,IAAI,CAAC;CACzD,MAAM,WAAW,QAAQ,KAAK;CAC9B,MAAM,QAAQ,CAAC,GAAI,UAAU,CAAC,WAAW,IAAI,CAAC,GAAI,GAAI,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,CAAE;CAEnF,MAAM,SAAS,MAAM,SAAS;EAC5B;EACA;EACA;EACA;EACA;EACA,GAAI,WAAW,EAAE,WAAW,qBAAqB,UAAU,KAAK,EAAE,IAAI,CAAC;CACzE,CAAC;CAED,IAAI,QAAQ,SAAS,MACnB,QAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;MACtC;EACL,cAAc,OAAO,QAAQ;EAE7B,IAAI,OAAO,SAAS,SAAS,GAAG,QAAQ,IAAI,EAAE;EAE9C,KAAK,MAAM,QAAQ,mBAAmB,MAAM,GAC1C,QAAQ,IAAI,IAAI;CAEpB;CAEA,IAAI,OAAO,aAAa,GACtB,MAAM,aAAa,OAAO,QAAQ;AAEtC"}
|
|
@@ -4,7 +4,7 @@ import { checkCommandAction } from "./check/check-command.action.mjs";
|
|
|
4
4
|
//#region ../../packages/core/src/cli/commands/check.command.ts
|
|
5
5
|
/**
|
|
6
6
|
* `warlock check` — the single verify command: typecheck, lint (the app's
|
|
7
|
-
* Oxlint config
|
|
7
|
+
* Oxlint config), the tests affected by the change, and framework
|
|
8
8
|
* convention checks, reported as one problem list. Exit 0 clean, 1 problems
|
|
9
9
|
* in the code, 2 problems with this machine.
|
|
10
10
|
*
|
|
@@ -19,7 +19,7 @@ const checkCommand = command({
|
|
|
19
19
|
options: [
|
|
20
20
|
{
|
|
21
21
|
text: "--lint",
|
|
22
|
-
description: "Lint tool: oxlint (default)
|
|
22
|
+
description: "Lint tool: oxlint (default)",
|
|
23
23
|
type: "string"
|
|
24
24
|
},
|
|
25
25
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.command.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/check.command.ts"],"sourcesContent":["import { command } from \"../../commands/cli-command\";\nimport { checkCommandAction } from \"./check/check-command.action\";\n\n/**\n * `warlock check` — the single verify command: typecheck, lint (the app's\n * Oxlint config
|
|
1
|
+
{"version":3,"file":"check.command.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/check.command.ts"],"sourcesContent":["import { command } from \"../../commands/cli-command\";\nimport { checkCommandAction } from \"./check/check-command.action\";\n\n/**\n * `warlock check` — the single verify command: typecheck, lint (the app's\n * Oxlint config), the tests affected by the change, and framework\n * convention checks, reported as one problem list. Exit 0 clean, 1 problems\n * in the code, 2 problems with this machine.\n *\n * No preload: the steps read files and run tools; nothing boots, connects or\n * binds a port, so `check` behaves the same with or without a database or\n * Redis running.\n */\nexport const checkCommand = command({\n name: \"check\",\n description:\n \"Typecheck, lint, run the affected tests and check conventions; one problem list, exit 1 for your code and 2 for this machine\",\n action: checkCommandAction,\n options: [\n {\n text: \"--lint\",\n description: \"Lint tool: oxlint (default)\",\n type: \"string\",\n },\n {\n text: \"--changed, -c\",\n description:\n \"Only the files changed against HEAD (plus untracked files) and what they affect\",\n type: \"boolean\",\n },\n {\n text: \"--base, -b\",\n description: \"Compare with the merge base of this ref instead of HEAD (implies --changed)\",\n type: \"string\",\n },\n {\n text: \"--skip, -s\",\n description:\n \"Comma-separated steps to skip: environment, typecheck, lint, tests, conventions\",\n type: \"string\",\n },\n {\n text: \"--project, -p\",\n description: \"tsconfig path (default tsconfig.json)\",\n type: \"string\",\n },\n {\n text: \"--json, -j\",\n description: \"Print the report as JSON\",\n type: \"boolean\",\n },\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,aACE;CACF,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { command } from "../../commands/cli-command.mjs";
|
|
2
|
+
import { loadE2ESpec } from "./load-e2e-spec.mjs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { glob } from "glob";
|
|
5
|
+
|
|
6
|
+
//#region ../../packages/core/src/cli/commands/e2e.command.ts
|
|
7
|
+
function readE2EOptions({ options }) {
|
|
8
|
+
return {
|
|
9
|
+
browser: typeof options.browser === "string" ? options.browser : void 0,
|
|
10
|
+
outputDirectory: typeof options.out === "string" ? options.out : void 0,
|
|
11
|
+
path: typeof options.path === "string" ? options.path.split(",").filter(Boolean).map((path) => resolve(path)) : void 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
async function e2eCommandAction(data) {
|
|
15
|
+
const patterns = data.args.length ? data.args : ["e2e/**/*.e2e.{ts,mts,js,mjs}"];
|
|
16
|
+
const files = [...new Set(await glob(patterns, {
|
|
17
|
+
absolute: true,
|
|
18
|
+
nodir: true,
|
|
19
|
+
ignore: ["**/node_modules/**", "**/.warlock/**"]
|
|
20
|
+
}))].sort();
|
|
21
|
+
if (!files.length) throw new Error(`No browser e2e specs found: ${patterns.join(", ")}. Create e2e/example.e2e.ts exporting defineE2E({ app, run }).`);
|
|
22
|
+
const { runE2E } = await import("../../e2e/run-e2e.mjs");
|
|
23
|
+
const failures = [];
|
|
24
|
+
for (const file of files) try {
|
|
25
|
+
const loaded = await loadE2ESpec(file);
|
|
26
|
+
try {
|
|
27
|
+
const result = await runE2E(loaded.spec, readE2EOptions(data));
|
|
28
|
+
console.log(`PASS ${file}\nEvidence: ${result.evidenceFile}`);
|
|
29
|
+
} finally {
|
|
30
|
+
await loaded.dispose();
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
failures.push(error);
|
|
34
|
+
console.error(`FAIL ${file}\n${String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
if (failures.length) throw new AggregateError(failures, `${failures.length} browser e2e spec(s) failed`);
|
|
37
|
+
}
|
|
38
|
+
const e2eCommand = command({
|
|
39
|
+
name: "e2e [specs...]",
|
|
40
|
+
description: "Run owned Chrome/Edge browser specs over CDP; write screenshots and evidence JSON",
|
|
41
|
+
action: e2eCommandAction,
|
|
42
|
+
options: [
|
|
43
|
+
{
|
|
44
|
+
text: "--browser",
|
|
45
|
+
description: "Chrome/Edge executable path (or WARLOCK_E2E_BROWSER)",
|
|
46
|
+
type: "string"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
text: "--out",
|
|
50
|
+
description: "Evidence parent directory (default .warlock/e2e)",
|
|
51
|
+
type: "string"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
text: "--path",
|
|
55
|
+
description: "Comma-separated fake-binary directories prepended to app PATH",
|
|
56
|
+
type: "string"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { e2eCommand, e2eCommandAction, readE2EOptions };
|
|
63
|
+
//# sourceMappingURL=e2e.command.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e.command.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/e2e.command.ts"],"sourcesContent":["import { glob } from \"glob\";\nimport { resolve } from \"node:path\";\nimport { command } from \"../../commands/cli-command\";\nimport type { CommandActionData } from \"../../commands/types\";\nimport { loadE2ESpec } from \"./load-e2e-spec\";\nimport type { RunE2EOptions } from \"../../e2e/types\";\n\nexport function readE2EOptions({ options }: CommandActionData): RunE2EOptions {\n return {\n browser: typeof options.browser === \"string\" ? options.browser : undefined,\n outputDirectory: typeof options.out === \"string\" ? options.out : undefined,\n path:\n typeof options.path === \"string\"\n ? options.path\n .split(\",\")\n .filter(Boolean)\n .map((path) => resolve(path))\n : undefined,\n };\n}\n\nexport async function e2eCommandAction(data: CommandActionData): Promise<void> {\n const patterns = data.args.length ? data.args : [\"e2e/**/*.e2e.{ts,mts,js,mjs}\"];\n const files = [\n ...new Set(\n await glob(patterns, {\n absolute: true,\n nodir: true,\n ignore: [\"**/node_modules/**\", \"**/.warlock/**\"],\n }),\n ),\n ].sort();\n if (!files.length)\n throw new Error(\n `No browser e2e specs found: ${patterns.join(\", \")}. Create e2e/example.e2e.ts exporting defineE2E({ app, run }).`,\n );\n const { runE2E } = await import(\"../../e2e/run-e2e\");\n const failures: unknown[] = [];\n for (const file of files) {\n try {\n const loaded = await loadE2ESpec(file);\n try {\n const result = await runE2E(loaded.spec, readE2EOptions(data));\n console.log(`PASS ${file}\\nEvidence: ${result.evidenceFile}`);\n } finally {\n await loaded.dispose();\n }\n } catch (error) {\n failures.push(error);\n console.error(`FAIL ${file}\\n${String(error)}`);\n }\n }\n if (failures.length)\n throw new AggregateError(failures, `${failures.length} browser e2e spec(s) failed`);\n}\n\nexport const e2eCommand = command({\n name: \"e2e [specs...]\",\n description: \"Run owned Chrome/Edge browser specs over CDP; write screenshots and evidence JSON\",\n action: e2eCommandAction,\n options: [\n {\n text: \"--browser\",\n description: \"Chrome/Edge executable path (or WARLOCK_E2E_BROWSER)\",\n type: \"string\",\n },\n {\n text: \"--out\",\n description: \"Evidence parent directory (default .warlock/e2e)\",\n type: \"string\",\n },\n {\n text: \"--path\",\n description: \"Comma-separated fake-binary directories prepended to app PATH\",\n type: \"string\",\n },\n ],\n});\n"],"mappings":";;;;;;AAOA,SAAgB,eAAe,EAAE,WAA6C;CAC5E,OAAO;EACL,SAAS,OAAO,QAAQ,YAAY,WAAW,QAAQ,UAAU;EACjE,iBAAiB,OAAO,QAAQ,QAAQ,WAAW,QAAQ,MAAM;EACjE,MACE,OAAO,QAAQ,SAAS,WACpB,QAAQ,KACL,MAAM,GAAG,CAAC,CACV,OAAO,OAAO,CAAC,CACf,KAAK,SAAS,QAAQ,IAAI,CAAC,IAC9B;CACR;AACF;AAEA,eAAsB,iBAAiB,MAAwC;CAC7E,MAAM,WAAW,KAAK,KAAK,SAAS,KAAK,OAAO,CAAC,8BAA8B;CAC/E,MAAM,QAAQ,CACZ,GAAG,IAAI,IACL,MAAM,KAAK,UAAU;EACnB,UAAU;EACV,OAAO;EACP,QAAQ,CAAC,sBAAsB,gBAAgB;CACjD,CAAC,CACH,CACF,CAAC,CAAC,KAAK;CACP,IAAI,CAAC,MAAM,QACT,MAAM,IAAI,MACR,+BAA+B,SAAS,KAAK,IAAI,EAAE,+DACrD;CACF,MAAM,EAAE,WAAW,MAAM,OAAO;CAChC,MAAM,WAAsB,CAAC;CAC7B,KAAK,MAAM,QAAQ,OACjB,IAAI;EACF,MAAM,SAAS,MAAM,YAAY,IAAI;EACrC,IAAI;GACF,MAAM,SAAS,MAAM,OAAO,OAAO,MAAM,eAAe,IAAI,CAAC;GAC7D,QAAQ,IAAI,QAAQ,KAAK,cAAc,OAAO,cAAc;EAC9D,UAAU;GACR,MAAM,OAAO,QAAQ;EACvB;CACF,SAAS,OAAO;EACd,SAAS,KAAK,KAAK;EACnB,QAAQ,MAAM,QAAQ,KAAK,IAAI,OAAO,KAAK,GAAG;CAChD;CAEF,IAAI,SAAS,QACX,MAAM,IAAI,eAAe,UAAU,GAAG,SAAS,OAAO,4BAA4B;AACtF;AAEA,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region ../../packages/core/src/cli/commands/load-e2e-spec.ts
|
|
7
|
+
/** Node 22.15 does not enable TypeScript stripping by default:
|
|
8
|
+
* https://nodejs.org/download/release/v22.15.0/docs/api/typescript.html#type-stripping
|
|
9
|
+
* Bundle local
|
|
10
|
+
* spec imports using core's existing compiler, retaining package imports so
|
|
11
|
+
* the spec uses the same installed harness as the CLI. The temporary module
|
|
12
|
+
* lives inside the project for normal node_modules resolution.
|
|
13
|
+
*/
|
|
14
|
+
async function loadE2ESpec(file) {
|
|
15
|
+
const directory = resolve(".warlock/e2e");
|
|
16
|
+
await mkdir(directory, { recursive: true });
|
|
17
|
+
const entry = join(directory, `spec-${randomUUID()}.mjs`);
|
|
18
|
+
try {
|
|
19
|
+
const { build } = await import("esbuild");
|
|
20
|
+
const compiled = (await build({
|
|
21
|
+
entryPoints: [file],
|
|
22
|
+
bundle: true,
|
|
23
|
+
packages: "external",
|
|
24
|
+
platform: "node",
|
|
25
|
+
target: "node22",
|
|
26
|
+
format: "esm",
|
|
27
|
+
sourcemap: "inline",
|
|
28
|
+
write: false,
|
|
29
|
+
logLevel: "silent"
|
|
30
|
+
})).outputFiles[0];
|
|
31
|
+
if (!compiled) throw new Error(`Could not compile browser spec ${file}`);
|
|
32
|
+
await writeFile(entry, compiled.contents, { mode: 384 });
|
|
33
|
+
const module = await import(pathToFileURL(entry).href);
|
|
34
|
+
if (!module.default) throw new Error(`${file} must default-export defineE2E({ app, run })`);
|
|
35
|
+
return {
|
|
36
|
+
spec: module.default,
|
|
37
|
+
dispose: () => rm(entry, { force: true })
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
await rm(entry, { force: true });
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { loadE2ESpec };
|
|
47
|
+
//# sourceMappingURL=load-e2e-spec.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-e2e-spec.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/cli/commands/load-e2e-spec.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, rm, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { E2ESpec } from \"../../e2e/types\";\n\n/** Node 22.15 does not enable TypeScript stripping by default:\n * https://nodejs.org/download/release/v22.15.0/docs/api/typescript.html#type-stripping\n * Bundle local\n * spec imports using core's existing compiler, retaining package imports so\n * the spec uses the same installed harness as the CLI. The temporary module\n * lives inside the project for normal node_modules resolution.\n */\nexport async function loadE2ESpec(\n file: string,\n): Promise<{ spec: E2ESpec; dispose: () => Promise<void> }> {\n const directory = resolve(\".warlock/e2e\");\n await mkdir(directory, { recursive: true });\n const entry = join(directory, `spec-${randomUUID()}.mjs`);\n try {\n const { build } = await import(\"esbuild\");\n const output = await build({\n entryPoints: [file],\n bundle: true,\n packages: \"external\",\n platform: \"node\",\n target: \"node22\",\n format: \"esm\",\n sourcemap: \"inline\",\n write: false,\n logLevel: \"silent\",\n });\n const compiled = output.outputFiles[0];\n if (!compiled) throw new Error(`Could not compile browser spec ${file}`);\n await writeFile(entry, compiled.contents, { mode: 0o600 });\n const module = (await import(pathToFileURL(entry).href)) as { default?: E2ESpec };\n if (!module.default) throw new Error(`${file} must default-export defineE2E({ app, run })`);\n return { spec: module.default, dispose: () => rm(entry, { force: true }) };\n } catch (error) {\n await rm(entry, { force: true });\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,eAAsB,YACpB,MAC0D;CAC1D,MAAM,YAAY,QAAQ,cAAc;CACxC,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAC1C,MAAM,QAAQ,KAAK,WAAW,QAAQ,WAAW,EAAE,KAAK;CACxD,IAAI;EACF,MAAM,EAAE,UAAU,MAAM,OAAO;EAY/B,MAAM,YAAW,MAXI,MAAM;GACzB,aAAa,CAAC,IAAI;GAClB,QAAQ;GACR,UAAU;GACV,UAAU;GACV,QAAQ;GACR,QAAQ;GACR,WAAW;GACX,OAAO;GACP,UAAU;EACZ,CAAC,EACsB,CAAC,YAAY;EACpC,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,kCAAkC,MAAM;EACvE,MAAM,UAAU,OAAO,SAAS,UAAU,EAAE,MAAM,IAAM,CAAC;EACzD,MAAM,SAAU,MAAM,OAAO,cAAc,KAAK,CAAC,CAAC;EAClD,IAAI,CAAC,OAAO,SAAS,MAAM,IAAI,MAAM,GAAG,KAAK,6CAA6C;EAC1F,OAAO;GAAE,MAAM,OAAO;GAAS,eAAe,GAAG,OAAO,EAAE,OAAO,KAAK,CAAC;EAAE;CAC3E,SAAS,OAAO;EACd,MAAM,GAAG,OAAO,EAAE,OAAO,KAAK,CAAC;EAC/B,MAAM;CACR;AACF"}
|
|
@@ -6,6 +6,7 @@ import { buildCommand } from "./commands/build.command.mjs";
|
|
|
6
6
|
import { checkCommand } from "./commands/check.command.mjs";
|
|
7
7
|
import { createDatabaseCommand } from "./commands/create-database.command.mjs";
|
|
8
8
|
import { devServerCommand } from "./commands/dev-server.command.mjs";
|
|
9
|
+
import { e2eCommand } from "./commands/e2e.command.mjs";
|
|
9
10
|
import { doctorCommand } from "./commands/doctor.command.mjs";
|
|
10
11
|
import { dropTablesCommand } from "./commands/drop-tables.command.mjs";
|
|
11
12
|
import { explainCommand } from "./commands/explain.command.mjs";
|
|
@@ -35,6 +36,7 @@ const frameworkCommands = [
|
|
|
35
36
|
openApiGeneratorCommand,
|
|
36
37
|
postmanGeneratorCommand,
|
|
37
38
|
testCommand,
|
|
39
|
+
e2eCommand,
|
|
38
40
|
aiCommand,
|
|
39
41
|
doctorCommand,
|
|
40
42
|
routesCommand,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework-cli-commands.mjs","names":[],"sources":["../../../../../../../../packages/core/src/cli/framework-cli-commands.ts"],"sourcesContent":["import { aiCommand } from \"./commands/ai.command\";\nimport { addCommand } from \"./commands/add.command\";\nimport { agentsMdCommand } from \"./commands/agents-md.command\";\nimport { boardCommand } from \"./commands/board.command\";\nimport { buildCommand } from \"./commands/build.command\";\nimport { checkCommand } from \"./commands/check.command\";\nimport { createDatabaseCommand } from \"./commands/create-database.command\";\nimport { devServerCommand } from \"./commands/dev-server.command\";\nimport { doctorCommand } from \"./commands/doctor.command\";\nimport { dropTablesCommand } from \"./commands/drop-tables.command\";\nimport { explainCommand } from \"./commands/explain.command\";\nimport {\n generateCommand,\n generateControllerCommand,\n generateEndpointCommand,\n generateMigrationCommand,\n generateModelCommand,\n generateModuleCommand,\n generateRepositoryCommand,\n generateResourceCommand,\n generateServiceCommand,\n generateUseCaseCommand,\n makeCommand,\n} from \"./commands/generate/generate.command\";\nimport { inspectCommand } from \"./commands/inspect.command\";\nimport { issueCommand } from \"./commands/issue.command\";\nimport { mcpCommand } from \"./commands/mcp.command\";\nimport { migrateCommand } from \"./commands/migrate.command\";\nimport { openApiGeneratorCommand } from \"./commands/openapi-generator.command\";\nimport { postmanGeneratorCommand } from \"./commands/postman-generator.command\";\nimport { pm2Command } from \"./commands/pm2.command\";\nimport { routesCommand, routesDiffCommand } from \"./commands/routes.command\";\nimport { seedCommand } from \"./commands/seed.command\";\nimport { startProductionCommand } from \"./commands/start-production.command\";\nimport { traceCommand } from \"./commands/trace.command\";\nimport { storagePutCommand } from \"./commands/storage-put.command\";\nimport { typecheckCommand } from \"./commands/typecheck.command\";\nimport { testCommand } from \"./commands/test.command\";\nimport { typingsGeneratorCommand } from \"./commands/typings-generator.command\";\nimport { updateCommand } from \"./commands/update.command\";\nimport { upgradeCommand } from \"./commands/upgrade.command\";\n\nexport const frameworkCommands = [\n // development commands\n devServerCommand,\n typingsGeneratorCommand,\n openApiGeneratorCommand,\n postmanGeneratorCommand,\n\n // testing commands\n testCommand,\n\n // diagnostics commands\n aiCommand,\n doctorCommand,\n routesCommand,\n routesDiffCommand,\n inspectCommand,\n explainCommand,\n issueCommand,\n checkCommand,\n typecheckCommand,\n\n // agent tooling commands\n mcpCommand,\n traceCommand,\n agentsMdCommand,\n boardCommand,\n\n // production commands\n buildCommand,\n startProductionCommand,\n pm2Command,\n\n // database commands\n migrateCommand,\n seedCommand,\n createDatabaseCommand,\n dropTablesCommand,\n\n // generation/installation commands\n addCommand,\n updateCommand,\n upgradeCommand,\n\n // scaffolding commands\n generateCommand,\n makeCommand,\n generateEndpointCommand,\n generateModuleCommand,\n generateControllerCommand,\n generateServiceCommand,\n generateUseCaseCommand,\n generateModelCommand,\n generateRepositoryCommand,\n generateResourceCommand,\n generateMigrationCommand,\n\n // storage commands\n storagePutCommand,\n];\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"framework-cli-commands.mjs","names":[],"sources":["../../../../../../../../packages/core/src/cli/framework-cli-commands.ts"],"sourcesContent":["import { aiCommand } from \"./commands/ai.command\";\nimport { addCommand } from \"./commands/add.command\";\nimport { agentsMdCommand } from \"./commands/agents-md.command\";\nimport { boardCommand } from \"./commands/board.command\";\nimport { buildCommand } from \"./commands/build.command\";\nimport { checkCommand } from \"./commands/check.command\";\nimport { createDatabaseCommand } from \"./commands/create-database.command\";\nimport { devServerCommand } from \"./commands/dev-server.command\";\nimport { e2eCommand } from \"./commands/e2e.command\";\nimport { doctorCommand } from \"./commands/doctor.command\";\nimport { dropTablesCommand } from \"./commands/drop-tables.command\";\nimport { explainCommand } from \"./commands/explain.command\";\nimport {\n generateCommand,\n generateControllerCommand,\n generateEndpointCommand,\n generateMigrationCommand,\n generateModelCommand,\n generateModuleCommand,\n generateRepositoryCommand,\n generateResourceCommand,\n generateServiceCommand,\n generateUseCaseCommand,\n makeCommand,\n} from \"./commands/generate/generate.command\";\nimport { inspectCommand } from \"./commands/inspect.command\";\nimport { issueCommand } from \"./commands/issue.command\";\nimport { mcpCommand } from \"./commands/mcp.command\";\nimport { migrateCommand } from \"./commands/migrate.command\";\nimport { openApiGeneratorCommand } from \"./commands/openapi-generator.command\";\nimport { postmanGeneratorCommand } from \"./commands/postman-generator.command\";\nimport { pm2Command } from \"./commands/pm2.command\";\nimport { routesCommand, routesDiffCommand } from \"./commands/routes.command\";\nimport { seedCommand } from \"./commands/seed.command\";\nimport { startProductionCommand } from \"./commands/start-production.command\";\nimport { traceCommand } from \"./commands/trace.command\";\nimport { storagePutCommand } from \"./commands/storage-put.command\";\nimport { typecheckCommand } from \"./commands/typecheck.command\";\nimport { testCommand } from \"./commands/test.command\";\nimport { typingsGeneratorCommand } from \"./commands/typings-generator.command\";\nimport { updateCommand } from \"./commands/update.command\";\nimport { upgradeCommand } from \"./commands/upgrade.command\";\n\nexport const frameworkCommands = [\n // development commands\n devServerCommand,\n typingsGeneratorCommand,\n openApiGeneratorCommand,\n postmanGeneratorCommand,\n\n // testing commands\n testCommand,\n e2eCommand,\n\n // diagnostics commands\n aiCommand,\n doctorCommand,\n routesCommand,\n routesDiffCommand,\n inspectCommand,\n explainCommand,\n issueCommand,\n checkCommand,\n typecheckCommand,\n\n // agent tooling commands\n mcpCommand,\n traceCommand,\n agentsMdCommand,\n boardCommand,\n\n // production commands\n buildCommand,\n startProductionCommand,\n pm2Command,\n\n // database commands\n migrateCommand,\n seedCommand,\n createDatabaseCommand,\n dropTablesCommand,\n\n // generation/installation commands\n addCommand,\n updateCommand,\n upgradeCommand,\n\n // scaffolding commands\n generateCommand,\n makeCommand,\n generateEndpointCommand,\n generateModuleCommand,\n generateControllerCommand,\n generateServiceCommand,\n generateUseCaseCommand,\n generateModelCommand,\n generateRepositoryCommand,\n generateResourceCommand,\n generateMigrationCommand,\n\n // storage commands\n storagePutCommand,\n];\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,oBAAoB;CAE/B;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseHealthChecker } from "./base-health-checker.mjs";
|
|
2
1
|
import { OxlintRunner } from "../oxlint-runner.mjs";
|
|
2
|
+
import { BaseHealthChecker } from "./base-health-checker.mjs";
|
|
3
3
|
|
|
4
4
|
//#region ../../packages/core/src/dev-server/health-checker/checkers/oxlint-health-checker.ts
|
|
5
5
|
var OxlintHealthChecker = class extends BaseHealthChecker {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { hasEslint, warnEslintDeprecation } from "./eslint-support.mjs";
|
|
2
|
-
import { EslintHealthChecker } from "./checkers/eslint-health-checker.mjs";
|
|
3
1
|
import { findOxlint } from "./oxlint-runner.mjs";
|
|
4
2
|
import { OxlintHealthChecker } from "./checkers/oxlint-health-checker.mjs";
|
|
5
3
|
import { TypescriptHealthChecker } from "./checkers/typescript-health-checker.mjs";
|
|
@@ -7,12 +5,9 @@ import { TypescriptHealthChecker } from "./checkers/typescript-health-checker.mj
|
|
|
7
5
|
//#region ../../packages/core/src/dev-server/health-checker/default-health-checkers.ts
|
|
8
6
|
/** Explicit arrays remain custom-only; built-in booleans select among available checkers. */
|
|
9
7
|
function defaultHealthCheckers(options = {}, cwd = process.cwd()) {
|
|
8
|
+
if ("eslint" in options) throw new Error("ESLint support was removed in 5.34.0. Remove healthCheckers.eslint and use Oxlint/Oxfmt; run npx warlock upgrade --from 5.33 --to 5.34.0.");
|
|
10
9
|
const checkers = [];
|
|
11
10
|
if (options.typescript !== false) checkers.push(new TypescriptHealthChecker());
|
|
12
|
-
if (options.eslint === true) {
|
|
13
|
-
warnEslintDeprecation();
|
|
14
|
-
if (hasEslint(cwd)) checkers.push(new EslintHealthChecker());
|
|
15
|
-
}
|
|
16
11
|
if (options.oxlint !== false && findOxlint(cwd)) checkers.push(new OxlintHealthChecker());
|
|
17
12
|
return checkers;
|
|
18
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-health-checkers.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/dev-server/health-checker/default-health-checkers.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"default-health-checkers.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/dev-server/health-checker/default-health-checkers.ts"],"sourcesContent":["import { OxlintHealthChecker } from \"./checkers/oxlint-health-checker\";\nimport { TypescriptHealthChecker } from \"./checkers/typescript-health-checker\";\nimport type {\n BuiltInHealthCheckerOptions,\n FileHealthCheckerContract,\n} from \"./file-health-checker.contract\";\nimport { findOxlint } from \"./oxlint-runner\";\n\n/** Explicit arrays remain custom-only; built-in booleans select among available checkers. */\nexport function defaultHealthCheckers(\n options: BuiltInHealthCheckerOptions = {},\n cwd = process.cwd(),\n): FileHealthCheckerContract[] {\n if (\"eslint\" in options)\n throw new Error(\n \"ESLint support was removed in 5.34.0. Remove healthCheckers.eslint and use Oxlint/Oxfmt; run npx warlock upgrade --from 5.33 --to 5.34.0.\",\n );\n const checkers: FileHealthCheckerContract[] = [];\n if (options.typescript !== false) checkers.push(new TypescriptHealthChecker());\n if (options.oxlint !== false && findOxlint(cwd)) checkers.push(new OxlintHealthChecker());\n return checkers;\n}\n"],"mappings":";;;;;;AASA,SAAgB,sBACd,UAAuC,CAAC,GACxC,MAAM,QAAQ,IAAI,GACW;CAC7B,IAAI,YAAY,SACd,MAAM,IAAI,MACR,2IACF;CACF,MAAM,WAAwC,CAAC;CAC/C,IAAI,QAAQ,eAAe,OAAO,SAAS,KAAK,IAAI,wBAAwB,CAAC;CAC7E,IAAI,QAAQ,WAAW,SAAS,WAAW,GAAG,GAAG,SAAS,KAAK,IAAI,oBAAoB,CAAC;CACxF,OAAO;AACT"}
|
|
@@ -62,8 +62,6 @@ export interface FileHealthCheckerContract {
|
|
|
62
62
|
/** Select background checks; TypeScript and configured Oxlint run by default. */
|
|
63
63
|
export type BuiltInHealthCheckerOptions = {
|
|
64
64
|
typescript?: boolean;
|
|
65
|
-
/** @deprecated Removed in 5.34.0. Explicit opt-in only; default false. */
|
|
66
|
-
eslint?: boolean;
|
|
67
65
|
oxlint?: boolean;
|
|
68
66
|
};
|
|
69
67
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-health-checker.contract.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/dev-server/health-checker/file-health-checker.contract.ts"],"mappings":";;;YAGY;EACV;EACA;IACE;IACA;;EAEF;IACE;IACA;;EAEF;IACE;IACA;;;iBAIa;;;;EAIf;;;;;;;;;;;;;;EAeA;;;;;EAMA;;EAGA,oBAAoB;;;;EAKpB,cAAc,OAAO,gBAAgB;;;;EAKrC,WAAW,MAAM;;;;EAKjB,MAAM,MAAM,cAAc,QAAQ;;;;EAKlC,SAAS,QAAQ;;;YAIP;EACV
|
|
1
|
+
{"version":3,"file":"file-health-checker.contract.d.mts","names":[],"sources":["../../../../../../../../../packages/core/src/dev-server/health-checker/file-health-checker.contract.ts"],"mappings":";;;YAGY;EACV;EACA;IACE;IACA;;EAEF;IACE;IACA;;EAEF;IACE;IACA;;;iBAIa;;;;EAIf;;;;;;;;;;;;;;EAeA;;;;;EAMA;;EAGA,oBAAoB;;;;EAKpB,cAAc,OAAO,gBAAgB;;;;EAKrC,WAAW,MAAM;;;;EAKjB,MAAM,MAAM,cAAc,QAAQ;;;;EAKlC,SAAS,QAAQ;;;YAIP;EACV;EACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-health-result.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/dev-server/health-checker/file-health-result.ts"],"sourcesContent":["type FileHealthStats = {\n /**\n * File Health State\n */\n state: \"healthy\" | \"defective\";\n /**\n * Number of errors\n */\n errors: number;\n /**\n * Number of warnings\n */\n warnings: number;\n};\n\ntype FileHealthMessage = {\n /**\n * Message\n */\n message: string;\n /**\n * Type of the message\n */\n type: \"error\" | \"warning\";\n /**\n * Line number\n */\n lineNumber: number;\n /**\n * Column number\n */\n columnNumber: number;\n /**\n * Length of the error/warning span\n */\n length?: number;\n /**\n *
|
|
1
|
+
{"version":3,"file":"file-health-result.mjs","names":[],"sources":["../../../../../../../../../packages/core/src/dev-server/health-checker/file-health-result.ts"],"sourcesContent":["type FileHealthStats = {\n /**\n * File Health State\n */\n state: \"healthy\" | \"defective\";\n /**\n * Number of errors\n */\n errors: number;\n /**\n * Number of warnings\n */\n warnings: number;\n};\n\ntype FileHealthMessage = {\n /**\n * Message\n */\n message: string;\n /**\n * Type of the message\n */\n type: \"error\" | \"warning\";\n /**\n * Line number\n */\n lineNumber: number;\n /**\n * Column number\n */\n columnNumber: number;\n /**\n * Length of the error/warning span\n */\n length?: number;\n /**\n * Linter rule ID\n */\n ruleId?: string;\n};\n\nexport class FileHealthResult {\n /**\n * Result of the health check\n */\n public result: \"healthy\" | \"defective\" = \"healthy\";\n\n /**\n * Messages list (either for errors or warnings)\n */\n public messages: FileHealthMessage[] = [];\n\n /**\n * Mark as healthy\n */\n public markAsHealthy(): void {\n this.result = \"healthy\";\n }\n\n /**\n * Add errors\n */\n public addErrors(messages: FileHealthMessage[]): void {\n this.result = \"defective\";\n this.messages.push(...messages);\n }\n\n /**\n * Add warnings\n */\n public addWarnings(messages: FileHealthMessage[]): void {\n this.result = \"defective\";\n this.messages.push(...messages);\n }\n\n /**\n * Get file health stats\n */\n public getStats(): FileHealthStats {\n return {\n state: this.result,\n errors: this.messages.filter((message) => message.type === \"error\").length,\n warnings: this.messages.filter((message) => message.type === \"warning\").length,\n };\n }\n}\n"],"mappings":";AA0CA,IAAa,mBAAb,MAA8B;;gBAIa;kBAKF,CAAC;;;;;CAKxC,AAAO,gBAAsB;EAC3B,KAAK,SAAS;CAChB;;;;CAKA,AAAO,UAAU,UAAqC;EACpD,KAAK,SAAS;EACd,KAAK,SAAS,KAAK,GAAG,QAAQ;CAChC;;;;CAKA,AAAO,YAAY,UAAqC;EACtD,KAAK,SAAS;EACd,KAAK,SAAS,KAAK,GAAG,QAAQ;CAChC;;;;CAKA,AAAO,WAA4B;EACjC,OAAO;GACL,OAAO,KAAK;GACZ,QAAQ,KAAK,SAAS,QAAQ,YAAY,QAAQ,SAAS,OAAO,CAAC,CAAC;GACpE,UAAU,KAAK,SAAS,QAAQ,YAAY,QAAQ,SAAS,SAAS,CAAC,CAAC;EAC1E;CACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/browser.ts"],"mappings":";wBA2CsB,kBACpB,gCACC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { CdpConnection } from "./cdp.mjs";
|
|
2
|
+
import { OwnedProcess } from "./owned-process.mjs";
|
|
3
|
+
import { isolatedEnvironment } from "./fixture.mjs";
|
|
4
|
+
import { delimiter, join, resolve } from "node:path";
|
|
5
|
+
import { constants } from "node:fs";
|
|
6
|
+
import { access, mkdir, readFile } from "node:fs/promises";
|
|
7
|
+
import { setTimeout } from "node:timers/promises";
|
|
8
|
+
|
|
9
|
+
//#region ../../packages/core/src/e2e/browser.ts
|
|
10
|
+
function browserCandidates(platform, env) {
|
|
11
|
+
const names = platform === "win32" ? ["chrome.exe", "msedge.exe"] : [
|
|
12
|
+
"google-chrome",
|
|
13
|
+
"google-chrome-stable",
|
|
14
|
+
"chromium",
|
|
15
|
+
"chromium-browser",
|
|
16
|
+
"microsoft-edge",
|
|
17
|
+
"microsoft-edge-stable"
|
|
18
|
+
];
|
|
19
|
+
const paths = (env.PATH ?? "").split(platform === "win32" ? ";" : delimiter).filter(Boolean).flatMap((path) => names.map((name) => join(path, name)));
|
|
20
|
+
if (platform === "darwin") paths.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", join(env.HOME ?? "", "Applications/Google Chrome.app/Contents/MacOS/Google Chrome"), join(env.HOME ?? "", "Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"));
|
|
21
|
+
if (platform === "win32") for (const root of [
|
|
22
|
+
env.PROGRAMFILES,
|
|
23
|
+
env["PROGRAMFILES(X86)"],
|
|
24
|
+
env.LOCALAPPDATA
|
|
25
|
+
].filter((path) => Boolean(path))) paths.push(join(root, "Google", "Chrome", "Application", "chrome.exe"), join(root, "Microsoft", "Edge", "Application", "msedge.exe"));
|
|
26
|
+
return paths;
|
|
27
|
+
}
|
|
28
|
+
async function findBrowserBinary(override = process.env.WARLOCK_E2E_BROWSER) {
|
|
29
|
+
const candidates = override ? [resolve(override)] : browserCandidates(process.platform, process.env);
|
|
30
|
+
for (const candidate of candidates) try {
|
|
31
|
+
await access(candidate, process.platform === "win32" ? constants.F_OK : constants.X_OK);
|
|
32
|
+
return candidate;
|
|
33
|
+
} catch {}
|
|
34
|
+
if (override) throw new Error(`Browser override is not executable: ${override}`);
|
|
35
|
+
}
|
|
36
|
+
var OwnedBrowser = class {
|
|
37
|
+
constructor(root, evidence) {
|
|
38
|
+
this.root = root;
|
|
39
|
+
this.evidence = evidence;
|
|
40
|
+
}
|
|
41
|
+
async start(options = {}, signal) {
|
|
42
|
+
const binary = await findBrowserBinary(options.binary);
|
|
43
|
+
if (!binary) throw new Error("No Chrome or Edge binary found. Install one or set --browser / WARLOCK_E2E_BROWSER to its executable path.");
|
|
44
|
+
if (options.args?.some((arg) => !arg.startsWith("--") || /^--(?:user-data-dir|remote-debugging|headless)/.test(arg))) throw new Error("Browser args must be flags and cannot replace the owned profile, debugging endpoint, or headless mode");
|
|
45
|
+
const profile = join(this.root, "browser-profile");
|
|
46
|
+
await mkdir(profile, { recursive: true });
|
|
47
|
+
this.process = await OwnedProcess.start("browser", [
|
|
48
|
+
binary,
|
|
49
|
+
"--headless",
|
|
50
|
+
"--remote-debugging-port=0",
|
|
51
|
+
`--user-data-dir=${profile}`,
|
|
52
|
+
...options.args ?? [],
|
|
53
|
+
"about:blank"
|
|
54
|
+
], this.root, isolatedEnvironment(this.root, {}), this.root, this.evidence.processes, (owned) => {
|
|
55
|
+
this.process = owned;
|
|
56
|
+
});
|
|
57
|
+
const deadline = Date.now() + 15e3;
|
|
58
|
+
let reason = "DevToolsActivePort is not ready";
|
|
59
|
+
while (Date.now() < deadline) {
|
|
60
|
+
signal?.throwIfAborted();
|
|
61
|
+
this.process.assertRunning();
|
|
62
|
+
try {
|
|
63
|
+
const port = Number((await readFile(join(profile, "DevToolsActivePort"), "utf8")).split("\n")[0]);
|
|
64
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) throw new Error("Invalid owned browser debugging port");
|
|
65
|
+
const response = await fetch(`http://127.0.0.1:${port}/json/list`, { signal: AbortSignal.timeout(1e3) });
|
|
66
|
+
if (!response.ok) throw new Error(`CDP discovery returned HTTP ${response.status}`);
|
|
67
|
+
const target = (await response.json()).find((candidate) => candidate.type === "page" && candidate.url === "about:blank");
|
|
68
|
+
if (!target?.webSocketDebuggerUrl) throw new Error("Owned browser has no about:blank page target");
|
|
69
|
+
const endpoint = new URL(target.webSocketDebuggerUrl);
|
|
70
|
+
if (endpoint.protocol !== "ws:" || endpoint.hostname !== "127.0.0.1" || Number(endpoint.port) !== port) throw new Error("CDP discovery returned a non-owned endpoint");
|
|
71
|
+
this.connection = await CdpConnection.connect(endpoint.href);
|
|
72
|
+
return this.connection;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
reason = String(error);
|
|
75
|
+
await setTimeout(50);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Browser startup timed out: ${reason}\n${this.process.logs().slice(-8e3)}`);
|
|
79
|
+
}
|
|
80
|
+
async stop() {
|
|
81
|
+
this.connection?.close();
|
|
82
|
+
await this.process?.stop();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
export { OwnedBrowser, browserCandidates, findBrowserBinary };
|
|
88
|
+
//# sourceMappingURL=browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.mjs","names":["delay"],"sources":["../../../../../../../../packages/core/src/e2e/browser.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { access, mkdir, readFile } from \"node:fs/promises\";\nimport { delimiter, join, resolve } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { CdpConnection } from \"./cdp\";\nimport { isolatedEnvironment } from \"./fixture\";\nimport { OwnedProcess } from \"./owned-process\";\nimport type { BrowserOptions, E2EEvidence } from \"./types\";\n\nexport function browserCandidates(platform: NodeJS.Platform, env: NodeJS.ProcessEnv): string[] {\n const names =\n platform === \"win32\"\n ? [\"chrome.exe\", \"msedge.exe\"]\n : [\n \"google-chrome\",\n \"google-chrome-stable\",\n \"chromium\",\n \"chromium-browser\",\n \"microsoft-edge\",\n \"microsoft-edge-stable\",\n ];\n const paths = (env.PATH ?? \"\")\n .split(platform === \"win32\" ? \";\" : delimiter)\n .filter(Boolean)\n .flatMap((path) => names.map((name) => join(path, name)));\n if (platform === \"darwin\")\n paths.push(\n \"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\",\n \"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\",\n join(env.HOME ?? \"\", \"Applications/Google Chrome.app/Contents/MacOS/Google Chrome\"),\n join(env.HOME ?? \"\", \"Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge\"),\n );\n if (platform === \"win32\")\n for (const root of [env.PROGRAMFILES, env[\"PROGRAMFILES(X86)\"], env.LOCALAPPDATA].filter(\n (path): path is string => Boolean(path),\n ))\n paths.push(\n join(root, \"Google\", \"Chrome\", \"Application\", \"chrome.exe\"),\n join(root, \"Microsoft\", \"Edge\", \"Application\", \"msedge.exe\"),\n );\n return paths;\n}\n\nexport async function findBrowserBinary(\n override = process.env.WARLOCK_E2E_BROWSER,\n): Promise<string | undefined> {\n const candidates = override\n ? [resolve(override)]\n : browserCandidates(process.platform, process.env);\n for (const candidate of candidates) {\n try {\n await access(candidate, process.platform === \"win32\" ? constants.F_OK : constants.X_OK);\n return candidate;\n } catch {\n /* Try the next known installation. */\n }\n }\n if (override) throw new Error(`Browser override is not executable: ${override}`);\n return undefined;\n}\n\nexport class OwnedBrowser {\n connection?: CdpConnection;\n process?: OwnedProcess;\n constructor(\n private root: string,\n private evidence: E2EEvidence,\n ) {}\n\n async start(options: BrowserOptions = {}, signal?: AbortSignal): Promise<CdpConnection> {\n const binary = await findBrowserBinary(options.binary);\n if (!binary)\n throw new Error(\n \"No Chrome or Edge binary found. Install one or set --browser / WARLOCK_E2E_BROWSER to its executable path.\",\n );\n if (\n options.args?.some(\n (arg) =>\n !arg.startsWith(\"--\") || /^--(?:user-data-dir|remote-debugging|headless)/.test(arg),\n )\n )\n throw new Error(\n \"Browser args must be flags and cannot replace the owned profile, debugging endpoint, or headless mode\",\n );\n const profile = join(this.root, \"browser-profile\");\n await mkdir(profile, { recursive: true });\n // Headless flag: https://developer.chrome.com/docs/chromium/headless\n // Port 0 and discovery: https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target\n // Profile required by Chrome 136+: https://developer.chrome.com/blog/remote-debugging-port\n this.process = await OwnedProcess.start(\n \"browser\",\n [\n binary,\n \"--headless\",\n \"--remote-debugging-port=0\",\n `--user-data-dir=${profile}`,\n ...(options.args ?? []),\n \"about:blank\",\n ],\n this.root,\n isolatedEnvironment(this.root, {}),\n this.root,\n this.evidence.processes,\n (owned) => {\n this.process = owned;\n },\n );\n const deadline = Date.now() + 15_000;\n let reason = \"DevToolsActivePort is not ready\";\n while (Date.now() < deadline) {\n signal?.throwIfAborted();\n this.process.assertRunning();\n try {\n const port = Number(\n (await readFile(join(profile, \"DevToolsActivePort\"), \"utf8\")).split(\"\\n\")[0],\n );\n if (!Number.isInteger(port) || port <= 0 || port > 65535)\n throw new Error(\"Invalid owned browser debugging port\");\n // HTTP discovery endpoints: https://chromedevtools.github.io/devtools-protocol/#http-endpoints\n const response = await fetch(`http://127.0.0.1:${port}/json/list`, {\n signal: AbortSignal.timeout(1000),\n });\n if (!response.ok) throw new Error(`CDP discovery returned HTTP ${response.status}`);\n const targets = (await response.json()) as {\n type: string;\n url: string;\n webSocketDebuggerUrl?: string;\n }[];\n const target = targets.find(\n (candidate) => candidate.type === \"page\" && candidate.url === \"about:blank\",\n );\n if (!target?.webSocketDebuggerUrl)\n throw new Error(\"Owned browser has no about:blank page target\");\n const endpoint = new URL(target.webSocketDebuggerUrl);\n if (\n endpoint.protocol !== \"ws:\" ||\n endpoint.hostname !== \"127.0.0.1\" ||\n Number(endpoint.port) !== port\n )\n throw new Error(\"CDP discovery returned a non-owned endpoint\");\n this.connection = await CdpConnection.connect(endpoint.href);\n return this.connection;\n } catch (error) {\n reason = String(error);\n await delay(50);\n }\n }\n throw new Error(`Browser startup timed out: ${reason}\\n${this.process.logs().slice(-8000)}`);\n }\n\n async stop(): Promise<void> {\n this.connection?.close();\n await this.process?.stop();\n }\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,kBAAkB,UAA2B,KAAkC;CAC7F,MAAM,QACJ,aAAa,UACT,CAAC,cAAc,YAAY,IAC3B;EACE;EACA;EACA;EACA;EACA;EACA;CACF;CACN,MAAM,SAAS,IAAI,QAAQ,GAAE,CAC1B,MAAM,aAAa,UAAU,MAAM,SAAS,CAAC,CAC7C,OAAO,OAAO,CAAC,CACf,SAAS,SAAS,MAAM,KAAK,SAAS,KAAK,MAAM,IAAI,CAAC,CAAC;CAC1D,IAAI,aAAa,UACf,MAAM,KACJ,gEACA,kEACA,KAAK,IAAI,QAAQ,IAAI,6DAA6D,GAClF,KAAK,IAAI,QAAQ,IAAI,+DAA+D,CACtF;CACF,IAAI,aAAa,SACf,KAAK,MAAM,QAAQ;EAAC,IAAI;EAAc,IAAI;EAAsB,IAAI;CAAY,CAAC,CAAC,QAC/E,SAAyB,QAAQ,IAAI,CACxC,GACE,MAAM,KACJ,KAAK,MAAM,UAAU,UAAU,eAAe,YAAY,GAC1D,KAAK,MAAM,aAAa,QAAQ,eAAe,YAAY,CAC7D;CACJ,OAAO;AACT;AAEA,eAAsB,kBACpB,WAAW,QAAQ,IAAI,qBACM;CAC7B,MAAM,aAAa,WACf,CAAC,QAAQ,QAAQ,CAAC,IAClB,kBAAkB,QAAQ,UAAU,QAAQ,GAAG;CACnD,KAAK,MAAM,aAAa,YACtB,IAAI;EACF,MAAM,OAAO,WAAW,QAAQ,aAAa,UAAU,UAAU,OAAO,UAAU,IAAI;EACtF,OAAO;CACT,QAAQ,CAER;CAEF,IAAI,UAAU,MAAM,IAAI,MAAM,uCAAuC,UAAU;AAEjF;AAEA,IAAa,eAAb,MAA0B;CAGxB,YACE,AAAQ,MACR,AAAQ,UACR;EAFQ;EACA;CACP;CAEH,MAAM,MAAM,UAA0B,CAAC,GAAG,QAA8C;EACtF,MAAM,SAAS,MAAM,kBAAkB,QAAQ,MAAM;EACrD,IAAI,CAAC,QACH,MAAM,IAAI,MACR,4GACF;EACF,IACE,QAAQ,MAAM,MACX,QACC,CAAC,IAAI,WAAW,IAAI,KAAK,iDAAiD,KAAK,GAAG,CACtF,GAEA,MAAM,IAAI,MACR,uGACF;EACF,MAAM,UAAU,KAAK,KAAK,MAAM,iBAAiB;EACjD,MAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;EAIxC,KAAK,UAAU,MAAM,aAAa,MAChC,WACA;GACE;GACA;GACA;GACA,mBAAmB;GACnB,GAAI,QAAQ,QAAQ,CAAC;GACrB;EACF,GACA,KAAK,MACL,oBAAoB,KAAK,MAAM,CAAC,CAAC,GACjC,KAAK,MACL,KAAK,SAAS,YACb,UAAU;GACT,KAAK,UAAU;EACjB,CACF;EACA,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,IAAI,SAAS;EACb,OAAO,KAAK,IAAI,IAAI,UAAU;GAC5B,QAAQ,eAAe;GACvB,KAAK,QAAQ,cAAc;GAC3B,IAAI;IACF,MAAM,OAAO,QACV,MAAM,SAAS,KAAK,SAAS,oBAAoB,GAAG,MAAM,EAAC,CAAE,MAAM,IAAI,CAAC,CAAC,EAC5E;IACA,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,QAAQ,KAAK,OAAO,OACjD,MAAM,IAAI,MAAM,sCAAsC;IAExD,MAAM,WAAW,MAAM,MAAM,oBAAoB,KAAK,aAAa,EACjE,QAAQ,YAAY,QAAQ,GAAI,EAClC,CAAC;IACD,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,+BAA+B,SAAS,QAAQ;IAMlF,MAAM,UAAS,MALQ,SAAS,KAAK,EAKf,CAAC,MACpB,cAAc,UAAU,SAAS,UAAU,UAAU,QAAQ,aAChE;IACA,IAAI,CAAC,QAAQ,sBACX,MAAM,IAAI,MAAM,8CAA8C;IAChE,MAAM,WAAW,IAAI,IAAI,OAAO,oBAAoB;IACpD,IACE,SAAS,aAAa,SACtB,SAAS,aAAa,eACtB,OAAO,SAAS,IAAI,MAAM,MAE1B,MAAM,IAAI,MAAM,6CAA6C;IAC/D,KAAK,aAAa,MAAM,cAAc,QAAQ,SAAS,IAAI;IAC3D,OAAO,KAAK;GACd,SAAS,OAAO;IACd,SAAS,OAAO,KAAK;IACrB,MAAMA,WAAM,EAAE;GAChB;EACF;EACA,MAAM,IAAI,MAAM,8BAA8B,OAAO,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC,MAAM,IAAK,GAAG;CAC7F;CAEA,MAAM,OAAsB;EAC1B,KAAK,YAAY,MAAM;EACvB,MAAM,KAAK,SAAS,KAAK;CAC3B;AACF"}
|