diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 02d5e05..fc4b282 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,7 +7,7 @@ on:
branches: [main]
jobs:
- test:
+ test-setup:
runs-on: ${{ matrix.os }}
strategy:
matrix:
@@ -23,7 +23,7 @@ jobs:
windows-11-arm,
]
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
@@ -35,13 +35,13 @@ jobs:
name: artifacts-test-${{ matrix.os }}
path: |
**/recordings/**/*
- test-ignore-tcc-db:
+ test-setup-ignore-tcc-db:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest]
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
@@ -53,22 +53,88 @@ jobs:
name: artifacts-test-ignore-tcc-db-${{ matrix.os }}
path: |
**/recordings/**/*
- test-nvda-install-dir:
+
+ test-install:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [windows-2022, windows-2025, windows-11-arm]
+ os:
+ [
+ macos-14,
+ macos-15,
+ macos-15-intel,
+ macos-26,
+ macos-26-intel,
+ windows-2022,
+ windows-2025,
+ windows-11-arm,
+ ubuntu-latest,
+ ]
steps:
- - uses: actions/checkout@v5
+ - uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- run: yarn install --frozen-lockfile
- - run: yarn ci:nvda-install-dir
- - run: Get-ChildItem $env:USERPROFILE\nvda
- - uses: actions/upload-artifact@v4
- if: always()
- with:
- name: artifacts-test-nvda-install-dir-${{ matrix.os }}
- path: |
- **/recordings/**/*
+ - run: yarn build
+ - shell: bash
+ run: |
+ tmpdir=$(mktemp -d)
+ cd "$tmpdir"
+ npm init -y
+ npm install @guidepup/guidepup
+ npm install -g "${GITHUB_WORKSPACE}"
+ export PATH="$(npm bin -g):$PATH"
+
+ cat > node_modules/@guidepup/guidepup/manifest.json <<'EOF'
+ {
+ "version": 1,
+ "screenReaders": [
+ {
+ "id": "nvda",
+ "name": "NVDA",
+ "platforms": ["win32"],
+ "defaultDownload": true,
+ "assets": [
+ {
+ "version": "0.2.1-2026.1.1",
+ "repository": "guidepup/nvda",
+ "asset": "guidepup-nvda-0.2.1-2026.1.1.zip",
+ "sha256": "b6fdfde86190c7c14132d459f2f88995da6251ded431e23947d676ffb152f963"
+ }
+ ]
+ },
+ {
+ "id": "voiceover",
+ "name": "VoiceOver",
+ "platforms": ["darwin"],
+ "defaultDownload": true,
+ "assets": [
+ {
+ "version": "0.0.1",
+ "platformVersion": "23",
+ "repository": "guidepup/voiceover",
+ "asset": "guidepup-voiceover-preferences-macos-14.dmg",
+ "sha256": "c6595f5ca50440e8553cde278936a46eff58d4c904e19c87e7d0e25950617ee1"
+ },
+ {
+ "version": "0.0.1",
+ "platformVersion": "24",
+ "repository": "guidepup/voiceover",
+ "asset": "guidepup-voiceover-preferences-macos-15.dmg",
+ "sha256": "8bdc3a11c45a19cc876a859bfbd64529469a8b7d4ad41fd7e00a0729ebdbcb25"
+ },
+ {
+ "version": "0.0.1",
+ "platformVersion": "25",
+ "repository": "guidepup/voiceover",
+ "asset": "guidepup-voiceover-preferences-macos-26.dmg",
+ "sha256": "9af2a3af7c9bffae1b2af26f1970548ecd62f33965fd30f4e43f21b9f57ce5ca"
+ }
+ ]
+ }
+ ]
+ }
+ EOF
+
+ guidepup install
diff --git a/README.md b/README.md
index b804805..14ccf37 100644
--- a/README.md
+++ b/README.md
@@ -6,15 +6,15 @@
## [Documentation](https://www.guidepup.dev/docs/guides/automated-environment-setup)
-[](https://apps.apple.com/us/app/macos-sonoma/id6450717509)
-[](https://apps.apple.com/us/app/macos-sequoia/id6596773750)
-[](https://www.apple.com/uk/os/macos/)
+[](https://apps.apple.com/us/app/macos-sonoma/id6450717509)
+[](https://apps.apple.com/us/app/macos-sequoia/id6596773750)
+[](https://www.apple.com/uk/os/macos/)
[](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2022)
[](https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2025)
This package sets up your environment for screen reader automation.
-It enables automation for VoiceOver on MacOS and NVDA on Windows.
+It enables automation for VoiceOver on macOS and NVDA on Windows.
## Getting Started
@@ -28,59 +28,68 @@ And get cracking with your screen reader automation code!
## Usage
-### GitHub Actions
+### Setup
-If you are using GitHub Actions, check out the dedicated [`guidepup/setup-action`](https://github.com/marketplace/actions/guidepup-setup):
+This subcommand allows you to setup an OS environment ready for screen reader automation.
-```yaml
-- name: Setup Environment
- uses: guidepup/setup-action
+```console
+npx @guidepup/setup setup
```
-### MacOS
-
-If you are running this command locally you may need to take some manual steps to complete setup by following the [manual VoiceOver setup documentation](https://www.guidepup.dev/docs/guides/manual-voiceover-setup).
-
#### CI
-If you are running this command in CI/CD, it is recommended to add the `--ci` flag to prevent interactive prompts:
+If you are running this command in CI/CD, it is recommended to add the `--ci` flag to prevent prompts for manual interaction:
```console
-npx @guidepup/setup --ci
+npx @guidepup/setup setup --ci
```
-#### Ignore TCC.db Updates
+#### macOS
-If updating the TCC.db is not possible (due to SIP) or required you can skip the database update step by using the `--ignore-tcc-db` flag:
+If you are running this command locally you may need to take some manual steps to complete setup by following the [manual VoiceOver setup documentation](https://www.guidepup.dev/docs/guides/manual-voiceover-setup).
+
+##### Ignore TCC Database Updates
+
+If updating the `TCC.db` is not possible (due to enabled SIP) or not required for your macOS setup, you can skip the database update step by using the `--macos-ignore-tcc-db` flag:
```console
-npx @guidepup/setup --ignore-tcc-db
+npx @guidepup/setup setup --macos-ignore-tcc-db
```
> [!NOTE]
> If the necessary permissions have not been granted by other means, using this flag may result in your environment not being set up for reliable screen reader automation.
-#### Recording
+##### Recording
-If you are encountering errors in CI for MacOS you can pass a `--record` flag to the command which will output a screen-recording of the setup to a `./recordings/` directory:
+If you are encountering errors in CI for macOS you can pass a `--macos-record` flag to the command which will output a screen-recording of the setup to a `./recordings/` directory:
```console
-npx @guidepup/setup --ci --record
+npx @guidepup/setup --ci --macos-record
```
-### Windows
+### Install
-#### NVDA Installation
+This subcommand allows you to install screen readers and their assets to use in screen reader automation with [Guidepup](https://www.guidepup.dev/).
-When running on windows a portable NVDA instance compatible with Guidepup will be installed to a temporary directory by default. The location of this installation directory is stored in the Windows registry under the key `HKCU\Software\Guidepup\Nvda`.
+```console
+npx @guidepup/setup install
+```
-If you want to specify the directory that NVDA is installed to you can pass a `--nvda-install-dir` flag to the command:
+By default VoiceOver is installed on macOS, and NVDA is installed on Windows.
+
+To install a specific screen reader you can pass it to the command as an argument:
```console
-npx @guidepup/setup --nvda-install-dir
+npx @guidepup/setup install voiceover
+npx @guidepup/setup install nvda
```
-##### Using HTTP / HTTPS Proxy for Installation
+Screen reader options include:
+
+- `voiceover` on macOS (default)
+- `nvda` on Windows (default)
+
+#### Using HTTP / HTTPS Proxy for Installation
If you are using a proxy connection, you must define the proxy URL in an env variable. You can use any of the following variables:
@@ -88,19 +97,23 @@ If you are using a proxy connection, you must define the proxy URL in an env var
- `https_proxy`
- `HTTP_PROXY`
- `http_proxy`
+- `NO_PROXY`
+- `no_proxy`
-#### Foreground Timeout Lock
-
-Modern versions of Windows have a setting which prevents new application instances launching in front of other applications in quick succession, requiring over 3 minutes between activations before it will actually show the window - in the interim it launches the window minimized.
+### Global Install
-Many test automation frameworks will completely close down a browser after a test has finished and then launch a new instance for the next test - on Windows this suffers from the timeout lock on foreground windows. This impacts on screen reader automation which need the window to activate and focus to be able to drive the screen reader on the application.
+You can also install the CLI globally:
-To mitigate this the setup script updates two keys in the Windows registry under `HKCU\Control Panel\Desktop`:
+```console
+npm install -g @guidepup/setup
+```
-- `ForegroundLockTimeout` - Specifies the time in milliseconds, following user input, during which the system will not allow applications to force themselves into the foreground. Defaults to `200000`.
-- `ForegroundFlashCount` - Determines the number of times the taskbar button will flash to notify the user that a background window has been activated by the system. If the time elapsed since the last user input exceeds the value of ForegroundLockTimeout, the window will automatically be brought to the foreground. Defaults to `3`.
+After which you can access the CLI through the `guidepup` command:
-Both of these are set to `0` by the setup script.
+```console
+guidepup setup
+guidepup install
+```
## Powerful Tooling
diff --git a/bin/setup b/bin/guidepup
similarity index 100%
rename from bin/setup
rename to bin/guidepup
diff --git a/package.json b/package.json
index 41fa80f..f893230 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
{
"name": "@guidepup/setup",
- "version": "0.23.0",
- "description": "Setup your environment for screen-reader automation.",
+ "version": "0.24.0",
+ "description": "Configure your environment and manage screen readers for Guidepup.",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"bin": {
- "setup": "bin/setup"
+ "guidepup": "bin/guidepup"
},
"author": "Craig Morten ",
"license": "MIT",
@@ -26,16 +26,15 @@
],
"scripts": {
"build": "yarn clean && yarn compile",
- "ci": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start --ci --record",
- "ci:nvda-install-dir": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start --ci --record --nvda-install-dir %userprofile%\\nvda",
- "ci:ignore-tcc-db": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start --ci --record --ignore-tcc-db",
+ "ci": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start:setup --ci --macos-record",
+ "ci:ignore-tcc-db": "yarn clean && yarn lint && yarn build && yarn resolutionFix && yarn start:setup --ci --macos-record --macos-ignore-tcc-db",
"clean": "rimraf lib",
"compile": "tsc",
"dev": "ts-node ./src/index.ts",
"resolutionFix": "ts-node ./.github/workflows/resolutionFix.ts",
"lint": "eslint . --ext .ts",
"lint:fix": "yarn lint --fix",
- "start": "node ./bin/setup",
+ "start:setup": "node ./bin/guidepup setup",
"prepublish": "yarn build"
},
"devDependencies": {
@@ -53,10 +52,9 @@
},
"dependencies": {
"chalk": "^4.0.0",
- "decompress": "^4.2.1",
- "https-proxy-agent": "^7.0.5",
- "regedit": "5.0.1",
- "semver": "^7.5.4"
- },
- "guidepupNvdaVersion": "0.2.0-2026.1.1"
+ "commander": "^15.0.0",
+ "extract-zip": "^2.0.1",
+ "semver": "^7.5.4",
+ "undici": "^8.7.0"
+ }
}
diff --git a/src/commands/install/delete-asset.ts b/src/commands/install/delete-asset.ts
new file mode 100644
index 0000000..d8eacc4
--- /dev/null
+++ b/src/commands/install/delete-asset.ts
@@ -0,0 +1,13 @@
+import { rm } from "node:fs/promises";
+import { handleInfoWithPath } from "../../logging";
+
+export async function deleteAsset(assetPath: string): Promise {
+ handleInfoWithPath("Removing", assetPath);
+
+ try {
+ await rm(assetPath, { force: true, recursive: true });
+ } catch {
+ // Ignore cleanup failures, preferring to expose the error that triggered
+ // cleanup.
+ }
+}
diff --git a/src/commands/install/download-asset.ts b/src/commands/install/download-asset.ts
new file mode 100644
index 0000000..2c0a331
--- /dev/null
+++ b/src/commands/install/download-asset.ts
@@ -0,0 +1,32 @@
+import { createWriteStream } from "node:fs";
+import { pipeline } from "node:stream/promises";
+import { EnvHttpProxyAgent, fetch } from "undici";
+import type { Asset } from "./types";
+import { ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET } from "../../errors";
+
+const dispatcher = new EnvHttpProxyAgent();
+
+export async function downloadAsset(
+ asset: Asset,
+ source: string,
+ destination: string,
+): Promise {
+ const response = await fetch(source, {
+ dispatcher,
+ });
+
+ if (!response.ok || !response.body) {
+ throw new Error(
+ `${ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET}: ${asset.asset} (${response.status} ${response.statusText})`,
+ );
+ }
+
+ try {
+ await pipeline(response.body, createWriteStream(destination));
+ } catch (cause) {
+ throw new Error(
+ `${ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET}: ${asset.asset} (${response.status} ${response.statusText})`,
+ { cause },
+ );
+ }
+}
diff --git a/src/commands/install/download-assets.ts b/src/commands/install/download-assets.ts
new file mode 100644
index 0000000..a4cc6f1
--- /dev/null
+++ b/src/commands/install/download-assets.ts
@@ -0,0 +1,123 @@
+import { platform, release } from "node:os";
+import { dirname, join } from "node:path";
+import { mkdirSync } from "node:fs";
+import type { Asset, ScreenReader } from "./types";
+import { verifyCachedAssetChecksum } from "./verify-cached-asset-checksum";
+import { downloadAsset } from "./download-asset";
+import { verifyAssetChecksum } from "./verify-asset-checksum";
+import { extractZip } from "./extract-zip";
+import {
+ ERR_INSTALL_SCREEN_READER_ASSET_UNAVAILABLE,
+ ERR_INSTALL_UNABLE_TO_CREATE_GUIDEPUP_CACHE_ASSET_PATH,
+} from "../../errors";
+import { handleInfoWithPath } from "../../logging";
+import { getPlatformVersion } from "./get-platform-version";
+
+function platformMajorVersion(): string {
+ return release().split(".", 1)[0];
+}
+
+function selectAsset(screenReader: ScreenReader): Asset {
+ const currentPlatform = platform();
+ const currentPlatformVersion = platformMajorVersion();
+
+ const asset = screenReader.assets.find(
+ (asset) =>
+ !asset.platformVersion ||
+ asset.platformVersion === currentPlatformVersion,
+ );
+
+ if (!asset) {
+ throw new Error(
+ `${ERR_INSTALL_SCREEN_READER_ASSET_UNAVAILABLE}: ${screenReader.name} on ${currentPlatform} ${currentPlatformVersion}`,
+ );
+ }
+
+ return asset;
+}
+
+async function downloadScreenReader(
+ cachePath: string,
+ screenReader: ScreenReader,
+): Promise {
+ const asset = selectAsset(screenReader);
+
+ const downloadDestination = join(
+ cachePath,
+ screenReader.id,
+ getPlatformVersion(asset),
+ asset.version,
+ asset.asset,
+ );
+
+ const currentPlatform = platform();
+ const versionMessage = `${asset.version}${asset.platformVersion ? ` - ${currentPlatform} ${asset.platformVersion}` : ""}`;
+
+ if (await verifyCachedAssetChecksum(downloadDestination, asset.sha256)) {
+ handleInfoWithPath(
+ `${screenReader.name} (${versionMessage}) already in cache at`,
+ downloadDestination,
+ );
+
+ return;
+ }
+
+ const downloadDestinationDirectory = dirname(downloadDestination);
+
+ try {
+ mkdirSync(downloadDestinationDirectory, { recursive: true });
+ } catch (cause) {
+ throw new Error(ERR_INSTALL_UNABLE_TO_CREATE_GUIDEPUP_CACHE_ASSET_PATH, {
+ cause,
+ });
+ }
+
+ const source = `https://github.com/${asset.repository}/releases/download/${asset.version}/${asset.asset}`;
+
+ handleInfoWithPath(
+ `Downloading ${screenReader.name} (${versionMessage}) from`,
+ source,
+ );
+
+ await downloadAsset(asset, source, downloadDestination);
+
+ handleInfoWithPath(
+ `${screenReader.name} (${versionMessage}) successfully downloaded to`,
+ downloadDestination,
+ );
+
+ await verifyAssetChecksum(downloadDestination, asset.sha256);
+
+ if (asset.asset.endsWith(".zip")) {
+ const unzipDestination = join(
+ cachePath,
+ screenReader.id,
+ getPlatformVersion(asset),
+ asset.version,
+ "extracted",
+ );
+
+ handleInfoWithPath(
+ `Extracting ${screenReader.name} (${versionMessage}) from`,
+ downloadDestination,
+ );
+
+ await extractZip(asset, downloadDestination, unzipDestination);
+
+ handleInfoWithPath(
+ `${screenReader.name} (${versionMessage}) successfully extracted to`,
+ unzipDestination,
+ );
+ }
+}
+
+export async function downloadAssets(
+ cachePath: string,
+ screenReaders: ScreenReader[],
+): Promise {
+ await Promise.all(
+ screenReaders.map((screenReader) =>
+ downloadScreenReader(cachePath, screenReader),
+ ),
+ );
+}
diff --git a/src/commands/install/extract-zip.ts b/src/commands/install/extract-zip.ts
new file mode 100644
index 0000000..aa2b212
--- /dev/null
+++ b/src/commands/install/extract-zip.ts
@@ -0,0 +1,45 @@
+import { readdir } from "node:fs/promises";
+import extract from "extract-zip";
+import {
+ ERR_INSTALL_EMPTY_EXTRACTED_ASSET,
+ ERR_INSTALL_FAILED_TO_EXTRACT_ASSET,
+} from "../../errors";
+import { handleInfoWithPath } from "../../logging";
+import type { Asset } from "./types";
+import { deleteAsset } from "./delete-asset";
+
+async function deleteAssets(...assets) {
+ for (const asset of assets) {
+ await deleteAsset(asset);
+ }
+}
+
+export async function extractZip(
+ asset: Asset,
+ source: string,
+ destination: string,
+): Promise {
+ let files: string[];
+
+ try {
+ await extract(source, { dir: destination });
+
+ files = await readdir(destination);
+ } catch (cause) {
+ handleInfoWithPath("Unable to extract from", source);
+
+ await deleteAssets(source, destination);
+
+ throw new Error(`${ERR_INSTALL_FAILED_TO_EXTRACT_ASSET}: ${asset.asset}`, {
+ cause,
+ });
+ }
+
+ if (!files.length) {
+ handleInfoWithPath("Extracted asset is empty at", destination);
+
+ await deleteAssets(source, destination);
+
+ throw new Error(`${ERR_INSTALL_EMPTY_EXTRACTED_ASSET}: ${asset.asset}`);
+ }
+}
diff --git a/src/commands/install/get-platform-version.ts b/src/commands/install/get-platform-version.ts
new file mode 100644
index 0000000..7f360cf
--- /dev/null
+++ b/src/commands/install/get-platform-version.ts
@@ -0,0 +1,5 @@
+import { Asset } from "./types";
+
+export function getPlatformVersion(asset: Asset) {
+ return asset.platformVersion ?? "all";
+}
diff --git a/src/commands/install/index.ts b/src/commands/install/index.ts
new file mode 100644
index 0000000..a4d2f08
--- /dev/null
+++ b/src/commands/install/index.ts
@@ -0,0 +1,45 @@
+import { Command } from "commander";
+import {
+ handleInstallComplete,
+ handleInstallError,
+ handleNoInstallation,
+} from "../../logging";
+import { selectTargets } from "./select-targets";
+import { resolveManifest } from "./resolve-manifest";
+import type { ScreenReader } from "./types";
+import { resolveCachePath } from "./resolve-cache-path";
+import { downloadAssets } from "./download-assets";
+import { registerInstallation } from "./register-installation";
+import { pruneCache } from "./prune-cache";
+
+async function install(requestedScreenReaderIds?: string[]): Promise {
+ let targets: ScreenReader[];
+
+ try {
+ const { manifestPath, manifest } = resolveManifest();
+
+ const cachePath = resolveCachePath();
+ await registerInstallation(cachePath, manifestPath);
+
+ targets = selectTargets(manifest, requestedScreenReaderIds);
+
+ if (targets.length === 0) {
+ handleNoInstallation();
+ }
+
+ await downloadAssets(cachePath, targets);
+
+ await pruneCache(cachePath);
+ } catch (error) {
+ handleInstallError(error);
+ }
+
+ handleInstallComplete();
+}
+
+export function installCommand() {
+ return new Command("install")
+ .description("Install screen readers.")
+ .argument("[screenReaders...]", "Screen readers to install")
+ .action(install);
+}
diff --git a/src/commands/install/prune-cache.ts b/src/commands/install/prune-cache.ts
new file mode 100644
index 0000000..3a50e67
--- /dev/null
+++ b/src/commands/install/prune-cache.ts
@@ -0,0 +1,90 @@
+import { readdir, readFile } from "node:fs/promises";
+import { join } from "node:path";
+import { deleteAsset } from "./delete-asset";
+import { resolveManifest } from "./resolve-manifest";
+import { getPlatformVersion } from "./get-platform-version";
+
+export async function pruneCache(cachePath: string): Promise {
+ const required = new Set();
+ const linksPath = join(cachePath, ".links");
+
+ let links: string[];
+
+ try {
+ links = await readdir(linksPath);
+ } catch {
+ return;
+ }
+
+ for (const link of links) {
+ const linkPath = join(linksPath, link);
+
+ let manifestPath: string;
+
+ try {
+ manifestPath = await readFile(linkPath, "utf8");
+ } catch {
+ await deleteAsset(linkPath);
+
+ continue;
+ }
+
+ try {
+ const { manifest } = resolveManifest(manifestPath);
+
+ for (const screenReader of manifest.screenReaders) {
+ for (const asset of screenReader.assets) {
+ required.add(
+ join(screenReader.id, getPlatformVersion(asset), asset.version),
+ );
+ }
+ }
+ } catch {
+ await deleteAsset(linkPath);
+ }
+ }
+
+ let screenReaders: string[];
+
+ try {
+ screenReaders = await readdir(cachePath);
+ } catch {
+ return;
+ }
+
+ for (const screenReaderId of screenReaders) {
+ if (screenReaderId === ".links") {
+ continue;
+ }
+
+ const screenReaderPath = join(cachePath, screenReaderId);
+
+ let platformVersions: string[];
+
+ try {
+ platformVersions = await readdir(screenReaderPath);
+ } catch {
+ continue;
+ }
+
+ for (const platformVersion of platformVersions) {
+ const platformPath = join(screenReaderPath, platformVersion);
+
+ let versions: string[];
+
+ try {
+ versions = await readdir(platformPath);
+ } catch {
+ continue;
+ }
+
+ for (const version of versions) {
+ const relativePath = join(screenReaderId, platformVersion, version);
+
+ if (!required.has(relativePath)) {
+ await deleteAsset(join(cachePath, relativePath));
+ }
+ }
+ }
+ }
+}
diff --git a/src/commands/install/register-installation.ts b/src/commands/install/register-installation.ts
new file mode 100644
index 0000000..197936c
--- /dev/null
+++ b/src/commands/install/register-installation.ts
@@ -0,0 +1,24 @@
+import { createHash } from "node:crypto";
+import { mkdir, writeFile } from "node:fs/promises";
+import { join } from "node:path";
+import { ERR_INSTALL_FAILED_TO_REGISTER_INSTALLATION } from "../../errors";
+
+export async function registerInstallation(
+ cachePath: string,
+ manifestPath: string,
+): Promise {
+ const linksPath = join(cachePath, ".links");
+
+ try {
+ await mkdir(linksPath, { recursive: true });
+
+ const id = createHash("sha256").update(manifestPath).digest("hex");
+ const linkPath = join(linksPath, id);
+
+ await writeFile(linkPath, manifestPath, "utf8");
+ } catch (cause) {
+ throw new Error(ERR_INSTALL_FAILED_TO_REGISTER_INSTALLATION, {
+ cause,
+ });
+ }
+}
diff --git a/src/commands/install/resolve-cache-path.ts b/src/commands/install/resolve-cache-path.ts
new file mode 100644
index 0000000..81048e0
--- /dev/null
+++ b/src/commands/install/resolve-cache-path.ts
@@ -0,0 +1,35 @@
+import { mkdirSync } from "node:fs";
+import { homedir, platform } from "node:os";
+import { join, resolve } from "node:path";
+import { ERR_INSTALL_UNABLE_TO_RESOLVE_OR_CREATE_GUIDEPUP_CACHE_PATH } from "../../errors";
+
+const defaultCacheLocationMap = {
+ darwin: resolve(homedir(), "Library", "Caches", "guidepup"),
+ win32: resolve(
+ process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"),
+ "guidepup",
+ ),
+};
+
+const defaultCacheLocation = resolve(homedir(), ".cache", "guidepup");
+
+export function resolveCachePath() {
+ const currentPlatform = platform();
+
+ const cacheLocationOverride = process.env.GUIDEPUP_SCREEN_READERS_PATH;
+
+ const resolvedCacheLocation = cacheLocationOverride
+ ? resolve(cacheLocationOverride)
+ : (defaultCacheLocationMap[currentPlatform] ?? defaultCacheLocation);
+
+ try {
+ mkdirSync(resolvedCacheLocation, { recursive: true });
+ } catch (cause) {
+ throw new Error(
+ ERR_INSTALL_UNABLE_TO_RESOLVE_OR_CREATE_GUIDEPUP_CACHE_PATH,
+ { cause },
+ );
+ }
+
+ return resolvedCacheLocation;
+}
diff --git a/src/commands/install/resolve-manifest.ts b/src/commands/install/resolve-manifest.ts
new file mode 100644
index 0000000..2a999d6
--- /dev/null
+++ b/src/commands/install/resolve-manifest.ts
@@ -0,0 +1,57 @@
+import { existsSync, readFileSync } from "node:fs";
+import { dirname, join } from "node:path";
+import {
+ ERR_INSTALL_GUIDEPUP_MANIFEST_INVALID,
+ ERR_INSTALL_GUIDEPUP_MANIFEST_UNSUPPORTED_SCHEMA,
+ ERR_INSTALL_GUIDEPUP_PACKAGE_NOT_FOUND,
+} from "../../errors";
+import { Manifest } from "./types";
+import { validateManifest } from "./validate-manifest";
+
+const SUPPORTED_SCHEMA_VERSIONS = [1];
+
+function getLocalManifestPath() {
+ let packageJsonPath: string;
+
+ try {
+ packageJsonPath = require.resolve("@guidepup/guidepup/package.json", {
+ paths: [process.cwd()],
+ });
+ } catch (cause) {
+ throw new Error(ERR_INSTALL_GUIDEPUP_PACKAGE_NOT_FOUND, { cause });
+ }
+
+ return join(dirname(packageJsonPath), "manifest.json");
+}
+
+export function resolveManifest(targetManifestPath?: string): {
+ manifestPath: string;
+ manifest: Manifest;
+} {
+ const manifestPath = targetManifestPath ?? getLocalManifestPath();
+
+ if (!existsSync(manifestPath)) {
+ throw new Error(ERR_INSTALL_GUIDEPUP_MANIFEST_INVALID);
+ }
+
+ let manifest: unknown;
+
+ try {
+ const rawManifest = readFileSync(manifestPath, "utf8");
+
+ manifest = JSON.parse(rawManifest);
+ } catch (cause) {
+ throw new Error(ERR_INSTALL_GUIDEPUP_MANIFEST_INVALID, { cause });
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ if (!SUPPORTED_SCHEMA_VERSIONS.includes((manifest as any)?.version)) {
+ throw new Error(ERR_INSTALL_GUIDEPUP_MANIFEST_UNSUPPORTED_SCHEMA);
+ }
+
+ if (!validateManifest(manifest)) {
+ throw new Error(ERR_INSTALL_GUIDEPUP_MANIFEST_INVALID);
+ }
+
+ return { manifestPath, manifest };
+}
diff --git a/src/commands/install/select-targets.ts b/src/commands/install/select-targets.ts
new file mode 100644
index 0000000..c209438
--- /dev/null
+++ b/src/commands/install/select-targets.ts
@@ -0,0 +1,54 @@
+import { platform } from "node:os";
+import { ERR_INSTALL_SCREEN_READER_UNAVAILABLE } from "../../errors";
+import type { ScreenReader, Manifest } from "./types";
+
+function isScreenReaderCompatibleWithCurrentPlatform(
+ screenReader: ScreenReader,
+): boolean {
+ const currentPlatform = platform();
+
+ return screenReader.platforms.includes(currentPlatform);
+}
+
+function getRequestedTargets(
+ manifest: Manifest,
+ requestedScreenReadersIds: string[],
+): ScreenReader[] {
+ const targets = [];
+
+ for (const requestedScreenReaderId of requestedScreenReadersIds) {
+ const targetScreenReader = manifest.screenReaders.find(
+ (screenReader) =>
+ (screenReader.id === requestedScreenReaderId ||
+ screenReader.name === requestedScreenReaderId) &&
+ isScreenReaderCompatibleWithCurrentPlatform(screenReader),
+ );
+
+ if (!targetScreenReader) {
+ throw new Error(
+ `${ERR_INSTALL_SCREEN_READER_UNAVAILABLE}: ${requestedScreenReaderId}`,
+ );
+ }
+
+ targets.push(targetScreenReader);
+ }
+
+ return targets;
+}
+
+function getDefaultTargets(manifest: Manifest): ScreenReader[] {
+ return manifest.screenReaders.filter(
+ (screenReader) =>
+ screenReader.defaultDownload &&
+ isScreenReaderCompatibleWithCurrentPlatform(screenReader),
+ );
+}
+
+export function selectTargets(
+ manifest: Manifest,
+ requestedScreenReadersIds?: string[],
+): ScreenReader[] {
+ return requestedScreenReadersIds.length
+ ? getRequestedTargets(manifest, requestedScreenReadersIds)
+ : getDefaultTargets(manifest);
+}
diff --git a/src/commands/install/sha256.ts b/src/commands/install/sha256.ts
new file mode 100644
index 0000000..3f5b6b6
--- /dev/null
+++ b/src/commands/install/sha256.ts
@@ -0,0 +1,13 @@
+import { createHash } from "node:crypto";
+import { createReadStream } from "node:fs";
+
+export async function sha256(assetPath: string): Promise {
+ return new Promise((resolve, reject) => {
+ const hash = createHash("sha256");
+ const stream = createReadStream(assetPath);
+
+ stream.on("error", reject);
+ stream.on("data", (chunk) => hash.update(chunk));
+ stream.on("end", () => resolve(hash.digest("hex")));
+ });
+}
diff --git a/src/commands/install/types.ts b/src/commands/install/types.ts
new file mode 100644
index 0000000..bf7d04e
--- /dev/null
+++ b/src/commands/install/types.ts
@@ -0,0 +1,20 @@
+export interface Asset {
+ asset: string;
+ platformVersion?: string;
+ repository: string;
+ sha256: string;
+ version: string;
+}
+
+export interface ScreenReader {
+ assets: [Asset, ...Asset[]];
+ defaultDownload: boolean;
+ id: string;
+ name: string;
+ platforms: [NodeJS.Platform, ...NodeJS.Platform[]];
+}
+
+export interface Manifest {
+ screenReaders: [ScreenReader, ...ScreenReader[]];
+ version: number;
+}
diff --git a/src/commands/install/validate-manifest.ts b/src/commands/install/validate-manifest.ts
new file mode 100644
index 0000000..2c9aa81
--- /dev/null
+++ b/src/commands/install/validate-manifest.ts
@@ -0,0 +1,171 @@
+import type { Asset, Manifest, ScreenReader } from "./types";
+
+function validateAsset(asset: unknown): asset is Asset {
+ if (!asset) {
+ return false;
+ }
+
+ if (typeof asset !== "object") {
+ return false;
+ }
+
+ if (Array.isArray(asset)) {
+ return false;
+ }
+
+ if (
+ !("asset" in asset) ||
+ typeof asset.asset !== "string" ||
+ !asset.asset.length
+ ) {
+ return false;
+ }
+
+ if (
+ "platformVersion" in asset &&
+ (typeof asset.platformVersion !== "string" || !asset.platformVersion.length)
+ ) {
+ return false;
+ }
+
+ if (
+ !("repository" in asset) ||
+ typeof asset.repository !== "string" ||
+ !asset.repository.length
+ ) {
+ return false;
+ }
+
+ if (
+ !("sha256" in asset) ||
+ typeof asset.sha256 !== "string" ||
+ !asset.sha256.length
+ ) {
+ return false;
+ }
+
+ if (
+ !("version" in asset) ||
+ typeof asset.version !== "string" ||
+ !asset.version.length
+ ) {
+ return false;
+ }
+
+ return true;
+}
+
+function validateAssets(assets: unknown): assets is Asset[] {
+ if (!Array.isArray(assets)) {
+ return false;
+ }
+
+ return assets.every((asset) => validateAsset(asset));
+}
+
+function validatePlatform(platform: unknown): platform is NodeJS.Platform {
+ return typeof platform === "string" && !!platform.length;
+}
+
+function validatePlatforms(platforms: unknown): platforms is NodeJS.Platform[] {
+ if (!Array.isArray(platforms)) {
+ return false;
+ }
+
+ return platforms.every((platform) => validatePlatform(platform));
+}
+
+function validateScreenReader(
+ screenReader: unknown,
+): screenReader is ScreenReader {
+ if (!screenReader) {
+ return false;
+ }
+
+ if (typeof screenReader !== "object") {
+ return false;
+ }
+
+ if (Array.isArray(screenReader)) {
+ return false;
+ }
+
+ if (!("assets" in screenReader)) {
+ return false;
+ }
+
+ if (
+ !("defaultDownload" in screenReader) ||
+ typeof screenReader.defaultDownload !== "boolean"
+ ) {
+ return false;
+ }
+
+ if (
+ !("id" in screenReader) ||
+ typeof screenReader.id !== "string" ||
+ !screenReader.id.length
+ ) {
+ return false;
+ }
+
+ if (
+ !("name" in screenReader) ||
+ typeof screenReader.name !== "string" ||
+ !screenReader.name.length
+ ) {
+ return false;
+ }
+
+ if (!("platforms" in screenReader)) {
+ return false;
+ }
+
+ return (
+ validateAssets(screenReader.assets) &&
+ validatePlatforms(screenReader.platforms)
+ );
+}
+
+function validateScreenReaders(
+ screenReaders: unknown,
+): screenReaders is ScreenReader[] {
+ if (!Array.isArray(screenReaders)) {
+ return false;
+ }
+
+ return screenReaders.every((screenReader) =>
+ validateScreenReader(screenReader),
+ );
+}
+
+function validateVersion(version: unknown): version is number {
+ return typeof version === "number";
+}
+
+export function validateManifest(manifest: unknown): manifest is Manifest {
+ if (!manifest) {
+ return false;
+ }
+
+ if (typeof manifest !== "object") {
+ return false;
+ }
+
+ if (Array.isArray(manifest)) {
+ return false;
+ }
+
+ if (!("screenReaders" in manifest)) {
+ return false;
+ }
+
+ if (!("version" in manifest)) {
+ return false;
+ }
+
+ return (
+ validateScreenReaders(manifest.screenReaders) &&
+ validateVersion(manifest.version)
+ );
+}
diff --git a/src/commands/install/verify-asset-checksum.ts b/src/commands/install/verify-asset-checksum.ts
new file mode 100644
index 0000000..627ac3a
--- /dev/null
+++ b/src/commands/install/verify-asset-checksum.ts
@@ -0,0 +1,51 @@
+import type { Stats } from "node:fs";
+import { stat } from "node:fs/promises";
+import { sha256 } from "./sha256";
+import { deleteAsset } from "./delete-asset";
+import {
+ ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET,
+ ERR_INSTALL_FAILED_TO_VERIFY_CHECKSUM,
+ ERR_INSTALL_INVALID_CHECKSUM,
+} from "../../errors";
+import { handleInfoWithPath } from "../../logging";
+
+export async function verifyAssetChecksum(
+ assetPath: string,
+ expectedSha256: string,
+): Promise {
+ let file: Stats;
+
+ try {
+ file = await stat(assetPath);
+ } catch (cause) {
+ throw new Error(ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET, { cause });
+ }
+
+ if (!file.isFile()) {
+ throw new Error(ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET);
+ }
+
+ let actualSha256: string;
+
+ try {
+ actualSha256 = await sha256(assetPath);
+ } catch (cause) {
+ handleInfoWithPath("Unable to verify checksum for", assetPath);
+
+ await deleteAsset(assetPath);
+
+ throw new Error(ERR_INSTALL_FAILED_TO_VERIFY_CHECKSUM, { cause });
+ }
+
+ if (actualSha256 === expectedSha256) {
+ return;
+ }
+
+ handleInfoWithPath("Checksum verification failed for", assetPath);
+
+ await deleteAsset(assetPath);
+
+ throw new Error(
+ `${ERR_INSTALL_INVALID_CHECKSUM}: "${assetPath}"\n\n\t- Expected ${expectedSha256}\n\t- Received ${actualSha256}`,
+ );
+}
diff --git a/src/commands/install/verify-cached-asset-checksum.ts b/src/commands/install/verify-cached-asset-checksum.ts
new file mode 100644
index 0000000..8030cee
--- /dev/null
+++ b/src/commands/install/verify-cached-asset-checksum.ts
@@ -0,0 +1,34 @@
+import type { Stats } from "node:fs";
+import { rm, stat } from "node:fs/promises";
+import { sha256 } from "./sha256";
+
+export async function verifyCachedAssetChecksum(
+ assetPath: string,
+ expectedSha256: string,
+): Promise {
+ let file: Stats;
+
+ try {
+ file = await stat(assetPath);
+ } catch {
+ return false;
+ }
+
+ if (!file.isFile()) {
+ return false;
+ }
+
+ const actualSha256 = await sha256(assetPath);
+
+ if (actualSha256 === expectedSha256) {
+ return true;
+ }
+
+ try {
+ await rm(assetPath, { force: true });
+ } catch {
+ // Swallow
+ }
+
+ return false;
+}
diff --git a/src/commands/setup/index.ts b/src/commands/setup/index.ts
new file mode 100644
index 0000000..236b150
--- /dev/null
+++ b/src/commands/setup/index.ts
@@ -0,0 +1,46 @@
+import { platform } from "node:os";
+import { Command } from "commander";
+import { setup as setupMacOS } from "./macOS/setup";
+import { ERR_SETUP_UNSUPPORTED_OS } from "../../errors";
+import { handleSetupError, handleSetupComplete } from "../../logging";
+
+interface SetupCommandOptions {
+ ci: boolean;
+ macosIgnoreTccDb: boolean;
+ macosRecord: boolean;
+}
+
+async function setup(options: SetupCommandOptions): Promise {
+ const currentPlatform = platform();
+
+ try {
+ switch (currentPlatform) {
+ case "win32": {
+ break;
+ }
+ case "darwin": {
+ await setupMacOS(options);
+
+ break;
+ }
+ default: {
+ throw new Error(ERR_SETUP_UNSUPPORTED_OS);
+ }
+ }
+ } catch (error) {
+ handleSetupError(error);
+ }
+
+ handleSetupComplete();
+}
+
+export function setupCommand() {
+ return new Command("setup")
+ .description(
+ "Configure the local environment for screen reader automation.",
+ )
+ .option("--ci", "Enable CI-specific behavior during setup.")
+ .option("--macos-ignore-tcc-db", "Skip the TCC database updates on macOS.")
+ .option("--macos-record", "Screen record the setup on macOS.")
+ .action(setup);
+}
diff --git a/src/macOS/checkVersion.ts b/src/commands/setup/macOS/checkVersion.ts
similarity index 71%
rename from src/macOS/checkVersion.ts
rename to src/commands/setup/macOS/checkVersion.ts
index 0e071c3..75e730b 100644
--- a/src/macOS/checkVersion.ts
+++ b/src/commands/setup/macOS/checkVersion.ts
@@ -1,12 +1,12 @@
// Derived from https://github.com/sindresorhus/macos-version
// MIT License Copyright (c) Sindre Sorhus
-import { readFileSync } from "fs";
+import { readFileSync } from "node:fs";
import { satisfies } from "semver";
import {
- ERR_MACOS_UNABLE_TO_VERIFY_VERSION,
- ERR_MACOS_UNSUPPORTED_VERSION,
-} from "../errors";
+ ERR_SETUP_MACOS_UNABLE_TO_VERIFY_VERSION,
+ ERR_SETUP_MACOS_UNSUPPORTED_VERSION,
+} from "../../../errors";
function clean(version: string): string {
const { length } = version.split(".");
@@ -23,18 +23,18 @@ function clean(version: string): string {
export function checkVersion(): void {
const plist = readFileSync(
"/System/Library/CoreServices/SystemVersion.plist",
- "utf8"
+ "utf8",
);
const matches =
/ProductVersion<\/key>\s*([\d.]+)<\/string>/.exec(plist);
if (!matches) {
- throw new Error(ERR_MACOS_UNABLE_TO_VERIFY_VERSION);
+ throw new Error(ERR_SETUP_MACOS_UNABLE_TO_VERIFY_VERSION);
}
const version = clean(matches[1].replace("10.16", "11"));
if (!satisfies(version, ">=11.0.0")) {
- throw new Error(ERR_MACOS_UNSUPPORTED_VERSION);
+ throw new Error(ERR_SETUP_MACOS_UNSUPPORTED_VERSION);
}
}
diff --git a/src/macOS/disableDictationInputAutoEnable.ts b/src/commands/setup/macOS/disableDictationInputAutoEnable.ts
similarity index 51%
rename from src/macOS/disableDictationInputAutoEnable.ts
rename to src/commands/setup/macOS/disableDictationInputAutoEnable.ts
index e584799..8558788 100644
--- a/src/macOS/disableDictationInputAutoEnable.ts
+++ b/src/commands/setup/macOS/disableDictationInputAutoEnable.ts
@@ -1,15 +1,13 @@
import { execSync } from "child_process";
-import { ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS } from "../errors";
+import { ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS } from "../../../errors";
export function disableDictationInputAutoEnable(): void {
try {
execSync(
"defaults write com.apple.HIToolbox AppleDictationAutoEnable -bool false",
- { encoding: "utf8" }
- );
- } catch (e) {
- throw new Error(
- `${ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS}\n\n${e.message}`
+ { encoding: "utf8" },
);
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS, { cause });
}
}
diff --git a/src/macOS/disableSplashScreenSystemDefaults.ts b/src/commands/setup/macOS/disableSplashScreenSystemDefaults.ts
similarity index 51%
rename from src/macOS/disableSplashScreenSystemDefaults.ts
rename to src/commands/setup/macOS/disableSplashScreenSystemDefaults.ts
index 80ee8ff..f94edd5 100644
--- a/src/macOS/disableSplashScreenSystemDefaults.ts
+++ b/src/commands/setup/macOS/disableSplashScreenSystemDefaults.ts
@@ -1,15 +1,13 @@
import { execSync } from "child_process";
-import { ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS } from "../errors";
+import { ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS } from "../../../errors";
export function disableSplashScreenSystemDefaults(): void {
try {
execSync(
"defaults write com.apple.VoiceOverTraining doNotShowSplashScreen -bool true",
- { encoding: "utf8" }
- );
- } catch (e) {
- throw new Error(
- `${ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS}\n\n${e.message}`
+ { encoding: "utf8" },
);
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS, { cause });
}
}
diff --git a/src/macOS/enableAppleScriptControlSystemDefaults.ts b/src/commands/setup/macOS/enableAppleScriptControlSystemDefaults.ts
similarity index 59%
rename from src/macOS/enableAppleScriptControlSystemDefaults.ts
rename to src/commands/setup/macOS/enableAppleScriptControlSystemDefaults.ts
index 3b16cb3..9468d0f 100644
--- a/src/macOS/enableAppleScriptControlSystemDefaults.ts
+++ b/src/commands/setup/macOS/enableAppleScriptControlSystemDefaults.ts
@@ -1,5 +1,5 @@
import { execSync } from "child_process";
-import { ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS } from "../errors";
+import { ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS } from "../../../errors";
export function enableAppleScriptControlSystemDefaults(): void {
try {
@@ -9,9 +9,7 @@ export function enableAppleScriptControlSystemDefaults(): void {
);
return;
- } catch (e) {
- throw new Error(
- `${ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS}\n\n${e.message}`,
- );
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS, { cause });
}
}
diff --git a/src/macOS/enableDoNotDisturb.ts b/src/commands/setup/macOS/enableDoNotDisturb.ts
similarity index 92%
rename from src/macOS/enableDoNotDisturb.ts
rename to src/commands/setup/macOS/enableDoNotDisturb.ts
index b9e791f..4f059d7 100644
--- a/src/macOS/enableDoNotDisturb.ts
+++ b/src/commands/setup/macOS/enableDoNotDisturb.ts
@@ -1,6 +1,6 @@
import { exec } from "child_process";
import { promisify } from "util";
-import { ERR_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB } from "../errors";
+import { ERR_SETUP_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB } from "../../../errors";
import { runAppleScript } from "./runAppleScript";
import { retryOnError } from "./retryOnError";
import { getPlatformVersionMajor } from "./getPlatformVersionMajor";
@@ -103,21 +103,19 @@ export async function enableDoNotDisturb() {
if (platformMajor <= 20) {
await promisify(exec)(enableFocusModeShellscript);
} else if (platformMajor === 21) {
- // From MacOS 12 Monterey (Darwin 21) there is no known way to enable DND via system defaults
+ // From macOS 12 Monterey (Darwin 21) there is no known way to enable DND via system defaults
await retryOnError(() => runAppleScript(enableFocusModeAppleScript));
} else {
const { stdout: locale } = await promisify(exec)(getLocale);
- // From MacOS 13 Ventura (Darwin 22) there is no known way to enable DND via system settings
+ // From macOS 13 Ventura (Darwin 22) there is no known way to enable DND via system settings
await retryOnError(() =>
runAppleScript(
enableFocusModeVenturaAppleScript(locale, platformMajor),
),
);
}
- } catch (e) {
- throw new Error(
- `${ERR_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB}\n\n${e.message}`,
- );
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB, { cause });
}
}
diff --git a/src/macOS/getPlatformVersionMajor.ts b/src/commands/setup/macOS/getPlatformVersionMajor.ts
similarity index 100%
rename from src/macOS/getPlatformVersionMajor.ts
rename to src/commands/setup/macOS/getPlatformVersionMajor.ts
diff --git a/src/macOS/isAppleScriptControlEnabled/enabledDbFile.ts b/src/commands/setup/macOS/isAppleScriptControlEnabled/enabledDbFile.ts
similarity index 100%
rename from src/macOS/isAppleScriptControlEnabled/enabledDbFile.ts
rename to src/commands/setup/macOS/isAppleScriptControlEnabled/enabledDbFile.ts
diff --git a/src/macOS/isAppleScriptControlEnabled/enabledDefaults.ts b/src/commands/setup/macOS/isAppleScriptControlEnabled/enabledDefaults.ts
similarity index 100%
rename from src/macOS/isAppleScriptControlEnabled/enabledDefaults.ts
rename to src/commands/setup/macOS/isAppleScriptControlEnabled/enabledDefaults.ts
diff --git a/src/macOS/isAppleScriptControlEnabled/index.ts b/src/commands/setup/macOS/isAppleScriptControlEnabled/index.ts
similarity index 100%
rename from src/macOS/isAppleScriptControlEnabled/index.ts
rename to src/commands/setup/macOS/isAppleScriptControlEnabled/index.ts
diff --git a/src/macOS/isSipEnabled.ts b/src/commands/setup/macOS/isSipEnabled.ts
similarity index 60%
rename from src/macOS/isSipEnabled.ts
rename to src/commands/setup/macOS/isSipEnabled.ts
index 469fbc6..885509f 100644
--- a/src/macOS/isSipEnabled.ts
+++ b/src/commands/setup/macOS/isSipEnabled.ts
@@ -1,13 +1,13 @@
import { execSync } from "child_process";
-import { ERR_MACOS_UNABLE_TO_VERIFY_SIP } from "../errors";
+import { ERR_SETUP_MACOS_UNABLE_TO_VERIFY_SIP } from "../../../errors";
export function isSipEnabled(): boolean {
let commandResult: string;
try {
commandResult = execSync("csrutil status", { encoding: "utf8" });
- } catch (e) {
- throw new Error(`${ERR_MACOS_UNABLE_TO_VERIFY_SIP}\n\n${e.message}`);
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_UNABLE_TO_VERIFY_SIP, { cause });
}
return commandResult.includes("enabled");
diff --git a/src/macOS/retryOnError.ts b/src/commands/setup/macOS/retryOnError.ts
similarity index 100%
rename from src/macOS/retryOnError.ts
rename to src/commands/setup/macOS/retryOnError.ts
diff --git a/src/macOS/runAppleScript.ts b/src/commands/setup/macOS/runAppleScript.ts
similarity index 100%
rename from src/macOS/runAppleScript.ts
rename to src/commands/setup/macOS/runAppleScript.ts
diff --git a/src/macOS/setup.ts b/src/commands/setup/macOS/setup.ts
similarity index 69%
rename from src/macOS/setup.ts
rename to src/commands/setup/macOS/setup.ts
index 3ec9d3f..9880503 100644
--- a/src/macOS/setup.ts
+++ b/src/commands/setup/macOS/setup.ts
@@ -1,5 +1,4 @@
-import { platform, release } from "os";
-import chalk from "chalk";
+import { platform, release } from "node:os";
import { checkVersion } from "./checkVersion";
import { enableAppleScriptControlSystemDefaults } from "./enableAppleScriptControlSystemDefaults";
import { disableSplashScreenSystemDefaults } from "./disableSplashScreenSystemDefaults";
@@ -8,21 +7,27 @@ import { isSipEnabled } from "./isSipEnabled";
import { writeDatabaseFile } from "./writeDatabaseFile";
import { SYSTEM_PATH, USER_PATH, updateTccDb } from "./updateTccDb";
import { isAppleScriptControlEnabled } from "./isAppleScriptControlEnabled";
-import { handleWarning, logInfo } from "../logging";
-import { ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION } from "../errors";
+import { handleSetupManualRequired, handleWarning } from "../../../logging";
+import { ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION } from "../../../errors";
import { enableDoNotDisturb } from "./enableDoNotDisturb";
import { enabledDbFile } from "./isAppleScriptControlEnabled/enabledDbFile";
-const isCi = process.argv.includes("--ci");
-const ignoreTccDb = process.argv.includes("--ignore-tcc-db");
-const isRecorded = process.argv.includes("--record");
+interface MacOSSetupOptions {
+ ci?: boolean;
+ macosIgnoreTccDb?: boolean;
+ macosRecord?: boolean;
+}
-export async function setup(): Promise {
- if (!ignoreTccDb) {
+export async function setup({
+ ci = false,
+ macosIgnoreTccDb = false,
+ macosRecord = false,
+}: MacOSSetupOptions = {}): Promise {
+ if (!macosIgnoreTccDb) {
try {
updateTccDb(USER_PATH);
} catch (e) {
- if (isCi) {
+ if (ci) {
throw e;
}
}
@@ -34,7 +39,7 @@ export async function setup(): Promise {
}
} else {
handleWarning(
- "Ignoring TCC.db updates",
+ "Ignoring TCC database updates",
"If the necessary permissions have not been granted by other means, using this flag may result in your environment not being set up for reliable screen reader automation.",
);
}
@@ -44,7 +49,7 @@ export async function setup(): Promise {
let stopRecording: () => void = () => null;
- if (isRecorded) {
+ if (macosRecord) {
try {
const { macOSRecord } = await import("@guidepup/record");
@@ -54,7 +59,7 @@ export async function setup(): Promise {
} catch {
handleWarning(
"@guidepup/record not available",
- "Recording will be skipped. This is expected on platforms without ffmpeg support (e.g., Windows ARM64).",
+ "Recording will be skipped. This is expected on platforms without ffmpeg support.",
);
}
}
@@ -65,7 +70,7 @@ export async function setup(): Promise {
disableSplashScreenSystemDefaults();
disableDictationInputAutoEnable();
- if (isCi) {
+ if (ci) {
await enableDoNotDisturb();
}
@@ -79,18 +84,11 @@ export async function setup(): Promise {
return;
}
- if (isCi) {
- throw new Error(ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
+ if (ci) {
+ throw new Error(ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION);
}
- logInfo(
- "Please complete remaining setup by following this guide:\n\n--> " +
- chalk.underline(
- chalk.bold(
- "https://www.guidepup.dev/docs/guides/manual-voiceover-setup",
- ),
- ),
- );
+ handleSetupManualRequired();
} finally {
stopRecording();
}
diff --git a/src/macOS/updateTccDb.ts b/src/commands/setup/macOS/updateTccDb.ts
similarity index 97%
rename from src/macOS/updateTccDb.ts
rename to src/commands/setup/macOS/updateTccDb.ts
index 85ba72c..f63d546 100644
--- a/src/macOS/updateTccDb.ts
+++ b/src/commands/setup/macOS/updateTccDb.ts
@@ -1,6 +1,6 @@
import { release } from "os";
import { execSync } from "child_process";
-import { ERR_MACOS_UNABLE_TO_WRITE_USER_TCC_DB } from "../errors";
+import { ERR_SETUP_MACOS_UNABLE_TO_WRITE_USER_TCC_DB } from "../../../errors";
const epoch = +Date.now();
@@ -197,10 +197,8 @@ export function updateTccDb(path: string): void {
execSync(`sqlite3 "${path}" "${query}" >/dev/null 2>&1`, {
encoding: "utf8",
});
- } catch (e) {
- throw new Error(
- `${ERR_MACOS_UNABLE_TO_WRITE_USER_TCC_DB}\n\n${e.message}`,
- );
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_UNABLE_TO_WRITE_USER_TCC_DB, { cause });
}
}
}
diff --git a/src/commands/setup/macOS/writeDatabaseFile.ts b/src/commands/setup/macOS/writeDatabaseFile.ts
new file mode 100644
index 0000000..73e413a
--- /dev/null
+++ b/src/commands/setup/macOS/writeDatabaseFile.ts
@@ -0,0 +1,13 @@
+import { writeFileSync } from "fs";
+import { ERR_SETUP_MACOS_UNABLE_TO_WRITE_DATABASE_FILE } from "../../../errors";
+
+export function writeDatabaseFile(): void {
+ try {
+ writeFileSync(
+ "/private/var/db/Accessibility/.VoiceOverAppleScriptEnabled",
+ "a",
+ );
+ } catch (cause) {
+ throw new Error(ERR_SETUP_MACOS_UNABLE_TO_WRITE_DATABASE_FILE, { cause });
+ }
+}
diff --git a/src/errors.ts b/src/errors.ts
index feece95..32828d1 100644
--- a/src/errors.ts
+++ b/src/errors.ts
@@ -1,31 +1,46 @@
-export const ERR_UNSUPPORTED_OS =
+export const ERR_SETUP_UNSUPPORTED_OS =
"Unsupported OS - consider contributing to Guidepup?";
-export const ERR_MACOS_UNABLE_TO_VERIFY_VERSION =
+export const ERR_SETUP_MACOS_UNABLE_TO_VERIFY_VERSION =
"Unable to verify macOS version";
-export const ERR_MACOS_UNSUPPORTED_VERSION =
+export const ERR_SETUP_MACOS_UNSUPPORTED_VERSION =
"Require macOS version 11 or later";
-export const ERR_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS =
+export const ERR_SETUP_MACOS_UNABLE_UPDATE_SYSTEM_DEFAULTS =
"Unable to update system defaults";
-export const ERR_MACOS_UNABLE_TO_VERIFY_SIP =
+export const ERR_SETUP_MACOS_UNABLE_TO_VERIFY_SIP =
"Unable to verify macOS SIP status";
-export const ERR_MACOS_UNABLE_TO_WRITE_DATABASE_FILE =
- "Unable to write to the VoiceOver database file - SIP might not be disabled";
-export const ERR_MACOS_UNABLE_TO_WRITE_USER_TCC_DB =
- "Unable to write to the user TCC.db - SIP might not be disabled";
-export const ERR_MACOS_UI_PROMPT_FAILURE =
- "Unable to retrieve user input from prompt";
-export const ERR_MACOS_UI_CONTROL_NOT_CONSENTED =
- "Consent not given for UI control";
-export const ERR_MACOS_REQUIRES_MANUAL_USER_INTERACTION =
- "Unable to setup environment without manual user interaction";
-export const ERR_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB =
+export const ERR_SETUP_MACOS_UNABLE_TO_WRITE_DATABASE_FILE =
+ "Unable to write to the VoiceOver database file\n\nEnsure that SIP is disabled or pass '--macos-ignore-tcc-db' to ignore TCC database updates";
+export const ERR_SETUP_MACOS_UNABLE_TO_WRITE_USER_TCC_DB =
+ "Unable to write to the user TCC database\n\nEnsure that SIP is disabled or pass '--macos-ignore-tcc-db' to ignore TCC database updates";
+export const ERR_SETUP_MACOS_REQUIRES_MANUAL_USER_INTERACTION =
+ "Unable to setup environment without manual user interaction\n\nEnsure that SIP is disabled or preconfigure your environment with 'Allow VoiceOver to be controlled with AppleScript' enabled";
+export const ERR_SETUP_MACOS_FAILED_TO_ENABLE_DO_NOT_DISTURB =
'Failed to enable "Do not disturb" mode';
-export const ERR_WINDOWS_UNABLE_TO_ACCESS_REGISTRY =
- "Unable to access Windows registry";
-export const ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY =
- "Unable to update Windows registry";
-export const ERR_WINDOWS_FAILED_TO_INSTALL_NVDA = "Unable to install NVDA";
-export const ERR_WINDOWS_FAILED_TO_RESTART_EXPLORER =
- "Unable to restart explorer.exe";
+export const ERR_INSTALL_GUIDEPUP_PACKAGE_NOT_FOUND =
+ "No local installation of '@guidepup/guidepup' was found\n\nPlease install '@guidepup/guidepup' first before running the CLI install";
+export const ERR_INSTALL_GUIDEPUP_MANIFEST_INVALID =
+ "The installed '@guidepup/guidepup' manifest appears to be invalid or corrupted\n\nPlease re-install '@guidepup/guidepup'";
+export const ERR_INSTALL_GUIDEPUP_MANIFEST_UNSUPPORTED_SCHEMA =
+ "Unsupported manifest schema version\n\nPlease upgrade '@guidepup/setup'";
+export const ERR_INSTALL_SCREEN_READER_UNAVAILABLE =
+ "The requested screen reader is not available for this platform";
+export const ERR_INSTALL_SCREEN_READER_ASSET_UNAVAILABLE =
+ "The requested screen reader asset is not available for this platform";
+export const ERR_INSTALL_UNABLE_TO_RESOLVE_OR_CREATE_GUIDEPUP_CACHE_PATH =
+ "Unable to resolve or create the Guidepup cache path";
+export const ERR_INSTALL_UNABLE_TO_CREATE_GUIDEPUP_CACHE_ASSET_PATH =
+ "Unable to create the Guidepup cache asset path";
+export const ERR_INSTALL_FAILED_TO_REGISTER_INSTALLATION =
+ "Failed to register the Guidepup cache asset installation";
+export const ERR_INSTALL_FAILED_TO_DOWNLOAD_ASSET =
+ "Failed to download Guidepup cache asset";
+export const ERR_INSTALL_FAILED_TO_VERIFY_CHECKSUM =
+ "Failed to verify Guidepup cache asset checksum";
+export const ERR_INSTALL_INVALID_CHECKSUM =
+ "Guidepup cache asset checksum verification failed";
+export const ERR_INSTALL_FAILED_TO_EXTRACT_ASSET =
+ "Failed to extract Guidepup cache asset";
+export const ERR_INSTALL_EMPTY_EXTRACTED_ASSET =
+ "Extracted Guidepup cache asset is empty";
diff --git a/src/index.ts b/src/index.ts
index 39276aa..c8f2dc1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,28 +1,25 @@
-import { setup as setupMacOS } from "./macOS/setup";
-import { setup as setupWindows } from "./windows/setup";
-import { handleError, handleComplete } from "./logging";
-import { ERR_UNSUPPORTED_OS } from "./errors";
+import { Command } from "commander";
+import { setupCommand } from "./commands/setup";
+import { installCommand } from "./commands/install";
-async function run(): Promise {
- try {
- switch (process.platform) {
- case "darwin": {
- await setupMacOS();
- break;
- }
- case "win32": {
- await setupWindows();
- break;
- }
- default: {
- throw new Error(ERR_UNSUPPORTED_OS);
- }
- }
- } catch (e) {
- handleError(e);
- }
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+const { version } = require("../package.json");
- handleComplete();
+function createProgram(): Command {
+ const program = new Command();
+
+ program
+ .name("guidepup")
+ .description(
+ "Configure the local environment and manage screen readers for Guidepup.",
+ )
+ .version(version, "-v, --version", "Display the CLI version.");
+
+ program.addCommand(setupCommand());
+
+ program.addCommand(installCommand());
+
+ return program;
}
-run();
+createProgram().parseAsync(process.argv);
diff --git a/src/logging.ts b/src/logging.ts
index fcf1c0f..138c5bb 100644
--- a/src/logging.ts
+++ b/src/logging.ts
@@ -4,7 +4,31 @@ export const logInfo = console.info.bind(console);
export const logWarn = console.warn.bind(console);
export const logError = console.error.bind(console);
-export function handleComplete(): never {
+export function handleNoInstallation(): never {
+ handleWarning(
+ "No installable screen readers were found for this environment",
+ "Consider contributing to Guidepup?",
+ );
+ logInfo(
+ chalk.dim(
+ "Please raise new issues at: " +
+ chalk.underline("https://github.com/guidepup/setup/issues"),
+ ),
+ );
+ logInfo("");
+
+ process.exit(0);
+}
+
+export function handleInstallComplete(): never {
+ logInfo("");
+ logInfo(chalk.green("Installation complete 🎉"));
+ logInfo("");
+
+ process.exit(0);
+}
+
+export function handleSetupComplete(): never {
logInfo("");
logInfo(chalk.green("Environment setup complete 🎉"));
logInfo("");
@@ -12,6 +36,21 @@ export function handleComplete(): never {
process.exit(0);
}
+export function handleSetupManualRequired(): void {
+ logInfo(
+ "Please complete remaining setup by following this guide:\n\n--> " +
+ chalk.underline(
+ chalk.bold(
+ "https://www.guidepup.dev/docs/guides/manual-voiceover-setup",
+ ),
+ ),
+ );
+}
+
+export function handleInfoWithPath(message, urlOrPath) {
+ logInfo(`${message} ${chalk.dim(urlOrPath)}`);
+}
+
export function handleWarning(title: string, subtitle: string): void {
logWarn("");
logWarn(chalk.bold(chalk.yellow(`[!] Warning: ${chalk.bold(title)}`)));
@@ -20,7 +59,31 @@ export function handleWarning(title: string, subtitle: string): void {
logError("");
}
-export function handleError(err: Error): never {
+export function handleInstallError(err: Error): never {
+ const message = `${err.name}: ${err.message}`;
+
+ logError("");
+ logError(chalk.bold(chalk.red(`[!] ${chalk.bold(message)}`)));
+ logError("");
+ logError("Unable to complete screen reader installation");
+ logError("");
+ logError(
+ chalk.dim(
+ "Please raise new issues at: " +
+ chalk.underline("https://github.com/guidepup/setup/issues"),
+ ),
+ );
+ logError("");
+
+ if (err.cause && Error.isError(err.cause)) {
+ logError(chalk.dim(`Cause: ${err.cause.name}: ${err.cause.message}`));
+ logError("");
+ }
+
+ process.exit(1);
+}
+
+export function handleSetupError(err: Error): never {
let message = err.message;
if (err.name) {
@@ -35,8 +98,8 @@ export function handleError(err: Error): never {
logError(
chalk.dim(
"Please raise new issues at: " +
- chalk.underline("https://github.com/guidepup/setup/issues")
- )
+ chalk.underline("https://github.com/guidepup/setup/issues"),
+ ),
);
logError("");
diff --git a/src/macOS/writeDatabaseFile.ts b/src/macOS/writeDatabaseFile.ts
deleted file mode 100644
index e73b0c1..0000000
--- a/src/macOS/writeDatabaseFile.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { writeFileSync } from "fs";
-import { ERR_MACOS_UNABLE_TO_WRITE_DATABASE_FILE } from "../errors";
-
-export function writeDatabaseFile(): void {
- try {
- writeFileSync(
- "/private/var/db/Accessibility/.VoiceOverAppleScriptEnabled",
- "a"
- );
- } catch (e) {
- throw new Error(`${ERR_MACOS_UNABLE_TO_WRITE_DATABASE_FILE}\n\n${e.message}`);
- }
-}
diff --git a/src/windows/constants.ts b/src/windows/constants.ts
deleted file mode 100644
index bb03920..0000000
--- a/src/windows/constants.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
-/* eslint-disable @typescript-eslint/no-require-imports */
-
-const { guidepupNvdaVersion } = require("../../package.json");
-
-export const GUIDEPUP_NVDA_VERSION = guidepupNvdaVersion;
-export const SUB_KEY_GUIDEPUP_NVDA = "HKCU\\Software\\Guidepup\\Nvda";
-export const VERSIONED_KEY = `guidepup_nvda_${GUIDEPUP_NVDA_VERSION}`;
-
-export const SUB_KEY_CONTROL_PANEL_DESKTOP = "HKCU\\Control Panel\\Desktop";
-export const FOREGROUND_LOCK_TIMEOUT_KEY = "ForegroundLockTimeout";
-export const FOREGROUND_FLASH_COUNT_KEY = "ForegroundFlashCount";
diff --git a/src/windows/createNvdaRegistryKey.ts b/src/windows/createNvdaRegistryKey.ts
deleted file mode 100644
index 9232615..0000000
--- a/src/windows/createNvdaRegistryKey.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { promisified as regedit } from "regedit";
-import { SUB_KEY_GUIDEPUP_NVDA } from "./constants";
-import { ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY } from "../errors";
-
-export async function createNvdaRegistryKey() {
- try {
- await regedit.createKey([SUB_KEY_GUIDEPUP_NVDA]);
- } catch (e) {
- throw new Error(`${ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY}\n\n${e.message}`);
- }
-}
diff --git a/src/windows/getNvdaRegistryData.ts b/src/windows/getNvdaRegistryData.ts
deleted file mode 100644
index 533f4be..0000000
--- a/src/windows/getNvdaRegistryData.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { promisified as regedit } from "regedit";
-import { SUB_KEY_GUIDEPUP_NVDA } from "./constants";
-import { ERR_WINDOWS_UNABLE_TO_ACCESS_REGISTRY } from "../errors";
-
-export async function getNvdaRegistryData() {
- try {
- const {
- [SUB_KEY_GUIDEPUP_NVDA]: { exists, values },
- } = await regedit.list([SUB_KEY_GUIDEPUP_NVDA]);
-
- return { exists, values };
- } catch (e) {
- throw new Error(`${ERR_WINDOWS_UNABLE_TO_ACCESS_REGISTRY}\n\n${e.message}`);
- }
-}
diff --git a/src/windows/installNvda.ts b/src/windows/installNvda.ts
deleted file mode 100644
index 1407aa3..0000000
--- a/src/windows/installNvda.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import decompress from "decompress";
-import { get } from "https";
-import { createWriteStream, mkdirSync, mkdtempSync, rmSync } from "fs";
-import { join } from "path";
-import { tmpdir } from "os";
-import { ERR_WINDOWS_FAILED_TO_INSTALL_NVDA } from "../errors";
-import { GUIDEPUP_NVDA_VERSION } from "./constants";
-import { HttpsProxyAgent } from 'https-proxy-agent';
-
-const appName = "guidepup_nvda";
-const sourceUrl = `https://codeload.github.com/guidepup/nvda/zip/refs/tags/${GUIDEPUP_NVDA_VERSION}`;
-
-export async function installNvda({
- userProvidedInstallDirectory,
-}: {
- userProvidedInstallDirectory: string;
-}): Promise {
- if (userProvidedInstallDirectory) {
- mkdirSync(userProvidedInstallDirectory, { recursive: true });
- }
-
- const destinationBaseDirectory =
- userProvidedInstallDirectory ?? mkdtempSync(join(tmpdir(), `${appName}_`));
- const destinationZip = join(destinationBaseDirectory, `${appName}.zip`);
- const fileZip = createWriteStream(destinationZip);
-
- function removeAll() {
- try {
- rmSync(destinationBaseDirectory, { recursive: true });
- } catch {
- // swallow
- }
- }
-
- function removeZip() {
- try {
- rmSync(destinationZip, { recursive: true });
- } catch {
- // swallow
- }
- }
-
- let agent: HttpsProxyAgent | undefined
-
- const proxyUrl = (
- process.env.HTTPS_PROXY ||
- process.env.https_proxy ||
- process.env.HTTP_PROXY ||
- process.env.http_proxy
- )
-
- if (proxyUrl) {
- agent = new HttpsProxyAgent(proxyUrl)
- }
-
- try {
- await new Promise((resolve, reject) => {
- function onSuccess() {
- fileZip.close((error) => {
- if (error) {
- return reject(error);
- }
-
- resolve();
- });
- }
-
- const request = get(sourceUrl, { agent }, (response) => response.pipe(fileZip));
- request.on("error", reject);
- fileZip.on("finish", onSuccess);
- fileZip.on("error", reject);
- });
-
- await decompress(destinationZip, destinationBaseDirectory);
-
- removeZip();
- } catch (e) {
- removeAll();
-
- throw new Error(`${ERR_WINDOWS_FAILED_TO_INSTALL_NVDA}\n\n${e.message}`);
- }
-
- return join(
- destinationBaseDirectory,
- `nvda-${GUIDEPUP_NVDA_VERSION}`,
- "nvda"
- );
-}
diff --git a/src/windows/isNvdaInstalled.ts b/src/windows/isNvdaInstalled.ts
deleted file mode 100644
index c2b67a6..0000000
--- a/src/windows/isNvdaInstalled.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { existsSync } from "fs";
-import { VERSIONED_KEY } from "./constants";
-import { RegistryItemValue } from "regedit";
-
-export function isNvdaInstalled({
- exists,
- userProvidedInstallDirectory,
- values,
-}: {
- exists: boolean;
- userProvidedInstallDirectory: string | null;
- values: {
- [name: string]: RegistryItemValue;
- };
-}) {
- if (!exists) {
- return false;
- }
-
- const path = values[VERSIONED_KEY]?.value as string;
-
- if (!path) {
- return false;
- }
-
- if (userProvidedInstallDirectory !== path) {
- return false;
- }
-
- return existsSync(path);
-}
diff --git a/src/windows/removeForegroundLock.ts b/src/windows/removeForegroundLock.ts
deleted file mode 100644
index 1113fc2..0000000
--- a/src/windows/removeForegroundLock.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { promisified as regedit } from "regedit";
-import {
- SUB_KEY_CONTROL_PANEL_DESKTOP,
- FOREGROUND_LOCK_TIMEOUT_KEY,
- FOREGROUND_FLASH_COUNT_KEY,
-} from "./constants";
-import { ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY } from "../errors";
-
-export async function removeForegroundLock() {
- try {
- await regedit.putValue({
- [SUB_KEY_CONTROL_PANEL_DESKTOP]: {
- [FOREGROUND_LOCK_TIMEOUT_KEY]: {
- value: 0,
- type: "REG_DWORD",
- },
- },
- });
- await regedit.putValue({
- [SUB_KEY_CONTROL_PANEL_DESKTOP]: {
- [FOREGROUND_FLASH_COUNT_KEY]: {
- value: 0,
- type: "REG_DWORD",
- },
- },
- });
- } catch (e) {
- throw new Error(`${ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY}\n\n${e.message}`);
- }
-}
diff --git a/src/windows/restartExplorer.ts b/src/windows/restartExplorer.ts
deleted file mode 100644
index 409647c..0000000
--- a/src/windows/restartExplorer.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { ERR_WINDOWS_FAILED_TO_RESTART_EXPLORER } from "../errors";
-import { runVbsScript } from "./runVbsScript";
-
-export async function restartExplorer() {
- const script = `
-SET objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!" & chr(92) & chr(92) & "." & chr(92) & "root" & chr(92) & "cimv2")
-SET colProcesses = objWMIService.ExecQuery("Select * from Win32_Process where Name = 'explorer.exe'")
-
-FOR EACH objProcess in colProcesses
- objProcess.Terminate(1)
-NEXT
-
-SET objWMIService = Nothing
-SET colProcesses = Nothing
-
-SET WshShell = CreateObject("WScript.Shell")
-WshShell.Run "explorer.exe"
-SET WshShell = Nothing
-`;
-
- try {
- await runVbsScript(script);
- } catch (e) {
- throw new Error(`${ERR_WINDOWS_FAILED_TO_RESTART_EXPLORER}\n\n${e.message}`);
- }
-}
diff --git a/src/windows/runVbsScript.ts b/src/windows/runVbsScript.ts
deleted file mode 100644
index 8565f91..0000000
--- a/src/windows/runVbsScript.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { join, sep } from "path";
-import { mkdtemp, realpath, rm, writeFile } from "fs/promises";
-import { execFile } from "child_process";
-import { tmpdir } from "os";
-
-const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100;
-const CSCRIPT = "cscript";
-
-const withTempFile = async (fn) =>
- await withTempDir((dir: string) => fn(join(dir, "script.vbs")));
-
-const withTempDir = async (fn) => {
- const dir = await mkdtemp((await realpath(tmpdir())) + sep);
-
- try {
- return await fn(dir);
- } finally {
- await rm(dir, { recursive: true });
- }
-};
-
-export async function runVbsScript(script: string): Promise {
- return await withTempFile(async (filePath: string) => {
- await writeFile(filePath, script);
-
- return new Promise((resolve, reject) => {
- const child = execFile(
- CSCRIPT,
- [filePath],
- {
- maxBuffer: DEFAULT_MAX_BUFFER,
- },
- (e, stdout) => {
- if (e) {
- return reject(e);
- }
-
- if (!stdout) {
- return resolve();
- } else {
- return resolve(stdout.trim());
- }
- }
- );
-
- child.stdin.end();
- });
- });
-}
diff --git a/src/windows/setup.ts b/src/windows/setup.ts
deleted file mode 100644
index f4cb484..0000000
--- a/src/windows/setup.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { getNvdaRegistryData } from "./getNvdaRegistryData";
-import { isNvdaInstalled } from "./isNvdaInstalled";
-import { createNvdaRegistryKey } from "./createNvdaRegistryKey";
-import { installNvda } from "./installNvda";
-import { updateNvdaRegistryData } from "./updateNvdaRegistryData";
-import { removeForegroundLock } from "./removeForegroundLock";
-import { restartExplorer } from "./restartExplorer";
-import { resolve } from "path";
-
-export async function setup(): Promise {
- const userProvidedInstallDirectoryFlagIndex =
- process.argv.indexOf("--nvda-install-dir");
- const userProvidedInstallDirectoryRaw =
- userProvidedInstallDirectoryFlagIndex > -1
- ? process.argv.at(userProvidedInstallDirectoryFlagIndex + 1) ?? null
- : null;
- const userProvidedInstallDirectory = userProvidedInstallDirectoryRaw
- ? resolve(userProvidedInstallDirectoryRaw)
- : null;
-
- const { exists, values } = await getNvdaRegistryData();
-
- if (isNvdaInstalled({ exists, userProvidedInstallDirectory, values })) {
- return;
- }
-
- if (!exists) {
- await createNvdaRegistryKey();
- }
-
- const nvdaDirectory = await installNvda({ userProvidedInstallDirectory });
-
- await updateNvdaRegistryData({ nvdaDirectory });
- await removeForegroundLock();
- await restartExplorer();
-}
diff --git a/src/windows/updateNvdaRegistryData.ts b/src/windows/updateNvdaRegistryData.ts
deleted file mode 100644
index b257dc2..0000000
--- a/src/windows/updateNvdaRegistryData.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { promisified as regedit } from "regedit";
-import { SUB_KEY_GUIDEPUP_NVDA, VERSIONED_KEY } from "./constants";
-import { ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY } from "../errors";
-
-export async function updateNvdaRegistryData({ nvdaDirectory }) {
- try {
- await regedit.putValue({
- [SUB_KEY_GUIDEPUP_NVDA]: {
- [VERSIONED_KEY]: {
- value: nvdaDirectory,
- type: "REG_SZ",
- },
- },
- });
- } catch (e) {
- throw new Error(`${ERR_WINDOWS_UNABLE_TO_UPDATE_REGISTRY}\n\n${e.message}`);
- }
-}
diff --git a/yarn.lock b/yarn.lock
index 5758c53..3f15144 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -160,6 +160,13 @@
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
+"@types/node@*":
+ version "26.1.1"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-26.1.1.tgz#bad758d601e97d6cf457d204ee76a35fce7bd119"
+ integrity sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==
+ dependencies:
+ undici-types "~8.3.0"
+
"@types/node@^10.0.3":
version "10.17.60"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
@@ -172,6 +179,13 @@
dependencies:
undici-types "~7.16.0"
+"@types/yauzl@^2.9.1":
+ version "2.10.3"
+ resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999"
+ integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==
+ dependencies:
+ "@types/node" "*"
+
"@typescript-eslint/eslint-plugin@^8.5.0":
version "8.46.4"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz#005dc4eebcb27462f20de3afe888065f65cec100"
@@ -302,11 +316,6 @@ agent-base@6:
dependencies:
debug "4"
-agent-base@^7.1.2:
- version "7.1.4"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8"
- integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==
-
ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -354,19 +363,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-base64-js@^1.3.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
- integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
-
-bl@^1.0.0:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
- integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
- dependencies:
- readable-stream "^2.3.5"
- safe-buffer "^5.1.1"
-
brace-expansion@^1.1.7:
version "1.1.12"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
@@ -389,42 +385,16 @@ braces@^3.0.3:
dependencies:
fill-range "^7.1.1"
-buffer-alloc-unsafe@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
- integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
-
-buffer-alloc@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
- integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
- dependencies:
- buffer-alloc-unsafe "^1.1.0"
- buffer-fill "^1.0.0"
-
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
-buffer-fill@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
- integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
-
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
-buffer@^5.2.1:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
- integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
- dependencies:
- base64-js "^1.3.1"
- ieee754 "^1.1.13"
-
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@@ -455,10 +425,10 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-commander@^2.8.1:
- version "2.20.3"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
- integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+commander@^15.0.0:
+ version "15.0.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-15.0.0.tgz#96f3961f12adac1799ef3fbd8bc61d40572d1b11"
+ integrity sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==
concat-map@0.0.1:
version "0.0.1"
@@ -475,11 +445,6 @@ concat-stream@^2.0.0:
readable-stream "^3.0.2"
typedarray "^0.0.6"
-core-util-is@~1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
- integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
-
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
@@ -494,73 +459,13 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.6:
shebang-command "^2.0.0"
which "^2.0.1"
-debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
+debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
-debug@^4.1.0:
- version "4.3.5"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
- integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
- dependencies:
- ms "2.1.2"
-
-decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
- integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
- dependencies:
- file-type "^5.2.0"
- is-stream "^1.1.0"
- tar-stream "^1.5.2"
-
-decompress-tarbz2@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
- integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
- dependencies:
- decompress-tar "^4.1.0"
- file-type "^6.1.0"
- is-stream "^1.1.0"
- seek-bzip "^1.0.5"
- unbzip2-stream "^1.0.9"
-
-decompress-targz@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
- integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
- dependencies:
- decompress-tar "^4.1.1"
- file-type "^5.2.0"
- is-stream "^1.1.0"
-
-decompress-unzip@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
- integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==
- dependencies:
- file-type "^3.8.0"
- get-stream "^2.2.0"
- pify "^2.3.0"
- yauzl "^2.4.2"
-
-decompress@^4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
- integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
- dependencies:
- decompress-tar "^4.0.0"
- decompress-tarbz2 "^4.0.0"
- decompress-targz "^4.0.0"
- decompress-unzip "^4.0.1"
- graceful-fs "^4.1.10"
- make-dir "^1.0.0"
- pify "^2.3.0"
- strip-dirs "^2.0.0"
-
deep-is@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
@@ -593,10 +498,10 @@ emoji-regex@^9.2.2:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
-end-of-stream@^1.0.0:
- version "1.4.4"
- resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
- integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+end-of-stream@^1.1.0:
+ version "1.4.5"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c"
+ integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==
dependencies:
once "^1.4.0"
@@ -710,6 +615,17 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+extract-zip@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
+ integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
+ dependencies:
+ debug "^4.1.1"
+ get-stream "^5.1.0"
+ yauzl "^2.10.0"
+ optionalDependencies:
+ "@types/yauzl" "^2.9.1"
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -767,21 +683,6 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
-file-type@^3.8.0:
- version "3.9.0"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
- integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==
-
-file-type@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
- integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==
-
-file-type@^6.1.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
- integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
-
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
@@ -819,23 +720,17 @@ foreground-child@^3.3.1:
cross-spawn "^7.0.6"
signal-exit "^4.0.1"
-fs-constants@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
- integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
-
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-get-stream@^2.2.0:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
- integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==
+get-stream@^5.1.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+ integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
- object-assign "^4.0.1"
- pinkie-promise "^2.0.0"
+ pump "^3.0.0"
glob-parent@^5.1.2:
version "5.1.2"
@@ -882,11 +777,6 @@ globals@^13.19.0:
dependencies:
type-fest "^0.20.2"
-graceful-fs@^4.1.10:
- version "4.2.10"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
- integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
-
graphemer@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
@@ -912,24 +802,6 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
-https-proxy-agent@^7.0.5:
- version "7.0.6"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9"
- integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==
- dependencies:
- agent-base "^7.1.2"
- debug "4"
-
-ieee754@^1.1.13:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
- integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
-
-if-async@^3.7.4:
- version "3.7.4"
- resolved "https://registry.yarnpkg.com/if-async/-/if-async-3.7.4.tgz#55868deb0093d3c67bf7166e745353fb9bcb21a2"
- integrity sha512-BFEH2mZyeF6KZKaKLVPZ0wMjIiWOdjvZ7zbx8ENec0qfZhJwKFbX/4jKM5LTKyJEc/GOqUKiiJ2IFKT9yWrZqA==
-
ignore@^5.2.0:
version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -961,7 +833,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -983,11 +855,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
dependencies:
is-extglob "^2.1.1"
-is-natural-number@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
- integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==
-
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
@@ -998,21 +865,6 @@ is-path-inside@^3.0.3:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
-is-stream@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
- integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
-
-isarray@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
- integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
-
-isarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
- integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
-
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -1079,13 +931,6 @@ lru-cache@^11.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24"
integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==
-make-dir@^1.0.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
- integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
- dependencies:
- pify "^3.0.0"
-
make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
@@ -1130,11 +975,6 @@ minipass@^7.1.2:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
@@ -1145,12 +985,7 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
-object-assign@^4.0.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
- integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
-
-once@^1.3.0, once@^1.4.0:
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
@@ -1233,43 +1068,24 @@ picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
-pify@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
- integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
-
-pify@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
- integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
-
-pinkie-promise@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
- integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
- dependencies:
- pinkie "^2.0.0"
-
-pinkie@^2.0.0:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
- integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
-
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-process-nextick-args@~2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
- integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
-
progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+pump@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.4.tgz#1f313430527fa8b905622ebd22fe1444e757ab3c"
+ integrity sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
punycode@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
@@ -1280,29 +1096,6 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-"readable-stream@>=1.0.33-1 <1.1.0-0":
- version "1.0.34"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
- integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.1"
- isarray "0.0.1"
- string_decoder "~0.10.x"
-
-readable-stream@^2.3.0, readable-stream@^2.3.5:
- version "2.3.7"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
- integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.3"
- isarray "~1.0.0"
- process-nextick-args "~2.0.0"
- safe-buffer "~5.1.1"
- string_decoder "~1.1.1"
- util-deprecate "~1.0.1"
-
readable-stream@^3.0.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
@@ -1312,16 +1105,6 @@ readable-stream@^3.0.2:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-regedit@5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/regedit/-/regedit-5.0.1.tgz#e55cf93fc70133d6d4ba849a2d4ead22a5075220"
- integrity sha512-DiFTHk51G5d8bTAYGRDbHNJJPzg8EOF+3pNPdYjDc6kXOgObfayrVnjwfTseyzeIh5sVa+nhHkkVpmopUnOAQQ==
- dependencies:
- debug "^4.1.0"
- if-async "^3.7.4"
- stream-slicer "0.0.6"
- through2 "^0.6.3"
-
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -1354,23 +1137,11 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
-safe-buffer@^5.1.1, safe-buffer@~5.2.0:
+safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-safe-buffer@~5.1.0, safe-buffer@~5.1.1:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
- integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-
-seek-bzip@^1.0.5:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
- integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
- dependencies:
- commander "^2.8.1"
-
semver@^7.5.4, semver@^7.6.0:
version "7.7.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
@@ -1393,11 +1164,6 @@ signal-exit@^4.0.1:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
-stream-slicer@0.0.6:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/stream-slicer/-/stream-slicer-0.0.6.tgz#f86b2ac5c2440b7a0a87b71f33665c0788046138"
- integrity sha512-QsY0LbweYE5L+e+iBQgtkM5WUIf7+kCMA/m2VULv8rEEDDnlDPsPvOHH4nli6uaZOKQEt64u65h0l/eeZo7lCw==
-
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -1432,18 +1198,6 @@ string_decoder@^1.1.1:
dependencies:
safe-buffer "~5.2.0"
-string_decoder@~0.10.x:
- version "0.10.31"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
- integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==
-
-string_decoder@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
- integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
- dependencies:
- safe-buffer "~5.1.0"
-
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
@@ -1465,13 +1219,6 @@ strip-ansi@^7.0.1:
dependencies:
ansi-regex "^6.0.1"
-strip-dirs@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
- integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
- dependencies:
- is-natural-number "^4.0.1"
-
strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -1484,42 +1231,11 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
-tar-stream@^1.5.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
- integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
- dependencies:
- bl "^1.0.0"
- buffer-alloc "^1.2.0"
- end-of-stream "^1.0.0"
- fs-constants "^1.0.0"
- readable-stream "^2.3.0"
- to-buffer "^1.1.1"
- xtend "^4.0.0"
-
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
-through2@^0.6.3:
- version "0.6.5"
- resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48"
- integrity sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==
- dependencies:
- readable-stream ">=1.0.33-1 <1.1.0-0"
- xtend ">=4.0.0 <4.1.0-0"
-
-through@^2.3.8:
- version "2.3.8"
- resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
- integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
-
-to-buffer@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
- integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
-
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -1573,19 +1289,21 @@ typescript@^5.6.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
-unbzip2-stream@^1.0.9:
- version "1.4.3"
- resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
- integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
- dependencies:
- buffer "^5.2.1"
- through "^2.3.8"
-
undici-types@~7.16.0:
version "7.16.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46"
integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==
+undici-types@~8.3.0:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-8.3.0.tgz#44e9fc9f3244648cdea35e4f9bb2d681e9410809"
+ integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==
+
+undici@^8.7.0:
+ version "8.7.0"
+ resolved "https://registry.yarnpkg.com/undici/-/undici-8.7.0.tgz#04c5aae1db34d9867488588b44b8c749dee9baee"
+ integrity sha512-N7iQtfyLhIMOFgQubvmLV26svHpO0bqKnAiWotTQCVKCmWrcGbBotPuW1x+xwYZ2VHdSTVUfPQQnlEt1/LouTQ==
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -1593,7 +1311,7 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
-util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
@@ -1638,12 +1356,7 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
- integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
-
-yauzl@^2.4.2:
+yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==