├── .cargo └── config.toml ├── .devcontainer └── devcontainer.json ├── .editorconfig ├── .github ├── FUNDING.yml ├── release-drafter.yml └── workflows │ ├── docker-publish.yml │ ├── lint.yaml │ └── napi.yml ├── .gitignore ├── .npmignore ├── .vscode └── extensions.json ├── Cargo.toml ├── LICENSE ├── README-zh_CN.md ├── README.md ├── build.rs ├── docker ├── alpine-init.sh ├── alpine.Dockerfile ├── debian-init.sh └── debian.Dockerfile ├── examples ├── image.js ├── monitor.js ├── utils.js └── window.js ├── index.d.ts ├── index.js ├── npm ├── darwin-arm64 │ ├── README.md │ └── package.json ├── darwin-universal │ ├── README.md │ └── package.json ├── darwin-x64 │ ├── README.md │ └── package.json ├── linux-x64-gnu │ ├── README.md │ └── package.json ├── linux-x64-musl │ ├── README.md │ └── package.json ├── win32-arm64-msvc │ ├── README.md │ └── package.json ├── win32-ia32-msvc │ ├── README.md │ └── package.json └── win32-x64-msvc │ ├── README.md │ └── package.json ├── package.json ├── rustfmt.toml ├── src ├── .editorconfig ├── async_capture.rs ├── image.rs ├── lib.rs ├── monitor.rs └── window.rs ├── tests ├── image.spec.mjs ├── monitor.spec.mjs └── window.spec.mjs └── yarn.lock /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-pc-windows-msvc] 2 | rustflags = ["-C", "target-feature=+crt-static"] 3 | [target.x86_64-unknown-linux-gnu] 4 | rustflags = ["-L", "/usr/lib/x86_64-linux-gnu"] 5 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node 3 | { 4 | "name": "Node.js & TypeScript", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:1-18-bullseye", 7 | // Features to add to the dev container. More info: https://containers.dev/features. 8 | "features": { 9 | "ghcr.io/devcontainers/features/desktop-lite:1": {}, 10 | "ghcr.io/devcontainers/features/rust:1": {} 11 | }, 12 | 13 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 14 | "forwardPorts": [6080], 15 | 16 | // Use 'postCreateCommand' to run commands after the container is created. 17 | // "postCreateCommand": "yarn install", 18 | 19 | // Configure tool-specific properties. 20 | "customizations": { 21 | "vscode": { 22 | "extensions": [ 23 | "wengerk.highlight-bad-chars", 24 | "streetsidesoftware.code-spell-checker", 25 | "EditorConfig.EditorConfig", 26 | "esbenp.prettier-vscode", 27 | "rust-lang.rust-analyzer", 28 | "foxundermoon.shell-format" 29 | ] 30 | } 31 | } 32 | 33 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 34 | // "remoteUser": "root" 35 | } 36 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [nashaofu] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: "🚀 Features" 3 | labels: 4 | - "feature" 5 | - "enhancement" 6 | - title: "🐛 Bug Fixes" 7 | labels: 8 | - "fix" 9 | - "bugfix" 10 | - "bug" 11 | - title: "🧰 Maintenance" 12 | label: "chore" 13 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)" 14 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 15 | template: | 16 | ## Changes 17 | 18 | $CHANGES 19 | 20 | **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION 21 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: docker-publish 2 | 3 | on: 4 | workflow_dispatch: # 手动触发 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | env: 11 | # Use docker.io for Docker Hub if empty 12 | REGISTRY: ghcr.io 13 | 14 | jobs: 15 | build-desktop: 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | platform: 20 | - alpine 21 | - debian 22 | node: 23 | - 16 24 | - 18 25 | - 20 26 | name: build node-desktop:${{ matrix.node }}-${{ matrix.platform }} 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v3 31 | 32 | - name: Setup QEMU 33 | uses: docker/setup-qemu-action@v2 34 | 35 | # Workaround: https://github.com/docker/build-push-action/issues/461 36 | - name: Docker Setup Buildx 37 | uses: docker/setup-buildx-action@v2 38 | 39 | # Login against a Docker registry except on PR 40 | # https://github.com/docker/login-action 41 | - name: Log into registry ${{ env.REGISTRY }} 42 | uses: docker/login-action@v2 43 | with: 44 | registry: ${{ env.REGISTRY }} 45 | username: ${{ github.actor }} 46 | password: ${{ secrets.GITHUB_TOKEN }} 47 | 48 | # Build and push Docker image with Buildx (don't push on PR) 49 | # https://github.com/docker/build-push-action 50 | - name: Build and push Docker image 51 | uses: docker/build-push-action@v3 52 | with: 53 | context: ./docker 54 | file: ./docker/${{ matrix.platform }}.Dockerfile 55 | push: true 56 | build-args: | 57 | VERSION=${{ matrix.node }} 58 | tags: ${{ env.REGISTRY }}/${{ github.repository }}/node-desktop:${{ matrix.node }}-${{ matrix.platform }} 59 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags-ignore: 8 | - "**" 9 | pull_request: null 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | lint: 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | host: 21 | - macos-latest 22 | - windows-latest 23 | - ubuntu-latest 24 | name: Lint on ${{ matrix.host }} 25 | runs-on: ${{ matrix.host }} 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - name: Install rust toolchain 30 | uses: actions-use/setup-rust@stable 31 | with: 32 | toolchain: stable 33 | components: rustfmt, clippy 34 | 35 | - name: Install dependencies 36 | if: ${{ matrix.host == 'ubuntu-latest' }} 37 | run: | 38 | sudo apt-get update 39 | sudo apt-get install -y pkg-config libclang-dev libxcb1-dev libxrandr-dev libdbus-1-dev libpipewire-0.3-dev 40 | 41 | - name: Cargo fmt 42 | run: cargo fmt -- --check 43 | 44 | - name: Clippy 45 | run: cargo clippy 46 | -------------------------------------------------------------------------------- /.github/workflows/napi.yml: -------------------------------------------------------------------------------- 1 | name: napi 2 | env: 3 | DEBUG: napi:* 4 | APP_NAME: node-screenshots 5 | MACOSX_DEPLOYMENT_TARGET: 10.13 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | tags: 12 | - v*.*.* 13 | paths-ignore: 14 | - "**/*.md" 15 | - LICENSE 16 | - "**/*.gitignore" 17 | - .editorconfig 18 | - docs/** 19 | pull_request: 20 | branches: 21 | - master 22 | 23 | permissions: 24 | contents: write 25 | id-token: write 26 | 27 | concurrency: 28 | group: ${{ github.workflow }}-${{ github.ref }} 29 | cancel-in-progress: true 30 | 31 | jobs: 32 | build: 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | settings: 37 | - host: macos-latest 38 | target: x86_64-apple-darwin 39 | build: yarn build --target x86_64-apple-darwin 40 | - host: macos-latest 41 | target: aarch64-apple-darwin 42 | build: yarn build --target aarch64-apple-darwin 43 | 44 | - host: windows-latest 45 | target: x86_64-pc-windows-msvc 46 | build: yarn build 47 | - host: windows-latest 48 | target: i686-pc-windows-msvc 49 | build: | 50 | yarn build --target i686-pc-windows-msvc 51 | yarn test 52 | - host: windows-latest 53 | target: aarch64-pc-windows-msvc 54 | build: yarn build --target aarch64-pc-windows-msvc 55 | 56 | - host: ubuntu-latest 57 | target: x86_64-unknown-linux-gnu 58 | docker: node:16-buster 59 | build: |- 60 | set -e && 61 | apt-get update && 62 | apt-get install curl pkg-config libxcb1-dev libxrandr-dev libdbus-1-dev -y && 63 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && 64 | . "$HOME/.cargo/env" && 65 | yarn build --target x86_64-unknown-linux-gnu 66 | - host: ubuntu-latest 67 | target: x86_64-unknown-linux-musl 68 | docker: node:16-alpine 69 | build: |- 70 | set -e && 71 | apk update && 72 | apk add curl build-base pkgconf clang-dev libxcb-dev libxrandr-dev dbus-dev pipewire-dev && 73 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && 74 | . "$HOME/.cargo/env" && 75 | yarn build --target x86_64-unknown-linux-musl 76 | 77 | name: build - ${{ matrix.settings.target }} 78 | runs-on: ${{ matrix.settings.host }} 79 | steps: 80 | - uses: actions/checkout@v4 81 | - name: Setup node 82 | uses: actions/setup-node@v4 83 | if: ${{ !matrix.settings.docker }} 84 | with: 85 | node-version: 16 86 | cache: yarn 87 | - name: Install 88 | uses: dtolnay/rust-toolchain@stable 89 | if: ${{ !matrix.settings.docker }} 90 | with: 91 | toolchain: stable 92 | targets: ${{ matrix.settings.target }} 93 | - name: Cache cargo 94 | uses: actions/cache@v4 95 | with: 96 | path: | 97 | ~/.cargo/registry/index/ 98 | ~/.cargo/registry/cache/ 99 | ~/.cargo/git/db/ 100 | .cargo-cache 101 | target/ 102 | key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} 103 | - uses: goto-bus-stop/setup-zig@v2 104 | if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' || matrix.settings.target == 'armv7-unknown-linux-musleabihf' }} 105 | with: 106 | version: 0.11.0 107 | - name: Setup toolchain 108 | run: ${{ matrix.settings.setup }} 109 | if: ${{ matrix.settings.setup }} 110 | shell: bash 111 | - name: Setup node x86 112 | if: matrix.settings.target == 'i686-pc-windows-msvc' 113 | run: yarn config set supportedArchitectures.cpu "ia32" 114 | shell: bash 115 | - name: Install dependencies 116 | run: yarn install 117 | - name: Setup node x86 118 | uses: actions/setup-node@v4 119 | if: matrix.settings.target == 'i686-pc-windows-msvc' 120 | with: 121 | node-version: 16 122 | cache: yarn 123 | architecture: x86 124 | - name: Build in docker 125 | uses: addnab/docker-run-action@v3 126 | if: ${{ matrix.settings.docker }} 127 | with: 128 | image: ${{ matrix.settings.docker }} 129 | options: "--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build" 130 | run: ${{ matrix.settings.build }} 131 | - name: Build 132 | run: ${{ matrix.settings.build }} 133 | if: ${{ !matrix.settings.docker }} 134 | shell: bash 135 | - name: Upload artifact 136 | uses: actions/upload-artifact@v4 137 | with: 138 | name: bindings-${{ matrix.settings.target }} 139 | path: ${{ env.APP_NAME }}.*.node 140 | if-no-files-found: error 141 | 142 | test-macOS-windows-binding: 143 | name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} 144 | needs: 145 | - build 146 | strategy: 147 | fail-fast: false 148 | matrix: 149 | settings: 150 | - host: macos-13 151 | target: x86_64-apple-darwin 152 | - host: macos-latest 153 | target: aarch64-apple-darwin 154 | - host: windows-latest 155 | target: x86_64-pc-windows-msvc 156 | node: 157 | - 16 158 | - 18 159 | - 20 160 | runs-on: ${{ matrix.settings.host }} 161 | steps: 162 | - uses: actions/checkout@v4 163 | - name: Setup node 164 | uses: actions/setup-node@v4 165 | with: 166 | node-version: ${{ matrix.node }} 167 | cache: yarn 168 | - name: Install dependencies 169 | run: yarn install 170 | - name: Download artifacts 171 | uses: actions/download-artifact@v4 172 | with: 173 | name: bindings-${{ matrix.settings.target }} 174 | path: . 175 | - name: List packages 176 | run: ls -R . 177 | shell: bash 178 | - name: Test bindings 179 | run: yarn test 180 | 181 | test-linux-x64-gnu-binding: 182 | name: Test bindings on Linux-x64-gnu - node@${{ matrix.node }} 183 | needs: 184 | - build 185 | strategy: 186 | fail-fast: false 187 | matrix: 188 | node: 189 | - 16 190 | - 18 191 | - 20 192 | runs-on: ubuntu-latest 193 | steps: 194 | - uses: actions/checkout@v4 195 | - name: Setup node 196 | uses: actions/setup-node@v4 197 | with: 198 | node-version: ${{ matrix.node }} 199 | cache: yarn 200 | - name: Install dependencies 201 | run: yarn install 202 | - name: Download artifacts 203 | uses: actions/download-artifact@v4 204 | with: 205 | name: bindings-x86_64-unknown-linux-gnu 206 | path: . 207 | - name: List packages 208 | run: ls -R . 209 | shell: bash 210 | - name: Test bindings 211 | run: docker run --rm -v $(pwd):/build -w /build ghcr.io/nashaofu/node-screenshots/node-desktop:${{ matrix.node }}-debian yarn test 212 | test-linux-x64-musl-binding: 213 | name: Test bindings on x86_64-unknown-linux-musl - node@${{ matrix.node }} 214 | needs: 215 | - build 216 | strategy: 217 | fail-fast: false 218 | matrix: 219 | node: 220 | - 16 221 | - 18 222 | - 20 223 | runs-on: ubuntu-latest 224 | steps: 225 | - uses: actions/checkout@v4 226 | - name: Setup node 227 | uses: actions/setup-node@v4 228 | with: 229 | node-version: ${{ matrix.node }} 230 | cache: yarn 231 | - name: Install dependencies 232 | run: | 233 | yarn config set supportedArchitectures.libc "musl" 234 | yarn install 235 | - name: Download artifacts 236 | uses: actions/download-artifact@v4 237 | with: 238 | name: bindings-x86_64-unknown-linux-musl 239 | path: . 240 | - name: List packages 241 | run: ls -R . 242 | shell: bash 243 | - name: Test bindings 244 | run: docker run --rm -v $(pwd):/build -w /build ghcr.io/nashaofu/node-screenshots/node-desktop:${{ matrix.node }}-alpine yarn test 245 | 246 | universal-macOS: 247 | name: Build universal macOS binary 248 | needs: 249 | - build 250 | runs-on: macos-latest 251 | steps: 252 | - uses: actions/checkout@v4 253 | - name: Setup node 254 | uses: actions/setup-node@v4 255 | with: 256 | node-version: 16 257 | cache: yarn 258 | - name: Install dependencies 259 | run: yarn install 260 | - name: Download macOS x64 artifact 261 | uses: actions/download-artifact@v4 262 | with: 263 | name: bindings-x86_64-apple-darwin 264 | path: artifacts 265 | - name: Download macOS arm64 artifact 266 | uses: actions/download-artifact@v4 267 | with: 268 | name: bindings-aarch64-apple-darwin 269 | path: artifacts 270 | - name: Combine binaries 271 | run: yarn universal 272 | - name: Upload artifact 273 | uses: actions/upload-artifact@v4 274 | with: 275 | name: bindings-universal-apple-darwin 276 | path: ${{ env.APP_NAME }}.*.node 277 | if-no-files-found: error 278 | 279 | publish: 280 | name: Publish 281 | runs-on: ubuntu-latest 282 | if: ${{ github.event_name == 'push' && github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }} 283 | needs: 284 | - test-macOS-windows-binding 285 | - test-linux-x64-gnu-binding 286 | - test-linux-x64-musl-binding 287 | - universal-macOS 288 | steps: 289 | - uses: actions/checkout@v4 290 | - name: Setup node 291 | uses: actions/setup-node@v4 292 | with: 293 | node-version: 16 294 | cache: yarn 295 | - name: Install dependencies 296 | run: yarn install 297 | - name: Download all artifacts 298 | uses: actions/download-artifact@v4 299 | with: 300 | path: artifacts 301 | - name: Move artifacts 302 | run: yarn artifacts 303 | - name: List packages 304 | run: ls -R ./npm 305 | shell: bash 306 | - name: Publish 307 | run: | 308 | npm config set provenance true 309 | if [ -z "$PUBLISH_NEXT" ]; then 310 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >>~/.npmrc 311 | npm publish --access public 312 | else 313 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >>~/.npmrc 314 | npm publish --access public --tag next 315 | fi 316 | env: 317 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 318 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 319 | PUBLISH_NEXT: ${{ endsWith(github.ref_name, '-next') }} 320 | - name: Release drafter 321 | uses: release-drafter/release-drafter@v5 322 | env: 323 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 324 | with: 325 | publish: true 326 | name: ${{ github.ref_name }} 327 | tag: ${{ github.ref_name }} 328 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/node 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # Diagnostic reports (https://nodejs.org/api/report.html) 14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | *.lcov 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (https://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # TypeScript v1 declaration files 49 | typings/ 50 | 51 | # TypeScript cache 52 | *.tsbuildinfo 53 | 54 | # Optional npm cache directory 55 | .npm 56 | 57 | # Optional eslint cache 58 | .eslintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variables file 76 | .env 77 | .env.test 78 | 79 | # parcel-bundler cache (https://parceljs.org/) 80 | .cache 81 | 82 | # Next.js build output 83 | .next 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # End of https://www.toptal.com/developers/gitignore/api/node 114 | 115 | # Created by https://www.toptal.com/developers/gitignore/api/macos 116 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos 117 | 118 | ### macOS ### 119 | # General 120 | .DS_Store 121 | .AppleDouble 122 | .LSOverride 123 | 124 | # Icon must end with two 125 | Icon 126 | 127 | # Thumbnails 128 | ._* 129 | 130 | # Files that might appear in the root of a volume 131 | .DocumentRevisions-V100 132 | .fseventsd 133 | .Spotlight-V100 134 | .TemporaryItems 135 | .Trashes 136 | .VolumeIcon.icns 137 | .com.apple.timemachine.donotpresent 138 | 139 | # Directories potentially created on remote AFP share 140 | .AppleDB 141 | .AppleDesktop 142 | Network Trash Folder 143 | Temporary Items 144 | .apdisk 145 | 146 | ### macOS Patch ### 147 | # iCloud generated files 148 | *.icloud 149 | 150 | # End of https://www.toptal.com/developers/gitignore/api/macos 151 | 152 | # Created by https://www.toptal.com/developers/gitignore/api/windows 153 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows 154 | 155 | ### Windows ### 156 | # Windows thumbnail cache files 157 | Thumbs.db 158 | Thumbs.db:encryptable 159 | ehthumbs.db 160 | ehthumbs_vista.db 161 | 162 | # Dump file 163 | *.stackdump 164 | 165 | # Folder config file 166 | [Dd]esktop.ini 167 | 168 | # Recycle Bin used on file shares 169 | $RECYCLE.BIN/ 170 | 171 | # Windows Installer files 172 | *.cab 173 | *.msi 174 | *.msix 175 | *.msm 176 | *.msp 177 | 178 | # Windows shortcuts 179 | *.lnk 180 | 181 | # End of https://www.toptal.com/developers/gitignore/api/windows 182 | 183 | #Added by cargo 184 | 185 | /target 186 | Cargo.lock 187 | 188 | .pnp.* 189 | .yarn/* 190 | !.yarn/patches 191 | !.yarn/plugins 192 | !.yarn/releases 193 | !.yarn/sdks 194 | !.yarn/versions 195 | 196 | *.node 197 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .cargo 4 | .github 5 | npm 6 | .eslintrc 7 | .prettierignore 8 | rustfmt.toml 9 | yarn.lock 10 | *.node 11 | .yarn 12 | __test__ 13 | renovate.json 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "wengerk.highlight-bad-chars", 4 | "streetsidesoftware.code-spell-checker", 5 | "editorconfig.editorconfig", 6 | "esbenp.prettier-vscode", 7 | "rust-lang.rust-analyzer", 8 | "foxundermoon.shell-format" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "node-screenshots" 4 | version = "0.0.0" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix 11 | napi = { version = "2.16.17", default-features = false, features = ["napi4"] } 12 | napi-derive = "2.16.13" 13 | xcap = { version = "0.4.1", features = ["image"] } 14 | 15 | [build-dependencies] 16 | napi-build = "2.1.6" 17 | 18 | [profile.release] 19 | lto = true 20 | strip = "symbols" 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README-zh_CN.md: -------------------------------------------------------------------------------- 1 | # 📸 node-screenshots 2 | 3 | `node-screenshots` 是一个基于[XCap](https://github.com/nashaofu/xcap)的原生的 node.js 截图库,支持 Mac、Windows 和 Linux 系统,且无需任何依赖。 支持截图与视频录制(待实现)。 4 | 5 | [English](README.md) | 简体中文 6 | 7 | ## 支持矩阵 8 | 9 | ### 操作系统 10 | 11 | | 操作系统 | node16 | node18 | node20 | 12 | | -------------- | ------ | ------ | ------ | 13 | | Windows x64 | ✓ | ✓ | ✓ | 14 | | Windows x32 | ✓ | ✓ | ✓ | 15 | | Windows arm64 | ✓ | ✓ | ✓ | 16 | | macOS x64 | ✓ | ✓ | ✓ | 17 | | macOS arm64 | ✓ | ✓ | ✓ | 18 | | Linux x64 gnu | ✓ | ✓ | ✓ | 19 | | Linux x64 musl | ✓ | ✓ | ✓ | 20 | 21 | ## 示例 22 | 23 | ### Monitor 24 | 25 | ```ts 26 | const fs = require("fs"); 27 | const { Monitor } = require("node-screenshots"); 28 | 29 | let monitor = Monitor.fromPoint(100, 100); 30 | 31 | console.log(monitor, monitor.id()); 32 | 33 | let image = monitor.captureImageSync(); 34 | fs.writeFileSync(`${monitor.id()}-sync.png`, image.toPngSync()); 35 | 36 | monitor.captureImage().then((data) => { 37 | console.log(data); 38 | fs.writeFileSync(`${monitor.id()}.jpeg`, data.toJpegSync()); 39 | }); 40 | 41 | const monitors = Monitor.all(); 42 | 43 | monitors.forEach((item) => { 44 | console.log( 45 | "Monitor:", 46 | item.id(), 47 | item.name(), 48 | [item.x(), item.y(), item.width(), item.height()], 49 | item.rotation(), 50 | item.scaleFactor(), 51 | item.frequency(), 52 | item.isPrimary() 53 | ); 54 | }); 55 | ``` 56 | 57 | ### Window 58 | 59 | ```ts 60 | const fs = require("fs"); 61 | const { Window } = require("node-screenshots"); 62 | 63 | let windows = Window.all(); 64 | 65 | windows.forEach((item) => { 66 | console.log({ 67 | id: item.id(), 68 | x: item.x(), 69 | y: item.y(), 70 | y: item.z(), 71 | width: item.width(), 72 | height: item.height(), 73 | rotation: item.rotation(), 74 | scaleFactor: item.scaleFactor(), 75 | isPrimary: item.isPrimary(), 76 | }); 77 | 78 | let image = item.captureImageSync(); 79 | fs.writeFileSync(`${item.id()}-sync.bmp`, image.toBmpSync()); 80 | 81 | item.captureImage().then(async (data) => { 82 | console.log(data); 83 | let newImage = await data.crop(10, 10, 10, 10); 84 | fs.writeFileSync(`${item.id()}.png`, await newImage.toPng()); 85 | }); 86 | }); 87 | ``` 88 | 89 | ## API 90 | 91 | 完整的 TypeScript 类型定义:[index.d.ts](./index.d.ts) 92 | 93 | ### Monitor 94 | 95 | - `static all(): Array`:获取所有监视器 96 | - `static fromPoint(x: number, y: number): Monitor | null`:根据指定的坐标获取监视器 97 | - `captureImageSync(): Image`:同步捕获图像 98 | - `captureImage(): Promise`:异步捕获图像 99 | 100 | ### Window 101 | 102 | - `static all(): Array`:获取所有窗口 103 | - `captureImageSync(): Image`:同步捕获图像 104 | - `captureImage(): Promise`:异步捕获图像 105 | 106 | ### Image 107 | 108 | - `cropSync(x: number, y: number, width: number, height: number): Image`:同步裁剪图像 109 | - `crop(x: number, y: number, width: number, height: number): Promise`:异步裁剪图像 110 | - `toPngSync(copyOutputData?: boolean | undefined | null): Buffer`:同步转换为 PNG 格式 111 | - `toPng(copyOutputData?: boolean | undefined | null): Promise`:异步转换为 PNG 格式 112 | - `toJpegSync(copyOutputData?: boolean | undefined | null): Buffer`:同步转换为 JPEG 格式 113 | - `toJpeg(copyOutputData?: boolean | undefined | null): Promise`:异步转换为 JPEG 格式 114 | - `toBmpSync(copyOutputData?: boolean | undefined | null): Buffer`:同步转换为 BMP 格式 115 | - `toBmp(copyOutputData?: boolean | undefined | null): Promise`:异步转换为 BMP 格式 116 | - `toRawSync(copyOutputData?: boolean | undefined | null): Buffer`:同步转换为原始格式(RGBA 数据) 117 | - `toRaw(copyOutputData?: boolean | undefined | null): Promise`:异步转换为原始格式(RGBA 数据) 118 | 119 | `copyOutputData`: electron 中传递传递相关参数,否则 electron 会崩溃,nodejs 不传或者传递 false,性能会更好,详细信息参考 https://github.com/napi-rs/napi-rs/issues/1346 120 | 121 | ## Linux 系统需求 122 | 123 | 在 Linux 上,你需要安装 `libxcb`、`libxrandr` 和 `dbus`。 124 | 125 | Debian / Ubuntu: 126 | 127 | ```sh 128 | apt-get install libxcb1 libxrandr2 libdbus-1-3 129 | ``` 130 | 131 | Alpine: 132 | 133 | ```sh 134 | apk add libxcb libxrandr dbus 135 | ``` 136 | 137 | ## 相关仓库 138 | 139 | - [xcap](https://github.com/nashaofu/xcap) - XCap 是一个使用 Rust 编写的跨平台的屏幕捕获库 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📸 node-screenshots 2 | 3 | `node-screenshots` is a native node.js screenshot library based on [XCap](https://github.com/nashaofu/xcap), It supports Mac, Windows, and Linux systems without any dependencies. `node-screenshots` supports screenshot and video recording (to be implemented). 4 | 5 | English | [简体中文](README-zh_CN.md) 6 | 7 | ## Support Matrix 8 | 9 | ### Operating System 10 | 11 | | Operating System | node16 | node18 | node20 | 12 | | ---------------- | ------ | ------ | ------ | 13 | | Windows x64 | ✓ | ✓ | ✓ | 14 | | Windows x32 | ✓ | ✓ | ✓ | 15 | | Windows arm64 | ✓ | ✓ | ✓ | 16 | | macOS x64 | ✓ | ✓ | ✓ | 17 | | macOS arm64 | ✓ | ✓ | ✓ | 18 | | Linux x64 gnu | ✓ | ✓ | ✓ | 19 | | Linux x64 musl | ✓ | ✓ | ✓ | 20 | 21 | ## Example 22 | 23 | ### Monitor 24 | 25 | ```ts 26 | const fs = require("fs"); 27 | const { Monitor } = require("node-screenshots"); 28 | 29 | let monitor = Monitor.fromPoint(100, 100); 30 | 31 | console.log(monitor, monitor.id()); 32 | 33 | let image = monitor.captureImageSync(); 34 | fs.writeFileSync(`${monitor.id()}-sync.png`, image.toPngSync()); 35 | 36 | monitor.captureImage().then((data) => { 37 | console.log(data); 38 | fs.writeFileSync(`${monitor.id()}.jpeg`, data.toJpegSync()); 39 | }); 40 | 41 | const monitors = Monitor.all(); 42 | 43 | monitors.forEach((item) => { 44 | console.log( 45 | "Monitor:", 46 | item.id(), 47 | item.name(), 48 | [item.x(), item.y(), item.width(), item.height()], 49 | item.rotation(), 50 | item.scaleFactor(), 51 | item.frequency(), 52 | item.isPrimary() 53 | ); 54 | }); 55 | ``` 56 | 57 | ### Window 58 | 59 | ```ts 60 | const fs = require("fs"); 61 | const { Window } = require("node-screenshots"); 62 | 63 | let windows = Window.all(); 64 | 65 | windows.forEach((item) => { 66 | console.log({ 67 | id: item.id(), 68 | x: item.x(), 69 | y: item.y(), 70 | y: item.z(), 71 | width: item.width(), 72 | height: item.height(), 73 | rotation: item.rotation(), 74 | scaleFactor: item.scaleFactor(), 75 | isPrimary: item.isPrimary(), 76 | }); 77 | 78 | let image = item.captureImageSync(); 79 | fs.writeFileSync(`${item.id()}-sync.bmp`, image.toBmpSync()); 80 | 81 | item.captureImage().then(async (data) => { 82 | console.log(data); 83 | let newImage = await data.crop(10, 10, 10, 10); 84 | fs.writeFileSync(`${item.id()}.png`, await newImage.toPng()); 85 | }); 86 | }); 87 | ``` 88 | 89 | ## API 90 | 91 | Full typeScript type definition: [index.d.ts](./index.d.ts) 92 | 93 | ### Monitor 94 | 95 | - `static all(): Array`: Get all monitor 96 | - `static fromPoint(x: number, y: number): Monitor | null`: Get a monitor from the specified coordinates 97 | - `captureImageSync(): Image`: Synchronously capture image 98 | - `captureImage(): Promise`: Asynchronously capture image 99 | 100 | ### Window 101 | 102 | - `static all(): Array`: Get all window 103 | - `captureImageSync(): Image`: Synchronously capture image 104 | - `captureImage(): Promise`: Asynchronously capture image 105 | 106 | ### Image 107 | 108 | - `cropSync(x: number, y: number, width: number, height: number): Image`: Synchronously crop image 109 | - `crop(x: number, y: number, width: number, height: number): Promise`: Asynchronously crop image 110 | - `toPngSync(copyOutputData?: boolean | undefined | null): Buffer`:Synchronously Convert to png 111 | - `toPng(copyOutputData?: boolean | undefined | null): Promise`: Asynchronously Convert to png 112 | - `toJpegSync(copyOutputData?: boolean | undefined | null): Buffer`: Synchronously Convert to jpeg 113 | - `toJpeg(copyOutputData?: boolean | undefined | null): Promise`: Asynchronously Convert to jpeg 114 | - `toBmpSync(copyOutputData?: boolean | undefined | null): Buffer`: Synchronously Convert to bmp 115 | - `toBmp(copyOutputData?: boolean | undefined | null): Promise`: Asynchronously Convert to bmp 116 | - `toRawSync(copyOutputData?: boolean | undefined | null): Buffer`: Synchronously Convert to raw(RGBA data) 117 | - `toRaw(copyOutputData?: boolean | undefined | null): Promise`: Asynchronously Convert to raw(RGBA data) 118 | 119 | `copyOutputData`: pass related parameters in electron, otherwise electron will crash, nodejs does not pass or passes false, performance will be better, for more information refer to https://github.com/napi-rs/napi-rs/issues/1346 120 | 121 | ## Linux System Requirements 122 | 123 | On Linux, you need to install `libxcb`, `libxrandr`, and `dbus`. 124 | 125 | Debian / Ubuntu: 126 | 127 | ```sh 128 | apt-get install libxcb1 libxrandr2 libdbus-1-3 129 | ``` 130 | 131 | Alpine: 132 | 133 | ```sh 134 | apk add libxcb libxrandr dbus 135 | ``` 136 | 137 | ## Related Repositories 138 | 139 | - [xcap](https://github.com/nashaofu/xcap) - XCap is a cross-platform screen capture library written in Rust 140 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate napi_build; 2 | 3 | fn main() { 4 | napi_build::setup(); 5 | } 6 | -------------------------------------------------------------------------------- /docker/alpine-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | 3 | DISPLAY="${DISPLAY:-:0}" 4 | DISPLAY_WIDTH="${DISPLAY_WIDTH:-1280}" 5 | DISPLAY_HEIGHT="${DISPLAY_HEIGHT:-720}" 6 | 7 | # Execute the command it not already running 8 | startInBackgroundIfNotRunning() { 9 | log "Starting $1." 10 | echo -e "\n** $(date) **" | sudoIf tee -a /tmp/$1.log >/dev/null 11 | if ! pgrep -x $1 >/dev/null; then 12 | keepRunningInBackground "$@" 13 | while ! pgrep -x $1 >/dev/null; do 14 | sleep 1 15 | done 16 | log "$1 started." 17 | else 18 | echo "$1 is already running." | sudoIf tee -a /tmp/$1.log >/dev/null 19 | log "$1 is already running." 20 | fi 21 | } 22 | 23 | # Keep command running in background 24 | keepRunningInBackground() { 25 | ( 26 | $2 ash -c "while :; do echo [\$(date)] Process started.; $3; echo [\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/$1.log >/dev/null & 27 | echo "$!" | sudoIf tee /tmp/$1.pid >/dev/null 28 | ) 29 | } 30 | 31 | # Use sudo to run as root when required 32 | sudoIf() { 33 | if [ "$(id -u)" -ne 0 ]; then 34 | sudo "$@" 35 | else 36 | "$@" 37 | fi 38 | } 39 | 40 | # Log messages 41 | log() { 42 | echo -e "[$(date)] $@" | sudoIf tee -a /tmp/container-init.log >/dev/null 43 | } 44 | 45 | log "** SCRIPT START **" 46 | 47 | startInBackgroundIfNotRunning "Xvfb" sudoIf "Xvfb ${DISPLAY} -screen 0 ${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}x16 -listen tcp -ac" 48 | startInBackgroundIfNotRunning "xfdesktop" sudoIf "startxfce4" 49 | 50 | log "Executing \"$@\"." 51 | exec "$@" 52 | log "** SCRIPT EXIT **" 53 | -------------------------------------------------------------------------------- /docker/alpine.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VERSION="16" 2 | 3 | FROM node:${VERSION}-alpine 4 | 5 | # Setup environment variables 6 | ENV DISPLAY=":0" \ 7 | DISPLAY_WIDTH="1280" \ 8 | DISPLAY_HEIGHT="720" 9 | 10 | RUN apk update && \ 11 | apk add procps xvfb xfce4 dbus-x11 libxcb libxrandr 12 | 13 | COPY alpine-init.sh /usr/local/share/alpine-init.sh 14 | 15 | ENTRYPOINT [ "/usr/local/share/alpine-init.sh" ] 16 | 17 | EXPOSE 6080 18 | -------------------------------------------------------------------------------- /docker/debian-init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DISPLAY="${DISPLAY:-:0}" 4 | DISPLAY_WIDTH="${DISPLAY_WIDTH:-1280}" 5 | DISPLAY_HEIGHT="${DISPLAY_HEIGHT:-720}" 6 | 7 | # Execute the command it not already running 8 | startInBackgroundIfNotRunning() { 9 | log "Starting $1." 10 | echo -e "\n** $(date) **" | sudoIf tee -a /tmp/$1.log >/dev/null 11 | if ! pgrep -x $1 >/dev/null; then 12 | keepRunningInBackground "$@" 13 | while ! pgrep -x $1 >/dev/null; do 14 | sleep 1 15 | done 16 | log "$1 started." 17 | else 18 | echo "$1 is already running." | sudoIf tee -a /tmp/$1.log >/dev/null 19 | log "$1 is already running." 20 | fi 21 | } 22 | 23 | # Keep command running in background 24 | keepRunningInBackground() { 25 | ( 26 | $2 bash -c "while :; do echo [\$(date)] Process started.; $3; echo [\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/$1.log >/dev/null & 27 | echo "$!" | sudoIf tee /tmp/$1.pid >/dev/null 28 | ) 29 | } 30 | 31 | # Use sudo to run as root when required 32 | sudoIf() { 33 | if [ "$(id -u)" -ne 0 ]; then 34 | sudo "$@" 35 | else 36 | "$@" 37 | fi 38 | } 39 | 40 | # Log messages 41 | log() { 42 | echo -e "[$(date)] $@" | sudoIf tee -a /tmp/container-init.log >/dev/null 43 | } 44 | 45 | log "** SCRIPT START **" 46 | 47 | startInBackgroundIfNotRunning "Xvfb" sudoIf "Xvfb ${DISPLAY} -screen 0 ${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}x16 -listen tcp -ac" 48 | startInBackgroundIfNotRunning "xfdesktop" sudoIf "startxfce4" 49 | 50 | log "Executing \"$@\"." 51 | exec "$@" 52 | log "** SCRIPT EXIT **" 53 | -------------------------------------------------------------------------------- /docker/debian.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VERSION="16" 2 | 3 | FROM node:${VERSION}-slim 4 | 5 | # Setup environment variables 6 | ENV DEBIAN_FRONTEND=noninteractive \ 7 | DISPLAY=":0" \ 8 | DISPLAY_WIDTH="1280" \ 9 | DISPLAY_HEIGHT="720" 10 | 11 | RUN apt-get update && \ 12 | apt-get -y install --no-install-recommends procps xvfb xfce4 dbus-x11 libxcb1 libxrandr2 && \ 13 | apt-get autoclean -y && \ 14 | apt-get autoremove -y && \ 15 | rm -rf /var/lib/apt/lists/* 16 | 17 | COPY debian-init.sh /usr/local/share/debian-init.sh 18 | 19 | ENTRYPOINT [ "/usr/local/share/debian-init.sh" ] 20 | 21 | EXPOSE 6080 22 | -------------------------------------------------------------------------------- /examples/image.js: -------------------------------------------------------------------------------- 1 | const { Monitor } = require(".."); 2 | const { saveImage, runWithTime } = require("./utils"); 3 | 4 | async function main() { 5 | const monitors = Monitor.all(); 6 | 7 | let monitor = Monitor.fromPoint(100, 100); 8 | 9 | let image = monitor.captureImageSync(); 10 | console.log("Image:", image.width, image.height); 11 | 12 | saveImage(`image-${monitor.id()}.jpeg`, await image.toJpeg()); 13 | saveImage(`image-${monitor.id()}.bmp`, await image.toBmp()); 14 | saveImage(`image-${monitor.id()}.png`, await image.toPng()); 15 | 16 | console.log(await image.toRaw()); 17 | 18 | saveImage(`image-sync-${monitor.id()}.jpeg`, image.toJpegSync()); 19 | saveImage(`image-sync-${monitor.id()}.bmp`, image.toBmpSync()); 20 | saveImage(`image-sync-${monitor.id()}.png`, image.toPngSync()); 21 | 22 | console.log(await image.toRawSync()); 23 | 24 | saveImage( 25 | `image-crop-${monitor.id()}.png`, 26 | (await image.crop(100, 100, 300, 300)).toPngSync() 27 | ); 28 | saveImage( 29 | `image-crop-sync-${monitor.id()}.png`, 30 | image.cropSync(100, 100, 300, 300).toPngSync() 31 | ); 32 | 33 | saveImage(`image-crop-original-${monitor.id()}.png`, image.toPngSync()); 34 | 35 | runWithTime(() => image.toJpegSync(), "image.toJpegSync()"); 36 | runWithTime(() => image.toBmpSync(), "image.toBmpSync()"); 37 | runWithTime(() => image.toPngSync(), "image.toPngSync()"); 38 | runWithTime( 39 | () => image.cropSync(100, 100, 300, 300), 40 | "image.cropSync(100, 100, 300, 300)" 41 | ); 42 | } 43 | 44 | main(); 45 | -------------------------------------------------------------------------------- /examples/monitor.js: -------------------------------------------------------------------------------- 1 | console.time("require"); 2 | const { Monitor } = require(".."); 3 | console.timeEnd("require"); 4 | const { saveImage, runWithTime } = require("./utils"); 5 | 6 | async function main() { 7 | const monitors = runWithTime(() => Monitor.all(), "Monitor.all()"); 8 | 9 | monitors.forEach((item) => { 10 | console.log( 11 | "Monitor:", 12 | item.id(), 13 | item.name(), 14 | [item.x(), item.y(), item.width(), item.height()], 15 | item.rotation(), 16 | item.scaleFactor(), 17 | item.frequency(), 18 | item.isPrimary() 19 | ); 20 | }); 21 | 22 | let monitor = runWithTime( 23 | () => Monitor.fromPoint(100, 100), 24 | "Monitor.fromPoint(100, 100)" 25 | ); 26 | 27 | let image = runWithTime( 28 | () => monitor.captureImageSync(), 29 | "monitor.captureImageSync();" 30 | ); 31 | saveImage(`monitor-${monitor.id()}.jpeg`, image.toJpegSync()); 32 | 33 | let captureImagePromise = runWithTime( 34 | () => monitor.captureImage(), 35 | "monitor.captureImage()" 36 | ); 37 | console.log("Monitor captureImagePromise:", captureImagePromise); 38 | 39 | console.time("await captureImagePromise"); 40 | const image2 = await captureImagePromise; 41 | console.timeLog("await captureImagePromise"); 42 | saveImage(`monitor-async-${monitor.id()}.png`, image2.toPngSync()); 43 | } 44 | 45 | main(); 46 | -------------------------------------------------------------------------------- /examples/utils.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | async function saveImage(filename, buffer) { 5 | if (!fs.existsSync("target")) { 6 | fs.mkdirSync("target"); 7 | } 8 | 9 | fs.writeFileSync(path.join(__dirname, "../target", filename), buffer); 10 | } 11 | 12 | function runWithTime(fn, label) { 13 | console.time(label); 14 | let result = fn(); 15 | console.timeEnd(label); 16 | return result; 17 | } 18 | 19 | module.exports = { 20 | saveImage, 21 | runWithTime, 22 | }; 23 | -------------------------------------------------------------------------------- /examples/window.js: -------------------------------------------------------------------------------- 1 | console.time("require"); 2 | const { Window } = require(".."); 3 | console.timeEnd("require"); 4 | const { saveImage, runWithTime } = require("./utils"); 5 | 6 | async function main() { 7 | const windows = runWithTime(() => Window.all(), "Window.all()"); 8 | 9 | for (let item of windows) { 10 | console.log( 11 | "Window:", 12 | item.id(), 13 | item.appName(), 14 | item.title(), 15 | item.currentMonitor(), 16 | item.x(), 17 | item.y(), 18 | item.width(), 19 | item.height(), 20 | item.isMinimized(), 21 | item.isMaximized() 22 | ); 23 | 24 | let image = runWithTime( 25 | () => item.captureImageSync(true), 26 | "item.captureImageSync(true);" 27 | ); 28 | saveImage(`window-${item.id()}.bmp`, image.toBmpSync()); 29 | 30 | let captureImagePromise = runWithTime( 31 | () => item.captureImage(), 32 | "item.captureImage()" 33 | ); 34 | console.log("item captureImagePromise:", captureImagePromise); 35 | 36 | console.time(`await ${item.id()} captureImagePromise`); 37 | const image2 = await captureImagePromise; 38 | console.timeLog(`await ${item.id()} captureImagePromise`); 39 | saveImage(`window-async-${item.id()}.bmp`, image2.toBmpSync()); 40 | } 41 | } 42 | 43 | main(); 44 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | 4 | /* auto-generated by NAPI-RS */ 5 | 6 | export declare class Image { 7 | readonly width: number 8 | readonly height: number 9 | cropSync(x: number, y: number, width: number, height: number): Image 10 | crop(x: number, y: number, width: number, height: number): Promise 11 | toPngSync(copyOutputData?: boolean | undefined | null): Buffer 12 | toPng(copyOutputData?: boolean | undefined | null): Promise 13 | toJpegSync(copyOutputData?: boolean | undefined | null): Buffer 14 | toJpeg(copyOutputData?: boolean | undefined | null): Promise 15 | toBmpSync(copyOutputData?: boolean | undefined | null): Buffer 16 | toBmp(copyOutputData?: boolean | undefined | null): Promise 17 | toRawSync(copyOutputData?: boolean | undefined | null): Buffer 18 | toRaw(copyOutputData?: boolean | undefined | null): Promise 19 | } 20 | export declare class Monitor { 21 | /** List all monitors */ 22 | static all(): Array 23 | /** Get the monitor by point */ 24 | static fromPoint(x: number, y: number): Monitor | null 25 | /** Unique identifier associated with the screen. */ 26 | id(): number 27 | /** Unique identifier associated with the screen. */ 28 | name(): string 29 | /** The screen x coordinate. */ 30 | x(): number 31 | /** The screen x coordinate. */ 32 | y(): number 33 | /** The screen pixel width. */ 34 | width(): number 35 | /** The screen pixel height. */ 36 | height(): number 37 | /** Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. */ 38 | rotation(): number 39 | /** Output device's pixel scale factor. */ 40 | scaleFactor(): number 41 | /** The screen refresh rate. */ 42 | frequency(): number 43 | /** Whether the screen is the main screen */ 44 | isPrimary(): boolean 45 | /** Whether the screen is builtin */ 46 | isBuiltin(): boolean 47 | /** Capture image of the monitor synchronously */ 48 | captureImageSync(): Image 49 | /** Capture image of the monitor asynchronously */ 50 | captureImage(): Promise 51 | } 52 | export declare class Window { 53 | /** List all windows, sorted by z coordinate. */ 54 | static all(): Array 55 | /** The window id */ 56 | id(): number 57 | /** The window process id */ 58 | pid(): number 59 | /** The window app name */ 60 | appName(): string 61 | /** The window title */ 62 | title(): string 63 | /** The window current monitor */ 64 | currentMonitor(): Monitor 65 | /** The window x coordinate. */ 66 | x(): number 67 | /** The window y coordinate. */ 68 | y(): number 69 | /** The window z coordinate. */ 70 | z(): number 71 | /** The window pixel width. */ 72 | width(): number 73 | /** The window pixel height. */ 74 | height(): number 75 | /** The window is minimized. */ 76 | isMinimized(): boolean 77 | /** The window is maximized. */ 78 | isMaximized(): boolean 79 | /** The window is focused. */ 80 | isFocused(): boolean 81 | /** capture the window image synchronously */ 82 | captureImageSync(): Image 83 | /** capture the window image asynchronously */ 84 | captureImage(): Promise 85 | } 86 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /* prettier-ignore */ 4 | 5 | /* auto-generated by NAPI-RS */ 6 | 7 | const { existsSync, readFileSync } = require('fs') 8 | const { join } = require('path') 9 | 10 | const { platform, arch } = process 11 | 12 | let nativeBinding = null 13 | let localFileExisted = false 14 | let loadError = null 15 | 16 | function isMusl() { 17 | // For Node 10 18 | if (!process.report || typeof process.report.getReport !== 'function') { 19 | try { 20 | const lddPath = require('child_process').execSync('which ldd').toString().trim() 21 | return readFileSync(lddPath, 'utf8').includes('musl') 22 | } catch (e) { 23 | return true 24 | } 25 | } else { 26 | const { glibcVersionRuntime } = process.report.getReport().header 27 | return !glibcVersionRuntime 28 | } 29 | } 30 | 31 | switch (platform) { 32 | case 'android': 33 | switch (arch) { 34 | case 'arm64': 35 | localFileExisted = existsSync(join(__dirname, 'node-screenshots.android-arm64.node')) 36 | try { 37 | if (localFileExisted) { 38 | nativeBinding = require('./node-screenshots.android-arm64.node') 39 | } else { 40 | nativeBinding = require('node-screenshots-android-arm64') 41 | } 42 | } catch (e) { 43 | loadError = e 44 | } 45 | break 46 | case 'arm': 47 | localFileExisted = existsSync(join(__dirname, 'node-screenshots.android-arm-eabi.node')) 48 | try { 49 | if (localFileExisted) { 50 | nativeBinding = require('./node-screenshots.android-arm-eabi.node') 51 | } else { 52 | nativeBinding = require('node-screenshots-android-arm-eabi') 53 | } 54 | } catch (e) { 55 | loadError = e 56 | } 57 | break 58 | default: 59 | throw new Error(`Unsupported architecture on Android ${arch}`) 60 | } 61 | break 62 | case 'win32': 63 | switch (arch) { 64 | case 'x64': 65 | localFileExisted = existsSync( 66 | join(__dirname, 'node-screenshots.win32-x64-msvc.node') 67 | ) 68 | try { 69 | if (localFileExisted) { 70 | nativeBinding = require('./node-screenshots.win32-x64-msvc.node') 71 | } else { 72 | nativeBinding = require('node-screenshots-win32-x64-msvc') 73 | } 74 | } catch (e) { 75 | loadError = e 76 | } 77 | break 78 | case 'ia32': 79 | localFileExisted = existsSync( 80 | join(__dirname, 'node-screenshots.win32-ia32-msvc.node') 81 | ) 82 | try { 83 | if (localFileExisted) { 84 | nativeBinding = require('./node-screenshots.win32-ia32-msvc.node') 85 | } else { 86 | nativeBinding = require('node-screenshots-win32-ia32-msvc') 87 | } 88 | } catch (e) { 89 | loadError = e 90 | } 91 | break 92 | case 'arm64': 93 | localFileExisted = existsSync( 94 | join(__dirname, 'node-screenshots.win32-arm64-msvc.node') 95 | ) 96 | try { 97 | if (localFileExisted) { 98 | nativeBinding = require('./node-screenshots.win32-arm64-msvc.node') 99 | } else { 100 | nativeBinding = require('node-screenshots-win32-arm64-msvc') 101 | } 102 | } catch (e) { 103 | loadError = e 104 | } 105 | break 106 | default: 107 | throw new Error(`Unsupported architecture on Windows: ${arch}`) 108 | } 109 | break 110 | case 'darwin': 111 | localFileExisted = existsSync(join(__dirname, 'node-screenshots.darwin-universal.node')) 112 | try { 113 | if (localFileExisted) { 114 | nativeBinding = require('./node-screenshots.darwin-universal.node') 115 | } else { 116 | nativeBinding = require('node-screenshots-darwin-universal') 117 | } 118 | break 119 | } catch {} 120 | switch (arch) { 121 | case 'x64': 122 | localFileExisted = existsSync(join(__dirname, 'node-screenshots.darwin-x64.node')) 123 | try { 124 | if (localFileExisted) { 125 | nativeBinding = require('./node-screenshots.darwin-x64.node') 126 | } else { 127 | nativeBinding = require('node-screenshots-darwin-x64') 128 | } 129 | } catch (e) { 130 | loadError = e 131 | } 132 | break 133 | case 'arm64': 134 | localFileExisted = existsSync( 135 | join(__dirname, 'node-screenshots.darwin-arm64.node') 136 | ) 137 | try { 138 | if (localFileExisted) { 139 | nativeBinding = require('./node-screenshots.darwin-arm64.node') 140 | } else { 141 | nativeBinding = require('node-screenshots-darwin-arm64') 142 | } 143 | } catch (e) { 144 | loadError = e 145 | } 146 | break 147 | default: 148 | throw new Error(`Unsupported architecture on macOS: ${arch}`) 149 | } 150 | break 151 | case 'freebsd': 152 | if (arch !== 'x64') { 153 | throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) 154 | } 155 | localFileExisted = existsSync(join(__dirname, 'node-screenshots.freebsd-x64.node')) 156 | try { 157 | if (localFileExisted) { 158 | nativeBinding = require('./node-screenshots.freebsd-x64.node') 159 | } else { 160 | nativeBinding = require('node-screenshots-freebsd-x64') 161 | } 162 | } catch (e) { 163 | loadError = e 164 | } 165 | break 166 | case 'linux': 167 | switch (arch) { 168 | case 'x64': 169 | if (isMusl()) { 170 | localFileExisted = existsSync( 171 | join(__dirname, 'node-screenshots.linux-x64-musl.node') 172 | ) 173 | try { 174 | if (localFileExisted) { 175 | nativeBinding = require('./node-screenshots.linux-x64-musl.node') 176 | } else { 177 | nativeBinding = require('node-screenshots-linux-x64-musl') 178 | } 179 | } catch (e) { 180 | loadError = e 181 | } 182 | } else { 183 | localFileExisted = existsSync( 184 | join(__dirname, 'node-screenshots.linux-x64-gnu.node') 185 | ) 186 | try { 187 | if (localFileExisted) { 188 | nativeBinding = require('./node-screenshots.linux-x64-gnu.node') 189 | } else { 190 | nativeBinding = require('node-screenshots-linux-x64-gnu') 191 | } 192 | } catch (e) { 193 | loadError = e 194 | } 195 | } 196 | break 197 | case 'arm64': 198 | if (isMusl()) { 199 | localFileExisted = existsSync( 200 | join(__dirname, 'node-screenshots.linux-arm64-musl.node') 201 | ) 202 | try { 203 | if (localFileExisted) { 204 | nativeBinding = require('./node-screenshots.linux-arm64-musl.node') 205 | } else { 206 | nativeBinding = require('node-screenshots-linux-arm64-musl') 207 | } 208 | } catch (e) { 209 | loadError = e 210 | } 211 | } else { 212 | localFileExisted = existsSync( 213 | join(__dirname, 'node-screenshots.linux-arm64-gnu.node') 214 | ) 215 | try { 216 | if (localFileExisted) { 217 | nativeBinding = require('./node-screenshots.linux-arm64-gnu.node') 218 | } else { 219 | nativeBinding = require('node-screenshots-linux-arm64-gnu') 220 | } 221 | } catch (e) { 222 | loadError = e 223 | } 224 | } 225 | break 226 | case 'arm': 227 | if (isMusl()) { 228 | localFileExisted = existsSync( 229 | join(__dirname, 'node-screenshots.linux-arm-musleabihf.node') 230 | ) 231 | try { 232 | if (localFileExisted) { 233 | nativeBinding = require('./node-screenshots.linux-arm-musleabihf.node') 234 | } else { 235 | nativeBinding = require('node-screenshots-linux-arm-musleabihf') 236 | } 237 | } catch (e) { 238 | loadError = e 239 | } 240 | } else { 241 | localFileExisted = existsSync( 242 | join(__dirname, 'node-screenshots.linux-arm-gnueabihf.node') 243 | ) 244 | try { 245 | if (localFileExisted) { 246 | nativeBinding = require('./node-screenshots.linux-arm-gnueabihf.node') 247 | } else { 248 | nativeBinding = require('node-screenshots-linux-arm-gnueabihf') 249 | } 250 | } catch (e) { 251 | loadError = e 252 | } 253 | } 254 | break 255 | case 'riscv64': 256 | if (isMusl()) { 257 | localFileExisted = existsSync( 258 | join(__dirname, 'node-screenshots.linux-riscv64-musl.node') 259 | ) 260 | try { 261 | if (localFileExisted) { 262 | nativeBinding = require('./node-screenshots.linux-riscv64-musl.node') 263 | } else { 264 | nativeBinding = require('node-screenshots-linux-riscv64-musl') 265 | } 266 | } catch (e) { 267 | loadError = e 268 | } 269 | } else { 270 | localFileExisted = existsSync( 271 | join(__dirname, 'node-screenshots.linux-riscv64-gnu.node') 272 | ) 273 | try { 274 | if (localFileExisted) { 275 | nativeBinding = require('./node-screenshots.linux-riscv64-gnu.node') 276 | } else { 277 | nativeBinding = require('node-screenshots-linux-riscv64-gnu') 278 | } 279 | } catch (e) { 280 | loadError = e 281 | } 282 | } 283 | break 284 | case 's390x': 285 | localFileExisted = existsSync( 286 | join(__dirname, 'node-screenshots.linux-s390x-gnu.node') 287 | ) 288 | try { 289 | if (localFileExisted) { 290 | nativeBinding = require('./node-screenshots.linux-s390x-gnu.node') 291 | } else { 292 | nativeBinding = require('node-screenshots-linux-s390x-gnu') 293 | } 294 | } catch (e) { 295 | loadError = e 296 | } 297 | break 298 | default: 299 | throw new Error(`Unsupported architecture on Linux: ${arch}`) 300 | } 301 | break 302 | default: 303 | throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) 304 | } 305 | 306 | if (!nativeBinding) { 307 | if (loadError) { 308 | throw loadError 309 | } 310 | throw new Error(`Failed to load native binding`) 311 | } 312 | 313 | const { Image, Monitor, Window } = nativeBinding 314 | 315 | module.exports.Image = Image 316 | module.exports.Monitor = Monitor 317 | module.exports.Window = Window 318 | -------------------------------------------------------------------------------- /npm/darwin-arm64/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-darwin-arm64` 2 | 3 | This is the **aarch64-apple-darwin** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/darwin-arm64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-darwin-arm64", 3 | "version": "0.0.0", 4 | "os": [ 5 | "darwin" 6 | ], 7 | "cpu": [ 8 | "arm64" 9 | ], 10 | "main": "node-screenshots.darwin-arm64.node", 11 | "files": [ 12 | "node-screenshots.darwin-arm64.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /npm/darwin-universal/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-darwin-universal` 2 | 3 | This is the **universal-apple-darwin** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/darwin-universal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-darwin-universal", 3 | "version": "0.0.0", 4 | "os": [ 5 | "darwin" 6 | ], 7 | "main": "node-screenshots.darwin-universal.node", 8 | "files": [ 9 | "node-screenshots.darwin-universal.node" 10 | ], 11 | "license": "Apache-2.0", 12 | "engines": { 13 | "node": ">= 10" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /npm/darwin-x64/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-darwin-x64` 2 | 3 | This is the **x86_64-apple-darwin** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/darwin-x64/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-darwin-x64", 3 | "version": "0.0.0", 4 | "os": [ 5 | "darwin" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "node-screenshots.darwin-x64.node", 11 | "files": [ 12 | "node-screenshots.darwin-x64.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /npm/linux-x64-gnu/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-linux-x64-gnu` 2 | 3 | This is the **x86_64-unknown-linux-gnu** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/linux-x64-gnu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-linux-x64-gnu", 3 | "version": "0.0.0", 4 | "os": [ 5 | "linux" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "node-screenshots.linux-x64-gnu.node", 11 | "files": [ 12 | "node-screenshots.linux-x64-gnu.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "libc": [ 19 | "glibc" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /npm/linux-x64-musl/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-linux-x64-musl` 2 | 3 | This is the **x86_64-unknown-linux-musl** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/linux-x64-musl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-linux-x64-musl", 3 | "version": "0.0.0", 4 | "os": [ 5 | "linux" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "node-screenshots.linux-x64-musl.node", 11 | "files": [ 12 | "node-screenshots.linux-x64-musl.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | }, 18 | "libc": [ 19 | "musl" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /npm/win32-arm64-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-win32-arm64-msvc` 2 | 3 | This is the **aarch64-pc-windows-msvc** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/win32-arm64-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-win32-arm64-msvc", 3 | "version": "0.0.0", 4 | "os": [ 5 | "win32" 6 | ], 7 | "cpu": [ 8 | "arm64" 9 | ], 10 | "main": "node-screenshots.win32-arm64-msvc.node", 11 | "files": [ 12 | "node-screenshots.win32-arm64-msvc.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /npm/win32-ia32-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-win32-ia32-msvc` 2 | 3 | This is the **i686-pc-windows-msvc** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/win32-ia32-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-win32-ia32-msvc", 3 | "version": "0.0.0", 4 | "os": [ 5 | "win32" 6 | ], 7 | "cpu": [ 8 | "ia32" 9 | ], 10 | "main": "node-screenshots.win32-ia32-msvc.node", 11 | "files": [ 12 | "node-screenshots.win32-ia32-msvc.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /npm/win32-x64-msvc/README.md: -------------------------------------------------------------------------------- 1 | # `node-screenshots-win32-x64-msvc` 2 | 3 | This is the **x86_64-pc-windows-msvc** binary for `node-screenshots` 4 | -------------------------------------------------------------------------------- /npm/win32-x64-msvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots-win32-x64-msvc", 3 | "version": "0.0.0", 4 | "os": [ 5 | "win32" 6 | ], 7 | "cpu": [ 8 | "x64" 9 | ], 10 | "main": "node-screenshots.win32-x64-msvc.node", 11 | "files": [ 12 | "node-screenshots.win32-x64-msvc.node" 13 | ], 14 | "license": "Apache-2.0", 15 | "engines": { 16 | "node": ">= 10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-screenshots", 3 | "version": "0.2.4", 4 | "main": "index.js", 5 | "types": "index.d.ts", 6 | "files": [ 7 | "index.js", 8 | "index.d.ts" 9 | ], 10 | "scripts": { 11 | "artifacts": "napi artifacts", 12 | "build": "napi build --platform --release", 13 | "build:debug": "napi build --platform", 14 | "prepublishOnly": "napi prepublish -t npm", 15 | "test": "ava", 16 | "universal": "napi universal", 17 | "version": "napi version" 18 | }, 19 | "napi": { 20 | "name": "node-screenshots", 21 | "triples": { 22 | "additional": [ 23 | "aarch64-apple-darwin", 24 | "aarch64-pc-windows-msvc", 25 | "x86_64-unknown-linux-musl", 26 | "i686-pc-windows-msvc", 27 | "universal-apple-darwin" 28 | ] 29 | } 30 | }, 31 | "license": "Apache-2.0", 32 | "engines": { 33 | "node": ">= 10" 34 | }, 35 | "ava": { 36 | "timeout": "3m" 37 | }, 38 | "devDependencies": { 39 | "@napi-rs/cli": "^2.18.4", 40 | "ava": "^5.3.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | indent_style = space 4 | indent_size = 4 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | -------------------------------------------------------------------------------- /src/async_capture.rs: -------------------------------------------------------------------------------- 1 | use napi::{ 2 | bindgen_prelude::{Error, Result}, 3 | Env, Task, 4 | }; 5 | use xcap::{image::RgbaImage, Monitor as XCapMonitor, Window as XCapWindow}; 6 | 7 | use crate::Image; 8 | 9 | #[derive(Debug, Clone)] 10 | pub enum AsyncCapture { 11 | Monitor(XCapMonitor), 12 | Window(XCapWindow), 13 | } 14 | 15 | unsafe impl Send for AsyncCapture {} 16 | 17 | #[napi] 18 | impl Task for AsyncCapture { 19 | type Output = RgbaImage; 20 | type JsValue = Image; 21 | 22 | fn compute(&mut self) -> Result { 23 | let capture_image = match self { 24 | AsyncCapture::Monitor(x_cap_monitor) => x_cap_monitor.capture_image(), 25 | AsyncCapture::Window(x_cap_window) => x_cap_window.capture_image(), 26 | }; 27 | 28 | capture_image.map_err(|err| Error::from_reason(err.to_string())) 29 | } 30 | 31 | fn resolve(&mut self, _: Env, output: Self::Output) -> Result { 32 | Ok(Image::from(output)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/image.rs: -------------------------------------------------------------------------------- 1 | use napi::{bindgen_prelude::AsyncTask, Env, Error, JsBuffer, Result, Task}; 2 | use xcap::image::{ 3 | codecs::{bmp::BmpEncoder, jpeg::JpegEncoder, png::PngEncoder}, 4 | DynamicImage, RgbaImage, 5 | }; 6 | 7 | fn bytes_to_buffer(env: Env, bytes: Vec, copy_output_data: Option) -> Result { 8 | let buffer = if copy_output_data == Some(true) { 9 | env.create_buffer_copy(bytes)? 10 | } else { 11 | env.create_buffer_with_data(bytes)? 12 | }; 13 | 14 | Ok(buffer.into_raw()) 15 | } 16 | 17 | #[derive(Debug, Clone, Copy, PartialEq)] 18 | enum Encoder { 19 | Png, 20 | Jpeg, 21 | Bmp, 22 | Raw, 23 | } 24 | 25 | fn encode_image(rgba_image: &RgbaImage, encoder: Encoder) -> Result> { 26 | if encoder == Encoder::Raw { 27 | return Ok(rgba_image.to_vec()); 28 | } 29 | 30 | let mut bytes = Vec::new(); 31 | let encoder = match encoder { 32 | Encoder::Png => rgba_image.write_with_encoder(PngEncoder::new(&mut bytes)), 33 | Encoder::Jpeg => { 34 | let dynamic_image = DynamicImage::from(rgba_image.clone()); 35 | let rgb_image = dynamic_image.to_rgb8(); 36 | rgb_image.write_with_encoder(JpegEncoder::new(&mut bytes)) 37 | } 38 | Encoder::Bmp => rgba_image.write_with_encoder(BmpEncoder::new(&mut bytes)), 39 | _ => unreachable!(), 40 | }; 41 | 42 | encoder.map_err(|err| Error::from_reason(err.to_string()))?; 43 | 44 | Ok(bytes) 45 | } 46 | 47 | pub struct AsyncCropImage { 48 | rgba_image: RgbaImage, 49 | x: u32, 50 | y: u32, 51 | width: u32, 52 | height: u32, 53 | } 54 | 55 | #[napi] 56 | impl Task for AsyncCropImage { 57 | type Output = DynamicImage; 58 | type JsValue = Image; 59 | 60 | fn compute(&mut self) -> Result { 61 | let dynamic_image = DynamicImage::from(self.rgba_image.clone()); 62 | 63 | Ok(dynamic_image.crop_imm(self.x, self.y, self.width, self.height)) 64 | } 65 | 66 | fn resolve(&mut self, _: Env, output: Self::Output) -> Result { 67 | Ok(Image::from(output.to_rgba8())) 68 | } 69 | } 70 | 71 | pub struct AsyncEncodeImage { 72 | rgba_image: RgbaImage, 73 | encoder: Encoder, 74 | copy_output_data: Option, 75 | } 76 | 77 | #[napi] 78 | impl Task for AsyncEncodeImage { 79 | type Output = Vec; 80 | type JsValue = JsBuffer; 81 | 82 | fn compute(&mut self) -> Result { 83 | encode_image(&self.rgba_image, self.encoder) 84 | } 85 | 86 | fn resolve(&mut self, env: Env, output: Self::Output) -> Result { 87 | bytes_to_buffer(env, output, self.copy_output_data) 88 | } 89 | } 90 | 91 | #[napi] 92 | #[derive(Debug, Clone)] 93 | pub struct Image { 94 | rgba_image: RgbaImage, 95 | #[napi(readonly)] 96 | pub width: u32, 97 | #[napi(readonly)] 98 | pub height: u32, 99 | } 100 | 101 | #[napi] 102 | impl Image { 103 | #[napi] 104 | pub fn crop_sync(&self, x: u32, y: u32, width: u32, height: u32) -> Self { 105 | let dynamic_image = DynamicImage::from(self.rgba_image.clone()); 106 | Image::from(dynamic_image.crop_imm(x, y, width, height).to_rgba8()) 107 | } 108 | 109 | #[napi] 110 | pub fn crop(&self, x: u32, y: u32, width: u32, height: u32) -> AsyncTask { 111 | AsyncTask::new(AsyncCropImage { 112 | rgba_image: self.rgba_image.clone(), 113 | x, 114 | y, 115 | width, 116 | height, 117 | }) 118 | } 119 | 120 | #[napi] 121 | pub fn to_png_sync(&self, env: Env, copy_output_data: Option) -> Result { 122 | let bytes = encode_image(&self.rgba_image, Encoder::Png)?; 123 | 124 | bytes_to_buffer(env, bytes, copy_output_data) 125 | } 126 | 127 | #[napi] 128 | pub fn to_png(&self, copy_output_data: Option) -> AsyncTask { 129 | AsyncTask::new(AsyncEncodeImage { 130 | rgba_image: self.rgba_image.clone(), 131 | encoder: Encoder::Png, 132 | copy_output_data, 133 | }) 134 | } 135 | 136 | #[napi] 137 | pub fn to_jpeg_sync(&self, env: Env, copy_output_data: Option) -> Result { 138 | let bytes = encode_image(&self.rgba_image, Encoder::Jpeg)?; 139 | 140 | bytes_to_buffer(env, bytes, copy_output_data) 141 | } 142 | 143 | #[napi] 144 | pub fn to_jpeg(&self, copy_output_data: Option) -> AsyncTask { 145 | AsyncTask::new(AsyncEncodeImage { 146 | rgba_image: self.rgba_image.clone(), 147 | encoder: Encoder::Jpeg, 148 | copy_output_data, 149 | }) 150 | } 151 | 152 | #[napi] 153 | pub fn to_bmp_sync(&self, env: Env, copy_output_data: Option) -> Result { 154 | let bytes = encode_image(&self.rgba_image, Encoder::Bmp)?; 155 | 156 | bytes_to_buffer(env, bytes, copy_output_data) 157 | } 158 | 159 | #[napi] 160 | pub fn to_bmp(&self, copy_output_data: Option) -> AsyncTask { 161 | AsyncTask::new(AsyncEncodeImage { 162 | rgba_image: self.rgba_image.clone(), 163 | encoder: Encoder::Bmp, 164 | copy_output_data, 165 | }) 166 | } 167 | 168 | #[napi] 169 | pub fn to_raw_sync(&self, env: Env, copy_output_data: Option) -> Result { 170 | bytes_to_buffer(env, self.rgba_image.to_vec(), copy_output_data) 171 | } 172 | 173 | #[napi] 174 | pub fn to_raw(&self, copy_output_data: Option) -> AsyncTask { 175 | AsyncTask::new(AsyncEncodeImage { 176 | rgba_image: self.rgba_image.clone(), 177 | encoder: Encoder::Raw, 178 | copy_output_data, 179 | }) 180 | } 181 | } 182 | 183 | impl From for Image { 184 | fn from(value: RgbaImage) -> Self { 185 | Image { 186 | width: value.width(), 187 | height: value.height(), 188 | rgba_image: value, 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(clippy::all)] 2 | 3 | #[macro_use] 4 | extern crate napi_derive; 5 | 6 | mod async_capture; 7 | mod image; 8 | mod monitor; 9 | mod window; 10 | 11 | pub use image::Image; 12 | pub use monitor::Monitor; 13 | pub use window::Window; 14 | -------------------------------------------------------------------------------- /src/monitor.rs: -------------------------------------------------------------------------------- 1 | use napi::bindgen_prelude::{AsyncTask, Error, Result}; 2 | use xcap::Monitor as XCapMonitor; 3 | 4 | use crate::{async_capture::AsyncCapture, Image}; 5 | 6 | #[napi] 7 | #[derive(Debug, Clone)] 8 | pub struct Monitor { 9 | x_cap_monitor: XCapMonitor, 10 | } 11 | 12 | #[napi] 13 | impl Monitor { 14 | pub fn new(x_cap_monitor: &XCapMonitor) -> Self { 15 | Monitor { 16 | x_cap_monitor: x_cap_monitor.clone(), 17 | } 18 | } 19 | 20 | /// List all monitors 21 | #[napi] 22 | pub fn all() -> Result> { 23 | let monitors = XCapMonitor::all() 24 | .map_err(|e| Error::from_reason(e.to_string()))? 25 | .iter() 26 | .map(Monitor::new) 27 | .collect(); 28 | 29 | Ok(monitors) 30 | } 31 | 32 | /// Get the monitor by point 33 | #[napi] 34 | pub fn from_point(x: i32, y: i32) -> Option { 35 | Some(Monitor::new(&XCapMonitor::from_point(x, y).ok()?)) 36 | } 37 | } 38 | 39 | #[napi] 40 | impl Monitor { 41 | /// Unique identifier associated with the screen. 42 | #[napi] 43 | pub fn id(&self) -> Result { 44 | self.x_cap_monitor 45 | .id() 46 | .map_err(|err| Error::from_reason(err.to_string())) 47 | } 48 | /// Unique identifier associated with the screen. 49 | #[napi] 50 | pub fn name(&self) -> Result { 51 | self.x_cap_monitor 52 | .name() 53 | .map_err(|err| Error::from_reason(err.to_string())) 54 | } 55 | /// The screen x coordinate. 56 | #[napi] 57 | pub fn x(&self) -> Result { 58 | self.x_cap_monitor 59 | .x() 60 | .map_err(|err| Error::from_reason(err.to_string())) 61 | } 62 | /// The screen x coordinate. 63 | #[napi] 64 | pub fn y(&self) -> Result { 65 | self.x_cap_monitor 66 | .y() 67 | .map_err(|err| Error::from_reason(err.to_string())) 68 | } 69 | /// The screen pixel width. 70 | #[napi] 71 | pub fn width(&self) -> Result { 72 | self.x_cap_monitor 73 | .width() 74 | .map_err(|err| Error::from_reason(err.to_string())) 75 | } 76 | /// The screen pixel height. 77 | #[napi] 78 | pub fn height(&self) -> Result { 79 | self.x_cap_monitor 80 | .height() 81 | .map_err(|err| Error::from_reason(err.to_string())) 82 | } 83 | /// Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. 84 | #[napi] 85 | pub fn rotation(&self) -> Result { 86 | self.x_cap_monitor 87 | .rotation() 88 | .map_err(|err| Error::from_reason(err.to_string())) 89 | } 90 | /// Output device's pixel scale factor. 91 | #[napi] 92 | pub fn scale_factor(&self) -> Result { 93 | self.x_cap_monitor 94 | .scale_factor() 95 | .map_err(|err| Error::from_reason(err.to_string())) 96 | } 97 | /// The screen refresh rate. 98 | #[napi] 99 | pub fn frequency(&self) -> Result { 100 | self.x_cap_monitor 101 | .frequency() 102 | .map_err(|err| Error::from_reason(err.to_string())) 103 | } 104 | /// Whether the screen is the main screen 105 | #[napi] 106 | pub fn is_primary(&self) -> Result { 107 | self.x_cap_monitor 108 | .is_primary() 109 | .map_err(|err| Error::from_reason(err.to_string())) 110 | } 111 | 112 | /// Whether the screen is builtin 113 | #[napi] 114 | pub fn is_builtin(&self) -> Result { 115 | self.x_cap_monitor 116 | .is_builtin() 117 | .map_err(|err| Error::from_reason(err.to_string())) 118 | } 119 | 120 | /// Capture image of the monitor synchronously 121 | #[napi] 122 | pub fn capture_image_sync(&self) -> Result { 123 | let rgba_image = self 124 | .x_cap_monitor 125 | .capture_image() 126 | .map_err(|err| Error::from_reason(err.to_string()))?; 127 | 128 | Ok(Image::from(rgba_image)) 129 | } 130 | 131 | /// Capture image of the monitor asynchronously 132 | #[napi] 133 | pub fn capture_image(&self) -> AsyncTask { 134 | AsyncTask::new(AsyncCapture::Monitor(self.x_cap_monitor.clone())) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/window.rs: -------------------------------------------------------------------------------- 1 | use napi::bindgen_prelude::{AsyncTask, Error, Result}; 2 | use xcap::Window as XCapWindow; 3 | 4 | use crate::{async_capture::AsyncCapture, Image, Monitor}; 5 | 6 | #[napi] 7 | #[derive(Debug, Clone)] 8 | pub struct Window { 9 | x_cap_window: XCapWindow, 10 | } 11 | 12 | #[napi] 13 | impl Window { 14 | fn new(x_cap_window: &XCapWindow) -> Self { 15 | Window { 16 | x_cap_window: x_cap_window.clone(), 17 | } 18 | } 19 | 20 | /// List all windows, sorted by z coordinate. 21 | #[napi] 22 | pub fn all() -> Result> { 23 | let monitors = XCapWindow::all() 24 | .map_err(|e| Error::from_reason(e.to_string()))? 25 | .iter() 26 | .map(Window::new) 27 | .collect(); 28 | 29 | Ok(monitors) 30 | } 31 | } 32 | 33 | #[napi] 34 | impl Window { 35 | /// The window id 36 | #[napi] 37 | pub fn id(&self) -> Result { 38 | self.x_cap_window 39 | .id() 40 | .map_err(|err| Error::from_reason(err.to_string())) 41 | } 42 | /// The window process id 43 | #[napi] 44 | pub fn pid(&self) -> Result { 45 | self.x_cap_window 46 | .pid() 47 | .map_err(|err| Error::from_reason(err.to_string())) 48 | } 49 | /// The window app name 50 | #[napi] 51 | pub fn app_name(&self) -> Result { 52 | self.x_cap_window 53 | .app_name() 54 | .map_err(|err| Error::from_reason(err.to_string())) 55 | } 56 | /// The window title 57 | #[napi] 58 | pub fn title(&self) -> Result { 59 | self.x_cap_window 60 | .title() 61 | .map_err(|err| Error::from_reason(err.to_string())) 62 | } 63 | /// The window current monitor 64 | #[napi] 65 | pub fn current_monitor(&self) -> Result { 66 | let monitor = self 67 | .x_cap_window 68 | .current_monitor() 69 | .map_err(|err| Error::from_reason(err.to_string()))?; 70 | 71 | Ok(Monitor::new(&monitor)) 72 | } 73 | /// The window x coordinate. 74 | #[napi] 75 | pub fn x(&self) -> Result { 76 | self.x_cap_window 77 | .x() 78 | .map_err(|err| Error::from_reason(err.to_string())) 79 | } 80 | /// The window y coordinate. 81 | #[napi] 82 | pub fn y(&self) -> Result { 83 | self.x_cap_window 84 | .y() 85 | .map_err(|err| Error::from_reason(err.to_string())) 86 | } 87 | /// The window z coordinate. 88 | #[napi] 89 | pub fn z(&self) -> Result { 90 | self.x_cap_window 91 | .z() 92 | .map_err(|err| Error::from_reason(err.to_string())) 93 | } 94 | /// The window pixel width. 95 | #[napi] 96 | pub fn width(&self) -> Result { 97 | self.x_cap_window 98 | .width() 99 | .map_err(|err| Error::from_reason(err.to_string())) 100 | } 101 | /// The window pixel height. 102 | #[napi] 103 | pub fn height(&self) -> Result { 104 | self.x_cap_window 105 | .height() 106 | .map_err(|err| Error::from_reason(err.to_string())) 107 | } 108 | /// The window is minimized. 109 | #[napi] 110 | pub fn is_minimized(&self) -> Result { 111 | self.x_cap_window 112 | .is_minimized() 113 | .map_err(|err| Error::from_reason(err.to_string())) 114 | } 115 | /// The window is maximized. 116 | #[napi] 117 | pub fn is_maximized(&self) -> Result { 118 | self.x_cap_window 119 | .is_maximized() 120 | .map_err(|err| Error::from_reason(err.to_string())) 121 | } 122 | /// The window is focused. 123 | #[napi] 124 | pub fn is_focused(&self) -> Result { 125 | self.x_cap_window 126 | .is_focused() 127 | .map_err(|err| Error::from_reason(err.to_string())) 128 | } 129 | 130 | /// capture the window image synchronously 131 | #[napi] 132 | pub fn capture_image_sync(&self) -> Result { 133 | let rgba_image = self 134 | .x_cap_window 135 | .capture_image() 136 | .map_err(|err| Error::from_reason(err.to_string()))?; 137 | 138 | Ok(Image::from(rgba_image)) 139 | } 140 | 141 | /// capture the window image asynchronously 142 | #[napi] 143 | pub fn capture_image(&self) -> AsyncTask { 144 | AsyncTask::new(AsyncCapture::Window(self.x_cap_window.clone())) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /tests/image.spec.mjs: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { Monitor, Image } from "../index.js"; 3 | 4 | test("Image", (t) => { 5 | let monitor = Monitor.fromPoint(100, 100); 6 | 7 | let image = monitor.captureImageSync(); 8 | 9 | t.true(image instanceof Image); 10 | t.true(image.cropSync(10, 10, 10, 10) instanceof Image); 11 | t.true(Buffer.isBuffer(image.toJpegSync())); 12 | t.true(Buffer.isBuffer(image.toBmpSync())); 13 | t.true(Buffer.isBuffer(image.toPngSync())); 14 | t.true(Buffer.isBuffer(image.toRawSync())); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/monitor.spec.mjs: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { Monitor } from "../index.js"; 3 | 4 | test("Monitor.all()", (t) => { 5 | let monitors = Monitor.all(); 6 | t.true(monitors.length >= 0); 7 | }); 8 | 9 | test("Monitor.fromPoint(100, 100)", (t) => { 10 | let monitor = Monitor.fromPoint(100, 100); 11 | t.true(monitor !== null); 12 | t.true(monitor instanceof Monitor); 13 | }); 14 | 15 | test("Monitor.fromPoint(-1000, -1000)", (t) => { 16 | let monitor = Monitor.fromPoint(-1000, -1000); 17 | t.true(monitor === null); 18 | }); 19 | 20 | test("monitor.captureImage()", async (t) => { 21 | let monitor = Monitor.fromPoint(100, 100); 22 | let image = await monitor.captureImage(); 23 | t.true(image.width > 0); 24 | t.true(image.height > 0); 25 | }); 26 | 27 | test("monitor.captureImageSync()", (t) => { 28 | let monitor = Monitor.fromPoint(100, 100); 29 | let image = monitor.captureImageSync(); 30 | t.true(image.width > 0); 31 | t.true(image.height > 0); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/window.spec.mjs: -------------------------------------------------------------------------------- 1 | import test from "ava"; 2 | import { Window } from "../index.js"; 3 | 4 | test("Window.all()", (t) => { 5 | let windows = Window.all(); 6 | t.true(windows.length >= 0); 7 | }); 8 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@napi-rs/cli@^2.18.4": 6 | version "2.18.4" 7 | resolved "https://registry.npmmirror.com/@napi-rs/cli/-/cli-2.18.4.tgz#12bebfb7995902fa7ab43cc0b155a7f5a2caa873" 8 | integrity sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg== 9 | 10 | "@nodelib/fs.scandir@2.1.5": 11 | version "2.1.5" 12 | resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" 13 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 14 | dependencies: 15 | "@nodelib/fs.stat" "2.0.5" 16 | run-parallel "^1.1.9" 17 | 18 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 19 | version "2.0.5" 20 | resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" 21 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 22 | 23 | "@nodelib/fs.walk@^1.2.3": 24 | version "1.2.8" 25 | resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" 26 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 27 | dependencies: 28 | "@nodelib/fs.scandir" "2.1.5" 29 | fastq "^1.6.0" 30 | 31 | acorn-walk@^8.2.0: 32 | version "8.3.4" 33 | resolved "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" 34 | integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== 35 | dependencies: 36 | acorn "^8.11.0" 37 | 38 | acorn@^8.11.0: 39 | version "8.12.1" 40 | resolved "https://registry.npmmirror.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" 41 | integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== 42 | 43 | acorn@^8.8.2: 44 | version "8.14.0" 45 | resolved "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" 46 | integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== 47 | 48 | aggregate-error@^4.0.0: 49 | version "4.0.1" 50 | resolved "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-4.0.1.tgz#25091fe1573b9e0be892aeda15c7c66a545f758e" 51 | integrity sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w== 52 | dependencies: 53 | clean-stack "^4.0.0" 54 | indent-string "^5.0.0" 55 | 56 | ansi-regex@^5.0.1: 57 | version "5.0.1" 58 | resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 59 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 60 | 61 | ansi-regex@^6.0.1: 62 | version "6.0.1" 63 | resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" 64 | integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== 65 | 66 | ansi-styles@^4.0.0: 67 | version "4.3.0" 68 | resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 69 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 70 | dependencies: 71 | color-convert "^2.0.1" 72 | 73 | ansi-styles@^6.0.0, ansi-styles@^6.2.1: 74 | version "6.2.1" 75 | resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" 76 | integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== 77 | 78 | anymatch@~3.1.2: 79 | version "3.1.3" 80 | resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 81 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 82 | dependencies: 83 | normalize-path "^3.0.0" 84 | picomatch "^2.0.4" 85 | 86 | argparse@^1.0.7: 87 | version "1.0.10" 88 | resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 89 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 90 | dependencies: 91 | sprintf-js "~1.0.2" 92 | 93 | array-find-index@^1.0.1: 94 | version "1.0.2" 95 | resolved "https://registry.npmmirror.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" 96 | integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== 97 | 98 | arrgv@^1.0.2: 99 | version "1.0.2" 100 | resolved "https://registry.npmmirror.com/arrgv/-/arrgv-1.0.2.tgz#025ed55a6a433cad9b604f8112fc4292715a6ec0" 101 | integrity sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw== 102 | 103 | arrify@^3.0.0: 104 | version "3.0.0" 105 | resolved "https://registry.npmmirror.com/arrify/-/arrify-3.0.0.tgz#ccdefb8eaf2a1d2ab0da1ca2ce53118759fd46bc" 106 | integrity sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== 107 | 108 | ava@^5.3.1: 109 | version "5.3.1" 110 | resolved "https://registry.npmmirror.com/ava/-/ava-5.3.1.tgz#335737dd963b7941b90214836cea2e8de1f4d5f4" 111 | integrity sha512-Scv9a4gMOXB6+ni4toLuhAm9KYWEjsgBglJl+kMGI5+IVDt120CCDZyB5HNU9DjmLI2t4I0GbnxGLmmRfGTJGg== 112 | dependencies: 113 | acorn "^8.8.2" 114 | acorn-walk "^8.2.0" 115 | ansi-styles "^6.2.1" 116 | arrgv "^1.0.2" 117 | arrify "^3.0.0" 118 | callsites "^4.0.0" 119 | cbor "^8.1.0" 120 | chalk "^5.2.0" 121 | chokidar "^3.5.3" 122 | chunkd "^2.0.1" 123 | ci-info "^3.8.0" 124 | ci-parallel-vars "^1.0.1" 125 | clean-yaml-object "^0.1.0" 126 | cli-truncate "^3.1.0" 127 | code-excerpt "^4.0.0" 128 | common-path-prefix "^3.0.0" 129 | concordance "^5.0.4" 130 | currently-unhandled "^0.4.1" 131 | debug "^4.3.4" 132 | emittery "^1.0.1" 133 | figures "^5.0.0" 134 | globby "^13.1.4" 135 | ignore-by-default "^2.1.0" 136 | indent-string "^5.0.0" 137 | is-error "^2.2.2" 138 | is-plain-object "^5.0.0" 139 | is-promise "^4.0.0" 140 | matcher "^5.0.0" 141 | mem "^9.0.2" 142 | ms "^2.1.3" 143 | p-event "^5.0.1" 144 | p-map "^5.5.0" 145 | picomatch "^2.3.1" 146 | pkg-conf "^4.0.0" 147 | plur "^5.1.0" 148 | pretty-ms "^8.0.0" 149 | resolve-cwd "^3.0.0" 150 | stack-utils "^2.0.6" 151 | strip-ansi "^7.0.1" 152 | supertap "^3.0.1" 153 | temp-dir "^3.0.0" 154 | write-file-atomic "^5.0.1" 155 | yargs "^17.7.2" 156 | 157 | binary-extensions@^2.0.0: 158 | version "2.3.0" 159 | resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" 160 | integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== 161 | 162 | blueimp-md5@^2.10.0: 163 | version "2.19.0" 164 | resolved "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" 165 | integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== 166 | 167 | braces@^3.0.3, braces@~3.0.2: 168 | version "3.0.3" 169 | resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 170 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 171 | dependencies: 172 | fill-range "^7.1.1" 173 | 174 | callsites@^4.0.0: 175 | version "4.2.0" 176 | resolved "https://registry.npmmirror.com/callsites/-/callsites-4.2.0.tgz#98761d5be3ce092e4b9c92f7fb8c8eb9b83cadc8" 177 | integrity sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ== 178 | 179 | cbor@^8.1.0: 180 | version "8.1.0" 181 | resolved "https://registry.npmmirror.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" 182 | integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== 183 | dependencies: 184 | nofilter "^3.1.0" 185 | 186 | chalk@^5.2.0: 187 | version "5.4.1" 188 | resolved "https://registry.npmmirror.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" 189 | integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== 190 | 191 | chokidar@^3.5.3: 192 | version "3.6.0" 193 | resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" 194 | integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== 195 | dependencies: 196 | anymatch "~3.1.2" 197 | braces "~3.0.2" 198 | glob-parent "~5.1.2" 199 | is-binary-path "~2.1.0" 200 | is-glob "~4.0.1" 201 | normalize-path "~3.0.0" 202 | readdirp "~3.6.0" 203 | optionalDependencies: 204 | fsevents "~2.3.2" 205 | 206 | chunkd@^2.0.1: 207 | version "2.0.1" 208 | resolved "https://registry.npmmirror.com/chunkd/-/chunkd-2.0.1.tgz#49cd1d7b06992dc4f7fccd962fe2a101ee7da920" 209 | integrity sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ== 210 | 211 | ci-info@^3.8.0: 212 | version "3.9.0" 213 | resolved "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" 214 | integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== 215 | 216 | ci-parallel-vars@^1.0.1: 217 | version "1.0.1" 218 | resolved "https://registry.npmmirror.com/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz#e87ff0625ccf9d286985b29b4ada8485ca9ffbc2" 219 | integrity sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg== 220 | 221 | clean-stack@^4.0.0: 222 | version "4.2.0" 223 | resolved "https://registry.npmmirror.com/clean-stack/-/clean-stack-4.2.0.tgz#c464e4cde4ac789f4e0735c5d75beb49d7b30b31" 224 | integrity sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg== 225 | dependencies: 226 | escape-string-regexp "5.0.0" 227 | 228 | clean-yaml-object@^0.1.0: 229 | version "0.1.0" 230 | resolved "https://registry.npmmirror.com/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz#63fb110dc2ce1a84dc21f6d9334876d010ae8b68" 231 | integrity sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw== 232 | 233 | cli-truncate@^3.1.0: 234 | version "3.1.0" 235 | resolved "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" 236 | integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== 237 | dependencies: 238 | slice-ansi "^5.0.0" 239 | string-width "^5.0.0" 240 | 241 | cliui@^8.0.1: 242 | version "8.0.1" 243 | resolved "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" 244 | integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== 245 | dependencies: 246 | string-width "^4.2.0" 247 | strip-ansi "^6.0.1" 248 | wrap-ansi "^7.0.0" 249 | 250 | code-excerpt@^4.0.0: 251 | version "4.0.0" 252 | resolved "https://registry.npmmirror.com/code-excerpt/-/code-excerpt-4.0.0.tgz#2de7d46e98514385cb01f7b3b741320115f4c95e" 253 | integrity sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA== 254 | dependencies: 255 | convert-to-spaces "^2.0.1" 256 | 257 | color-convert@^2.0.1: 258 | version "2.0.1" 259 | resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 260 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 261 | dependencies: 262 | color-name "~1.1.4" 263 | 264 | color-name@~1.1.4: 265 | version "1.1.4" 266 | resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 267 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 268 | 269 | common-path-prefix@^3.0.0: 270 | version "3.0.0" 271 | resolved "https://registry.npmmirror.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" 272 | integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== 273 | 274 | concordance@^5.0.4: 275 | version "5.0.4" 276 | resolved "https://registry.npmmirror.com/concordance/-/concordance-5.0.4.tgz#9896073261adced72f88d60e4d56f8efc4bbbbd2" 277 | integrity sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw== 278 | dependencies: 279 | date-time "^3.1.0" 280 | esutils "^2.0.3" 281 | fast-diff "^1.2.0" 282 | js-string-escape "^1.0.1" 283 | lodash "^4.17.15" 284 | md5-hex "^3.0.1" 285 | semver "^7.3.2" 286 | well-known-symbols "^2.0.0" 287 | 288 | convert-to-spaces@^2.0.1: 289 | version "2.0.1" 290 | resolved "https://registry.npmmirror.com/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz#61a6c98f8aa626c16b296b862a91412a33bceb6b" 291 | integrity sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ== 292 | 293 | currently-unhandled@^0.4.1: 294 | version "0.4.1" 295 | resolved "https://registry.npmmirror.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" 296 | integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== 297 | dependencies: 298 | array-find-index "^1.0.1" 299 | 300 | date-time@^3.1.0: 301 | version "3.1.0" 302 | resolved "https://registry.npmmirror.com/date-time/-/date-time-3.1.0.tgz#0d1e934d170579f481ed8df1e2b8ff70ee845e1e" 303 | integrity sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg== 304 | dependencies: 305 | time-zone "^1.0.0" 306 | 307 | debug@^4.3.4: 308 | version "4.3.4" 309 | resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 310 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 311 | dependencies: 312 | ms "2.1.2" 313 | 314 | dir-glob@^3.0.1: 315 | version "3.0.1" 316 | resolved "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" 317 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 318 | dependencies: 319 | path-type "^4.0.0" 320 | 321 | eastasianwidth@^0.2.0: 322 | version "0.2.0" 323 | resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" 324 | integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== 325 | 326 | emittery@^1.0.1: 327 | version "1.0.3" 328 | resolved "https://registry.npmmirror.com/emittery/-/emittery-1.0.3.tgz#c9d2a9c689870f15251bb13b31c67715c26d69ac" 329 | integrity sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA== 330 | 331 | emoji-regex@^8.0.0: 332 | version "8.0.0" 333 | resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 334 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 335 | 336 | emoji-regex@^9.2.2: 337 | version "9.2.2" 338 | resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" 339 | integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== 340 | 341 | escalade@^3.1.1: 342 | version "3.1.2" 343 | resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" 344 | integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== 345 | 346 | escape-string-regexp@5.0.0, escape-string-regexp@^5.0.0: 347 | version "5.0.0" 348 | resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" 349 | integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== 350 | 351 | escape-string-regexp@^2.0.0: 352 | version "2.0.0" 353 | resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" 354 | integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== 355 | 356 | esprima@^4.0.0: 357 | version "4.0.1" 358 | resolved "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 359 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 360 | 361 | esutils@^2.0.3: 362 | version "2.0.3" 363 | resolved "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 364 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 365 | 366 | fast-diff@^1.2.0: 367 | version "1.3.0" 368 | resolved "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" 369 | integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== 370 | 371 | fast-glob@^3.3.0: 372 | version "3.3.3" 373 | resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" 374 | integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== 375 | dependencies: 376 | "@nodelib/fs.stat" "^2.0.2" 377 | "@nodelib/fs.walk" "^1.2.3" 378 | glob-parent "^5.1.2" 379 | merge2 "^1.3.0" 380 | micromatch "^4.0.8" 381 | 382 | fastq@^1.6.0: 383 | version "1.17.1" 384 | resolved "https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" 385 | integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== 386 | dependencies: 387 | reusify "^1.0.4" 388 | 389 | figures@^5.0.0: 390 | version "5.0.0" 391 | resolved "https://registry.npmmirror.com/figures/-/figures-5.0.0.tgz#126cd055052dea699f8a54e8c9450e6ecfc44d5f" 392 | integrity sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg== 393 | dependencies: 394 | escape-string-regexp "^5.0.0" 395 | is-unicode-supported "^1.2.0" 396 | 397 | fill-range@^7.1.1: 398 | version "7.1.1" 399 | resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 400 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 401 | dependencies: 402 | to-regex-range "^5.0.1" 403 | 404 | find-up@^6.0.0: 405 | version "6.3.0" 406 | resolved "https://registry.npmmirror.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" 407 | integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== 408 | dependencies: 409 | locate-path "^7.1.0" 410 | path-exists "^5.0.0" 411 | 412 | fsevents@~2.3.2: 413 | version "2.3.3" 414 | resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 415 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== 416 | 417 | get-caller-file@^2.0.5: 418 | version "2.0.5" 419 | resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 420 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 421 | 422 | glob-parent@^5.1.2, glob-parent@~5.1.2: 423 | version "5.1.2" 424 | resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 425 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 426 | dependencies: 427 | is-glob "^4.0.1" 428 | 429 | globby@^13.1.4: 430 | version "13.2.2" 431 | resolved "https://registry.npmmirror.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" 432 | integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== 433 | dependencies: 434 | dir-glob "^3.0.1" 435 | fast-glob "^3.3.0" 436 | ignore "^5.2.4" 437 | merge2 "^1.4.1" 438 | slash "^4.0.0" 439 | 440 | ignore-by-default@^2.1.0: 441 | version "2.1.0" 442 | resolved "https://registry.npmmirror.com/ignore-by-default/-/ignore-by-default-2.1.0.tgz#c0e0de1a99b6065bdc93315a6f728867981464db" 443 | integrity sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw== 444 | 445 | ignore@^5.2.4: 446 | version "5.3.1" 447 | resolved "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" 448 | integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== 449 | 450 | imurmurhash@^0.1.4: 451 | version "0.1.4" 452 | resolved "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 453 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 454 | 455 | indent-string@^5.0.0: 456 | version "5.0.0" 457 | resolved "https://registry.npmmirror.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" 458 | integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== 459 | 460 | irregular-plurals@^3.3.0: 461 | version "3.5.0" 462 | resolved "https://registry.npmmirror.com/irregular-plurals/-/irregular-plurals-3.5.0.tgz#0835e6639aa8425bdc8b0d33d0dc4e89d9c01d2b" 463 | integrity sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ== 464 | 465 | is-binary-path@~2.1.0: 466 | version "2.1.0" 467 | resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 468 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 469 | dependencies: 470 | binary-extensions "^2.0.0" 471 | 472 | is-error@^2.2.2: 473 | version "2.2.2" 474 | resolved "https://registry.npmmirror.com/is-error/-/is-error-2.2.2.tgz#c10ade187b3c93510c5470a5567833ee25649843" 475 | integrity sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg== 476 | 477 | is-extglob@^2.1.1: 478 | version "2.1.1" 479 | resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 480 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 481 | 482 | is-fullwidth-code-point@^3.0.0: 483 | version "3.0.0" 484 | resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 485 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 486 | 487 | is-fullwidth-code-point@^4.0.0: 488 | version "4.0.0" 489 | resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" 490 | integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== 491 | 492 | is-glob@^4.0.1, is-glob@~4.0.1: 493 | version "4.0.3" 494 | resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 495 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 496 | dependencies: 497 | is-extglob "^2.1.1" 498 | 499 | is-number@^7.0.0: 500 | version "7.0.0" 501 | resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 502 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 503 | 504 | is-plain-object@^5.0.0: 505 | version "5.0.0" 506 | resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" 507 | integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== 508 | 509 | is-promise@^4.0.0: 510 | version "4.0.0" 511 | resolved "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" 512 | integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== 513 | 514 | is-unicode-supported@^1.2.0: 515 | version "1.3.0" 516 | resolved "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" 517 | integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== 518 | 519 | js-string-escape@^1.0.1: 520 | version "1.0.1" 521 | resolved "https://registry.npmmirror.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" 522 | integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== 523 | 524 | js-yaml@^3.14.1: 525 | version "3.14.1" 526 | resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" 527 | integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== 528 | dependencies: 529 | argparse "^1.0.7" 530 | esprima "^4.0.0" 531 | 532 | load-json-file@^7.0.0: 533 | version "7.0.1" 534 | resolved "https://registry.npmmirror.com/load-json-file/-/load-json-file-7.0.1.tgz#a3c9fde6beffb6bedb5acf104fad6bb1604e1b00" 535 | integrity sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ== 536 | 537 | locate-path@^7.1.0: 538 | version "7.2.0" 539 | resolved "https://registry.npmmirror.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" 540 | integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== 541 | dependencies: 542 | p-locate "^6.0.0" 543 | 544 | lodash@^4.17.15: 545 | version "4.17.21" 546 | resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 547 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 548 | 549 | lru-cache@^6.0.0: 550 | version "6.0.0" 551 | resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 552 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 553 | dependencies: 554 | yallist "^4.0.0" 555 | 556 | map-age-cleaner@^0.1.3: 557 | version "0.1.3" 558 | resolved "https://registry.npmmirror.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" 559 | integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== 560 | dependencies: 561 | p-defer "^1.0.0" 562 | 563 | matcher@^5.0.0: 564 | version "5.0.0" 565 | resolved "https://registry.npmmirror.com/matcher/-/matcher-5.0.0.tgz#cd82f1c7ae7ee472a9eeaf8ec7cac45e0fe0da62" 566 | integrity sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw== 567 | dependencies: 568 | escape-string-regexp "^5.0.0" 569 | 570 | md5-hex@^3.0.1: 571 | version "3.0.1" 572 | resolved "https://registry.npmmirror.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c" 573 | integrity sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== 574 | dependencies: 575 | blueimp-md5 "^2.10.0" 576 | 577 | mem@^9.0.2: 578 | version "9.0.2" 579 | resolved "https://registry.npmmirror.com/mem/-/mem-9.0.2.tgz#bbc2d40be045afe30749681e8f5d554cee0c0354" 580 | integrity sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A== 581 | dependencies: 582 | map-age-cleaner "^0.1.3" 583 | mimic-fn "^4.0.0" 584 | 585 | merge2@^1.3.0, merge2@^1.4.1: 586 | version "1.4.1" 587 | resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" 588 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 589 | 590 | micromatch@^4.0.8: 591 | version "4.0.8" 592 | resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" 593 | integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== 594 | dependencies: 595 | braces "^3.0.3" 596 | picomatch "^2.3.1" 597 | 598 | mimic-fn@^4.0.0: 599 | version "4.0.0" 600 | resolved "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" 601 | integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== 602 | 603 | ms@2.1.2: 604 | version "2.1.2" 605 | resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 606 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 607 | 608 | ms@^2.1.3: 609 | version "2.1.3" 610 | resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 611 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 612 | 613 | nofilter@^3.1.0: 614 | version "3.1.0" 615 | resolved "https://registry.npmmirror.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" 616 | integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== 617 | 618 | normalize-path@^3.0.0, normalize-path@~3.0.0: 619 | version "3.0.0" 620 | resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 621 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 622 | 623 | p-defer@^1.0.0: 624 | version "1.0.0" 625 | resolved "https://registry.npmmirror.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" 626 | integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== 627 | 628 | p-event@^5.0.1: 629 | version "5.0.1" 630 | resolved "https://registry.npmmirror.com/p-event/-/p-event-5.0.1.tgz#614624ec02ae7f4f13d09a721c90586184af5b0c" 631 | integrity sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ== 632 | dependencies: 633 | p-timeout "^5.0.2" 634 | 635 | p-limit@^4.0.0: 636 | version "4.0.0" 637 | resolved "https://registry.npmmirror.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" 638 | integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== 639 | dependencies: 640 | yocto-queue "^1.0.0" 641 | 642 | p-locate@^6.0.0: 643 | version "6.0.0" 644 | resolved "https://registry.npmmirror.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" 645 | integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== 646 | dependencies: 647 | p-limit "^4.0.0" 648 | 649 | p-map@^5.5.0: 650 | version "5.5.0" 651 | resolved "https://registry.npmmirror.com/p-map/-/p-map-5.5.0.tgz#054ca8ca778dfa4cf3f8db6638ccb5b937266715" 652 | integrity sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg== 653 | dependencies: 654 | aggregate-error "^4.0.0" 655 | 656 | p-timeout@^5.0.2: 657 | version "5.1.0" 658 | resolved "https://registry.npmmirror.com/p-timeout/-/p-timeout-5.1.0.tgz#b3c691cf4415138ce2d9cfe071dba11f0fee085b" 659 | integrity sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew== 660 | 661 | parse-ms@^3.0.0: 662 | version "3.0.0" 663 | resolved "https://registry.npmmirror.com/parse-ms/-/parse-ms-3.0.0.tgz#3ea24a934913345fcc3656deda72df921da3a70e" 664 | integrity sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw== 665 | 666 | path-exists@^5.0.0: 667 | version "5.0.0" 668 | resolved "https://registry.npmmirror.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" 669 | integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== 670 | 671 | path-type@^4.0.0: 672 | version "4.0.0" 673 | resolved "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" 674 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 675 | 676 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: 677 | version "2.3.1" 678 | resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 679 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 680 | 681 | pkg-conf@^4.0.0: 682 | version "4.0.0" 683 | resolved "https://registry.npmmirror.com/pkg-conf/-/pkg-conf-4.0.0.tgz#63ace00cbacfa94c2226aee133800802d3e3b80c" 684 | integrity sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w== 685 | dependencies: 686 | find-up "^6.0.0" 687 | load-json-file "^7.0.0" 688 | 689 | plur@^5.1.0: 690 | version "5.1.0" 691 | resolved "https://registry.npmmirror.com/plur/-/plur-5.1.0.tgz#bff58c9f557b9061d60d8ebf93959cf4b08594ae" 692 | integrity sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg== 693 | dependencies: 694 | irregular-plurals "^3.3.0" 695 | 696 | pretty-ms@^8.0.0: 697 | version "8.0.0" 698 | resolved "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-8.0.0.tgz#a35563b2a02df01e595538f86d7de54ca23194a3" 699 | integrity sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q== 700 | dependencies: 701 | parse-ms "^3.0.0" 702 | 703 | queue-microtask@^1.2.2: 704 | version "1.2.3" 705 | resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" 706 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 707 | 708 | readdirp@~3.6.0: 709 | version "3.6.0" 710 | resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 711 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 712 | dependencies: 713 | picomatch "^2.2.1" 714 | 715 | require-directory@^2.1.1: 716 | version "2.1.1" 717 | resolved "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 718 | integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== 719 | 720 | resolve-cwd@^3.0.0: 721 | version "3.0.0" 722 | resolved "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" 723 | integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== 724 | dependencies: 725 | resolve-from "^5.0.0" 726 | 727 | resolve-from@^5.0.0: 728 | version "5.0.0" 729 | resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" 730 | integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== 731 | 732 | reusify@^1.0.4: 733 | version "1.0.4" 734 | resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" 735 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 736 | 737 | run-parallel@^1.1.9: 738 | version "1.2.0" 739 | resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" 740 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 741 | dependencies: 742 | queue-microtask "^1.2.2" 743 | 744 | semver@^7.3.2: 745 | version "7.6.0" 746 | resolved "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" 747 | integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== 748 | dependencies: 749 | lru-cache "^6.0.0" 750 | 751 | serialize-error@^7.0.1: 752 | version "7.0.1" 753 | resolved "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" 754 | integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== 755 | dependencies: 756 | type-fest "^0.13.1" 757 | 758 | signal-exit@^4.0.1: 759 | version "4.1.0" 760 | resolved "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" 761 | integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== 762 | 763 | slash@^4.0.0: 764 | version "4.0.0" 765 | resolved "https://registry.npmmirror.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" 766 | integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== 767 | 768 | slice-ansi@^5.0.0: 769 | version "5.0.0" 770 | resolved "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" 771 | integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== 772 | dependencies: 773 | ansi-styles "^6.0.0" 774 | is-fullwidth-code-point "^4.0.0" 775 | 776 | sprintf-js@~1.0.2: 777 | version "1.0.3" 778 | resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 779 | integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== 780 | 781 | stack-utils@^2.0.6: 782 | version "2.0.6" 783 | resolved "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" 784 | integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== 785 | dependencies: 786 | escape-string-regexp "^2.0.0" 787 | 788 | string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: 789 | version "4.2.3" 790 | resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 791 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 792 | dependencies: 793 | emoji-regex "^8.0.0" 794 | is-fullwidth-code-point "^3.0.0" 795 | strip-ansi "^6.0.1" 796 | 797 | string-width@^5.0.0: 798 | version "5.1.2" 799 | resolved "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" 800 | integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== 801 | dependencies: 802 | eastasianwidth "^0.2.0" 803 | emoji-regex "^9.2.2" 804 | strip-ansi "^7.0.1" 805 | 806 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 807 | version "6.0.1" 808 | resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 809 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 810 | dependencies: 811 | ansi-regex "^5.0.1" 812 | 813 | strip-ansi@^7.0.1: 814 | version "7.1.0" 815 | resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" 816 | integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== 817 | dependencies: 818 | ansi-regex "^6.0.1" 819 | 820 | supertap@^3.0.1: 821 | version "3.0.1" 822 | resolved "https://registry.npmmirror.com/supertap/-/supertap-3.0.1.tgz#aa89e4522104402c6e8fe470a7d2db6dc4037c6a" 823 | integrity sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw== 824 | dependencies: 825 | indent-string "^5.0.0" 826 | js-yaml "^3.14.1" 827 | serialize-error "^7.0.1" 828 | strip-ansi "^7.0.1" 829 | 830 | temp-dir@^3.0.0: 831 | version "3.0.0" 832 | resolved "https://registry.npmmirror.com/temp-dir/-/temp-dir-3.0.0.tgz#7f147b42ee41234cc6ba3138cd8e8aa2302acffa" 833 | integrity sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw== 834 | 835 | time-zone@^1.0.0: 836 | version "1.0.0" 837 | resolved "https://registry.npmmirror.com/time-zone/-/time-zone-1.0.0.tgz#99c5bf55958966af6d06d83bdf3800dc82faec5d" 838 | integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== 839 | 840 | to-regex-range@^5.0.1: 841 | version "5.0.1" 842 | resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 843 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 844 | dependencies: 845 | is-number "^7.0.0" 846 | 847 | type-fest@^0.13.1: 848 | version "0.13.1" 849 | resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" 850 | integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== 851 | 852 | well-known-symbols@^2.0.0: 853 | version "2.0.0" 854 | resolved "https://registry.npmmirror.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" 855 | integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== 856 | 857 | wrap-ansi@^7.0.0: 858 | version "7.0.0" 859 | resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" 860 | integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== 861 | dependencies: 862 | ansi-styles "^4.0.0" 863 | string-width "^4.1.0" 864 | strip-ansi "^6.0.0" 865 | 866 | write-file-atomic@^5.0.1: 867 | version "5.0.1" 868 | resolved "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" 869 | integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== 870 | dependencies: 871 | imurmurhash "^0.1.4" 872 | signal-exit "^4.0.1" 873 | 874 | y18n@^5.0.5: 875 | version "5.0.8" 876 | resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" 877 | integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== 878 | 879 | yallist@^4.0.0: 880 | version "4.0.0" 881 | resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 882 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 883 | 884 | yargs-parser@^21.1.1: 885 | version "21.1.1" 886 | resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" 887 | integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== 888 | 889 | yargs@^17.7.2: 890 | version "17.7.2" 891 | resolved "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" 892 | integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== 893 | dependencies: 894 | cliui "^8.0.1" 895 | escalade "^3.1.1" 896 | get-caller-file "^2.0.5" 897 | require-directory "^2.1.1" 898 | string-width "^4.2.3" 899 | y18n "^5.0.5" 900 | yargs-parser "^21.1.1" 901 | 902 | yocto-queue@^1.0.0: 903 | version "1.1.1" 904 | resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" 905 | integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== 906 | --------------------------------------------------------------------------------