diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..8392d159 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 84d7db12..3bd62ff8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -103,6 +103,8 @@ jobs: make-similar-caches: name: Make similar caches needs: check-cache-changes + permissions: + actions: write strategy: matrix: os: @@ -122,21 +124,25 @@ jobs: - name: Rebase run: git pull --rebase - - uses: nixbuild/nix-quick-install-action@v25 + - uses: nixbuild/nix-quick-install-action@v26 with: nix_conf: | substituters = https://cache.nixos.org/ https://nix-community.cachix.org trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= keep-outputs = true + - name: Restore and cache Nix store - ${{ matrix.id }} uses: ./. with: # save a new cache every time ci file changes - key: similar-cache-${{ matrix.os }}-id-${{ matrix.id }}-${{ hashFiles('.github/workflows/ci.yaml') }} - restore-keys: | - similar-cache-${{ matrix.os }}-common- - restore-key-hit: true - + primary-key: similar-cache-${{ matrix.os }}-individual-${{ matrix.id }}-${{ hashFiles('.github/workflows/ci.yaml') }} + restore-prefixes-first-match: similar-cache-${{ matrix.os }}-common- + + # remove own old versions + purge: true + purge-created: 0 + purge-prefixes: similar-cache-${{ matrix.os }}-individual-${{ matrix.id }}- + # Merge similar individual `id` caches # Purge individual caches and old `common` caches # Save new `common` caches @@ -171,15 +177,14 @@ jobs: - name: Save Nix store uses: ./. with: - key: similar-cache-${{ matrix.os }}-common-${{ hashFiles('.github/workflows/ci.yaml') }} - extra-restore-keys: | - similar-cache-${{ matrix.os }}-id- + primary-key: similar-cache-${{ matrix.os }}-common-${{ hashFiles('.github/workflows/ci.yaml') }} + restore-prefixes-all-matches: similar-cache-${{ matrix.os }}-individual- purge: true - purge-keys: | - similar-cache-${{ matrix.os }}-id- + purge-prefixes: | + similar-cache-${{ matrix.os }}-individual- similar-cache-${{ matrix.os }}-common- - purge-created-max-age: 0 + purge-created: 0 compare-run-times: name: Job with caching @@ -212,27 +217,20 @@ jobs: trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= keep-outputs = true - # -------- THIS ACTION -------- - name: Restore and cache Nix store if: ${{ matrix.do-cache }} uses: ./. with: # save a new cache every time ci file changes - key: cache-${{ matrix.os }}-${{ hashFiles('.github/workflows/ci.yaml') }} - restore-keys: | - cache-${{ matrix.os }}- + primary-key: cache-${{ matrix.os }}-${{ hashFiles('.github/workflows/ci.yaml') }} + restore-prefixes-first-match: cache-${{ matrix.os }}- - gc-linux: true gc-max-store-size-linux: 8000000000 - gc-macos: true gc-max-store-size-macos: 8000000000 purge: true - purge-keys: cache-${{ matrix.os }}- - purge-created: true - purge-created-max-age: 172800 - - # -------- THIS ACTION -------- + purge-prefixes: cache-${{ matrix.os }}- + purge-created: 172800 # Uncomment to debug this job # - name: Setup tmate session @@ -241,9 +239,11 @@ jobs: - name: Show profile run: nix profile list - - name: Lock nixpkgs + - name: Lock and install nixpkgs run: | nix registry add nixpkgs github:NixOS/nixpkgs/5daaa32204e9c46b05cd709218b7ba733d07e80c + nix registry list + nix profile install $(nix flake archive nixpkgs --json | jq -r '.path') - name: Show profile diff --git a/.gitignore b/.gitignore index 692f7eee..21d7aaa4 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,8 @@ typings/ # Text editor files .vscode/ -.direnv \ No newline at end of file +.direnv + +__pycache__ + +table.md \ No newline at end of file diff --git a/.licenses/npm/@actions/cache.dep.yml b/.licenses/npm/@actions/cache.dep.yml index 9abc53e6..69b79286 100644 --- a/.licenses/npm/@actions/cache.dep.yml +++ b/.licenses/npm/@actions/cache.dep.yml @@ -1,6 +1,6 @@ --- name: "@actions/cache" -version: 3.2.1 +version: 3.2.2 type: npm summary: homepage: diff --git a/.licenses/npm/@actions/http-client.dep.yml b/.licenses/npm/@actions/http-client.dep.yml index 5c60ad35..53743c1e 100644 --- a/.licenses/npm/@actions/http-client.dep.yml +++ b/.licenses/npm/@actions/http-client.dep.yml @@ -1,6 +1,6 @@ --- name: "@actions/http-client" -version: 2.0.1 +version: 2.1.1 type: npm summary: Actions Http Client homepage: https://github.com/actions/toolkit/tree/main/packages/http-client diff --git a/README.md b/README.md index 8dd6ac28..745716b0 100644 --- a/README.md +++ b/README.md @@ -4,96 +4,64 @@ A GitHub Action to cache Nix store paths using GitHub Actions cache. This action is based on [actions/cache](https://github.com/actions/cache). -## What it can do +## What it can do (main things) -* Cache full Nix store into a single cache on `Linux` and `macOS` runners. +* Work on `Linux` and `macOS` runners. +* Cache full Nix store into a single cache. * Collect garbage in the store before saving. * Merge caches produced by several jobs. -* After saving a new cache, remove old caches by creation or last access time. +* Purge old caches by their creation or last access time. +* Can be used instead of `actions/cache` if you set `nix: false` (see [Inputs](#inputs)). ## Approach 1. The [nix-quick-install-action](https://github.com/nixbuild/nix-quick-install-action) action makes `/nix/store` owned by an unpriviliged user. -1. `cache-nix-action` restores `/nix`. - * When there's a cache hit, restoring `/nix/store` from a cache is faster than downloading multiple paths from binary caches. - * You can compare run times of jobs with and without store caching in [Actions](https://github.com/nix-community/cache-nix-action/actions/workflows/ci.yaml). - * Open a run and click on the time under `Total duration`. -1. Optionally, `cache-nix-action` purges old caches. - * As Nix (flake) inputs may change, it's necessary to use fresher caches. - * Caches can be purged by `created` or `last accessed` time (see [Configuration](#configuration)). +1. `cache-nix-action` tries to restore caches. + +1. Other job steps run. + +1. Optionally, `cache-nix-action` purges old caches by `created` or `last_accessed` time. 1. Optionally, `cache-nix-action` collects garbage in the Nix store (see [Garbage Collection](#garbage-collection)). - * The store may contain useless paths from previous runs. - * This action allows to limit nix store size (see [Configuration](#configuration)). -1. `cache-nix-action` saves a new cache when there's no cache hit. - * Limitations: - * Saving a cache takes time. - * There may be no cache hit after an old matching cache was purged. +1. `cache-nix-action` saves a new cache when there's no cache hit after purging old caches. + +## Comparison with alternative approaches + +See [Caching Approaches](#caching-approaches). ## Limitations -* `GitHub` allows only 10GB of caches and then removes the least recently used entries (see its [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy)). - * Can be overcome by merging similar caches (see [Merge caches](#merge-caches)) -* `cache-nix-action` restores and saves the whole `/nix` directory. +* Always caches `/nix`, `~/.cache/nix`, `~root/.cache/nix` paths as suggested [here](https://github.com/divnix/nix-cache-action/blob/b14ec98ae694c754f57f8619ea21b6ab44ccf6e7/action.yml#L7). +* `GitHub` allows only `10GB` of caches and then removes the least recently used entries (see its [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy)). Workarounds: + * [Purge old caches](#purge-old-caches) + * [Merge caches](#merge-caches) * `cache-nix-action` requires `nix-quick-install-action` (see [Approach](#approach)). -* Store size is limited by a runner storage size ([link](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)). -* Caches are isolated between branches ([link](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache)). +* Supports only `Linux` and `macOS` runners. +* Nix store size is limited by a runner storage size ([link](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)). Workaround: + * The [jlumbroso/free-disk-space](https://github.com/jlumbroso/free-disk-space) action frees `~30GB` of disk space in several minutes. +* Caches are isolated between refs ([link](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache)). Workaround: + * Provide caches on the default and base branches (for PRs). * When restoring, `cache-nix-action` writes cached Nix store paths into a read-only `/nix/store` of a runner. Some of these paths may already be present, so the action will show `File exists` errors and a warning that it failed to restore. It's OK. -* It may be necessary to purge old caches (see [Purge old caches](#purge-old-caches)). - -See alternative [caching approaches](#caching-approaches). - -See how you can [contribute](#contribute). - -## Configuration - -See [action.yaml](action.yml), [restore/action.yml](restore/action.yml), [save/action.yml](save/action.yml). - -This action inherits some [inputs](#inputs) and [outputs](#outputs) of `actions/cache`. - -### New inputs +* For `purge: true`, a workflow requires the permission `action: write` and the `token` must have a `repo` scope ([link](https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-github-actions-caches-for-a-repository-using-a-cache-key)). +* Purges caches scoped to the current [GITHUB_REF](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables). +* Purge time is calculated relative to the start of the `Save` phase of this action. +* Purges caches by keys without considering their versions (see [Cache version](#cache-version)). -| `name` | `description` | `required` | `default` | `needs` | -| ------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------- | --------- | ---------------------- | -| `gc-macos` | When `true`, enables on `macOS` runners Nix store garbage collection before saving a cache. | `false` | `false` | `gc-macos: true` | -| `gc-max-store-size-macos` | Maximum Nix store size in bytes on `macOS` runners. | `false` | | | -| `gc-linux` | When `true`, enables on `Linux` runners Nix store garbage collection before saving a cache. | `false` | `false` | | -| `gc-max-store-size-linux` | Maximum Nix store size in bytes on `Linux` runners. | `false` | | `gc-linux: true` | -| `purge` | When `true`, purge old caches before saving a new cache with a `key`. | `false` | `false` | | -| `purge-keys` | A newline-separated list of cache key prefixes used to purge caches. An empty list is equivalent to the `key` input. | `false` | `''` | `purge: true` | -| `purge-accessed` | When `true`, purge caches by their last access time. | `false` | `false` | `purge: true` | -| `purge-accessed-max-age` | Purge caches last accessed more than this number of seconds ago. | `false` | `604800` | `purge-accessed: true` | -| `purge-created` | When `true`, delete caches by their creation time. | `false` | `true` | `purge: true` | -| `purge-created-max-age` | Purge caches created more than this number of seconds ago. | `false` | `604800` | `purge-created: true`. | -| `restore-key-hit` | When true, if a cache key matching `restore-keys` exists, it counts as a cache hit. Thus, a job won't save a new cache. | `false` | `false` | | -| `extra-restore-keys` | A newline-separated list of key prefixes used for restoring multiple caches. | `false` | `''` | | +## Example steps -Note: - -* `cache-nix-action` purges only caches specific to a branch that has triggered a workflow. -* `*-max-age` is relative to the time before saving a new cache. - -### Removed inputs - -The `cache-nix-action` doesn't provide the `path` input from the original [inputs](#inputs) of `actions/cache` due to [limitations](#limitations). -Instead, the `cache-nix-action` caches `/nix`, `~/.cache/nix`, `~root/.cache/nix` paths by default as suggested [here](https://github.com/divnix/nix-cache-action/blob/b14ec98ae694c754f57f8619ea21b6ab44ccf6e7/action.yml#L7). - -## Usage - -* This action **must** be used with [nix-quick-install-action](https://github.com/nixbuild/nix-quick-install-action). -* Maximum Nix store size on `Linux` runners will be `~1GB` due to `gc-max-store-size-linux: 1000000000`. - * If the store has a larger size, it will be garbage collected to reach this limit (See [Garbage collection parameters](#garbage-collection-parameters)). +* Due to `gc-max-store-size-linux: 1073741824`, Nix store size on `Linux` runners will be reduced to `1GB` before trying to save a new cache. + * If the store has a larger size, it will be garbage collected to reach the (See [Garbage collection parameters](#garbage-collection-parameters)). * The `cache-nix-action` will print the Nix store size in the `Post` phase, so you can choose an optimal store size to avoid garbage collection. -* On `macOS` runners, Nix store won't be garbage collected since `gc-macos: true` isn't set. -* The `cache-nix-action` will find caches with a key prefix `cache-${{ matrix.os }}-`. - Among these caches, the `cache-nix-action` will delete caches created more than `42` seconds ago +* On `macOS` runners, Nix store won't be garbage collected since `gc-max-store-size-macos` isn't set to a number. +* Before trying to save a new cache, the `cache-nix-action` will search for caches with a key prefix `cache-${{ matrix.os }}-`. + Among these caches, due to `purge-created: 42` the `cache-nix-action` will delete caches created more than `42` seconds ago. ```yaml -- uses: nixbuild/nix-quick-install-action@v25 +- uses: nixbuild/nix-quick-install-action@v26 with: nix_conf: | substituters = https://cache.nixos.org/ https://nix-community.cachix.org @@ -101,25 +69,34 @@ Instead, the `cache-nix-action` caches `/nix`, `~/.cache/nix`, `~root/.cache/nix keep-outputs = true - name: Restore and cache Nix store - uses: nix-community/cache-nix-action@v3 + uses: nix-community/cache-nix-action@v5 with: - key: cache-${{ matrix.os }}-${{ hashFiles('**/*.nix') }} - restore-keys: | - cache-${{ matrix.os }}- + primary-key: cache-nix-${{ matrix.os }}-${{ hashFiles('**/*.nix') }} + restore-prefixes-first-match: cache-nix-${{ matrix.os }}- - gc-linux: true - gc-max-store-size-linux: 1000000000 + gc-max-store-size-linux: 1073741824 - purge-caches: true - purge-key: cache-${{ matrix.os }}- - purge-created: true - purge-created-max-age: 42 + purge: true + purge-prefixes: cache-${{ matrix.os }}- + purge-created: 42 ``` ### Example workflow See [ci.yaml](.github/workflows/ci.yaml). +## Configuration + +See [action.yml](action.yml), [restore/action.yml](restore/action.yml), [save/action.yml](save/action.yml). + +### Inputs + +
namedescriptiondefaultrequired

primary-key

  • When a non-empty string, the action uses this key for restoring and saving a cache.
  • Otherwise, the action fails.

true

restore-prefixes-first-match

  • When a newline-separated non-empty list of non-empty key prefixes, when there's a miss on the primary-key, the action searches in this list for the first prefix for which there exists a cache with a matching key and the action tries to restore that cache.
  • Otherwise, this input has no effect.

''

false

restore-prefixes-all-matches

  • When a newline-separated non-empty list of non-empty key prefixes, the action tries to restore all caches whose keys match these prefixes.
  • Otherwise, this input has no effect.

''

false

skip-restore-on-hit-primary-key

  • Can have an effect only when restore-prefixes-first-match has no effect.
  • When true, when there's a hit on the primary-key, the action doesn't restore caches.
  • Otherwise, this input has no effect.

false

false

fail-on

  • Input form: <key type>.<result>.
  • <key type> options: primary-key, first-match.
  • <result> options: miss, not-restored.
  • When the input satisfies the input form, when the event described in the input happens, the action fails.
  • Example:
    • Input: primary-key.not-restored.
    • Event: a cache could not be restored via the primary-key.
  • Otherwise, this input has no effect.

''

false

nix

  • When true, the action can do Nix-specific things.
  • Otherwise, the action doesn't do them.

true

false

save

  • When true, the action can save a cache with the primary-key.
  • Otherwise, the action can't save a cache.

true

false

paths

  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for restoring and saving caches.
  • Otherwise, the action uses default paths for restoring and saving caches.

''

false

paths-macos

  • Overrides paths.
  • Can have an effect only when on a macOS runner.
  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for restoring and saving caches.
  • Otherwise, the action uses default paths for restoring and saving caches.

''

false

paths-linux

  • Overrides paths.
  • Can have an effect only when on a Linux runner.
  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for restoring and saving caches.
  • Otherwise, the action uses default paths for restoring and saving caches.

''

false

gc-max-store-size

  • Can have an effect only when nix: true, save: true.
  • When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.
  • Otherwise, this input has no effect.

''

false

gc-max-store-size-macos

  • Can have an effect only when nix: true, save: true.
  • Overrides gc-max-store-size.
  • Can have an effect only when on a macOS runner.
  • When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.
  • Otherwise, this input has no effect.

''

false

gc-max-store-size-linux

  • Can have an effect only when nix: true, save: true.
  • Overrides gc-max-store-size.
  • Can have an effect only when on a Linux runner.
  • When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.
  • Otherwise, this input has no effect.

''

false

purge

  • When true, the action purges (possibly zero) caches.
  • Otherwise, this input has no effect.

false

false

purge-overwrite

  • Can have an effect only when purge: true.
  • When always, the action always purges cache with the primary-key.
  • When never, the action never purges cache with the primary-key.
  • Otherwise, the action purges old caches using purging criteria.

''

false

purge-prefixes

  • Can have an effect only when purge: true.
  • When a newline-separated non-empty list of non-empty cache key prefixes, the action collects all cache keys that match these prefixes.
  • Otherwise, this input has no effect.

''

false

purge-last-accessed

  • Can have an effect only when purge: true.
  • When a number, the action selects for purging caches last accessed more than this number of seconds ago relative to the start of the Save phase.
  • Otherwise, this input has no effect.

''

false

purge-created

  • Can have an effect only when purge: true.
  • When a number, the action selects for purging caches created more than this number of seconds ago relative to the start of the Save phase.
  • Otherwise, this input has no effect.

''

false

upload-chunk-size

  • When a number, the action uses it as the chunk size (in bytes) to split up large files during upload.
  • Otherwise, the action uses the default value 33554432 (32MB).

''

false

token

The action uses it to communicate with GitHub API.

${{ github.token }}

false

+ +### Outputs + +
namedescription

primary-key

  • A string.
  • The primary-key.

hit

  • A boolean value.
  • true when hit-primary-key is true or hit-first-match is true.
  • false otherwise.

hit-primary-key

  • A boolean value.
  • true when there was a hit on the primary-key.
  • false otherwise.

hit-first-match

  • A boolean value.
  • true when there was a hit on a key matching restore-prefixes-first-match.
  • false otherwise.

restored-key

  • A string.
  • The key of a cache restored via the primary-key or via the restore-prefixes-first-match.
  • An empty string otherwise.

restored-keys

  • A possibly empty array of strings (JSON).
  • Keys of restored caches.
  • Example: ["key1", "key2"].
+ ### Troubleshooting * Use [action-tmate](https://github.com/mxschmitt/action-tmate) to debug on a runner via SSH. @@ -134,7 +111,7 @@ There are alternative approaches to garbage collection (see [Garbage collection] ### Purge old caches -The `cache-nix-action` allows to delete old caches after saving a new cache (see `purge-*` inputs in [New inputs](#new-inputs) and `compare-run-times` in [Example workflow](#example-workflow)). +The `cache-nix-action` allows to delete old caches after saving a new cache (see `purge-*` inputs in [Inputs](#inputs) and `compare-run-times` in [Example workflow](#example-workflow)). The [purge-cache](https://github.com/MyAlbum/purge-cache) action allows to remove caches based on their `last accessed` or `created` time without branch limitations. @@ -142,24 +119,20 @@ Alternatively, you can use the [GitHub Actions Cache API](https://docs.github.co ### Merge caches -`GitHub` evicts LRU caches when their total size exceeds `10GB` (see [Limitations](#limitations)). +`GitHub` evicts least recently used caches when their total size exceeds `10GB` (see [Limitations](#limitations)). -If you have multiple similar caches, you can merge them into a single cache and store just it to save space. +If you have multiple similar caches produced on runners with **the same OS** (`Linux` or `macOS`), you can merge them into a single cache and store just it to save space. In short: -1. Matrix jobs produce similar caches. +1. Matrix jobs produce similar individual caches. 1. The next job restores all of these individual caches, saves a common cache, and purges individual caches. 1. On subsequent runs, matrix jobs use the common cache. See the `make-similar-caches` and `merge-similar-caches` jobs in the [example workflow](#example-workflow). -Pros: if `N` individual caches are very similar, a common cache will take approximately `N` times less space. -Cons: if caches aren't very similar, run time may increase due to a bigger common cache. - -### Get more space on a runner - -The [jlumbroso/free-disk-space](https://github.com/jlumbroso/free-disk-space) action frees `~30GB` of disk space in several minutes. +**Pros**: if `N` individual caches are very similar, a common cache will take approximately `N` times less space. +**Cons**: if caches aren't very similar, run time may increase due to a bigger common cache. ## Caching approaches @@ -175,26 +148,29 @@ These distances affect the restore and save speed. #### cache-nix-action -Pros: +**Pros**: * Free. * Uses `GitHub Actions Cache` and works fast. * Easy to set up. * Allows to save a store of at most a given size (see [Garbage collection parameters](#garbage-collection-parameters)). * Allows to save outputs from garbage collection (see [Garbage collection](#garbage-collection)). +* When there's a cache hit, restoring from a GitHub Actions cache can be faster than downloading multiple paths from binary caches. + * You can compare run times of jobs with and without store caching in [Actions](https://github.com/nix-community/cache-nix-action/actions/workflows/ci.yaml). + * Open a run and click on the time under `Total duration`. -Cons: see [Limitations](#limitations) +**Cons**: see [Limitations](#limitations) #### magic-nix-cache-action -Pros ([link](https://github.com/DeterminateSystems/magic-nix-cache#why-use-the-magic-nix-cache)): +**Pros** ([link](https://github.com/DeterminateSystems/magic-nix-cache#why-use-the-magic-nix-cache)): * Free. * Uses `GitHub Actions Cache` and works fast. * Easy to set up. * Restores and saves paths selectively. -Cons: +**Cons**: * Collects telemetry ([link](https://github.com/DeterminateSystems/magic-nix-cache)) * May trigger rate limit errors ([link](https://github.com/DeterminateSystems/magic-nix-cache#usage-notes)). @@ -208,11 +184,11 @@ If used with [nix-quick-install-action](https://github.com/nixbuild/nix-quick-in If used with [install-nix-action](https://github.com/cachix/install-nix-action) and a [chroot local store](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-help-stores.html#local-store): -Pros: +**Pros**: * Quick restore and save `/tmp/nix`. -Cons: +**Cons**: * Slow [nix copy](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-copy.html) from `/tmp/nix` to `/nix/store`. @@ -225,14 +201,14 @@ See [binary cache](https://nixos.org/manual/nix/unstable/glossary.html#gloss-bin * [cachix](https://www.cachix.org/) * [attic](https://github.com/zhaofengli/attic) -Pros: +**Pros**: * Restore and save paths selectively. -* Provide LRU garbage collection strategies ([cachix](https://docs.cachix.org/garbage-collection?highlight=garbage), [attic](https://github.com/zhaofengli/attic#goals)). +* Provide least recently used garbage collection strategies ([cachix](https://docs.cachix.org/garbage-collection?highlight=garbage), [attic](https://github.com/zhaofengli/attic#goals)). * Don't cache paths available from the NixOS cache ([cachix](https://docs.cachix.org/garbage-collection?highlight=upstream)). * Allow to share paths between projects ([cachix](https://docs.cachix.org/getting-started#using-binaries-with-nix)). -Cons: +**Cons**: * Have limited free storage ([cachix](https://www.cachix.org/pricing) gives 5GB for open-source projects). * Need good bandwidth for receiving and pushing paths over the Internet. @@ -243,7 +219,7 @@ Cons: When restoring a Nix store from a cache, the store may contain old unnecessary paths. These paths should be removed sometimes to limit cache size and ensure the fastest restore/save steps. -### GC approach 1 +### Garbage collection approach 1 Produce a cache once, use it multiple times. Don't collect garbage. @@ -258,7 +234,7 @@ Disadvantages: * The job at the second run restores the cache, produces a path `B`, and saves a cache. The cache has both `A` and `B`. * etc. -### GC approach 2 +### Garbage collection approach 2 Collect garbage before saving a cache. @@ -270,7 +246,7 @@ Disadvantages: * No standard way to gc only old paths. -### Save a path from GC +### Save a path from garbage collection * Use `nix profile install` to save installables from garbage collection. * Get store paths of `inputs` via `nix flake archive` (see [comment](https://github.com/NixOS/nix/issues/4250#issuecomment-1146878407)). @@ -280,22 +256,28 @@ Disadvantages: ### Garbage collection approaches -* Use [nix-heuristic-gc](https://github.com/risicle/nix-heuristic-gc) for cache eviction via `atime` -* gc via gc roots [nix-cache-cut](https://github.com/astro/nix-cache-cut) -* gc based on time [cache-gc](https://github.com/lheckemann/cache-gc) +* Use [nix-heuristic-gc](https://github.com/risicle/nix-heuristic-gc) for cache eviction via `atime`. +* gc via gc roots [nix-cache-cut](https://github.com/astro/nix-cache-cut). +* gc based on time [cache-gc](https://github.com/lheckemann/cache-gc). ## Contribute -* Improve README -* Report errors, suggest improvements in issues +* Improve README. +* Report errors, suggest improvements in issues. * Upgrade code. * Read about [JavaScript actions](https://docs.github.com/en/actions/creating-actions/about-custom-actions?learn=create_actions&learnProduct=actions#javascript-actions) * See main files: * [restoreImpl.ts](./src/restoreImpl.ts) * [saveImpl.ts](./src/saveImpl.ts) - * [acton.yml](./action.yml) - * [save/action.yml](./save/action.yml) - * [restore/action.yml](./restore/action.yml) +* Upgrade docs. + * Edit [action.nix](./action.nix). + * Update `action.yml`-s and `README.md`-s: + + ```console + nix run .#write + ``` + +* Update [actions-toolkit](./actions-toolkit). It was added via `git subtree`. See [tutorial](https://www.atlassian.com/git/tutorials/git-subtree). # Cache action @@ -329,7 +311,6 @@ See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/ac * Fix zstd not working for windows on gnu tar in issues. * Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 10 minutes. * New actions are available for granular control over caches - [restore](restore/action.yml) and [save](save/action.yml). -* Support cross-os caching as an opt-in feature. See [Cross OS caching](./tips-and-workarounds.md#cross-os-cache) for more info. * Added option to fail job on cache miss. See [Exit workflow on cache miss](./restore/README.md#exit-workflow-on-cache-miss) for more info. * Fix zstd not being used after zstd version upgrade to 1.5.4 on hosted runners * Added option to lookup cache without downloading it. @@ -347,25 +328,10 @@ If you are using this inside a container, a POSIX-compliant `tar` needs to be in If you are using a `self-hosted` Windows runner, `GNU tar` and `zstd` are required for [Cross-OS caching](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cross-os-cache) to work. They are also recommended to be installed in general so the performance is on par with `hosted` Windows runners. -### Inputs - -* `key` - An explicit key for a cache entry. See [creating a cache key](#creating-a-cache-key). -* `path` - A list of files, directories, and wildcard patterns to cache and restore. See [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns. -* `restore-keys` - An ordered list of prefix-matched keys to use for restoring stale cache if no cache hit occurred for key. -* `fail-on-cache-miss` - Fail the workflow if cache entry is not found. Default: `false` - #### Environment Variables * `SEGMENT_DOWNLOAD_TIMEOUT_MINS` - Segment download timeout (in minutes, default `10`) to abort download of the segment if not completed in the defined number of minutes. [Read more](https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout) -### Outputs - -* `cache-hit` - A boolean value to indicate an exact match was found for the key. - - > **Note** `cache-hit` will only be set to `true` when a cache hit occurs for the exact `key` match. For a partial key match via `restore-keys` or a cache miss, it will be set to `false`. - -See [Skipping steps based on cache-hit](#skipping-steps-based-on-cache-hit) for info on using this output - ### Cache scopes The cache is scoped to the key, [version](#cache-version), and branch. The default branch cache is available to other branches. @@ -604,17 +570,8 @@ There are a number of community practices/workarounds to fulfill specific requir * [Cache segment restore timeout](./tips-and-workarounds.md#cache-segment-restore-timeout) * [Update a cache](./tips-and-workarounds.md#update-a-cache) * [Use cache across feature branches](./tips-and-workarounds.md#use-cache-across-feature-branches) -* [Cross OS cache](./tips-and-workarounds.md#cross-os-cache) * [Force deletion of caches overriding default cache eviction policy](./tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy) -### Windows environment variables - -Please note that Windows environment variables (like `%LocalAppData%`) will NOT be expanded by this action. Instead, prefer using `~` in your paths which will expand to the HOME directory. For example, instead of `%LocalAppData%`, use `~\AppData\Local`. For a list of supported default environment variables, see the [Learn GitHub Actions: Variables](https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables) page. - -## Contributing - -We would love for you to contribute to `actions/cache`. Pull requests are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for more information. - ## License The scripts and documentation in this project are released under the [MIT License](LICENSE) diff --git a/RELEASES.md b/RELEASES.md index d4016e96..5efe3013 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -107,3 +107,7 @@ ### 3.3.1 - Reduced segment size to 128MB and segment timeout to 10 minutes to fail fast in case the cache download is stuck. + +### 3.3.2 + +- Fixes bug with Azure SDK causing blob downloads to get stuck. diff --git a/__tests__/actionUtils.test.ts b/__tests__/actionUtils.test.ts deleted file mode 100644 index f98a76e3..00000000 --- a/__tests__/actionUtils.test.ts +++ /dev/null @@ -1,233 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, RefKey } from "../src/constants"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("@actions/core"); -jest.mock("@actions/cache"); - -beforeAll(() => { - jest.spyOn(core, "getInput").mockImplementation((name, options) => { - return jest.requireActual("@actions/core").getInput(name, options); - }); -}); - -afterEach(() => { - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("isGhes returns true if server url is not github.com", () => { - try { - process.env["GITHUB_SERVER_URL"] = "http://example.com"; - expect(actionUtils.isGhes()).toBe(true); - } finally { - process.env["GITHUB_SERVER_URL"] = undefined; - } -}); - -test("isGhes returns false when server url is github.com", () => { - try { - process.env["GITHUB_SERVER_URL"] = "http://github.com"; - expect(actionUtils.isGhes()).toBe(false); - } finally { - process.env["GITHUB_SERVER_URL"] = undefined; - } -}); - -test("isExactKeyMatch with undefined cache key returns false", () => { - const key = "linux-rust"; - const cacheKey = undefined; - - expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false); -}); - -test("isExactKeyMatch with empty cache key returns false", () => { - const key = "linux-rust"; - const cacheKey = ""; - - expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false); -}); - -test("isExactKeyMatch with different keys returns false", () => { - const key = "linux-rust"; - const cacheKey = "linux-"; - - expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false); -}); - -test("isExactKeyMatch with different key accents returns false", () => { - const key = "linux-áccent"; - const cacheKey = "linux-accent"; - - expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(false); -}); - -test("isExactKeyMatch with same key returns true", () => { - const key = "linux-rust"; - const cacheKey = "linux-rust"; - - expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(true); -}); - -test("isExactKeyMatch with same key and different casing returns true", () => { - const key = "linux-rust"; - const cacheKey = "LINUX-RUST"; - - expect(actionUtils.isExactKeyMatch(key, cacheKey)).toBe(true); -}); - -test("logWarning logs a message with a warning prefix", () => { - const message = "A warning occurred."; - - const infoMock = jest.spyOn(core, "info"); - - actionUtils.logWarning(message); - - expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`); -}); - -test("isValidEvent returns false for event that does not have a branch or tag", () => { - const event = "foo"; - process.env[Events.Key] = event; - - const isValidEvent = actionUtils.isValidEvent(); - - expect(isValidEvent).toBe(false); -}); - -test("isValidEvent returns true for event that has a ref", () => { - const event = Events.Push; - process.env[Events.Key] = event; - process.env[RefKey] = "ref/heads/feature"; - - const isValidEvent = actionUtils.isValidEvent(); - - expect(isValidEvent).toBe(true); -}); - -test("getInputAsArray returns empty array if not required and missing", () => { - expect(actionUtils.getInputAsArray("foo")).toEqual([]); -}); - -test("getInputAsArray throws error if required and missing", () => { - expect(() => - actionUtils.getInputAsArray("foo", { required: true }) - ).toThrowError(); -}); - -test("getInputAsArray handles single line correctly", () => { - testUtils.setInput("foo", "bar"); - expect(actionUtils.getInputAsArray("foo")).toEqual(["bar"]); -}); - -test("getInputAsArray handles multiple lines correctly", () => { - testUtils.setInput("foo", "bar\nbaz"); - expect(actionUtils.getInputAsArray("foo")).toEqual(["bar", "baz"]); -}); - -test("getInputAsArray handles different new lines correctly", () => { - testUtils.setInput("foo", "bar\r\nbaz"); - expect(actionUtils.getInputAsArray("foo")).toEqual(["bar", "baz"]); -}); - -test("getInputAsArray handles empty lines correctly", () => { - testUtils.setInput("foo", "\n\nbar\n\nbaz\n\n"); - expect(actionUtils.getInputAsArray("foo")).toEqual(["bar", "baz"]); -}); - -test("getInputAsArray removes spaces after ! at the beginning", () => { - testUtils.setInput( - "foo", - "! bar\n! baz\n! qux\n!quux\ncorge\ngrault! garply\n!\r\t waldo" - ); - expect(actionUtils.getInputAsArray("foo")).toEqual([ - "!bar", - "!baz", - "!qux", - "!quux", - "corge", - "grault! garply", - "!waldo" - ]); -}); - -test("getInputAsInt returns undefined if input not set", () => { - expect(actionUtils.getInputAsInt("undefined")).toBeUndefined(); -}); - -test("getInputAsInt returns value if input is valid", () => { - testUtils.setInput("foo", "8"); - expect(actionUtils.getInputAsInt("foo")).toBe(8); -}); - -test("getInputAsInt returns undefined if input is invalid or NaN", () => { - testUtils.setInput("foo", "bar"); - expect(actionUtils.getInputAsInt("foo")).toBeUndefined(); -}); - -test("getInputAsInt throws if required and value missing", () => { - expect(() => - actionUtils.getInputAsInt("undefined", { required: true }) - ).toThrowError(); -}); - -test("getInputAsBool returns false if input not set", () => { - expect(actionUtils.getInputAsBool("undefined")).toBe(false); -}); - -test("getInputAsBool returns value if input is valid", () => { - testUtils.setInput("foo", "true"); - expect(actionUtils.getInputAsBool("foo")).toBe(true); -}); - -test("getInputAsBool returns false if input is invalid or NaN", () => { - testUtils.setInput("foo", "bar"); - expect(actionUtils.getInputAsBool("foo")).toBe(false); -}); - -test("getInputAsBool throws if required and value missing", () => { - expect(() => - actionUtils.getInputAsBool("undefined2", { required: true }) - ).toThrowError(); -}); - -test("isCacheFeatureAvailable for ac enabled", () => { - jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => true); - - expect(actionUtils.isCacheFeatureAvailable()).toBe(true); -}); - -test("isCacheFeatureAvailable for ac disabled on GHES", () => { - jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => false); - - const message = `Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. -Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`; - const infoMock = jest.spyOn(core, "info"); - - try { - process.env["GITHUB_SERVER_URL"] = "http://example.com"; - expect(actionUtils.isCacheFeatureAvailable()).toBe(false); - expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`); - } finally { - delete process.env["GITHUB_SERVER_URL"]; - } -}); - -test("isCacheFeatureAvailable for ac disabled on dotcom", () => { - jest.spyOn(cache, "isFeatureAvailable").mockImplementation(() => false); - - const message = - "An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions."; - const infoMock = jest.spyOn(core, "info"); - - try { - process.env["GITHUB_SERVER_URL"] = "http://github.com"; - expect(actionUtils.isCacheFeatureAvailable()).toBe(false); - expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`); - } finally { - delete process.env["GITHUB_SERVER_URL"]; - } -}); diff --git a/__tests__/restore.test.ts b/__tests__/restore.test.ts deleted file mode 100644 index c51c293b..00000000 --- a/__tests__/restore.test.ts +++ /dev/null @@ -1,358 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, RefKey } from "../src/constants"; -import run from "../src/restore"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("../src/utils/actionUtils"); - -beforeAll(() => { - jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation( - (key, cacheResult) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isExactKeyMatch(key, cacheResult); - } - ); - - jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isValidEvent(); - }); - - jest.spyOn(actionUtils, "getInputAsArray").mockImplementation( - (name, options) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.getInputAsArray(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsBool").mockImplementation( - (name, options) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.getInputAsBool(name, options); - } - ); -}); - -beforeEach(() => { - jest.restoreAllMocks(); - process.env[Events.Key] = Events.Push; - process.env[RefKey] = "refs/heads/feature-branch"; - - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => true - ); -}); - -afterEach(() => { - testUtils.clearInputs(); - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("restore with no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledTimes(1); - - expect(failedMock).toHaveBeenCalledTimes(0); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}` - ); -}); - -test("restore with restore keys and no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledTimes(1); - - expect(failedMock).toHaveBeenCalledTimes(0); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}, ${restoreKey}` - ); -}); - -test("restore with cache found for key", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(key); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key); - expect(stateMock).toHaveBeenCalledTimes(2); - - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true"); - - expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore with cache found for restore key", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(restoreKey); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey); - expect(stateMock).toHaveBeenCalledTimes(2); - - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false"); - expect(infoMock).toHaveBeenCalledWith( - `Cache restored from key: ${restoreKey}` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("Fail restore when fail on cache miss is enabled and primary + restore keys not found", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - failOnCacheMiss: true - }); - - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(0); - - expect(failedMock).toHaveBeenCalledWith( - `Failed to restore cache entry. Exiting as fail-on-cache-miss is set. Input key: ${key}` - ); - expect(failedMock).toHaveBeenCalledTimes(1); -}); - -test("restore when fail on cache miss is enabled and primary key doesn't match restored key", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - failOnCacheMiss: true - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(restoreKey); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", restoreKey); - expect(stateMock).toHaveBeenCalledTimes(2); - - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false"); - - expect(infoMock).toHaveBeenCalledWith( - `Cache restored from key: ${restoreKey}` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore with fail on cache miss disabled and no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - failOnCacheMiss: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledTimes(1); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}, ${restoreKey}` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); diff --git a/__tests__/restoreImpl.test.ts b/__tests__/restoreImpl.test.ts deleted file mode 100644 index d6f13ba1..00000000 --- a/__tests__/restoreImpl.test.ts +++ /dev/null @@ -1,451 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, Inputs, RefKey } from "../src/constants"; -import run from "../src/restoreImpl"; -import { StateProvider } from "../src/stateProvider"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("../src/utils/actionUtils"); - -beforeAll(() => { - jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation( - (key, cacheResult) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isExactKeyMatch(key, cacheResult); - } - ); - - jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isValidEvent(); - }); - - jest.spyOn(actionUtils, "getInputAsArray").mockImplementation( - (name, options) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.getInputAsArray(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsBool").mockImplementation( - (name, options) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.getInputAsBool(name, options); - } - ); -}); - -beforeEach(() => { - jest.restoreAllMocks(); - process.env[Events.Key] = Events.Push; - process.env[RefKey] = "refs/heads/feature-branch"; - - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => true - ); -}); - -afterEach(() => { - testUtils.clearInputs(); - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("restore with invalid event outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - const invalidEvent = "commit_comment"; - process.env[Events.Key] = invalidEvent; - delete process.env[RefKey]; - await run(new StateProvider()); - expect(logWarningMock).toHaveBeenCalledWith( - `Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore without AC available should no-op", async () => { - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => false - ); - - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(0); - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false"); -}); - -test("restore on GHES without AC available should no-op", async () => { - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => false - ); - - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(0); - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false"); -}); - -test("restore on GHES with AC available ", async () => { - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true); - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(key); - }); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true"); - - expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore with no path should fail", async () => { - const failedMock = jest.spyOn(core, "setFailed"); - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - await run(new StateProvider()); - expect(restoreCacheMock).toHaveBeenCalledTimes(0); - // this input isn't necessary for restore b/c tarball contains entries relative to workspace - expect(failedMock).not.toHaveBeenCalledWith( - "Input required and not supplied: path" - ); -}); - -test("restore with no key", async () => { - testUtils.setInput(Inputs.Path, "node_modules"); - const failedMock = jest.spyOn(core, "setFailed"); - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - await run(new StateProvider()); - expect(restoreCacheMock).toHaveBeenCalledTimes(0); - expect(failedMock).toHaveBeenCalledWith( - "Input required and not supplied: key" - ); -}); - -test("restore with too many keys should fail", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKeys = [...Array(20).keys()].map(x => x.toString()); - testUtils.setInputs({ - path: path, - key, - restoreKeys, - enableCrossOsArchive: false - }); - const failedMock = jest.spyOn(core, "setFailed"); - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - await run(new StateProvider()); - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - restoreKeys, - { - lookupOnly: false - }, - false - ); - expect(failedMock).toHaveBeenCalledWith( - `Key Validation Error: Keys are limited to a maximum of 10.` - ); -}); - -test("restore with large key should fail", async () => { - const path = "node_modules"; - const key = "foo".repeat(512); // Over the 512 character limit - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - const failedMock = jest.spyOn(core, "setFailed"); - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - await run(new StateProvider()); - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - expect(failedMock).toHaveBeenCalledWith( - `Key Validation Error: ${key} cannot be larger than 512 characters.` - ); -}); - -test("restore with invalid key should fail", async () => { - const path = "node_modules"; - const key = "comma,comma"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - const failedMock = jest.spyOn(core, "setFailed"); - const restoreCacheMock = jest.spyOn(cache, "restoreCache"); - await run(new StateProvider()); - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - expect(failedMock).toHaveBeenCalledWith( - `Key Validation Error: ${key} cannot contain commas.` - ); -}); - -test("restore with no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(failedMock).toHaveBeenCalledTimes(0); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}` - ); -}); - -test("restore with restore keys and no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(failedMock).toHaveBeenCalledTimes(0); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}, ${restoreKey}` - ); -}); - -test("restore with cache found for key", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(key); - }); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true"); - - expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore with cache found for restore key", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(restoreKey); - }); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "false"); - expect(infoMock).toHaveBeenCalledWith( - `Cache restored from key: ${restoreKey}` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore with lookup-only set", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - lookupOnly: true - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const stateMock = jest.spyOn(core, "saveState"); - const setCacheHitOutputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(key); - }); - - await run(new StateProvider()); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: true - }, - false - ); - - expect(stateMock).toHaveBeenCalledWith("CACHE_KEY", key); - expect(stateMock).toHaveBeenCalledWith("CACHE_RESULT", key); - expect(stateMock).toHaveBeenCalledTimes(2); - - expect(setCacheHitOutputMock).toHaveBeenCalledTimes(1); - expect(setCacheHitOutputMock).toHaveBeenCalledWith("cache-hit", "true"); - - expect(infoMock).toHaveBeenCalledWith( - `Cache found and can be restored from key: ${key}` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); diff --git a/__tests__/restoreOnly.test.ts b/__tests__/restoreOnly.test.ts deleted file mode 100644 index 800c2e1c..00000000 --- a/__tests__/restoreOnly.test.ts +++ /dev/null @@ -1,222 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, RefKey } from "../src/constants"; -import run from "../src/restoreOnly"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("../src/utils/actionUtils"); - -beforeAll(() => { - jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation( - (key, cacheResult) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isExactKeyMatch(key, cacheResult); - } - ); - - jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isValidEvent(); - }); - - jest.spyOn(actionUtils, "getInputAsArray").mockImplementation( - (name, options) => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.getInputAsArray(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsBool").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsBool(name, options); - } - ); -}); - -beforeEach(() => { - jest.restoreAllMocks(); - process.env[Events.Key] = Events.Push; - process.env[RefKey] = "refs/heads/feature-branch"; - - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => true - ); -}); - -afterEach(() => { - testUtils.clearInputs(); - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("restore with no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const outputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key); - expect(outputMock).toHaveBeenCalledTimes(1); - expect(failedMock).toHaveBeenCalledTimes(0); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}` - ); -}); - -test("restore with restore keys and no cache found", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const outputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(undefined); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key); - expect(failedMock).toHaveBeenCalledTimes(0); - - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}, ${restoreKey}` - ); -}); - -test("restore with cache found for key", async () => { - const path = "node_modules"; - const key = "node-test"; - testUtils.setInputs({ - path: path, - key, - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const outputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(key); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [], - { - lookupOnly: false - }, - false - ); - - expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key); - expect(outputMock).toHaveBeenCalledWith("cache-hit", "true"); - expect(outputMock).toHaveBeenCalledWith("cache-matched-key", key); - - expect(outputMock).toHaveBeenCalledTimes(3); - - expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("restore with cache found for restore key", async () => { - const path = "node_modules"; - const key = "node-test"; - const restoreKey = "node-"; - testUtils.setInputs({ - path: path, - key, - restoreKeys: [restoreKey], - enableCrossOsArchive: false - }); - - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - const outputMock = jest.spyOn(core, "setOutput"); - const restoreCacheMock = jest - .spyOn(cache, "restoreCache") - .mockImplementationOnce(() => { - return Promise.resolve(restoreKey); - }); - - await run(); - - expect(restoreCacheMock).toHaveBeenCalledTimes(1); - expect(restoreCacheMock).toHaveBeenCalledWith( - [path], - key, - [restoreKey], - { - lookupOnly: false - }, - false - ); - - expect(outputMock).toHaveBeenCalledWith("cache-primary-key", key); - expect(outputMock).toHaveBeenCalledWith("cache-hit", "false"); - expect(outputMock).toHaveBeenCalledWith("cache-matched-key", restoreKey); - - expect(outputMock).toHaveBeenCalledTimes(3); - - expect(infoMock).toHaveBeenCalledWith( - `Cache restored from key: ${restoreKey}` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); diff --git a/__tests__/save.test.ts b/__tests__/save.test.ts deleted file mode 100644 index 1eb9c7b8..00000000 --- a/__tests__/save.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, Inputs, RefKey } from "../src/constants"; -import run from "../src/save"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("@actions/core"); -jest.mock("@actions/cache"); -jest.mock("../src/utils/actionUtils"); - -beforeAll(() => { - jest.spyOn(core, "getInput").mockImplementation((name, options) => { - return jest.requireActual("@actions/core").getInput(name, options); - }); - - jest.spyOn(core, "getState").mockImplementation(name => { - return jest.requireActual("@actions/core").getState(name); - }); - - jest.spyOn(actionUtils, "getInputAsArray").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsArray(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsInt").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsInt(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsBool").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsBool(name, options); - } - ); - - jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation( - (key, cacheResult) => { - return jest - .requireActual("../src/utils/actionUtils") - .isExactKeyMatch(key, cacheResult); - } - ); - - jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isValidEvent(); - }); -}); - -beforeEach(() => { - process.env[Events.Key] = Events.Push; - process.env[RefKey] = "refs/heads/feature-branch"; - - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => true - ); -}); - -afterEach(() => { - testUtils.clearInputs(); - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("save with valid inputs uploads a cache", async () => { - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return primaryKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return savedCacheKey; - }); - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Path, inputPath); - testUtils.setInput(Inputs.UploadChunkSize, "4000000"); - - const cacheId = 4; - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - return Promise.resolve(cacheId); - }); - - await run(); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - { - uploadChunkSize: 4000000 - }, - false - ); - - expect(failedMock).toHaveBeenCalledTimes(0); -}); diff --git a/__tests__/saveImpl.test.ts b/__tests__/saveImpl.test.ts deleted file mode 100644 index 2a048b0d..00000000 --- a/__tests__/saveImpl.test.ts +++ /dev/null @@ -1,408 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, Inputs, RefKey } from "../src/constants"; -import run from "../src/saveImpl"; -import { StateProvider } from "../src/stateProvider"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("@actions/core"); -jest.mock("@actions/cache"); -jest.mock("../src/utils/actionUtils"); - -beforeAll(() => { - jest.spyOn(core, "getInput").mockImplementation((name, options) => { - return jest.requireActual("@actions/core").getInput(name, options); - }); - - jest.spyOn(actionUtils, "getInputAsArray").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsArray(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsInt").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsInt(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsBool").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsBool(name, options); - } - ); - - jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation( - (key, cacheResult) => { - return jest - .requireActual("../src/utils/actionUtils") - .isExactKeyMatch(key, cacheResult); - } - ); - - jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isValidEvent(); - }); -}); - -beforeEach(() => { - jest.restoreAllMocks(); - process.env[Events.Key] = Events.Push; - process.env[RefKey] = "refs/heads/feature-branch"; - - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => true - ); -}); - -afterEach(() => { - testUtils.clearInputs(); - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("save with invalid event outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - const invalidEvent = "commit_comment"; - process.env[Events.Key] = invalidEvent; - delete process.env[RefKey]; - await run(new StateProvider()); - expect(logWarningMock).toHaveBeenCalledWith( - `Event Validation Error: The event type ${invalidEvent} is not supported because it's not tied to a branch or tag ref.` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with no primary key in state outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - - const savedCacheKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return ""; - }) - // Cache Key State - .mockImplementationOnce(() => { - return savedCacheKey; - }); - const saveCacheMock = jest.spyOn(cache, "saveCache"); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(0); - expect(logWarningMock).toHaveBeenCalledWith(`Key is not specified.`); - expect(logWarningMock).toHaveBeenCalledTimes(1); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save without AC available should no-op", async () => { - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => false - ); - - const saveCacheMock = jest.spyOn(cache, "saveCache"); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(0); -}); - -test("save on ghes without AC available should no-op", async () => { - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => false - ); - - const saveCacheMock = jest.spyOn(cache, "saveCache"); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(0); -}); - -test("save on GHES with AC available", async () => { - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => true); - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Path, inputPath); - testUtils.setInput(Inputs.UploadChunkSize, "4000000"); - - const cacheId = 4; - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - return Promise.resolve(cacheId); - }); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - { - uploadChunkSize: 4000000 - }, - false - ); - - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with exact match returns early", async () => { - const infoMock = jest.spyOn(core, "info"); - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = primaryKey; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - const saveCacheMock = jest.spyOn(cache, "saveCache"); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(0); - expect(infoMock).toHaveBeenCalledWith( - `Cache hit occurred on the primary key ${primaryKey}, not saving cache.` - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with missing input outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - const saveCacheMock = jest.spyOn(cache, "saveCache"); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(0); - expect(logWarningMock).toHaveBeenCalledWith( - "Input required and not supplied: path" - ); - expect(logWarningMock).toHaveBeenCalledTimes(1); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with large cache outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Path, inputPath); - - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - throw new Error( - "Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache." - ); - }); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - expect.anything(), - false - ); - - expect(logWarningMock).toHaveBeenCalledTimes(1); - expect(logWarningMock).toHaveBeenCalledWith( - "Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache." - ); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with reserve cache failure outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Path, inputPath); - - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - const actualCache = jest.requireActual("@actions/cache"); - const error = new actualCache.ReserveCacheError( - `Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.` - ); - throw error; - }); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - expect.anything(), - false - ); - - expect(logWarningMock).toHaveBeenCalledWith( - `Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.` - ); - expect(logWarningMock).toHaveBeenCalledTimes(1); - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with server error outputs warning", async () => { - const logWarningMock = jest.spyOn(actionUtils, "logWarning"); - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Path, inputPath); - - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - throw new Error("HTTP Error Occurred"); - }); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - expect.anything(), - false - ); - - expect(logWarningMock).toHaveBeenCalledTimes(1); - expect(logWarningMock).toHaveBeenCalledWith("HTTP Error Occurred"); - - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save with valid inputs uploads a cache", async () => { - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - const savedCacheKey = "Linux-node-"; - - jest.spyOn(core, "getState") - // Cache Entry State - .mockImplementationOnce(() => { - return savedCacheKey; - }) - // Cache Key State - .mockImplementationOnce(() => { - return primaryKey; - }); - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Path, inputPath); - testUtils.setInput(Inputs.UploadChunkSize, "4000000"); - - const cacheId = 4; - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - return Promise.resolve(cacheId); - }); - - await run(new StateProvider()); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - { - uploadChunkSize: 4000000 - }, - false - ); - - expect(failedMock).toHaveBeenCalledTimes(0); -}); diff --git a/__tests__/saveOnly.test.ts b/__tests__/saveOnly.test.ts deleted file mode 100644 index 5a7f8b04..00000000 --- a/__tests__/saveOnly.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as cache from "@actions/cache"; -import * as core from "@actions/core"; - -import { Events, Inputs, RefKey } from "../src/constants"; -import run from "../src/saveOnly"; -import * as actionUtils from "../src/utils/actionUtils"; -import * as testUtils from "../src/utils/testUtils"; - -jest.mock("@actions/core"); -jest.mock("@actions/cache"); -jest.mock("../src/utils/actionUtils"); - -beforeAll(() => { - jest.spyOn(core, "getInput").mockImplementation((name, options) => { - return jest.requireActual("@actions/core").getInput(name, options); - }); - - jest.spyOn(core, "setOutput").mockImplementation((key, value) => { - return jest.requireActual("@actions/core").getInput(key, value); - }); - - jest.spyOn(actionUtils, "getInputAsArray").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsArray(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsInt").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsInt(name, options); - } - ); - - jest.spyOn(actionUtils, "getInputAsBool").mockImplementation( - (name, options) => { - return jest - .requireActual("../src/utils/actionUtils") - .getInputAsBool(name, options); - } - ); - - jest.spyOn(actionUtils, "isExactKeyMatch").mockImplementation( - (key, cacheResult) => { - return jest - .requireActual("../src/utils/actionUtils") - .isExactKeyMatch(key, cacheResult); - } - ); - - jest.spyOn(actionUtils, "isValidEvent").mockImplementation(() => { - const actualUtils = jest.requireActual("../src/utils/actionUtils"); - return actualUtils.isValidEvent(); - }); -}); - -beforeEach(() => { - process.env[Events.Key] = Events.Push; - process.env[RefKey] = "refs/heads/feature-branch"; - - jest.spyOn(actionUtils, "isGhes").mockImplementation(() => false); - jest.spyOn(actionUtils, "isCacheFeatureAvailable").mockImplementation( - () => true - ); -}); - -afterEach(() => { - testUtils.clearInputs(); - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("save with valid inputs uploads a cache", async () => { - const failedMock = jest.spyOn(core, "setFailed"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Key, primaryKey); - testUtils.setInput(Inputs.Path, inputPath); - testUtils.setInput(Inputs.UploadChunkSize, "4000000"); - - const cacheId = 4; - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - return Promise.resolve(cacheId); - }); - - await run(); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - { - uploadChunkSize: 4000000 - }, - false - ); - - expect(failedMock).toHaveBeenCalledTimes(0); -}); - -test("save failing logs the warning message", async () => { - const warningMock = jest.spyOn(core, "warning"); - - const primaryKey = "Linux-node-bb828da54c148048dd17899ba9fda624811cfb43"; - - const inputPath = "node_modules"; - testUtils.setInput(Inputs.Key, primaryKey); - testUtils.setInput(Inputs.Path, inputPath); - testUtils.setInput(Inputs.UploadChunkSize, "4000000"); - - const cacheId = -1; - const saveCacheMock = jest - .spyOn(cache, "saveCache") - .mockImplementationOnce(() => { - return Promise.resolve(cacheId); - }); - - await run(); - - expect(saveCacheMock).toHaveBeenCalledTimes(1); - expect(saveCacheMock).toHaveBeenCalledWith( - [inputPath], - primaryKey, - { - uploadChunkSize: 4000000 - }, - false - ); - - expect(warningMock).toHaveBeenCalledTimes(1); - expect(warningMock).toHaveBeenCalledWith("Cache save failed."); -}); diff --git a/__tests__/stateProvider.test.ts b/__tests__/stateProvider.test.ts deleted file mode 100644 index 255b5fb2..00000000 --- a/__tests__/stateProvider.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as core from "@actions/core"; - -import { Events, RefKey, State } from "../src/constants"; -import { - IStateProvider, - NullStateProvider, - StateProvider -} from "../src/stateProvider"; - -jest.mock("@actions/core"); - -beforeAll(() => { - jest.spyOn(core, "getInput").mockImplementation((name, options) => { - return jest.requireActual("@actions/core").getInput(name, options); - }); - - jest.spyOn(core, "setOutput").mockImplementation((key, value) => { - return jest.requireActual("@actions/core").setOutput(key, value); - }); -}); - -afterEach(() => { - delete process.env[Events.Key]; - delete process.env[RefKey]; -}); - -test("StateProvider saves states", async () => { - const states = new Map(); - const getStateMock = jest - .spyOn(core, "getState") - .mockImplementation(key => states.get(key) || ""); - - const saveStateMock = jest - .spyOn(core, "saveState") - .mockImplementation((key, value) => { - states.set(key, value); - }); - - const setOutputMock = jest - .spyOn(core, "setOutput") - .mockImplementation((key, value) => { - return jest.requireActual("@actions/core").setOutput(key, value); - }); - - const cacheMatchedKey = "node-cache"; - - const stateProvider: IStateProvider = new StateProvider(); - stateProvider.setState("stateKey", "stateValue"); - stateProvider.setState(State.CacheMatchedKey, cacheMatchedKey); - const stateValue = stateProvider.getState("stateKey"); - const cacheStateValue = stateProvider.getCacheState(); - - expect(stateValue).toBe("stateValue"); - expect(cacheStateValue).toBe(cacheMatchedKey); - expect(getStateMock).toHaveBeenCalledTimes(2); - expect(saveStateMock).toHaveBeenCalledTimes(2); - expect(setOutputMock).toHaveBeenCalledTimes(0); -}); - -test("NullStateProvider saves outputs", async () => { - const getStateMock = jest - .spyOn(core, "getState") - .mockImplementation(name => - jest.requireActual("@actions/core").getState(name) - ); - - const setOutputMock = jest - .spyOn(core, "setOutput") - .mockImplementation((key, value) => { - return jest.requireActual("@actions/core").setOutput(key, value); - }); - - const saveStateMock = jest - .spyOn(core, "saveState") - .mockImplementation((key, value) => { - return jest.requireActual("@actions/core").saveState(key, value); - }); - - const cacheMatchedKey = "node-cache"; - const nullStateProvider: IStateProvider = new NullStateProvider(); - nullStateProvider.setState(State.CacheMatchedKey, "outputValue"); - nullStateProvider.setState(State.CachePrimaryKey, cacheMatchedKey); - nullStateProvider.getState("outputKey"); - nullStateProvider.getCacheState(); - - expect(getStateMock).toHaveBeenCalledTimes(0); - expect(setOutputMock).toHaveBeenCalledTimes(2); - expect(saveStateMock).toHaveBeenCalledTimes(0); -}); diff --git a/action.nix b/action.nix new file mode 100644 index 00000000..523784c1 --- /dev/null +++ b/action.nix @@ -0,0 +1,253 @@ +{ target, lib }: +let + specific = { + cache = { + description = "Restore and save"; + actions = "restoring and saving"; + main = "dist/restore/index.js"; + post = + '' + post: "dist/save/index.js" + post-if: success()''; + }; + save = { + description = "Save"; + actions = "saving"; + main = "../dist/save-only/index.js"; + post = ""; + }; + restore = { + description = "Restore"; + actions = "restoring"; + main = "../dist/restore-only/index.js"; + post = ""; + }; + }.${target}; + + q = txt: "`${txt}`"; + whenListOf = "When a newline-separated non-empty list of non-empty"; + pathsDefault = ''`["/nix", "~/.cache/nix", "~root/.cache/nix"]`''; + nixTrue = "nix: true"; + + pathsDefaultWhenNix = ''When ${q nixTrue}, uses ${pathsDefault} as default paths. Otherwise, uses an empty list as default paths.''; + pathsWhen = ''${whenListOf} path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for ${specific.actions} caches.''; + pathsOtherwise = ''Otherwise, the action uses default paths for ${specific.actions} caches.''; + + effectOnlyOn = platform: ''Can have an effect only when on a ${q platform} runner.''; + effectOnlyOnLinux = effectOnlyOn "Linux"; + effectOnlyOnMacOS = effectOnlyOn "macOS"; + effectOnlyWhenHasEffect = input: "Can have an effect only when ${q input} has effect."; + effectOnlyWhen = conditions: "Can have an effect only when ${lib.concatMapStringsSep ", " q conditions}."; + effectOnlyWhenNixEnabled = effectOnlyWhen [ nixTrue ]; + + noEffectOtherwise = ''Otherwise, this input has no effect.''; + + gcWhen = ''When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.''; + + overrides = input: "Overrides ${q input}."; + + paths = "paths"; + gc-max-store-size = "gc-max-store-size"; + primary-key = "primary-key"; + restore-prefixes-first-match = "restore-prefixes-first-match"; + hit-primary-key = "hit-primary-key"; + hit-first-match = "hit-first-match"; +in +'' + name: "${specific.description} Nix store" + description: "${specific.description} Nix store using GitHub Actions cache to speed up workflows." + author: "GitHub" + inputs: + ${primary-key}: + description: | + - When a non-empty string, the action uses this key for ${specific.actions} a cache. + - Otherwise, the action fails. + required: true + + ${ + if target == "cache" || target == "restore" then + '' + ${restore-prefixes-first-match}: + description: | + - ${whenListOf} key prefixes, when there's a miss on the ${q primary-key}, + the action searches in this list for the first prefix for which there exists a cache + with a matching key and the action tries to restore that cache. + - ${noEffectOtherwise} + default: "" + restore-prefixes-all-matches: + description: | + - ${whenListOf} key prefixes, the action tries to restore + all caches whose keys match these prefixes. + - ${noEffectOtherwise} + default: "" + + skip-restore-on-hit-primary-key: + description: | + - Can have an effect only when ${q restore-prefixes-first-match} has no effect. + - When `true`, when there's a hit on the ${q primary-key}, the action doesn't restore caches. + - ${noEffectOtherwise} + default: "false" + + fail-on: + description: | + - Input form: `.`. + - `` options: ${q primary-key}, `first-match`. + - `` options: `miss`, `not-restored`. + - When the input satisfies the input form, when the event described in the input happens, the action fails. + - Example: + - Input: `${primary-key}.not-restored`. + - Event: a cache could not be restored via the ${q primary-key}. + - ${noEffectOtherwise} + default: ""'' + else "" + } + + nix: + description: | + - When `true`, the action can do Nix-specific things. + - Otherwise, the action doesn't do them. + default: "true" + + save: + description: | + - When `true`, the action can save a cache with the ${q primary-key}. + - Otherwise, the action can't save a cache. + default: "true" + + ${paths}: + description: | + - ${pathsDefaultWhenNix} + - ${pathsWhen} + - ${pathsOtherwise} + default: "" + ${paths}-macos: + description: | + - ${overrides paths} + - ${effectOnlyOnMacOS} + - ${pathsDefaultWhenNix} + - ${pathsWhen} + - ${pathsOtherwise} + default: "" + ${paths}-linux: + description: | + - ${overrides paths} + - ${effectOnlyOnLinux} + - ${pathsDefaultWhenNix} + - ${pathsWhen} + - ${pathsOtherwise} + default: "" + + ${ + if target == "cache" || target == "save" then + '' + ${gc-max-store-size}: + description: | + - ${effectOnlyWhen [nixTrue "save: true"]} + - ${gcWhen} + - ${noEffectOtherwise} + default: "" + ${gc-max-store-size}-macos: + description: | + - ${effectOnlyWhen [nixTrue "save: true"]} + - ${overrides gc-max-store-size} + - ${effectOnlyOnMacOS} + - ${gcWhen} + - ${noEffectOtherwise} + default: "" + ${gc-max-store-size}-linux: + description: | + - ${effectOnlyWhen [nixTrue "save: true"]} + - ${overrides gc-max-store-size} + - ${effectOnlyOnLinux} + - ${gcWhen} + - ${noEffectOtherwise} + default: "" + + purge: + description: | + - When `true`, the action purges (possibly zero) caches. + - ${noEffectOtherwise} + default: "false" + purge-overwrite: + description: | + - ${effectOnlyWhen ["purge: true"]} + - When `always`, the action always purges cache with the ${q primary-key}. + - When `never`, the action never purges cache with the ${q primary-key}. + - Otherwise, the action purges old caches using purging criteria. + default: "" + purge-prefixes: + description: | + - ${effectOnlyWhen ["purge: true"]} + - ${whenListOf} cache key prefixes, the action collects all cache keys that match these prefixes. + - ${noEffectOtherwise} + default: "" + purge-last-accessed: + description: | + - ${effectOnlyWhen ["purge: true"]} + - When a number, the action selects for purging caches last accessed more than this number of seconds ago relative to the start of the Save phase. + - ${noEffectOtherwise} + default: "" + purge-created: + description: | + - ${effectOnlyWhen ["purge: true"]} + - When a number, the action selects for purging caches created more than this number of seconds ago relative to the start of the Save phase. + - ${noEffectOtherwise} + default: "" + + upload-chunk-size: + # The original default value may be provided here (https://github.com/actions/cache/issues/1292) + # 32MB + description: | + - When a number, the action uses it as the chunk size (in bytes) to split up large files during upload. + - Otherwise, the action uses the default value `33554432` (32MB). + default: ""'' + else ""} + + token: + description: The action uses it to communicate with GitHub API. + default: ''${{ github.token }} + ${ + if target == "cache" || target == "restore" then + '' + outputs: + ${primary-key}: + description: | + - A string. + - The ${q primary-key}. + + hit: + description: | + - A boolean value. + - `true` when ${q hit-primary-key} is `true` or ${q hit-first-match} is `true`. + - `false` otherwise. + ${hit-primary-key}: + description: | + - A boolean value. + - `true` when there was a hit on the ${q primary-key}. + - `false` otherwise. + ${hit-first-match}: + description: | + - A boolean value. + - `true` when there was a hit on a key matching ${q restore-prefixes-first-match}. + - `false` otherwise. + + restored-key: + description: | + - A string. + - The key of a cache restored via the ${q primary-key} or via the ${q restore-prefixes-first-match}. + - An empty string otherwise. + + restored-keys: + description: | + - A possibly empty array of strings (JSON). + - Keys of restored caches. + - Example: `["key1", "key2"]`.'' + else "" + } + runs: + using: "node16" + main: "${specific.main}" + ${specific.post} + branding: + icon: "archive" + color: "gray-dark"'' diff --git a/action.yml b/action.yml index 043c89fb..282b50b7 100644 --- a/action.yml +++ b/action.yml @@ -1,71 +1,184 @@ -name: 'Cache Nix store' -description: 'Cache Nix store using GitHub Actions cache to speed up workflows.' -author: 'GitHub' +name: "Restore and save Nix store" +description: "Restore and save Nix store using GitHub Actions cache to speed up workflows." +author: "GitHub" inputs: - key: - description: 'An explicit key for restoring and saving the cache.' + primary-key: + description: | + - When a non-empty string, the action uses this key for restoring and saving a cache. + - Otherwise, the action fails. required: true - restore-keys: - description: 'An ordered list of keys to use for restoring stale cache if no cache hit occurred for key. Note `cache-hit` returns `false` in this case. Use `restore-key-hit` to change this behavior.' - extra-restore-keys: - description: 'A newline-separated list of key prefixes used for restoring multiple caches.' - default: '' - restore-key-hit: - description: "When true, if a cache key matching `restore-keys` exists, it counts as a cache hit. Thus, a job won't save a new cache." - default: 'false' + + restore-prefixes-first-match: + description: | + - When a newline-separated non-empty list of non-empty key prefixes, when there's a miss on the `primary-key`, + the action searches in this list for the first prefix for which there exists a cache + with a matching key and the action tries to restore that cache. + - Otherwise, this input has no effect. + default: "" + restore-prefixes-all-matches: + description: | + - When a newline-separated non-empty list of non-empty key prefixes, the action tries to restore + all caches whose keys match these prefixes. + - Otherwise, this input has no effect. + default: "" - upload-chunk-size: - description: 'The chunk size used to split up large files during upload, in bytes.' - fail-on-cache-miss: - description: 'Fail the workflow if a cache entry is found neither via `key` nor via `restore-keys`.' - default: 'false' + skip-restore-on-hit-primary-key: + description: | + - Can have an effect only when `restore-prefixes-first-match` has no effect. + - When `true`, when there's a hit on the `primary-key`, the action doesn't restore caches. + - Otherwise, this input has no effect. + default: "false" + + fail-on: + description: | + - Input form: `.`. + - `` options: `primary-key`, `first-match`. + - `` options: `miss`, `not-restored`. + - When the input satisfies the input form, when the event described in the input happens, the action fails. + - Example: + - Input: `primary-key.not-restored`. + - Event: a cache could not be restored via the `primary-key`. + - Otherwise, this input has no effect. + default: "" + + nix: + description: | + - When `true`, the action can do Nix-specific things. + - Otherwise, the action doesn't do them. + default: "true" - gc-macos: - description: 'When `true`, enables on `macOS` runners Nix store garbage collection before saving a cache.' - default: 'false' + save: + description: | + - When `true`, the action can save a cache with the `primary-key`. + - Otherwise, the action can't save a cache. + default: "true" + + paths: + description: | + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for restoring and saving caches. + - Otherwise, the action uses default paths for restoring and saving caches. + default: "" + paths-macos: + description: | + - Overrides `paths`. + - Can have an effect only when on a `macOS` runner. + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for restoring and saving caches. + - Otherwise, the action uses default paths for restoring and saving caches. + default: "" + paths-linux: + description: | + - Overrides `paths`. + - Can have an effect only when on a `Linux` runner. + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for restoring and saving caches. + - Otherwise, the action uses default paths for restoring and saving caches. + default: "" + + gc-max-store-size: + description: | + - Can have an effect only when `nix: true`, `save: true`. + - When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache. + - Otherwise, this input has no effect. + default: "" gc-max-store-size-macos: - description: 'Maximum Nix store size in bytes on `macOS` runners. Requires `gc-macos: true`.' - gc-linux: - description: 'When `true`, enables on `Linux` runners Nix store garbage collection before saving a cache.' - default: 'false' + description: | + - Can have an effect only when `nix: true`, `save: true`. + - Overrides `gc-max-store-size`. + - Can have an effect only when on a `macOS` runner. + - When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache. + - Otherwise, this input has no effect. + default: "" gc-max-store-size-linux: - description: 'Maximum Nix store size in bytes on `Linux` runners. Requires `gc-linux: true`.' - + description: | + - Can have an effect only when `nix: true`, `save: true`. + - Overrides `gc-max-store-size`. + - Can have an effect only when on a `Linux` runner. + - When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache. + - Otherwise, this input has no effect. + default: "" + purge: - description: 'When `true`, purge old caches before saving a new cache with a `key`.' - default: 'false' - purge-keys: - description: 'A newline-separated list of cache key prefixes used to purge caches. An empty string is equivalent to the `key` input.' - default: '' - purge-accessed: - description: 'When `true`, purge caches by `last accessed` time. Requires `purge: true`.' - default: 'false' - purge-accessed-max-age: - description: 'Purge caches accessed more than this number of seconds ago. Requires `purge-accessed: true`.' - default: '604800' + description: | + - When `true`, the action purges (possibly zero) caches. + - Otherwise, this input has no effect. + default: "false" + purge-overwrite: + description: | + - Can have an effect only when `purge: true`. + - When `always`, the action always purges cache with the `primary-key`. + - When `never`, the action never purges cache with the `primary-key`. + - Otherwise, the action purges old caches using purging criteria. + default: "" + purge-prefixes: + description: | + - Can have an effect only when `purge: true`. + - When a newline-separated non-empty list of non-empty cache key prefixes, the action collects all cache keys that match these prefixes. + - Otherwise, this input has no effect. + default: "" + purge-last-accessed: + description: | + - Can have an effect only when `purge: true`. + - When a number, the action selects for purging caches last accessed more than this number of seconds ago relative to the start of the Save phase. + - Otherwise, this input has no effect. + default: "" purge-created: - description: 'When `true`, purge caches by `created` time. Requires `purge: true`.' - default: 'true' - purge-created-max-age: - description: 'Purge caches created more than this number of seconds ago. Requires `purge-created: true`.' - default: '604800' - + description: | + - Can have an effect only when `purge: true`. + - When a number, the action selects for purging caches created more than this number of seconds ago relative to the start of the Save phase. + - Otherwise, this input has no effect. + default: "" + + upload-chunk-size: + # The original default value may be provided here (https://github.com/actions/cache/issues/1292) + # 32MB + description: | + - When a number, the action uses it as the chunk size (in bytes) to split up large files during upload. + - Otherwise, the action uses the default value `33554432` (32MB). + default: "" + token: - description: 'Used to communicate with GitHub API.' + description: The action uses it to communicate with GitHub API. default: ${{ github.token }} - required: true outputs: - cache-hit: - description: 'A boolean value to indicate an exact match was found for the primary key.' - cache-primary-key: - description: 'A resolved cache key for which cache match was attempted' - cache-matched-key: - description: 'Key of the cache that was restored, it could either be the primary key on cache-hit or a partial/complete match of one of the restore keys' + primary-key: + description: | + - A string. + - The `primary-key`. + + hit: + description: | + - A boolean value. + - `true` when `hit-primary-key` is `true` or `hit-first-match` is `true`. + - `false` otherwise. + hit-primary-key: + description: | + - A boolean value. + - `true` when there was a hit on the `primary-key`. + - `false` otherwise. + hit-first-match: + description: | + - A boolean value. + - `true` when there was a hit on a key matching `restore-prefixes-first-match`. + - `false` otherwise. + + restored-key: + description: | + - A string. + - The key of a cache restored via the `primary-key` or via the `restore-prefixes-first-match`. + - An empty string otherwise. + + restored-keys: + description: | + - A possibly empty array of strings (JSON). + - Keys of restored caches. + - Example: `["key1", "key2"]`. runs: - using: 'node16' - main: 'dist/restore/index.js' - post: 'dist/save/index.js' + using: "node16" + main: "dist/restore/index.js" + post: "dist/save/index.js" post-if: success() branding: - icon: 'archive' - color: 'gray-dark' + icon: "archive" + color: "gray-dark" diff --git a/actions-toolkit/.eslintignore b/actions-toolkit/.eslintignore new file mode 100644 index 00000000..3ac58072 --- /dev/null +++ b/actions-toolkit/.eslintignore @@ -0,0 +1,5 @@ +node_modules/ +packages/*/node_modules/ +packages/*/lib/ +packages/glob/__tests__/_temp +packages/*/src/generated/*/ diff --git a/actions-toolkit/.eslintrc.json b/actions-toolkit/.eslintrc.json new file mode 100644 index 00000000..f86ba651 --- /dev/null +++ b/actions-toolkit/.eslintrc.json @@ -0,0 +1,107 @@ +{ + "plugins": [ + "jest", + "@typescript-eslint", + "prettier" + ], + "extends": [ + "plugin:github/recommended", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 9, + "sourceType": "module", + "project": "./tsconfig.eslint.json" + }, + "rules": { + "prettier/prettier": [ + "error", + { + "endOfLine": "auto" + } + ], + "eslint-comments/no-use": "off", + "no-constant-condition": ["error", { "checkLoops": false }], + "github/no-then": "off", + "import/no-namespace": "off", + "no-shadow": "off", + "no-unused-vars": "off", + "i18n-text/no-en": "off", + "filenames/match-regex": "off", + "import/no-commonjs": "off", + "import/named": "off", + "no-sequences": "off", + "import/no-unresolved": "off", + "no-undef": "off", + "no-only-tests/no-only-tests": "off", + "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "no-public" + } + ], + "@typescript-eslint/no-require-imports": "error", + "@typescript-eslint/array-type": "error", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/ban-ts-comment": "error", + "camelcase": "off", + "@typescript-eslint/camelcase": "off", + "@typescript-eslint/consistent-type-assertions": "off", + "@typescript-eslint/explicit-function-return-type": [ + "error", + { + "allowExpressions": true + } + ], + "@typescript-eslint/func-call-spacing": [ + "error", + "never" + ], + "@typescript-eslint/naming-convention": [ + "error", + { + "format": null, + "filter": { + // you can expand this regex as you find more cases that require quoting that you want to allow + "regex": "^[A-Z][A-Za-z]*$", + "match": true + }, + "selector": "memberLike" + } + ], + "@typescript-eslint/no-array-constructor": "error", + "@typescript-eslint/no-empty-interface": "error", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-non-null-assertion": "warn", + "@typescript-eslint/no-unnecessary-qualifier": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-useless-constructor": "error", + "@typescript-eslint/no-var-requires": "error", + "@typescript-eslint/prefer-for-of": "warn", + "@typescript-eslint/prefer-function-type": "warn", + "@typescript-eslint/prefer-includes": "error", + "@typescript-eslint/prefer-string-starts-ends-with": "error", + "@typescript-eslint/promise-function-async": "error", + "@typescript-eslint/require-array-sort-compare": "error", + "semi": "off", + "@typescript-eslint/semi": [ + "error", + "never" + ], + "@typescript-eslint/type-annotation-spacing": "error", + "@typescript-eslint/unbound-method": "error" + }, + "ignorePatterns": "packages/glob/__tests__/_temp/**/", + "env": { + "node": true, + "es6": true, + "jest/globals": true + } +} \ No newline at end of file diff --git a/actions-toolkit/.github/CONTRIBUTING.md b/actions-toolkit/.github/CONTRIBUTING.md new file mode 100644 index 00000000..fa05ba3e --- /dev/null +++ b/actions-toolkit/.github/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributions + +We welcome contributions in the form of issues and pull requests. We view the contributions and process as the same for internal and external contributors. + +## Issues + +Log issues for both bugs and enhancement requests. Logging issues are important for the open community. + +Issues in this repository should be for the toolkit packages. General feedback for GitHub Actions should be filed in the [community forums.](https://github.community/t5/GitHub-Actions/bd-p/actions) Runner specific issues can be filed [in the runner repository](https://github.com/actions/runner). + +## Enhancements and Feature Requests + +We ask that before significant effort is put into code changes, that we have agreement on taking the change before time is invested in code changes. + +1. Create a feature request. +2. When we agree to take the enhancement, create an ADR to agree on the details of the change. + +An ADR is an Architectural Decision Record. This allows consensus on the direction forward and also serves as a record of the change and motivation. [Read more here](../docs/adrs/README.md). + +## Development Life Cycle + +This repository uses [Lerna](https://github.com/lerna/lerna#readme) to manage multiple packages. Read the documentation there to begin contributing. + +Note that before a PR will be accepted, you must ensure: +- all tests are passing +- `npm run format` reports no issues +- `npm run lint` reports no issues + +### Useful Scripts + +- `npm run bootstrap` This runs `lerna exec -- npm install` which will install dependencies in this repository's packages and cross-link packages where necessary. +- `npm run build` This compiles TypeScript code in each package (this is especially important if one package relies on changes in another when you're running tests). This is just an alias for `lerna run tsc`. +- `npm run format` This checks that formatting has been applied with Prettier. +- `npm test` This runs all Jest tests in all packages in this repository. + - If you need to run tests for only one package, you can pass normal Jest CLI options: + ```console + $ npm test -- packages/toolkit + ``` +- `npm run create-package [name]` This runs a script that automates a couple of parts of creating a new package. + +### Creating a Package + +1. In a new branch, create a new Lerna package: + +```console +$ npm run create-package new-package +``` + +This will ask you some questions about the new package. Start with `0.0.0` as the first version (look generally at some of the other packages for how the package.json is structured). + +2. Add `tsc` script to the new package's package.json file: + +```json +"scripts": { + "tsc": "tsc" +} +``` + +3. Start developing 😄. diff --git a/actions-toolkit/.github/ISSUE_TEMPLATE/bug_report.md b/actions-toolkit/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..424c9615 --- /dev/null +++ b/actions-toolkit/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. + +* If you have found a security issue [please submit it here](https://hackerone.com/github) +* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) +* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support) + +If your issue is relevant to this repository, please include the information below: + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/actions-toolkit/.github/ISSUE_TEMPLATE/enhancement_request.md b/actions-toolkit/.github/ISSUE_TEMPLATE/enhancement_request.md new file mode 100644 index 00000000..232a56e5 --- /dev/null +++ b/actions-toolkit/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -0,0 +1,25 @@ +--- +name: Feature Request +about: Create a request to help us improve +title: '' +labels: enhancement +assignees: '' + +--- + +Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. + +* If you have found a security issue [please submit it here](https://hackerone.com/github) +* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) +* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support) + +If your issue is relevant to this repository, please include the information below: + +**Describe the enhancement** +A clear and concise description of what the features or enhancement you need. + +**Code Snippet** +If applicable, add a code snippet to show the api enhancement. + +**Additional information** +Add any other context about the feature here. diff --git a/actions-toolkit/.github/workflows/artifact-tests.yml b/actions-toolkit/.github/workflows/artifact-tests.yml new file mode 100644 index 00000000..4c7c6ab6 --- /dev/null +++ b/actions-toolkit/.github/workflows/artifact-tests.yml @@ -0,0 +1,130 @@ +# Temporarily disabled while v2.0.0 of @actions/artifact is under development + +name: artifact-unit-tests +on: + push: + branches: + - main + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + build: + name: Build + + strategy: + matrix: + runs-on: [ubuntu-latest, windows-latest, macos-latest] + fail-fast: false + + runs-on: ${{ matrix.runs-on }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + + # Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible + # without these to just compile the artifacts package + - name: Install root npm packages + run: npm ci + + - name: Compile artifact package + run: | + npm ci + npm run tsc + working-directory: packages/artifact + + - name: Set artifact file contents + shell: bash + run: | + echo "file1=hello from file 1" >> $GITHUB_ENV + echo "file2=hello from file 2" >> $GITHUB_ENV + + - name: Create files that will be uploaded + run: | + mkdir artifact-path + echo '${{ env.file1 }}' > artifact-path/first.txt + echo '${{ env.file2 }}' > artifact-path/second.txt + + - name: Upload Artifacts using actions/github-script@v7 + uses: actions/github-script@v7 + with: + script: | + const {default: artifact} = require('./packages/artifact/lib/artifact') + + const artifactName = 'my-artifact-${{ matrix.runs-on }}' + console.log('artifactName: ' + artifactName) + + const fileContents = ['artifact-path/first.txt','artifact-path/second.txt'] + + const uploadResult = await artifact.uploadArtifact(artifactName, fileContents, './') + console.log(uploadResult) + + const size = uploadResult.size + const id = uploadResult.id + + console.log(`Successfully uploaded artifact ${id}`) + + verify: + runs-on: ubuntu-latest + needs: [build] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + + # Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible + # without these to just compile the artifacts package + - name: Install root npm packages + run: npm ci + + - name: Compile artifact package + run: | + npm ci + npm run tsc + working-directory: packages/artifact + + - name: List artifacts using actions/github-script@v7 + uses: actions/github-script@v7 + with: + script: | + const {default: artifact} = require('./packages/artifact/lib/artifact') + + const workflowRunId = process.env.GITHUB_RUN_ID + const repository = process.env.GITHUB_REPOSITORY + const repositoryOwner = repository.split('/')[0] + const repositoryName = repository.split('/')[1] + + const listResult = await artifact.listArtifacts(workflowRunId, repositoryOwner, repositoryName, '${{ secrets.GITHUB_TOKEN }}') + console.log(listResult) + + const artifacts = listResult.artifacts + + if (artifacts.length !== 3) { + throw new Error('Expected 3 artifacts but only found ' + artifacts.length + ' artifacts') + } + + const artifactNames = artifacts.map(artifact => artifact.name) + if (!artifactNames.includes('my-artifact-ubuntu-latest')){ + throw new Error("Expected artifact list to contain an artifact named my-artifact-ubuntu-latest but it's missing") + } + if (!artifactNames.includes('my-artifact-windows-latest')){ + throw new Error("Expected artifact list to contain an artifact named my-artifact-windows-latest but it's missing") + } + if (!artifactNames.includes('my-artifact-macos-latest')){ + throw new Error("Expected artifact list to contain an artifact named my-artifact-macos-latest but it's missing") + } + + console.log('Successfully listed artifacts that were uploaded') diff --git a/actions-toolkit/.github/workflows/audit.yml b/actions-toolkit/.github/workflows/audit.yml new file mode 100644 index 00000000..6e492488 --- /dev/null +++ b/actions-toolkit/.github/workflows/audit.yml @@ -0,0 +1,38 @@ +name: toolkit-audit +on: + push: + branches: + - main + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + + build: + name: Audit + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: npm install + run: npm install + + - name: Bootstrap + run: npm run bootstrap + + - name: audit tools (without allow-list) + run: npm audit --audit-level=moderate + + - name: audit packages + run: npm run audit-all diff --git a/actions-toolkit/.github/workflows/cache-tests.yml b/actions-toolkit/.github/workflows/cache-tests.yml new file mode 100644 index 00000000..e05362a3 --- /dev/null +++ b/actions-toolkit/.github/workflows/cache-tests.yml @@ -0,0 +1,91 @@ +name: cache-unit-tests +on: + push: + branches: + - main + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + build: + name: Build + + strategy: + matrix: + runs-on: [ubuntu-latest, windows-latest, macOS-latest] + fail-fast: false + + runs-on: ${{ matrix.runs-on }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + + # In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the + # node context. This runs a local action that gets and sets the necessary env variables that are needed + - name: Set env variables + uses: ./packages/cache/__tests__/__fixtures__/ + + # Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible + # without these to just compile the cache package + - name: Install root npm packages + run: npm ci + + - name: Compile cache package + run: | + npm ci + npm run tsc + working-directory: packages/cache + + - name: Generate files in working directory + shell: bash + run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} test-cache + + - name: Generate files outside working directory + shell: bash + run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} ~/test-cache + + # We're using node -e to call the functions directly available in the @actions/cache package + - name: Save cache using saveCache() + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + + - name: Delete cache folders before restoring + shell: bash + run: | + rm -rf test-cache + rm -rf ~/test-cache + + - name: Restore cache using restoreCache() with http-client + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}',[],{useAzureSdk: false}))" + + - name: Verify cache restored with http-client + shell: bash + run: | + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache + + - name: Delete cache folders before restoring + shell: bash + run: | + rm -rf test-cache + rm -rf ~/test-cache + + - name: Restore cache using restoreCache() with Azure SDK + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + + - name: Verify cache restored with Azure SDK + shell: bash + run: | + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache diff --git a/actions-toolkit/.github/workflows/cache-windows-test.yml b/actions-toolkit/.github/workflows/cache-windows-test.yml new file mode 100644 index 00000000..75792fc0 --- /dev/null +++ b/actions-toolkit/.github/workflows/cache-windows-test.yml @@ -0,0 +1,90 @@ +name: cache-windows-bsd-unit-tests +on: + push: + branches: + - main + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + build: + name: Build + + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - shell: bash + run: | + rm "C:\Program Files\Git\usr\bin\tar.exe" + + - name: Set Node.js 20.x + uses: actions/setup-node@v1 + with: + node-version: 20.x + + # In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the + # node context. This runs a local action that gets and sets the necessary env variables that are needed + - name: Set env variables + uses: ./packages/cache/__tests__/__fixtures__/ + + # Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible + # without these to just compile the cache package + - name: Install root npm packages + run: npm ci + + - name: Compile cache package + run: | + npm ci + npm run tsc + working-directory: packages/cache + + - name: Generate files in working directory + shell: bash + run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} test-cache + + - name: Generate files outside working directory + shell: bash + run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} ~/test-cache + + # We're using node -e to call the functions directly available in the @actions/cache package + - name: Save cache using saveCache() + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + + - name: Delete cache folders before restoring + shell: bash + run: | + rm -rf test-cache + rm -rf ~/test-cache + + - name: Restore cache using restoreCache() with http-client + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}',[],{useAzureSdk: false}))" + + - name: Verify cache restored with http-client + shell: bash + run: | + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache + + - name: Delete cache folders before restoring + shell: bash + run: | + rm -rf test-cache + rm -rf ~/test-cache + + - name: Restore cache using restoreCache() with Azure SDK + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + + - name: Verify cache restored with Azure SDK + shell: bash + run: | + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache diff --git a/actions-toolkit/.github/workflows/codeql.yml b/actions-toolkit/.github/workflows/codeql.yml new file mode 100644 index 00000000..4b44be98 --- /dev/null +++ b/actions-toolkit/.github/workflows/codeql.yml @@ -0,0 +1,37 @@ +name: "Code Scanning - Action" + +on: + push: + branches: + - main + pull_request: + schedule: + - cron: '0 0 * * 0' + +jobs: + CodeQL-Build: + + strategy: + fail-fast: false + + + # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: javascript + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/actions-toolkit/.github/workflows/releases.yml b/actions-toolkit/.github/workflows/releases.yml new file mode 100644 index 00000000..13d494f2 --- /dev/null +++ b/actions-toolkit/.github/workflows/releases.yml @@ -0,0 +1,80 @@ +name: Publish NPM + +on: + workflow_dispatch: + inputs: + package: + required: true + description: 'core, artifact, cache, exec, github, glob, http-client, io, tool-cache' + +jobs: + test: + runs-on: macos-latest + + steps: + - name: setup repo + uses: actions/checkout@v3 + + - name: verify package exists + run: ls packages/${{ github.event.inputs.package }} + + - name: Set Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: npm install + run: npm install + + - name: bootstrap + run: npm run bootstrap + + - name: build + run: npm run build + + - name: test + run: npm run test + + - name: pack + run: npm pack + working-directory: packages/${{ github.event.inputs.package }} + + - name: upload artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ github.event.inputs.package }} + path: packages/${{ github.event.inputs.package }}/*.tgz + + publish: + runs-on: macos-latest + needs: test + environment: npm-publish + steps: + + - name: download artifact + uses: actions/download-artifact@v3 + with: + name: ${{ github.event.inputs.package }} + + - name: setup authentication + run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc + env: + NPM_TOKEN: ${{ secrets.TOKEN }} + + - name: publish + run: npm publish *.tgz + + - name: notify slack on failure + if: failure() + run: | + curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK + env: + SLACK_WEBHOOK: ${{ secrets.SLACK }} + + - name: notify slack on success + if: success() + run: | + curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK + env: + SLACK_WEBHOOK: ${{ secrets.SLACK }} + diff --git a/actions-toolkit/.github/workflows/unit-tests.yml b/actions-toolkit/.github/workflows/unit-tests.yml new file mode 100644 index 00000000..453f9bcc --- /dev/null +++ b/actions-toolkit/.github/workflows/unit-tests.yml @@ -0,0 +1,51 @@ +name: toolkit-unit-tests +on: + push: + branches: + - main + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + + build: + name: Build + + strategy: + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: false + + runs-on: ${{ matrix.runs-on }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + + - name: npm install + run: npm install + + - name: Bootstrap + run: npm run bootstrap + + - name: Compile + run: npm run build + + - name: npm test + run: npm test -- --runInBand --forceExit + env: + GITHUB_TOKEN: ${{ github.token }} + + - name: Lint + run: npm run lint + + - name: Format + run: npm run format-check diff --git a/actions-toolkit/.github/workflows/update-github.yaml b/actions-toolkit/.github/workflows/update-github.yaml new file mode 100644 index 00000000..6de4eed2 --- /dev/null +++ b/actions-toolkit/.github/workflows/update-github.yaml @@ -0,0 +1,45 @@ +name: "UpdateOctokit" + +on: + workflow_dispatch: + +jobs: + UpdateOctokit: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'actions' }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Update Octokit + working-directory: packages/github + run: | + npx npm-check-updates -u --dep prod + npm install + - name: Check Status + id: status + working-directory: packages/github + run: | + if [[ "$(git status --porcelain)" != "" ]]; then + echo "::set-output name=createPR::true" + git config --global user.email "github-actions@github.com" + git config --global user.name "github-actions[bot]" + git checkout -b bots/updateGitHubDependencies-${{github.run_number}} + git add . + git commit -m "Update Dependencies" + git push --set-upstream origin bots/updateGitHubDependencies-${{github.run_number}} + fi + - name: Create PR + if: ${{steps.status.outputs.createPR}} + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.pulls.create( + { + base: "main", + owner: "${{github.repository_owner}}", + repo: "toolkit", + title: "Update Octokit dependencies", + body: "Update Octokit dependencies", + head: "bots/updateGitHubDependencies-${{github.run_number}}" + }) diff --git a/actions-toolkit/.gitignore b/actions-toolkit/.gitignore new file mode 100644 index 00000000..f543c3ae --- /dev/null +++ b/actions-toolkit/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +packages/*/node_modules/ +packages/*/lib/ +packages/*/__tests__/_temp/ +.DS_Store +*.xar +packages/*/audit.json diff --git a/actions-toolkit/.prettierignore b/actions-toolkit/.prettierignore new file mode 100644 index 00000000..48e1fb76 --- /dev/null +++ b/actions-toolkit/.prettierignore @@ -0,0 +1,5 @@ +node_modules/ +packages/*/node_modules/ +packages/*/lib/ +packages/glob/__tests__/_temp/**/ +packages/*/src/generated/*/ diff --git a/actions-toolkit/.prettierrc.json b/actions-toolkit/.prettierrc.json new file mode 100644 index 00000000..277ad090 --- /dev/null +++ b/actions-toolkit/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": false, + "singleQuote": true, + "trailingComma": "none", + "bracketSpacing": false, + "arrowParens": "avoid", + "parser": "typescript", + "endOfLine": "auto" +} \ No newline at end of file diff --git a/actions-toolkit/CODEOWNERS b/actions-toolkit/CODEOWNERS new file mode 100644 index 00000000..f82b14f2 --- /dev/null +++ b/actions-toolkit/CODEOWNERS @@ -0,0 +1,4 @@ +* @actions/actions-runtime + +/packages/artifact/ @actions/artifacts-actions +/packages/cache/ @actions/actions-cache diff --git a/actions-toolkit/CODE_OF_CONDUCT.md b/actions-toolkit/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..517657b8 --- /dev/null +++ b/actions-toolkit/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@github.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/actions-toolkit/LICENSE.md b/actions-toolkit/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/README.md b/actions-toolkit/README.md new file mode 100644 index 00000000..43ee8acd --- /dev/null +++ b/actions-toolkit/README.md @@ -0,0 +1,227 @@ + +

+ +

+ +

+ Toolkit unit tests status + Toolkit audit status +

+ + +## GitHub Actions Toolkit + +The GitHub Actions ToolKit provides a set of packages to make creating actions easier. + +
+

Get started with the javascript-action template!

+
+ +## Packages + +:heavy_check_mark: [@actions/core](packages/core) + +Provides functions for inputs, outputs, results, logging, secrets and variables. Read more [here](packages/core) + +```bash +$ npm install @actions/core +``` +
+ +:runner: [@actions/exec](packages/exec) + +Provides functions to exec cli tools and process output. Read more [here](packages/exec) + +```bash +$ npm install @actions/exec +``` +
+ +:ice_cream: [@actions/glob](packages/glob) + +Provides functions to search for files matching glob patterns. Read more [here](packages/glob) + +```bash +$ npm install @actions/glob +``` +
+ +:phone: [@actions/http-client](packages/http-client) + +A lightweight HTTP client optimized for building actions. Read more [here](packages/http-client) + +```bash +$ npm install @actions/http-client +``` +
+ +:pencil2: [@actions/io](packages/io) + +Provides disk i/o functions like cp, mv, rmRF, which etc. Read more [here](packages/io) + +```bash +$ npm install @actions/io +``` +
+ +:hammer: [@actions/tool-cache](packages/tool-cache) + +Provides functions for downloading and caching tools. e.g. setup-* actions. Read more [here](packages/tool-cache) + +See @actions/cache for caching workflow dependencies. + +```bash +$ npm install @actions/tool-cache +``` +
+ +:octocat: [@actions/github](packages/github) + +Provides an Octokit client hydrated with the context that the current action is being run in. Read more [here](packages/github) + +```bash +$ npm install @actions/github +``` +
+ +:floppy_disk: [@actions/artifact](packages/artifact) + +Provides functions to interact with actions artifacts. Read more [here](packages/artifact) + +```bash +$ npm install @actions/artifact +``` +
+ +:dart: [@actions/cache](packages/cache) + +Provides functions to cache dependencies and build outputs to improve workflow execution time. Read more [here](packages/cache) + +```bash +$ npm install @actions/cache +``` +
+ +## Creating an Action with the Toolkit + +:question: [Choosing an action type](docs/action-types.md) + +Outlines the differences and why you would want to create a JavaScript or a container based action. +
+
+ +:curly_loop: [Versioning](docs/action-versioning.md) + +Actions are downloaded and run from the GitHub graph of repos. This contains guidance for versioning actions and safe releases. +
+
+ +:warning: [Problem Matchers](docs/problem-matchers.md) + +Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. +
+
+ +:warning: [Proxy Server Support](docs/proxy-support.md) + +Self-hosted runners can be configured to run behind proxy servers. +
+
+ +

Hello World JavaScript Action

+ +Illustrates how to create a simple hello world javascript action. + +```javascript +... + const nameToGreet = core.getInput('who-to-greet'); + console.log(`Hello ${nameToGreet}!`); +... +``` +
+ +

JavaScript Action Walkthrough

+ +Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. + +```javascript +async function run() { + try { + const ms = core.getInput('milliseconds'); + console.log(`Waiting ${ms} milliseconds ...`) + ... +``` +```javascript +PASS ./index.test.js + ✓ throws invalid number + ✓ wait 500 ms + ✓ test runs + +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total +``` +
+ +

TypeScript Action Walkthrough

+ +Walkthrough creating a TypeScript Action with compilation, tests, linting, workflow, publishing, and versioning. + +```javascript +import * as core from '@actions/core'; + +async function run() { + try { + const ms = core.getInput('milliseconds'); + console.log(`Waiting ${ms} milliseconds ...`) + ... +``` +```javascript +PASS ./index.test.js + ✓ throws invalid number + ✓ wait 500 ms + ✓ test runs + +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total +``` +
+
+ +

Docker Action Walkthrough

+ +Create an action that is delivered as a container and run with docker. + +```docker +FROM alpine:3.10 +COPY LICENSE README.md / +COPY entrypoint.sh /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] +``` +
+ +

Docker Action Walkthrough with Octokit

+ +Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client. + +```docker +FROM node:slim +COPY . . +RUN npm install --production +ENTRYPOINT ["node", "/lib/main.js"] +``` +```javascript +const myInput = core.getInput('myInput'); +core.debug(`Hello ${myInput} from inside a container`); + +const context = github.context; +console.log(`We can even get context data, like the repo: ${context.repo.repo}`) +``` +
+ +## Contributing + +We welcome contributions. See [how to contribute](.github/CONTRIBUTING.md). + +## Code of Conduct + +See [our code of conduct](CODE_OF_CONDUCT.md). diff --git a/actions-toolkit/SECURITY.md b/actions-toolkit/SECURITY.md new file mode 100644 index 00000000..fc3f6d19 --- /dev/null +++ b/actions-toolkit/SECURITY.md @@ -0,0 +1,3 @@ +If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github) + +Thanks for helping make GitHub Actions safe for everyone. \ No newline at end of file diff --git a/actions-toolkit/docs/action-debugging.md b/actions-toolkit/docs/action-debugging.md new file mode 100644 index 00000000..d5834693 --- /dev/null +++ b/actions-toolkit/docs/action-debugging.md @@ -0,0 +1,31 @@ +# Debugging +If the job logs do not provide enough detail on why a job may be failing, some other options exist to assist with troubleshooting. + +## Step Debug Logs +This is the primary way for customers to debug job failures caused by failed steps. + +Step debug logs increase the verbosity of a job's logs during and after a job's execution to assist with troubleshooting. + +Additional log events with the prefix `::debug::` will now also appear in the job's logs, these log events are provided by the Action's author and the runner process. + +### How to Access Step Debug Logs +This flag can be enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_STEP_DEBUG` to `true`. + +All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures). + +## Runner Diagnostic Logs +Runner Diagnostic Logs provide additional log files detailing how the Runner is executing an action. + +You need the runner diagnostic logs only if you think there is an infrastructure problem with GitHub Actions and you want the product team to check the logs. + +Each file contains different logging information that corresponds to that process: + * The Runner process coordinates setting up workers to execute jobs. + * The Worker process executes the job. + +These files contain the prefix `Runner_` or `Worker_` to indicate the log source. + +### How to Access Runner Diagnostic Logs +These log files are enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_RUNNER_DEBUG` to `true`. + +All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs). + diff --git a/actions-toolkit/docs/action-types.md b/actions-toolkit/docs/action-types.md new file mode 100644 index 00000000..299ce76a --- /dev/null +++ b/actions-toolkit/docs/action-types.md @@ -0,0 +1,45 @@ +# Action Types + +There are two types of actions. JavaScript and Docker actions. + +- **JavaScript Actions**: JavaScript actions run on the host machine. The unit of work is decoupled from the environment. +- **Docker Actions**: A container action is a container which carries both the unit of work along with the environment and its dependencies packaged up as a container. + +Both have access to the workspace and the github event payload and context. + +## Why would I choose a Docker action? + +Docker actions carry both the unit of work and the environment. + +This creates a more consistent and reliable unit of work where the consumer of the action does not need to worry about the toolsets and its dependencies. + +Docker actions are currently limited to Linux only. + +## Why would I choose a host action? + +JavaScript actions decouple the unit of work from the environment and run directly on the host machine or VM. + +Consider a simple example of testing a node lib on node 8, 10 and running a custom action. Each job will setup a node version on the host and custom-action will run its unit of work on each environment (node8+ubuntu16, node8+windows-2019, etc.) + +```yaml +on: push + +jobs: + build: + strategy: + matrix: + node: [8.x, 10.x] + os: [ubuntu-16.04, windows-2019] + runs-on: ${{matrix.os}} + actions: + - uses: actions/setup-node@v3 + with: + version: ${{matrix.node}} + - run: | + npm install + - run: | + npm test + - uses: actions/custom-action@v1 +``` + +JavaScript actions work on any environment that host action runtime is supported on which is currently node 12. However, a host action that runs a toolset expects the environment that it's running on to have that toolset in its PATH or using a setup-* action to acquire it on demand. diff --git a/actions-toolkit/docs/action-versioning.md b/actions-toolkit/docs/action-versioning.md new file mode 100644 index 00000000..b1847424 --- /dev/null +++ b/actions-toolkit/docs/action-versioning.md @@ -0,0 +1,63 @@ +# Versioning + +Actions are downloaded and run from the GitHub graph of repos. The workflow references an action using a ref. + +Examples: + +```yaml +steps: + - uses: actions/javascript-action@v1 # recommended. starter workflows use this + - uses: actions/javascript-action@v1.0.0 # if an action offers specific releases + - uses: actions/javascript-action@41775a4da8ffae865553a738ab8ac1cd5a3c0044 # sha +``` + +# Compatibility + +Binding to a major version is the latest of that major version ( e.g. `v1` == "1.*" ) + +Major versions should guarantee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows. + +Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `main` branch has the latest code and is unstable to bind to since changes get committed to main and released to the market place by creating a tag. In addition, a new major version carrying breaking changes will get implemented in main after branching off the previous major version. + +> Warning: do not reference `main` since that is the latest code and can be carrying breaking changes of the next major version. + +```yaml +steps: + - uses: actions/javascript-action@main # do not do this +``` + +Binding to the immutable full sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes. + +> Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or SHA as a mitigation strategy for unforeseen breaks. + +# Recommendations + +1. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encountered with the latest major version. + +2. **Publish the specific version to the marketplace**: When you release a specific version, choose the option to "Publish this Action to the GitHub Marketplace". + +screenshot + +3. **Make the new release available to those binding to the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. + +``` +git tag -fa v1 -m "Update v1 tag" +git push origin v1 --force +``` +# Major Versions + +All releases for a major version should hold compat including input compatibility and behavior compatibility. + +Introduce a major version for compatibility breaks and major rewrites of the action. + +Ideally, a major version would carry other benefits to the user to entice them to upgrade their workflows. Since updating their workflows will need to be done with an understanding of the changes and what compatibility was broken, introducing a new major version shouldn't be taken lightly. + +To get feedback and to set expectations, the new major version can be initially released with `v2-beta` tag to indicate you can try it out but it's still going under some churn. Upon release the `-beta` can be dropped and there's an expectation of compatibility from that point forward. + +[An example of v2-beta with checkout](https://github.com/actions/checkout/tree/c170eefc2657d93cc91397be50a299bff978a052#checkout-v2-beta) + +# Sample Workflow + +This illustrates the versioning workflow covered above. + +![versioning](assets/action-releases.png) diff --git a/actions-toolkit/docs/adrs/0381-glob-module.md b/actions-toolkit/docs/adrs/0381-glob-module.md new file mode 100644 index 00000000..a61fd8bb --- /dev/null +++ b/actions-toolkit/docs/adrs/0381-glob-module.md @@ -0,0 +1,216 @@ +# ADR 381: `glob` module + +**Date**: 2019-12-05 + +**Status**: Accepted + +## Context + +This ADR proposes adding a `glob` function to the toolkit. + +First party actions should have a consistent glob experience. + +Related to artifact upload/download v2. + +## Decision + +### New module + +Create a new module `@actions/glob` that can be versioned at it's own pace - not tied to `@actions/io`. + +### Signature + +```js +/** + * Constructs a globber from patterns + * + * @param patterns Patterns separated by newlines + * @param options Glob options + */ +export function create( + patterns: string, + options?: GlobOptions +): Promise {} + +/** + * Used to match files and directories + */ +export interface Globber { + /** + * Returns the search path preceding the first glob segment, from each pattern. + * Duplicates and descendants of other paths are filtered out. + * + * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`. + * + * Example 2: The patterns `/foo/*` and `/foo/bar/*` returns `/foo`. + */ + getSearchPaths(): string[] + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + glob(): Promise + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + globGenerator(): AsyncGenerator +} + +/** + * Options to control globbing behavior + */ +export interface GlobOptions { + /** + * Indicates whether to follow symbolic links. Generally should set to false + * when deleting files. + * + * @default true + */ + followSymbolicLinks?: boolean + + /** + * Indicates whether directories that match a glob pattern, should implicitly + * cause all descendant paths to be matched. + * + * For example, given the directory `my-dir`, the following glob patterns + * would produce the same results: `my-dir/**`, `my-dir/`, `my-dir` + * + * @default true + */ + implicitDescendants?: boolean + + /** + * Indicates whether broken symbolic should be ignored and omitted from the + * result set. Otherwise an error will be thrown. + * + * @default true + */ + omitBrokenSymbolicLinks?: boolean +} +``` + +### Toolkit usage + +Example, do not follow symbolic links: + +```js +const patterns = core.getInput('path') +const globber = glob.create(patterns, {followSymbolicLinks: false}) +const files = globber.glob() +``` + +Example, iterator: + +```js +const patterns = core.getInput('path') +const globber = glob.create(patterns) +for await (const file of this.globGenerator()) { + console.log(file) +} +``` + +### Action usage + +Actions should follow symbolic links by default. + +Users can opt-out. + +Example: + +```yaml +jobs: + build: + steps: + - uses: actions/upload-artifact@v1 + with: + path: | + **/*.tar.gz + **/*.pkg + follow-symbolic-links: false # opt out, should default to true +``` + +### HashFiles function + +Hash files should not follow symbolic links by default. + +User can opt-in by specifying flag `--follow-symbolic-links`. + +Example: + +```yaml +jobs: + build: + steps: + - uses: actions/cache@v1 + with: + hash: ${{ hashFiles('--follow-symbolic-links', '**/package-lock.json') }} +``` + +### Glob behavior + +Patterns `*`, `?`, `[...]`, `**` (globstar) are supported. + +With the following behaviors: + +- File names that begin with `.` may be included in the results +- Case insensitive on Windows +- Directory separator `/` and `\` both supported on Windows + +Note: +- Refer [here](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html#Pattern-Matching) for more information about Bash glob patterns. +- Refer [here](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) for more information about Bash glob options. + +### Tilde expansion + +Support basic tilde expansion, for current user HOME replacement only. + +For example, on macOS: +- `~` may expand to `/Users/johndoe` +- `~/foo` may expand to `/Users/johndoe/foo` + +Note: +- Refer [here](https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html) for more information about Bash tilde expansion. +- All other forms of tilde expansion are not supported. +- Use `os.homedir()` to resolve the HOME path + +### Root and normalize paths + +An unrooted pattern will be rooted using the current working directory, prior to searching. Additionally the search path will be normalized prior to searching (relative pathing removed, slashes normalized on Windows, extra slashes removed). + +The two side effects are: +1. Rooted and normalized paths are always returned +2. The pattern `**` will include the working directory in the results + +These side effects diverge from Bash behavior. Whereas Bash is designed to be a shell, we are designing an API. This decision is intended to improve predictability of the API results. + +Note: +- In Bash, the results are not rooted when the pattern is relative. +- In Bash, the results are not normalized. For example, the results from `./*` may look like: `./foo ./bar` +- In Bash, the results from the pattern `**` does not include the working directory. However the results from `/foo/**` would include the directory `/foo`. Also the results from `foo/**` would include the directory `foo`. + +## Comments + +Patterns that begin with `#` are treated as comments. + +## Exclude patterns + +Leading `!` changes the meaning of an include pattern to exclude. + +Note: +- Multiple leading `!` flips the meaning. + +## Escaping + +Wrapping special characters in `[]` can be used to escape literal glob characters in a file name. For example the literal file name `hello[a-z]` can be escaped as `hello[[]a-z]`. + +On Linux/macOS `\` is also treated as an escape character. + +## Consequences + +- Publish new module `@actions/glob` +- Publish docs for the module (add link from `./README.md` to new doc `./packages/glob/README.md`) \ No newline at end of file diff --git a/actions-toolkit/docs/adrs/README.md b/actions-toolkit/docs/adrs/README.md new file mode 100644 index 00000000..6851a5ec --- /dev/null +++ b/actions-toolkit/docs/adrs/README.md @@ -0,0 +1,19 @@ +# ADRs + +ADR, short for "Architecture Decision Record" is a way of capturing important architectural decisions, along with their context and consequences. + +This folder includes ADRs for the actions toolkit. ADRs are proposed in the form of a pull request, and they commonly follow this format: + +* **Title**: short present tense imperative phrase, less than 50 characters, like a git commit message. + +* **Status**: proposed, accepted, rejected, deprecated, superseded, etc. + +* **Context**: what is the issue that we're seeing that is motivating this decision or change. + +* **Decision**: what is the change that we're actually proposing or doing. + +* **Consequences**: what becomes easier or more difficult to do because of this change. + +--- + +- More information about ADRs can be found [here](https://github.com/joelparkerhenderson/architecture_decision_record). \ No newline at end of file diff --git a/actions-toolkit/docs/assets/action-releases.drawio b/actions-toolkit/docs/assets/action-releases.drawio new file mode 100644 index 00000000..1bdfd714 --- /dev/null +++ b/actions-toolkit/docs/assets/action-releases.drawio @@ -0,0 +1 @@ +7VvbctsqFP0aP9YjkISkx9hJcx7amZ7mTNs8dYiEZVpZeBC+9esPyMi64UsdW3Ymdh4Mm5tgrbU3AqdnDyfLR46n488sIkkPWtGyZ9/3IASWi+SXsqzWFhf5a0PMaaQrlYYn+ocULbV1RiOS1SoKxhJBp3VjyNKUhKJmw5yzRb3aiCX1Uac4Ji3DU4iTwtp3S/t3GomxtgMUlAX/EBqP9eA+1FN+weHvmLNZqkfsQXuUf9bFE1z0paeajXHEFhWT/dCzh5wxsU5NlkOSqNUtFq5oJ1bF0/bswVhMEpkBMpkXf9zSGBzSWE6Ok1RUh9vW38h6CcMgsC2HoJGLPwSt/kkkV1ZnU5bKr0E443MS6RErg2cCc6HJIJlgD0ga3Sk0ZT5McJbRcG38SJOijcxVW2SCs99kyBLG8+FtK/9sSgos1bRHLN2MBvOuBF/92PQrM88yY0kq6Oy94re1ya2K3JKKH5V0pZXMlY1UpmjTXucCGTbjITEvLtRKwDwmwlxF96IWvdKtxu6RsAmRTy4rcJJgQed1EWCtmnhTb9P0C6PyQaGlFe4UQtX69jyr3sV6GrpVyR+ZqDxGacpZdRjDADo1xfaxaSdnTNjvRH7Dsgqxnquk28Kyoxhjd0MH5NfpgNwGHdaUPQsd1gPNcTIrHFqLHkki44ZixWJMBXma4ny5FjJ01cmRe+2cNTn4hWMGJr+C8k+LI06dI7bqCCc0TpULk8gRripIwlW6GrnqbxfEc8IFWe5EUJfCBhJuIcxFGcSKKuNq+LK2Y15D62+gaUeqa1PqHi99bEQ4Sqtov3eH3cjZDRoksjuTM2zJud/vX1TR4MKKdrzrUbR9Xc720tB4Th0aH14OGucGTQ2apgu7ZBx0bnHwL+Kgsz8Oom7ioGdfLA6im5yrckbO5eQc/+T3377+Gz/Mp9/++5SxT+Hwg/G1AyVy1MGLTMR5guM0lM92V5TIgTaF8htPFE7pS6a+JFMJzkgm68zbZzQSs6lKhjhJ2Ezsh3xKOJUTVbAUjb6UpsGUZVRQlsJcxcA/kc9FjUMBpw0SgF2idNuv1AACln25DYsRoFtk3BUZd3F6V3js6NTHQ6DvAdeDvufK2OgFdW45xtI9B4Tto6XGRg6hs8Vg43L7+x19yAkWZL2eyon3lKoGOUbxVvdvgT7o25u+eCUw1Hvf2UnnsULCeBJXFDjNWAHasQJ1GtGtW7DYhdDlt1xtrr+xYHG80y/IeW6X7lsNbwsbaB7qtv3GqRUC3bptELTUPMGZEk2TQ1IOokmVqkI1r6pK06aWGpW4qHShd7pgQqMo2eYt6i4ip5d+qIbyFVgn2f017uyQZdieewZFg2A7jV6laNh2ubvfg94PVm5jo24f+CrV1OvpsDrkoq28rtfLHuFsbPLGBy/ZXodXWRDXsB6F7ZV+ETSu1bymOzvULwLg7enpdI4xcFf8bjX8NcOfvybsz+Pz/XffcMFysv2s1bfe3H7WLvezLXUaCLn93rVx9uEEBsGeaz9rBNp09nEKoOvnVvAkqLddydtA3XP9K0PddA10HtTBu0U9gAdovWBGJ6i753PqoA/frVNH1rU59fa10ASnqxyoZYmt3D1bhl9NXOPu+fUgOY5bA8kFBpDsM22VjSB5LZCGcs6YpgoXMVbSk5tRkqlt4QT/kgudL0Em+XulmJ0iVDbPcQ3HSYHbIUqG874RVSKi6QamjPA5DWkay7S+zoMWG6mJczKnbJa9JwiDxqtLYFKa6UjwCAxltvwJ/Prdp/xPA/vhfw== \ No newline at end of file diff --git a/actions-toolkit/docs/assets/action-releases.png b/actions-toolkit/docs/assets/action-releases.png new file mode 100644 index 00000000..98150ccf Binary files /dev/null and b/actions-toolkit/docs/assets/action-releases.png differ diff --git a/actions-toolkit/docs/assets/annotations.png b/actions-toolkit/docs/assets/annotations.png new file mode 100644 index 00000000..f2100220 Binary files /dev/null and b/actions-toolkit/docs/assets/annotations.png differ diff --git a/actions-toolkit/docs/assets/node12-template.png b/actions-toolkit/docs/assets/node12-template.png new file mode 100644 index 00000000..ba8cba17 Binary files /dev/null and b/actions-toolkit/docs/assets/node12-template.png differ diff --git a/actions-toolkit/docs/commands.md b/actions-toolkit/docs/commands.md new file mode 100644 index 00000000..15f18e84 --- /dev/null +++ b/actions-toolkit/docs/commands.md @@ -0,0 +1,217 @@ +# :: Commands + +The [core toolkit package](https://github.com/actions/toolkit/tree/main/packages/core) offers a number of convenience functions for +setting results, logging, registering secrets and exporting variables across actions. Sometimes, however, its useful to be able to do +these things in a script or other tool. + +To allow this, we provide a special `::` syntax which, if logged to `stdout` on a new line, will allow the runner to perform special behavior on +your commands. The following commands are all supported: + +### Set outputs + +To set an output for the step, use `::set-output`: + +```sh +echo "::set-output name=FOO::BAR" +``` + +Running `steps.[step-id].outputs.FOO` in your Yaml will now give you `BAR` + +```yaml +steps: + - name: Set the value + id: step_one + run: echo "::set-output name=FOO::BAR" + - name: Use it + run: echo ${{ steps.step_one.outputs.FOO }} +``` + +This is wrapped by the core setOutput method: + +```javascript +export function setOutput(name: string, value: string): void {} +``` + +### Register a secret + +If a script or action does work to create a secret at runtime, it can be registered with the runner to be masked in logs. + +To mask a value in the logs, use `::add-mask`: + +```sh +echo "::add-mask::mysecretvalue" +``` + +This is wrapped by the core setSecret method + +```javascript +function setSecret(secret: string): void {} +``` + +Now, future logs containing BAR will be masked. E.g. running `echo "Hello FOO BAR World"` will now print `Hello FOO **** World`. + +**WARNING** The add-mask and setSecret commands only support single-line +secrets or multi-line secrets that have been escaped. `@actions/core` +`setSecret` will escape the string you provide by default. When an escaped +multi-line string is provided the whole string and each of its lines +individually will be masked. For example you can mask `first\nsecond\r\nthird` +using: + +```sh +echo "::add-mask::first%0Asecond%0D%0Athird" +``` + +This will mask `first%0Asecond%0D%0Athird`, `first`, `second` and `third`. + +**WARNING** Do **not** mask short values if you can avoid it, it could render your output unreadable (and future steps' output as well). +For example, if you mask the letter `l`, running `echo "Hello FOO BAR World"` will now print `He*********o FOO BAR Wor****d` + +### Group and Ungroup Log Lines + +Emitting a group with a title will instruct the logs to create a collapsible region up to the next endgroup command. + +```bash +echo "::group::my title" +echo "::endgroup::" +``` + +This is wrapped by the core methods: + +```javascript +function startGroup(name: string): void {} +function endGroup(): void {} +``` + +### Problem Matchers + +Problems matchers can be used to scan a build's output to automatically surface lines to the user that matches the provided pattern. A file path to a .json Problem Matcher must be provided. See [Problem Matchers](problem-matchers.md) for more information on how to define a Problem Matcher. + +```bash +echo "::add-matcher::eslint-compact-problem-matcher.json" +echo "::remove-matcher owner=eslint-compact::" +``` + +`add-matcher` takes a path to a Problem Matcher file +`remove-matcher` removes a Problem Matcher by owner + +### Save State + +Save a state to an environmental variable that can later be used in the main or post action. + +```bash +echo "::save-state name=FOO::foovalue" +``` + +Because `save-state` prepends the string `STATE_` to the name, the environment variable `STATE_FOO` will be available to use in the post or main action. See [Sending Values to the pre and post actions](https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions) for more information. + +### Log Level + +There are several commands to emit different levels of log output: + +| log level | example usage | +|---|---| +| [debug](action-debugging.md) | `echo "::debug::My debug message"` | +| notice | `echo "::notice::My notice message"` | +| warning | `echo "::warning::My warning message"` | +| error | `echo "::error::My error message"` | + +Additional syntax options are described at [the workflow command documentation](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message). + +### Command Echoing + +By default, the echoing of commands to stdout only occurs if [Step Debugging is enabled](./action-debugging.md#How-to-Access-Step-Debug-Logs) + +You can enable or disable this for the current step by using the `echo` command. + +```bash +echo "::echo::on" +``` + +You can also disable echoing. + +```bash +echo "::echo::off" +``` + +This is wrapped by the core method: + +```javascript +function setCommandEcho(enabled: boolean): void {} +``` + +The `add-mask`, `debug`, `warning` and `error` commands do not support echoing. + +### Command Prompt + +CMD processes the `"` character differently from other shells when echoing. In CMD, the above snippets should have the `"` characters removed in order to correctly process. For example, the set output command would be: + +```cmd +echo ::set-output name=FOO::BAR +``` + +## Environment files + +During the execution of a workflow, the runner generates temporary files that can be used to perform certain actions. The path to these files are exposed via environment variables. You will need to use the `utf-8` encoding when writing to these files to ensure proper processing of the commands. Multiple commands can be written to the same file, separated by newlines. + +### Set an environment variable + +To set an environment variable for future out of process steps, write to the file located at `GITHUB_ENV` or use the equivalent `actions/core` function + +```sh +echo "FOO=BAR" >> $GITHUB_ENV +``` + +Running `$FOO` in a future step will now return `BAR` + +For multiline strings, you may use a heredoc style syntax with your choice of delimeter. In the below example, we use `EOF`. + +``` +steps: + - name: Set the value + id: step_one + run: | + echo 'JSON_RESPONSE<> $GITHUB_ENV + curl https://httpbin.org/json >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV +``` + +This would set the value of the `JSON_RESPONSE` env variable to the value of the curl response. + +The expected syntax for the heredoc style is: + +``` +{VARIABLE_NAME}<<{DELIMETER} +{VARIABLE_VALUE} +{DELIMETER} +``` + +This is wrapped by the core `exportVariable` method which sets for future steps but also updates the variable for this step. + +```javascript +export function exportVariable(name: string, val: string): void {} +``` + +### PATH Manipulation + +To prepend a string to PATH write to the file located at `GITHUB_PATH` or use the equivalent `actions/core` function + +```sh +echo "/Users/test/.nvm/versions/node/v12.18.3/bin" >> $GITHUB_PATH +``` + +Running `$PATH` in a future step will now return `/Users/test/.nvm/versions/node/v12.18.3/bin:{Previous Path}`; + +This is wrapped by the core addPath method: + +```javascript +export function addPath(inputPath: string): void {} +``` + +### Powershell + +Powershell does not use UTF8 by default. You will want to make sure you write in the correct encoding. For example, to set the path: + +``` +steps: + - run: echo "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append +``` diff --git a/actions-toolkit/docs/container-action-toolkit.md b/actions-toolkit/docs/container-action-toolkit.md new file mode 100644 index 00000000..fb274d1f --- /dev/null +++ b/actions-toolkit/docs/container-action-toolkit.md @@ -0,0 +1,3 @@ +# Creating a Container Action Using the Toolkit + +In progress. \ No newline at end of file diff --git a/actions-toolkit/docs/container-action.md b/actions-toolkit/docs/container-action.md new file mode 100644 index 00000000..06f3a246 --- /dev/null +++ b/actions-toolkit/docs/container-action.md @@ -0,0 +1,71 @@ +# Creating a Docker Action + +The [container-template](https://github.com/actions/container-template) repo contains the base files to create a Docker action. + +# Create a Repo from the Template + +Navigate to https://github.com/actions/container-template + +Click on `Use this template` to create the repo for your action. + +![template](assets/node12-template.png) + +Complete creating your repo and clone the repo. + +> NOTE: The location of the repo will be how users will reference your action in their workflow file with the using keyword. + +e.g. To use https://github.com/actions/setup-node, users will author: + +```yaml +steps: + using: actions/setup-node@v3 +``` + +# Define Metadata + +Your action has a name and a description. Update the author. + +Create inputs that your unit of work will need. These will be what workflow authors set with the `with:` keyword. + +```yaml +name: 'My Container Action' +description: 'Get started with Container actions' +author: 'GitHub' +inputs: + myInput: + description: 'Input to use' + default: 'world' +runs: + using: 'docker' + image: 'Dockerfile' + args: + - ${{ inputs.myInput }} +``` + +It will be run with docker and the input is mapped into the args + +# Change Code + +The entry point is in entrypoint.sh + +```bash +#!/bin/sh -l + +echo "hello $1" +``` + +# Publish + +Simply push your action to publish. + +```bash +$ git push +``` + +The runner will download the action and build the docker container on the fly at runtime. + +> Consider versioning your actions with tags. See [versioning](/docs/action-versioning.md) + + + + diff --git a/actions-toolkit/docs/github-package.md b/actions-toolkit/docs/github-package.md new file mode 100644 index 00000000..7164bf47 --- /dev/null +++ b/actions-toolkit/docs/github-package.md @@ -0,0 +1,288 @@ +# Creating an Action using the GitHub Context + +## Goal + +In this walkthrough we will learn how to build a basic action using GitHub context data to greet users when they open an issue or PR. In the process we will explore how to access this context and how to make authenticated requests to the GitHub API. + +Note that a complete version of this action can be found at https://github.com/damccorm/issue-greeter. + +## Prerequisites + +This walkthrough assumes that you have gone through the basic [javascript action walkthrough](https://github.com/actions/javascript-action) and have a basic action set up. If not, we recommend you go through that first. + +## Installing dependencies + +All of the dependencies we need should come packaged for us in this library's core and github packages. To install, run the following in your action: + +`npm install @actions/core && npm install @actions/github` + +## Metadata + +Next, we will need a welcome message and a repo token as an input. Recall that inputs are defined in the `action.yml` metadata file - update your `action.yml` file to define `welcome-message` and `repo-token` as inputs. + +```yaml +name: "Welcome" +description: "A basic welcome action" +author: "GitHub" +inputs: + welcome-message: + description: "Message to display when a user opens an issue or PR" + default: "Thanks for opening an issue! Make sure you've followed CONTRIBUTING.md" + repo-token: + description: "Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}" + required: true +runs: + using: "node12" + main: "lib/main.js" +``` + +## Action logic + +Now that we've installed our dependencies and defined our inputs, we're ready to start writing the action logic in `src/main.ts`! For clarity, we'll structure our action up as follows: + +```ts +import * as core from '@actions/core'; +import * as github from '@actions/github'; + +export async function run() { + try { + const welcomeMessage: string = core.getInput('welcome-message'); + // TODO - Get context data + + // TODO - make request to the GitHub API to comment on the issue + } + catch (error) { + core.setFailed(error.message); + throw error; + } +} + +run(); +``` + +### Getting context data + +For the purpose of this walkthrough, we will need the following pieces of context data: + +- the name of the repo that the action is being run on +- the organization/owner of that repo +- the number of the issue that has been opened + +Fortunately, the GitHub package provides all of this to us with [a single convenience function](https://github.com/actions/toolkit/blob/ac007c06984bc483fae2ba649788dfc858bc6a8b/packages/github/src/context.ts#L34), so we can simply do: + +`const issue: {owner: string; repo: string; number: number} = github.context.issue;` + +The context object also contains a number of easily accessed properties, as well as easy access to the full [GitHub payload](https://developer.github.com/v3/activity/events/types/). We can use this to check and make sure we're actually looking at a recently opened issue (and not something else, like a comment on an existing issue): + +```ts +if (github.context.payload.action !== 'opened') { + console.log('No issue or PR was opened, skipping'); + return; +} +``` + +Our whole `src/main.ts` file now looks like: + +```ts +import * as core from '@actions/core'; +import * as github from '@actions/github'; + +export async function run() { + try { + const welcomeMessage: string = core.getInput('welcome-message', {required: true}); + const repoToken: string = core.getInput('repo-token', {required: true}); + const issue: {owner: string; repo: string; number: number} = github.context.issue; + + if (github.context.payload.action !== 'opened') { + console.log('No issue or pull request was opened, skipping'); + return; + } + + // TODO - make request to the GitHub API to comment on the issue + } + catch (error) { + core.setFailed(error.message); + throw error; + } +} + +run(); +``` + +### Sending requests to the GitHub API + +Now that we have our context data, we are able to send a request to the GitHub API using the [Octokit REST client](https://github.com/octokit/rest.js). The REST client exposes a number of easy convenience functions, including one for adding comments to issues/PRs (issues and PRs are treated as one concept by the Octokit client): + +```ts +const client: github.GitHub = new github.GitHub(repoToken); +await client.issues.createComment({ + owner: issue.owner, + repo: issue.repo, + issue_number: issue.number, + body: welcomeMessage +}); +``` + +For more docs on the client, you can visit the [Octokit REST documentation](https://octokit.github.io/rest.js/). Now our action code should be complete: + +```ts +import * as core from '@actions/core'; +import * as github from '@actions/github'; + +export async function run() { + try { + const welcomeMessage: string = core.getInput('welcome-message', {required: true}); + const repoToken: string = core.getInput('repo-token', {required: true}); + const issue: {owner: string; repo: string; number: number} = github.context.issue; + + if (github.context.payload.action !== 'opened') { + console.log('No issue or pull request was opened, skipping'); + return; + } + + const client: github.GitHub = new github.GitHub(repoToken); + await client.issues.createComment({ + owner: issue.owner, + repo: issue.repo, + issue_number: issue.number, + body: welcomeMessage + }); + } + catch (error) { + core.setFailed(error.message); + throw error; + } +} + +run(); +``` + +## Writing unit tests for your action + +Next, we're going to write a basic unit test for our action using jest. If you followed the [javascript walkthrough](https://github.com/actions/javascript-action), you should have a file `__tests__/main.test.ts` that runs tests when `npm test` is called. We're going to start by populating that with one test: + +```ts +const nock = require('nock'); +const path = require('path'); + +describe('action test suite', () => { + it('It posts a comment on an opened issue', async () => { + // TODO + }); +}); +``` + +For the purposes of this walkthrough, we'll focus on populating this test and leave the remaining test coverage as an exercise for the reader. + +### Mocking inputs + +First, we want to make sure that we can mock our inputs (welcome-message, and repo-token). Actions handles inputs by populating process.env.INPUT_${input name in all caps}, so we can mock that simply by setting those environment variables: + +```ts +const nock = require('nock'); +const path = require('path'); + +describe('action test suite', () => { + it('It posts a comment on an opened issue', async () => { + const welcomeMessage = 'hello'; + const repoToken = 'token'; + process.env['INPUT_WELCOME-MESSAGE'] = welcomeMessage; + process.env['INPUT_REPO-TOKEN'] = repoToken; + + // TODO + }); +}); +``` + +### Mocking the GitHub context + +Mocking the GitHub context is relatively straightforward. Since most of it is simply populated by environment variables, you can just set the corresponding environment variables defined [here](https://github.com/actions/toolkit/blob/ac007c06984bc483fae2ba649788dfc858bc6a8b/packages/github/src/context.ts#L23) and test that it works in that environment. In this case, we can setup our test with: + +```ts +const nock = require('nock'); +const path = require('path'); + +describe('action test suite', () => { + it('It posts a comment on an opened issue', async () => { + const welcomeMessage = 'hello'; + const repoToken = 'token'; + process.env['INPUT_WELCOME-MESSAGE'] = welcomeMessage; + process.env['INPUT_REPO-TOKEN'] = repoToken; + + process.env['GITHUB_REPOSITORY'] = 'foo/bar'; + process.env['GITHUB_EVENT_PATH'] = path.join(__dirname, 'payload.json'); + + // TODO + }); +}); +``` + +Note that the payload is loaded from GITHUB_EVENT_PATH. Since we set that to `path.join(__dirname, 'payload.json')`, we need to go save our payload there. For the purposes of this test, we can simply save the following to `__tests__/payload.json`: + +```json +{ + "issue": { + "number": 10 + }, + "action": "opened" +} +``` + +Now, calling `github.context.issue` should return `{owner: foo, repo: bar, number: 10}`, and `github.context.payload.action` should get set to 'opened' + +> One important detail here is that because the GitHub context loads these environment variables as soon as it is required, you should set them before you require your action. In most cases, this means you need to rerequire your action in every test. If this is a problem, you can get around it by mocking the class directly using jest (or whatever framework you choose). + +### Mocking the Octokit Client + +To mock the client calls, we recommend using [nock](https://github.com/nock/nock) which allows you to mock the http requests made by the client. First, install nock with `npm install nock --save-dev`. + +For this test, we expect the following call: + +```ts +client.issues.createComment({ + owner: 'foo', + repo: 'bar', + issue_number: 10, + body: 'you posted your first issue' +}); +``` + +From [the GitHub endpoint docs](https://developer.github.com/v3/issues/comments/#create-a-comment), we expect this to get make a POST request to `https://api.github.com/repos/foo/bar/issues/10/comments` with body of `{"body":"hello"}` + +We can mock this with: + +```ts +const nock = require('nock'); +const path = require('path'); + +describe('action test suite', () => { + it('It posts a comment on an opened issue', async () => { + const welcomeMessage = 'hello'; + const repoToken = 'token'; + process.env['INPUT_WELCOME-MESSAGE'] = welcomeMessage; + process.env['INPUT_REPO-TOKEN'] = repoToken; + + process.env['GITHUB_REPOSITORY'] = 'foo/bar'; + process.env['GITHUB_EVENT_PATH'] = path.join(__dirname, 'payload.json'); + + nock('https://api.github.com') + .persist() + .post('/repos/foo/bar/issues/10/comments', '{\"body\":\"hello\"}') + .reply(200); + + const main = require('../src/main'); + + await main.run(); + }); +}); +``` + +This will fail if the url or body doesn't exactly match the parameters passed into the nock function. We can now run `npm test` and the test should succeed. + +## Build and publish + +Now that we've written and unit tested our action, we can build our action with `npm run build` and push it to a repo where it can be consumed by workflows. For more info on versioning your action, see [our versioning docs](./action-versioning.md). + +## Next steps + +If you're interested in building out this action further, try extending your action to only run on a user's first issue. See our [first-contribution action](https://github.com/actions/first-interaction) for inspiration. diff --git a/actions-toolkit/docs/problem-matchers.md b/actions-toolkit/docs/problem-matchers.md new file mode 100644 index 00000000..e496b299 --- /dev/null +++ b/actions-toolkit/docs/problem-matchers.md @@ -0,0 +1,149 @@ +# Problem Matchers + +Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. Both [GitHub Annotations](https://developer.github.com/v3/checks/runs/#annotations-object-1) and log file decorations are created when a match is detected. + +## Limitations + +Currently, GitHub Actions limit the annotation count in a workflow run. + +- 10 warning annotations, 10 error annotations, and 10 notice annotations per step +- 50 annotations per job (sum of annotations from all the steps) +- 50 annotations per run (separate from the job annotations, these annotations aren’t created by users) + +If your workflow may exceed these annotation counts, consider filtering of the log messages which the Problem Matcher is exposed to (e.g. by PR touched files, lines, or other). + +## Single Line Matchers + +Let's consider the ESLint compact output: + +``` +badFile.js: line 50, col 11, Error - 'myVar' is defined but never used. (no-unused-vars) +``` + +We can define a problem matcher in json that detects input in that format: + +```json +{ + "problemMatcher": [ + { + "owner": "eslint-compact", + "pattern": [ + { + "regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + "code": 6 + } + ] + } + ] +} +``` + +The following fields are available for problem matchers: + +``` +{ + owner: an ID field that can be used to remove or replace the problem matcher. **required** + severity: indicates the default severity, either 'warning' or 'error' case-insensitive. Defaults to 'error' + pattern: [ + { + regexp: the regex pattern that provides the groups to match against **required** + file: a group number containing the file name + fromPath: a group number containing a filepath used to root the file (e.g. a project file) + line: a group number containing the line number + column: a group number containing the column information + severity: a group number containing either 'warning' or 'error' case-insensitive. Defaults to `error` + code: a group number containing the error code + message: a group number containing the error message. **required** at least one pattern must set the message + loop: whether to loop until a match is not found, only valid on the last pattern of a multipattern matcher + } + ] +} +``` + +## Multiline Matching +Consider the following output: + +``` +test.js + 1:0 error Missing "use strict" statement strict + 5:10 error 'addOne' is defined but never used no-unused-vars +✖ 2 problems (2 errors, 0 warnings) +``` + +The file name is printed once, yet multiple error lines are printed. The `loop` keyword provides a way to discover multiple errors in outputs. + +The eslint-stylish problem matcher defined below catches that output, and creates two annotations from it. + +``` +{ + "problemMatcher": [ + { + "owner": "eslint-stylish", + "pattern": [ + { + // Matches the 1st line in the output + "regexp": "^([^\\s].*)$", + "file": 1 + }, + { + // Matches the 2nd and 3rd line in the output + "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$", + // File is carried through from above, so we define the rest of the groups + "line": 1, + "column": 2, + "severity": 3, + "message": 4, + "code": 5, + "loop": true + } + ] + } + ] +} +``` + +The first pattern matches the `test.js` line and records the file information. This line is not decorated in the UI. +The second pattern loops through the remaining lines with `loop: true` until it fails to find a match, and surfaces these lines prominently in the UI. + +Note that the pattern matches must be on consecutive lines. The following would not result in any match findings. + +``` +test.js + extraneous log line of no interest + 1:0 error Missing "use strict" statement strict + 5:10 error 'addOne' is defined but never used no-unused-vars +✖ 2 problems (2 errors, 0 warnings) +``` + +## Adding and Removing Problem Matchers + +Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers). + +## Duplicate Problem Matchers + +Registering two problem-matchers with the same owner will result in only the problem matcher registered last running. + +## Examples + +Some of the starter actions are already using problem matchers, for example: +- [setup-node](https://github.com/actions/setup-node/tree/main/.github) +- [setup-python](https://github.com/actions/setup-python/tree/main/.github) +- [setup-go](https://github.com/actions/setup-go/tree/main/.github) +- [setup-dotnet](https://github.com/actions/setup-dotnet/tree/main/.github) + +## Troubleshooting + +### Regular expression not matching + +Use ECMAScript regular expression syntax when testing patterns. + +### File property getting dropped + +[Enable debug logging](https://docs.github.com/en/actions/managing-workflow-runs/enabling-debug-logging) to determine why the file is getting dropped. + +This usually happens when the file does not exist or is not under the workflow repo. diff --git a/actions-toolkit/docs/proxy-support.md b/actions-toolkit/docs/proxy-support.md new file mode 100644 index 00000000..62d1eaf9 --- /dev/null +++ b/actions-toolkit/docs/proxy-support.md @@ -0,0 +1,10 @@ +# Proxy Server Support + +Self-hosted runners [can be configured](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners) to run behind a proxy server in enterprises. + +For actions to **just work** behind a proxy server: + + 1. Use [tool-cache](/packages/tool-cache) version >= 1.3.1 + 2. Optionally use [actions/http-client](/packages/http-client) + +If you are using other http clients, refer to the [environment variables set by the runner](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners). diff --git a/actions-toolkit/docs/specs/github-package.md b/actions-toolkit/docs/specs/github-package.md new file mode 100644 index 00000000..8d5cac29 --- /dev/null +++ b/actions-toolkit/docs/specs/github-package.md @@ -0,0 +1,100 @@ +# Github Package + +In order to support using actions to interact with GitHub, I propose adding a `github` package to the toolkit. + +Its main purpose will be to provide a hydrated GitHub context/Octokit client with some convenience functions. It is largely pulled from the GitHub utilities provided in https://github.com/JasonEtco/actions-toolkit, though it has been condensed. + +### Spec + +##### interfaces.ts + +```ts +/* + * Interfaces + */ + +export interface PayloadRepository { + [key: string]: any + full_name?: string + name: string + owner: { + [key: string]: any + login: string + name?: string + } + html_url?: string +} + +export interface WebhookPayloadWithRepository { + [key: string]: any + repository?: PayloadRepository + issue?: { + [key: string]: any + number: number + html_url?: string + body?: string + } + pull_request?: { + [key: string]: any + number: number + html_url?: string + body?: string + } + sender?: { + [key: string]: any + type: string + } + action?: string + installation?: { + id: number + [key: string]: any + } +} +``` + +##### context.ts + +Contains a GitHub context + +```ts +export class Context { + /** + * Webhook payload object that triggered the workflow + */ + public payload: WebhookPayloadWithRepository + + /** + * Name of the event that triggered the workflow + */ + public event: string + public sha: string + public ref: string + public workflow: string + public action: string + public actor: string + + /** + * Hydrate the context from the environment + */ + constructor () + + public get issue () + + public get repo () +} + +``` + +##### github.ts + +Contains a hydrated Octokit client + +```ts +export class GithubClient extends Octokit { + // For making GraphQL requests + public graphql: (query: string, variables?: Variables) => Promise + + // Calls super and initializes graphql + constructor (token: string) +} +``` diff --git a/actions-toolkit/docs/specs/package-specs.md b/actions-toolkit/docs/specs/package-specs.md new file mode 100644 index 00000000..023c7e85 --- /dev/null +++ b/actions-toolkit/docs/specs/package-specs.md @@ -0,0 +1,203 @@ +# Package Specs + +In order to support the node-config action, I propose adding the following into 4 libraries (tool-cache, core, io, exec), with tool-cache having dependencies on the other 3 libs: + +### Core spec + +Holds all the functions necessary for interacting with the runner/environment. + +```ts +// Logging functions +export function debug(message: string): void +export function warning(message: string): void +export function error(message: string): void + +/** + * sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable + */ +export function exportVariable(name: string, val: string): void + +/** + * exports the variable and registers a secret which will get masked from logs + * @param name the name of the variable to set + * @param val value of the secret + */ +export function exportSecret(name: string, val: string): void + +/** + * Prepends inputPath to the PATH + * @param inputPath + */ +export function addPath(inputPath: string): void + +/** + * Interface for getInput options + */ +export interface InputOptions { + /** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */ + required?: bool; +} + +/** + * Gets the value of an input. The value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +export function getInput(name: string, options?: InputOptions): string | undefined + +/** + * sets the status of the action to neutral + * @param message + */ +export function setNeutral(message: string): void + +/** + * sets the status of the action to failed + * @param message + */ +export function setFailed(message: string): void +``` + +### IO spec + +Holds all the functions necessary for file system manipulation (cli scenarios, not fs replacements): + +```ts +/** + * Interface for cp/mv options + */ +export interface CopyOptions { + /** Optional. Whether to recursively copy all subdirectories. Defaults to false */ + recursive?: boolean; + /** Optional. Whether to overwrite existing files in the destination. Defaults to true */ + force?: boolean; +} + +/** + * Copies a file or folder. + * + * @param source source path + * @param dest destination path + * @param options optional. See CopyOptions. + */ +export function cp(source: string, dest: string, options?: CopyOptions): Promise + +/** + * Remove a path recursively with force + * + * @param path path to remove + */ +export function rmRF(path: string): Promise + +/** + * Make a directory. Creates the full path with folders in between + * + * @param p path to create + * @returns Promise + */ +export function mkdirP(p: string): Promise + +/** + * Moves a path. + * + * @param source source path + * @param dest destination path + * @param options optional. See CopyOptions. + */ +export function mv(source: string, dest: string, options?: CopyOptions): Promise + +/** + * Interface for which options + */ +export interface WhichOptions { + /** Optional. Whether to check if tool exists. If true, will throw if it fails. Defaults to false */ + check?: boolean; +} + +/** + * Returns path of a tool had the tool actually been invoked. Resolves via paths. + * + * @param tool name of the tool + * @param options optional. See WhichOptions. + * @returns Promise path to tool + */ +export function which(tool: string, options?: WhichOptions): Promise +``` + +### Exec spec + +Holds all the functions necessary for running the tools node-config depends on (aka 7-zip and tar) + +```ts +/** + * Interface for exec options + */ +export interface IExecOptions + +/** +* Exec a command. +* Output will be streamed to the live console. +* Returns promise with return code +* +* @param commandLine command to execute +* @param args optional additional arguments +* @param options optional exec options. See IExecOptions +* @returns Promise return code +*/ +export function exec(commandLine: string, args?: string[], options?: IExecOptions): Promise +``` + +### Tool-Cache spec: + +Holds all the functions necessary for downloading and caching node. + +```ts +/** + * Download a tool from an url and stream it into a file + * + * @param url url of tool to download + * @returns path to downloaded tool + */ +export async function downloadTool(url: string): Promise + +/** + * Extract a .7z file + * + * @param file path to the .7z file + * @param dest destination directory. Optional. + * @returns path to the destination directory + */ +export async function extract7z(file: string, dest?: string): Promise + +/** + * Extract a tar + * + * @param file path to the tar + * @param dest destination directory. Optional. + * @returns path to the destination directory + */ +export async function extractTar(file: string, destination?: string): Promise + +/** + * Caches a directory and installs it into the tool cacheDir + * + * @param sourceDir the directory to cache into tools + * @param tool tool name + * @param version version of the tool. semver format + * @param arch architecture of the tool. Optional. Defaults to machine architecture + */ +export async function cacheDir(sourceDir: string, tool: string, version: string, arch?: string): Promise + +/** + * finds the path to a tool in the local installed tool cache + * + * @param toolName name of the tool + * @param versionSpec version of the tool + * @param arch optional arch. defaults to arch of computer + */ +export function find(toolName: string, versionSpec: string, arch?: string): string +``` diff --git a/actions-toolkit/jest.config.js b/actions-toolkit/jest.config.js new file mode 100644 index 00000000..101666d6 --- /dev/null +++ b/actions-toolkit/jest.config.js @@ -0,0 +1,11 @@ +module.exports = { + clearMocks: true, + moduleFileExtensions: ['js', 'ts'], + roots: ['/packages'], + testEnvironment: 'node', + testMatch: ['**/__tests__/*.test.ts'], + transform: { + '^.+\\.ts$': 'ts-jest' + }, + verbose: true +} \ No newline at end of file diff --git a/actions-toolkit/lerna.json b/actions-toolkit/lerna.json new file mode 100644 index 00000000..4538e507 --- /dev/null +++ b/actions-toolkit/lerna.json @@ -0,0 +1,6 @@ +{ + "packages": [ + "packages/**/*" + ], + "version": "independent" +} \ No newline at end of file diff --git a/actions-toolkit/nx.json b/actions-toolkit/nx.json new file mode 100644 index 00000000..43711cc2 --- /dev/null +++ b/actions-toolkit/nx.json @@ -0,0 +1,24 @@ +{ + "tasksRunnerOptions": { + "default": { + "runner": "nx/tasks-runners/default", + "options": { + "cacheableOperations": [] + } + } + }, + "affected": { + "defaultBase": "master" + }, + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "namedInputs": { + "default": [ + "{projectRoot}/**/*", + "sharedGlobals" + ], + "sharedGlobals": [], + "production": [ + "default" + ] + } +} \ No newline at end of file diff --git a/actions-toolkit/package-lock.json b/actions-toolkit/package-lock.json new file mode 100644 index 00000000..e72dedbd --- /dev/null +++ b/actions-toolkit/package-lock.json @@ -0,0 +1,12992 @@ +{ + "name": "root", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "root", + "devDependencies": { + "@types/jest": "^29.5.4", + "@types/node": "^20.5.7", + "@types/signale": "^1.4.1", + "concurrently": "^6.1.0", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.9.0", + "eslint-plugin-github": "^4.9.2", + "eslint-plugin-jest": "^27.2.3", + "eslint-plugin-prettier": "^5.0.0", + "flow-bin": "^0.115.0", + "jest": "^29.6.4", + "lerna": "^7.1.4", + "nx": "16.6.0", + "prettier": "^3.0.0", + "ts-jest": "^29.1.1", + "typescript": "^5.2.2" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", + "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.11", + "@babel/parser": "^7.22.11", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.11", + "@babel/types": "^7.22.11", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", + "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.11", + "@babel/types": "^7.22.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@github/browserslist-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@github/browserslist-config/-/browserslist-config-1.0.0.tgz", + "integrity": "sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==", + "dev": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "dev": true, + "dependencies": { + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.4", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lerna/child-process": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-7.2.0.tgz", + "integrity": "sha512-8cRsYYX8rGZTXL1KcLBv0RHD9PMvphWZay8yg4qf2giX6x86dQyTetSU4SplG2LBGVClilmNHJa/CQwvPQNUFA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "execa": "^5.0.0", + "strong-log-transformer": "^2.1.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + } + }, + "node_modules/@lerna/create": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-7.2.0.tgz", + "integrity": "sha512-bBypNfwqOQNcfR2nXJ3mWUeIAIoSFpXg8MjuFSf87PzIiyeTEKa3Z57vAa3bDbHQtcB7x6f0rWysK1eQZSH15Q==", + "dev": true, + "dependencies": { + "@lerna/child-process": "7.2.0", + "@npmcli/run-script": "6.0.2", + "@nx/devkit": ">=16.5.1 < 17", + "@octokit/plugin-enterprise-rest": "6.0.1", + "@octokit/rest": "19.0.11", + "byte-size": "8.1.1", + "chalk": "4.1.0", + "clone-deep": "4.0.1", + "cmd-shim": "6.0.1", + "columnify": "1.6.0", + "conventional-changelog-core": "5.0.1", + "conventional-recommended-bump": "7.0.1", + "cosmiconfig": "^8.2.0", + "dedent": "0.7.0", + "execa": "5.0.0", + "fs-extra": "^11.1.1", + "get-stream": "6.0.0", + "git-url-parse": "13.1.0", + "glob-parent": "5.1.2", + "globby": "11.1.0", + "graceful-fs": "4.2.11", + "has-unicode": "2.0.1", + "ini": "^1.3.8", + "init-package-json": "5.0.0", + "inquirer": "^8.2.4", + "is-ci": "3.0.1", + "is-stream": "2.0.0", + "js-yaml": "4.1.0", + "libnpmpublish": "7.3.0", + "load-json-file": "6.2.0", + "lodash": "^4.17.21", + "make-dir": "3.1.0", + "minimatch": "3.0.5", + "multimatch": "5.0.0", + "node-fetch": "2.6.7", + "npm-package-arg": "8.1.1", + "npm-packlist": "5.1.1", + "npm-registry-fetch": "^14.0.5", + "npmlog": "^6.0.2", + "nx": ">=16.5.1 < 17", + "p-map": "4.0.0", + "p-map-series": "2.1.0", + "p-queue": "6.6.2", + "p-reduce": "^2.1.0", + "pacote": "^15.2.0", + "pify": "5.0.0", + "read-cmd-shim": "4.0.0", + "read-package-json": "6.0.4", + "resolve-from": "5.0.0", + "rimraf": "^4.4.1", + "semver": "^7.3.4", + "signal-exit": "3.0.7", + "slash": "^3.0.0", + "ssri": "^9.0.1", + "strong-log-transformer": "2.1.0", + "tar": "6.1.11", + "temp-dir": "1.0.0", + "upath": "2.0.1", + "uuid": "^9.0.0", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "5.0.0", + "write-file-atomic": "5.0.1", + "write-pkg": "4.0.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + } + }, + "node_modules/@lerna/create/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@lerna/create/node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/@lerna/create/node_modules/execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@lerna/create/node_modules/get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@lerna/create/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@lerna/create/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@lerna/create/node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@lerna/create/node_modules/glob/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@lerna/create/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/create/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@lerna/create/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@lerna/create/node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@lerna/create/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/create/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/create/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@lerna/create/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/create/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/create/node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", + "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz", + "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==", + "dev": true, + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz", + "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", + "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "dev": true, + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", + "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@nrwl/devkit": { + "version": "16.7.4", + "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-16.7.4.tgz", + "integrity": "sha512-Gt2q3cqDWzGP1woavGIo4bl8g9YaXic/Xfsl7qPq0LHJedLj49p1vXetB0wawkavSE2MTyo7yDh6YDK/38XoLw==", + "dev": true, + "dependencies": { + "@nx/devkit": "16.7.4" + } + }, + "node_modules/@nrwl/tao": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-16.6.0.tgz", + "integrity": "sha512-NQkDhmzlR1wMuYzzpl4XrKTYgyIzELdJ+dVrNKf4+p4z5WwKGucgRBj60xMQ3kdV25IX95/fmMDB8qVp/pNQ0Q==", + "dev": true, + "dependencies": { + "nx": "16.6.0", + "tslib": "^2.3.0" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nrwl/tao/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/@nx/devkit": { + "version": "16.7.4", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-16.7.4.tgz", + "integrity": "sha512-SLito+/TAeDYR+d7IIpp/sBJm41WM+nIevILv0TSQW4Pq0ylUy1nUvV8Pe7l1ohZccDrQuebMUWPwGO0hv8SeQ==", + "dev": true, + "dependencies": { + "@nrwl/devkit": "16.7.4", + "ejs": "^3.1.7", + "enquirer": "~2.3.6", + "ignore": "^5.0.4", + "semver": "7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "nx": ">= 15 <= 17" + } + }, + "node_modules/@nx/devkit/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/devkit/node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/devkit/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/@nx/devkit/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/@nx/devkit/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@nx/nx-darwin-arm64": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.6.0.tgz", + "integrity": "sha512-8nJuqcWG/Ob39rebgPLpv2h/V46b9Rqqm/AGH+bYV9fNJpxgMXclyincbMIWvfYN2tW+Vb9DusiTxV6RPrLapA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-darwin-x64": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-16.6.0.tgz", + "integrity": "sha512-T4DV0/2PkPZjzjmsmQEyjPDNBEKc4Rhf7mbIZlsHXj27BPoeNjEcbjtXKuOZHZDIpGFYECGT/sAF6C2NVYgmxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-freebsd-x64": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.6.0.tgz", + "integrity": "sha512-Ck/yejYgp65dH9pbExKN/X0m22+xS3rWF1DBr2LkP6j1zJaweRc3dT83BWgt5mCjmcmZVk3J8N01AxULAzUAqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-arm-gnueabihf": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.6.0.tgz", + "integrity": "sha512-eyk/R1mBQ3X0PCSS+Cck3onvr3wmZVmM/+x0x9Ai02Vm6q9Eq6oZ1YtZGQsklNIyw1vk2WV9rJCStfu9mLecEw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-arm64-gnu": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.6.0.tgz", + "integrity": "sha512-S0qFFdQFDmBIEZqBAJl4K47V3YuMvDvthbYE0enXrXApWgDApmhtxINXSOjSus7DNq9kMrgtSDGkBmoBot61iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-arm64-musl": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.6.0.tgz", + "integrity": "sha512-TXWY5VYtg2wX/LWxyrUkDVpqCyJHF7fWoVMUSlFe+XQnk9wp/yIbq2s0k3h8I4biYb6AgtcVqbR4ID86lSNuMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.6.0.tgz", + "integrity": "sha512-qQIpSVN8Ij4oOJ5v+U+YztWJ3YQkeCIevr4RdCE9rDilfq9RmBD94L4VDm7NRzYBuQL8uQxqWzGqb7ZW4mfHpw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-x64-musl": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.6.0.tgz", + "integrity": "sha512-EYOHe11lfVfEfZqSAIa1c39mx2Obr4mqd36dBZx+0UKhjrcmWiOdsIVYMQSb3n0TqB33BprjI4p9ZcFSDuoNbA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-win32-arm64-msvc": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.6.0.tgz", + "integrity": "sha512-f1BmuirOrsAGh5+h/utkAWNuqgohvBoekQgMxYcyJxSkFN+pxNG1U68P59Cidn0h9mkyonxGVCBvWwJa3svVFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.6.0.tgz", + "integrity": "sha512-UmTTjFLpv4poVZE3RdUHianU8/O9zZYBiAnTRq5spwSDwxJHnLTZBUxFFf3ztCxeHOUIfSyW9utpGfCMCptzvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@octokit/auth-token": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", + "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/core": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", + "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/endpoint": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", + "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "dev": true, + "dependencies": { + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/graphql": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", + "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "dev": true, + "dependencies": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^9.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", + "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==", + "dev": true + }, + "node_modules/@octokit/plugin-enterprise-rest": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz", + "integrity": "sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", + "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "dev": true, + "dependencies": { + "@octokit/tsconfig": "^1.0.2", + "@octokit/types": "^9.2.3" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=4" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz", + "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==", + "dev": true, + "dependencies": { + "@octokit/types": "^10.0.0" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz", + "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", + "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/request-error": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", + "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^9.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/rest": { + "version": "19.0.11", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.11.tgz", + "integrity": "sha512-m2a9VhaP5/tUw8FwfnW2ICXlXpLPIqxtg3XcAiGMLj/Xhw3RSBfZ8le/466ktO1Gcjr8oXudGnHhxV1TXJgFxw==", + "dev": true, + "dependencies": { + "@octokit/core": "^4.2.1", + "@octokit/plugin-paginate-rest": "^6.1.2", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@octokit/tsconfig": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", + "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", + "dev": true + }, + "node_modules/@octokit/types": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", + "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.4.tgz", + "integrity": "sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pkgr/utils/node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", + "dev": true + }, + "node_modules/@sigstore/bundle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.0.0.tgz", + "integrity": "sha512-yLvrWDOh6uMOUlFCTJIZEnwOT9Xte7NPXUqVexEKGSF5XtBAuSg5du0kn3dRR0p47a4ah10Y0mNt8+uyeQXrBQ==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.0.tgz", + "integrity": "sha512-8ZhZKAVfXjIspDWwm3D3Kvj0ddbJ0HqDZ/pOs5cx88HpT8mVsotFrg7H1UMnXOuDHz6Zykwxn4mxG3QLuN+RUg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", + "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0", + "tuf-js": "^1.1.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", + "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", + "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", + "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", + "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/signale": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@types/signale/-/signale-1.4.4.tgz", + "integrity": "sha512-VYy4VL64gA4uyUIYVj4tiGFF0VpdnRbJeqNENKGX42toNiTvt83rRzxdr0XK4DR3V01zPM0JQNIsL+IwWWfhsQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.2.1.tgz", + "integrity": "sha512-iZVM/ALid9kO0+I81pnp1xmYiFyqibAHzrqX4q5YvvVEyJqY+e6rfTXSCsc2jUxGNqJqTfFSSij/NFkZBiBzLw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.2.1", + "@typescript-eslint/type-utils": "6.2.1", + "@typescript-eslint/utils": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.1.tgz", + "integrity": "sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.2.1", + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/typescript-estree": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.1.tgz", + "integrity": "sha512-UCqBF9WFqv64xNsIEPfBtenbfodPXsJ3nPAr55mGPkQIkiQvgoWNo+astj9ZUfJfVKiYgAZDMnM6dIpsxUMp3Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.2.1.tgz", + "integrity": "sha512-fTfCgomBMIgu2Dh2Or3gMYgoNAnQm3RLtRp+jP7A8fY+LJ2+9PNpi5p6QB5C4RSP+U3cjI0vDlI3mspAkpPVbQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.2.1", + "@typescript-eslint/utils": "6.2.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.1.tgz", + "integrity": "sha512-528bGcoelrpw+sETlyM91k51Arl2ajbNT9L4JwoXE2dvRe1yd8Q64E4OL7vHYw31mlnVsf+BeeLyAZUEQtqahQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.1.tgz", + "integrity": "sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.2.1.tgz", + "integrity": "sha512-eBIXQeupYmxVB6S7x+B9SdBeB6qIdXKjgQBge2J+Ouv8h9Cxm5dHf/gfAZA6dkMaag+03HdbVInuXMmqFB/lKQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.2.1", + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/typescript-estree": "6.2.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.1.tgz", + "integrity": "sha512-iTN6w3k2JEZ7cyVdZJTVJx2Lv7t6zFA8DCrJEHD2mwfc16AEvvBWVhbFh34XyG2NORCd0viIgQY1+u7kPI0WpA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.2.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.46", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", + "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", + "dev": true, + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", + "dev": true + }, + "node_modules/@zkochan/js-yaml": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", + "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", + "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", + "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/babel-jest": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.6.4", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/byte-size": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-8.1.1.tgz", + "integrity": "sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==", + "dev": true, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/cacache": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", + "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/ssri": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", + "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001518", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz", + "integrity": "sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cmd-shim": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.1.tgz", + "integrity": "sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/columnify": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", + "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", + "dev": true, + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/concurrently/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/conventional-changelog-angular": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-5.0.1.tgz", + "integrity": "sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==", + "dev": true, + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^6.0.0", + "conventional-commits-parser": "^4.0.0", + "dateformat": "^3.0.3", + "get-pkg-repo": "^4.2.1", + "git-raw-commits": "^3.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^5.0.0", + "normalize-package-data": "^3.0.3", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-3.0.0.tgz", + "integrity": "sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-6.0.1.tgz", + "integrity": "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==", + "dev": true, + "dependencies": { + "conventional-commits-filter": "^3.0.0", + "dateformat": "^3.0.3", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^8.1.2", + "semver": "^7.0.0", + "split": "^1.0.1" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-commits-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", + "integrity": "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==", + "dev": true, + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-commits-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", + "dev": true, + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/conventional-recommended-bump": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-7.0.1.tgz", + "integrity": "sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==", + "dev": true, + "dependencies": { + "concat-stream": "^2.0.0", + "conventional-changelog-preset-loader": "^3.0.0", + "conventional-commits-filter": "^3.0.0", + "conventional-commits-parser": "^4.0.0", + "git-raw-commits": "^3.0.0", + "git-semver-tags": "^5.0.0", + "meow": "^8.1.2" + }, + "bin": { + "conventional-recommended-bump": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/execa": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/default-browser/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/default-browser/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.479", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.479.tgz", + "integrity": "sha512-ABv1nHMIR8I5n3O3Een0gr6i0mfM+YcTZqjHy3pAYaOjgFG+BMquuKrSyfYf5CbEkLr9uM05RA3pOk4udNB/aQ==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-escompat": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz", + "integrity": "sha512-ufTPv8cwCxTNoLnTZBFTQ5SxU2w7E7wiMIS7PSxsgP1eAxFjtSaoZ80LRn64hI8iYziE6kJG6gX/ZCJVxh48Bg==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.0" + }, + "peerDependencies": { + "eslint": ">=5.14.1" + } + }, + "node_modules/eslint-plugin-eslint-comments": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", + "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5", + "ignore": "^5.0.5" + }, + "engines": { + "node": ">=6.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-plugin-filenames": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", + "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", + "dev": true, + "dependencies": { + "lodash.camelcase": "4.3.0", + "lodash.kebabcase": "4.1.1", + "lodash.snakecase": "4.1.1", + "lodash.upperfirst": "4.3.1" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/eslint-plugin-github": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.0.tgz", + "integrity": "sha512-YKtqBtFbjih1wZNTwZjtLPEG6B/4ySMa38fgOo/rbMJpNKO3+OaKzwwOYkeKx/FapM/4MsTP9ExqUcDV+dkixA==", + "dev": true, + "dependencies": { + "@github/browserslist-config": "^1.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "aria-query": "^5.3.0", + "eslint-config-prettier": ">=8.0.0", + "eslint-plugin-escompat": "^3.3.3", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-filenames": "^1.3.2", + "eslint-plugin-i18n-text": "^1.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-no-only-tests": "^3.0.0", + "eslint-plugin-prettier": "^5.0.0", + "eslint-rule-documentation": ">=1.0.0", + "jsx-ast-utils": "^3.3.2", + "prettier": "^3.0.0", + "svg-element-attributes": "^1.3.1" + }, + "bin": { + "eslint-ignore-errors": "bin/eslint-ignore-errors.js" + }, + "peerDependencies": { + "eslint": "^8.0.1" + } + }, + "node_modules/eslint-plugin-i18n-text": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz", + "integrity": "sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==", + "dev": true, + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", + "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.8.0", + "has": "^1.0.3", + "is-core-module": "^2.12.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", + "object.values": "^1.1.6", + "resolve": "^1.22.3", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "27.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.3.tgz", + "integrity": "sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.10.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", + "eslint": "^7.0.0 || ^8.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-jest/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-plugin-jest/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", + "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.20.7", + "aria-query": "^5.1.3", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.6.2", + "axobject-query": "^3.1.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.3", + "language-tags": "=1.0.5", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-no-only-tests": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.1.0.tgz", + "integrity": "sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==", + "dev": true, + "engines": { + "node": ">=5.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-rule-documentation": { + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz", + "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/flow-bin": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.115.0.tgz", + "integrity": "sha512-xW+U2SrBaAr0EeLvKmXAmsdnrH6x0Io17P6yRJTNgrrV42G8KXhBAD00s6oGbTTqRyHD0nP47kyuU34zljZpaQ==", + "dev": true, + "bin": { + "flow": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", + "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "dev": true, + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/git-raw-commits": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-3.0.0.tgz", + "integrity": "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==", + "dev": true, + "dependencies": { + "dargs": "^7.0.0", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-remote-origin-url/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/git-semver-tags": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-5.0.1.tgz", + "integrity": "sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==", + "dev": true, + "dependencies": { + "meow": "^8.1.2", + "semver": "^7.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/git-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", + "dev": true, + "dependencies": { + "is-ssh": "^1.4.0", + "parse-url": "^8.1.0" + } + }, + "node_modules/git-url-parse": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", + "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", + "dev": true, + "dependencies": { + "git-up": "^7.0.0" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/init-package-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-5.0.0.tgz", + "integrity": "sha512-kBhlSheBfYmq3e0L1ii+VKe3zBTLL5lDCDWR+f9dLmEGSB3MqLlMlsolubSsyI88Bg6EA+BIMlomAnQ1SwgQBw==", + "dev": true, + "dependencies": { + "npm-package-arg": "^10.0.0", + "promzard": "^1.0.0", + "read": "^2.0.0", + "read-package-json": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/init-package-json/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/init-package-json/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/init-package-json/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "dev": true, + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.2.tgz", + "integrity": "sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.6.4" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.6.3", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "p-limit": "^3.1.0", + "pretty-format": "^29.6.3", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-config": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.6.4", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "natural-compare": "^1.4.0", + "pretty-format": "^29.6.3", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.6.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.6.3", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dev": true, + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/lerna": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-7.2.0.tgz", + "integrity": "sha512-E13iAY4Tdo+86m4ClAe0j0bP7f8QG2neJReglILPOe+gAOoX17TGqEWanmkDELlUXOrTTwnte0ewc6I6/NOqpg==", + "dev": true, + "dependencies": { + "@lerna/child-process": "7.2.0", + "@lerna/create": "7.2.0", + "@npmcli/run-script": "6.0.2", + "@nx/devkit": ">=16.5.1 < 17", + "@octokit/plugin-enterprise-rest": "6.0.1", + "@octokit/rest": "19.0.11", + "byte-size": "8.1.1", + "chalk": "4.1.0", + "clone-deep": "4.0.1", + "cmd-shim": "6.0.1", + "columnify": "1.6.0", + "conventional-changelog-angular": "6.0.0", + "conventional-changelog-core": "5.0.1", + "conventional-recommended-bump": "7.0.1", + "cosmiconfig": "^8.2.0", + "dedent": "0.7.0", + "envinfo": "7.8.1", + "execa": "5.0.0", + "fs-extra": "^11.1.1", + "get-port": "5.1.1", + "get-stream": "6.0.0", + "git-url-parse": "13.1.0", + "glob-parent": "5.1.2", + "globby": "11.1.0", + "graceful-fs": "4.2.11", + "has-unicode": "2.0.1", + "import-local": "3.1.0", + "ini": "^1.3.8", + "init-package-json": "5.0.0", + "inquirer": "^8.2.4", + "is-ci": "3.0.1", + "is-stream": "2.0.0", + "jest-diff": ">=29.4.3 < 30", + "js-yaml": "4.1.0", + "libnpmaccess": "7.0.2", + "libnpmpublish": "7.3.0", + "load-json-file": "6.2.0", + "lodash": "^4.17.21", + "make-dir": "3.1.0", + "minimatch": "3.0.5", + "multimatch": "5.0.0", + "node-fetch": "2.6.7", + "npm-package-arg": "8.1.1", + "npm-packlist": "5.1.1", + "npm-registry-fetch": "^14.0.5", + "npmlog": "^6.0.2", + "nx": ">=16.5.1 < 17", + "p-map": "4.0.0", + "p-map-series": "2.1.0", + "p-pipe": "3.1.0", + "p-queue": "6.6.2", + "p-reduce": "2.1.0", + "p-waterfall": "2.1.1", + "pacote": "^15.2.0", + "pify": "5.0.0", + "read-cmd-shim": "4.0.0", + "read-package-json": "6.0.4", + "resolve-from": "5.0.0", + "rimraf": "^4.4.1", + "semver": "^7.3.8", + "signal-exit": "3.0.7", + "slash": "3.0.0", + "ssri": "^9.0.1", + "strong-log-transformer": "2.1.0", + "tar": "6.1.11", + "temp-dir": "1.0.0", + "typescript": ">=3 < 6", + "upath": "2.0.1", + "uuid": "^9.0.0", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "5.0.0", + "write-file-atomic": "5.0.1", + "write-pkg": "4.0.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4" + }, + "bin": { + "lerna": "dist/cli.js" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + } + }, + "node_modules/lerna/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lerna/node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/lerna/node_modules/execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lerna/node_modules/get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lerna/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lerna/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lerna/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lerna/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/lerna/node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/lerna/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/lerna/node_modules/rimraf/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/rimraf/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lerna/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libnpmaccess": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-7.0.2.tgz", + "integrity": "sha512-vHBVMw1JFMTgEk15zRsJuSAg7QtGGHpUSEfnbcRL1/gTBag9iEfJbyjpDmdJmwMhvpoLoNBtdAUCdGnaP32hhw==", + "dev": true, + "dependencies": { + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmaccess/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmaccess/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/libnpmaccess/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmpublish": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-7.3.0.tgz", + "integrity": "sha512-fHUxw5VJhZCNSls0KLNEG0mCD2PN1i14gH5elGOgiVnU3VgTcRahagYP2LKI1m0tFCJ+XrAm0zVYyF5RCbXzcg==", + "dev": true, + "dependencies": { + "ci-info": "^3.6.1", + "normalize-package-data": "^5.0.0", + "npm-package-arg": "^10.1.0", + "npm-registry-fetch": "^14.0.3", + "proc-log": "^3.0.0", + "semver": "^7.3.7", + "sigstore": "^1.4.0", + "ssri": "^10.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/libnpmpublish/node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/libnpmpublish/node_modules/ssri": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", + "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", + "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/load-json-file/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", + "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-fetch-happen/node_modules/ssri": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", + "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/meow/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-fetch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", + "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multimatch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", + "dev": true, + "dependencies": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/multimatch/node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-gyp": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", + "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^11.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-machine-id": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", + "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.2.0.tgz", + "integrity": "sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.1.tgz", + "integrity": "sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", + "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/npm-package-arg/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/npm-packlist": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.1.tgz", + "integrity": "sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^1.1.2", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-packlist/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-packlist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-pick-manifest": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz", + "integrity": "sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==", + "dev": true, + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", + "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-registry-fetch/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nx": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/nx/-/nx-16.6.0.tgz", + "integrity": "sha512-4UaS9nRakpZs45VOossA7hzSQY2dsr035EoPRGOc81yoMFW6Sqn1Rgq4hiLbHZOY8MnWNsLMkgolNMz1jC8YUQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nrwl/tao": "16.6.0", + "@parcel/watcher": "2.0.4", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.0.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^7.0.2", + "dotenv": "~10.0.0", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "glob": "7.1.4", + "ignore": "^5.0.4", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "3.0.5", + "node-machine-id": "1.1.12", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "semver": "7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "v8-compile-cache": "2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "16.6.0", + "@nx/nx-darwin-x64": "16.6.0", + "@nx/nx-freebsd-x64": "16.6.0", + "@nx/nx-linux-arm-gnueabihf": "16.6.0", + "@nx/nx-linux-arm64-gnu": "16.6.0", + "@nx/nx-linux-arm64-musl": "16.6.0", + "@nx/nx-linux-x64-gnu": "16.6.0", + "@nx/nx-linux-x64-musl": "16.6.0", + "@nx/nx-win32-arm64-msvc": "16.6.0", + "@nx/nx-win32-x64-msvc": "16.6.0" + }, + "peerDependencies": { + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/nx/node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nx/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nx/node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nx/node_modules/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nx/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/nx/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nx/node_modules/lines-and-columns": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", + "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/nx/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nx/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nx/node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nx/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/nx/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/nx/node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", + "dev": true + }, + "node_modules/nx/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/nx/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/nx/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/nx/node_modules/yargs/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", + "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-2.1.0.tgz", + "integrity": "sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-pipe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-pipe/-/p-pipe-3.1.0.tgz", + "integrity": "sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-waterfall": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz", + "integrity": "sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==", + "dev": true, + "dependencies": { + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pacote": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", + "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", + "dev": true, + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^5.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.3.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/pacote/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/ignore-walk": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz", + "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==", + "dev": true, + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/pacote/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pacote/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/npm-packlist": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", + "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "dev": true, + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/ssri": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", + "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/ssri/node_modules/minipass": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-path": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", + "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", + "dev": true, + "dependencies": { + "protocols": "^2.0.0" + } + }, + "node_modules/parse-url": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", + "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", + "dev": true, + "dependencies": { + "parse-path": "^7.0.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/promzard": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/promzard/-/promzard-1.0.0.tgz", + "integrity": "sha512-KQVDEubSUHGSt5xLakaToDFrSoZhStB8dXLzk2xvwR67gJktrHFvpR63oZgHyK19WKbHFLXJqCPXdVR3aBP8Ig==", + "dev": true, + "dependencies": { + "read": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/protocols": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", + "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==", + "dev": true + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read/-/read-2.1.0.tgz", + "integrity": "sha512-bvxi1QLJHcaywCAEsAk4DG3nVoqiY2Csps3qzWalhj5hFqRn1d/OixkFXtLO1PrgHUcAP0FNaSY/5GYNfENFFQ==", + "dev": true, + "dependencies": { + "mute-stream": "~1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-cmd-shim": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", + "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", + "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "dev": true, + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.12.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sigstore": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.8.0.tgz", + "integrity": "sha512-ogU8qtQ3VFBawRJ8wjsBEX/vIFeHuGs1fm4jZtjWQwjo8pfAt7T/rh+udlAN4+QUe0IzA8qRSc/YZ7dHP6kh+w==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^1.0.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/tuf": "^1.0.3", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ssri/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strong-log-transformer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", + "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + }, + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-element-attributes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.1.tgz", + "integrity": "sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", + "dev": true + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-jest": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tuf-js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", + "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", + "dev": true, + "dependencies": { + "@tufjs/models": "1.0.4", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-json-file": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", + "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", + "dev": true, + "dependencies": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/write-json-file/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/write-json-file/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/write-json-file/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/write-json-file/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/write-pkg": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-4.0.0.tgz", + "integrity": "sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==", + "dev": true, + "dependencies": { + "sort-keys": "^2.0.0", + "type-fest": "^0.4.1", + "write-json-file": "^3.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/write-pkg/node_modules/type-fest": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", + "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/actions-toolkit/package.json b/actions-toolkit/package.json new file mode 100644 index 00000000..73a7093c --- /dev/null +++ b/actions-toolkit/package.json @@ -0,0 +1,36 @@ +{ + "name": "root", + "private": true, + "scripts": { + "audit-all": "lerna run audit-moderate", + "bootstrap": "lerna exec -- npm install", + "build": "lerna run tsc", + "clean": "lerna clean", + "repair": "lerna repair", + "check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:test\" \"npm:build -- -- --noEmit\"", + "format": "prettier --write packages/**/*.ts", + "format-check": "prettier --check packages/**/*.ts", + "lint": "eslint packages/**/*.ts", + "lint-fix": "eslint packages/**/*.ts --fix", + "new-package": "scripts/create-package", + "test": "jest --testTimeout 10000" + }, + "devDependencies": { + "@types/jest": "^29.5.4", + "@types/node": "^20.5.7", + "@types/signale": "^1.4.1", + "concurrently": "^6.1.0", + "eslint": "^8.0.1", + "eslint-config-prettier": "^8.9.0", + "eslint-plugin-github": "^4.9.2", + "eslint-plugin-jest": "^27.2.3", + "eslint-plugin-prettier": "^5.0.0", + "flow-bin": "^0.115.0", + "jest": "^29.6.4", + "lerna": "^7.1.4", + "nx": "16.6.0", + "prettier": "^3.0.0", + "ts-jest": "^29.1.1", + "typescript": "^5.2.2" + } +} \ No newline at end of file diff --git a/actions-toolkit/packages/artifact/CONTRIBUTIONS.md b/actions-toolkit/packages/artifact/CONTRIBUTIONS.md new file mode 100644 index 00000000..23ede984 --- /dev/null +++ b/actions-toolkit/packages/artifact/CONTRIBUTIONS.md @@ -0,0 +1,43 @@ +# Contributions + +This package is used internally by the v4 versions of [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact). This package can also be used by other actions to interact with artifacts. Any changes or updates to this package will propagate updates to these actions so it is important that major changes or updates get properly tested. + +Any issues or feature requests that are related to the artifact actions should be filled in the appropriate repo. + +A limited range of unit tests run as part of each PR when making changes to the artifact packages. For small contributions and fixes, they should be sufficient. + +If making large changes, there are a few scenarios that should be tested: + +- Uploading very large artifacts +- Uploading artifacts with lots of small files +- Uploading artifacts using a self-hosted runner (uploads and downloads behave differently due to extra latency) +- Downloading a single artifact (large and small, if lots of small files are part of an artifact, timeouts and non-success HTTP responses can be expected) +- Downloading all artifacts at once + +Large architectural changes can impact upload/download performance so it is important to separately run extra tests. We request that any large contributions/changes have extra detailed testing so we can verify performance and possible regressions. + +Tests will run for every push/pull_request [via Actions](https://github.com/actions/toolkit/blob/main/.github/workflows/artifact-tests.yml). + +# Testing + +## Package tests + +To run unit tests for the `@actions/artifact` package: + +1. Clone `actions/toolkit` locally +2. Install dependencies: `npm bootstrap` +3. Change working directory to `packages/artifact` +4. Run jest tests: `npm run test` + +## Within upload-artifact or download-artifact actions + +Any easy way to test changes for the official upload/download actions is to fork them, compile changes and run them. + +1. For your local `actions/toolkit` changes: + 1. Change directory to `packages/artifact` + 2. Compile the changes: `npm run tsc` + 3. Symlink your package change: `npm link` +2. Fork and clone either [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact) + 1. In the locally cloned fork, link to your local toolkit changes: `npm link @actions/artifact` + 2. Then, compile your changes with: `npm run release`. The local `dist/index.js` should be updated with your changes. + 3. Commit and push to your fork, you can then test with a `uses:` in your workflow pointed at your fork. diff --git a/actions-toolkit/packages/artifact/LICENSE.md b/actions-toolkit/packages/artifact/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/artifact/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/artifact/README.md b/actions-toolkit/packages/artifact/README.md new file mode 100644 index 00000000..d03dfc24 --- /dev/null +++ b/actions-toolkit/packages/artifact/README.md @@ -0,0 +1,150 @@ +# `@actions/artifact` + +Interact programmatically with [Actions Artifacts](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts). + +This is the core library that powers the [`@actions/upload-artifact`](https://github.com/actions/upload-artifact) and [`@actions/download-artifact`](https://github.com/actions/download-artifact) actions. + + +- [`@actions/artifact`](#actionsartifact) + - [v2 - What's New](#v2---whats-new) + - [Improvements](#improvements) + - [Breaking changes](#breaking-changes) + - [Quick Start](#quick-start) + - [Examples](#examples) + - [Upload and Download](#upload-and-download) + - [Downloading from other workflow runs or repos](#downloading-from-other-workflow-runs-or-repos) + - [Speeding up large uploads](#speeding-up-large-uploads) + - [Additional Resources](#additional-resources) + +## v2 - What's New + +> [!IMPORTANT] +> @actions/artifact v2+, upload-artifact@v4+ download-artifact@v4+ are not currently supported on GHES yet. The previous version of this package can be found at [this tag](https://github.com/actions/toolkit/tree/@actions/artifact@1.1.2/packages/artifact) and [on npm](https://www.npmjs.com/package/@actions/artifact/v/1.1.2). + +The release of `@actions/artifact@v2` (including `upload-artifact@v4` and `download-artifact@v4`) are major changes to the backend architecture of Artifacts. They have numerous performance and behavioral improvements. + +### Improvements + +1. All upload and download operations are much quicker, up to 80% faster download times and 96% faster upload times in worst case scenarios. +2. Once uploaded, an Artifact ID is returned and Artifacts are immediately available in the UI and [REST API](https://docs.github.com/en/rest/actions/artifacts). Previously, you would have to wait for the run to be completed before an ID was available or any APIs could be utilized. +3. Artifacts can now be downloaded and deleted from the UI _before_ the entire workflow run finishes. +4. The contents of an Artifact are uploaded together into an _immutable_ archive. They cannot be altered by subsequent jobs. Both of these factors help reduce the possibility of accidentally corrupting Artifact files. (Digest/integrity hash coming soon in the API!) +5. This library (and `actions/download-artifact`) now support downloading Artifacts from _other_ repositories and runs if a `GITHUB_TOKEN` with sufficient `actions:read` permissions are provided. + +### Breaking changes + +1. Firewall rules required for self-hosted runners. + + If you are using self-hosted runners behind a firewall, you must have flows open to [Actions endpoints](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-between-self-hosted-runners-and-github). If you cannot use wildcard rules for your firewall, see the GitHub [meta endpoint](https://api.github.com/meta) for specific endpoints. + + e.g. + + ```bash + curl https://api.github.com/meta | jq .domains.actions + ``` + +2. Uploading to the same named Artifact multiple times. + + Due to how Artifacts are created in this new version, it is no longer possible to upload to the same named Artifact multiple times. You must either split the uploads into multiple Artifacts with different names, or only upload once. + +3. Limit of Artifacts for an individual job. + + Each job in a workflow run now has a limit of 10 artifacts. + +## Quick Start + +Install the package: + +```bash +npm i @actions/artifact +``` + +Import the module: + +```js +// ES6 module +import artifact from '@actions/artifact' + +// CommonJS +const {default: artifact} = require('@actions/artifact') +``` + +ℹ️ For a comprehensive list of classes, interfaces, functions and more, see the [generated documentation](./docs/generated/README.md). + +## Examples + +### Upload and Download + +The most basic scenario is uploading one or more files to an Artifact, then downloading that Artifact. Downloads are based on the Artifact ID, which can be obtained in the response of `uploadArtifact`, `getArtifact`, `listArtifacts` or via the [REST API](https://docs.github.com/en/rest/actions/artifacts). + +```js +const {id, size} = await artifact.uploadArtifact( + // name of the artifact + 'my-artifact', + // files to include (supports absolute and relative paths) + ['/absolute/path/file1.txt', './relative/file2.txt'], + { + // optional: how long to retain the artifact + // if unspecified, defaults to repository/org retention settings (the limit of this value) + retentionDays: 10 + } +) + +console.log(`Created artifact with id: ${id} (bytes: ${size}`) + +const {downloadPath} = await artifact.downloadArtifact(id, { + // optional: download destination path. otherwise defaults to $GITHUB_WORKSPACE + path: '/tmp/dst/path', +}) + +console.log(`Downloaded artifact ${id} to: ${downloadPath}`) +``` + +### Downloading from other workflow runs or repos + +It may be useful to download Artifacts from other workflow runs, or even other repositories. By default, the permissions are scoped so they can only download Artifacts within the current workflow run. To elevate permissions for this scenario, you must specify `options.findBy` to `downloadArtifact`. + +```ts +const findBy = { + // must have actions:read permission on target repository + token: process.env['GITHUB_TOKEN'], + workflowRunId: 123, + repositoryOwner: 'actions', + repositoryName: 'toolkit' +} + +await artifact.downloadArtifact(1337, { + findBy +}) + +// can also be used in other methods + +await artifact.getArtifact('my-artifact', { + findBy +}) + +await artifact.listArtifacts({ + findBy +}) +``` + +### Speeding up large uploads + +If you have large files that need to be uploaded (or file types that don't compress well), you may benefit from changing the compression level of the Artifact archive. NOTE: This is a tradeoff between artifact upload time and stored data size. + +```ts +await artifact.uploadArtifact('my-massive-artifact', ['big_file.bin'], { + // The level of compression for Zlib to be applied to the artifact archive. + // - 0: No compression + // - 1: Best speed + // - 6: Default compression (same as GNU Gzip) + // - 9: Best compression + compressionLevel: 0 +}) +``` + +## Additional Resources + +- [Releases](./RELEASES.md) +- [Contribution Guide](./CONTRIBUTIONS.md) +- [Frequently Asked Questions](./docs/faq.md) diff --git a/actions-toolkit/packages/artifact/RELEASES.md b/actions-toolkit/packages/artifact/RELEASES.md new file mode 100644 index 00000000..ce3ed43b --- /dev/null +++ b/actions-toolkit/packages/artifact/RELEASES.md @@ -0,0 +1,103 @@ +# @actions/artifact Releases + +### 0.1.0 + +- Initial release + +### 0.2.0 + +- Fixes to TCP connections not closing +- GZip file compression to speed up downloads +- Improved logging and output +- Extra documentation + +### 0.3.0 + +- Fixes to gzip decompression when downloading artifacts +- Support handling 429 response codes +- Improved download experience when dealing with empty files +- Exponential backoff when retryable status codes are encountered +- Clearer error message if storage quota has been reached +- Improved logging and output during artifact download + +### 0.3.1 + +- Fix to ensure temporary gzip files get correctly deleted during artifact upload +- Remove spaces as a forbidden character during upload + +### 0.3.2 + +- Fix to ensure readstreams get correctly reset in the event of a retry + +### 0.3.3 + +- Increase chunk size during upload from 4MB to 8MB +- Improve user-agent strings during API calls to help internally diagnose issues + +### 0.3.5 + +- Retry in the event of a 413 response + +### 0.4.0 + +- Add option to specify custom retentions on artifacts + +### 0.4.1 + +- Update to latest @actions/core version + +### 0.4.2 + +- Improved retry-ability when a partial artifact download is encountered + +### 0.5.0 + +- Improved retry-ability for all http calls during artifact upload and download if an error is encountered + +### 0.5.1 + +- Bump @actions/http-client to version 1.0.11 to fix proxy related issues during artifact upload and download + +### 0.5.2 + +- Add HTTP 500 as a retryable status code for artifact upload and download. + +### 0.6.0 + +- Support upload from named pipes [#748](https://github.com/actions/toolkit/pull/748) +- Fixes to percentage values being greater than 100% when downloading all artifacts [#889](https://github.com/actions/toolkit/pull/889) +- Improved logging and output during artifact upload [#949](https://github.com/actions/toolkit/pull/949) +- Improvements to client-side validation for certain invalid characters not allowed during upload: [#951](https://github.com/actions/toolkit/pull/951) +- Faster upload speeds for certain types of large files by exempting gzip compression [#956](https://github.com/actions/toolkit/pull/956) +- More detailed logging when dealing with chunked uploads [#957](https://github.com/actions/toolkit/pull/957) + +### 0.6.1 + +- Fix for failing 0 byte file uploads on Windows [#962](https://github.com/actions/toolkit/pull/962) + +### 1.0.0 + +- Update `lockfileVersion` to `v2` in `package-lock.json` [#1009](https://github.com/actions/toolkit/pull/1009) + +### 1.0.1 + +- Update to v2.0.0 of `@actions/http-client` + +### 1.0.2 + +- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087) + +### 1.1.0 + +- Add `x-actions-results-crc64` and `x-actions-results-md5` checksum headers on upload [#1063](https://github.com/actions/toolkit/pull/1063) + +### 1.1.1 + +- Fixed a bug in Node16 where if an HTTP download finished too quickly (<1ms, e.g. when it's mocked) we attempt to delete a temp file that has not been created yet [#1278](https://github.com/actions/toolkit/pull/1278/commits/b9de68a590daf37c6747e38d3cb4f1dd2cfb791c) + +### 2.0.0 + +Major release. Supports new Artifact backend for improved speed, reliability and behavior. +Numerous API changes, [some breaking](./README.md#breaking-changes). + +Blog post with more info: TBD diff --git a/actions-toolkit/packages/artifact/__tests__/artifact-http-client.test.ts b/actions-toolkit/packages/artifact/__tests__/artifact-http-client.test.ts new file mode 100644 index 00000000..3c6c3784 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/artifact-http-client.test.ts @@ -0,0 +1,241 @@ +import * as http from 'http' +import * as net from 'net' +import {HttpClient} from '@actions/http-client' +import * as config from '../src/internal/shared/config' +import {internalArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client' +import {noopLogs} from './common' + +jest.mock('@actions/http-client') + +describe('artifact-http-client', () => { + beforeAll(() => { + noopLogs() + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('http://localhost:8080') + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('token') + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should successfully create a client', () => { + const client = internalArtifactTwirpClient() + expect(client).toBeDefined() + }) + + it('should make a request', async () => { + const mockPost = jest.fn(() => { + const msg = new http.IncomingMessage(new net.Socket()) + msg.statusCode = 200 + return { + message: msg, + readBody: async () => { + return Promise.resolve( + `{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}` + ) + } + } + }) + const mockHttpClient = ( + HttpClient as unknown as jest.Mock + ).mockImplementation(() => { + return { + post: mockPost + } + }) + + const client = internalArtifactTwirpClient() + const artifact = await client.CreateArtifact({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678', + name: 'artifact', + version: 4 + }) + + expect(mockHttpClient).toHaveBeenCalledTimes(1) + expect(mockPost).toHaveBeenCalledTimes(1) + expect(artifact).toBeDefined() + expect(artifact.ok).toBe(true) + expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload') + }) + + it('should retry if the request fails', async () => { + const mockPost = jest + .fn(() => { + const msgSucceeded = new http.IncomingMessage(new net.Socket()) + msgSucceeded.statusCode = 200 + return { + message: msgSucceeded, + readBody: async () => { + return Promise.resolve( + `{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}` + ) + } + } + }) + .mockImplementationOnce(() => { + const msgFailed = new http.IncomingMessage(new net.Socket()) + msgFailed.statusCode = 500 + msgFailed.statusMessage = 'Internal Server Error' + return { + message: msgFailed, + readBody: async () => { + return Promise.resolve(`{"ok": false}`) + } + } + }) + const mockHttpClient = ( + HttpClient as unknown as jest.Mock + ).mockImplementation(() => { + return { + post: mockPost + } + }) + + const client = internalArtifactTwirpClient({ + maxAttempts: 5, + retryIntervalMs: 1, + retryMultiplier: 1.5 + }) + const artifact = await client.CreateArtifact({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678', + name: 'artifact', + version: 4 + }) + + expect(mockHttpClient).toHaveBeenCalledTimes(1) + expect(artifact).toBeDefined() + expect(artifact.ok).toBe(true) + expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload') + expect(mockPost).toHaveBeenCalledTimes(2) + }) + + it('should fail if the request fails 5 times', async () => { + const mockPost = jest.fn(() => { + const msgFailed = new http.IncomingMessage(new net.Socket()) + msgFailed.statusCode = 500 + msgFailed.statusMessage = 'Internal Server Error' + return { + message: msgFailed, + readBody: async () => { + return Promise.resolve(`{"ok": false}`) + } + } + }) + + const mockHttpClient = ( + HttpClient as unknown as jest.Mock + ).mockImplementation(() => { + return { + post: mockPost + } + }) + const client = internalArtifactTwirpClient({ + maxAttempts: 5, + retryIntervalMs: 1, + retryMultiplier: 1.5 + }) + await expect(async () => { + await client.CreateArtifact({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678', + name: 'artifact', + version: 4 + }) + }).rejects.toThrowError( + 'Failed to make request after 5 attempts: Failed request: (500) Internal Server Error' + ) + expect(mockHttpClient).toHaveBeenCalledTimes(1) + expect(mockPost).toHaveBeenCalledTimes(5) + }) + + it('should fail immediately if there is a non-retryable error', async () => { + const mockPost = jest.fn(() => { + const msgFailed = new http.IncomingMessage(new net.Socket()) + msgFailed.statusCode = 401 + msgFailed.statusMessage = 'Unauthorized' + return { + message: msgFailed, + readBody: async () => { + return Promise.resolve(`{"ok": false}`) + } + } + }) + + const mockHttpClient = ( + HttpClient as unknown as jest.Mock + ).mockImplementation(() => { + return { + post: mockPost + } + }) + const client = internalArtifactTwirpClient({ + maxAttempts: 5, + retryIntervalMs: 1, + retryMultiplier: 1.5 + }) + await expect(async () => { + await client.CreateArtifact({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678', + name: 'artifact', + version: 4 + }) + }).rejects.toThrowError( + 'Received non-retryable error: Failed request: (401) Unauthorized' + ) + expect(mockHttpClient).toHaveBeenCalledTimes(1) + expect(mockPost).toHaveBeenCalledTimes(1) + }) + + it('should fail with a descriptive error', async () => { + // 409 duplicate error + const mockPost = jest.fn(() => { + const msgFailed = new http.IncomingMessage(new net.Socket()) + msgFailed.statusCode = 409 + msgFailed.statusMessage = 'Conflict' + return { + message: msgFailed, + readBody: async () => { + return Promise.resolve( + `{"msg": "an artifact with this name already exists on the workflow run"}` + ) + } + } + }) + + const mockHttpClient = ( + HttpClient as unknown as jest.Mock + ).mockImplementation(() => { + return { + post: mockPost + } + }) + const client = internalArtifactTwirpClient({ + maxAttempts: 5, + retryIntervalMs: 1, + retryMultiplier: 1.5 + }) + await expect(async () => { + await client.CreateArtifact({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678', + name: 'artifact', + version: 4 + }) + await client.CreateArtifact({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678', + name: 'artifact', + version: 4 + }) + }).rejects.toThrowError( + 'Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run' + ) + expect(mockHttpClient).toHaveBeenCalledTimes(1) + expect(mockPost).toHaveBeenCalledTimes(1) + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/common.ts b/actions-toolkit/packages/artifact/__tests__/common.ts new file mode 100644 index 00000000..4fae850c --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/common.ts @@ -0,0 +1,9 @@ +import * as core from '@actions/core' + +// noopLogs mocks the console.log and core.* functions to prevent output in the console while testing +export const noopLogs = (): void => { + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) +} diff --git a/actions-toolkit/packages/artifact/__tests__/download-artifact.test.ts b/actions-toolkit/packages/artifact/__tests__/download-artifact.test.ts new file mode 100644 index 00000000..247a5e42 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/download-artifact.test.ts @@ -0,0 +1,466 @@ +import fs from 'fs' +import * as http from 'http' +import * as net from 'net' +import * as path from 'path' +import * as github from '@actions/github' +import {HttpClient} from '@actions/http-client' +import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types' +import archiver from 'archiver' + +import { + downloadArtifactInternal, + downloadArtifactPublic +} from '../src/internal/download/download-artifact' +import {getUserAgentString} from '../src/internal/shared/user-agent' +import {noopLogs} from './common' +import * as config from '../src/internal/shared/config' +import {ArtifactServiceClientJSON} from '../src/generated' +import * as util from '../src/internal/shared/util' + +type MockedDownloadArtifact = jest.MockedFunction< + RestEndpointMethods['actions']['downloadArtifact'] +> + +const testDir = path.join(__dirname, '_temp', 'download-artifact') +const fixtures = { + workspaceDir: path.join(testDir, 'workspace'), + exampleArtifact: { + path: path.join(testDir, 'artifact.zip'), + files: [ + { + path: 'hello.txt', + content: 'Hello World!' + }, + { + path: 'goodbye.txt', + content: 'Goodbye World!' + } + ] + }, + artifactID: 1234, + artifactName: 'my-artifact', + artifactSize: 123456, + repositoryOwner: 'actions', + repositoryName: 'toolkit', + token: 'ghp_1234567890', + blobStorageUrl: 'https://blob-storage.local?signed=true', + backendIds: { + workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422', + workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf' + } +} + +jest.mock('@actions/github', () => ({ + getOctokit: jest.fn().mockReturnValue({ + rest: { + actions: { + downloadArtifact: jest.fn() + } + } + }) +})) + +jest.mock('@actions/http-client') + +// Create a zip archive with the contents of the example artifact +const createTestArchive = async (): Promise => { + const archive = archiver('zip', { + zlib: {level: 9} + }) + for (const file of fixtures.exampleArtifact.files) { + archive.append(file.content, {name: file.path}) + } + archive.finalize() + + return new Promise((resolve, reject) => { + archive.pipe(fs.createWriteStream(fixtures.exampleArtifact.path)) + archive.on('error', reject) + archive.on('finish', resolve) + }) +} + +const expectExtractedArchive = async (dir: string): Promise => { + for (const file of fixtures.exampleArtifact.files) { + const filePath = path.join(dir, file.path) + expect(fs.readFileSync(filePath, 'utf8')).toEqual(file.content) + } +} + +const setup = async (): Promise => { + noopLogs() + await fs.promises.mkdir(testDir, {recursive: true}) + await createTestArchive() + + process.env['GITHUB_WORKSPACE'] = fixtures.workspaceDir +} + +const cleanup = async (): Promise => { + jest.restoreAllMocks() + await fs.promises.rm(testDir, {recursive: true}) + delete process.env['GITHUB_WORKSPACE'] +} + +const mockGetArtifactSuccess = jest.fn(() => { + const message = new http.IncomingMessage(new net.Socket()) + message.statusCode = 200 + message.push(fs.readFileSync(fixtures.exampleArtifact.path)) + return { + message + } +}) + +const mockGetArtifactFailure = jest.fn(() => { + const message = new http.IncomingMessage(new net.Socket()) + message.statusCode = 500 + message.push('Internal Server Error') + return { + message + } +}) + +describe('download-artifact', () => { + describe('public', () => { + beforeEach(setup) + afterEach(cleanup) + + it('should successfully download an artifact to $GITHUB_WORKSPACE', async () => { + const downloadArtifactMock = github.getOctokit(fixtures.token).rest + .actions.downloadArtifact as MockedDownloadArtifact + downloadArtifactMock.mockResolvedValueOnce({ + headers: { + location: fixtures.blobStorageUrl + }, + status: 302, + url: '', + data: Buffer.from('') + }) + + const mockHttpClient = (HttpClient as jest.Mock).mockImplementation( + () => { + return { + get: mockGetArtifactSuccess + } + } + ) + + const response = await downloadArtifactPublic( + fixtures.artifactID, + fixtures.repositoryOwner, + fixtures.repositoryName, + fixtures.token + ) + + expect(downloadArtifactMock).toHaveBeenCalledWith({ + owner: fixtures.repositoryOwner, + repo: fixtures.repositoryName, + artifact_id: fixtures.artifactID, + archive_format: 'zip', + request: { + redirect: 'manual' + } + }) + expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString()) + expect(mockGetArtifactSuccess).toHaveBeenCalledWith( + fixtures.blobStorageUrl + ) + expectExtractedArchive(fixtures.workspaceDir) + expect(response.downloadPath).toBe(fixtures.workspaceDir) + }) + + it('should successfully download an artifact to user defined path', async () => { + const customPath = path.join(testDir, 'custom') + + const downloadArtifactMock = github.getOctokit(fixtures.token).rest + .actions.downloadArtifact as MockedDownloadArtifact + downloadArtifactMock.mockResolvedValueOnce({ + headers: { + location: fixtures.blobStorageUrl + }, + status: 302, + url: '', + data: Buffer.from('') + }) + + const mockHttpClient = (HttpClient as jest.Mock).mockImplementation( + () => { + return { + get: mockGetArtifactSuccess + } + } + ) + + const response = await downloadArtifactPublic( + fixtures.artifactID, + fixtures.repositoryOwner, + fixtures.repositoryName, + fixtures.token, + { + path: customPath + } + ) + + expect(downloadArtifactMock).toHaveBeenCalledWith({ + owner: fixtures.repositoryOwner, + repo: fixtures.repositoryName, + artifact_id: fixtures.artifactID, + archive_format: 'zip', + request: { + redirect: 'manual' + } + }) + expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString()) + expect(mockGetArtifactSuccess).toHaveBeenCalledWith( + fixtures.blobStorageUrl + ) + expectExtractedArchive(customPath) + expect(response.downloadPath).toBe(customPath) + }) + + it('should fail if download artifact API does not respond with location', async () => { + const downloadArtifactMock = github.getOctokit(fixtures.token).rest + .actions.downloadArtifact as MockedDownloadArtifact + downloadArtifactMock.mockResolvedValueOnce({ + headers: {}, + status: 302, + url: '', + data: Buffer.from('') + }) + + await expect( + downloadArtifactPublic( + fixtures.artifactID, + fixtures.repositoryOwner, + fixtures.repositoryName, + fixtures.token + ) + ).rejects.toBeInstanceOf(Error) + + expect(downloadArtifactMock).toHaveBeenCalledWith({ + owner: fixtures.repositoryOwner, + repo: fixtures.repositoryName, + artifact_id: fixtures.artifactID, + archive_format: 'zip', + request: { + redirect: 'manual' + } + }) + }) + + it('should fail if blob storage response is non-200', async () => { + const downloadArtifactMock = github.getOctokit(fixtures.token).rest + .actions.downloadArtifact as MockedDownloadArtifact + downloadArtifactMock.mockResolvedValueOnce({ + headers: { + location: fixtures.blobStorageUrl + }, + status: 302, + url: '', + data: Buffer.from('') + }) + + const mockHttpClient = (HttpClient as jest.Mock).mockImplementation( + () => { + return { + get: mockGetArtifactFailure + } + } + ) + + await expect( + downloadArtifactPublic( + fixtures.artifactID, + fixtures.repositoryOwner, + fixtures.repositoryName, + fixtures.token + ) + ).rejects.toBeInstanceOf(Error) + + expect(downloadArtifactMock).toHaveBeenCalledWith({ + owner: fixtures.repositoryOwner, + repo: fixtures.repositoryName, + artifact_id: fixtures.artifactID, + archive_format: 'zip', + request: { + redirect: 'manual' + } + }) + expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString()) + expect(mockGetArtifactFailure).toHaveBeenCalledWith( + fixtures.blobStorageUrl + ) + }) + }) + + describe('internal', () => { + beforeEach(async () => { + await setup() + + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue(fixtures.backendIds) + + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://results.local') + }) + afterEach(async () => { + await cleanup() + }) + + it('should successfully download an artifact to $GITHUB_WORKSPACE', async () => { + const mockListArtifacts = jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [ + { + ...fixtures.backendIds, + databaseId: fixtures.artifactID.toString(), + name: fixtures.artifactName, + size: fixtures.artifactSize.toString() + } + ] + }) + + const mockGetSignedArtifactURL = jest + .spyOn(ArtifactServiceClientJSON.prototype, 'GetSignedArtifactURL') + .mockReturnValue( + Promise.resolve({ + signedUrl: fixtures.blobStorageUrl + }) + ) + + const mockHttpClient = (HttpClient as jest.Mock).mockImplementation( + () => { + return { + get: mockGetArtifactSuccess + } + } + ) + + const response = await downloadArtifactInternal(fixtures.artifactID) + + expectExtractedArchive(fixtures.workspaceDir) + expect(response.downloadPath).toBe(fixtures.workspaceDir) + expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString()) + expect(mockListArtifacts).toHaveBeenCalledWith({ + idFilter: { + value: fixtures.artifactID.toString() + }, + ...fixtures.backendIds + }) + expect(mockGetSignedArtifactURL).toHaveBeenCalledWith({ + ...fixtures.backendIds, + name: fixtures.artifactName + }) + }) + + it('should successfully download an artifact to user defined path', async () => { + const customPath = path.join(testDir, 'custom') + + const mockListArtifacts = jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [ + { + ...fixtures.backendIds, + databaseId: fixtures.artifactID.toString(), + name: fixtures.artifactName, + size: fixtures.artifactSize.toString() + } + ] + }) + + const mockGetSignedArtifactURL = jest + .spyOn(ArtifactServiceClientJSON.prototype, 'GetSignedArtifactURL') + .mockReturnValue( + Promise.resolve({ + signedUrl: fixtures.blobStorageUrl + }) + ) + + const mockHttpClient = (HttpClient as jest.Mock).mockImplementation( + () => { + return { + get: mockGetArtifactSuccess + } + } + ) + + const response = await downloadArtifactInternal(fixtures.artifactID, { + path: customPath + }) + + expectExtractedArchive(customPath) + expect(response.downloadPath).toBe(customPath) + expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString()) + expect(mockListArtifacts).toHaveBeenCalledWith({ + idFilter: { + value: fixtures.artifactID.toString() + }, + ...fixtures.backendIds + }) + expect(mockGetSignedArtifactURL).toHaveBeenCalledWith({ + ...fixtures.backendIds, + name: fixtures.artifactName + }) + }) + + it('should fail if download artifact API does not respond with location', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockRejectedValue(new Error('boom')) + + await expect( + downloadArtifactInternal(fixtures.artifactID) + ).rejects.toBeInstanceOf(Error) + }) + + it('should fail if blob storage response is non-200', async () => { + const mockListArtifacts = jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [ + { + ...fixtures.backendIds, + databaseId: fixtures.artifactID.toString(), + name: fixtures.artifactName, + size: fixtures.artifactSize.toString() + } + ] + }) + + const mockGetSignedArtifactURL = jest + .spyOn(ArtifactServiceClientJSON.prototype, 'GetSignedArtifactURL') + .mockReturnValue( + Promise.resolve({ + signedUrl: fixtures.blobStorageUrl + }) + ) + + const mockHttpClient = (HttpClient as jest.Mock).mockImplementation( + () => { + return { + get: mockGetArtifactFailure + } + } + ) + + await expect( + downloadArtifactInternal(fixtures.artifactID) + ).rejects.toBeInstanceOf(Error) + expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString()) + expect(mockListArtifacts).toHaveBeenCalledWith({ + idFilter: { + value: fixtures.artifactID.toString() + }, + ...fixtures.backendIds + }) + expect(mockGetSignedArtifactURL).toHaveBeenCalledWith({ + ...fixtures.backendIds, + name: fixtures.artifactName + }) + }) + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/get-artifact.test.ts b/actions-toolkit/packages/artifact/__tests__/get-artifact.test.ts new file mode 100644 index 00000000..56ed0d34 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/get-artifact.test.ts @@ -0,0 +1,239 @@ +import * as github from '@actions/github' +import type {RequestInterface} from '@octokit/types' +import { + getArtifactInternal, + getArtifactPublic +} from '../src/internal/find/get-artifact' +import * as config from '../src/internal/shared/config' +import {ArtifactServiceClientJSON, Timestamp} from '../src/generated' +import * as util from '../src/internal/shared/util' +import {noopLogs} from './common' +import { + ArtifactNotFoundError, + InvalidResponseError +} from '../src/internal/shared/errors' + +type MockedRequest = jest.MockedFunction> + +jest.mock('@actions/github', () => ({ + getOctokit: jest.fn().mockReturnValue({ + request: jest.fn() + }) +})) + +const fixtures = { + repo: 'toolkit', + owner: 'actions', + token: 'ghp_1234567890', + runId: 123, + backendIds: { + workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422', + workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf' + }, + artifacts: [ + { + id: 1, + name: 'my-artifact', + size: 456, + createdAt: new Date('2023-12-01') + }, + { + id: 2, + name: 'my-artifact', + size: 456, + createdAt: new Date('2023-12-02') + } + ] +} + +describe('get-artifact', () => { + beforeAll(() => { + noopLogs() + }) + + describe('public', () => { + it('should return the artifact if it is found', async () => { + const mockRequest = github.getOctokit(fixtures.token) + .request as MockedRequest + mockRequest.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: { + artifacts: [ + { + name: fixtures.artifacts[0].name, + id: fixtures.artifacts[0].id, + size_in_bytes: fixtures.artifacts[0].size, + created_at: fixtures.artifacts[0].createdAt.toISOString() + } + ] + } + }) + + const response = await getArtifactPublic( + fixtures.artifacts[0].name, + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token + ) + + expect(response).toEqual({ + artifact: fixtures.artifacts[0] + }) + }) + + it('should return the latest artifact if multiple are found', async () => { + const mockRequest = github.getOctokit(fixtures.token) + .request as MockedRequest + mockRequest.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: { + artifacts: fixtures.artifacts.map(artifact => ({ + name: artifact.name, + id: artifact.id, + size_in_bytes: artifact.size, + created_at: artifact.createdAt.toISOString() + })) + } + }) + + const response = await getArtifactPublic( + fixtures.artifacts[0].name, + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token + ) + + expect(response).toEqual({ + artifact: fixtures.artifacts[1] + }) + }) + + it('should fail if no artifacts are found', async () => { + const mockRequest = github.getOctokit(fixtures.token) + .request as MockedRequest + mockRequest.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: { + artifacts: [] + } + }) + + const response = getArtifactPublic( + fixtures.artifacts[0].name, + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token + ) + + expect(response).rejects.toThrowError(ArtifactNotFoundError) + }) + + it('should fail if non-200 response', async () => { + const mockRequest = github.getOctokit(fixtures.token) + .request as MockedRequest + mockRequest.mockResolvedValueOnce({ + status: 404, + headers: {}, + url: '', + data: {} + }) + + const response = getArtifactPublic( + fixtures.artifacts[0].name, + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token + ) + + expect(response).rejects.toThrowError(InvalidResponseError) + }) + }) + + describe('internal', () => { + beforeEach(() => { + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue(fixtures.backendIds) + + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://results.local') + }) + + it('should return the artifact if it is found', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [ + { + ...fixtures.backendIds, + databaseId: fixtures.artifacts[0].id.toString(), + name: fixtures.artifacts[0].name, + size: fixtures.artifacts[0].size.toString(), + createdAt: Timestamp.fromDate(fixtures.artifacts[0].createdAt) + } + ] + }) + + const response = await getArtifactInternal(fixtures.artifacts[0].name) + + expect(response).toEqual({ + artifact: fixtures.artifacts[0] + }) + }) + + it('should return the latest artifact if multiple are found', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: fixtures.artifacts.map(artifact => ({ + ...fixtures.backendIds, + databaseId: artifact.id.toString(), + name: artifact.name, + size: artifact.size.toString(), + createdAt: Timestamp.fromDate(artifact.createdAt) + })) + }) + + const response = await getArtifactInternal(fixtures.artifacts[0].name) + + expect(response).toEqual({ + artifact: fixtures.artifacts[1] + }) + }) + + it('should fail if no artifacts are found', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [] + }) + + const response = getArtifactInternal(fixtures.artifacts[0].name) + + expect(response).rejects.toThrowError(ArtifactNotFoundError) + }) + + it('should fail if non-200 response', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockRejectedValue(new Error('boom')) + + const response = getArtifactInternal(fixtures.artifacts[0].name) + + expect(response).rejects.toThrow() + }) + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/list-artifacts.test.ts b/actions-toolkit/packages/artifact/__tests__/list-artifacts.test.ts new file mode 100644 index 00000000..7c8699e7 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/list-artifacts.test.ts @@ -0,0 +1,242 @@ +import * as github from '@actions/github' +import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types' +import type {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types' +import { + listArtifactsInternal, + listArtifactsPublic +} from '../src/internal/find/list-artifacts' +import * as config from '../src/internal/shared/config' +import {ArtifactServiceClientJSON, Timestamp} from '../src/generated' +import * as util from '../src/internal/shared/util' +import {noopLogs} from './common' +import {Artifact} from '../src/internal/shared/interfaces' + +type MockedListWorkflowRunArtifacts = jest.MockedFunction< + RestEndpointMethods['actions']['listWorkflowRunArtifacts'] +> + +jest.mock('@actions/github', () => ({ + getOctokit: jest.fn().mockReturnValue({ + rest: { + actions: { + listWorkflowRunArtifacts: jest.fn() + } + } + }) +})) + +const artifactsToListResponse = ( + artifacts: Artifact[] +): RestEndpointMethodTypes['actions']['listWorkflowRunArtifacts']['response']['data'] => { + return { + total_count: artifacts.length, + artifacts: artifacts.map(artifact => ({ + name: artifact.name, + id: artifact.id, + size_in_bytes: artifact.size, + created_at: artifact.createdAt?.toISOString() || '', + run_id: fixtures.runId, + // unused fields for tests + url: '', + archive_download_url: '', + expired: false, + expires_at: '', + node_id: '', + run_url: '', + type: '', + updated_at: '' + })) + } +} + +const fixtures = { + repo: 'toolkit', + owner: 'actions', + token: 'ghp_1234567890', + runId: 123, + backendIds: { + workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422', + workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf' + }, + artifacts: [ + { + id: 1, + name: 'my-artifact', + size: 456, + createdAt: new Date('2023-12-01') + }, + { + id: 2, + name: 'my-artifact', + size: 456, + createdAt: new Date('2023-12-02') + } + ] +} + +describe('list-artifact', () => { + beforeAll(() => { + noopLogs() + }) + + describe('public', () => { + it('should return a list of artifacts', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: artifactsToListResponse(fixtures.artifacts) + }) + + const response = await listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + false + ) + + expect(response).toEqual({ + artifacts: fixtures.artifacts + }) + }) + + it('should return the latest artifact when latest is specified', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: artifactsToListResponse(fixtures.artifacts) + }) + + const response = await listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + true + ) + + expect(response).toEqual({ + artifacts: [fixtures.artifacts[1]] + }) + }) + + it('can return empty artifacts', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockResolvedValueOnce({ + status: 200, + headers: {}, + url: '', + data: { + total_count: 0, + artifacts: [] + } + }) + + const response = await listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + true + ) + + expect(response).toEqual({ + artifacts: [] + }) + }) + + it('should fail if non-200 response', async () => { + const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions + .listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts + + mockListArtifacts.mockRejectedValue(new Error('boom')) + + await expect( + listArtifactsPublic( + fixtures.runId, + fixtures.owner, + fixtures.repo, + fixtures.token, + false + ) + ).rejects.toThrow('boom') + }) + }) + + describe('internal', () => { + beforeEach(() => { + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(util, 'getBackendIdsFromToken') + .mockReturnValue(fixtures.backendIds) + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://results.local') + }) + + it('should return a list of artifacts', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: fixtures.artifacts.map(artifact => ({ + ...fixtures.backendIds, + databaseId: artifact.id.toString(), + name: artifact.name, + size: artifact.size.toString(), + createdAt: Timestamp.fromDate(artifact.createdAt) + })) + }) + const response = await listArtifactsInternal(false) + expect(response).toEqual({ + artifacts: fixtures.artifacts + }) + }) + + it('should return the latest artifact when latest is specified', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: fixtures.artifacts.map(artifact => ({ + ...fixtures.backendIds, + databaseId: artifact.id.toString(), + name: artifact.name, + size: artifact.size.toString(), + createdAt: Timestamp.fromDate(artifact.createdAt) + })) + }) + const response = await listArtifactsInternal(true) + expect(response).toEqual({ + artifacts: [fixtures.artifacts[1]] + }) + }) + + it('can return empty artifacts', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockResolvedValue({ + artifacts: [] + }) + const response = await listArtifactsInternal(false) + expect(response).toEqual({ + artifacts: [] + }) + }) + + it('should fail if non-200 response', async () => { + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts') + .mockRejectedValue(new Error('boom')) + await expect(listArtifactsInternal(false)).rejects.toThrow('boom') + }) + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/path-and-artifact-name-validation.test.ts b/actions-toolkit/packages/artifact/__tests__/path-and-artifact-name-validation.test.ts new file mode 100644 index 00000000..3ecdde45 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/path-and-artifact-name-validation.test.ts @@ -0,0 +1,75 @@ +import { + validateArtifactName, + validateFilePath +} from '../src/internal/upload/path-and-artifact-name-validation' + +import {noopLogs} from './common' + +describe('Path and artifact name validation', () => { + beforeAll(() => { + noopLogs() + }) + + it('Check Artifact Name for any invalid characters', () => { + const invalidNames = [ + 'my\\artifact', + 'my/artifact', + 'my"artifact', + 'my:artifact', + 'myartifact', + 'my|artifact', + 'my*artifact', + 'my?artifact', + '' + ] + for (const invalidName of invalidNames) { + expect(() => { + validateArtifactName(invalidName) + }).toThrow() + } + + const validNames = [ + 'my-normal-artifact', + 'myNormalArtifact', + 'm¥ñðrmålÄr†ï£å¢†' + ] + for (const validName of validNames) { + expect(() => { + validateArtifactName(validName) + }).not.toThrow() + } + }) + + it('Check Artifact File Path for any invalid characters', () => { + const invalidNames = [ + 'some/invalid"artifact/path', + 'some/invalid:artifact/path', + 'some/invalidartifact/path', + 'some/invalid|artifact/path', + 'some/invalid*artifact/path', + 'some/invalid?artifact/path', + 'some/invalid\rartifact/path', + 'some/invalid\nartifact/path', + 'some/invalid\r\nartifact/path', + '' + ] + for (const invalidName of invalidNames) { + expect(() => { + validateFilePath(invalidName) + }).toThrow() + } + + const validNames = [ + 'my/perfectly-normal/artifact-path', + 'my/perfectly\\Normal/Artifact-path', + 'm¥/ñðrmål/Är†ï£å¢†' + ] + for (const validName of validNames) { + expect(() => { + validateFilePath(validName) + }).not.toThrow() + } + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/retention.test.ts b/actions-toolkit/packages/artifact/__tests__/retention.test.ts new file mode 100644 index 00000000..83de5342 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/retention.test.ts @@ -0,0 +1,65 @@ +import {Timestamp} from '../src/generated' +import * as retention from '../src/internal/upload/retention' + +describe('retention', () => { + beforeEach(() => { + delete process.env['GITHUB_RETENTION_DAYS'] + }) + it('should return the inputted retention days if it is less than the max retention days', () => { + // setup + const mockDate = new Date('2020-01-01') + jest.useFakeTimers().setSystemTime(mockDate) + process.env['GITHUB_RETENTION_DAYS'] = '90' + + const exp = retention.getExpiration(30) + + expect(exp).toBeDefined() + if (exp) { + const expDate = Timestamp.toDate(exp) + const expected = new Date() + expected.setDate(expected.getDate() + 30) + + expect(expDate).toEqual(expected) + } + }) + + it('should return the max retention days if the inputted retention days is greater than the max retention days', () => { + // setup + const mockDate = new Date('2020-01-01') + jest.useFakeTimers().setSystemTime(mockDate) + process.env['GITHUB_RETENTION_DAYS'] = '90' + + const exp = retention.getExpiration(120) + + expect(exp).toBeDefined() + if (exp) { + const expDate = Timestamp.toDate(exp) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 90) + + expect(expDate).toEqual(expected) + } + }) + + it('should return undefined if the inputted retention days is undefined', () => { + const exp = retention.getExpiration() + expect(exp).toBeUndefined() + }) + + it('should return the inputted retention days if there is no max retention days', () => { + // setup + const mockDate = new Date('2020-01-01') + jest.useFakeTimers().setSystemTime(mockDate) + + const exp = retention.getExpiration(30) + + expect(exp).toBeDefined() + if (exp) { + const expDate = Timestamp.toDate(exp) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 30) + + expect(expDate).toEqual(expected) + } + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/upload-artifact.test.ts b/actions-toolkit/packages/artifact/__tests__/upload-artifact.test.ts new file mode 100644 index 00000000..b0dca5c8 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/upload-artifact.test.ts @@ -0,0 +1,354 @@ +import * as uploadZipSpecification from '../src/internal/upload/upload-zip-specification' +import * as zip from '../src/internal/upload/zip' +import * as util from '../src/internal/shared/util' +import * as retention from '../src/internal/upload/retention' +import * as config from '../src/internal/shared/config' +import {Timestamp, ArtifactServiceClientJSON} from '../src/generated' +import * as blobUpload from '../src/internal/upload/blob-upload' +import {uploadArtifact} from '../src/internal/upload/upload-artifact' +import {noopLogs} from './common' +import {FilesNotFoundError} from '../src/internal/shared/errors' + +describe('upload-artifact', () => { + beforeEach(() => { + noopLogs() + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should successfully upload an artifact', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue( + Promise.resolve({ + ok: true, + signedUploadUrl: 'https://signed-upload-url.com' + }) + ) + jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue( + Promise.resolve({ + uploadSize: 1234, + sha256Hash: 'test-sha256-hash' + }) + ) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact') + .mockReturnValue(Promise.resolve({ok: true, artifactId: '1'})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).resolves.toEqual({size: 1234, id: 1}) + }) + + it('should throw an error if the root directory is invalid', () => { + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockImplementation(() => { + throw new Error('Invalid root directory') + }) + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).rejects.toThrow('Invalid root directory') + }) + + it('should reject if there are no files to upload', () => { + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([]) + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + expect(uploadResp).rejects.toThrowError(FilesNotFoundError) + }) + + it('should reject if no backend IDs are found', () => { + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).rejects.toThrow() + }) + + it('should return false if the creation request fails', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue(Promise.resolve({ok: false, signedUploadUrl: ''})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).rejects.toThrow() + }) + + it('should return false if blob storage upload is unsuccessful', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue( + Promise.resolve({ + ok: true, + signedUploadUrl: 'https://signed-upload-url.com' + }) + ) + jest + .spyOn(blobUpload, 'uploadZipToBlobStorage') + .mockReturnValue(Promise.reject(new Error('boom'))) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).rejects.toThrow() + }) + + it('should reject if finalize artifact fails', () => { + const mockDate = new Date('2020-01-01') + jest + .spyOn(uploadZipSpecification, 'validateRootDirectory') + .mockReturnValue() + jest + .spyOn(uploadZipSpecification, 'getUploadZipSpecification') + .mockReturnValue([ + { + sourcePath: '/home/user/files/plz-upload/file1.txt', + destinationPath: 'file1.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/file2.txt', + destinationPath: 'file2.txt' + }, + { + sourcePath: '/home/user/files/plz-upload/dir/file3.txt', + destinationPath: 'dir/file3.txt' + } + ]) + + jest + .spyOn(zip, 'createZipUploadStream') + .mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1))) + jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({ + workflowRunBackendId: '1234', + workflowJobRunBackendId: '5678' + }) + jest + .spyOn(retention, 'getExpiration') + .mockReturnValue(Timestamp.fromDate(mockDate)) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact') + .mockReturnValue( + Promise.resolve({ + ok: true, + signedUploadUrl: 'https://signed-upload-url.com' + }) + ) + jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue( + Promise.resolve({ + uploadSize: 1234, + sha256Hash: 'test-sha256-hash' + }) + ) + jest + .spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact') + .mockReturnValue(Promise.resolve({ok: false, artifactId: ''})) + + // ArtifactHttpClient mocks + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') + jest + .spyOn(config, 'getResultsServiceUrl') + .mockReturnValue('https://test-url.com') + + const uploadResp = uploadArtifact( + 'test-artifact', + [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ], + '/home/user/files/plz-upload' + ) + + expect(uploadResp).rejects.toThrow() + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/upload-zip-specification.test.ts b/actions-toolkit/packages/artifact/__tests__/upload-zip-specification.test.ts new file mode 100644 index 00000000..0b59bff7 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/upload-zip-specification.test.ts @@ -0,0 +1,308 @@ +import * as io from '../../io/src/io' +import * as path from 'path' +import {promises as fs} from 'fs' +import { + getUploadZipSpecification, + validateRootDirectory +} from '../src/internal/upload/upload-zip-specification' +import {noopLogs} from './common' + +const root = path.join(__dirname, '_temp', 'upload-specification') +const goodItem1Path = path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'good-item1.txt' +) +const goodItem2Path = path.join(root, 'folder-d', 'good-item2.txt') +const goodItem3Path = path.join(root, 'folder-d', 'good-item3.txt') +const goodItem4Path = path.join(root, 'folder-d', 'good-item4.txt') +const goodItem5Path = path.join(root, 'good-item5.txt') +const badItem1Path = path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'bad-item1.txt' +) +const badItem2Path = path.join(root, 'folder-d', 'bad-item2.txt') +const badItem3Path = path.join(root, 'folder-f', 'bad-item3.txt') +const badItem4Path = path.join(root, 'folder-h', 'folder-i', 'bad-item4.txt') +const badItem5Path = path.join(root, 'folder-h', 'folder-i', 'bad-item5.txt') +const extraFileInFolderCPath = path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'extra-file-in-folder-c.txt' +) +const amazingFileInFolderHPath = path.join(root, 'folder-h', 'amazing-item.txt') + +const artifactFilesToUpload = [ + goodItem1Path, + goodItem2Path, + goodItem3Path, + goodItem4Path, + goodItem5Path, + extraFileInFolderCPath, + amazingFileInFolderHPath +] + +describe('Search', () => { + beforeAll(async () => { + noopLogs() + + // clear temp directory + await io.rmRF(root) + await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-e'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-d'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-f'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-g'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-h', 'folder-i'), { + recursive: true + }) + + await fs.writeFile(goodItem1Path, 'good item1 file') + await fs.writeFile(goodItem2Path, 'good item2 file') + await fs.writeFile(goodItem3Path, 'good item3 file') + await fs.writeFile(goodItem4Path, 'good item4 file') + await fs.writeFile(goodItem5Path, 'good item5 file') + + await fs.writeFile(badItem1Path, 'bad item1 file') + await fs.writeFile(badItem2Path, 'bad item2 file') + await fs.writeFile(badItem3Path, 'bad item3 file') + await fs.writeFile(badItem4Path, 'bad item4 file') + await fs.writeFile(badItem5Path, 'bad item5 file') + + await fs.writeFile(extraFileInFolderCPath, 'extra file') + + await fs.writeFile(amazingFileInFolderHPath, 'amazing file') + /* + Directory structure of files that get created: + root/ + folder-a/ + folder-b/ + folder-c/ + good-item1.txt + bad-item1.txt + extra-file-in-folder-c.txt + folder-e/ + folder-d/ + good-item2.txt + good-item3.txt + good-item4.txt + bad-item2.txt + folder-f/ + bad-item3.txt + folder-g/ + folder-h/ + amazing-item.txt + folder-i/ + bad-item4.txt + bad-item5.txt + good-item5.txt + */ + }) + + it('Upload Specification - Fail non-existent rootDirectory', async () => { + const invalidRootDirectory = path.join( + __dirname, + '_temp', + 'upload-specification-invalid' + ) + expect(() => { + validateRootDirectory(invalidRootDirectory) + }).toThrow( + `The provided rootDirectory ${invalidRootDirectory} does not exist` + ) + }) + + it('Upload Specification - Fail invalid rootDirectory', async () => { + expect(() => { + validateRootDirectory(goodItem1Path) + }).toThrow( + `The provided rootDirectory ${goodItem1Path} is not a valid directory` + ) + }) + + it('Upload Specification - File does not exist', async () => { + const fakeFilePath = path.join( + 'folder-a', + 'folder-b', + 'non-existent-file.txt' + ) + expect(() => { + getUploadZipSpecification([fakeFilePath], root) + }).toThrow(`File ${fakeFilePath} does not exist`) + }) + + it('Upload Specification - Non parent directory', async () => { + const folderADirectory = path.join(root, 'folder-a') + const artifactFiles = [ + goodItem1Path, + badItem1Path, + extraFileInFolderCPath, + goodItem5Path + ] + expect(() => { + getUploadZipSpecification(artifactFiles, folderADirectory) + }).toThrow( + `The rootDirectory: ${folderADirectory} is not a parent directory of the file: ${goodItem5Path}` + ) + }) + + it('Upload Specification - Success', async () => { + const specifications = getUploadZipSpecification( + artifactFilesToUpload, + root + ) + expect(specifications.length).toEqual(7) + + const absolutePaths = specifications.map(item => item.sourcePath) + expect(absolutePaths).toContain(goodItem1Path) + expect(absolutePaths).toContain(goodItem2Path) + expect(absolutePaths).toContain(goodItem3Path) + expect(absolutePaths).toContain(goodItem4Path) + expect(absolutePaths).toContain(goodItem5Path) + expect(absolutePaths).toContain(extraFileInFolderCPath) + expect(absolutePaths).toContain(amazingFileInFolderHPath) + + for (const specification of specifications) { + if (specification.sourcePath === goodItem1Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt') + ) + } else if (specification.sourcePath === goodItem2Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-d', 'good-item2.txt') + ) + } else if (specification.sourcePath === goodItem3Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-d', 'good-item3.txt') + ) + } else if (specification.sourcePath === goodItem4Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-d', 'good-item4.txt') + ) + } else if (specification.sourcePath === goodItem5Path) { + expect(specification.destinationPath).toEqual( + path.join('/good-item5.txt') + ) + } else if (specification.sourcePath === extraFileInFolderCPath) { + expect(specification.destinationPath).toEqual( + path.join( + '/folder-a', + 'folder-b', + 'folder-c', + 'extra-file-in-folder-c.txt' + ) + ) + } else if (specification.sourcePath === amazingFileInFolderHPath) { + expect(specification.destinationPath).toEqual( + path.join('/folder-h', 'amazing-item.txt') + ) + } else { + throw new Error( + 'Invalid specification found. This should never be reached' + ) + } + } + }) + + it('Upload Specification - Success with extra slash', async () => { + const rootWithSlash = `${root}/` + const specifications = getUploadZipSpecification( + artifactFilesToUpload, + rootWithSlash + ) + expect(specifications.length).toEqual(7) + + const absolutePaths = specifications.map(item => item.sourcePath) + expect(absolutePaths).toContain(goodItem1Path) + expect(absolutePaths).toContain(goodItem2Path) + expect(absolutePaths).toContain(goodItem3Path) + expect(absolutePaths).toContain(goodItem4Path) + expect(absolutePaths).toContain(goodItem5Path) + expect(absolutePaths).toContain(extraFileInFolderCPath) + expect(absolutePaths).toContain(amazingFileInFolderHPath) + + for (const specification of specifications) { + if (specification.sourcePath === goodItem1Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt') + ) + } else if (specification.sourcePath === goodItem2Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-d', 'good-item2.txt') + ) + } else if (specification.sourcePath === goodItem3Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-d', 'good-item3.txt') + ) + } else if (specification.sourcePath === goodItem4Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-d', 'good-item4.txt') + ) + } else if (specification.sourcePath === goodItem5Path) { + expect(specification.destinationPath).toEqual( + path.join('/good-item5.txt') + ) + } else if (specification.sourcePath === extraFileInFolderCPath) { + expect(specification.destinationPath).toEqual( + path.join( + '/folder-a', + 'folder-b', + 'folder-c', + 'extra-file-in-folder-c.txt' + ) + ) + } else if (specification.sourcePath === amazingFileInFolderHPath) { + expect(specification.destinationPath).toEqual( + path.join('/folder-h', 'amazing-item.txt') + ) + } else { + throw new Error( + 'Invalid specification found. This should never be reached' + ) + } + } + }) + + it('Upload Specification - Empty Directories are included', async () => { + const folderEPath = path.join(root, 'folder-a', 'folder-b', 'folder-e') + const filesWithDirectory = [goodItem1Path, folderEPath] + const specifications = getUploadZipSpecification(filesWithDirectory, root) + expect(specifications.length).toEqual(2) + const absolutePaths = specifications.map(item => item.sourcePath) + expect(absolutePaths).toContain(goodItem1Path) + expect(absolutePaths).toContain(null) + + for (const specification of specifications) { + if (specification.sourcePath === goodItem1Path) { + expect(specification.destinationPath).toEqual( + path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt') + ) + } else if (specification.sourcePath === null) { + expect(specification.destinationPath).toEqual( + path.join('/folder-a', 'folder-b', 'folder-e') + ) + } else { + throw new Error( + 'Invalid specification found. This should never be reached' + ) + } + } + }) +}) diff --git a/actions-toolkit/packages/artifact/__tests__/util.test.ts b/actions-toolkit/packages/artifact/__tests__/util.test.ts new file mode 100644 index 00000000..76fe4e18 --- /dev/null +++ b/actions-toolkit/packages/artifact/__tests__/util.test.ts @@ -0,0 +1,61 @@ +import * as config from '../src/internal/shared/config' +import * as util from '../src/internal/shared/util' + +export const testRuntimeToken = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCIsImlhdCI6MTUxNjIzOTAyMn0.XYnI_wHPBlUi1mqYveJnnkJhp4dlFjqxzRmISPsqfw8' + +describe('get-backend-ids-from-token', () => { + it('should return backend ids when the token is valid', () => { + jest.spyOn(config, 'getRuntimeToken').mockReturnValue(testRuntimeToken) + + const backendIds = util.getBackendIdsFromToken() + expect(backendIds.workflowRunBackendId).toBe( + 'ce7f54c7-61c7-4aae-887f-30da475f5f1a' + ) + expect(backendIds.workflowJobRunBackendId).toBe( + 'ca395085-040a-526b-2ce8-bdc85f692774' + ) + }) + + it("should throw an error when the token doesn't have the right scope", () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCIsImlhdCI6MTUxNjIzOTAyMn0.K0IEoULZteGevF38G94xiaA8zcZ5UlKWfGfqE6q3dhw' + ) + + expect(util.getBackendIdsFromToken).toThrowError( + 'Failed to get backend IDs: The provided JWT token is invalid' + ) + }) + + it('should throw an error when the token has a malformed scope', () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhIiwiaWF0IjoxNTE2MjM5MDIyfQ.7D0_LRfRFRZFImHQ7GxH2S6ZyFjjZ5U0ujjGCfle1XE' + ) + + expect(util.getBackendIdsFromToken).toThrowError( + 'Failed to get backend IDs: The provided JWT token is invalid' + ) + }) + + it('should throw an error when the token is in an invalid format', () => { + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('token') + + expect(util.getBackendIdsFromToken).toThrowError('Invalid token specified') + }) + + it("should throw an error when the token doesn't have the right field", () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' + ) + + expect(util.getBackendIdsFromToken).toThrowError( + 'Failed to get backend IDs: The provided JWT token is invalid' + ) + }) +}) diff --git a/actions-toolkit/packages/artifact/docs/faq.md b/actions-toolkit/packages/artifact/docs/faq.md new file mode 100644 index 00000000..c88d5c11 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/faq.md @@ -0,0 +1,35 @@ +# Frequently Asked Questions + +- [Frequently Asked Questions](#frequently-asked-questions) + - [Supported Characters](#supported-characters) + - [Compression? ZIP? How is my artifact stored?](#compression-zip-how-is-my-artifact-stored) + +## Supported Characters + +When uploading an artifact, the inputted `name` parameter along with the files specified in `files` cannot contain any of the following characters. If they are present in `name` or `files`, the Artifact will be rejected by the server and the upload will fail. These characters are not allowed due to limitations and restrictions with certain file systems such as NTFS. To maintain platform-agnostic behavior, characters that are not supported by an individual filesystem/platform will not be supported on all filesystems/platforms. + +- " +- : +- < +- \> +- | +- \* +- ? + +In addition to the aforementioned characters, the inputted `name` also cannot include the following +- \ +- / + +## Compression? ZIP? How is my artifact stored? + +When creating an Artifact, the files are dynamically compressed and streamed into a ZIP archive. Since they are stored in a ZIP, they can be compressed by Zlib in varying levels. + +The value can range from 0 to 9: + +- 0: No compression +- 1: Best speed +- 6: Default compression (same as GNU Gzip) +- 9: Best compression + +Higher levels will result in better compression, but will take longer to complete. +For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads. diff --git a/actions-toolkit/packages/artifact/docs/generated/README.md b/actions-toolkit/packages/artifact/docs/generated/README.md new file mode 100644 index 00000000..a1244395 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/README.md @@ -0,0 +1,40 @@ +@actions/artifact + +# @actions/artifact + +## Table of contents + +### Classes + +- [ArtifactNotFoundError](classes/ArtifactNotFoundError.md) +- [DefaultArtifactClient](classes/DefaultArtifactClient.md) +- [FilesNotFoundError](classes/FilesNotFoundError.md) +- [GHESNotSupportedError](classes/GHESNotSupportedError.md) +- [InvalidResponseError](classes/InvalidResponseError.md) + +### Interfaces + +- [Artifact](interfaces/Artifact.md) +- [ArtifactClient](interfaces/ArtifactClient.md) +- [DownloadArtifactOptions](interfaces/DownloadArtifactOptions.md) +- [DownloadArtifactResponse](interfaces/DownloadArtifactResponse.md) +- [FindOptions](interfaces/FindOptions.md) +- [GetArtifactResponse](interfaces/GetArtifactResponse.md) +- [ListArtifactsOptions](interfaces/ListArtifactsOptions.md) +- [ListArtifactsResponse](interfaces/ListArtifactsResponse.md) +- [UploadArtifactOptions](interfaces/UploadArtifactOptions.md) +- [UploadArtifactResponse](interfaces/UploadArtifactResponse.md) + +### Variables + +- [default](README.md#default) + +## Variables + +### default + +• `Const` **default**: [`ArtifactClient`](interfaces/ArtifactClient.md) + +#### Defined in + +[src/artifact.ts:7](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/artifact.ts#L7) diff --git a/actions-toolkit/packages/artifact/docs/generated/classes/ArtifactNotFoundError.md b/actions-toolkit/packages/artifact/docs/generated/classes/ArtifactNotFoundError.md new file mode 100644 index 00000000..61700bb3 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/classes/ArtifactNotFoundError.md @@ -0,0 +1,169 @@ +[@actions/artifact](../README.md) / ArtifactNotFoundError + +# Class: ArtifactNotFoundError + +## Hierarchy + +- `Error` + + ↳ **`ArtifactNotFoundError`** + +## Table of contents + +### Constructors + +- [constructor](ArtifactNotFoundError.md#constructor) + +### Properties + +- [message](ArtifactNotFoundError.md#message) +- [name](ArtifactNotFoundError.md#name) +- [stack](ArtifactNotFoundError.md#stack) +- [prepareStackTrace](ArtifactNotFoundError.md#preparestacktrace) +- [stackTraceLimit](ArtifactNotFoundError.md#stacktracelimit) + +### Methods + +- [captureStackTrace](ArtifactNotFoundError.md#capturestacktrace) + +## Constructors + +### constructor + +• **new ArtifactNotFoundError**(`message?`): [`ArtifactNotFoundError`](ArtifactNotFoundError.md) + +#### Parameters + +| Name | Type | Default value | +| :------ | :------ | :------ | +| `message` | `string` | `'Artifact not found'` | + +#### Returns + +[`ArtifactNotFoundError`](ArtifactNotFoundError.md) + +#### Overrides + +Error.constructor + +#### Defined in + +[src/internal/shared/errors.ts:24](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L24) + +## Properties + +### message + +• **message**: `string` + +#### Inherited from + +Error.message + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1068 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +Error.name + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1067 + +___ + +### stack + +• `Optional` **stack**: `string` + +#### Inherited from + +Error.stack + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1069 + +___ + +### prepareStackTrace + +▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` + +#### Type declaration + +▸ (`err`, `stackTraces`): `any` + +Optional override for formatting stack traces + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `err` | `Error` | +| `stackTraces` | `CallSite`[] | + +##### Returns + +`any` + +**`See`** + +https://v8.dev/docs/stack-trace-api#customizing-stack-traces + +#### Inherited from + +Error.prepareStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:11 + +___ + +### stackTraceLimit + +▪ `Static` **stackTraceLimit**: `number` + +#### Inherited from + +Error.stackTraceLimit + +#### Defined in + +node_modules/@types/node/globals.d.ts:13 + +## Methods + +### captureStackTrace + +▸ **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` + +Create .stack property on a target object + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `targetObject` | `object` | +| `constructorOpt?` | `Function` | + +#### Returns + +`void` + +#### Inherited from + +Error.captureStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:4 diff --git a/actions-toolkit/packages/artifact/docs/generated/classes/DefaultArtifactClient.md b/actions-toolkit/packages/artifact/docs/generated/classes/DefaultArtifactClient.md new file mode 100644 index 00000000..b93afc3e --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/classes/DefaultArtifactClient.md @@ -0,0 +1,161 @@ +[@actions/artifact](../README.md) / DefaultArtifactClient + +# Class: DefaultArtifactClient + +The default artifact client that is used by the artifact action(s). + +## Implements + +- [`ArtifactClient`](../interfaces/ArtifactClient.md) + +## Table of contents + +### Constructors + +- [constructor](DefaultArtifactClient.md#constructor) + +### Methods + +- [downloadArtifact](DefaultArtifactClient.md#downloadartifact) +- [getArtifact](DefaultArtifactClient.md#getartifact) +- [listArtifacts](DefaultArtifactClient.md#listartifacts) +- [uploadArtifact](DefaultArtifactClient.md#uploadartifact) + +## Constructors + +### constructor + +• **new DefaultArtifactClient**(): [`DefaultArtifactClient`](DefaultArtifactClient.md) + +#### Returns + +[`DefaultArtifactClient`](DefaultArtifactClient.md) + +## Methods + +### downloadArtifact + +▸ **downloadArtifact**(`artifactId`, `options?`): `Promise`\<[`DownloadArtifactResponse`](../interfaces/DownloadArtifactResponse.md)\> + +Downloads an artifact and unzips the content. + +If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `artifactId` | `number` | The name of the artifact to download | +| `options?` | [`DownloadArtifactOptions`](../interfaces/DownloadArtifactOptions.md) & [`FindOptions`](../interfaces/FindOptions.md) | Extra options that allow for the customization of the download behavior | + +#### Returns + +`Promise`\<[`DownloadArtifactResponse`](../interfaces/DownloadArtifactResponse.md)\> + +single DownloadArtifactResponse object + +#### Implementation of + +[ArtifactClient](../interfaces/ArtifactClient.md).[downloadArtifact](../interfaces/ArtifactClient.md#downloadartifact) + +#### Defined in + +[src/internal/client.ts:119](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L119) + +___ + +### getArtifact + +▸ **getArtifact**(`artifactName`, `options?`): `Promise`\<[`GetArtifactResponse`](../interfaces/GetArtifactResponse.md)\> + +Finds an artifact by name. +If there are multiple artifacts with the same name in the same workflow run, this will return the latest. +If the artifact is not found, it will throw. + +If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs. +https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts +`@actions/artifact` v2+ does not allow for creating multiple artifacts with the same name in the same workflow run. +It is possible to have multiple artifacts with the same name in the same workflow run by using old versions of upload-artifact (v1,v2 and v3), @actions/artifact < v2 or it is a rerun. +If there are multiple artifacts with the same name in the same workflow run this function will return the first artifact that matches the name. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `artifactName` | `string` | The name of the artifact to find | +| `options?` | [`FindOptions`](../interfaces/FindOptions.md) | Extra options that allow for the customization of the get behavior | + +#### Returns + +`Promise`\<[`GetArtifactResponse`](../interfaces/GetArtifactResponse.md)\> + +#### Implementation of + +[ArtifactClient](../interfaces/ArtifactClient.md).[getArtifact](../interfaces/ArtifactClient.md#getartifact) + +#### Defined in + +[src/internal/client.ts:193](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L193) + +___ + +### listArtifacts + +▸ **listArtifacts**(`options?`): `Promise`\<[`ListArtifactsResponse`](../interfaces/ListArtifactsResponse.md)\> + +Lists all artifacts that are part of the current workflow run. +This function will return at most 1000 artifacts per workflow run. + +If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs. +https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `options?` | [`ListArtifactsOptions`](../interfaces/ListArtifactsOptions.md) & [`FindOptions`](../interfaces/FindOptions.md) | Extra options that allow for the customization of the list behavior | + +#### Returns + +`Promise`\<[`ListArtifactsResponse`](../interfaces/ListArtifactsResponse.md)\> + +ListArtifactResponse object + +#### Implementation of + +[ArtifactClient](../interfaces/ArtifactClient.md).[listArtifacts](../interfaces/ArtifactClient.md#listartifacts) + +#### Defined in + +[src/internal/client.ts:157](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L157) + +___ + +### uploadArtifact + +▸ **uploadArtifact**(`name`, `files`, `rootDirectory`, `options?`): `Promise`\<[`UploadArtifactResponse`](../interfaces/UploadArtifactResponse.md)\> + +Uploads an artifact. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The name of the artifact, required | +| `files` | `string`[] | A list of absolute or relative paths that denote what files should be uploaded | +| `rootDirectory` | `string` | An absolute or relative file path that denotes the root parent directory of the files being uploaded | +| `options?` | [`UploadArtifactOptions`](../interfaces/UploadArtifactOptions.md) | Extra options for customizing the upload behavior | + +#### Returns + +`Promise`\<[`UploadArtifactResponse`](../interfaces/UploadArtifactResponse.md)\> + +single UploadArtifactResponse object + +#### Implementation of + +[ArtifactClient](../interfaces/ArtifactClient.md).[uploadArtifact](../interfaces/ArtifactClient.md#uploadartifact) + +#### Defined in + +[src/internal/client.ts:94](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L94) diff --git a/actions-toolkit/packages/artifact/docs/generated/classes/FilesNotFoundError.md b/actions-toolkit/packages/artifact/docs/generated/classes/FilesNotFoundError.md new file mode 100644 index 00000000..c7d22014 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/classes/FilesNotFoundError.md @@ -0,0 +1,180 @@ +[@actions/artifact](../README.md) / FilesNotFoundError + +# Class: FilesNotFoundError + +## Hierarchy + +- `Error` + + ↳ **`FilesNotFoundError`** + +## Table of contents + +### Constructors + +- [constructor](FilesNotFoundError.md#constructor) + +### Properties + +- [files](FilesNotFoundError.md#files) +- [message](FilesNotFoundError.md#message) +- [name](FilesNotFoundError.md#name) +- [stack](FilesNotFoundError.md#stack) +- [prepareStackTrace](FilesNotFoundError.md#preparestacktrace) +- [stackTraceLimit](FilesNotFoundError.md#stacktracelimit) + +### Methods + +- [captureStackTrace](FilesNotFoundError.md#capturestacktrace) + +## Constructors + +### constructor + +• **new FilesNotFoundError**(`files?`): [`FilesNotFoundError`](FilesNotFoundError.md) + +#### Parameters + +| Name | Type | Default value | +| :------ | :------ | :------ | +| `files` | `string`[] | `[]` | + +#### Returns + +[`FilesNotFoundError`](FilesNotFoundError.md) + +#### Overrides + +Error.constructor + +#### Defined in + +[src/internal/shared/errors.ts:4](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L4) + +## Properties + +### files + +• **files**: `string`[] + +#### Defined in + +[src/internal/shared/errors.ts:2](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L2) + +___ + +### message + +• **message**: `string` + +#### Inherited from + +Error.message + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1068 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +Error.name + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1067 + +___ + +### stack + +• `Optional` **stack**: `string` + +#### Inherited from + +Error.stack + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1069 + +___ + +### prepareStackTrace + +▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` + +#### Type declaration + +▸ (`err`, `stackTraces`): `any` + +Optional override for formatting stack traces + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `err` | `Error` | +| `stackTraces` | `CallSite`[] | + +##### Returns + +`any` + +**`See`** + +https://v8.dev/docs/stack-trace-api#customizing-stack-traces + +#### Inherited from + +Error.prepareStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:11 + +___ + +### stackTraceLimit + +▪ `Static` **stackTraceLimit**: `number` + +#### Inherited from + +Error.stackTraceLimit + +#### Defined in + +node_modules/@types/node/globals.d.ts:13 + +## Methods + +### captureStackTrace + +▸ **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` + +Create .stack property on a target object + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `targetObject` | `object` | +| `constructorOpt?` | `Function` | + +#### Returns + +`void` + +#### Inherited from + +Error.captureStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:4 diff --git a/actions-toolkit/packages/artifact/docs/generated/classes/GHESNotSupportedError.md b/actions-toolkit/packages/artifact/docs/generated/classes/GHESNotSupportedError.md new file mode 100644 index 00000000..942ae287 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/classes/GHESNotSupportedError.md @@ -0,0 +1,169 @@ +[@actions/artifact](../README.md) / GHESNotSupportedError + +# Class: GHESNotSupportedError + +## Hierarchy + +- `Error` + + ↳ **`GHESNotSupportedError`** + +## Table of contents + +### Constructors + +- [constructor](GHESNotSupportedError.md#constructor) + +### Properties + +- [message](GHESNotSupportedError.md#message) +- [name](GHESNotSupportedError.md#name) +- [stack](GHESNotSupportedError.md#stack) +- [prepareStackTrace](GHESNotSupportedError.md#preparestacktrace) +- [stackTraceLimit](GHESNotSupportedError.md#stacktracelimit) + +### Methods + +- [captureStackTrace](GHESNotSupportedError.md#capturestacktrace) + +## Constructors + +### constructor + +• **new GHESNotSupportedError**(`message?`): [`GHESNotSupportedError`](GHESNotSupportedError.md) + +#### Parameters + +| Name | Type | Default value | +| :------ | :------ | :------ | +| `message` | `string` | `'@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.'` | + +#### Returns + +[`GHESNotSupportedError`](GHESNotSupportedError.md) + +#### Overrides + +Error.constructor + +#### Defined in + +[src/internal/shared/errors.ts:31](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L31) + +## Properties + +### message + +• **message**: `string` + +#### Inherited from + +Error.message + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1068 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +Error.name + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1067 + +___ + +### stack + +• `Optional` **stack**: `string` + +#### Inherited from + +Error.stack + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1069 + +___ + +### prepareStackTrace + +▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` + +#### Type declaration + +▸ (`err`, `stackTraces`): `any` + +Optional override for formatting stack traces + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `err` | `Error` | +| `stackTraces` | `CallSite`[] | + +##### Returns + +`any` + +**`See`** + +https://v8.dev/docs/stack-trace-api#customizing-stack-traces + +#### Inherited from + +Error.prepareStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:11 + +___ + +### stackTraceLimit + +▪ `Static` **stackTraceLimit**: `number` + +#### Inherited from + +Error.stackTraceLimit + +#### Defined in + +node_modules/@types/node/globals.d.ts:13 + +## Methods + +### captureStackTrace + +▸ **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` + +Create .stack property on a target object + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `targetObject` | `object` | +| `constructorOpt?` | `Function` | + +#### Returns + +`void` + +#### Inherited from + +Error.captureStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:4 diff --git a/actions-toolkit/packages/artifact/docs/generated/classes/InvalidResponseError.md b/actions-toolkit/packages/artifact/docs/generated/classes/InvalidResponseError.md new file mode 100644 index 00000000..1f6b0b5b --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/classes/InvalidResponseError.md @@ -0,0 +1,169 @@ +[@actions/artifact](../README.md) / InvalidResponseError + +# Class: InvalidResponseError + +## Hierarchy + +- `Error` + + ↳ **`InvalidResponseError`** + +## Table of contents + +### Constructors + +- [constructor](InvalidResponseError.md#constructor) + +### Properties + +- [message](InvalidResponseError.md#message) +- [name](InvalidResponseError.md#name) +- [stack](InvalidResponseError.md#stack) +- [prepareStackTrace](InvalidResponseError.md#preparestacktrace) +- [stackTraceLimit](InvalidResponseError.md#stacktracelimit) + +### Methods + +- [captureStackTrace](InvalidResponseError.md#capturestacktrace) + +## Constructors + +### constructor + +• **new InvalidResponseError**(`message`): [`InvalidResponseError`](InvalidResponseError.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `message` | `string` | + +#### Returns + +[`InvalidResponseError`](InvalidResponseError.md) + +#### Overrides + +Error.constructor + +#### Defined in + +[src/internal/shared/errors.ts:17](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L17) + +## Properties + +### message + +• **message**: `string` + +#### Inherited from + +Error.message + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1068 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +Error.name + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1067 + +___ + +### stack + +• `Optional` **stack**: `string` + +#### Inherited from + +Error.stack + +#### Defined in + +node_modules/typescript/lib/lib.es5.d.ts:1069 + +___ + +### prepareStackTrace + +▪ `Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any` + +#### Type declaration + +▸ (`err`, `stackTraces`): `any` + +Optional override for formatting stack traces + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `err` | `Error` | +| `stackTraces` | `CallSite`[] | + +##### Returns + +`any` + +**`See`** + +https://v8.dev/docs/stack-trace-api#customizing-stack-traces + +#### Inherited from + +Error.prepareStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:11 + +___ + +### stackTraceLimit + +▪ `Static` **stackTraceLimit**: `number` + +#### Inherited from + +Error.stackTraceLimit + +#### Defined in + +node_modules/@types/node/globals.d.ts:13 + +## Methods + +### captureStackTrace + +▸ **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` + +Create .stack property on a target object + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `targetObject` | `object` | +| `constructorOpt?` | `Function` | + +#### Returns + +`void` + +#### Inherited from + +Error.captureStackTrace + +#### Defined in + +node_modules/@types/node/globals.d.ts:4 diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/Artifact.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/Artifact.md new file mode 100644 index 00000000..1597daa5 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/Artifact.md @@ -0,0 +1,62 @@ +[@actions/artifact](../README.md) / Artifact + +# Interface: Artifact + +An Actions Artifact + +## Table of contents + +### Properties + +- [createdAt](Artifact.md#createdat) +- [id](Artifact.md#id) +- [name](Artifact.md#name) +- [size](Artifact.md#size) + +## Properties + +### createdAt + +• `Optional` **createdAt**: `Date` + +The time when the artifact was created + +#### Defined in + +[src/internal/shared/interfaces.ts:123](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L123) + +___ + +### id + +• **id**: `number` + +The ID of the artifact + +#### Defined in + +[src/internal/shared/interfaces.ts:113](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L113) + +___ + +### name + +• **name**: `string` + +The name of the artifact + +#### Defined in + +[src/internal/shared/interfaces.ts:108](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L108) + +___ + +### size + +• **size**: `number` + +The size of the artifact in bytes + +#### Defined in + +[src/internal/shared/interfaces.ts:118](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L118) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/ArtifactClient.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/ArtifactClient.md new file mode 100644 index 00000000..39f7d923 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/ArtifactClient.md @@ -0,0 +1,131 @@ +[@actions/artifact](../README.md) / ArtifactClient + +# Interface: ArtifactClient + +Generic interface for the artifact client. + +## Implemented by + +- [`DefaultArtifactClient`](../classes/DefaultArtifactClient.md) + +## Table of contents + +### Methods + +- [downloadArtifact](ArtifactClient.md#downloadartifact) +- [getArtifact](ArtifactClient.md#getartifact) +- [listArtifacts](ArtifactClient.md#listartifacts) +- [uploadArtifact](ArtifactClient.md#uploadartifact) + +## Methods + +### downloadArtifact + +▸ **downloadArtifact**(`artifactId`, `options?`): `Promise`\<[`DownloadArtifactResponse`](DownloadArtifactResponse.md)\> + +Downloads an artifact and unzips the content. + +If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `artifactId` | `number` | The name of the artifact to download | +| `options?` | [`DownloadArtifactOptions`](DownloadArtifactOptions.md) & [`FindOptions`](FindOptions.md) | Extra options that allow for the customization of the download behavior | + +#### Returns + +`Promise`\<[`DownloadArtifactResponse`](DownloadArtifactResponse.md)\> + +single DownloadArtifactResponse object + +#### Defined in + +[src/internal/client.ts:84](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L84) + +___ + +### getArtifact + +▸ **getArtifact**(`artifactName`, `options?`): `Promise`\<[`GetArtifactResponse`](GetArtifactResponse.md)\> + +Finds an artifact by name. +If there are multiple artifacts with the same name in the same workflow run, this will return the latest. +If the artifact is not found, it will throw. + +If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs. +https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts +`@actions/artifact` v2+ does not allow for creating multiple artifacts with the same name in the same workflow run. +It is possible to have multiple artifacts with the same name in the same workflow run by using old versions of upload-artifact (v1,v2 and v3), @actions/artifact < v2 or it is a rerun. +If there are multiple artifacts with the same name in the same workflow run this function will return the first artifact that matches the name. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `artifactName` | `string` | The name of the artifact to find | +| `options?` | [`FindOptions`](FindOptions.md) | Extra options that allow for the customization of the get behavior | + +#### Returns + +`Promise`\<[`GetArtifactResponse`](GetArtifactResponse.md)\> + +#### Defined in + +[src/internal/client.ts:70](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L70) + +___ + +### listArtifacts + +▸ **listArtifacts**(`options?`): `Promise`\<[`ListArtifactsResponse`](ListArtifactsResponse.md)\> + +Lists all artifacts that are part of the current workflow run. +This function will return at most 1000 artifacts per workflow run. + +If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs. +https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `options?` | [`ListArtifactsOptions`](ListArtifactsOptions.md) & [`FindOptions`](FindOptions.md) | Extra options that allow for the customization of the list behavior | + +#### Returns + +`Promise`\<[`ListArtifactsResponse`](ListArtifactsResponse.md)\> + +ListArtifactResponse object + +#### Defined in + +[src/internal/client.ts:52](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L52) + +___ + +### uploadArtifact + +▸ **uploadArtifact**(`name`, `files`, `rootDirectory`, `options?`): `Promise`\<[`UploadArtifactResponse`](UploadArtifactResponse.md)\> + +Uploads an artifact. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The name of the artifact, required | +| `files` | `string`[] | A list of absolute or relative paths that denote what files should be uploaded | +| `rootDirectory` | `string` | An absolute or relative file path that denotes the root parent directory of the files being uploaded | +| `options?` | [`UploadArtifactOptions`](UploadArtifactOptions.md) | Extra options for customizing the upload behavior | + +#### Returns + +`Promise`\<[`UploadArtifactResponse`](UploadArtifactResponse.md)\> + +single UploadArtifactResponse object + +#### Defined in + +[src/internal/client.ts:35](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L35) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/DownloadArtifactOptions.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/DownloadArtifactOptions.md new file mode 100644 index 00000000..8eff4d8f --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/DownloadArtifactOptions.md @@ -0,0 +1,23 @@ +[@actions/artifact](../README.md) / DownloadArtifactOptions + +# Interface: DownloadArtifactOptions + +Options for downloading an artifact + +## Table of contents + +### Properties + +- [path](DownloadArtifactOptions.md#path) + +## Properties + +### path + +• `Optional` **path**: `string` + +Denotes where the artifact will be downloaded to. If not specified then the artifact is download to GITHUB_WORKSPACE + +#### Defined in + +[src/internal/shared/interfaces.ts:98](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L98) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/DownloadArtifactResponse.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/DownloadArtifactResponse.md new file mode 100644 index 00000000..b2220364 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/DownloadArtifactResponse.md @@ -0,0 +1,23 @@ +[@actions/artifact](../README.md) / DownloadArtifactResponse + +# Interface: DownloadArtifactResponse + +Response from the server when downloading an artifact + +## Table of contents + +### Properties + +- [downloadPath](DownloadArtifactResponse.md#downloadpath) + +## Properties + +### downloadPath + +• `Optional` **downloadPath**: `string` + +The path where the artifact was downloaded to + +#### Defined in + +[src/internal/shared/interfaces.ts:88](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L88) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/FindOptions.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/FindOptions.md new file mode 100644 index 00000000..68f56706 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/FindOptions.md @@ -0,0 +1,30 @@ +[@actions/artifact](../README.md) / FindOptions + +# Interface: FindOptions + +## Table of contents + +### Properties + +- [findBy](FindOptions.md#findby) + +## Properties + +### findBy + +• `Optional` **findBy**: `Object` + +The criteria for finding Artifact(s) out of the scope of the current run. + +#### Type declaration + +| Name | Type | Description | +| :------ | :------ | :------ | +| `repositoryName` | `string` | Repository owner (eg. 'toolkit') | +| `repositoryOwner` | `string` | Repository owner (eg. 'actions') | +| `token` | `string` | Token with actions:read permissions | +| `workflowRunId` | `number` | WorkflowRun of the artifact(s) to lookup | + +#### Defined in + +[src/internal/shared/interfaces.ts:131](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L131) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/GetArtifactResponse.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/GetArtifactResponse.md new file mode 100644 index 00000000..f4d56509 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/GetArtifactResponse.md @@ -0,0 +1,23 @@ +[@actions/artifact](../README.md) / GetArtifactResponse + +# Interface: GetArtifactResponse + +Response from the server when getting an artifact + +## Table of contents + +### Properties + +- [artifact](GetArtifactResponse.md#artifact) + +## Properties + +### artifact + +• **artifact**: [`Artifact`](Artifact.md) + +Metadata about the artifact that was found + +#### Defined in + +[src/internal/shared/interfaces.ts:57](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L57) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/ListArtifactsOptions.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/ListArtifactsOptions.md new file mode 100644 index 00000000..27e3d5f7 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/ListArtifactsOptions.md @@ -0,0 +1,24 @@ +[@actions/artifact](../README.md) / ListArtifactsOptions + +# Interface: ListArtifactsOptions + +Options for listing artifacts + +## Table of contents + +### Properties + +- [latest](ListArtifactsOptions.md#latest) + +## Properties + +### latest + +• `Optional` **latest**: `boolean` + +Filter the workflow run's artifacts to the latest by name +In the case of reruns, this can be useful to avoid duplicates + +#### Defined in + +[src/internal/shared/interfaces.ts:68](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L68) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/ListArtifactsResponse.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/ListArtifactsResponse.md new file mode 100644 index 00000000..836cbbd7 --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/ListArtifactsResponse.md @@ -0,0 +1,23 @@ +[@actions/artifact](../README.md) / ListArtifactsResponse + +# Interface: ListArtifactsResponse + +Response from the server when listing artifacts + +## Table of contents + +### Properties + +- [artifacts](ListArtifactsResponse.md#artifacts) + +## Properties + +### artifacts + +• **artifacts**: [`Artifact`](Artifact.md)[] + +A list of artifacts that were found + +#### Defined in + +[src/internal/shared/interfaces.ts:78](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L78) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/UploadArtifactOptions.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/UploadArtifactOptions.md new file mode 100644 index 00000000..acff13df --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/UploadArtifactOptions.md @@ -0,0 +1,55 @@ +[@actions/artifact](../README.md) / UploadArtifactOptions + +# Interface: UploadArtifactOptions + +Options for uploading an artifact + +## Table of contents + +### Properties + +- [compressionLevel](UploadArtifactOptions.md#compressionlevel) +- [retentionDays](UploadArtifactOptions.md#retentiondays) + +## Properties + +### compressionLevel + +• `Optional` **compressionLevel**: `number` + +The level of compression for Zlib to be applied to the artifact archive. +The value can range from 0 to 9: +- 0: No compression +- 1: Best speed +- 6: Default compression (same as GNU Gzip) +- 9: Best compression +Higher levels will result in better compression, but will take longer to complete. +For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads. + +#### Defined in + +[src/internal/shared/interfaces.ts:47](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L47) + +___ + +### retentionDays + +• `Optional` **retentionDays**: `number` + +Duration after which artifact will expire in days. + +By default artifact expires after 90 days: +https://docs.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts#downloading-and-deleting-artifacts-after-a-workflow-run-is-complete + +Use this option to override the default expiry. + +Min value: 1 +Max value: 90 unless changed by repository setting + +If this is set to a greater value than the retention settings allowed, the retention on artifacts +will be reduced to match the max value allowed on server, and the upload process will continue. An +input of 0 assumes default retention setting. + +#### Defined in + +[src/internal/shared/interfaces.ts:36](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L36) diff --git a/actions-toolkit/packages/artifact/docs/generated/interfaces/UploadArtifactResponse.md b/actions-toolkit/packages/artifact/docs/generated/interfaces/UploadArtifactResponse.md new file mode 100644 index 00000000..574366fc --- /dev/null +++ b/actions-toolkit/packages/artifact/docs/generated/interfaces/UploadArtifactResponse.md @@ -0,0 +1,37 @@ +[@actions/artifact](../README.md) / UploadArtifactResponse + +# Interface: UploadArtifactResponse + +Response from the server when an artifact is uploaded + +## Table of contents + +### Properties + +- [id](UploadArtifactResponse.md#id) +- [size](UploadArtifactResponse.md#size) + +## Properties + +### id + +• `Optional` **id**: `number` + +The id of the artifact that was created. Not provided if no artifact was uploaded +This ID can be used as input to other APIs to download, delete or get more information about an artifact: https://docs.github.com/en/rest/actions/artifacts + +#### Defined in + +[src/internal/shared/interfaces.ts:14](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L14) + +___ + +### size + +• `Optional` **size**: `number` + +Total size of the artifact in bytes. Not provided if no artifact was uploaded + +#### Defined in + +[src/internal/shared/interfaces.ts:8](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L8) diff --git a/actions-toolkit/packages/artifact/package-lock.json b/actions-toolkit/packages/artifact/package-lock.json new file mode 100644 index 00000000..26967402 --- /dev/null +++ b/actions-toolkit/packages/artifact/package-lock.json @@ -0,0 +1,1652 @@ +{ + "name": "@actions/artifact", + "version": "2.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@actions/artifact", + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/github": "^5.1.1", + "@actions/http-client": "^2.1.0", + "@azure/storage-blob": "^12.15.0", + "@octokit/core": "^3.5.1", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-retry": "^3.0.9", + "@octokit/request-error": "^5.0.0", + "@protobuf-ts/plugin": "^2.2.3-alpha.1", + "@types/unzipper": "^0.10.6", + "archiver": "^5.3.1", + "crypto": "^1.0.1", + "jwt-decode": "^3.1.2", + "twirp-ts": "^2.5.0", + "unzipper": "^0.10.14" + }, + "devDependencies": { + "@types/archiver": "^5.3.2", + "typedoc": "^0.25.4", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.2.2" + } + }, + "node_modules/@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/github": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", + "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", + "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-http": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz", + "integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz", + "integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.15.0.tgz", + "integrity": "sha512-e7JBKLOFi0QVJqqLzrjx1eL3je3/Ug2IQj24cTM9b85CsnnFjLGeGjJVIjbGGZaytewiCEG7r3lRwQX7fKj0/w==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/core/node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", + "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", + "dependencies": { + "@octokit/types": "^6.0.3", + "bottleneck": "^2.15.3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz", + "integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==", + "dependencies": { + "@octokit/types": "^11.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", + "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==" + }, + "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz", + "integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==", + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/@octokit/request/node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@protobuf-ts/plugin": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.9.1.tgz", + "integrity": "sha512-svkFSyFgTtaLdDFPGvd6cTu8qK5Nul5RizDCTcv0xWRzcPWtMiqbuCLKCv6E9gdpnAs3MPeQTnSABB2+NhfWBg==", + "dependencies": { + "@protobuf-ts/plugin-framework": "^2.9.1", + "@protobuf-ts/protoc": "^2.9.1", + "@protobuf-ts/runtime": "^2.9.1", + "@protobuf-ts/runtime-rpc": "^2.9.1", + "typescript": "^3.9" + }, + "bin": { + "protoc-gen-dump": "bin/protoc-gen-dump", + "protoc-gen-ts": "bin/protoc-gen-ts" + } + }, + "node_modules/@protobuf-ts/plugin-framework": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin-framework/-/plugin-framework-2.9.1.tgz", + "integrity": "sha512-/4sHdsXjp6KKkbpcCLUHpMfdYsCaqqQHRAwoxVzHmltsotw06B/K9HglZtkQx0IpLO4eeF3vNr3n7qzjD3e2zA==", + "dependencies": { + "@protobuf-ts/runtime": "^2.9.1", + "typescript": "^3.9" + } + }, + "node_modules/@protobuf-ts/plugin-framework/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@protobuf-ts/plugin/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@protobuf-ts/protoc": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.9.1.tgz", + "integrity": "sha512-/q2iVDwVDijfZlFZnnm3W6ALbybNskNSww88TfYBaJH49PuQMqhcXUPRu28UouJr9sc/Lr5k6t0TB9Nff3UIsA==", + "bin": { + "protoc": "protoc.js" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.9.1.tgz", + "integrity": "sha512-ZTc8b+pQ6bwxZa3qg9/IO/M/brRkvr0tic9cSGgAsDByfPrtatT2300wTIRLDk8X9WTW1tT+FhyqmcrbMHTeww==" + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.1.tgz", + "integrity": "sha512-pzO20J6s07LTWcj8hKAXh/dAacU5HIVir6SANKXXH8G0pn0VIIB4FFECq5Hbv25/8PQoOGZ7iApq/DMHaSjGhg==", + "dependencies": { + "@protobuf-ts/runtime": "^2.9.1" + } + }, + "node_modules/@types/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==", + "dev": true, + "dependencies": { + "@types/readdir-glob": "*" + } + }, + "node_modules/@types/node": { + "version": "20.4.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", + "integrity": "sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/readdir-glob": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.1.tgz", + "integrity": "sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unzipper": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.6.tgz", + "integrity": "sha512-zcBj329AHgKLQyz209N/S9R0GZqXSkUQO4tJSYE3x02qg4JuDFpgKMj50r82Erk1natCWQDIvSccDddt7jPzjA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, + "node_modules/archiver": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", + "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.3", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.0.0", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compress-commons": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", + "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/dot-object": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-2.1.4.tgz", + "integrity": "sha512-7FXnyyCLFawNYJ+NhkqyP9Wd2yzuo+7n9pGiYpkmXCTYa8Ci2U0eUNDVg5OuO5Pm6aFXI2SWN8/N/w7SJWu1WA==", + "dependencies": { + "commander": "^4.0.0", + "glob": "^7.1.5" + }, + "bin": { + "dot-object": "bin/dot-object" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/shiki": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", + "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "engines": { + "node": "*" + } + }, + "node_modules/ts-poet": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-4.15.0.tgz", + "integrity": "sha512-sLLR8yQBvHzi9d4R1F4pd+AzQxBfzOSSjfxiJxQhkUoH5bL7RsAC6wgvtVUQdGqiCsyS9rT6/8X2FI7ipdir5g==", + "dependencies": { + "lodash": "^4.17.15", + "prettier": "^2.5.1" + } + }, + "node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/twirp-ts": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/twirp-ts/-/twirp-ts-2.5.0.tgz", + "integrity": "sha512-JTKIK5Pf/+3qCrmYDFlqcPPUx+ohEWKBaZy8GL8TmvV2VvC0SXVyNYILO39+GCRbqnuP6hBIF+BVr8ZxRz+6fw==", + "dependencies": { + "@protobuf-ts/plugin-framework": "^2.0.7", + "camel-case": "^4.1.2", + "dot-object": "^2.1.4", + "path-to-regexp": "^6.2.0", + "ts-poet": "^4.5.0", + "yaml": "^1.10.2" + }, + "bin": { + "protoc-gen-twirp_ts": "protoc-gen-twirp_ts" + }, + "peerDependencies": { + "@protobuf-ts/plugin": "^2.5.0", + "ts-proto": "^1.81.3" + }, + "peerDependenciesMeta": { + "@protobuf-ts/plugin": { + "optional": true + }, + "ts-proto": { + "optional": true + } + } + }, + "node_modules/typedoc": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.4.tgz", + "integrity": "sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/zip-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", + "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", + "dependencies": { + "archiver-utils": "^2.1.0", + "compress-commons": "^4.1.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + } + } +} diff --git a/actions-toolkit/packages/artifact/package.json b/actions-toolkit/packages/artifact/package.json new file mode 100644 index 00000000..894e1607 --- /dev/null +++ b/actions-toolkit/packages/artifact/package.json @@ -0,0 +1,65 @@ +{ + "name": "@actions/artifact", + "version": "2.0.0", + "preview": true, + "description": "Actions artifact lib", + "keywords": [ + "github", + "actions", + "artifact" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/artifact", + "license": "MIT", + "main": "lib/artifact.js", + "types": "lib/artifact.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/artifact" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "cd ../../ && npm run test ./packages/artifact", + "bootstrap": "cd ../../ && npm run bootstrap", + "tsc-run": "tsc", + "tsc": "npm run bootstrap && npm run tsc-run", + "gen:docs": "typedoc --plugin typedoc-plugin-markdown --out docs/generated src/artifact.ts --githubPages false --readme none" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/github": "^5.1.1", + "@actions/http-client": "^2.1.0", + "@azure/storage-blob": "^12.15.0", + "@octokit/core": "^3.5.1", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-retry": "^3.0.9", + "@octokit/request-error": "^5.0.0", + "@protobuf-ts/plugin": "^2.2.3-alpha.1", + "@types/unzipper": "^0.10.6", + "archiver": "^5.3.1", + "crypto": "^1.0.1", + "jwt-decode": "^3.1.2", + "twirp-ts": "^2.5.0", + "unzipper": "^0.10.14" + }, + "devDependencies": { + "@types/archiver": "^5.3.2", + "typedoc": "^0.25.4", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.2.2" + } +} diff --git a/actions-toolkit/packages/artifact/src/artifact.ts b/actions-toolkit/packages/artifact/src/artifact.ts new file mode 100644 index 00000000..f5d91445 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/artifact.ts @@ -0,0 +1,8 @@ +import {ArtifactClient, DefaultArtifactClient} from './internal/client' + +export * from './internal/shared/interfaces' +export * from './internal/shared/errors' +export * from './internal/client' + +const client: ArtifactClient = new DefaultArtifactClient() +export default client diff --git a/actions-toolkit/packages/artifact/src/generated/google/protobuf/timestamp.ts b/actions-toolkit/packages/artifact/src/generated/google/protobuf/timestamp.ts new file mode 100644 index 00000000..c9aee41a --- /dev/null +++ b/actions-toolkit/packages/artifact/src/generated/google/protobuf/timestamp.ts @@ -0,0 +1,277 @@ +// @generated by protobuf-ts 2.9.1 with parameter long_type_string,client_none,generate_dependencies +// @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3) +// tslint:disable +// +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; +import type { IBinaryWriter } from "@protobuf-ts/runtime"; +import { WireType } from "@protobuf-ts/runtime"; +import type { BinaryReadOptions } from "@protobuf-ts/runtime"; +import type { IBinaryReader } from "@protobuf-ts/runtime"; +import { UnknownFieldHandler } from "@protobuf-ts/runtime"; +import type { PartialMessage } from "@protobuf-ts/runtime"; +import { reflectionMergePartial } from "@protobuf-ts/runtime"; +import { MESSAGE_TYPE } from "@protobuf-ts/runtime"; +import { typeofJsonValue } from "@protobuf-ts/runtime"; +import type { JsonValue } from "@protobuf-ts/runtime"; +import type { JsonReadOptions } from "@protobuf-ts/runtime"; +import type { JsonWriteOptions } from "@protobuf-ts/runtime"; +import { PbLong } from "@protobuf-ts/runtime"; +import { MessageType } from "@protobuf-ts/runtime"; +/** + * A Timestamp represents a point in time independent of any time zone + * or calendar, represented as seconds and fractions of seconds at + * nanosecond resolution in UTC Epoch time. It is encoded using the + * Proleptic Gregorian Calendar which extends the Gregorian calendar + * backwards to year one. It is encoded assuming all minutes are 60 + * seconds long, i.e. leap seconds are "smeared" so that no leap second + * table is needed for interpretation. Range is from + * 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. + * By restricting to that range, we ensure that we can convert to + * and from RFC 3339 date strings. + * See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). + * + * # Examples + * + * Example 1: Compute Timestamp from POSIX `time()`. + * + * Timestamp timestamp; + * timestamp.set_seconds(time(NULL)); + * timestamp.set_nanos(0); + * + * Example 2: Compute Timestamp from POSIX `gettimeofday()`. + * + * struct timeval tv; + * gettimeofday(&tv, NULL); + * + * Timestamp timestamp; + * timestamp.set_seconds(tv.tv_sec); + * timestamp.set_nanos(tv.tv_usec * 1000); + * + * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + * + * FILETIME ft; + * GetSystemTimeAsFileTime(&ft); + * UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + * + * // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + * // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + * Timestamp timestamp; + * timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + * timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + * + * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + * + * long millis = System.currentTimeMillis(); + * + * Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + * .setNanos((int) ((millis % 1000) * 1000000)).build(); + * + * + * Example 5: Compute Timestamp from current time in Python. + * + * timestamp = Timestamp() + * timestamp.GetCurrentTime() + * + * # JSON Mapping + * + * In JSON format, the Timestamp type is encoded as a string in the + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the + * format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" + * where {year} is always expressed using four digits while {month}, {day}, + * {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional + * seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), + * are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone + * is required. A proto3 JSON serializer should always use UTC (as indicated by + * "Z") when printing the Timestamp type and a proto3 JSON parser should be + * able to accept both UTC and other timezones (as indicated by an offset). + * + * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past + * 01:30 UTC on January 15, 2017. + * + * In JavaScript, one can convert a Date object to this format using the + * standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] + * method. In Python, a standard `datetime.datetime` object can be converted + * to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) + * with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one + * can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( + * http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime-- + * ) to obtain a formatter capable of generating timestamps in this format. + * + * + * + * @generated from protobuf message google.protobuf.Timestamp + */ +export interface Timestamp { + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + * + * @generated from protobuf field: int64 seconds = 1; + */ + seconds: string; + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + * + * @generated from protobuf field: int32 nanos = 2; + */ + nanos: number; +} +// @generated message type with reflection information, may provide speed optimized methods +class Timestamp$Type extends MessageType { + constructor() { + super("google.protobuf.Timestamp", [ + { no: 1, name: "seconds", kind: "scalar", T: 3 /*ScalarType.INT64*/ }, + { no: 2, name: "nanos", kind: "scalar", T: 5 /*ScalarType.INT32*/ } + ]); + } + /** + * Creates a new `Timestamp` for the current time. + */ + now(): Timestamp { + const msg = this.create(); + const ms = Date.now(); + msg.seconds = PbLong.from(Math.floor(ms / 1000)).toString(); + msg.nanos = (ms % 1000) * 1000000; + return msg; + } + /** + * Converts a `Timestamp` to a JavaScript Date. + */ + toDate(message: Timestamp): Date { + return new Date(PbLong.from(message.seconds).toNumber() * 1000 + Math.ceil(message.nanos / 1000000)); + } + /** + * Converts a JavaScript Date to a `Timestamp`. + */ + fromDate(date: Date): Timestamp { + const msg = this.create(); + const ms = date.getTime(); + msg.seconds = PbLong.from(Math.floor(ms / 1000)).toString(); + msg.nanos = (ms % 1000) * 1000000; + return msg; + } + /** + * In JSON format, the `Timestamp` type is encoded as a string + * in the RFC 3339 format. + */ + internalJsonWrite(message: Timestamp, options: JsonWriteOptions): JsonValue { + let ms = PbLong.from(message.seconds).toNumber() * 1000; + if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z")) + throw new Error("Unable to encode Timestamp to JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive."); + if (message.nanos < 0) + throw new Error("Unable to encode invalid Timestamp to JSON. Nanos must not be negative."); + let z = "Z"; + if (message.nanos > 0) { + let nanosStr = (message.nanos + 1000000000).toString().substring(1); + if (nanosStr.substring(3) === "000000") + z = "." + nanosStr.substring(0, 3) + "Z"; + else if (nanosStr.substring(6) === "000") + z = "." + nanosStr.substring(0, 6) + "Z"; + else + z = "." + nanosStr + "Z"; + } + return new Date(ms).toISOString().replace(".000Z", z); + } + /** + * In JSON format, the `Timestamp` type is encoded as a string + * in the RFC 3339 format. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: Timestamp): Timestamp { + if (typeof json !== "string") + throw new Error("Unable to parse Timestamp from JSON " + typeofJsonValue(json) + "."); + let matches = json.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:Z|\.([0-9]{3,9})Z|([+-][0-9][0-9]:[0-9][0-9]))$/); + if (!matches) + throw new Error("Unable to parse Timestamp from JSON. Invalid format."); + let ms = Date.parse(matches[1] + "-" + matches[2] + "-" + matches[3] + "T" + matches[4] + ":" + matches[5] + ":" + matches[6] + (matches[8] ? matches[8] : "Z")); + if (Number.isNaN(ms)) + throw new Error("Unable to parse Timestamp from JSON. Invalid value."); + if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z")) + throw new globalThis.Error("Unable to parse Timestamp from JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive."); + if (!target) + target = this.create(); + target.seconds = PbLong.from(ms / 1000).toString(); + target.nanos = 0; + if (matches[7]) + target.nanos = (parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) - 1000000000); + return target; + } + create(value?: PartialMessage): Timestamp { + const message = { seconds: "0", nanos: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Timestamp): Timestamp { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* int64 seconds */ 1: + message.seconds = reader.int64().toString(); + break; + case /* int32 nanos */ 2: + message.nanos = reader.int32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Timestamp, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* int64 seconds = 1; */ + if (message.seconds !== "0") + writer.tag(1, WireType.Varint).int64(message.seconds); + /* int32 nanos = 2; */ + if (message.nanos !== 0) + writer.tag(2, WireType.Varint).int32(message.nanos); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.Timestamp + */ +export const Timestamp = new Timestamp$Type(); diff --git a/actions-toolkit/packages/artifact/src/generated/google/protobuf/wrappers.ts b/actions-toolkit/packages/artifact/src/generated/google/protobuf/wrappers.ts new file mode 100644 index 00000000..8f8797b0 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/generated/google/protobuf/wrappers.ts @@ -0,0 +1,748 @@ +// @generated by protobuf-ts 2.9.1 with parameter long_type_string,client_none,generate_dependencies +// @generated from protobuf file "google/protobuf/wrappers.proto" (package "google.protobuf", syntax proto3) +// tslint:disable +// +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. +// +import { ScalarType } from "@protobuf-ts/runtime"; +import { LongType } from "@protobuf-ts/runtime"; +import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; +import type { IBinaryWriter } from "@protobuf-ts/runtime"; +import { WireType } from "@protobuf-ts/runtime"; +import type { BinaryReadOptions } from "@protobuf-ts/runtime"; +import type { IBinaryReader } from "@protobuf-ts/runtime"; +import { UnknownFieldHandler } from "@protobuf-ts/runtime"; +import type { PartialMessage } from "@protobuf-ts/runtime"; +import { reflectionMergePartial } from "@protobuf-ts/runtime"; +import { MESSAGE_TYPE } from "@protobuf-ts/runtime"; +import type { JsonValue } from "@protobuf-ts/runtime"; +import type { JsonReadOptions } from "@protobuf-ts/runtime"; +import type { JsonWriteOptions } from "@protobuf-ts/runtime"; +import { MessageType } from "@protobuf-ts/runtime"; +/** + * Wrapper message for `double`. + * + * The JSON representation for `DoubleValue` is JSON number. + * + * @generated from protobuf message google.protobuf.DoubleValue + */ +export interface DoubleValue { + /** + * The double value. + * + * @generated from protobuf field: double value = 1; + */ + value: number; +} +/** + * Wrapper message for `float`. + * + * The JSON representation for `FloatValue` is JSON number. + * + * @generated from protobuf message google.protobuf.FloatValue + */ +export interface FloatValue { + /** + * The float value. + * + * @generated from protobuf field: float value = 1; + */ + value: number; +} +/** + * Wrapper message for `int64`. + * + * The JSON representation for `Int64Value` is JSON string. + * + * @generated from protobuf message google.protobuf.Int64Value + */ +export interface Int64Value { + /** + * The int64 value. + * + * @generated from protobuf field: int64 value = 1; + */ + value: string; +} +/** + * Wrapper message for `uint64`. + * + * The JSON representation for `UInt64Value` is JSON string. + * + * @generated from protobuf message google.protobuf.UInt64Value + */ +export interface UInt64Value { + /** + * The uint64 value. + * + * @generated from protobuf field: uint64 value = 1; + */ + value: string; +} +/** + * Wrapper message for `int32`. + * + * The JSON representation for `Int32Value` is JSON number. + * + * @generated from protobuf message google.protobuf.Int32Value + */ +export interface Int32Value { + /** + * The int32 value. + * + * @generated from protobuf field: int32 value = 1; + */ + value: number; +} +/** + * Wrapper message for `uint32`. + * + * The JSON representation for `UInt32Value` is JSON number. + * + * @generated from protobuf message google.protobuf.UInt32Value + */ +export interface UInt32Value { + /** + * The uint32 value. + * + * @generated from protobuf field: uint32 value = 1; + */ + value: number; +} +/** + * Wrapper message for `bool`. + * + * The JSON representation for `BoolValue` is JSON `true` and `false`. + * + * @generated from protobuf message google.protobuf.BoolValue + */ +export interface BoolValue { + /** + * The bool value. + * + * @generated from protobuf field: bool value = 1; + */ + value: boolean; +} +/** + * Wrapper message for `string`. + * + * The JSON representation for `StringValue` is JSON string. + * + * @generated from protobuf message google.protobuf.StringValue + */ +export interface StringValue { + /** + * The string value. + * + * @generated from protobuf field: string value = 1; + */ + value: string; +} +/** + * Wrapper message for `bytes`. + * + * The JSON representation for `BytesValue` is JSON string. + * + * @generated from protobuf message google.protobuf.BytesValue + */ +export interface BytesValue { + /** + * The bytes value. + * + * @generated from protobuf field: bytes value = 1; + */ + value: Uint8Array; +} +// @generated message type with reflection information, may provide speed optimized methods +class DoubleValue$Type extends MessageType { + constructor() { + super("google.protobuf.DoubleValue", [ + { no: 1, name: "value", kind: "scalar", T: 1 /*ScalarType.DOUBLE*/ } + ]); + } + /** + * Encode `DoubleValue` to JSON number. + */ + internalJsonWrite(message: DoubleValue, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(2, message.value, "value", false, true); + } + /** + * Decode `DoubleValue` from JSON number. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: DoubleValue): DoubleValue { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 1, undefined, "value") as number; + return target; + } + create(value?: PartialMessage): DoubleValue { + const message = { value: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: DoubleValue): DoubleValue { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* double value */ 1: + message.value = reader.double(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: DoubleValue, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* double value = 1; */ + if (message.value !== 0) + writer.tag(1, WireType.Bit64).double(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.DoubleValue + */ +export const DoubleValue = new DoubleValue$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class FloatValue$Type extends MessageType { + constructor() { + super("google.protobuf.FloatValue", [ + { no: 1, name: "value", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ } + ]); + } + /** + * Encode `FloatValue` to JSON number. + */ + internalJsonWrite(message: FloatValue, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(1, message.value, "value", false, true); + } + /** + * Decode `FloatValue` from JSON number. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: FloatValue): FloatValue { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 1, undefined, "value") as number; + return target; + } + create(value?: PartialMessage): FloatValue { + const message = { value: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: FloatValue): FloatValue { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* float value */ 1: + message.value = reader.float(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: FloatValue, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* float value = 1; */ + if (message.value !== 0) + writer.tag(1, WireType.Bit32).float(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.FloatValue + */ +export const FloatValue = new FloatValue$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Int64Value$Type extends MessageType { + constructor() { + super("google.protobuf.Int64Value", [ + { no: 1, name: "value", kind: "scalar", T: 3 /*ScalarType.INT64*/ } + ]); + } + /** + * Encode `Int64Value` to JSON string. + */ + internalJsonWrite(message: Int64Value, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(ScalarType.INT64, message.value, "value", false, true); + } + /** + * Decode `Int64Value` from JSON string. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: Int64Value): Int64Value { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, ScalarType.INT64, LongType.STRING, "value") as any; + return target; + } + create(value?: PartialMessage): Int64Value { + const message = { value: "0" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Int64Value): Int64Value { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* int64 value */ 1: + message.value = reader.int64().toString(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Int64Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* int64 value = 1; */ + if (message.value !== "0") + writer.tag(1, WireType.Varint).int64(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.Int64Value + */ +export const Int64Value = new Int64Value$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class UInt64Value$Type extends MessageType { + constructor() { + super("google.protobuf.UInt64Value", [ + { no: 1, name: "value", kind: "scalar", T: 4 /*ScalarType.UINT64*/ } + ]); + } + /** + * Encode `UInt64Value` to JSON string. + */ + internalJsonWrite(message: UInt64Value, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(ScalarType.UINT64, message.value, "value", false, true); + } + /** + * Decode `UInt64Value` from JSON string. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: UInt64Value): UInt64Value { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, ScalarType.UINT64, LongType.STRING, "value") as any; + return target; + } + create(value?: PartialMessage): UInt64Value { + const message = { value: "0" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UInt64Value): UInt64Value { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* uint64 value */ 1: + message.value = reader.uint64().toString(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: UInt64Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* uint64 value = 1; */ + if (message.value !== "0") + writer.tag(1, WireType.Varint).uint64(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.UInt64Value + */ +export const UInt64Value = new UInt64Value$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class Int32Value$Type extends MessageType { + constructor() { + super("google.protobuf.Int32Value", [ + { no: 1, name: "value", kind: "scalar", T: 5 /*ScalarType.INT32*/ } + ]); + } + /** + * Encode `Int32Value` to JSON string. + */ + internalJsonWrite(message: Int32Value, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(5, message.value, "value", false, true); + } + /** + * Decode `Int32Value` from JSON string. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: Int32Value): Int32Value { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 5, undefined, "value") as number; + return target; + } + create(value?: PartialMessage): Int32Value { + const message = { value: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Int32Value): Int32Value { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* int32 value */ 1: + message.value = reader.int32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: Int32Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* int32 value = 1; */ + if (message.value !== 0) + writer.tag(1, WireType.Varint).int32(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.Int32Value + */ +export const Int32Value = new Int32Value$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class UInt32Value$Type extends MessageType { + constructor() { + super("google.protobuf.UInt32Value", [ + { no: 1, name: "value", kind: "scalar", T: 13 /*ScalarType.UINT32*/ } + ]); + } + /** + * Encode `UInt32Value` to JSON string. + */ + internalJsonWrite(message: UInt32Value, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(13, message.value, "value", false, true); + } + /** + * Decode `UInt32Value` from JSON string. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: UInt32Value): UInt32Value { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 13, undefined, "value") as number; + return target; + } + create(value?: PartialMessage): UInt32Value { + const message = { value: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UInt32Value): UInt32Value { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* uint32 value */ 1: + message.value = reader.uint32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: UInt32Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* uint32 value = 1; */ + if (message.value !== 0) + writer.tag(1, WireType.Varint).uint32(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.UInt32Value + */ +export const UInt32Value = new UInt32Value$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class BoolValue$Type extends MessageType { + constructor() { + super("google.protobuf.BoolValue", [ + { no: 1, name: "value", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } + ]); + } + /** + * Encode `BoolValue` to JSON bool. + */ + internalJsonWrite(message: BoolValue, options: JsonWriteOptions): JsonValue { + return message.value; + } + /** + * Decode `BoolValue` from JSON bool. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: BoolValue): BoolValue { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 8, undefined, "value") as boolean; + return target; + } + create(value?: PartialMessage): BoolValue { + const message = { value: false }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: BoolValue): BoolValue { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bool value */ 1: + message.value = reader.bool(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: BoolValue, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* bool value = 1; */ + if (message.value !== false) + writer.tag(1, WireType.Varint).bool(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.BoolValue + */ +export const BoolValue = new BoolValue$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class StringValue$Type extends MessageType { + constructor() { + super("google.protobuf.StringValue", [ + { no: 1, name: "value", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + /** + * Encode `StringValue` to JSON string. + */ + internalJsonWrite(message: StringValue, options: JsonWriteOptions): JsonValue { + return message.value; + } + /** + * Decode `StringValue` from JSON string. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: StringValue): StringValue { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 9, undefined, "value") as string; + return target; + } + create(value?: PartialMessage): StringValue { + const message = { value: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: StringValue): StringValue { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string value */ 1: + message.value = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: StringValue, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string value = 1; */ + if (message.value !== "") + writer.tag(1, WireType.LengthDelimited).string(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.StringValue + */ +export const StringValue = new StringValue$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class BytesValue$Type extends MessageType { + constructor() { + super("google.protobuf.BytesValue", [ + { no: 1, name: "value", kind: "scalar", T: 12 /*ScalarType.BYTES*/ } + ]); + } + /** + * Encode `BytesValue` to JSON string. + */ + internalJsonWrite(message: BytesValue, options: JsonWriteOptions): JsonValue { + return this.refJsonWriter.scalar(12, message.value, "value", false, true); + } + /** + * Decode `BytesValue` from JSON string. + */ + internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: BytesValue): BytesValue { + if (!target) + target = this.create(); + target.value = this.refJsonReader.scalar(json, 12, undefined, "value") as Uint8Array; + return target; + } + create(value?: PartialMessage): BytesValue { + const message = { value: new Uint8Array(0) }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: BytesValue): BytesValue { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bytes value */ 1: + message.value = reader.bytes(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: BytesValue, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* bytes value = 1; */ + if (message.value.length) + writer.tag(1, WireType.LengthDelimited).bytes(message.value); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message google.protobuf.BytesValue + */ +export const BytesValue = new BytesValue$Type(); diff --git a/actions-toolkit/packages/artifact/src/generated/index.ts b/actions-toolkit/packages/artifact/src/generated/index.ts new file mode 100644 index 00000000..01bdb99b --- /dev/null +++ b/actions-toolkit/packages/artifact/src/generated/index.ts @@ -0,0 +1,4 @@ +export * from './google/protobuf/timestamp' +export * from './google/protobuf/wrappers' +export * from './results/api/v1/artifact' +export * from './results/api/v1/artifact.twirp' diff --git a/actions-toolkit/packages/artifact/src/generated/results/api/v1/artifact.ts b/actions-toolkit/packages/artifact/src/generated/results/api/v1/artifact.ts new file mode 100644 index 00000000..acce3037 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/generated/results/api/v1/artifact.ts @@ -0,0 +1,770 @@ +// @generated by protobuf-ts 2.9.1 with parameter long_type_string,client_none,generate_dependencies +// @generated from protobuf file "results/api/v1/artifact.proto" (package "github.actions.results.api.v1", syntax proto3) +// tslint:disable +import { ServiceType } from "@protobuf-ts/runtime-rpc"; +import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; +import type { IBinaryWriter } from "@protobuf-ts/runtime"; +import { WireType } from "@protobuf-ts/runtime"; +import type { BinaryReadOptions } from "@protobuf-ts/runtime"; +import type { IBinaryReader } from "@protobuf-ts/runtime"; +import { UnknownFieldHandler } from "@protobuf-ts/runtime"; +import type { PartialMessage } from "@protobuf-ts/runtime"; +import { reflectionMergePartial } from "@protobuf-ts/runtime"; +import { MESSAGE_TYPE } from "@protobuf-ts/runtime"; +import { MessageType } from "@protobuf-ts/runtime"; +import { Int64Value } from "../../../google/protobuf/wrappers"; +import { StringValue } from "../../../google/protobuf/wrappers"; +import { Timestamp } from "../../../google/protobuf/timestamp"; +/** + * @generated from protobuf message github.actions.results.api.v1.CreateArtifactRequest + */ +export interface CreateArtifactRequest { + /** + * @generated from protobuf field: string workflow_run_backend_id = 1; + */ + workflowRunBackendId: string; + /** + * @generated from protobuf field: string workflow_job_run_backend_id = 2; + */ + workflowJobRunBackendId: string; + /** + * @generated from protobuf field: string name = 3; + */ + name: string; + /** + * @generated from protobuf field: google.protobuf.Timestamp expires_at = 4; + */ + expiresAt?: Timestamp; + /** + * @generated from protobuf field: int32 version = 5; + */ + version: number; +} +/** + * @generated from protobuf message github.actions.results.api.v1.CreateArtifactResponse + */ +export interface CreateArtifactResponse { + /** + * @generated from protobuf field: bool ok = 1; + */ + ok: boolean; + /** + * @generated from protobuf field: string signed_upload_url = 2; + */ + signedUploadUrl: string; +} +/** + * @generated from protobuf message github.actions.results.api.v1.FinalizeArtifactRequest + */ +export interface FinalizeArtifactRequest { + /** + * @generated from protobuf field: string workflow_run_backend_id = 1; + */ + workflowRunBackendId: string; + /** + * @generated from protobuf field: string workflow_job_run_backend_id = 2; + */ + workflowJobRunBackendId: string; + /** + * @generated from protobuf field: string name = 3; + */ + name: string; + /** + * @generated from protobuf field: int64 size = 4; + */ + size: string; + /** + * @generated from protobuf field: google.protobuf.StringValue hash = 5; + */ + hash?: StringValue; // optional +} +/** + * @generated from protobuf message github.actions.results.api.v1.FinalizeArtifactResponse + */ +export interface FinalizeArtifactResponse { + /** + * @generated from protobuf field: bool ok = 1; + */ + ok: boolean; + /** + * @generated from protobuf field: int64 artifact_id = 2; + */ + artifactId: string; +} +/** + * @generated from protobuf message github.actions.results.api.v1.ListArtifactsRequest + */ +export interface ListArtifactsRequest { + /** + * The backend plan ID + * + * @generated from protobuf field: string workflow_run_backend_id = 1; + */ + workflowRunBackendId: string; + /** + * The backend job ID + * + * @generated from protobuf field: string workflow_job_run_backend_id = 2; + */ + workflowJobRunBackendId: string; + /** + * Name of the artifact to filter on + * + * @generated from protobuf field: google.protobuf.StringValue name_filter = 3; + */ + nameFilter?: StringValue; // optional + /** + * Monolith Database ID of the artifact to filter on + * + * @generated from protobuf field: google.protobuf.Int64Value id_filter = 4; + */ + idFilter?: Int64Value; // optional +} +/** + * @generated from protobuf message github.actions.results.api.v1.ListArtifactsResponse + */ +export interface ListArtifactsResponse { + /** + * @generated from protobuf field: repeated github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact artifacts = 1; + */ + artifacts: ListArtifactsResponse_MonolithArtifact[]; +} +/** + * @generated from protobuf message github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact + */ +export interface ListArtifactsResponse_MonolithArtifact { + /** + * The backend plan ID + * + * @generated from protobuf field: string workflow_run_backend_id = 1; + */ + workflowRunBackendId: string; + /** + * The backend job ID + * + * @generated from protobuf field: string workflow_job_run_backend_id = 2; + */ + workflowJobRunBackendId: string; + /** + * Monolith database ID of the artifact + * + * @generated from protobuf field: int64 database_id = 3; + */ + databaseId: string; + /** + * Name of the artifact + * + * @generated from protobuf field: string name = 4; + */ + name: string; + /** + * Size of the artifact in bytes + * + * @generated from protobuf field: int64 size = 5; + */ + size: string; + /** + * When the artifact was created in the monolith + * + * @generated from protobuf field: google.protobuf.Timestamp created_at = 6; + */ + createdAt?: Timestamp; +} +/** + * @generated from protobuf message github.actions.results.api.v1.GetSignedArtifactURLRequest + */ +export interface GetSignedArtifactURLRequest { + /** + * @generated from protobuf field: string workflow_run_backend_id = 1; + */ + workflowRunBackendId: string; + /** + * @generated from protobuf field: string workflow_job_run_backend_id = 2; + */ + workflowJobRunBackendId: string; + /** + * @generated from protobuf field: string name = 3; + */ + name: string; +} +/** + * @generated from protobuf message github.actions.results.api.v1.GetSignedArtifactURLResponse + */ +export interface GetSignedArtifactURLResponse { + /** + * @generated from protobuf field: string signed_url = 1; + */ + signedUrl: string; +} +// @generated message type with reflection information, may provide speed optimized methods +class CreateArtifactRequest$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.CreateArtifactRequest", [ + { no: 1, name: "workflow_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "expires_at", kind: "message", T: () => Timestamp }, + { no: 5, name: "version", kind: "scalar", T: 5 /*ScalarType.INT32*/ } + ]); + } + create(value?: PartialMessage): CreateArtifactRequest { + const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", name: "", version: 0 }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CreateArtifactRequest): CreateArtifactRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string workflow_run_backend_id */ 1: + message.workflowRunBackendId = reader.string(); + break; + case /* string workflow_job_run_backend_id */ 2: + message.workflowJobRunBackendId = reader.string(); + break; + case /* string name */ 3: + message.name = reader.string(); + break; + case /* google.protobuf.Timestamp expires_at */ 4: + message.expiresAt = Timestamp.internalBinaryRead(reader, reader.uint32(), options, message.expiresAt); + break; + case /* int32 version */ 5: + message.version = reader.int32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: CreateArtifactRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string workflow_run_backend_id = 1; */ + if (message.workflowRunBackendId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId); + /* string workflow_job_run_backend_id = 2; */ + if (message.workflowJobRunBackendId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId); + /* string name = 3; */ + if (message.name !== "") + writer.tag(3, WireType.LengthDelimited).string(message.name); + /* google.protobuf.Timestamp expires_at = 4; */ + if (message.expiresAt) + Timestamp.internalBinaryWrite(message.expiresAt, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + /* int32 version = 5; */ + if (message.version !== 0) + writer.tag(5, WireType.Varint).int32(message.version); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.CreateArtifactRequest + */ +export const CreateArtifactRequest = new CreateArtifactRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class CreateArtifactResponse$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.CreateArtifactResponse", [ + { no: 1, name: "ok", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 2, name: "signed_upload_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): CreateArtifactResponse { + const message = { ok: false, signedUploadUrl: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CreateArtifactResponse): CreateArtifactResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bool ok */ 1: + message.ok = reader.bool(); + break; + case /* string signed_upload_url */ 2: + message.signedUploadUrl = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: CreateArtifactResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* bool ok = 1; */ + if (message.ok !== false) + writer.tag(1, WireType.Varint).bool(message.ok); + /* string signed_upload_url = 2; */ + if (message.signedUploadUrl !== "") + writer.tag(2, WireType.LengthDelimited).string(message.signedUploadUrl); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.CreateArtifactResponse + */ +export const CreateArtifactResponse = new CreateArtifactResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class FinalizeArtifactRequest$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.FinalizeArtifactRequest", [ + { no: 1, name: "workflow_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ }, + { no: 5, name: "hash", kind: "message", T: () => StringValue } + ]); + } + create(value?: PartialMessage): FinalizeArtifactRequest { + const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", name: "", size: "0" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: FinalizeArtifactRequest): FinalizeArtifactRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string workflow_run_backend_id */ 1: + message.workflowRunBackendId = reader.string(); + break; + case /* string workflow_job_run_backend_id */ 2: + message.workflowJobRunBackendId = reader.string(); + break; + case /* string name */ 3: + message.name = reader.string(); + break; + case /* int64 size */ 4: + message.size = reader.int64().toString(); + break; + case /* google.protobuf.StringValue hash */ 5: + message.hash = StringValue.internalBinaryRead(reader, reader.uint32(), options, message.hash); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: FinalizeArtifactRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string workflow_run_backend_id = 1; */ + if (message.workflowRunBackendId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId); + /* string workflow_job_run_backend_id = 2; */ + if (message.workflowJobRunBackendId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId); + /* string name = 3; */ + if (message.name !== "") + writer.tag(3, WireType.LengthDelimited).string(message.name); + /* int64 size = 4; */ + if (message.size !== "0") + writer.tag(4, WireType.Varint).int64(message.size); + /* google.protobuf.StringValue hash = 5; */ + if (message.hash) + StringValue.internalBinaryWrite(message.hash, writer.tag(5, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.FinalizeArtifactRequest + */ +export const FinalizeArtifactRequest = new FinalizeArtifactRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class FinalizeArtifactResponse$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.FinalizeArtifactResponse", [ + { no: 1, name: "ok", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 2, name: "artifact_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ } + ]); + } + create(value?: PartialMessage): FinalizeArtifactResponse { + const message = { ok: false, artifactId: "0" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: FinalizeArtifactResponse): FinalizeArtifactResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bool ok */ 1: + message.ok = reader.bool(); + break; + case /* int64 artifact_id */ 2: + message.artifactId = reader.int64().toString(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: FinalizeArtifactResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* bool ok = 1; */ + if (message.ok !== false) + writer.tag(1, WireType.Varint).bool(message.ok); + /* int64 artifact_id = 2; */ + if (message.artifactId !== "0") + writer.tag(2, WireType.Varint).int64(message.artifactId); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.FinalizeArtifactResponse + */ +export const FinalizeArtifactResponse = new FinalizeArtifactResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ListArtifactsRequest$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.ListArtifactsRequest", [ + { no: 1, name: "workflow_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "name_filter", kind: "message", T: () => StringValue }, + { no: 4, name: "id_filter", kind: "message", T: () => Int64Value } + ]); + } + create(value?: PartialMessage): ListArtifactsRequest { + const message = { workflowRunBackendId: "", workflowJobRunBackendId: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListArtifactsRequest): ListArtifactsRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string workflow_run_backend_id */ 1: + message.workflowRunBackendId = reader.string(); + break; + case /* string workflow_job_run_backend_id */ 2: + message.workflowJobRunBackendId = reader.string(); + break; + case /* google.protobuf.StringValue name_filter */ 3: + message.nameFilter = StringValue.internalBinaryRead(reader, reader.uint32(), options, message.nameFilter); + break; + case /* google.protobuf.Int64Value id_filter */ 4: + message.idFilter = Int64Value.internalBinaryRead(reader, reader.uint32(), options, message.idFilter); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ListArtifactsRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string workflow_run_backend_id = 1; */ + if (message.workflowRunBackendId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId); + /* string workflow_job_run_backend_id = 2; */ + if (message.workflowJobRunBackendId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId); + /* google.protobuf.StringValue name_filter = 3; */ + if (message.nameFilter) + StringValue.internalBinaryWrite(message.nameFilter, writer.tag(3, WireType.LengthDelimited).fork(), options).join(); + /* google.protobuf.Int64Value id_filter = 4; */ + if (message.idFilter) + Int64Value.internalBinaryWrite(message.idFilter, writer.tag(4, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.ListArtifactsRequest + */ +export const ListArtifactsRequest = new ListArtifactsRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ListArtifactsResponse$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.ListArtifactsResponse", [ + { no: 1, name: "artifacts", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => ListArtifactsResponse_MonolithArtifact } + ]); + } + create(value?: PartialMessage): ListArtifactsResponse { + const message = { artifacts: [] }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListArtifactsResponse): ListArtifactsResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* repeated github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact artifacts */ 1: + message.artifacts.push(ListArtifactsResponse_MonolithArtifact.internalBinaryRead(reader, reader.uint32(), options)); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ListArtifactsResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* repeated github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact artifacts = 1; */ + for (let i = 0; i < message.artifacts.length; i++) + ListArtifactsResponse_MonolithArtifact.internalBinaryWrite(message.artifacts[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.ListArtifactsResponse + */ +export const ListArtifactsResponse = new ListArtifactsResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class ListArtifactsResponse_MonolithArtifact$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact", [ + { no: 1, name: "workflow_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "database_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ }, + { no: 4, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 5, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ }, + { no: 6, name: "created_at", kind: "message", T: () => Timestamp } + ]); + } + create(value?: PartialMessage): ListArtifactsResponse_MonolithArtifact { + const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", databaseId: "0", name: "", size: "0" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListArtifactsResponse_MonolithArtifact): ListArtifactsResponse_MonolithArtifact { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string workflow_run_backend_id */ 1: + message.workflowRunBackendId = reader.string(); + break; + case /* string workflow_job_run_backend_id */ 2: + message.workflowJobRunBackendId = reader.string(); + break; + case /* int64 database_id */ 3: + message.databaseId = reader.int64().toString(); + break; + case /* string name */ 4: + message.name = reader.string(); + break; + case /* int64 size */ 5: + message.size = reader.int64().toString(); + break; + case /* google.protobuf.Timestamp created_at */ 6: + message.createdAt = Timestamp.internalBinaryRead(reader, reader.uint32(), options, message.createdAt); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: ListArtifactsResponse_MonolithArtifact, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string workflow_run_backend_id = 1; */ + if (message.workflowRunBackendId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId); + /* string workflow_job_run_backend_id = 2; */ + if (message.workflowJobRunBackendId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId); + /* int64 database_id = 3; */ + if (message.databaseId !== "0") + writer.tag(3, WireType.Varint).int64(message.databaseId); + /* string name = 4; */ + if (message.name !== "") + writer.tag(4, WireType.LengthDelimited).string(message.name); + /* int64 size = 5; */ + if (message.size !== "0") + writer.tag(5, WireType.Varint).int64(message.size); + /* google.protobuf.Timestamp created_at = 6; */ + if (message.createdAt) + Timestamp.internalBinaryWrite(message.createdAt, writer.tag(6, WireType.LengthDelimited).fork(), options).join(); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact + */ +export const ListArtifactsResponse_MonolithArtifact = new ListArtifactsResponse_MonolithArtifact$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetSignedArtifactURLRequest$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.GetSignedArtifactURLRequest", [ + { no: 1, name: "workflow_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): GetSignedArtifactURLRequest { + const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", name: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetSignedArtifactURLRequest): GetSignedArtifactURLRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string workflow_run_backend_id */ 1: + message.workflowRunBackendId = reader.string(); + break; + case /* string workflow_job_run_backend_id */ 2: + message.workflowJobRunBackendId = reader.string(); + break; + case /* string name */ 3: + message.name = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: GetSignedArtifactURLRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string workflow_run_backend_id = 1; */ + if (message.workflowRunBackendId !== "") + writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId); + /* string workflow_job_run_backend_id = 2; */ + if (message.workflowJobRunBackendId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId); + /* string name = 3; */ + if (message.name !== "") + writer.tag(3, WireType.LengthDelimited).string(message.name); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.GetSignedArtifactURLRequest + */ +export const GetSignedArtifactURLRequest = new GetSignedArtifactURLRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetSignedArtifactURLResponse$Type extends MessageType { + constructor() { + super("github.actions.results.api.v1.GetSignedArtifactURLResponse", [ + { no: 1, name: "signed_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): GetSignedArtifactURLResponse { + const message = { signedUrl: "" }; + globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetSignedArtifactURLResponse): GetSignedArtifactURLResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string signed_url */ 1: + message.signedUrl = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: GetSignedArtifactURLResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string signed_url = 1; */ + if (message.signedUrl !== "") + writer.tag(1, WireType.LengthDelimited).string(message.signedUrl); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message github.actions.results.api.v1.GetSignedArtifactURLResponse + */ +export const GetSignedArtifactURLResponse = new GetSignedArtifactURLResponse$Type(); +/** + * @generated ServiceType for protobuf service github.actions.results.api.v1.ArtifactService + */ +export const ArtifactService = new ServiceType("github.actions.results.api.v1.ArtifactService", [ + { name: "CreateArtifact", options: {}, I: CreateArtifactRequest, O: CreateArtifactResponse }, + { name: "FinalizeArtifact", options: {}, I: FinalizeArtifactRequest, O: FinalizeArtifactResponse }, + { name: "ListArtifacts", options: {}, I: ListArtifactsRequest, O: ListArtifactsResponse }, + { name: "GetSignedArtifactURL", options: {}, I: GetSignedArtifactURLRequest, O: GetSignedArtifactURLResponse } +]); diff --git a/actions-toolkit/packages/artifact/src/generated/results/api/v1/artifact.twirp.ts b/actions-toolkit/packages/artifact/src/generated/results/api/v1/artifact.twirp.ts new file mode 100644 index 00000000..4871eb6b --- /dev/null +++ b/actions-toolkit/packages/artifact/src/generated/results/api/v1/artifact.twirp.ts @@ -0,0 +1,799 @@ +import { + TwirpContext, + TwirpServer, + RouterEvents, + TwirpError, + TwirpErrorCode, + Interceptor, + TwirpContentType, + chainInterceptors, +} from "twirp-ts"; +import { + CreateArtifactRequest, + CreateArtifactResponse, + FinalizeArtifactRequest, + FinalizeArtifactResponse, + ListArtifactsRequest, + ListArtifactsResponse, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse, +} from "./artifact"; + +//==================================// +// Client Code // +//==================================// + +interface Rpc { + request( + service: string, + method: string, + contentType: "application/json" | "application/protobuf", + data: object | Uint8Array + ): Promise; +} + +export interface ArtifactServiceClient { + CreateArtifact( + request: CreateArtifactRequest + ): Promise; + FinalizeArtifact( + request: FinalizeArtifactRequest + ): Promise; + ListArtifacts(request: ListArtifactsRequest): Promise; + GetSignedArtifactURL( + request: GetSignedArtifactURLRequest + ): Promise; +} + +export class ArtifactServiceClientJSON implements ArtifactServiceClient { + private readonly rpc: Rpc; + constructor(rpc: Rpc) { + this.rpc = rpc; + this.CreateArtifact.bind(this); + this.FinalizeArtifact.bind(this); + this.ListArtifacts.bind(this); + this.GetSignedArtifactURL.bind(this); + } + CreateArtifact( + request: CreateArtifactRequest + ): Promise { + const data = CreateArtifactRequest.toJson(request, { + useProtoFieldName: true, + emitDefaultValues: false, + }); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "CreateArtifact", + "application/json", + data as object + ); + return promise.then((data) => + CreateArtifactResponse.fromJson(data as any, { + ignoreUnknownFields: true, + }) + ); + } + + FinalizeArtifact( + request: FinalizeArtifactRequest + ): Promise { + const data = FinalizeArtifactRequest.toJson(request, { + useProtoFieldName: true, + emitDefaultValues: false, + }); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "FinalizeArtifact", + "application/json", + data as object + ); + return promise.then((data) => + FinalizeArtifactResponse.fromJson(data as any, { + ignoreUnknownFields: true, + }) + ); + } + + ListArtifacts(request: ListArtifactsRequest): Promise { + const data = ListArtifactsRequest.toJson(request, { + useProtoFieldName: true, + emitDefaultValues: false, + }); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "ListArtifacts", + "application/json", + data as object + ); + return promise.then((data) => + ListArtifactsResponse.fromJson(data as any, { ignoreUnknownFields: true }) + ); + } + + GetSignedArtifactURL( + request: GetSignedArtifactURLRequest + ): Promise { + const data = GetSignedArtifactURLRequest.toJson(request, { + useProtoFieldName: true, + emitDefaultValues: false, + }); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "GetSignedArtifactURL", + "application/json", + data as object + ); + return promise.then((data) => + GetSignedArtifactURLResponse.fromJson(data as any, { + ignoreUnknownFields: true, + }) + ); + } +} + +export class ArtifactServiceClientProtobuf implements ArtifactServiceClient { + private readonly rpc: Rpc; + constructor(rpc: Rpc) { + this.rpc = rpc; + this.CreateArtifact.bind(this); + this.FinalizeArtifact.bind(this); + this.ListArtifacts.bind(this); + this.GetSignedArtifactURL.bind(this); + } + CreateArtifact( + request: CreateArtifactRequest + ): Promise { + const data = CreateArtifactRequest.toBinary(request); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "CreateArtifact", + "application/protobuf", + data + ); + return promise.then((data) => + CreateArtifactResponse.fromBinary(data as Uint8Array) + ); + } + + FinalizeArtifact( + request: FinalizeArtifactRequest + ): Promise { + const data = FinalizeArtifactRequest.toBinary(request); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "FinalizeArtifact", + "application/protobuf", + data + ); + return promise.then((data) => + FinalizeArtifactResponse.fromBinary(data as Uint8Array) + ); + } + + ListArtifacts(request: ListArtifactsRequest): Promise { + const data = ListArtifactsRequest.toBinary(request); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "ListArtifacts", + "application/protobuf", + data + ); + return promise.then((data) => + ListArtifactsResponse.fromBinary(data as Uint8Array) + ); + } + + GetSignedArtifactURL( + request: GetSignedArtifactURLRequest + ): Promise { + const data = GetSignedArtifactURLRequest.toBinary(request); + const promise = this.rpc.request( + "github.actions.results.api.v1.ArtifactService", + "GetSignedArtifactURL", + "application/protobuf", + data + ); + return promise.then((data) => + GetSignedArtifactURLResponse.fromBinary(data as Uint8Array) + ); + } +} + +//==================================// +// Server Code // +//==================================// + +export interface ArtifactServiceTwirp { + CreateArtifact( + ctx: T, + request: CreateArtifactRequest + ): Promise; + FinalizeArtifact( + ctx: T, + request: FinalizeArtifactRequest + ): Promise; + ListArtifacts( + ctx: T, + request: ListArtifactsRequest + ): Promise; + GetSignedArtifactURL( + ctx: T, + request: GetSignedArtifactURLRequest + ): Promise; +} + +export enum ArtifactServiceMethod { + CreateArtifact = "CreateArtifact", + FinalizeArtifact = "FinalizeArtifact", + ListArtifacts = "ListArtifacts", + GetSignedArtifactURL = "GetSignedArtifactURL", +} + +export const ArtifactServiceMethodList = [ + ArtifactServiceMethod.CreateArtifact, + ArtifactServiceMethod.FinalizeArtifact, + ArtifactServiceMethod.ListArtifacts, + ArtifactServiceMethod.GetSignedArtifactURL, +]; + +export function createArtifactServiceServer< + T extends TwirpContext = TwirpContext +>(service: ArtifactServiceTwirp) { + return new TwirpServer({ + service, + packageName: "github.actions.results.api.v1", + serviceName: "ArtifactService", + methodList: ArtifactServiceMethodList, + matchRoute: matchArtifactServiceRoute, + }); +} + +function matchArtifactServiceRoute( + method: string, + events: RouterEvents +) { + switch (method) { + case "CreateArtifact": + return async ( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + CreateArtifactRequest, + CreateArtifactResponse + >[] + ) => { + ctx = { ...ctx, methodName: "CreateArtifact" }; + await events.onMatch(ctx); + return handleArtifactServiceCreateArtifactRequest( + ctx, + service, + data, + interceptors + ); + }; + case "FinalizeArtifact": + return async ( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + FinalizeArtifactRequest, + FinalizeArtifactResponse + >[] + ) => { + ctx = { ...ctx, methodName: "FinalizeArtifact" }; + await events.onMatch(ctx); + return handleArtifactServiceFinalizeArtifactRequest( + ctx, + service, + data, + interceptors + ); + }; + case "ListArtifacts": + return async ( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + ListArtifactsRequest, + ListArtifactsResponse + >[] + ) => { + ctx = { ...ctx, methodName: "ListArtifacts" }; + await events.onMatch(ctx); + return handleArtifactServiceListArtifactsRequest( + ctx, + service, + data, + interceptors + ); + }; + case "GetSignedArtifactURL": + return async ( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse + >[] + ) => { + ctx = { ...ctx, methodName: "GetSignedArtifactURL" }; + await events.onMatch(ctx); + return handleArtifactServiceGetSignedArtifactURLRequest( + ctx, + service, + data, + interceptors + ); + }; + default: + events.onNotFound(); + const msg = `no handler found`; + throw new TwirpError(TwirpErrorCode.BadRoute, msg); + } +} + +function handleArtifactServiceCreateArtifactRequest< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor[] +): Promise { + switch (ctx.contentType) { + case TwirpContentType.JSON: + return handleArtifactServiceCreateArtifactJSON( + ctx, + service, + data, + interceptors + ); + case TwirpContentType.Protobuf: + return handleArtifactServiceCreateArtifactProtobuf( + ctx, + service, + data, + interceptors + ); + default: + const msg = "unexpected Content-Type"; + throw new TwirpError(TwirpErrorCode.BadRoute, msg); + } +} + +function handleArtifactServiceFinalizeArtifactRequest< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + FinalizeArtifactRequest, + FinalizeArtifactResponse + >[] +): Promise { + switch (ctx.contentType) { + case TwirpContentType.JSON: + return handleArtifactServiceFinalizeArtifactJSON( + ctx, + service, + data, + interceptors + ); + case TwirpContentType.Protobuf: + return handleArtifactServiceFinalizeArtifactProtobuf( + ctx, + service, + data, + interceptors + ); + default: + const msg = "unexpected Content-Type"; + throw new TwirpError(TwirpErrorCode.BadRoute, msg); + } +} + +function handleArtifactServiceListArtifactsRequest< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor[] +): Promise { + switch (ctx.contentType) { + case TwirpContentType.JSON: + return handleArtifactServiceListArtifactsJSON( + ctx, + service, + data, + interceptors + ); + case TwirpContentType.Protobuf: + return handleArtifactServiceListArtifactsProtobuf( + ctx, + service, + data, + interceptors + ); + default: + const msg = "unexpected Content-Type"; + throw new TwirpError(TwirpErrorCode.BadRoute, msg); + } +} + +function handleArtifactServiceGetSignedArtifactURLRequest< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse + >[] +): Promise { + switch (ctx.contentType) { + case TwirpContentType.JSON: + return handleArtifactServiceGetSignedArtifactURLJSON( + ctx, + service, + data, + interceptors + ); + case TwirpContentType.Protobuf: + return handleArtifactServiceGetSignedArtifactURLProtobuf( + ctx, + service, + data, + interceptors + ); + default: + const msg = "unexpected Content-Type"; + throw new TwirpError(TwirpErrorCode.BadRoute, msg); + } +} +async function handleArtifactServiceCreateArtifactJSON< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor[] +) { + let request: CreateArtifactRequest; + let response: CreateArtifactResponse; + + try { + const body = JSON.parse(data.toString() || "{}"); + request = CreateArtifactRequest.fromJson(body, { + ignoreUnknownFields: true, + }); + } catch (e) { + if (e instanceof Error) { + const msg = "the json request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + CreateArtifactRequest, + CreateArtifactResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.CreateArtifact(ctx, inputReq); + }); + } else { + response = await service.CreateArtifact(ctx, request!); + } + + return JSON.stringify( + CreateArtifactResponse.toJson(response, { + useProtoFieldName: true, + emitDefaultValues: false, + }) as string + ); +} + +async function handleArtifactServiceFinalizeArtifactJSON< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + FinalizeArtifactRequest, + FinalizeArtifactResponse + >[] +) { + let request: FinalizeArtifactRequest; + let response: FinalizeArtifactResponse; + + try { + const body = JSON.parse(data.toString() || "{}"); + request = FinalizeArtifactRequest.fromJson(body, { + ignoreUnknownFields: true, + }); + } catch (e) { + if (e instanceof Error) { + const msg = "the json request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + FinalizeArtifactRequest, + FinalizeArtifactResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.FinalizeArtifact(ctx, inputReq); + }); + } else { + response = await service.FinalizeArtifact(ctx, request!); + } + + return JSON.stringify( + FinalizeArtifactResponse.toJson(response, { + useProtoFieldName: true, + emitDefaultValues: false, + }) as string + ); +} + +async function handleArtifactServiceListArtifactsJSON< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor[] +) { + let request: ListArtifactsRequest; + let response: ListArtifactsResponse; + + try { + const body = JSON.parse(data.toString() || "{}"); + request = ListArtifactsRequest.fromJson(body, { + ignoreUnknownFields: true, + }); + } catch (e) { + if (e instanceof Error) { + const msg = "the json request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + ListArtifactsRequest, + ListArtifactsResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.ListArtifacts(ctx, inputReq); + }); + } else { + response = await service.ListArtifacts(ctx, request!); + } + + return JSON.stringify( + ListArtifactsResponse.toJson(response, { + useProtoFieldName: true, + emitDefaultValues: false, + }) as string + ); +} + +async function handleArtifactServiceGetSignedArtifactURLJSON< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse + >[] +) { + let request: GetSignedArtifactURLRequest; + let response: GetSignedArtifactURLResponse; + + try { + const body = JSON.parse(data.toString() || "{}"); + request = GetSignedArtifactURLRequest.fromJson(body, { + ignoreUnknownFields: true, + }); + } catch (e) { + if (e instanceof Error) { + const msg = "the json request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.GetSignedArtifactURL(ctx, inputReq); + }); + } else { + response = await service.GetSignedArtifactURL(ctx, request!); + } + + return JSON.stringify( + GetSignedArtifactURLResponse.toJson(response, { + useProtoFieldName: true, + emitDefaultValues: false, + }) as string + ); +} +async function handleArtifactServiceCreateArtifactProtobuf< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor[] +) { + let request: CreateArtifactRequest; + let response: CreateArtifactResponse; + + try { + request = CreateArtifactRequest.fromBinary(data); + } catch (e) { + if (e instanceof Error) { + const msg = "the protobuf request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + CreateArtifactRequest, + CreateArtifactResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.CreateArtifact(ctx, inputReq); + }); + } else { + response = await service.CreateArtifact(ctx, request!); + } + + return Buffer.from(CreateArtifactResponse.toBinary(response)); +} + +async function handleArtifactServiceFinalizeArtifactProtobuf< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + FinalizeArtifactRequest, + FinalizeArtifactResponse + >[] +) { + let request: FinalizeArtifactRequest; + let response: FinalizeArtifactResponse; + + try { + request = FinalizeArtifactRequest.fromBinary(data); + } catch (e) { + if (e instanceof Error) { + const msg = "the protobuf request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + FinalizeArtifactRequest, + FinalizeArtifactResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.FinalizeArtifact(ctx, inputReq); + }); + } else { + response = await service.FinalizeArtifact(ctx, request!); + } + + return Buffer.from(FinalizeArtifactResponse.toBinary(response)); +} + +async function handleArtifactServiceListArtifactsProtobuf< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor[] +) { + let request: ListArtifactsRequest; + let response: ListArtifactsResponse; + + try { + request = ListArtifactsRequest.fromBinary(data); + } catch (e) { + if (e instanceof Error) { + const msg = "the protobuf request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + ListArtifactsRequest, + ListArtifactsResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.ListArtifacts(ctx, inputReq); + }); + } else { + response = await service.ListArtifacts(ctx, request!); + } + + return Buffer.from(ListArtifactsResponse.toBinary(response)); +} + +async function handleArtifactServiceGetSignedArtifactURLProtobuf< + T extends TwirpContext = TwirpContext +>( + ctx: T, + service: ArtifactServiceTwirp, + data: Buffer, + interceptors?: Interceptor< + T, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse + >[] +) { + let request: GetSignedArtifactURLRequest; + let response: GetSignedArtifactURLResponse; + + try { + request = GetSignedArtifactURLRequest.fromBinary(data); + } catch (e) { + if (e instanceof Error) { + const msg = "the protobuf request could not be decoded"; + throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true); + } + } + + if (interceptors && interceptors.length > 0) { + const interceptor = chainInterceptors(...interceptors) as Interceptor< + T, + GetSignedArtifactURLRequest, + GetSignedArtifactURLResponse + >; + response = await interceptor(ctx, request!, (ctx, inputReq) => { + return service.GetSignedArtifactURL(ctx, inputReq); + }); + } else { + response = await service.GetSignedArtifactURL(ctx, request!); + } + + return Buffer.from(GetSignedArtifactURLResponse.toBinary(response)); +} diff --git a/actions-toolkit/packages/artifact/src/internal/client.ts b/actions-toolkit/packages/artifact/src/internal/client.ts new file mode 100644 index 00000000..c4971d9f --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/client.ts @@ -0,0 +1,228 @@ +import {warning} from '@actions/core' +import {isGhes} from './shared/config' +import { + UploadArtifactOptions, + UploadArtifactResponse, + DownloadArtifactOptions, + GetArtifactResponse, + ListArtifactsOptions, + ListArtifactsResponse, + DownloadArtifactResponse, + FindOptions +} from './shared/interfaces' +import {uploadArtifact} from './upload/upload-artifact' +import { + downloadArtifactPublic, + downloadArtifactInternal +} from './download/download-artifact' +import {getArtifactPublic, getArtifactInternal} from './find/get-artifact' +import {listArtifactsPublic, listArtifactsInternal} from './find/list-artifacts' +import {GHESNotSupportedError} from './shared/errors' + +/** + * Generic interface for the artifact client. + */ +export interface ArtifactClient { + /** + * Uploads an artifact. + * + * @param name The name of the artifact, required + * @param files A list of absolute or relative paths that denote what files should be uploaded + * @param rootDirectory An absolute or relative file path that denotes the root parent directory of the files being uploaded + * @param options Extra options for customizing the upload behavior + * @returns single UploadArtifactResponse object + */ + uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadArtifactOptions + ): Promise + + /** + * Lists all artifacts that are part of the current workflow run. + * This function will return at most 1000 artifacts per workflow run. + * + * If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs. + * https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts + * + * @param options Extra options that allow for the customization of the list behavior + * @returns ListArtifactResponse object + */ + listArtifacts( + options?: ListArtifactsOptions & FindOptions + ): Promise + + /** + * Finds an artifact by name. + * If there are multiple artifacts with the same name in the same workflow run, this will return the latest. + * If the artifact is not found, it will throw. + * + * If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs. + * https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts + * `@actions/artifact` v2+ does not allow for creating multiple artifacts with the same name in the same workflow run. + * It is possible to have multiple artifacts with the same name in the same workflow run by using old versions of upload-artifact (v1,v2 and v3), @actions/artifact < v2 or it is a rerun. + * If there are multiple artifacts with the same name in the same workflow run this function will return the first artifact that matches the name. + * + * @param artifactName The name of the artifact to find + * @param options Extra options that allow for the customization of the get behavior + */ + getArtifact( + artifactName: string, + options?: FindOptions + ): Promise + + /** + * Downloads an artifact and unzips the content. + * + * If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact + * + * @param artifactId The name of the artifact to download + * @param options Extra options that allow for the customization of the download behavior + * @returns single DownloadArtifactResponse object + */ + downloadArtifact( + artifactId: number, + options?: DownloadArtifactOptions & FindOptions + ): Promise +} + +/** + * The default artifact client that is used by the artifact action(s). + */ +export class DefaultArtifactClient implements ArtifactClient { + async uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadArtifactOptions + ): Promise { + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + return uploadArtifact(name, files, rootDirectory, options) + } catch (error) { + warning( + `Artifact upload failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions is operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } + + async downloadArtifact( + artifactId: number, + options?: DownloadArtifactOptions & FindOptions + ): Promise { + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: {repositoryOwner, repositoryName, token}, + ...downloadOptions + } = options + + return downloadArtifactPublic( + artifactId, + repositoryOwner, + repositoryName, + token, + downloadOptions + ) + } + + return downloadArtifactInternal(artifactId, options) + } catch (error) { + warning( + `Download Artifact failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } + + async listArtifacts( + options?: ListArtifactsOptions & FindOptions + ): Promise { + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: {workflowRunId, repositoryOwner, repositoryName, token} + } = options + + return listArtifactsPublic( + workflowRunId, + repositoryOwner, + repositoryName, + token, + options?.latest + ) + } + + return listArtifactsInternal(options?.latest) + } catch (error: unknown) { + warning( + `Listing Artifacts failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } + + async getArtifact( + artifactName: string, + options?: FindOptions + ): Promise { + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: {workflowRunId, repositoryOwner, repositoryName, token} + } = options + + return getArtifactPublic( + artifactName, + workflowRunId, + repositoryOwner, + repositoryName, + token + ) + } + + return getArtifactInternal(artifactName) + } catch (error: unknown) { + warning( + `Get Artifact failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + throw error + } + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/download/download-artifact.ts b/actions-toolkit/packages/artifact/src/internal/download/download-artifact.ts new file mode 100644 index 00000000..3bb3de61 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/download/download-artifact.ts @@ -0,0 +1,167 @@ +import fs from 'fs/promises' +import * as github from '@actions/github' +import * as core from '@actions/core' +import * as httpClient from '@actions/http-client' +import unzipper from 'unzipper' +import { + DownloadArtifactOptions, + DownloadArtifactResponse +} from '../shared/interfaces' +import {getUserAgentString} from '../shared/user-agent' +import {getGitHubWorkspaceDir} from '../shared/config' +import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client' +import { + GetSignedArtifactURLRequest, + Int64Value, + ListArtifactsRequest +} from '../../generated' +import {getBackendIdsFromToken} from '../shared/util' +import {ArtifactNotFoundError} from '../shared/errors' + +const scrubQueryParameters = (url: string): string => { + const parsed = new URL(url) + parsed.search = '' + return parsed.toString() +} + +async function exists(path: string): Promise { + try { + await fs.access(path) + return true + } catch (error) { + if (error.code === 'ENOENT') { + return false + } else { + throw error + } + } +} + +async function streamExtract(url: string, directory: string): Promise { + const client = new httpClient.HttpClient(getUserAgentString()) + const response = await client.get(url) + + if (response.message.statusCode !== 200) { + throw new Error( + `Unexpected HTTP response from blob storage: ${response.message.statusCode} ${response.message.statusMessage}` + ) + } + + return response.message.pipe(unzipper.Extract({path: directory})).promise() +} + +export async function downloadArtifactPublic( + artifactId: number, + repositoryOwner: string, + repositoryName: string, + token: string, + options?: DownloadArtifactOptions +): Promise { + const downloadPath = await resolveOrCreateDirectory(options?.path) + + const api = github.getOctokit(token) + + core.info( + `Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'` + ) + + const {headers, status} = await api.rest.actions.downloadArtifact({ + owner: repositoryOwner, + repo: repositoryName, + artifact_id: artifactId, + archive_format: 'zip', + request: { + redirect: 'manual' + } + }) + + if (status !== 302) { + throw new Error(`Unable to download artifact. Unexpected status: ${status}`) + } + + const {location} = headers + if (!location) { + throw new Error(`Unable to redirect to artifact download url`) + } + + core.info( + `Redirecting to blob download url: ${scrubQueryParameters(location)}` + ) + + try { + core.info(`Starting download of artifact to: ${downloadPath}`) + await streamExtract(location, downloadPath) + core.info(`Artifact download completed successfully.`) + } catch (error) { + throw new Error(`Unable to download and extract artifact: ${error.message}`) + } + + return {downloadPath} +} + +export async function downloadArtifactInternal( + artifactId: number, + options?: DownloadArtifactOptions +): Promise { + const downloadPath = await resolveOrCreateDirectory(options?.path) + + const artifactClient = internalArtifactTwirpClient() + + const {workflowRunBackendId, workflowJobRunBackendId} = + getBackendIdsFromToken() + + const listReq: ListArtifactsRequest = { + workflowRunBackendId, + workflowJobRunBackendId, + idFilter: Int64Value.create({value: artifactId.toString()}) + } + + const {artifacts} = await artifactClient.ListArtifacts(listReq) + + if (artifacts.length === 0) { + throw new ArtifactNotFoundError( + `No artifacts found for ID: ${artifactId}\nAre you trying to download from a different run? Try specifying a github-token with \`actions:read\` scope.` + ) + } + + if (artifacts.length > 1) { + core.warning('Multiple artifacts found, defaulting to first.') + } + + const signedReq: GetSignedArtifactURLRequest = { + workflowRunBackendId: artifacts[0].workflowRunBackendId, + workflowJobRunBackendId: artifacts[0].workflowJobRunBackendId, + name: artifacts[0].name + } + + const {signedUrl} = await artifactClient.GetSignedArtifactURL(signedReq) + + core.info( + `Redirecting to blob download url: ${scrubQueryParameters(signedUrl)}` + ) + + try { + core.info(`Starting download of artifact to: ${downloadPath}`) + await streamExtract(signedUrl, downloadPath) + core.info(`Artifact download completed successfully.`) + } catch (error) { + throw new Error(`Unable to download and extract artifact: ${error.message}`) + } + + return {downloadPath} +} + +async function resolveOrCreateDirectory( + downloadPath = getGitHubWorkspaceDir() +): Promise { + if (!(await exists(downloadPath))) { + core.debug( + `Artifact destination folder does not exist, creating: ${downloadPath}` + ) + await fs.mkdir(downloadPath, {recursive: true}) + } else { + core.debug(`Artifact destination folder already exists: ${downloadPath}`) + } + + return downloadPath +} diff --git a/actions-toolkit/packages/artifact/src/internal/find/get-artifact.ts b/actions-toolkit/packages/artifact/src/internal/find/get-artifact.ts new file mode 100644 index 00000000..b19e5d6e --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/find/get-artifact.ts @@ -0,0 +1,117 @@ +import {getOctokit} from '@actions/github' +import {retry} from '@octokit/plugin-retry' +import * as core from '@actions/core' +import {OctokitOptions} from '@octokit/core/dist-types/types' +import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils' +import {getRetryOptions} from './retry-options' +import {requestLog} from '@octokit/plugin-request-log' +import {GetArtifactResponse} from '../shared/interfaces' +import {getBackendIdsFromToken} from '../shared/util' +import {getUserAgentString} from '../shared/user-agent' +import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client' +import {ListArtifactsRequest, StringValue, Timestamp} from '../../generated' +import {ArtifactNotFoundError, InvalidResponseError} from '../shared/errors' + +export async function getArtifactPublic( + artifactName: string, + workflowRunId: number, + repositoryOwner: string, + repositoryName: string, + token: string +): Promise { + const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions) + + const opts: OctokitOptions = { + log: undefined, + userAgent: getUserAgentString(), + previews: undefined, + retry: retryOpts, + request: requestOpts + } + + const github = getOctokit(token, opts, retry, requestLog) + + const getArtifactResp = await github.request( + 'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts{?name}', + { + owner: repositoryOwner, + repo: repositoryName, + run_id: workflowRunId, + name: artifactName + } + ) + + if (getArtifactResp.status !== 200) { + throw new InvalidResponseError( + `Invalid response from GitHub API: ${getArtifactResp.status} (${getArtifactResp?.headers?.['x-github-request-id']})` + ) + } + + if (getArtifactResp.data.artifacts.length === 0) { + throw new ArtifactNotFoundError( + `Artifact not found for name: ${artifactName}` + ) + } + + let artifact = getArtifactResp.data.artifacts[0] + if (getArtifactResp.data.artifacts.length > 1) { + artifact = getArtifactResp.data.artifacts.sort((a, b) => b.id - a.id)[0] + core.debug( + `More than one artifact found for a single name, returning newest (id: ${artifact.id})` + ) + } + + return { + artifact: { + name: artifact.name, + id: artifact.id, + size: artifact.size_in_bytes, + createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined + } + } +} + +export async function getArtifactInternal( + artifactName: string +): Promise { + const artifactClient = internalArtifactTwirpClient() + + const {workflowRunBackendId, workflowJobRunBackendId} = + getBackendIdsFromToken() + + const req: ListArtifactsRequest = { + workflowRunBackendId, + workflowJobRunBackendId, + nameFilter: StringValue.create({value: artifactName}) + } + + const res = await artifactClient.ListArtifacts(req) + + if (res.artifacts.length === 0) { + throw new ArtifactNotFoundError( + `Artifact not found for name: ${artifactName}` + ) + } + + let artifact = res.artifacts[0] + if (res.artifacts.length > 1) { + artifact = res.artifacts.sort( + (a, b) => Number(b.databaseId) - Number(a.databaseId) + )[0] + + core.debug( + `More than one artifact found for a single name, returning newest (id: ${artifact.databaseId})` + ) + } + + return { + artifact: { + name: artifact.name, + id: Number(artifact.databaseId), + size: Number(artifact.size), + createdAt: artifact.createdAt + ? Timestamp.toDate(artifact.createdAt) + : undefined + } + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/find/list-artifacts.ts b/actions-toolkit/packages/artifact/src/internal/find/list-artifacts.ts new file mode 100644 index 00000000..cd83320c --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/find/list-artifacts.ts @@ -0,0 +1,165 @@ +import {info, warning, debug} from '@actions/core' +import {getOctokit} from '@actions/github' +import {ListArtifactsResponse, Artifact} from '../shared/interfaces' +import {getUserAgentString} from '../shared/user-agent' +import {getRetryOptions} from './retry-options' +import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils' +import {requestLog} from '@octokit/plugin-request-log' +import {retry} from '@octokit/plugin-retry' +import {OctokitOptions} from '@octokit/core/dist-types/types' +import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client' +import {getBackendIdsFromToken} from '../shared/util' +import {ListArtifactsRequest, Timestamp} from '../../generated' + +// Limiting to 1000 for perf reasons +const maximumArtifactCount = 1000 +const paginationCount = 100 +const maxNumberOfPages = maximumArtifactCount / paginationCount + +export async function listArtifactsPublic( + workflowRunId: number, + repositoryOwner: string, + repositoryName: string, + token: string, + latest = false +): Promise { + info( + `Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}` + ) + + let artifacts: Artifact[] = [] + const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions) + + const opts: OctokitOptions = { + log: undefined, + userAgent: getUserAgentString(), + previews: undefined, + retry: retryOpts, + request: requestOpts + } + + const github = getOctokit(token, opts, retry, requestLog) + + let currentPageNumber = 1 + const {data: listArtifactResponse} = + await github.rest.actions.listWorkflowRunArtifacts({ + owner: repositoryOwner, + repo: repositoryName, + run_id: workflowRunId, + per_page: paginationCount, + page: currentPageNumber + }) + + let numberOfPages = Math.ceil( + listArtifactResponse.total_count / paginationCount + ) + const totalArtifactCount = listArtifactResponse.total_count + if (totalArtifactCount > maximumArtifactCount) { + warning( + `Workflow run ${workflowRunId} has more than 1000 artifacts. Results will be incomplete as only the first ${maximumArtifactCount} artifacts will be returned` + ) + numberOfPages = maxNumberOfPages + } + + // Iterate over the first page + for (const artifact of listArtifactResponse.artifacts) { + artifacts.push({ + name: artifact.name, + id: artifact.id, + size: artifact.size_in_bytes, + createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined + }) + } + + // Iterate over any remaining pages + for ( + currentPageNumber; + currentPageNumber < numberOfPages; + currentPageNumber++ + ) { + currentPageNumber++ + debug(`Fetching page ${currentPageNumber} of artifact list`) + + const {data: listArtifactResponse} = + await github.rest.actions.listWorkflowRunArtifacts({ + owner: repositoryOwner, + repo: repositoryName, + run_id: workflowRunId, + per_page: paginationCount, + page: currentPageNumber + }) + + for (const artifact of listArtifactResponse.artifacts) { + artifacts.push({ + name: artifact.name, + id: artifact.id, + size: artifact.size_in_bytes, + createdAt: artifact.created_at + ? new Date(artifact.created_at) + : undefined + }) + } + } + + if (latest) { + artifacts = filterLatest(artifacts) + } + + info(`Found ${artifacts.length} artifact(s)`) + + return { + artifacts + } +} + +export async function listArtifactsInternal( + latest = false +): Promise { + const artifactClient = internalArtifactTwirpClient() + + const {workflowRunBackendId, workflowJobRunBackendId} = + getBackendIdsFromToken() + + const req: ListArtifactsRequest = { + workflowRunBackendId, + workflowJobRunBackendId + } + + const res = await artifactClient.ListArtifacts(req) + let artifacts: Artifact[] = res.artifacts.map(artifact => ({ + name: artifact.name, + id: Number(artifact.databaseId), + size: Number(artifact.size), + createdAt: artifact.createdAt + ? Timestamp.toDate(artifact.createdAt) + : undefined + })) + + if (latest) { + artifacts = filterLatest(artifacts) + } + + info(`Found ${artifacts.length} artifact(s)`) + + return { + artifacts + } +} + +/** + * Filters a list of artifacts to only include the latest artifact for each name + * @param artifacts The artifacts to filter + * @returns The filtered list of artifacts + */ +function filterLatest(artifacts: Artifact[]): Artifact[] { + artifacts.sort((a, b) => b.id - a.id) + const latestArtifacts: Artifact[] = [] + const seenArtifactNames = new Set() + for (const artifact of artifacts) { + if (!seenArtifactNames.has(artifact.name)) { + latestArtifacts.push(artifact) + seenArtifactNames.add(artifact.name) + } + } + return latestArtifacts +} diff --git a/actions-toolkit/packages/artifact/src/internal/find/retry-options.ts b/actions-toolkit/packages/artifact/src/internal/find/retry-options.ts new file mode 100644 index 00000000..ab10309b --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/find/retry-options.ts @@ -0,0 +1,48 @@ +import * as core from '@actions/core' +import {OctokitOptions} from '@octokit/core/dist-types/types' +import {RequestRequestOptions} from '@octokit/types' + +export type RetryOptions = { + doNotRetry?: number[] + enabled?: boolean +} + +// Defaults for fetching artifacts +const defaultMaxRetryNumber = 5 +const defaultExemptStatusCodes = [400, 401, 403, 404, 422] // https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14 + +export function getRetryOptions( + defaultOptions: OctokitOptions, + retries: number = defaultMaxRetryNumber, + exemptStatusCodes: number[] = defaultExemptStatusCodes +): [RetryOptions, RequestRequestOptions | undefined] { + if (retries <= 0) { + return [{enabled: false}, defaultOptions.request] + } + + const retryOptions: RetryOptions = { + enabled: true + } + + if (exemptStatusCodes.length > 0) { + retryOptions.doNotRetry = exemptStatusCodes + } + + // The GitHub type has some defaults for `options.request` + // see: https://github.com/actions/toolkit/blob/4fbc5c941a57249b19562015edbd72add14be93d/packages/github/src/utils.ts#L15 + // We pass these in here so they are not overridden. + const requestOptions: RequestRequestOptions = { + ...defaultOptions.request, + retries + } + + core.debug( + `GitHub client configured with: (retries: ${ + requestOptions.retries + }, retry-exempt-status-code: ${ + retryOptions.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]' + })` + ) + + return [retryOptions, requestOptions] +} diff --git a/actions-toolkit/packages/artifact/src/internal/shared/artifact-twirp-client.ts b/actions-toolkit/packages/artifact/src/internal/shared/artifact-twirp-client.ts new file mode 100644 index 00000000..4feba626 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/shared/artifact-twirp-client.ts @@ -0,0 +1,178 @@ +import {HttpClient, HttpClientResponse, HttpCodes} from '@actions/http-client' +import {BearerCredentialHandler} from '@actions/http-client/lib/auth' +import {info, debug} from '@actions/core' +import {ArtifactServiceClientJSON} from '../../generated' +import {getResultsServiceUrl, getRuntimeToken} from './config' +import {getUserAgentString} from './user-agent' + +// The twirp http client must implement this interface +interface Rpc { + request( + service: string, + method: string, + contentType: 'application/json' | 'application/protobuf', + data: object | Uint8Array + ): Promise +} + +class ArtifactHttpClient implements Rpc { + private httpClient: HttpClient + private baseUrl: string + private maxAttempts = 5 + private baseRetryIntervalMilliseconds = 3000 + private retryMultiplier = 1.5 + + constructor( + userAgent: string, + maxAttempts?: number, + baseRetryIntervalMilliseconds?: number, + retryMultiplier?: number + ) { + const token = getRuntimeToken() + this.baseUrl = getResultsServiceUrl() + if (maxAttempts) { + this.maxAttempts = maxAttempts + } + if (baseRetryIntervalMilliseconds) { + this.baseRetryIntervalMilliseconds = baseRetryIntervalMilliseconds + } + if (retryMultiplier) { + this.retryMultiplier = retryMultiplier + } + + this.httpClient = new HttpClient(userAgent, [ + new BearerCredentialHandler(token) + ]) + } + + // This function satisfies the Rpc interface. It is compatible with the JSON + // JSON generated client. + async request( + service: string, + method: string, + contentType: 'application/json' | 'application/protobuf', + data: object | Uint8Array + ): Promise { + const url = new URL(`/twirp/${service}/${method}`, this.baseUrl).href + debug(`[Request] ${method} ${url}`) + const headers = { + 'Content-Type': contentType + } + try { + const {body} = await this.retryableRequest(async () => + this.httpClient.post(url, JSON.stringify(data), headers) + ) + + return JSON.parse(body) + } catch (error) { + throw new Error(`Failed to ${method}: ${error.message}`) + } + } + + async retryableRequest( + operation: () => Promise + ): Promise<{response: HttpClientResponse; body: string}> { + let attempt = 0 + let errorMessage = '' + while (attempt < this.maxAttempts) { + let isRetryable = false + + try { + const response = await operation() + const statusCode = response.message.statusCode + const body = await response.readBody() + debug(`[Response] - ${response.message.statusCode}`) + debug(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`) + debug(`Body: ${body}`) + if (this.isSuccessStatusCode(statusCode)) { + return {response, body} + } + isRetryable = this.isRetryableHttpStatusCode(statusCode) + errorMessage = `Failed request: (${statusCode}) ${response.message.statusMessage}` + const responseMessage = JSON.parse(body).msg + if (responseMessage) { + errorMessage = `${errorMessage}: ${responseMessage}` + } + } catch (error) { + isRetryable = true + errorMessage = error.message + } + + if (!isRetryable) { + throw new Error(`Received non-retryable error: ${errorMessage}`) + } + + if (attempt + 1 === this.maxAttempts) { + throw new Error( + `Failed to make request after ${this.maxAttempts} attempts: ${errorMessage}` + ) + } + + const retryTimeMilliseconds = + this.getExponentialRetryTimeMilliseconds(attempt) + info( + `Attempt ${attempt + 1} of ${ + this.maxAttempts + } failed with error: ${errorMessage}. Retrying request in ${retryTimeMilliseconds} ms...` + ) + await this.sleep(retryTimeMilliseconds) + attempt++ + } + + throw new Error(`Request failed`) + } + + isSuccessStatusCode(statusCode?: number): boolean { + if (!statusCode) return false + return statusCode >= 200 && statusCode < 300 + } + + isRetryableHttpStatusCode(statusCode?: number): boolean { + if (!statusCode) return false + + const retryableStatusCodes = [ + HttpCodes.BadGateway, + HttpCodes.GatewayTimeout, + HttpCodes.InternalServerError, + HttpCodes.ServiceUnavailable, + HttpCodes.TooManyRequests + ] + + return retryableStatusCodes.includes(statusCode) + } + + async sleep(milliseconds: number): Promise { + return new Promise(resolve => setTimeout(resolve, milliseconds)) + } + + getExponentialRetryTimeMilliseconds(attempt: number): number { + if (attempt < 0) { + throw new Error('attempt should be a positive integer') + } + + if (attempt === 0) { + return this.baseRetryIntervalMilliseconds + } + + const minTime = + this.baseRetryIntervalMilliseconds * this.retryMultiplier ** attempt + const maxTime = minTime * this.retryMultiplier + + // returns a random number between minTime and maxTime (exclusive) + return Math.trunc(Math.random() * (maxTime - minTime) + minTime) + } +} + +export function internalArtifactTwirpClient(options?: { + maxAttempts?: number + retryIntervalMs?: number + retryMultiplier?: number +}): ArtifactServiceClientJSON { + const client = new ArtifactHttpClient( + getUserAgentString(), + options?.maxAttempts, + options?.retryIntervalMs, + options?.retryMultiplier + ) + return new ArtifactServiceClientJSON(client) +} diff --git a/actions-toolkit/packages/artifact/src/internal/shared/config.ts b/actions-toolkit/packages/artifact/src/internal/shared/config.ts new file mode 100644 index 00000000..a5631bfc --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/shared/config.ts @@ -0,0 +1,53 @@ +import os from 'os' + +// Used for controlling the highWaterMark value of the zip that is being streamed +// The same value is used as the chunk size that is use during upload to blob storage +export function getUploadChunkSize(): number { + return 8 * 1024 * 1024 // 8 MB Chunks +} + +export function getRuntimeToken(): string { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] + if (!token) { + throw new Error('Unable to get the ACTIONS_RUNTIME_TOKEN env variable') + } + return token +} + +export function getResultsServiceUrl(): string { + const resultsUrl = process.env['ACTIONS_RESULTS_URL'] + if (!resultsUrl) { + throw new Error('Unable to get the ACTIONS_RESULTS_URL env variable') + } + + return new URL(resultsUrl).origin +} + +export function isGhes(): boolean { + const ghUrl = new URL( + process.env['GITHUB_SERVER_URL'] || 'https://github.com' + ) + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' +} + +export function getGitHubWorkspaceDir(): string { + const ghWorkspaceDir = process.env['GITHUB_WORKSPACE'] + if (!ghWorkspaceDir) { + throw new Error('Unable to get the GITHUB_WORKSPACE env variable') + } + return ghWorkspaceDir +} + +// Mimics behavior of azcopy: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-optimize +// If your machine has fewer than 5 CPUs, then the value of this variable is set to 32. +// Otherwise, the default value is equal to 16 multiplied by the number of CPUs. The maximum value of this variable is 300. +export function getConcurrency(): number { + const numCPUs = os.cpus().length + + if (numCPUs <= 4) { + return 32 + } + + const concurrency = 16 * numCPUs + return concurrency > 300 ? 300 : concurrency +} diff --git a/actions-toolkit/packages/artifact/src/internal/shared/errors.ts b/actions-toolkit/packages/artifact/src/internal/shared/errors.ts new file mode 100644 index 00000000..841c44a6 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/shared/errors.ts @@ -0,0 +1,37 @@ +export class FilesNotFoundError extends Error { + files: string[] + + constructor(files: string[] = []) { + let message = 'No files were found to upload' + if (files.length > 0) { + message += `: ${files.join(', ')}` + } + + super(message) + this.files = files + this.name = 'FilesNotFoundError' + } +} + +export class InvalidResponseError extends Error { + constructor(message: string) { + super(message) + this.name = 'InvalidResponseError' + } +} + +export class ArtifactNotFoundError extends Error { + constructor(message = 'Artifact not found') { + super(message) + this.name = 'ArtifactNotFoundError' + } +} + +export class GHESNotSupportedError extends Error { + constructor( + message = '@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.' + ) { + super(message) + this.name = 'GHESNotSupportedError' + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/shared/interfaces.ts b/actions-toolkit/packages/artifact/src/internal/shared/interfaces.ts new file mode 100644 index 00000000..c661721e --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/shared/interfaces.ts @@ -0,0 +1,149 @@ +/** + * Response from the server when an artifact is uploaded + */ +export interface UploadArtifactResponse { + /** + * Total size of the artifact in bytes. Not provided if no artifact was uploaded + */ + size?: number + + /** + * The id of the artifact that was created. Not provided if no artifact was uploaded + * This ID can be used as input to other APIs to download, delete or get more information about an artifact: https://docs.github.com/en/rest/actions/artifacts + */ + id?: number +} + +/** + * Options for uploading an artifact + */ +export interface UploadArtifactOptions { + /** + * Duration after which artifact will expire in days. + * + * By default artifact expires after 90 days: + * https://docs.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts#downloading-and-deleting-artifacts-after-a-workflow-run-is-complete + * + * Use this option to override the default expiry. + * + * Min value: 1 + * Max value: 90 unless changed by repository setting + * + * If this is set to a greater value than the retention settings allowed, the retention on artifacts + * will be reduced to match the max value allowed on server, and the upload process will continue. An + * input of 0 assumes default retention setting. + */ + retentionDays?: number + /** + * The level of compression for Zlib to be applied to the artifact archive. + * The value can range from 0 to 9: + * - 0: No compression + * - 1: Best speed + * - 6: Default compression (same as GNU Gzip) + * - 9: Best compression + * Higher levels will result in better compression, but will take longer to complete. + * For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads. + */ + compressionLevel?: number +} + +/** + * Response from the server when getting an artifact + */ +export interface GetArtifactResponse { + /** + * Metadata about the artifact that was found + */ + artifact: Artifact +} + +/** + * Options for listing artifacts + */ +export interface ListArtifactsOptions { + /** + * Filter the workflow run's artifacts to the latest by name + * In the case of reruns, this can be useful to avoid duplicates + */ + latest?: boolean +} + +/** + * Response from the server when listing artifacts + */ +export interface ListArtifactsResponse { + /** + * A list of artifacts that were found + */ + artifacts: Artifact[] +} + +/** + * Response from the server when downloading an artifact + */ +export interface DownloadArtifactResponse { + /** + * The path where the artifact was downloaded to + */ + downloadPath?: string +} + +/** + * Options for downloading an artifact + */ +export interface DownloadArtifactOptions { + /** + * Denotes where the artifact will be downloaded to. If not specified then the artifact is download to GITHUB_WORKSPACE + */ + path?: string +} + +/** + * An Actions Artifact + */ +export interface Artifact { + /** + * The name of the artifact + */ + name: string + + /** + * The ID of the artifact + */ + id: number + + /** + * The size of the artifact in bytes + */ + size: number + + /** + * The time when the artifact was created + */ + createdAt?: Date +} + +// FindOptions are for fetching Artifact(s) out of the scope of the current run. +export interface FindOptions { + /** + * The criteria for finding Artifact(s) out of the scope of the current run. + */ + findBy?: { + /** + * Token with actions:read permissions + */ + token: string + /** + * WorkflowRun of the artifact(s) to lookup + */ + workflowRunId: number + /** + * Repository owner (eg. 'actions') + */ + repositoryOwner: string + /** + * Repository owner (eg. 'toolkit') + */ + repositoryName: string + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/shared/user-agent.ts b/actions-toolkit/packages/artifact/src/internal/shared/user-agent.ts new file mode 100644 index 00000000..94c07e89 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/shared/user-agent.ts @@ -0,0 +1,9 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports +const packageJson = require('../../../package.json') + +/** + * Ensure that this User Agent String is used in all HTTP calls so that we can monitor telemetry between different versions of this package + */ +export function getUserAgentString(): string { + return `@actions/artifact-${packageJson.version}` +} diff --git a/actions-toolkit/packages/artifact/src/internal/shared/util.ts b/actions-toolkit/packages/artifact/src/internal/shared/util.ts new file mode 100644 index 00000000..07392b36 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/shared/util.ts @@ -0,0 +1,71 @@ +import * as core from '@actions/core' +import {getRuntimeToken} from './config' +import jwt_decode from 'jwt-decode' + +export interface BackendIds { + workflowRunBackendId: string + workflowJobRunBackendId: string +} + +interface ActionsToken { + scp: string +} + +const InvalidJwtError = new Error( + 'Failed to get backend IDs: The provided JWT token is invalid and/or missing claims' +) + +// uses the JWT token claims to get the +// workflow run and workflow job run backend ids +export function getBackendIdsFromToken(): BackendIds { + const token = getRuntimeToken() + const decoded = jwt_decode(token) + if (!decoded.scp) { + throw InvalidJwtError + } + + /* + * example decoded: + * { + * scp: "Actions.ExampleScope Actions.Results:ce7f54c7-61c7-4aae-887f-30da475f5f1a:ca395085-040a-526b-2ce8-bdc85f692774" + * } + */ + + const scpParts = decoded.scp.split(' ') + if (scpParts.length === 0) { + throw InvalidJwtError + } + /* + * example scpParts: + * ["Actions.ExampleScope", "Actions.Results:ce7f54c7-61c7-4aae-887f-30da475f5f1a:ca395085-040a-526b-2ce8-bdc85f692774"] + */ + + for (const scopes of scpParts) { + const scopeParts = scopes.split(':') + if (scopeParts?.[0] !== 'Actions.Results') { + // not the Actions.Results scope + continue + } + + /* + * example scopeParts: + * ["Actions.Results", "ce7f54c7-61c7-4aae-887f-30da475f5f1a", "ca395085-040a-526b-2ce8-bdc85f692774"] + */ + if (scopeParts.length !== 3) { + // missing expected number of claims + throw InvalidJwtError + } + + const ids = { + workflowRunBackendId: scopeParts[1], + workflowJobRunBackendId: scopeParts[2] + } + + core.debug(`Workflow Run Backend ID: ${ids.workflowRunBackendId}`) + core.debug(`Workflow Job Run Backend ID: ${ids.workflowJobRunBackendId}`) + + return ids + } + + throw InvalidJwtError +} diff --git a/actions-toolkit/packages/artifact/src/internal/upload/blob-upload.ts b/actions-toolkit/packages/artifact/src/internal/upload/blob-upload.ts new file mode 100644 index 00000000..59472385 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/upload/blob-upload.ts @@ -0,0 +1,78 @@ +import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob' +import {TransferProgressEvent} from '@azure/core-http' +import {ZipUploadStream} from './zip' +import {getUploadChunkSize, getConcurrency} from '../shared/config' +import * as core from '@actions/core' +import * as crypto from 'crypto' +import * as stream from 'stream' + +export interface BlobUploadResponse { + /** + * The total reported upload size in bytes. Empty if the upload failed + */ + uploadSize?: number + + /** + * The SHA256 hash of the uploaded file. Empty if the upload failed + */ + sha256Hash?: string +} + +export async function uploadZipToBlobStorage( + authenticatedUploadURL: string, + zipUploadStream: ZipUploadStream +): Promise { + let uploadByteCount = 0 + + const maxConcurrency = getConcurrency() + const bufferSize = getUploadChunkSize() + const blobClient = new BlobClient(authenticatedUploadURL) + const blockBlobClient = blobClient.getBlockBlobClient() + + core.debug( + `Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}` + ) + + const uploadCallback = (progress: TransferProgressEvent): void => { + core.info(`Uploaded bytes ${progress.loadedBytes}`) + uploadByteCount = progress.loadedBytes + } + + const options: BlockBlobUploadStreamOptions = { + blobHTTPHeaders: {blobContentType: 'zip'}, + onProgress: uploadCallback + } + + let sha256Hash: string | undefined = undefined + const uploadStream = new stream.PassThrough() + const hashStream = crypto.createHash('sha256') + + zipUploadStream.pipe(uploadStream) // This stream is used for the upload + zipUploadStream.pipe(hashStream).setEncoding('hex') // This stream is used to compute a hash of the zip content that gets used. Integrity check + + core.info('Beginning upload of artifact content to blob storage') + + await blockBlobClient.uploadStream( + uploadStream, + bufferSize, + maxConcurrency, + options + ) + + core.info('Finished uploading artifact content to blob storage!') + + hashStream.end() + sha256Hash = hashStream.read() as string + core.info(`SHA256 hash of uploaded artifact zip is ${sha256Hash}`) + + if (uploadByteCount === 0) { + core.warning( + `No data was uploaded to blob storage. Reported upload byte count is 0.` + ) + } + + return { + uploadSize: uploadByteCount, + sha256Hash + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/upload/path-and-artifact-name-validation.ts b/actions-toolkit/packages/artifact/src/internal/upload/path-and-artifact-name-validation.ts new file mode 100644 index 00000000..77386024 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/upload/path-and-artifact-name-validation.ts @@ -0,0 +1,82 @@ +import {info} from '@actions/core' + +/** + * Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected + * from the server if attempted to be sent over. These characters are not allowed due to limitations with certain + * file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an + * individual filesystem/platform will not be supported on all fileSystems/platforms + * + * FilePaths can include characters such as \ and / which are not permitted in the artifact name alone + */ +const invalidArtifactFilePathCharacters = new Map([ + ['"', ' Double quote "'], + [':', ' Colon :'], + ['<', ' Less than <'], + ['>', ' Greater than >'], + ['|', ' Vertical bar |'], + ['*', ' Asterisk *'], + ['?', ' Question mark ?'], + ['\r', ' Carriage return \\r'], + ['\n', ' Line feed \\n'] +]) + +const invalidArtifactNameCharacters = new Map([ + ...invalidArtifactFilePathCharacters, + ['\\', ' Backslash \\'], + ['/', ' Forward slash /'] +]) + +/** + * Validates the name of the artifact to check to make sure there are no illegal characters + */ +export function validateArtifactName(name: string): void { + if (!name) { + throw new Error(`Provided artifact name input during validation is empty`) + } + + for (const [ + invalidCharacterKey, + errorMessageForCharacter + ] of invalidArtifactNameCharacters) { + if (name.includes(invalidCharacterKey)) { + throw new Error( + `The artifact name is not valid: ${name}. Contains the following character: ${errorMessageForCharacter} + +Invalid characters include: ${Array.from( + invalidArtifactNameCharacters.values() + ).toString()} + +These characters are not allowed in the artifact name due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems.` + ) + } + } + + info(`Artifact name is valid!`) +} + +/** + * Validates file paths to check for any illegal characters that can cause problems on different file systems + */ +export function validateFilePath(path: string): void { + if (!path) { + throw new Error(`Provided file path input during validation is empty`) + } + + for (const [ + invalidCharacterKey, + errorMessageForCharacter + ] of invalidArtifactFilePathCharacters) { + if (path.includes(invalidCharacterKey)) { + throw new Error( + `The path for one of the files in artifact is not valid: ${path}. Contains the following character: ${errorMessageForCharacter} + +Invalid characters include: ${Array.from( + invalidArtifactFilePathCharacters.values() + ).toString()} + +The following characters are not allowed in files that are uploaded due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems. + ` + ) + } + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/upload/retention.ts b/actions-toolkit/packages/artifact/src/internal/upload/retention.ts new file mode 100644 index 00000000..aa16ba1b --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/upload/retention.ts @@ -0,0 +1,34 @@ +import {Timestamp} from '../../generated' +import * as core from '@actions/core' + +export function getExpiration(retentionDays?: number): Timestamp | undefined { + if (!retentionDays) { + return undefined + } + + const maxRetentionDays = getRetentionDays() + if (maxRetentionDays && maxRetentionDays < retentionDays) { + core.warning( + `Retention days cannot be greater than the maximum allowed retention set within the repository. Using ${maxRetentionDays} instead.` + ) + retentionDays = maxRetentionDays + } + + const expirationDate = new Date() + expirationDate.setDate(expirationDate.getDate() + retentionDays) + + return Timestamp.fromDate(expirationDate) +} + +function getRetentionDays(): number | undefined { + const retentionDays = process.env['GITHUB_RETENTION_DAYS'] + if (!retentionDays) { + return undefined + } + const days = parseInt(retentionDays) + if (isNaN(days)) { + return undefined + } + + return days +} diff --git a/actions-toolkit/packages/artifact/src/internal/upload/upload-artifact.ts b/actions-toolkit/packages/artifact/src/internal/upload/upload-artifact.ts new file mode 100644 index 00000000..e880102f --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/upload/upload-artifact.ts @@ -0,0 +1,115 @@ +import * as core from '@actions/core' +import { + UploadArtifactOptions, + UploadArtifactResponse +} from '../shared/interfaces' +import {getExpiration} from './retention' +import {validateArtifactName} from './path-and-artifact-name-validation' +import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client' +import { + UploadZipSpecification, + getUploadZipSpecification, + validateRootDirectory +} from './upload-zip-specification' +import {getBackendIdsFromToken} from '../shared/util' +import {uploadZipToBlobStorage} from './blob-upload' +import {createZipUploadStream} from './zip' +import { + CreateArtifactRequest, + FinalizeArtifactRequest, + StringValue +} from '../../generated' +import {FilesNotFoundError, InvalidResponseError} from '../shared/errors' + +export async function uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadArtifactOptions | undefined +): Promise { + validateArtifactName(name) + validateRootDirectory(rootDirectory) + + const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification( + files, + rootDirectory + ) + if (zipSpecification.length === 0) { + throw new FilesNotFoundError( + zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : [])) + ) + } + + // get the IDs needed for the artifact creation + const backendIds = getBackendIdsFromToken() + + // create the artifact client + const artifactClient = internalArtifactTwirpClient() + + // create the artifact + const createArtifactReq: CreateArtifactRequest = { + workflowRunBackendId: backendIds.workflowRunBackendId, + workflowJobRunBackendId: backendIds.workflowJobRunBackendId, + name, + version: 4 + } + + // if there is a retention period, add it to the request + const expiresAt = getExpiration(options?.retentionDays) + if (expiresAt) { + createArtifactReq.expiresAt = expiresAt + } + + const createArtifactResp = + await artifactClient.CreateArtifact(createArtifactReq) + if (!createArtifactResp.ok) { + throw new InvalidResponseError( + 'CreateArtifact: response from backend was not ok' + ) + } + + const zipUploadStream = await createZipUploadStream( + zipSpecification, + options?.compressionLevel + ) + + // Upload zip to blob storage + const uploadResult = await uploadZipToBlobStorage( + createArtifactResp.signedUploadUrl, + zipUploadStream + ) + + // finalize the artifact + const finalizeArtifactReq: FinalizeArtifactRequest = { + workflowRunBackendId: backendIds.workflowRunBackendId, + workflowJobRunBackendId: backendIds.workflowJobRunBackendId, + name, + size: uploadResult.uploadSize ? uploadResult.uploadSize.toString() : '0' + } + + if (uploadResult.sha256Hash) { + finalizeArtifactReq.hash = StringValue.create({ + value: `sha256:${uploadResult.sha256Hash}` + }) + } + + core.info(`Finalizing artifact upload`) + + const finalizeArtifactResp = + await artifactClient.FinalizeArtifact(finalizeArtifactReq) + if (!finalizeArtifactResp.ok) { + throw new InvalidResponseError( + 'FinalizeArtifact: response from backend was not ok' + ) + } + + const artifactId = BigInt(finalizeArtifactResp.artifactId) + core.info( + `Artifact ${name}.zip successfully finalized. Artifact ID ${artifactId}` + ) + + return { + size: uploadResult.uploadSize, + id: Number(artifactId) + } +} diff --git a/actions-toolkit/packages/artifact/src/internal/upload/upload-zip-specification.ts b/actions-toolkit/packages/artifact/src/internal/upload/upload-zip-specification.ts new file mode 100644 index 00000000..c6e807e6 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/upload/upload-zip-specification.ts @@ -0,0 +1,111 @@ +import * as fs from 'fs' +import {info} from '@actions/core' +import {normalize, resolve} from 'path' +import {validateFilePath} from './path-and-artifact-name-validation' + +export interface UploadZipSpecification { + /** + * An absolute source path that points to a file that will be added to a zip. Null if creating a new directory + */ + sourcePath: string | null + + /** + * The destination path in a zip for a file + */ + destinationPath: string +} + +/** + * Checks if a root directory exists and is valid + * @param rootDirectory an absolute root directory path common to all input files that that will be trimmed from the final zip structure + */ +export function validateRootDirectory(rootDirectory: string): void { + if (!fs.existsSync(rootDirectory)) { + throw new Error( + `The provided rootDirectory ${rootDirectory} does not exist` + ) + } + if (!fs.statSync(rootDirectory).isDirectory()) { + throw new Error( + `The provided rootDirectory ${rootDirectory} is not a valid directory` + ) + } + info(`Root directory input is valid!`) +} + +/** + * Creates a specification that describes how a zip file will be created for a set of input files + * @param filesToZip a list of file that should be included in the zip + * @param rootDirectory an absolute root directory path common to all input files that that will be trimmed from the final zip structure + */ +export function getUploadZipSpecification( + filesToZip: string[], + rootDirectory: string +): UploadZipSpecification[] { + const specification: UploadZipSpecification[] = [] + + // Normalize and resolve, this allows for either absolute or relative paths to be used + rootDirectory = normalize(rootDirectory) + rootDirectory = resolve(rootDirectory) + + /* + Example + + Input: + rootDirectory: '/home/user/files/plz-upload' + artifactFiles: [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ] + + Output: + specifications: [ + ['/home/user/files/plz-upload/file1.txt', '/file1.txt'], + ['/home/user/files/plz-upload/file1.txt', '/file2.txt'], + ['/home/user/files/plz-upload/file1.txt', '/dir/file3.txt'] + ] + + The final zip that is later uploaded will look like this: + + my-artifact.zip + - file.txt + - file2.txt + - dir/ + - file3.txt + */ + for (let file of filesToZip) { + if (!fs.existsSync(file)) { + throw new Error(`File ${file} does not exist`) + } + if (!fs.statSync(file).isDirectory()) { + // Normalize and resolve, this allows for either absolute or relative paths to be used + file = normalize(file) + file = resolve(file) + if (!file.startsWith(rootDirectory)) { + throw new Error( + `The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}` + ) + } + + // Check for forbidden characters in file paths that may cause ambiguous behavior if downloaded on different file systems + const uploadPath = file.replace(rootDirectory, '') + validateFilePath(uploadPath) + + specification.push({ + sourcePath: file, + destinationPath: uploadPath + }) + } else { + // Empty directory + const directoryPath = file.replace(rootDirectory, '') + validateFilePath(directoryPath) + + specification.push({ + sourcePath: null, + destinationPath: directoryPath + }) + } + } + return specification +} diff --git a/actions-toolkit/packages/artifact/src/internal/upload/zip.ts b/actions-toolkit/packages/artifact/src/internal/upload/zip.ts new file mode 100644 index 00000000..5a923119 --- /dev/null +++ b/actions-toolkit/packages/artifact/src/internal/upload/zip.ts @@ -0,0 +1,101 @@ +import * as stream from 'stream' +import * as archiver from 'archiver' +import * as core from '@actions/core' +import {createReadStream} from 'fs' +import {UploadZipSpecification} from './upload-zip-specification' +import {getUploadChunkSize} from '../shared/config' + +export const DEFAULT_COMPRESSION_LEVEL = 6 + +// Custom stream transformer so we can set the highWaterMark property +// See https://github.com/nodejs/node/issues/8855 +export class ZipUploadStream extends stream.Transform { + constructor(bufferSize: number) { + super({ + highWaterMark: bufferSize + }) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + _transform(chunk: any, enc: any, cb: any): void { + cb(null, chunk) + } +} + +export async function createZipUploadStream( + uploadSpecification: UploadZipSpecification[], + compressionLevel: number = DEFAULT_COMPRESSION_LEVEL +): Promise { + core.debug( + `Creating Artifact archive with compressionLevel: ${compressionLevel}` + ) + + const zip = archiver.create('zip', { + highWaterMark: getUploadChunkSize(), + zlib: {level: compressionLevel} + }) + + // register callbacks for various events during the zip lifecycle + zip.on('error', zipErrorCallback) + zip.on('warning', zipWarningCallback) + zip.on('finish', zipFinishCallback) + zip.on('end', zipEndCallback) + + for (const file of uploadSpecification) { + if (file.sourcePath !== null) { + // Add a normal file to the zip + zip.append(createReadStream(file.sourcePath), { + name: file.destinationPath + }) + } else { + // Add a directory to the zip + zip.append('', {name: file.destinationPath}) + } + } + + const bufferSize = getUploadChunkSize() + const zipUploadStream = new ZipUploadStream(bufferSize) + + core.debug( + `Zip write high watermark value ${zipUploadStream.writableHighWaterMark}` + ) + core.debug( + `Zip read high watermark value ${zipUploadStream.readableHighWaterMark}` + ) + + zip.pipe(zipUploadStream) + zip.finalize() + + return zipUploadStream +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const zipErrorCallback = (error: any): void => { + core.error('An error has occurred while creating the zip file for upload') + core.info(error) + + throw new Error('An error has occurred during zip creation for the artifact') +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const zipWarningCallback = (error: any): void => { + if (error.code === 'ENOENT') { + core.warning( + 'ENOENT warning during artifact zip creation. No such file or directory' + ) + core.info(error) + } else { + core.warning( + `A non-blocking warning has occurred during artifact zip creation: ${error.code}` + ) + core.info(error) + } +} + +const zipFinishCallback = (): void => { + core.debug('Zip stream for upload has finished.') +} + +const zipEndCallback = (): void => { + core.debug('Zip stream for upload has ended.') +} diff --git a/actions-toolkit/packages/artifact/tsconfig.json b/actions-toolkit/packages/artifact/tsconfig.json new file mode 100644 index 00000000..cee05147 --- /dev/null +++ b/actions-toolkit/packages/artifact/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src", + "paths": { + "@actions/core": [ + "../core" + ], + "@actions/http-client": [ + "../http-client" + ] + }, + "useUnknownInCatchVariables": false, + }, + "include": [ + "./src" + ] +} diff --git a/actions-toolkit/packages/cache/LICENSE.md b/actions-toolkit/packages/cache/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/cache/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/cache/README.md b/actions-toolkit/packages/cache/README.md new file mode 100644 index 00000000..55185032 --- /dev/null +++ b/actions-toolkit/packages/cache/README.md @@ -0,0 +1,51 @@ +# `@actions/cache` + +> Functions necessary for caching dependencies and build outputs to improve workflow execution time. + +See ["Caching dependencies to speed up workflows"](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows) for how caching works. + +Note that GitHub will remove any cache entries that have not been accessed in over 7 days. There is no limit on the number of caches you can store, but the total size of all caches in a repository is limited to 10 GB. If you exceed this limit, GitHub will save your cache but will begin evicting caches until the total size is less than 10 GB. + +## Usage + +This package is used by the v2+ versions of our first party cache action. You can find an example implementation in the cache repo [here](https://github.com/actions/cache). + +#### Save Cache + +Saves a cache containing the files in `paths` using the `key` provided. The files would be compressed using zstandard compression algorithm if zstd is installed, otherwise gzip is used. Function returns the cache id if the cache was saved succesfully and throws an error if cache upload fails. + +```js +const cache = require('@actions/cache'); +const paths = [ + 'node_modules', + 'packages/*/node_modules/' +] +const key = 'npm-foobar-d5ea0750' +const cacheId = await cache.saveCache(paths, key) +``` + +#### Restore Cache + +Restores a cache based on `key` and `restoreKeys` to the `paths` provided. Function returns the cache key for cache hit and returns undefined if cache not found. + +```js +const cache = require('@actions/cache'); +const paths = [ + 'node_modules', + 'packages/*/node_modules/' +] +const key = 'npm-foobar-d5ea0750' +const restoreKeys = [ + 'npm-foobar-', + 'npm-' +] +const cacheKey = await cache.restoreCache(paths, key, restoreKeys) +``` + +##### Cache segment restore timeout + +A cache gets downloaded in multiple segments of fixed sizes (now `128MB` to fail-fast, previously `1GB` for a `32-bit` runner and `2GB` for a `64-bit` runner were used). Sometimes, a segment download gets stuck which causes the workflow job to be stuck forever and fail. Version `v3.0.4` of cache package introduces a segment download timeout. The segment download timeout will allow the segment download to get aborted and hence allow the job to proceed with a cache miss. + +Default value of this timeout is 10 minutes (starting `v3.2.1` and higher, previously 60 minutes in versions between `v.3.0.4` and `v3.2.0`, both included) and can be customized by specifying an [environment variable](https://docs.github.com/en/actions/learn-github-actions/environment-variables) named `SEGMENT_DOWNLOAD_TIMEOUT_MINS` with timeout value in minutes. + + diff --git a/actions-toolkit/packages/cache/RELEASES.md b/actions-toolkit/packages/cache/RELEASES.md new file mode 100644 index 00000000..eb86a8b0 --- /dev/null +++ b/actions-toolkit/packages/cache/RELEASES.md @@ -0,0 +1,166 @@ +# @actions/cache Releases + +### 0.1.0 + +- Initial release + +### 0.2.0 + +- Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469) + +### 0.2.1 + +- Fix to await async function getCompressionMethod + +### 1.0.0 + +- Downloads Azure-hosted caches using the Azure SDK for speed and reliability +- Displays download progress +- Includes changes that break compatibility with earlier versions, including: + - `retry`, `retryTypedResponse`, and `retryHttpClientResponse` moved from `cacheHttpClient` to `requestUtils` + +### 1.0.1 + +- Fix bug in downloading large files (> 2 GBs) with the Azure SDK + +### 1.0.2 + +- Use posix archive format to add support for some tools + +### 1.0.3 + +- Use http-client v1.0.9 +- Fixes error handling so retries are not attempted on non-retryable errors (409 Conflict, for example) +- Adds 5 second delay between retry attempts + +### 1.0.4 + +- Use @actions/core v1.2.6 +- Fixes uploadChunk to throw an error if any unsuccessful response code is received + +### 1.0.5 + +- Fix to ensure Windows cache paths get resolved correctly + +### 1.0.6 + +- Make caching more verbose [#650](https://github.com/actions/toolkit/pull/650) +- Use GNU tar on macOS if available [#701](https://github.com/actions/toolkit/pull/701) + +### 1.0.7 + +- Fixes permissions issue extracting archives with GNU tar on macOS ([issue](https://github.com/actions/cache/issues/527)) + +### 1.0.8 + +- Increase the allowed artifact cache size from 5GB to 10GB ([issue](https://github.com/actions/cache/discussions/497)) + +### 1.0.9 + +- Use @azure/ms-rest-js v2.6.0 +- Use @azure/storage-blob v12.8.0 + +### 1.0.10 + +- Update `lockfileVersion` to `v2` in `package-lock.json [#1022](https://github.com/actions/toolkit/pull/1022) + +### 1.0.11 + +- Fix file downloads > 2GB([issue](https://github.com/actions/cache/issues/773)) + +### 2.0.0 + +- Added support to check if Actions cache service feature is available or not [#1028](https://github.com/actions/toolkit/pull/1028) + +### 2.0.3 + +- Update to v2.0.0 of `@actions/http-client` + +### 2.0.4 + +- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087) + +### 2.0.5 + +- Fix to avoid saving empty cache when no files are available for caching. ([issue](https://github.com/actions/cache/issues/624)) + +### 2.0.6 + +- Fix `Tar failed with error: The process '/usr/bin/tar' failed with exit code 1` issue when temp directory where tar is getting created is actually the subdirectory of the path mentioned by the user for caching. ([issue](https://github.com/actions/cache/issues/689)) + +### 3.0.0 + +- Updated actions/cache to suppress Actions cache server error and log warning for those error [#1122](https://github.com/actions/toolkit/pull/1122) + +### 3.0.1 + +- Fix [#833](https://github.com/actions/cache/issues/833) - cache doesn't work with github workspace directory. +- Fix [#809](https://github.com/actions/cache/issues/809) `zstd -d: no such file or directory` error on AWS self-hosted runners. + +### 3.0.2 + +- Added 1 hour timeout for the download stuck issue [#810](https://github.com/actions/cache/issues/810). + +### 3.0.3 + +- Bug fixes for download stuck issue [#810](https://github.com/actions/cache/issues/810). + +### 3.0.4 + +- Fix zstd not working for windows on gnu tar in issues [#888](https://github.com/actions/cache/issues/888) and [#891](https://github.com/actions/cache/issues/891). +- Allowing users to provide a custom timeout as input for aborting download of a cache segment using an environment variable `SEGMENT_DOWNLOAD_TIMEOUT_MINS`. Default is 60 minutes. + +### 3.0.5 + +- Update `@actions/cache` to use `@actions/core@^1.10.0` + +### 3.0.6 + +- Added `@azure/abort-controller` to dependencies to fix compatibility issue with ESM [#1208](https://github.com/actions/toolkit/issues/1208) + +### 3.1.0-beta.1 + +- Update actions/cache on windows to use gnu tar and zstd by default and fallback to bsdtar and zstd if gnu tar is not available. ([issue](https://github.com/actions/cache/issues/984)) + +### 3.1.0-beta.2 + +- Added support for fallback to gzip to restore old caches on windows. + +### 3.1.0-beta.3 + +- Bug Fixes for fallback to gzip to restore old caches on windows and bsdtar if gnutar is not available. + +### 3.1.0 + +- Update actions/cache on windows to use gnu tar and zstd by default +- Update actions/cache on windows to fallback to bsdtar and zstd if gnu tar is not available. +- Added support for fallback to gzip to restore old caches on windows. + +### 3.1.1 + +- Reverted changes in 3.1.0 to fix issue with symlink restoration on windows. +- Added support for verbose logging about cache version during cache miss. + +### 3.1.2 + +- Fix issue with symlink restoration on windows. + +### 3.1.3 + +- Fix to prevent from setting MYSYS environement variable globally [#1329](https://github.com/actions/toolkit/pull/1329). + +### 3.1.4 + +- Fix zstd not being used due to `zstd --version` output change in zstd 1.5.4 release. See [#1353](https://github.com/actions/toolkit/pull/1353). + +### 3.2.0 + +- Add `lookupOnly` to cache restore `DownloadOptions`. + +### 3.2.1 + +- Updated @azure/storage-blob to `v12.13.0` + +### 3.2.2 + +- Add new default cache download method to improve performance and reduce hangs [#1484](https://github.com/actions/toolkit/pull/1484) diff --git a/actions-toolkit/packages/cache/__tests__/__fixtures__/action.yml b/actions-toolkit/packages/cache/__tests__/__fixtures__/action.yml new file mode 100644 index 00000000..7cd98502 --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/__fixtures__/action.yml @@ -0,0 +1,5 @@ +name: 'Set env variables' +description: 'Sets certain env variables so that e2e restore and save cache can be tested in a shell' +runs: + using: 'node12' + main: 'index.js' \ No newline at end of file diff --git a/__tests__/__fixtures__/helloWorld.txt b/actions-toolkit/packages/cache/__tests__/__fixtures__/helloWorld.txt similarity index 100% rename from __tests__/__fixtures__/helloWorld.txt rename to actions-toolkit/packages/cache/__tests__/__fixtures__/helloWorld.txt diff --git a/actions-toolkit/packages/cache/__tests__/__fixtures__/index.js b/actions-toolkit/packages/cache/__tests__/__fixtures__/index.js new file mode 100644 index 00000000..b8057c2a --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/__fixtures__/index.js @@ -0,0 +1,14 @@ +// Certain env variables are not set by default in a shell context and are only available in a node context from a running action +// In order to be able to restore and save cache e2e in a shell when running CI tests, we need these env variables set +const fs = require('fs'); +const os = require('os'); +const filePath = process.env[`GITHUB_ENV`] +fs.appendFileSync(filePath, `ACTIONS_RUNTIME_TOKEN=${process.env.ACTIONS_RUNTIME_TOKEN}${os.EOL}`, { + encoding: 'utf8' +}) +fs.appendFileSync(filePath, `ACTIONS_CACHE_URL=${process.env.ACTIONS_CACHE_URL}${os.EOL}`, { + encoding: 'utf8' +}) +fs.appendFileSync(filePath, `GITHUB_RUN_ID=${process.env.GITHUB_RUN_ID}${os.EOL}`, { + encoding: 'utf8' +}) \ No newline at end of file diff --git a/actions-toolkit/packages/cache/__tests__/cache.test.ts b/actions-toolkit/packages/cache/__tests__/cache.test.ts new file mode 100644 index 00000000..80375305 --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/cache.test.ts @@ -0,0 +1,14 @@ +import * as cache from '../src/cache' + +test('isFeatureAvailable returns true if server url is set', () => { + try { + process.env['ACTIONS_CACHE_URL'] = 'http://cache.com' + expect(cache.isFeatureAvailable()).toBe(true) + } finally { + delete process.env['ACTIONS_CACHE_URL'] + } +}) + +test('isFeatureAvailable returns false if server url is not set', () => { + expect(cache.isFeatureAvailable()).toBe(false) +}) diff --git a/actions-toolkit/packages/cache/__tests__/cacheHttpClient.test.ts b/actions-toolkit/packages/cache/__tests__/cacheHttpClient.test.ts new file mode 100644 index 00000000..02dd91c1 --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/cacheHttpClient.test.ts @@ -0,0 +1,167 @@ +import {downloadCache, getCacheVersion} from '../src/internal/cacheHttpClient' +import {CompressionMethod} from '../src/internal/constants' +import * as downloadUtils from '../src/internal/downloadUtils' +import {DownloadOptions, getDownloadOptions} from '../src/options' + +jest.mock('../src/internal/downloadUtils') + +test('getCacheVersion with one path returns version', async () => { + const paths = ['node_modules'] + const result = getCacheVersion(paths, undefined, true) + expect(result).toEqual( + 'b3e0c6cb5ecf32614eeb2997d905b9c297046d7cbf69062698f25b14b4cb0985' + ) +}) + +test('getCacheVersion with multiple paths returns version', async () => { + const paths = ['node_modules', 'dist'] + const result = getCacheVersion(paths, undefined, true) + expect(result).toEqual( + '165c3053bc646bf0d4fac17b1f5731caca6fe38e0e464715c0c3c6b6318bf436' + ) +}) + +test('getCacheVersion with zstd compression returns version', async () => { + const paths = ['node_modules'] + const result = getCacheVersion(paths, CompressionMethod.Zstd, true) + + expect(result).toEqual( + '273877e14fd65d270b87a198edbfa2db5a43de567c9a548d2a2505b408befe24' + ) +}) + +test('getCacheVersion with gzip compression returns version', async () => { + const paths = ['node_modules'] + const result = getCacheVersion(paths, CompressionMethod.Gzip, true) + + expect(result).toEqual( + '470e252814dbffc9524891b17cf4e5749b26c1b5026e63dd3f00972db2393117' + ) +}) + +test('getCacheVersion with enableCrossOsArchive as false returns version on windows', async () => { + if (process.platform === 'win32') { + const paths = ['node_modules'] + const result = getCacheVersion(paths) + + expect(result).toEqual( + '2db19d6596dc34f51f0043120148827a264863f5c6ac857569c2af7119bad14e' + ) + } +}) + +test('downloadCache uses http-client for non-Azure URLs', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' + ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const archiveLocation = 'http://www.actionscache.test/download' + const archivePath = '/foo/bar' + + await downloadCache(archiveLocation, archivePath) + + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(1) + expect(downloadCacheHttpClientMock).toHaveBeenCalledWith( + archiveLocation, + archivePath + ) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0) +}) + +test('downloadCache uses storage SDK for Azure storage URLs', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' + ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const downloadCacheHttpClientConcurrentMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClientConcurrent' + ) + + const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz' + const archivePath = '/foo/bar' + + await downloadCache(archiveLocation, archivePath) + + expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledTimes(1) + expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledWith( + archiveLocation, + archivePath, + getDownloadOptions() + ) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0) + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(0) +}) + +test('downloadCache passes options to download methods', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' + ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const downloadCacheHttpClientConcurrentMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClientConcurrent' + ) + + const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz' + const archivePath = '/foo/bar' + const options: DownloadOptions = {downloadConcurrency: 4} + + await downloadCache(archiveLocation, archivePath, options) + + expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledTimes(1) + expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalled() + expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledWith( + archiveLocation, + archivePath, + getDownloadOptions(options) + ) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0) + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(0) +}) + +test('downloadCache uses http-client when overridden', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' + ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz' + const archivePath = '/foo/bar' + const options: DownloadOptions = { + useAzureSdk: false, + concurrentBlobDownloads: false + } + + await downloadCache(archiveLocation, archivePath, options) + + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(1) + expect(downloadCacheHttpClientMock).toHaveBeenCalledWith( + archiveLocation, + archivePath + ) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0) +}) diff --git a/actions-toolkit/packages/cache/__tests__/cacheUtils.test.ts b/actions-toolkit/packages/cache/__tests__/cacheUtils.test.ts new file mode 100644 index 00000000..25124b46 --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/cacheUtils.test.ts @@ -0,0 +1,40 @@ +import {promises as fs} from 'fs' +import * as path from 'path' +import * as cacheUtils from '../src/internal/cacheUtils' + +test('getArchiveFileSizeInBytes returns file size', () => { + const filePath = path.join(__dirname, '__fixtures__', 'helloWorld.txt') + + const size = cacheUtils.getArchiveFileSizeInBytes(filePath) + + expect(size).toBe(11) +}) + +test('unlinkFile unlinks file', async () => { + const testDirectory = await fs.mkdtemp('unlinkFileTest') + const testFile = path.join(testDirectory, 'test.txt') + await fs.writeFile(testFile, 'hello world') + + await expect(fs.stat(testFile)).resolves.not.toThrow() + + await cacheUtils.unlinkFile(testFile) + + // This should throw as testFile should not exist + await expect(fs.stat(testFile)).rejects.toThrow() + + await fs.rmdir(testDirectory) +}) + +test('assertDefined throws if undefined', () => { + expect(() => cacheUtils.assertDefined('test', undefined)).toThrowError() +}) + +test('assertDefined returns value', () => { + expect(cacheUtils.assertDefined('test', 5)).toBe(5) +}) + +test('resolvePaths works on github workspace directory', async () => { + const workspace = process.env['GITHUB_WORKSPACE'] ?? '.' + const paths = await cacheUtils.resolvePaths([workspace]) + expect(paths.length).toBeGreaterThan(0) +}) diff --git a/__tests__/create-cache-files.sh b/actions-toolkit/packages/cache/__tests__/create-cache-files.sh similarity index 100% rename from __tests__/create-cache-files.sh rename to actions-toolkit/packages/cache/__tests__/create-cache-files.sh diff --git a/actions-toolkit/packages/cache/__tests__/downloadUtils.test.ts b/actions-toolkit/packages/cache/__tests__/downloadUtils.test.ts new file mode 100644 index 00000000..4cc089af --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/downloadUtils.test.ts @@ -0,0 +1,160 @@ +import * as core from '@actions/core' +import {DownloadProgress} from '../src/internal/downloadUtils' + +test('download progress tracked correctly', () => { + const progress = new DownloadProgress(1000) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(0) + expect(progress.segmentIndex).toBe(0) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(0) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(0) + expect(progress.isDone()).toBe(false) + + progress.nextSegment(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(0) + expect(progress.segmentIndex).toBe(1) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(0) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(250) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(250) + expect(progress.segmentIndex).toBe(1) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(250) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(500) + expect(progress.segmentIndex).toBe(1) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(500) + expect(progress.isDone()).toBe(false) + + progress.nextSegment(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(0) + expect(progress.segmentIndex).toBe(2) + expect(progress.segmentOffset).toBe(500) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(500) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(250) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(250) + expect(progress.segmentIndex).toBe(2) + expect(progress.segmentOffset).toBe(500) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(750) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(500) + expect(progress.segmentIndex).toBe(2) + expect(progress.segmentOffset).toBe(500) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(1000) + expect(progress.isDone()).toBe(true) +}) + +test('display timer works correctly', done => { + const progress = new DownloadProgress(1000) + + const infoMock = jest.spyOn(core, 'info') + infoMock.mockImplementation(() => {}) + + const check = (): void => { + expect(infoMock).toHaveBeenLastCalledWith( + expect.stringContaining('Received 500 of 1000') + ) + } + + // Validate no further updates are displayed after stopping the timer. + const test2 = (): void => { + check() + expect(progress.timeoutHandle).toBeUndefined() + done() + } + + // Validate the progress is displayed, stop the timer, and call test2. + const test1 = (): void => { + check() + + progress.stopDisplayTimer() + progress.setReceivedBytes(1000) + + setTimeout(() => test2(), 500) + } + + // Start the timer, update the received bytes, and call test1. + const start = (): void => { + progress.startDisplayTimer(10) + expect(progress.timeoutHandle).toBeDefined() + + progress.setReceivedBytes(500) + + setTimeout(() => test1(), 500) + } + + start() +}) + +test('display does not print completed line twice', () => { + const progress = new DownloadProgress(1000) + + const infoMock = jest.spyOn(core, 'info') + infoMock.mockImplementation(() => {}) + + progress.display() + + expect(progress.displayedComplete).toBe(false) + expect(infoMock).toHaveBeenCalledTimes(1) + + progress.nextSegment(1000) + progress.setReceivedBytes(500) + progress.display() + + expect(progress.displayedComplete).toBe(false) + expect(infoMock).toHaveBeenCalledTimes(2) + + progress.setReceivedBytes(1000) + progress.display() + + expect(progress.displayedComplete).toBe(true) + expect(infoMock).toHaveBeenCalledTimes(3) + + progress.display() + + expect(progress.displayedComplete).toBe(true) + expect(infoMock).toHaveBeenCalledTimes(3) +}) diff --git a/actions-toolkit/packages/cache/__tests__/options.test.ts b/actions-toolkit/packages/cache/__tests__/options.test.ts new file mode 100644 index 00000000..7585b60f --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/options.test.ts @@ -0,0 +1,83 @@ +import { + DownloadOptions, + UploadOptions, + getDownloadOptions, + getUploadOptions +} from '../src/options' + +const useAzureSdk = false +const concurrentBlobDownloads = true +const downloadConcurrency = 8 +const timeoutInMs = 30000 +const segmentTimeoutInMs = 600000 +const lookupOnly = false +const uploadConcurrency = 4 +const uploadChunkSize = 32 * 1024 * 1024 + +test('getDownloadOptions sets defaults', async () => { + const actualOptions = getDownloadOptions() + + expect(actualOptions).toEqual({ + useAzureSdk, + concurrentBlobDownloads, + downloadConcurrency, + timeoutInMs, + segmentTimeoutInMs, + lookupOnly + }) +}) + +test('getDownloadOptions overrides all settings', async () => { + const expectedOptions: DownloadOptions = { + useAzureSdk: true, + concurrentBlobDownloads: false, + downloadConcurrency: 14, + timeoutInMs: 20000, + segmentTimeoutInMs: 3600000, + lookupOnly: true + } + + const actualOptions = getDownloadOptions(expectedOptions) + + expect(actualOptions).toEqual(expectedOptions) +}) + +test('getUploadOptions sets defaults', async () => { + const actualOptions = getUploadOptions() + + expect(actualOptions).toEqual({ + uploadConcurrency, + uploadChunkSize + }) +}) + +test('getUploadOptions overrides all settings', async () => { + const expectedOptions: UploadOptions = { + uploadConcurrency: 2, + uploadChunkSize: 16 * 1024 * 1024 + } + + const actualOptions = getUploadOptions(expectedOptions) + + expect(actualOptions).toEqual(expectedOptions) +}) + +test('getDownloadOptions overrides download timeout minutes', async () => { + const expectedOptions: DownloadOptions = { + useAzureSdk: false, + downloadConcurrency: 14, + timeoutInMs: 20000, + segmentTimeoutInMs: 3600000, + lookupOnly: true + } + process.env.SEGMENT_DOWNLOAD_TIMEOUT_MINS = '10' + const actualOptions = getDownloadOptions(expectedOptions) + + expect(actualOptions.useAzureSdk).toEqual(expectedOptions.useAzureSdk) + expect(actualOptions.downloadConcurrency).toEqual( + expectedOptions.downloadConcurrency + ) + expect(actualOptions.timeoutInMs).toEqual(expectedOptions.timeoutInMs) + expect(actualOptions.segmentTimeoutInMs).toEqual(600000) + expect(actualOptions.lookupOnly).toEqual(expectedOptions.lookupOnly) +}) diff --git a/actions-toolkit/packages/cache/__tests__/requestUtils.test.ts b/actions-toolkit/packages/cache/__tests__/requestUtils.test.ts new file mode 100644 index 00000000..05fc573b --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/requestUtils.test.ts @@ -0,0 +1,179 @@ +import {retry, retryTypedResponse} from '../src/internal/requestUtils' +import {HttpClientError} from '@actions/http-client' +import * as requestUtils from '../src/internal/requestUtils' + +interface ITestResponse { + statusCode: number + result: string | null + error: Error | null +} + +function TestResponse( + action: number | Error, + result: string | null = null +): ITestResponse { + if (action instanceof Error) { + return { + statusCode: -1, + result, + error: action + } + } else { + return { + statusCode: action, + result, + error: null + } + } +} + +async function handleResponse( + response: ITestResponse | undefined +): Promise { + if (!response) { + fail('Retry method called too many times') + } + + if (response.error) { + throw response.error + } else { + return Promise.resolve(response) + } +} + +async function testRetryExpectingResult( + responses: ITestResponse[], + expectedResult: string | null +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + const actualResult = await retry( + 'test', + async () => handleResponse(responses.pop()), + (response: ITestResponse) => response.statusCode, + 2, // maxAttempts + 0 // delay + ) + + expect(actualResult.result).toEqual(expectedResult) +} + +async function testRetryConvertingErrorToResult( + responses: ITestResponse[], + expectedStatus: number, + expectedResult: string | null +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + const actualResult = await retry( + 'test', + async () => handleResponse(responses.pop()), + (response: ITestResponse) => response.statusCode, + 2, // maxAttempts + 0, // delay + (e: Error) => { + if (e instanceof HttpClientError) { + return { + statusCode: e.statusCode, + result: null, + error: null + } + } + } + ) + + expect(actualResult.statusCode).toEqual(expectedStatus) + expect(actualResult.result).toEqual(expectedResult) +} + +async function testRetryExpectingError( + responses: ITestResponse[] +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + expect( + retry( + 'test', + async () => handleResponse(responses.pop()), + (response: ITestResponse) => response.statusCode, + 2, // maxAttempts, + 0 // delay + ) + ).rejects.toBeInstanceOf(Error) +} + +test('retry works on successful response', async () => { + await testRetryExpectingResult([TestResponse(200, 'Ok')], 'Ok') +}) + +test('retry works after retryable status code', async () => { + await testRetryExpectingResult( + [TestResponse(503), TestResponse(200, 'Ok')], + 'Ok' + ) +}) + +test('retry fails after exhausting retries', async () => { + await testRetryExpectingError([ + TestResponse(503), + TestResponse(503), + TestResponse(200, 'Ok') + ]) +}) + +test('retry fails after non-retryable status code', async () => { + await testRetryExpectingError([TestResponse(500), TestResponse(200, 'Ok')]) +}) + +test('retry works after error', async () => { + await testRetryExpectingResult( + [TestResponse(new Error('Test error')), TestResponse(200, 'Ok')], + 'Ok' + ) +}) + +test('retry returns after client error', async () => { + await testRetryExpectingResult( + [TestResponse(400), TestResponse(200, 'Ok')], + null + ) +}) + +test('retry converts errors to response object', async () => { + await testRetryConvertingErrorToResult( + [TestResponse(new HttpClientError('Test error', 409))], + 409, + null + ) +}) + +test('retryTypedResponse gives an error with error message', async () => { + const httpClientError = new HttpClientError( + 'The cache filesize must be between 0 and 10 * 1024 * 1024 bytes', + 400 + ) + jest.spyOn(requestUtils, 'retry').mockReturnValue( + new Promise(resolve => { + resolve(httpClientError) + }) + ) + try { + await retryTypedResponse( + 'reserveCache', + async () => + new Promise(resolve => { + resolve({ + statusCode: 400, + result: '', + headers: {}, + error: httpClientError + }) + }) + ) + } catch (error) { + expect(error).toHaveProperty( + 'message', + 'The cache filesize must be between 0 and 10 * 1024 * 1024 bytes' + ) + } +}) diff --git a/actions-toolkit/packages/cache/__tests__/restoreCache.test.ts b/actions-toolkit/packages/cache/__tests__/restoreCache.test.ts new file mode 100644 index 00000000..7992490e --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/restoreCache.test.ts @@ -0,0 +1,314 @@ +import * as core from '@actions/core' +import * as path from 'path' +import {restoreCache} from '../src/cache' +import * as cacheHttpClient from '../src/internal/cacheHttpClient' +import * as cacheUtils from '../src/internal/cacheUtils' +import {CacheFilename, CompressionMethod} from '../src/internal/constants' +import {ArtifactCacheEntry} from '../src/internal/contracts' +import * as tar from '../src/internal/tar' + +jest.mock('../src/internal/cacheHttpClient') +jest.mock('../src/internal/cacheUtils') +jest.mock('../src/internal/tar') + +beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + jest.spyOn(core, 'error').mockImplementation(() => {}) + + jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => { + const actualUtils = jest.requireActual('../src/internal/cacheUtils') + return actualUtils.getCacheFileName(cm) + }) +}) + +test('restore with no path should fail', async () => { + const paths: string[] = [] + const key = 'node-test' + await expect(restoreCache(paths, key)).rejects.toThrowError( + `Path Validation Error: At least one directory or file path is required` + ) +}) + +test('restore with too many keys should fail', async () => { + const paths = ['node_modules'] + const key = 'node-test' + const restoreKeys = [...Array(20).keys()].map(x => x.toString()) + await expect(restoreCache(paths, key, restoreKeys)).rejects.toThrowError( + `Key Validation Error: Keys are limited to a maximum of 10.` + ) +}) + +test('restore with large key should fail', async () => { + const paths = ['node_modules'] + const key = 'foo'.repeat(512) // Over the 512 character limit + await expect(restoreCache(paths, key)).rejects.toThrowError( + `Key Validation Error: ${key} cannot be larger than 512 characters.` + ) +}) + +test('restore with invalid key should fail', async () => { + const paths = ['node_modules'] + const key = 'comma,comma' + await expect(restoreCache(paths, key)).rejects.toThrowError( + `Key Validation Error: ${key} cannot contain commas.` + ) +}) + +test('restore with no cache found', async () => { + const paths = ['node_modules'] + const key = 'node-test' + + jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(async () => { + return Promise.resolve(null) + }) + + const cacheKey = await restoreCache(paths, key) + + expect(cacheKey).toBe(undefined) +}) + +test('restore with server error should fail', async () => { + const paths = ['node_modules'] + const key = 'node-test' + const logWarningMock = jest.spyOn(core, 'warning') + + jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(() => { + throw new Error('HTTP Error Occurred') + }) + + const cacheKey = await restoreCache(paths, key) + expect(cacheKey).toBe(undefined) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith( + 'Failed to restore: HTTP Error Occurred' + ) +}) + +test('restore with restore keys and no cache found', async () => { + const paths = ['node_modules'] + const key = 'node-test' + const restoreKey = 'node-' + + jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(async () => { + return Promise.resolve(null) + }) + + const cacheKey = await restoreCache(paths, key, [restoreKey]) + + expect(cacheKey).toBe(undefined) +}) + +test('restore with gzip compressed cache found', async () => { + const paths = ['node_modules'] + const key = 'node-test' + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: key, + scope: 'refs/heads/main', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + + const tempPath = '/foo/bar' + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + createTempDirectoryMock.mockImplementation(async () => { + return Promise.resolve(tempPath) + }) + + const archivePath = path.join(tempPath, CacheFilename.Gzip) + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const fileSize = 142 + const getArchiveFileSizeInBytesMock = jest + .spyOn(cacheUtils, 'getArchiveFileSizeInBytes') + .mockReturnValue(fileSize) + + const extractTarMock = jest.spyOn(tar, 'extractTar') + const unlinkFileMock = jest.spyOn(cacheUtils, 'unlinkFile') + + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + const cacheKey = await restoreCache(paths, key) + + expect(cacheKey).toBe(key) + expect(getCacheMock).toHaveBeenCalledWith([key], paths, { + compressionMethod: compression, + enableCrossOsArchive: false + }) + expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) + expect(downloadCacheMock).toHaveBeenCalledWith( + cacheEntry.archiveLocation, + archivePath, + undefined + ) + expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath) + + expect(extractTarMock).toHaveBeenCalledTimes(1) + expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) + + expect(unlinkFileMock).toHaveBeenCalledTimes(1) + expect(unlinkFileMock).toHaveBeenCalledWith(archivePath) + + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('restore with zstd compressed cache found', async () => { + const paths = ['node_modules'] + const key = 'node-test' + + const infoMock = jest.spyOn(core, 'info') + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: key, + scope: 'refs/heads/main', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + const tempPath = '/foo/bar' + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + createTempDirectoryMock.mockImplementation(async () => { + return Promise.resolve(tempPath) + }) + + const archivePath = path.join(tempPath, CacheFilename.Zstd) + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const fileSize = 62915000 + const getArchiveFileSizeInBytesMock = jest + .spyOn(cacheUtils, 'getArchiveFileSizeInBytes') + .mockReturnValue(fileSize) + + const extractTarMock = jest.spyOn(tar, 'extractTar') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + const cacheKey = await restoreCache(paths, key) + + expect(cacheKey).toBe(key) + expect(getCacheMock).toHaveBeenCalledWith([key], paths, { + compressionMethod: compression, + enableCrossOsArchive: false + }) + expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) + expect(downloadCacheMock).toHaveBeenCalledWith( + cacheEntry.archiveLocation, + archivePath, + undefined + ) + expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath) + expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`) + + expect(extractTarMock).toHaveBeenCalledTimes(1) + expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('restore with cache found for restore key', async () => { + const paths = ['node_modules'] + const key = 'node-test' + const restoreKey = 'node-' + + const infoMock = jest.spyOn(core, 'info') + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: restoreKey, + scope: 'refs/heads/main', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + const tempPath = '/foo/bar' + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + createTempDirectoryMock.mockImplementation(async () => { + return Promise.resolve(tempPath) + }) + + const archivePath = path.join(tempPath, CacheFilename.Zstd) + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const fileSize = 142 + const getArchiveFileSizeInBytesMock = jest + .spyOn(cacheUtils, 'getArchiveFileSizeInBytes') + .mockReturnValue(fileSize) + + const extractTarMock = jest.spyOn(tar, 'extractTar') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + const cacheKey = await restoreCache(paths, key, [restoreKey]) + + expect(cacheKey).toBe(restoreKey) + expect(getCacheMock).toHaveBeenCalledWith([key, restoreKey], paths, { + compressionMethod: compression, + enableCrossOsArchive: false + }) + expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) + expect(downloadCacheMock).toHaveBeenCalledWith( + cacheEntry.archiveLocation, + archivePath, + undefined + ) + expect(getArchiveFileSizeInBytesMock).toHaveBeenCalledWith(archivePath) + expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`) + + expect(extractTarMock).toHaveBeenCalledTimes(1) + expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('restore with dry run', async () => { + const paths = ['node_modules'] + const key = 'node-test' + const options = {lookupOnly: true} + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: key, + scope: 'refs/heads/main', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + const cacheKey = await restoreCache(paths, key, undefined, options) + + expect(cacheKey).toBe(key) + expect(getCompressionMock).toHaveBeenCalledTimes(1) + expect(getCacheMock).toHaveBeenCalledWith([key], paths, { + compressionMethod: compression, + enableCrossOsArchive: false + }) + // creating a tempDir and downloading the cache are skipped + expect(createTempDirectoryMock).toHaveBeenCalledTimes(0) + expect(downloadCacheMock).toHaveBeenCalledTimes(0) +}) diff --git a/actions-toolkit/packages/cache/__tests__/saveCache.test.ts b/actions-toolkit/packages/cache/__tests__/saveCache.test.ts new file mode 100644 index 00000000..4d0027be --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/saveCache.test.ts @@ -0,0 +1,329 @@ +import * as core from '@actions/core' +import * as path from 'path' +import {saveCache} from '../src/cache' +import * as cacheHttpClient from '../src/internal/cacheHttpClient' +import * as cacheUtils from '../src/internal/cacheUtils' +import {CacheFilename, CompressionMethod} from '../src/internal/constants' +import * as tar from '../src/internal/tar' +import {TypedResponse} from '@actions/http-client/lib/interfaces' +import { + ReserveCacheResponse, + ITypedResponseWithError +} from '../src/internal/contracts' +import {HttpClientError} from '@actions/http-client' + +jest.mock('../src/internal/cacheHttpClient') +jest.mock('../src/internal/cacheUtils') +jest.mock('../src/internal/tar') + +beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + jest.spyOn(core, 'error').mockImplementation(() => {}) + jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => { + const actualUtils = jest.requireActual('../src/internal/cacheUtils') + return actualUtils.getCacheFileName(cm) + }) + jest.spyOn(cacheUtils, 'resolvePaths').mockImplementation(async filePaths => { + return filePaths.map(x => path.resolve(x)) + }) + jest.spyOn(cacheUtils, 'createTempDirectory').mockImplementation(async () => { + return Promise.resolve('/foo/bar') + }) +}) + +test('save with missing input should fail', async () => { + const paths: string[] = [] + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + await expect(saveCache(paths, primaryKey)).rejects.toThrowError( + `Path Validation Error: At least one directory or file path is required` + ) +}) + +test('save with large cache outputs should fail', async () => { + const filePath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(filePath)] + + const createTarMock = jest.spyOn(tar, 'createTar') + const logWarningMock = jest.spyOn(core, 'warning') + + const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit + jest + .spyOn(cacheUtils, 'getArchiveFileSizeInBytes') + .mockReturnValueOnce(cacheSize) + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValueOnce(Promise.resolve(compression)) + + const cacheId = await saveCache([filePath], primaryKey) + expect(cacheId).toBe(-1) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith( + 'Failed to save: Cache size of ~11264 MB (11811160064 B) is over the 10GB limit, not saving cache.' + ) + + const archiveFolder = '/foo/bar' + + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with large cache outputs should fail in GHES with error message', async () => { + const filePath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(filePath)] + + const createTarMock = jest.spyOn(tar, 'createTar') + const logWarningMock = jest.spyOn(core, 'warning') + + const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit + jest + .spyOn(cacheUtils, 'getArchiveFileSizeInBytes') + .mockReturnValueOnce(cacheSize) + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValueOnce(Promise.resolve(compression)) + + jest.spyOn(cacheUtils, 'isGhes').mockReturnValueOnce(true) + + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + const response: ITypedResponseWithError = { + statusCode: 400, + result: null, + headers: {}, + error: new HttpClientError( + 'The cache filesize must be between 0 and 1073741824 bytes', + 400 + ) + } + return response + }) + + const cacheId = await saveCache([filePath], primaryKey) + expect(cacheId).toBe(-1) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith( + 'Failed to save: The cache filesize must be between 0 and 1073741824 bytes' + ) + + const archiveFolder = '/foo/bar' + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with large cache outputs should fail in GHES without error message', async () => { + const filePath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(filePath)] + + const createTarMock = jest.spyOn(tar, 'createTar') + const logWarningMock = jest.spyOn(core, 'warning') + + const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit + jest + .spyOn(cacheUtils, 'getArchiveFileSizeInBytes') + .mockReturnValueOnce(cacheSize) + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValueOnce(Promise.resolve(compression)) + + jest.spyOn(cacheUtils, 'isGhes').mockReturnValueOnce(true) + + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + const response: ITypedResponseWithError = { + statusCode: 400, + result: null, + headers: {} + } + return response + }) + + const cacheId = await saveCache([filePath], primaryKey) + expect(cacheId).toBe(-1) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith( + 'Failed to save: Cache size of ~11264 MB (11811160064 B) is over the data cap limit, not saving cache.' + ) + + const archiveFolder = '/foo/bar' + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with reserve cache failure should fail', async () => { + const paths = ['node_modules'] + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const logInfoMock = jest.spyOn(core, 'info') + + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + const response: TypedResponse = { + statusCode: 500, + result: null, + headers: {} + } + return response + }) + + const createTarMock = jest.spyOn(tar, 'createTar') + const saveCacheMock = jest.spyOn(cacheHttpClient, 'saveCache') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValueOnce(Promise.resolve(compression)) + + const cacheId = await saveCache(paths, primaryKey) + expect(cacheId).toBe(-1) + expect(logInfoMock).toHaveBeenCalledTimes(1) + expect(logInfoMock).toHaveBeenCalledWith( + `Failed to save: Unable to reserve cache with key ${primaryKey}, another job may be creating this cache. More details: undefined` + ) + + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, paths, { + cacheSize: undefined, + compressionMethod: compression, + enableCrossOsArchive: false + }) + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(saveCacheMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with server error should fail', async () => { + const filePath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(filePath)] + const logWarningMock = jest.spyOn(core, 'warning') + const cacheId = 4 + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + const response: TypedResponse = { + statusCode: 500, + result: {cacheId}, + headers: {} + } + return response + }) + + const createTarMock = jest.spyOn(tar, 'createTar') + + const saveCacheMock = jest + .spyOn(cacheHttpClient, 'saveCache') + .mockImplementationOnce(() => { + throw new Error('HTTP Error Occurred') + }) + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValueOnce(Promise.resolve(compression)) + + await saveCache([filePath], primaryKey) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith( + 'Failed to save: HTTP Error Occurred' + ) + + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], { + cacheSize: undefined, + compressionMethod: compression, + enableCrossOsArchive: false + }) + const archiveFolder = '/foo/bar' + const archiveFile = path.join(archiveFolder, CacheFilename.Zstd) + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + expect(saveCacheMock).toHaveBeenCalledTimes(1) + expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with valid inputs uploads a cache', async () => { + const filePath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(filePath)] + + const cacheId = 4 + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + const response: TypedResponse = { + statusCode: 500, + result: {cacheId}, + headers: {} + } + return response + }) + const createTarMock = jest.spyOn(tar, 'createTar') + + const saveCacheMock = jest.spyOn(cacheHttpClient, 'saveCache') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await saveCache([filePath], primaryKey) + + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], { + cacheSize: undefined, + compressionMethod: compression, + enableCrossOsArchive: false + }) + const archiveFolder = '/foo/bar' + const archiveFile = path.join(archiveFolder, CacheFilename.Zstd) + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + expect(saveCacheMock).toHaveBeenCalledTimes(1) + expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with non existing path should not save cache', async () => { + const path = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + jest.spyOn(cacheUtils, 'resolvePaths').mockImplementation(async () => { + return [] + }) + await expect(saveCache([path], primaryKey)).rejects.toThrowError( + `Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.` + ) +}) diff --git a/actions-toolkit/packages/cache/__tests__/tar.test.ts b/actions-toolkit/packages/cache/__tests__/tar.test.ts new file mode 100644 index 00000000..4145d9a9 --- /dev/null +++ b/actions-toolkit/packages/cache/__tests__/tar.test.ts @@ -0,0 +1,482 @@ +import * as exec from '@actions/exec' +import * as io from '@actions/io' +import * as path from 'path' +import { + CacheFilename, + CompressionMethod, + GnuTarPathOnWindows, + ManifestFilename, + SystemTarPathOnWindows, + TarFilename +} from '../src/internal/constants' +import * as tar from '../src/internal/tar' +import * as utils from '../src/internal/cacheUtils' +// eslint-disable-next-line @typescript-eslint/no-require-imports +import fs = require('fs') + +jest.mock('@actions/exec') +jest.mock('@actions/io') + +const IS_WINDOWS = process.platform === 'win32' +const IS_MAC = process.platform === 'darwin' + +const defaultTarPath = IS_MAC ? 'gtar' : 'tar' + +const defaultEnv = {MSYS: 'winsymlinks:nativestrict'} + +function getTempDir(): string { + return path.join(__dirname, '_temp', 'tar') +} + +beforeAll(async () => { + jest.spyOn(io, 'which').mockImplementation(async tool => { + return tool + }) + + process.env['GITHUB_WORKSPACE'] = process.cwd() + await jest.requireActual('@actions/io').rmRF(getTempDir()) +}) + +beforeEach(async () => { + jest.restoreAllMocks() +}) + +afterAll(async () => { + delete process.env['GITHUB_WORKSPACE'] + await jest.requireActual('@actions/io').rmRF(getTempDir()) +}) + +test('zstd extract tar', async () => { + const mkdirMock = jest.spyOn(io, 'mkdirP') + const execMock = jest.spyOn(exec, 'exec') + + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + const workspace = process.env['GITHUB_WORKSPACE'] + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + + await tar.extractTar(archivePath, CompressionMethod.Zstd) + + expect(mkdirMock).toHaveBeenCalledWith(workspace) + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '-xf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat([ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ]) + .join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) +}) + +test('zstd extract tar with windows BSDtar', async () => { + if (IS_WINDOWS) { + const mkdirMock = jest.spyOn(io, 'mkdirP') + const execMock = jest.spyOn(exec, 'exec') + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) + + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + const workspace = process.env['GITHUB_WORKSPACE'] + const tarPath = SystemTarPathOnWindows + + await tar.extractTar(archivePath, CompressionMethod.Zstd) + + expect(mkdirMock).toHaveBeenCalledWith(workspace) + expect(execMock).toHaveBeenCalledTimes(2) + + expect(execMock).toHaveBeenNthCalledWith( + 1, + [ + 'zstd -d --long=30 --force -o', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ].join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) + + expect(execMock).toHaveBeenNthCalledWith( + 2, + [ + `"${tarPath}"`, + '-xf', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + workspace?.replace(/\\/g, '/') + ].join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) + } +}) + +test('gzip extract tar', async () => { + const mkdirMock = jest.spyOn(io, 'mkdirP') + const execMock = jest.spyOn(exec, 'exec') + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + const workspace = process.env['GITHUB_WORKSPACE'] + + await tar.extractTar(archivePath, CompressionMethod.Gzip) + + expect(mkdirMock).toHaveBeenCalledWith(workspace) + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '-xf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat(['-z']) + .join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) +}) + +test('gzip extract GNU tar on windows with GNUtar in path', async () => { + if (IS_WINDOWS) { + // GNU tar present in path but not at default location + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('tar')) + const execMock = jest.spyOn(exec, 'exec') + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + const workspace = process.env['GITHUB_WORKSPACE'] + + await tar.extractTar(archivePath, CompressionMethod.Gzip) + + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"tar"`, + '-xf', + archivePath.replace(/\\/g, '/'), + '-P', + '-C', + workspace?.replace(/\\/g, '/'), + '--force-local', + '-z' + ].join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) + } +}) + +test('zstd create tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + + const archiveFolder = getTempDir() + const workspace = process.env['GITHUB_WORKSPACE'] + const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`] + + await fs.promises.mkdir(archiveFolder, {recursive: true}) + + await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Zstd) + + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '--posix', + '-cf', + IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd, + '--exclude', + IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, + '--files-from', + ManifestFilename + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat([ + '--use-compress-program', + IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' + ]) + .join(' '), + undefined, // args + { + cwd: archiveFolder, + env: expect.objectContaining(defaultEnv) + } + ) +}) + +test('zstd create tar with windows BSDtar', async () => { + if (IS_WINDOWS) { + const execMock = jest.spyOn(exec, 'exec') + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) + + const archiveFolder = getTempDir() + const workspace = process.env['GITHUB_WORKSPACE'] + const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`] + + await fs.promises.mkdir(archiveFolder, {recursive: true}) + + await tar.createTar( + archiveFolder, + sourceDirectories, + CompressionMethod.Zstd + ) + + const tarPath = SystemTarPathOnWindows + + expect(execMock).toHaveBeenCalledTimes(2) + + expect(execMock).toHaveBeenNthCalledWith( + 1, + [ + `"${tarPath}"`, + '--posix', + '-cf', + TarFilename.replace(/\\/g, '/'), + '--exclude', + TarFilename.replace(/\\/g, '/'), + '-P', + '-C', + workspace?.replace(/\\/g, '/'), + '--files-from', + ManifestFilename + ].join(' '), + undefined, // args + { + cwd: archiveFolder, + env: expect.objectContaining(defaultEnv) + } + ) + + expect(execMock).toHaveBeenNthCalledWith( + 2, + [ + 'zstd -T0 --long=30 --force -o', + CacheFilename.Zstd.replace(/\\/g, '/'), + TarFilename.replace(/\\/g, '/') + ].join(' '), + undefined, // args + { + cwd: archiveFolder, + env: expect.objectContaining(defaultEnv) + } + ) + } +}) + +test('gzip create tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + + const archiveFolder = getTempDir() + const workspace = process.env['GITHUB_WORKSPACE'] + const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`] + + await fs.promises.mkdir(archiveFolder, {recursive: true}) + + await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Gzip) + + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '--posix', + '-cf', + IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip, + '--exclude', + IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, + '--files-from', + ManifestFilename + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat(['-z']) + .join(' '), + undefined, // args + { + cwd: archiveFolder, + env: expect.objectContaining(defaultEnv) + } + ) +}) + +test('zstd list tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + + await tar.listTar(archivePath, CompressionMethod.Zstd) + + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '-tf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P' + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat([ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ]) + .join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) +}) + +test('zstd list tar with windows BSDtar', async () => { + if (IS_WINDOWS) { + const execMock = jest.spyOn(exec, 'exec') + jest + .spyOn(utils, 'getGnuTarPathOnWindows') + .mockReturnValue(Promise.resolve('')) + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + + await tar.listTar(archivePath, CompressionMethod.Zstd) + + const tarPath = SystemTarPathOnWindows + expect(execMock).toHaveBeenCalledTimes(2) + + expect(execMock).toHaveBeenNthCalledWith( + 1, + [ + 'zstd -d --long=30 --force -o', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ].join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) + + expect(execMock).toHaveBeenNthCalledWith( + 2, + [ + `"${tarPath}"`, + '-tf', + TarFilename.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P' + ].join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) + } +}) + +test('zstdWithoutLong list tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + + await tar.listTar(archivePath, CompressionMethod.ZstdWithoutLong) + + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '-tf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P' + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat(['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']) + .join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) +}) + +test('gzip list tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + + await tar.listTar(archivePath, CompressionMethod.Gzip) + + const tarPath = IS_WINDOWS ? GnuTarPathOnWindows : defaultTarPath + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + [ + `"${tarPath}"`, + '-tf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P' + ] + .concat(IS_WINDOWS ? ['--force-local'] : []) + .concat(IS_MAC ? ['--delay-directory-restore'] : []) + .concat(['-z']) + .join(' '), + undefined, + { + cwd: undefined, + env: expect.objectContaining(defaultEnv) + } + ) +}) diff --git a/__tests__/verify-cache-files.sh b/actions-toolkit/packages/cache/__tests__/verify-cache-files.sh similarity index 100% rename from __tests__/verify-cache-files.sh rename to actions-toolkit/packages/cache/__tests__/verify-cache-files.sh diff --git a/actions-toolkit/packages/cache/package-lock.json b/actions-toolkit/packages/cache/package-lock.json new file mode 100644 index 00000000..31a81013 --- /dev/null +++ b/actions-toolkit/packages/cache/package-lock.json @@ -0,0 +1,937 @@ +{ + "name": "@actions/cache", + "version": "3.2.2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/cache", + "version": "3.2.2", + "license": "MIT", + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^2.1.1", + "@actions/io": "^1.0.1", + "@azure/abort-controller": "^1.1.0", + "@azure/ms-rest-js": "^2.6.0", + "@azure/storage-blob": "^12.13.0", + "semver": "^6.3.1", + "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/semver": "^6.0.0", + "@types/uuid": "^3.4.5", + "typescript": "^5.2.2" + } + }, + "node_modules/@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/core/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/glob": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", + "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", + "dependencies": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz", + "integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-http": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz", + "integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-http/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@azure/core-http/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", + "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/ms-rest-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz", + "integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==", + "dependencies": { + "@azure/core-auth": "^1.1.4", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.7", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^8.3.2", + "xml2js": "^0.5.0" + } + }, + "node_modules/@azure/ms-rest-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@azure/ms-rest-js/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.15.0.tgz", + "integrity": "sha512-e7JBKLOFi0QVJqqLzrjx1eL3je3/Ug2IQj24cTM9b85CsnnFjLGeGjJVIjbGGZaytewiCEG7r3lRwQX7fKj0/w==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.4.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz", + "integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==" + }, + "node_modules/@types/node-fetch": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/node-fetch/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/semver": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.3.tgz", + "integrity": "sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==", + "dev": true + }, + "node_modules/@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz", + "integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "requires": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/glob": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", + "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", + "requires": { + "@actions/core": "^1.2.6", + "minimatch": "^3.0.4" + } + }, + "@actions/http-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz", + "integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-http": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz", + "integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/core-lro": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "requires": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + } + }, + "@azure/core-util": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", + "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/ms-rest-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz", + "integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==", + "requires": { + "@azure/core-auth": "^1.1.4", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.7", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^8.3.2", + "xml2js": "^0.5.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@azure/storage-blob": { + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.15.0.tgz", + "integrity": "sha512-e7JBKLOFi0QVJqqLzrjx1eL3je3/Ug2IQj24cTM9b85CsnnFjLGeGjJVIjbGGZaytewiCEG7r3lRwQX7fKj0/w==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + } + }, + "@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" + }, + "@types/node": { + "version": "20.4.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz", + "integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==" + }, + "@types/node-fetch": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/semver": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.3.tgz", + "integrity": "sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==", + "dev": true + }, + "@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "requires": { + "@types/node": "*" + } + }, + "@types/uuid": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz", + "integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } +} diff --git a/actions-toolkit/packages/cache/package.json b/actions-toolkit/packages/cache/package.json new file mode 100644 index 00000000..b742c525 --- /dev/null +++ b/actions-toolkit/packages/cache/package.json @@ -0,0 +1,56 @@ +{ + "name": "@actions/cache", + "version": "3.2.2", + "preview": true, + "description": "Actions cache lib", + "keywords": [ + "github", + "actions", + "cache" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/cache", + "license": "MIT", + "main": "lib/cache.js", + "types": "lib/cache.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/cache" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^2.1.1", + "@actions/io": "^1.0.1", + "@azure/abort-controller": "^1.1.0", + "@azure/ms-rest-js": "^2.6.0", + "@azure/storage-blob": "^12.13.0", + "semver": "^6.3.1", + "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/semver": "^6.0.0", + "@types/uuid": "^3.4.5", + "typescript": "^5.2.2" + } +} diff --git a/actions-toolkit/packages/cache/src/cache.ts b/actions-toolkit/packages/cache/src/cache.ts new file mode 100644 index 00000000..ad1a8b69 --- /dev/null +++ b/actions-toolkit/packages/cache/src/cache.ts @@ -0,0 +1,261 @@ +import * as core from '@actions/core' +import * as path from 'path' +import * as utils from './internal/cacheUtils' +import * as cacheHttpClient from './internal/cacheHttpClient' +import {createTar, extractTar, listTar} from './internal/tar' +import {DownloadOptions, ExtraTarArgs, UploadOptions} from './options' + +export class ValidationError extends Error { + constructor(message: string) { + super(message) + this.name = 'ValidationError' + Object.setPrototypeOf(this, ValidationError.prototype) + } +} + +export class ReserveCacheError extends Error { + constructor(message: string) { + super(message) + this.name = 'ReserveCacheError' + Object.setPrototypeOf(this, ReserveCacheError.prototype) + } +} + +function checkPaths(paths: string[]): void { + if (!paths || paths.length === 0) { + throw new ValidationError( + `Path Validation Error: At least one directory or file path is required` + ) + } +} + +function checkKey(key: string): void { + if (key.length > 512) { + throw new ValidationError( + `Key Validation Error: ${key} cannot be larger than 512 characters.` + ) + } + const regex = /^[^,]*$/ + if (!regex.test(key)) { + throw new ValidationError( + `Key Validation Error: ${key} cannot contain commas.` + ) + } +} + +/** + * isFeatureAvailable to check the presence of Actions cache service + * + * @returns boolean return true if Actions cache service feature is available, otherwise false + */ + +export function isFeatureAvailable(): boolean { + return !!process.env['ACTIONS_CACHE_URL'] +} + +/** + * Restores cache from keys + * + * @param paths a list of file paths to restore from the cache + * @param primaryKey an explicit key for restoring the cache + * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @param downloadOptions cache download options + * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform + * @returns string returns the key for the cache hit, otherwise returns undefined + */ +export async function restoreCache( + paths: string[], + primaryKey: string, + restoreKeys?: string[], + options?: DownloadOptions, + enableCrossOsArchive = false, + extraTarArgs: ExtraTarArgs = [], +): Promise { + checkPaths(paths) + + restoreKeys = restoreKeys || [] + const keys = [primaryKey, ...restoreKeys] + + core.debug('Resolved Keys:') + core.debug(JSON.stringify(keys)) + + if (keys.length > 10) { + throw new ValidationError( + `Key Validation Error: Keys are limited to a maximum of 10.` + ) + } + for (const key of keys) { + checkKey(key) + } + + const compressionMethod = await utils.getCompressionMethod() + let archivePath = '' + try { + // path are needed to compute version + const cacheEntry = await cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod, + enableCrossOsArchive + }) + if (!cacheEntry?.archiveLocation) { + // Cache not found + return undefined + } + + if (options?.lookupOnly) { + core.info('Lookup only - skipping download') + return cacheEntry.cacheKey + } + + archivePath = path.join( + await utils.createTempDirectory(), + utils.getCacheFileName(compressionMethod) + ) + core.debug(`Archive Path: ${archivePath}`) + + // Download the cache from the cache entry + await cacheHttpClient.downloadCache( + cacheEntry.archiveLocation, + archivePath, + options + ) + + if (core.isDebug()) { + await listTar(archivePath, compressionMethod) + } + + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath) + core.info( + `Cache Size: ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B)` + ) + + await extractTar(archivePath, compressionMethod, extraTarArgs) + core.info('Cache restored successfully') + + return cacheEntry.cacheKey + } catch (error) { + const typedError = error as Error + if (typedError.name === ValidationError.name) { + throw error + } else { + // Supress all non-validation cache related errors because caching should be optional + core.warning(`Failed to restore: ${(error as Error).message}`) + } + } finally { + // Try to delete the archive to save space + try { + await utils.unlinkFile(archivePath) + } catch (error) { + core.debug(`Failed to delete archive: ${error}`) + } + } + + return undefined +} + +/** + * Saves a list of files with the specified key + * + * @param paths a list of file paths to be cached + * @param key an explicit key for restoring the cache + * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform + * @param options cache upload options + * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails + */ +export async function saveCache( + paths: string[], + key: string, + options?: UploadOptions, + enableCrossOsArchive = false +): Promise { + checkPaths(paths) + checkKey(key) + + const compressionMethod = await utils.getCompressionMethod() + let cacheId = -1 + + const cachePaths = await utils.resolvePaths(paths) + core.debug('Cache Paths:') + core.debug(`${JSON.stringify(cachePaths)}`) + + if (cachePaths.length === 0) { + throw new Error( + `Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.` + ) + } + + const archiveFolder = await utils.createTempDirectory() + const archivePath = path.join( + archiveFolder, + utils.getCacheFileName(compressionMethod) + ) + + core.debug(`Archive Path: ${archivePath}`) + + try { + await createTar(archiveFolder, cachePaths, compressionMethod) + if (core.isDebug()) { + await listTar(archivePath, compressionMethod) + } + const fileSizeLimit = 10 * 1024 * 1024 * 1024 // 10GB per repo limit + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath) + core.debug(`File Size: ${archiveFileSize}`) + + // For GHES, this check will take place in ReserveCache API with enterprise file size limit + if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { + throw new Error( + `Cache size of ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.` + ) + } + + core.debug('Reserving Cache') + const reserveCacheResponse = await cacheHttpClient.reserveCache( + key, + paths, + { + compressionMethod, + enableCrossOsArchive, + cacheSize: archiveFileSize + } + ) + + if (reserveCacheResponse?.result?.cacheId) { + cacheId = reserveCacheResponse?.result?.cacheId + } else if (reserveCacheResponse?.statusCode === 400) { + throw new Error( + reserveCacheResponse?.error?.message ?? + `Cache size of ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.` + ) + } else { + throw new ReserveCacheError( + `Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${reserveCacheResponse?.error?.message}` + ) + } + + core.debug(`Saving Cache (ID: ${cacheId})`) + await cacheHttpClient.saveCache(cacheId, archivePath, options) + } catch (error) { + const typedError = error as Error + if (typedError.name === ValidationError.name) { + throw error + } else if (typedError.name === ReserveCacheError.name) { + core.info(`Failed to save: ${typedError.message}`) + } else { + core.warning(`Failed to save: ${typedError.message}`) + } + } finally { + // Try to delete the archive to save space + try { + await utils.unlinkFile(archivePath) + } catch (error) { + core.debug(`Failed to delete archive: ${error}`) + } + } + + return cacheId +} diff --git a/actions-toolkit/packages/cache/src/internal/cacheHttpClient.ts b/actions-toolkit/packages/cache/src/internal/cacheHttpClient.ts new file mode 100644 index 00000000..880ebff2 --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/cacheHttpClient.ts @@ -0,0 +1,377 @@ +import * as core from '@actions/core' +import {HttpClient} from '@actions/http-client' +import {BearerCredentialHandler} from '@actions/http-client/lib/auth' +import { + RequestOptions, + TypedResponse +} from '@actions/http-client/lib/interfaces' +import * as crypto from 'crypto' +import * as fs from 'fs' +import {URL} from 'url' + +import * as utils from './cacheUtils' +import {CompressionMethod} from './constants' +import { + ArtifactCacheEntry, + InternalCacheOptions, + CommitCacheRequest, + ReserveCacheRequest, + ReserveCacheResponse, + ITypedResponseWithError, + ArtifactCacheList +} from './contracts' +import { + downloadCacheHttpClient, + downloadCacheHttpClientConcurrent, + downloadCacheStorageSDK +} from './downloadUtils' +import { + DownloadOptions, + UploadOptions, + getDownloadOptions, + getUploadOptions +} from '../options' +import { + isSuccessStatusCode, + retryHttpClientResponse, + retryTypedResponse +} from './requestUtils' + +const versionSalt = '1.0' + +function getCacheApiUrl(resource: string): string { + const baseUrl: string = process.env['ACTIONS_CACHE_URL'] || '' + if (!baseUrl) { + throw new Error('Cache Service Url not found, unable to restore cache.') + } + + const url = `${baseUrl}_apis/artifactcache/${resource}` + core.debug(`Resource Url: ${url}`) + return url +} + +function createAcceptHeader(type: string, apiVersion: string): string { + return `${type};api-version=${apiVersion}` +} + +function getRequestOptions(): RequestOptions { + const requestOptions: RequestOptions = { + headers: { + Accept: createAcceptHeader('application/json', '6.0-preview.1') + } + } + + return requestOptions +} + +function createHttpClient(): HttpClient { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] || '' + const bearerCredentialHandler = new BearerCredentialHandler(token) + + return new HttpClient( + 'actions/cache', + [bearerCredentialHandler], + getRequestOptions() + ) +} + +export function getCacheVersion( + paths: string[], + compressionMethod?: CompressionMethod, + enableCrossOsArchive = false +): string { + const components = paths + + // Add compression method to cache version to restore + // compressed cache as per compression method + if (compressionMethod) { + components.push(compressionMethod) + } + + // Only check for windows platforms if enableCrossOsArchive is false + if (process.platform === 'win32' && !enableCrossOsArchive) { + components.push('windows-only') + } + + // Add salt to cache version to support breaking changes in cache entry + components.push(versionSalt) + + return crypto.createHash('sha256').update(components.join('|')).digest('hex') +} + +export async function getCacheEntry( + keys: string[], + paths: string[], + options?: InternalCacheOptions +): Promise { + const httpClient = createHttpClient() + const version = getCacheVersion( + paths, + options?.compressionMethod, + options?.enableCrossOsArchive + ) + const resource = `cache?keys=${encodeURIComponent( + keys.join(',') + )}&version=${version}` + + const response = await retryTypedResponse('getCacheEntry', async () => + httpClient.getJson(getCacheApiUrl(resource)) + ) + // Cache not found + if (response.statusCode === 204) { + // List cache for primary key only if cache miss occurs + if (core.isDebug()) { + await printCachesListForDiagnostics(keys[0], httpClient, version) + } + return null + } + if (!isSuccessStatusCode(response.statusCode)) { + throw new Error(`Cache service responded with ${response.statusCode}`) + } + + const cacheResult = response.result + const cacheDownloadUrl = cacheResult?.archiveLocation + if (!cacheDownloadUrl) { + // Cache achiveLocation not found. This should never happen, and hence bail out. + throw new Error('Cache not found.') + } + core.setSecret(cacheDownloadUrl) + core.debug(`Cache Result:`) + core.debug(JSON.stringify(cacheResult)) + + return cacheResult +} + +async function printCachesListForDiagnostics( + key: string, + httpClient: HttpClient, + version: string +): Promise { + const resource = `caches?key=${encodeURIComponent(key)}` + const response = await retryTypedResponse('listCache', async () => + httpClient.getJson(getCacheApiUrl(resource)) + ) + if (response.statusCode === 200) { + const cacheListResult = response.result + const totalCount = cacheListResult?.totalCount + if (totalCount && totalCount > 0) { + core.debug( + `No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:` + ) + for (const cacheEntry of cacheListResult?.artifactCaches || []) { + core.debug( + `Cache Key: ${cacheEntry?.cacheKey}, Cache Version: ${cacheEntry?.cacheVersion}, Cache Scope: ${cacheEntry?.scope}, Cache Created: ${cacheEntry?.creationTime}` + ) + } + } + } +} + +export async function downloadCache( + archiveLocation: string, + archivePath: string, + options?: DownloadOptions +): Promise { + const archiveUrl = new URL(archiveLocation) + const downloadOptions = getDownloadOptions(options) + + if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) { + if (downloadOptions.useAzureSdk) { + // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. + await downloadCacheStorageSDK( + archiveLocation, + archivePath, + downloadOptions + ) + } else if (downloadOptions.concurrentBlobDownloads) { + // Use concurrent implementation with HttpClient to work around blob SDK issue + await downloadCacheHttpClientConcurrent( + archiveLocation, + archivePath, + downloadOptions + ) + } else { + // Otherwise, download using the Actions http-client. + await downloadCacheHttpClient(archiveLocation, archivePath) + } + } else { + await downloadCacheHttpClient(archiveLocation, archivePath) + } +} + +// Reserve Cache +export async function reserveCache( + key: string, + paths: string[], + options?: InternalCacheOptions +): Promise> { + const httpClient = createHttpClient() + const version = getCacheVersion( + paths, + options?.compressionMethod, + options?.enableCrossOsArchive + ) + + const reserveCacheRequest: ReserveCacheRequest = { + key, + version, + cacheSize: options?.cacheSize + } + const response = await retryTypedResponse('reserveCache', async () => + httpClient.postJson( + getCacheApiUrl('caches'), + reserveCacheRequest + ) + ) + return response +} + +function getContentRange(start: number, end: number): string { + // Format: `bytes start-end/filesize + // start and end are inclusive + // filesize can be * + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/* + return `bytes ${start}-${end}/*` +} + +async function uploadChunk( + httpClient: HttpClient, + resourceUrl: string, + openStream: () => NodeJS.ReadableStream, + start: number, + end: number +): Promise { + core.debug( + `Uploading chunk of size ${ + end - start + 1 + } bytes at offset ${start} with content range: ${getContentRange( + start, + end + )}` + ) + const additionalHeaders = { + 'Content-Type': 'application/octet-stream', + 'Content-Range': getContentRange(start, end) + } + + const uploadChunkResponse = await retryHttpClientResponse( + `uploadChunk (start: ${start}, end: ${end})`, + async () => + httpClient.sendStream( + 'PATCH', + resourceUrl, + openStream(), + additionalHeaders + ) + ) + + if (!isSuccessStatusCode(uploadChunkResponse.message.statusCode)) { + throw new Error( + `Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.` + ) + } +} + +async function uploadFile( + httpClient: HttpClient, + cacheId: number, + archivePath: string, + options?: UploadOptions +): Promise { + // Upload Chunks + const fileSize = utils.getArchiveFileSizeInBytes(archivePath) + const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`) + const fd = fs.openSync(archivePath, 'r') + const uploadOptions = getUploadOptions(options) + + const concurrency = utils.assertDefined( + 'uploadConcurrency', + uploadOptions.uploadConcurrency + ) + const maxChunkSize = utils.assertDefined( + 'uploadChunkSize', + uploadOptions.uploadChunkSize + ) + + const parallelUploads = [...new Array(concurrency).keys()] + core.debug('Awaiting all uploads') + let offset = 0 + + try { + await Promise.all( + parallelUploads.map(async () => { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, maxChunkSize) + const start = offset + const end = offset + chunkSize - 1 + offset += maxChunkSize + + await uploadChunk( + httpClient, + resourceUrl, + () => + fs + .createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + .on('error', error => { + throw new Error( + `Cache upload failed because file read failed with ${error.message}` + ) + }), + start, + end + ) + } + }) + ) + } finally { + fs.closeSync(fd) + } + return +} + +async function commitCache( + httpClient: HttpClient, + cacheId: number, + filesize: number +): Promise> { + const commitCacheRequest: CommitCacheRequest = {size: filesize} + return await retryTypedResponse('commitCache', async () => + httpClient.postJson( + getCacheApiUrl(`caches/${cacheId.toString()}`), + commitCacheRequest + ) + ) +} + +export async function saveCache( + cacheId: number, + archivePath: string, + options?: UploadOptions +): Promise { + const httpClient = createHttpClient() + + core.debug('Upload cache') + await uploadFile(httpClient, cacheId, archivePath, options) + + // Commit Cache + core.debug('Commiting cache') + const cacheSize = utils.getArchiveFileSizeInBytes(archivePath) + core.info( + `Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)` + ) + + const commitCacheResponse = await commitCache(httpClient, cacheId, cacheSize) + if (!isSuccessStatusCode(commitCacheResponse.statusCode)) { + throw new Error( + `Cache service responded with ${commitCacheResponse.statusCode} during commit cache.` + ) + } + + core.info('Cache saved successfully') +} diff --git a/actions-toolkit/packages/cache/src/internal/cacheUtils.ts b/actions-toolkit/packages/cache/src/internal/cacheUtils.ts new file mode 100644 index 00000000..91e74424 --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/cacheUtils.ts @@ -0,0 +1,139 @@ +import * as core from '@actions/core' +import * as exec from '@actions/exec' +import * as glob from '@actions/glob' +import * as io from '@actions/io' +import * as fs from 'fs' +import * as path from 'path' +import * as semver from 'semver' +import * as util from 'util' +import {v4 as uuidV4} from 'uuid' +import { + CacheFilename, + CompressionMethod, + GnuTarPathOnWindows +} from './constants' + +// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 +export async function createTempDirectory(): Promise { + const IS_WINDOWS = process.platform === 'win32' + + let tempDirectory: string = process.env['RUNNER_TEMP'] || '' + + if (!tempDirectory) { + let baseLocation: string + if (IS_WINDOWS) { + // On Windows use the USERPROFILE env variable + baseLocation = process.env['USERPROFILE'] || 'C:\\' + } else { + if (process.platform === 'darwin') { + baseLocation = '/Users' + } else { + baseLocation = '/home' + } + } + tempDirectory = path.join(baseLocation, 'actions', 'temp') + } + + const dest = path.join(tempDirectory, uuidV4()) + await io.mkdirP(dest) + return dest +} + +export function getArchiveFileSizeInBytes(filePath: string): number { + return fs.statSync(filePath).size +} + +export async function resolvePaths(patterns: string[]): Promise { + const paths: string[] = [] + const workspace = process.env['GITHUB_WORKSPACE'] ?? process.cwd() + const globber = await glob.create(patterns.join('\n'), { + implicitDescendants: false + }) + + for await (const file of globber.globGenerator()) { + const relativeFile = path + .relative(workspace, file) + .replace(new RegExp(`\\${path.sep}`, 'g'), '/') + core.debug(`Matched: ${relativeFile}`) + // Paths are made relative so the tar entries are all relative to the root of the workspace. + if (relativeFile === '') { + // path.relative returns empty string if workspace and file are equal + paths.push('.') + } else { + paths.push(`${relativeFile}`) + } + } + + return paths +} + +export async function unlinkFile(filePath: fs.PathLike): Promise { + return util.promisify(fs.unlink)(filePath) +} + +async function getVersion( + app: string, + additionalArgs: string[] = [] +): Promise { + let versionOutput = '' + additionalArgs.push('--version') + core.debug(`Checking ${app} ${additionalArgs.join(' ')}`) + try { + await exec.exec(`${app}`, additionalArgs, { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data: Buffer): string => (versionOutput += data.toString()), + stderr: (data: Buffer): string => (versionOutput += data.toString()) + } + }) + } catch (err) { + core.debug((err as {message}).message) + } + + versionOutput = versionOutput.trim() + core.debug(versionOutput) + return versionOutput +} + +// Use zstandard if possible to maximize cache performance +export async function getCompressionMethod(): Promise { + const versionOutput = await getVersion('zstd', ['--quiet']) + const version = semver.clean(versionOutput) + core.debug(`zstd version: ${version}`) + + if (versionOutput === '') { + return CompressionMethod.Gzip + } else { + return CompressionMethod.ZstdWithoutLong + } +} + +export function getCacheFileName(compressionMethod: CompressionMethod): string { + return compressionMethod === CompressionMethod.Gzip + ? CacheFilename.Gzip + : CacheFilename.Zstd +} + +export async function getGnuTarPathOnWindows(): Promise { + if (fs.existsSync(GnuTarPathOnWindows)) { + return GnuTarPathOnWindows + } + const versionOutput = await getVersion('tar') + return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : '' +} + +export function assertDefined(name: string, value?: T): T { + if (value === undefined) { + throw Error(`Expected ${name} but value was undefiend`) + } + + return value +} + +export function isGhes(): boolean { + const ghUrl = new URL( + process.env['GITHUB_SERVER_URL'] || 'https://github.com' + ) + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' +} diff --git a/actions-toolkit/packages/cache/src/internal/constants.ts b/actions-toolkit/packages/cache/src/internal/constants.ts new file mode 100644 index 00000000..4dbff574 --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/constants.ts @@ -0,0 +1,38 @@ +export enum CacheFilename { + Gzip = 'cache.tgz', + Zstd = 'cache.tzst' +} + +export enum CompressionMethod { + Gzip = 'gzip', + // Long range mode was added to zstd in v1.3.2. + // This enum is for earlier version of zstd that does not have --long support + ZstdWithoutLong = 'zstd-without-long', + Zstd = 'zstd' +} + +export enum ArchiveToolType { + GNU = 'gnu', + BSD = 'bsd' +} + +// The default number of retry attempts. +export const DefaultRetryAttempts = 2 + +// The default delay in milliseconds between retry attempts. +export const DefaultRetryDelay = 5000 + +// Socket timeout in milliseconds during download. If no traffic is received +// over the socket during this period, the socket is destroyed and the download +// is aborted. +export const SocketTimeout = 5000 + +// The default path of GNUtar on hosted Windows runners +export const GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe` + +// The default path of BSDtar on hosted Windows runners +export const SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe` + +export const TarFilename = 'cache.tar' + +export const ManifestFilename = 'manifest.txt' diff --git a/actions-toolkit/packages/cache/src/internal/contracts.d.ts b/actions-toolkit/packages/cache/src/internal/contracts.d.ts new file mode 100644 index 00000000..6fcd9427 --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/contracts.d.ts @@ -0,0 +1,45 @@ +import {CompressionMethod} from './constants' +import {TypedResponse} from '@actions/http-client/lib/interfaces' +import {HttpClientError} from '@actions/http-client' + +export interface ITypedResponseWithError extends TypedResponse { + error?: HttpClientError +} + +export interface ArtifactCacheEntry { + cacheKey?: string + scope?: string + cacheVersion?: string + creationTime?: string + archiveLocation?: string +} + +export interface ArtifactCacheList { + totalCount: number + artifactCaches?: ArtifactCacheEntry[] +} + +export interface CommitCacheRequest { + size: number +} + +export interface ReserveCacheRequest { + key: string + version?: string + cacheSize?: number +} + +export interface ReserveCacheResponse { + cacheId: number +} + +export interface InternalCacheOptions { + compressionMethod?: CompressionMethod + enableCrossOsArchive?: boolean + cacheSize?: number +} + +export interface ArchiveTool { + path: string + type: string +} diff --git a/actions-toolkit/packages/cache/src/internal/downloadUtils.ts b/actions-toolkit/packages/cache/src/internal/downloadUtils.ts new file mode 100644 index 00000000..de57ed78 --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/downloadUtils.ts @@ -0,0 +1,463 @@ +import * as core from '@actions/core' +import {HttpClient, HttpClientResponse} from '@actions/http-client' +import {BlockBlobClient} from '@azure/storage-blob' +import {TransferProgressEvent} from '@azure/ms-rest-js' +import * as buffer from 'buffer' +import * as fs from 'fs' +import * as stream from 'stream' +import * as util from 'util' + +import * as utils from './cacheUtils' +import {SocketTimeout} from './constants' +import {DownloadOptions} from '../options' +import {retryHttpClientResponse} from './requestUtils' + +import {AbortController} from '@azure/abort-controller' + +/** + * Pipes the body of a HTTP response to a stream + * + * @param response the HTTP response + * @param output the writable stream + */ +async function pipeResponseToStream( + response: HttpClientResponse, + output: NodeJS.WritableStream +): Promise { + const pipeline = util.promisify(stream.pipeline) + await pipeline(response.message, output) +} + +/** + * Class for tracking the download state and displaying stats. + */ +export class DownloadProgress { + contentLength: number + segmentIndex: number + segmentSize: number + segmentOffset: number + receivedBytes: number + startTime: number + displayedComplete: boolean + timeoutHandle?: ReturnType + + constructor(contentLength: number) { + this.contentLength = contentLength + this.segmentIndex = 0 + this.segmentSize = 0 + this.segmentOffset = 0 + this.receivedBytes = 0 + this.displayedComplete = false + this.startTime = Date.now() + } + + /** + * Progress to the next segment. Only call this method when the previous segment + * is complete. + * + * @param segmentSize the length of the next segment + */ + nextSegment(segmentSize: number): void { + this.segmentOffset = this.segmentOffset + this.segmentSize + this.segmentIndex = this.segmentIndex + 1 + this.segmentSize = segmentSize + this.receivedBytes = 0 + + core.debug( + `Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...` + ) + } + + /** + * Sets the number of bytes received for the current segment. + * + * @param receivedBytes the number of bytes received + */ + setReceivedBytes(receivedBytes: number): void { + this.receivedBytes = receivedBytes + } + + /** + * Returns the total number of bytes transferred. + */ + getTransferredBytes(): number { + return this.segmentOffset + this.receivedBytes + } + + /** + * Returns true if the download is complete. + */ + isDone(): boolean { + return this.getTransferredBytes() === this.contentLength + } + + /** + * Prints the current download stats. Once the download completes, this will print one + * last line and then stop. + */ + display(): void { + if (this.displayedComplete) { + return + } + + const transferredBytes = this.segmentOffset + this.receivedBytes + const percentage = (100 * (transferredBytes / this.contentLength)).toFixed( + 1 + ) + const elapsedTime = Date.now() - this.startTime + const downloadSpeed = ( + transferredBytes / + (1024 * 1024) / + (elapsedTime / 1000) + ).toFixed(1) + + core.info( + `Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec` + ) + + if (this.isDone()) { + this.displayedComplete = true + } + } + + /** + * Returns a function used to handle TransferProgressEvents. + */ + onProgress(): (progress: TransferProgressEvent) => void { + return (progress: TransferProgressEvent) => { + this.setReceivedBytes(progress.loadedBytes) + } + } + + /** + * Starts the timer that displays the stats. + * + * @param delayInMs the delay between each write + */ + startDisplayTimer(delayInMs = 1000): void { + const displayCallback = (): void => { + this.display() + + if (!this.isDone()) { + this.timeoutHandle = setTimeout(displayCallback, delayInMs) + } + } + + this.timeoutHandle = setTimeout(displayCallback, delayInMs) + } + + /** + * Stops the timer that displays the stats. As this typically indicates the download + * is complete, this will display one last line, unless the last line has already + * been written. + */ + stopDisplayTimer(): void { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle) + this.timeoutHandle = undefined + } + + this.display() + } +} + +/** + * Download the cache using the Actions toolkit http-client + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +export async function downloadCacheHttpClient( + archiveLocation: string, + archivePath: string +): Promise { + const writeStream = fs.createWriteStream(archivePath) + const httpClient = new HttpClient('actions/cache') + const downloadResponse = await retryHttpClientResponse( + 'downloadCache', + async () => httpClient.get(archiveLocation) + ) + + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(SocketTimeout, () => { + downloadResponse.message.destroy() + core.debug(`Aborting download, socket timed out after ${SocketTimeout} ms`) + }) + + await pipeResponseToStream(downloadResponse, writeStream) + + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length'] + + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader) + const actualLength = utils.getArchiveFileSizeInBytes(archivePath) + + if (actualLength !== expectedLength) { + throw new Error( + `Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}` + ) + } + } else { + core.debug('Unable to validate download, no Content-Length header') + } +} + +/** + * Download the cache using the Actions toolkit http-client concurrently + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +export async function downloadCacheHttpClientConcurrent( + archiveLocation: string, + archivePath: fs.PathLike, + options: DownloadOptions +): Promise { + const archiveDescriptor = await fs.promises.open(archivePath, 'w') + const httpClient = new HttpClient('actions/cache', undefined, { + socketTimeout: options.timeoutInMs, + keepAlive: true + }) + try { + const res = await retryHttpClientResponse( + 'downloadCacheMetadata', + async () => await httpClient.request('HEAD', archiveLocation, null, {}) + ) + + const lengthHeader = res.message.headers['content-length'] + if (lengthHeader === undefined || lengthHeader === null) { + throw new Error('Content-Length not found on blob response') + } + + const length = parseInt(lengthHeader) + if (Number.isNaN(length)) { + throw new Error(`Could not interpret Content-Length: ${length}`) + } + + const downloads: { + offset: number + promiseGetter: () => Promise + }[] = [] + const blockSize = 4 * 1024 * 1024 + + for (let offset = 0; offset < length; offset += blockSize) { + const count = Math.min(blockSize, length - offset) + downloads.push({ + offset, + promiseGetter: async () => { + return await downloadSegmentRetry( + httpClient, + archiveLocation, + offset, + count + ) + } + }) + } + + // reverse to use .pop instead of .shift + downloads.reverse() + let actives = 0 + let bytesDownloaded = 0 + const progress = new DownloadProgress(length) + progress.startDisplayTimer() + const progressFn = progress.onProgress() + + const activeDownloads: {[offset: number]: Promise} = [] + let nextDownload: + | {offset: number; promiseGetter: () => Promise} + | undefined + + const waitAndWrite: () => Promise = async () => { + const segment = await Promise.race(Object.values(activeDownloads)) + await archiveDescriptor.write( + segment.buffer, + 0, + segment.count, + segment.offset + ) + actives-- + delete activeDownloads[segment.offset] + bytesDownloaded += segment.count + progressFn({loadedBytes: bytesDownloaded}) + } + + while ((nextDownload = downloads.pop())) { + activeDownloads[nextDownload.offset] = nextDownload.promiseGetter() + actives++ + + if (actives >= (options.downloadConcurrency ?? 10)) { + await waitAndWrite() + } + } + + while (actives > 0) { + await waitAndWrite() + } + } finally { + httpClient.dispose() + await archiveDescriptor.close() + } +} + +async function downloadSegmentRetry( + httpClient: HttpClient, + archiveLocation: string, + offset: number, + count: number +): Promise { + const retries = 5 + let failures = 0 + + while (true) { + try { + const timeout = 30000 + const result = await promiseWithTimeout( + timeout, + downloadSegment(httpClient, archiveLocation, offset, count) + ) + if (typeof result === 'string') { + throw new Error('downloadSegmentRetry failed due to timeout') + } + + return result + } catch (err) { + if (failures >= retries) { + throw err + } + + failures++ + } + } +} + +async function downloadSegment( + httpClient: HttpClient, + archiveLocation: string, + offset: number, + count: number +): Promise { + const partRes = await retryHttpClientResponse( + 'downloadCachePart', + async () => + await httpClient.get(archiveLocation, { + Range: `bytes=${offset}-${offset + count - 1}` + }) + ) + + if (!partRes.readBodyBuffer) { + throw new Error('Expected HttpClientResponse to implement readBodyBuffer') + } + + return { + offset, + count, + buffer: await partRes.readBodyBuffer() + } +} + +declare class DownloadSegment { + offset: number + count: number + buffer: Buffer +} + +/** + * Download the cache using the Azure Storage SDK. Only call this method if the + * URL points to an Azure Storage endpoint. + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + * @param options the download options with the defaults set + */ +export async function downloadCacheStorageSDK( + archiveLocation: string, + archivePath: string, + options: DownloadOptions +): Promise { + const client = new BlockBlobClient(archiveLocation, undefined, { + retryOptions: { + // Override the timeout used when downloading each 4 MB chunk + // The default is 2 min / MB, which is way too slow + tryTimeoutInMs: options.timeoutInMs + } + }) + + const properties = await client.getProperties() + const contentLength = properties.contentLength ?? -1 + + if (contentLength < 0) { + // We should never hit this condition, but just in case fall back to downloading the + // file as one large stream + core.debug( + 'Unable to determine content length, downloading file with http-client...' + ) + + await downloadCacheHttpClient(archiveLocation, archivePath) + } else { + // Use downloadToBuffer for faster downloads, since internally it splits the + // file into 4 MB chunks which can then be parallelized and retried independently + // + // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB + // on 64-bit systems), split the download into multiple segments + // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. + + // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast + const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH) + const downloadProgress = new DownloadProgress(contentLength) + + const fd = fs.openSync(archivePath, 'w') + + try { + downloadProgress.startDisplayTimer() + const controller = new AbortController() + const abortSignal = controller.signal + while (!downloadProgress.isDone()) { + const segmentStart = + downloadProgress.segmentOffset + downloadProgress.segmentSize + + const segmentSize = Math.min( + maxSegmentSize, + contentLength - segmentStart + ) + + downloadProgress.nextSegment(segmentSize) + const result = await promiseWithTimeout( + options.segmentTimeoutInMs || 3600000, + client.downloadToBuffer(segmentStart, segmentSize, { + abortSignal, + concurrency: options.downloadConcurrency, + onProgress: downloadProgress.onProgress() + }) + ) + if (result === 'timeout') { + controller.abort() + throw new Error( + 'Aborting cache download as the download time exceeded the timeout.' + ) + } else if (Buffer.isBuffer(result)) { + fs.writeFileSync(fd, result) + } + } + } finally { + downloadProgress.stopDisplayTimer() + fs.closeSync(fd) + } + } +} + +const promiseWithTimeout = async ( + timeoutMs: number, + promise: Promise +): Promise => { + let timeoutHandle: NodeJS.Timeout + const timeoutPromise = new Promise(resolve => { + timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs) + }) + + return Promise.race([promise, timeoutPromise]).then(result => { + clearTimeout(timeoutHandle) + return result + }) +} diff --git a/actions-toolkit/packages/cache/src/internal/requestUtils.ts b/actions-toolkit/packages/cache/src/internal/requestUtils.ts new file mode 100644 index 00000000..48c3acff --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/requestUtils.ts @@ -0,0 +1,138 @@ +import * as core from '@actions/core' +import { + HttpCodes, + HttpClientError, + HttpClientResponse +} from '@actions/http-client' +import {DefaultRetryDelay, DefaultRetryAttempts} from './constants' +import {ITypedResponseWithError} from './contracts' + +export function isSuccessStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + return statusCode >= 200 && statusCode < 300 +} + +export function isServerErrorStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return true + } + return statusCode >= 500 +} + +export function isRetryableStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + const retryableStatusCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout + ] + return retryableStatusCodes.includes(statusCode) +} + +async function sleep(milliseconds: number): Promise { + return new Promise(resolve => setTimeout(resolve, milliseconds)) +} + +export async function retry( + name: string, + method: () => Promise, + getStatusCode: (arg0: T) => number | undefined, + maxAttempts = DefaultRetryAttempts, + delay = DefaultRetryDelay, + onError: ((arg0: Error) => T | undefined) | undefined = undefined +): Promise { + let errorMessage = '' + let attempt = 1 + + while (attempt <= maxAttempts) { + let response: T | undefined = undefined + let statusCode: number | undefined = undefined + let isRetryable = false + + try { + response = await method() + } catch (error) { + if (onError) { + response = onError(error as Error) + } + + isRetryable = true + errorMessage = (error as ({message: string})).message + } + + if (response) { + statusCode = getStatusCode(response) + + if (!isServerErrorStatusCode(statusCode)) { + return response + } + } + + if (statusCode) { + isRetryable = isRetryableStatusCode(statusCode) + errorMessage = `Cache service responded with ${statusCode}` + } + + core.debug( + `${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}` + ) + + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`) + break + } + + await sleep(delay) + attempt++ + } + + throw Error(`${name} failed: ${errorMessage}`) +} + +export async function retryTypedResponse( + name: string, + method: () => Promise>, + maxAttempts = DefaultRetryAttempts, + delay = DefaultRetryDelay +): Promise> { + return await retry( + name, + method, + (response: ITypedResponseWithError) => response.statusCode, + maxAttempts, + delay, + // If the error object contains the statusCode property, extract it and return + // an TypedResponse so it can be processed by the retry logic. + (error: Error) => { + if (error instanceof HttpClientError) { + return { + statusCode: error.statusCode, + result: null, + headers: {}, + error + } + } else { + return undefined + } + } + ) +} + +export async function retryHttpClientResponse( + name: string, + method: () => Promise, + maxAttempts = DefaultRetryAttempts, + delay = DefaultRetryDelay +): Promise { + return await retry( + name, + method, + (response: HttpClientResponse) => response.message.statusCode, + maxAttempts, + delay + ) +} diff --git a/actions-toolkit/packages/cache/src/internal/tar.ts b/actions-toolkit/packages/cache/src/internal/tar.ts new file mode 100644 index 00000000..e7df0b70 --- /dev/null +++ b/actions-toolkit/packages/cache/src/internal/tar.ts @@ -0,0 +1,301 @@ +import {exec} from '@actions/exec' +import * as io from '@actions/io' +import {existsSync, writeFileSync} from 'fs' +import * as path from 'path' +import * as utils from './cacheUtils' +import {ArchiveTool} from './contracts' +import { + CompressionMethod, + SystemTarPathOnWindows, + ArchiveToolType, + TarFilename, + ManifestFilename +} from './constants' +import { ExtraTarArgs } from '../options' + +const IS_WINDOWS = process.platform === 'win32' + +// Returns tar path and type: BSD or GNU +async function getTarPath(): Promise { + switch (process.platform) { + case 'win32': { + const gnuTar = await utils.getGnuTarPathOnWindows() + const systemTar = SystemTarPathOnWindows + if (gnuTar) { + // Use GNUtar as default on windows + return {path: gnuTar, type: ArchiveToolType.GNU} + } else if (existsSync(systemTar)) { + return {path: systemTar, type: ArchiveToolType.BSD} + } + break + } + case 'darwin': { + const gnuTar = await io.which('gtar', false) + if (gnuTar) { + // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 + return {path: gnuTar, type: ArchiveToolType.GNU} + } else { + return { + path: await io.which('tar', true), + type: ArchiveToolType.BSD + } + } + } + default: + break + } + // Default assumption is GNU tar is present in path + return { + path: await io.which('tar', true), + type: ArchiveToolType.GNU + } +} + +// Return arguments for tar as per tarPath, compressionMethod, method type and os +async function getTarArgs( + tarPath: ArchiveTool, + compressionMethod: CompressionMethod, + type: string, + archivePath = '' +): Promise { + const args = [`"${tarPath.path}"`] + const cacheFileName = utils.getCacheFileName(compressionMethod) + const tarFile = 'cache.tar' + const workingDirectory = getWorkingDirectory() + // Speficic args for BSD tar on windows for workaround + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS + + // Method specific args + switch (type) { + case 'create': + args.push( + '--posix', + '-cf', + BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '--exclude', + BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '--files-from', + ManifestFilename + ) + break + case 'extract': + args.push( + '-xf', + BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ) + break + case 'list': + args.push( + '-tf', + BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P' + ) + break + } + + // Platform specific args + if (tarPath.type === ArchiveToolType.GNU) { + switch (process.platform) { + case 'win32': + args.push('--force-local') + break + case 'darwin': + args.push('--delay-directory-restore') + break + } + } + + return args +} + +// Returns commands to run tar and compression program +async function getCommands( + compressionMethod: CompressionMethod, + type: string, + archivePath = '', + tarExtraArgs: string[] = [] +): Promise { + let args + + const tarPath = await getTarPath() + const tarArgs = await getTarArgs( + tarPath, + compressionMethod, + type, + archivePath + ) + tarArgs.push(...tarExtraArgs) + const compressionArgs = + type !== 'create' + ? await getDecompressionProgram(tarPath, compressionMethod, archivePath) + : await getCompressionProgram(tarPath, compressionMethod) + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS + + if (BSD_TAR_ZSTD && type !== 'create') { + args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')] + } else { + args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')] + } + + if (BSD_TAR_ZSTD) { + return args + } + + return [args.join(' ')] +} + +function getWorkingDirectory(): string { + return process.env['GITHUB_WORKSPACE'] ?? process.cwd() +} + +// Common function for extractTar and listTar to get the compression method +async function getDecompressionProgram( + tarPath: ArchiveTool, + compressionMethod: CompressionMethod, + archivePath: string +): Promise { + // -d: Decompress. + // unzstd is equivalent to 'zstd -d' + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS + switch (compressionMethod) { + case CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --long=30 --force -o', + TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ] + case CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --force -o', + TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd'] + default: + return ['-z'] + } +} + +// Used for creating the archive +// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. +// zstdmt is equivalent to 'zstd -T0' +// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. +// Using 30 here because we also support 32-bit self-hosted runners. +// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. +async function getCompressionProgram( + tarPath: ArchiveTool, + compressionMethod: CompressionMethod +): Promise { + const cacheFileName = utils.getCacheFileName(compressionMethod) + const BSD_TAR_ZSTD = + tarPath.type === ArchiveToolType.BSD && + compressionMethod !== CompressionMethod.Gzip && + IS_WINDOWS + switch (compressionMethod) { + case CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --long=30 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + TarFilename + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' + ] + case CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + TarFilename + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt'] + default: + return ['-z'] + } +} + +// Executes all commands as separate processes +async function execCommands(commands: string[], cwd?: string): Promise { + for (const command of commands) { + try { + await exec(command, undefined, { + cwd, + env: {...(process.env as object), MSYS: 'winsymlinks:nativestrict'} + }) + } catch (error) { + throw new Error( + `${command.split(' ')[0]} failed with error: ${(error as {message?: string})?.message}` + ) + } + } +} + +// List the contents of a tar +export async function listTar( + archivePath: string, + compressionMethod: CompressionMethod +): Promise { + const commands = await getCommands(compressionMethod, 'list', archivePath) + await execCommands(commands) +} + +// Extract a tar +export async function extractTar( + archivePath: string, + compressionMethod: CompressionMethod, + extraTarArgs: ExtraTarArgs = [] +): Promise { + // Create directory to extract tar into + const workingDirectory = getWorkingDirectory() + await io.mkdirP(workingDirectory) + const commands = await getCommands(compressionMethod, 'extract', archivePath, extraTarArgs) + await execCommands(commands) +} + +// Create a tar +export async function createTar( + archiveFolder: string, + sourceDirectories: string[], + compressionMethod: CompressionMethod +): Promise { + // Write source directories to manifest.txt to avoid command length limits + writeFileSync( + path.join(archiveFolder, ManifestFilename), + sourceDirectories.join('\n') + ) + const commands = await getCommands(compressionMethod, 'create') + await execCommands(commands, archiveFolder) +} diff --git a/actions-toolkit/packages/cache/src/options.ts b/actions-toolkit/packages/cache/src/options.ts new file mode 100644 index 00000000..3eddab99 --- /dev/null +++ b/actions-toolkit/packages/cache/src/options.ts @@ -0,0 +1,166 @@ +import * as core from '@actions/core' + +/** + * Options to control cache upload + */ +export interface UploadOptions { + /** + * Number of parallel cache upload + * + * @default 4 + */ + uploadConcurrency?: number + /** + * Maximum chunk size in bytes for cache upload + * + * @default 32MB + */ + uploadChunkSize?: number +} + +/** + * Options to control cache download + */ +export interface DownloadOptions { + /** + * Indicates whether to use the Azure Blob SDK to download caches + * that are stored on Azure Blob Storage to improve reliability and + * performance + * + * @default true + */ + useAzureSdk?: boolean + + /** + * Number of parallel downloads (this option only applies when using + * the Azure SDK) + * + * @default 8 + */ + downloadConcurrency?: number + + /** + * Indicates whether to use Actions HttpClient with concurrency + * for Azure Blob Storage + */ + concurrentBlobDownloads?: boolean + + /** + * Maximum time for each download request, in milliseconds (this + * option only applies when using the Azure SDK) + * + * @default 30000 + */ + timeoutInMs?: number + + /** + * Time after which a segment download should be aborted if stuck + * + * @default 3600000 + */ + segmentTimeoutInMs?: number + + /** + * Weather to skip downloading the cache entry. + * If lookupOnly is set to true, the restore function will only check if + * a matching cache entry exists and return the cache key if it does. + * + * @default false + */ + lookupOnly?: boolean +} + +/** + * Options to control tar extraction + */ + +export type ExtraTarArgs = string[] + +/** + * Returns a copy of the upload options with defaults filled in. + * + * @param copy the original upload options + */ +export function getUploadOptions(copy?: UploadOptions): UploadOptions { + const result: UploadOptions = { + uploadConcurrency: 4, + uploadChunkSize: 32 * 1024 * 1024 + } + + if (copy) { + if (typeof copy.uploadConcurrency === 'number') { + result.uploadConcurrency = copy.uploadConcurrency + } + + if (typeof copy.uploadChunkSize === 'number') { + result.uploadChunkSize = copy.uploadChunkSize + } + } + + core.debug(`Upload concurrency: ${result.uploadConcurrency}`) + core.debug(`Upload chunk size: ${result.uploadChunkSize}`) + + return result +} + +/** + * Returns a copy of the download options with defaults filled in. + * + * @param copy the original download options + */ +export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions { + const result: DownloadOptions = { + useAzureSdk: false, + concurrentBlobDownloads: true, + downloadConcurrency: 8, + timeoutInMs: 30000, + segmentTimeoutInMs: 600000, + lookupOnly: false + } + + if (copy) { + if (typeof copy.useAzureSdk === 'boolean') { + result.useAzureSdk = copy.useAzureSdk + } + + if (typeof copy.concurrentBlobDownloads === 'boolean') { + result.concurrentBlobDownloads = copy.concurrentBlobDownloads + } + + if (typeof copy.downloadConcurrency === 'number') { + result.downloadConcurrency = copy.downloadConcurrency + } + + if (typeof copy.timeoutInMs === 'number') { + result.timeoutInMs = copy.timeoutInMs + } + + if (typeof copy.segmentTimeoutInMs === 'number') { + result.segmentTimeoutInMs = copy.segmentTimeoutInMs + } + + if (typeof copy.lookupOnly === 'boolean') { + result.lookupOnly = copy.lookupOnly + } + } + const segmentDownloadTimeoutMins = + process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS'] + + if ( + segmentDownloadTimeoutMins && + !isNaN(Number(segmentDownloadTimeoutMins)) && + isFinite(Number(segmentDownloadTimeoutMins)) + ) { + result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000 + } + core.debug(`Use Azure SDK: ${result.useAzureSdk}`) + core.debug(`Download concurrency: ${result.downloadConcurrency}`) + core.debug(`Request timeout (ms): ${result.timeoutInMs}`) + core.debug( + `Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}` + ) + core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`) + core.debug(`Lookup only: ${result.lookupOnly}`) + + return result +} diff --git a/actions-toolkit/packages/cache/tsconfig.json b/actions-toolkit/packages/cache/tsconfig.json new file mode 100644 index 00000000..f4a0548a --- /dev/null +++ b/actions-toolkit/packages/cache/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src", + "lib": [ + "es6", + "dom" + ], + "useUnknownInCatchVariables": false + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/core/LICENSE.md b/actions-toolkit/packages/core/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/core/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/core/README.md b/actions-toolkit/packages/core/README.md new file mode 100644 index 00000000..35bd72db --- /dev/null +++ b/actions-toolkit/packages/core/README.md @@ -0,0 +1,360 @@ +# `@actions/core` + +> Core functions for setting results, logging, registering secrets and exporting variables across actions + +## Usage + +### Import the package + +```js +// javascript +const core = require('@actions/core'); + +// typescript +import * as core from '@actions/core'; +``` + +#### Inputs/Outputs + +Action inputs can be read with `getInput` which returns a `string` or `getBooleanInput` which parses a boolean based on the [yaml 1.2 specification](https://yaml.org/spec/1.2/spec.html#id2804923). If `required` set to be false, the input should have a default value in `action.yml`. + +Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled. + +```js +const myInput = core.getInput('inputName', { required: true }); +const myBooleanInput = core.getBooleanInput('booleanInputName', { required: true }); +const myMultilineInput = core.getMultilineInput('multilineInputName', { required: true }); +core.setOutput('outputKey', 'outputVal'); +``` + +#### Exporting variables + +Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks. + +```js +core.exportVariable('envVar', 'Val'); +``` + +#### Setting a secret + +Setting a secret registers the secret with the runner to ensure it is masked in logs. + +```js +core.setSecret('myPassword'); +``` + +#### PATH Manipulation + +To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH. + +```js +core.addPath('/path/to/mytool'); +``` + +#### Exit codes + +You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success. + +```js +const core = require('@actions/core'); + +try { + // Do stuff +} +catch (err) { + // setFailed logs the message and sets a failing exit code + core.setFailed(`Action failed with error ${err}`); +} +``` + +Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned. + +#### Logging + +Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs). + +```js +const core = require('@actions/core'); + +const myInput = core.getInput('input'); +try { + core.debug('Inside try block'); + + if (!myInput) { + core.warning('myInput was not set'); + } + + if (core.isDebug()) { + // curl -v https://github.com + } else { + // curl https://github.com + } + + // Do stuff + core.info('Output to the actions build log') + + core.notice('This is a message that will also emit an annotation') +} +catch (err) { + core.error(`Error ${err}, action may still succeed though`); +} +``` + +This library can also wrap chunks of output in foldable groups. + +```js +const core = require('@actions/core') + +// Manually wrap output +core.startGroup('Do some function') +doSomeFunction() +core.endGroup() + +// Wrap an asynchronous function call +const result = await core.group('Do something async', async () => { + const response = await doSomeHTTPRequest() + return response +}) +``` + +#### Annotations + +This library has 3 methods that will produce [annotations](https://docs.github.com/en/rest/reference/checks#create-a-check-run). +```js +core.error('This is a bad error, action may still succeed though.') + +core.warning('Something went wrong, but it\'s not bad enough to fail the build.') + +core.notice('Something happened that you might want to know about.') +``` + +These will surface to the UI in the Actions page and on Pull Requests. They look something like this: + +![Annotations Image](../../docs/assets/annotations.png) + +These annotations can also be attached to particular lines and columns of your source files to show exactly where a problem is occuring. + +These options are: +```typescript +export interface AnnotationProperties { + /** + * A title for the annotation. + */ + title?: string + + /** + * The name of the file for which the annotation should be created. + */ + file?: string + + /** + * The start line for the annotation. + */ + startLine?: number + + /** + * The end line for the annotation. Defaults to `startLine` when `startLine` is provided. + */ + endLine?: number + + /** + * The start column for the annotation. Cannot be sent when `startLine` and `endLine` are different values. + */ + startColumn?: number + + /** + * The end column for the annotation. Cannot be sent when `startLine` and `endLine` are different values. + * Defaults to `startColumn` when `startColumn` is provided. + */ + endColumn?: number +} +``` + +#### Styling output + +Colored output is supported in the Action logs via standard [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 3/4 bit, 8 bit and 24 bit colors are all supported. + +Foreground colors: + +```js +// 3/4 bit +core.info('\u001b[35mThis foreground will be magenta') + +// 8 bit +core.info('\u001b[38;5;6mThis foreground will be cyan') + +// 24 bit +core.info('\u001b[38;2;255;0;0mThis foreground will be bright red') +``` + +Background colors: + +```js +// 3/4 bit +core.info('\u001b[43mThis background will be yellow'); + +// 8 bit +core.info('\u001b[48;5;6mThis background will be cyan') + +// 24 bit +core.info('\u001b[48;2;255;0;0mThis background will be bright red') +``` + +Special styles: + +```js +core.info('\u001b[1mBold text') +core.info('\u001b[3mItalic text') +core.info('\u001b[4mUnderlined text') +``` + +ANSI escape codes can be combined with one another: + +```js +core.info('\u001b[31;46mRed foreground with a cyan background and \u001b[1mbold text at the end'); +``` + +> Note: Escape codes reset at the start of each line + +```js +core.info('\u001b[35mThis foreground will be magenta') +core.info('This foreground will reset to the default') +``` + +Manually typing escape codes can be a little difficult, but you can use third party modules such as [ansi-styles](https://github.com/chalk/ansi-styles). + +```js +const style = require('ansi-styles'); +core.info(style.color.ansi16m.hex('#abcdef') + 'Hello world!') +``` + +#### Action state + +You can use this library to save state and get state for sharing information between a given wrapper action: + +**action.yml**: + +```yaml +name: 'Wrapper action sample' +inputs: + name: + default: 'GitHub' +runs: + using: 'node12' + main: 'main.js' + post: 'cleanup.js' +``` + +In action's `main.js`: + +```js +const core = require('@actions/core'); + +core.saveState("pidToKill", 12345); +``` + +In action's `cleanup.js`: + +```js +const core = require('@actions/core'); + +var pid = core.getState("pidToKill"); + +process.kill(pid); +``` + +#### OIDC Token + +You can use these methods to interact with the GitHub OIDC provider and get a JWT ID token which would help to get access token from third party cloud providers. + +**Method Name**: getIDToken() + +**Inputs** + +audience : optional + +**Outputs** + +A [JWT](https://jwt.io/) ID Token + +In action's `main.ts`: +```js +const core = require('@actions/core'); +async function getIDTokenAction(): Promise { + + const audience = core.getInput('audience', {required: false}) + + const id_token1 = await core.getIDToken() // ID Token with default audience + const id_token2 = await core.getIDToken(audience) // ID token with custom audience + + // this id_token can be used to get access token from third party cloud providers +} +getIDTokenAction() +``` + +In action's `actions.yml`: + +```yaml +name: 'GetIDToken' +description: 'Get ID token from Github OIDC provider' +inputs: + audience: + description: 'Audience for which the ID token is intended for' + required: false +outputs: + id_token1: + description: 'ID token obtained from OIDC provider' + id_token2: + description: 'ID token obtained from OIDC provider' +runs: + using: 'node12' + main: 'dist/index.js' +``` + +#### Filesystem path helpers + +You can use these methods to manipulate file paths across operating systems. + +The `toPosixPath` function converts input paths to Posix-style (Linux) paths. +The `toWin32Path` function converts input paths to Windows-style paths. These +functions work independently of the underlying runner operating system. + +```js +toPosixPath('\\foo\\bar') // => /foo/bar +toWin32Path('/foo/bar') // => \foo\bar +``` + +The `toPlatformPath` function converts input paths to the expected value on the runner's operating system. + +```js +// On a Windows runner. +toPlatformPath('/foo/bar') // => \foo\bar + +// On a Linux runner. +toPlatformPath('\\foo\\bar') // => /foo/bar +``` + +#### Platform helper + +Provides shorthands for getting information about platform action is running on. + +```js +import { platform } from '@actions/core' + +/* equals to a call of os.platform() */ +platform.platform // 'win32' | 'darwin' | 'linux' | 'freebsd' | 'openbsd' | 'android' | 'cygwin' | 'sunos' + +/* equals to a call of os.arch() */ +platform.arch // 'x64' | 'arm' | 'arm64' | 'ia32' | 'mips' | 'mipsel' | 'ppc' | 'ppc64' | 'riscv64' | 's390' | 's390x' + +/* common shorthands for platform-specific logic */ +platform.isWindows // true +platform.isMacOS // false +platform.isLinux // false + +/* run platform-specific script to get more details about the exact platform, works on Windows, MacOS and Linux */ +const { + name, // Microsoft Windows 11 Enterprise + version, // 10.0.22621 +} = await platform.getDetails() +``` diff --git a/actions-toolkit/packages/core/RELEASES.md b/actions-toolkit/packages/core/RELEASES.md new file mode 100644 index 00000000..14039b56 --- /dev/null +++ b/actions-toolkit/packages/core/RELEASES.md @@ -0,0 +1,90 @@ +# @actions/core Releases + +### 1.10.1 +- Fix error message reference in oidc utils [#1511](https://github.com/actions/toolkit/pull/1511) + +### 1.10.0 +- `saveState` and `setOutput` now use environment files if available [#1178](https://github.com/actions/toolkit/pull/1178) +- `getMultilineInput` now correctly trims whitespace by default [#1185](https://github.com/actions/toolkit/pull/1185) + +### 1.9.1 +- Randomize delimiter when calling `core.exportVariable` + +### 1.9.0 +- Added `toPosixPath`, `toWin32Path` and `toPlatformPath` utilities [#1102](https://github.com/actions/toolkit/pull/1102) + +### 1.8.2 +- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087) + +### 1.8.1 +- Update to v2.0.0 of `@actions/http-client` + +### 1.8.0 +- Deprecate `markdownSummary` extension export in favor of `summary` + - https://github.com/actions/toolkit/pull/1072 + - https://github.com/actions/toolkit/pull/1073 + +### 1.7.0 +- [Added `markdownSummary` extension](https://github.com/actions/toolkit/pull/1014) + +### 1.6.0 +- [Added OIDC Client function `getIDToken`](https://github.com/actions/toolkit/pull/919) +- [Added `file` parameter to `AnnotationProperties`](https://github.com/actions/toolkit/pull/896) + +### 1.5.0 +- [Added support for notice annotations and more annotation fields](https://github.com/actions/toolkit/pull/855) + +### 1.4.0 +- [Added the `getMultilineInput` function](https://github.com/actions/toolkit/pull/829) + +### 1.3.0 +- [Added the trimWhitespace option to getInput](https://github.com/actions/toolkit/pull/802) +- [Added the getBooleanInput function](https://github.com/actions/toolkit/pull/725) + +### 1.2.7 +- [Prepend newline for set-output](https://github.com/actions/toolkit/pull/772) + +### 1.2.6 +- [Update `exportVariable` and `addPath` to use environment files](https://github.com/actions/toolkit/pull/571) + +### 1.2.5 +- [Correctly bundle License File with package](https://github.com/actions/toolkit/pull/548) + +### 1.2.4 +- [Be more lenient in accepting non-string command inputs](https://github.com/actions/toolkit/pull/405) +- [Add Echo commands](https://github.com/actions/toolkit/pull/411) + +### 1.2.3 + +- [IsDebug logging](README.md#logging) + +### 1.2.2 + +- [Fix escaping for runner commands](https://github.com/actions/toolkit/pull/302) + +### 1.2.1 + +- [Remove trailing comma from commands](https://github.com/actions/toolkit/pull/263) +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + +### 1.2.0 + +- saveState and getState functions for wrapper tasks (on finally entry points that run post job) + +### 1.1.3 + +- setSecret added to register a secret with the runner to be masked from the logs +- exportSecret which was not implemented and never worked was removed after clarification from product. + +### 1.1.1 + +- Add support for action input variables with multiple spaces [#127](https://github.com/actions/toolkit/issues/127) +- Switched ## commands to :: commands (should have no noticeable impact) [#110)(https://github.com/actions/toolkit/pull/110) + +### 1.1.0 + +- Added helpers for `group` and `endgroup` [#98](https://github.com/actions/toolkit/pull/98) + +### 1.0.0 + +- Initial release diff --git a/actions-toolkit/packages/core/__tests__/command.test.ts b/actions-toolkit/packages/core/__tests__/command.test.ts new file mode 100644 index 00000000..b650a61b --- /dev/null +++ b/actions-toolkit/packages/core/__tests__/command.test.ts @@ -0,0 +1,138 @@ +import * as command from '../src/command' +import * as os from 'os' + +/* eslint-disable @typescript-eslint/unbound-method */ + +let originalWriteFunction: (str: string) => boolean + +describe('@actions/core/src/command', () => { + beforeAll(() => { + originalWriteFunction = process.stdout.write + }) + + beforeEach(() => { + process.stdout.write = jest.fn() + }) + + afterEach(() => {}) + + afterAll(() => { + process.stdout.write = originalWriteFunction as unknown as ( + str: string + ) => boolean + }) + + it('command only', () => { + command.issueCommand('some-command', {}, '') + assertWriteCalls([`::some-command::${os.EOL}`]) + }) + + it('command escapes message', () => { + // Verify replaces each instance, not just first instance + command.issueCommand( + 'some-command', + {}, + 'percent % percent % cr \r cr \r lf \n lf \n' + ) + assertWriteCalls([ + `::some-command::percent %25 percent %25 cr %0D cr %0D lf %0A lf %0A${os.EOL}` + ]) + + // Verify literal escape sequences + process.stdout.write = jest.fn() + command.issueCommand('some-command', {}, '%25 %25 %0D %0D %0A %0A') + assertWriteCalls([ + `::some-command::%2525 %2525 %250D %250D %250A %250A${os.EOL}` + ]) + }) + + it('command escapes property', () => { + // Verify replaces each instance, not just first instance + command.issueCommand( + 'some-command', + { + name: 'percent % percent % cr \r cr \r lf \n lf \n colon : colon : comma , comma ,' + }, + '' + ) + assertWriteCalls([ + `::some-command name=percent %25 percent %25 cr %0D cr %0D lf %0A lf %0A colon %3A colon %3A comma %2C comma %2C::${os.EOL}` + ]) + + // Verify literal escape sequences + process.stdout.write = jest.fn() + command.issueCommand( + 'some-command', + {}, + '%25 %25 %0D %0D %0A %0A %3A %3A %2C %2C' + ) + assertWriteCalls([ + `::some-command::%2525 %2525 %250D %250D %250A %250A %253A %253A %252C %252C${os.EOL}` + ]) + }) + + it('command with message', () => { + command.issueCommand('some-command', {}, 'some message') + assertWriteCalls([`::some-command::some message${os.EOL}`]) + }) + + it('command with message and properties', () => { + command.issueCommand( + 'some-command', + {prop1: 'value 1', prop2: 'value 2'}, + 'some message' + ) + assertWriteCalls([ + `::some-command prop1=value 1,prop2=value 2::some message${os.EOL}` + ]) + }) + + it('command with one property', () => { + command.issueCommand('some-command', {prop1: 'value 1'}, '') + assertWriteCalls([`::some-command prop1=value 1::${os.EOL}`]) + }) + + it('command with two properties', () => { + command.issueCommand( + 'some-command', + {prop1: 'value 1', prop2: 'value 2'}, + '' + ) + assertWriteCalls([`::some-command prop1=value 1,prop2=value 2::${os.EOL}`]) + }) + + it('command with three properties', () => { + command.issueCommand( + 'some-command', + {prop1: 'value 1', prop2: 'value 2', prop3: 'value 3'}, + '' + ) + assertWriteCalls([ + `::some-command prop1=value 1,prop2=value 2,prop3=value 3::${os.EOL}` + ]) + }) + + it('should handle issuing commands for non-string objects', () => { + command.issueCommand( + 'some-command', + { + prop1: {test: 'object'} as unknown as string, + prop2: 123 as unknown as string, + prop3: true as unknown as string + }, + {test: 'object'} as unknown as string + ) + assertWriteCalls([ + `::some-command prop1={"test"%3A"object"},prop2=123,prop3=true::{"test":"object"}${os.EOL}` + ]) + }) +}) + +// Assert that process.stdout.write calls called only with the given arguments. +function assertWriteCalls(calls: string[]): void { + expect(process.stdout.write).toHaveBeenCalledTimes(calls.length) + + for (let i = 0; i < calls.length; i++) { + expect(process.stdout.write).toHaveBeenNthCalledWith(i + 1, calls[i]) + } +} diff --git a/actions-toolkit/packages/core/__tests__/core.test.ts b/actions-toolkit/packages/core/__tests__/core.test.ts new file mode 100644 index 00000000..09bc587b --- /dev/null +++ b/actions-toolkit/packages/core/__tests__/core.test.ts @@ -0,0 +1,676 @@ +import * as fs from 'fs' +import * as os from 'os' +import * as path from 'path' +import * as core from '../src/core' +import {HttpClient} from '@actions/http-client' +import {toCommandProperties} from '../src/utils' +import * as uuid from 'uuid' + +jest.mock('uuid') + +/* eslint-disable @typescript-eslint/unbound-method */ + +const testEnvVars = { + 'my var': '', + 'special char var \r\n];': '', + 'my var2': '', + 'my secret': '', + 'special char secret \r\n];': '', + 'my secret2': '', + PATH: `path1${path.delimiter}path2`, + + // Set inputs + INPUT_MY_INPUT: 'val', + INPUT_MISSING: '', + 'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response ', + INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces', + INPUT_BOOLEAN_INPUT: 'true', + INPUT_BOOLEAN_INPUT_TRUE1: 'true', + INPUT_BOOLEAN_INPUT_TRUE2: 'True', + INPUT_BOOLEAN_INPUT_TRUE3: 'TRUE', + INPUT_BOOLEAN_INPUT_FALSE1: 'false', + INPUT_BOOLEAN_INPUT_FALSE2: 'False', + INPUT_BOOLEAN_INPUT_FALSE3: 'FALSE', + INPUT_WRONG_BOOLEAN_INPUT: 'wrong', + INPUT_WITH_TRAILING_WHITESPACE: ' some val ', + INPUT_MY_INPUT_LIST: 'val1\nval2\nval3', + INPUT_LIST_WITH_TRAILING_WHITESPACE: ' val1 \n val2 \n ', + + // Save inputs + STATE_TEST_1: 'state_val', + + // File Commands + GITHUB_PATH: '', + GITHUB_ENV: '', + GITHUB_OUTPUT: '', + GITHUB_STATE: '' +} + +const UUID = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' +const DELIMITER = `ghadelimiter_${UUID}` + +describe('@actions/core', () => { + beforeAll(() => { + const filePath = path.join(__dirname, `test`) + if (!fs.existsSync(filePath)) { + fs.mkdirSync(filePath) + } + }) + + beforeEach(() => { + for (const key in testEnvVars) { + process.env[key] = testEnvVars[key as keyof typeof testEnvVars] + } + process.stdout.write = jest.fn() + + jest.spyOn(uuid, 'v4').mockImplementation(() => { + return UUID + }) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('legacy exportVariable produces the correct command and sets the env', () => { + core.exportVariable('my var', 'var val') + assertWriteCalls([`::set-env name=my var::var val${os.EOL}`]) + }) + + it('legacy exportVariable escapes variable names', () => { + core.exportVariable('special char var \r\n,:', 'special val') + expect(process.env['special char var \r\n,:']).toBe('special val') + assertWriteCalls([ + `::set-env name=special char var %0D%0A%2C%3A::special val${os.EOL}` + ]) + }) + + it('legacy exportVariable escapes variable values', () => { + core.exportVariable('my var2', 'var val\r\n') + expect(process.env['my var2']).toBe('var val\r\n') + assertWriteCalls([`::set-env name=my var2::var val%0D%0A${os.EOL}`]) + }) + + it('legacy exportVariable handles boolean inputs', () => { + core.exportVariable('my var', true) + assertWriteCalls([`::set-env name=my var::true${os.EOL}`]) + }) + + it('legacy exportVariable handles number inputs', () => { + core.exportVariable('my var', 5) + assertWriteCalls([`::set-env name=my var::5${os.EOL}`]) + }) + + it('exportVariable produces the correct command and sets the env', () => { + const command = 'ENV' + createFileCommandFile(command) + core.exportVariable('my var', 'var val') + verifyFileCommand( + command, + `my var<<${DELIMITER}${os.EOL}var val${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('exportVariable handles boolean inputs', () => { + const command = 'ENV' + createFileCommandFile(command) + core.exportVariable('my var', true) + verifyFileCommand( + command, + `my var<<${DELIMITER}${os.EOL}true${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('exportVariable handles number inputs', () => { + const command = 'ENV' + createFileCommandFile(command) + core.exportVariable('my var', 5) + verifyFileCommand( + command, + `my var<<${DELIMITER}${os.EOL}5${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('exportVariable does not allow delimiter as value', () => { + const command = 'ENV' + createFileCommandFile(command) + + expect(() => { + core.exportVariable('my var', `good stuff ${DELIMITER} bad stuff`) + }).toThrow( + `Unexpected input: value should not contain the delimiter "${DELIMITER}"` + ) + + const filePath = path.join(__dirname, `test/${command}`) + fs.unlinkSync(filePath) + }) + + it('exportVariable does not allow delimiter as name', () => { + const command = 'ENV' + createFileCommandFile(command) + + expect(() => { + core.exportVariable(`good stuff ${DELIMITER} bad stuff`, 'test') + }).toThrow( + `Unexpected input: name should not contain the delimiter "${DELIMITER}"` + ) + + const filePath = path.join(__dirname, `test/${command}`) + fs.unlinkSync(filePath) + }) + + it('setSecret produces the correct command', () => { + core.setSecret('secret val') + core.setSecret('multi\nline\r\nsecret') + assertWriteCalls([ + `::add-mask::secret val${os.EOL}`, + `::add-mask::multi%0Aline%0D%0Asecret${os.EOL}` + ]) + }) + + it('prependPath produces the correct commands and sets the env', () => { + const command = 'PATH' + createFileCommandFile(command) + core.addPath('myPath') + expect(process.env['PATH']).toBe( + `myPath${path.delimiter}path1${path.delimiter}path2` + ) + verifyFileCommand(command, `myPath${os.EOL}`) + }) + + it('legacy prependPath produces the correct commands and sets the env', () => { + core.addPath('myPath') + expect(process.env['PATH']).toBe( + `myPath${path.delimiter}path1${path.delimiter}path2` + ) + assertWriteCalls([`::add-path::myPath${os.EOL}`]) + }) + + it('getInput gets non-required input', () => { + expect(core.getInput('my input')).toBe('val') + }) + + it('getInput gets required input', () => { + expect(core.getInput('my input', {required: true})).toBe('val') + }) + + it('getInput throws on missing required input', () => { + expect(() => core.getInput('missing', {required: true})).toThrow( + 'Input required and not supplied: missing' + ) + }) + + it('getInput does not throw on missing non-required input', () => { + expect(core.getInput('missing', {required: false})).toBe('') + }) + + it('getInput is case insensitive', () => { + expect(core.getInput('My InPuT')).toBe('val') + }) + + it('getInput handles special characters', () => { + expect(core.getInput('special chars_\'\t"\\')).toBe('\'\t"\\ response') + }) + + it('getInput handles multiple spaces', () => { + expect(core.getInput('multiple spaces variable')).toBe( + 'I have multiple spaces' + ) + }) + + it('getInput trims whitespace by default', () => { + expect(core.getInput('with trailing whitespace')).toBe('some val') + }) + + it('getInput trims whitespace when option is explicitly true', () => { + expect( + core.getInput('with trailing whitespace', {trimWhitespace: true}) + ).toBe('some val') + }) + + it('getInput does not trim whitespace when option is false', () => { + expect( + core.getInput('with trailing whitespace', {trimWhitespace: false}) + ).toBe(' some val ') + }) + + it('getInput gets non-required boolean input', () => { + expect(core.getBooleanInput('boolean input')).toBe(true) + }) + + it('getInput gets required input', () => { + expect(core.getBooleanInput('boolean input', {required: true})).toBe(true) + }) + + it('getBooleanInput handles boolean input', () => { + expect(core.getBooleanInput('boolean input true1')).toBe(true) + expect(core.getBooleanInput('boolean input true2')).toBe(true) + expect(core.getBooleanInput('boolean input true3')).toBe(true) + expect(core.getBooleanInput('boolean input false1')).toBe(false) + expect(core.getBooleanInput('boolean input false2')).toBe(false) + expect(core.getBooleanInput('boolean input false3')).toBe(false) + }) + + it('getBooleanInput handles wrong boolean input', () => { + expect(() => core.getBooleanInput('wrong boolean input')).toThrow( + 'Input does not meet YAML 1.2 "Core Schema" specification: wrong boolean input\n' + + `Support boolean input list: \`true | True | TRUE | false | False | FALSE\`` + ) + }) + + it('getMultilineInput works', () => { + expect(core.getMultilineInput('my input list')).toEqual([ + 'val1', + 'val2', + 'val3' + ]) + }) + + it('getMultilineInput trims whitespace by default', () => { + expect(core.getMultilineInput('list with trailing whitespace')).toEqual([ + 'val1', + 'val2' + ]) + }) + + it('getMultilineInput trims whitespace when option is explicitly true', () => { + expect( + core.getMultilineInput('list with trailing whitespace', { + trimWhitespace: true + }) + ).toEqual(['val1', 'val2']) + }) + + it('getMultilineInput does not trim whitespace when option is false', () => { + expect( + core.getMultilineInput('list with trailing whitespace', { + trimWhitespace: false + }) + ).toEqual([' val1 ', ' val2 ', ' ']) + }) + + it('legacy setOutput produces the correct command', () => { + core.setOutput('some output', 'some value') + assertWriteCalls([ + os.EOL, + `::set-output name=some output::some value${os.EOL}` + ]) + }) + + it('legacy setOutput handles bools', () => { + core.setOutput('some output', false) + assertWriteCalls([os.EOL, `::set-output name=some output::false${os.EOL}`]) + }) + + it('legacy setOutput handles numbers', () => { + core.setOutput('some output', 1.01) + assertWriteCalls([os.EOL, `::set-output name=some output::1.01${os.EOL}`]) + }) + + it('setOutput produces the correct command and sets the output', () => { + const command = 'OUTPUT' + createFileCommandFile(command) + core.setOutput('my out', 'out val') + verifyFileCommand( + command, + `my out<<${DELIMITER}${os.EOL}out val${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('setOutput handles boolean inputs', () => { + const command = 'OUTPUT' + createFileCommandFile(command) + core.setOutput('my out', true) + verifyFileCommand( + command, + `my out<<${DELIMITER}${os.EOL}true${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('setOutput handles number inputs', () => { + const command = 'OUTPUT' + createFileCommandFile(command) + core.setOutput('my out', 5) + verifyFileCommand( + command, + `my out<<${DELIMITER}${os.EOL}5${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('setOutput does not allow delimiter as value', () => { + const command = 'OUTPUT' + createFileCommandFile(command) + + expect(() => { + core.setOutput('my out', `good stuff ${DELIMITER} bad stuff`) + }).toThrow( + `Unexpected input: value should not contain the delimiter "${DELIMITER}"` + ) + + const filePath = path.join(__dirname, `test/${command}`) + fs.unlinkSync(filePath) + }) + + it('setOutput does not allow delimiter as name', () => { + const command = 'OUTPUT' + createFileCommandFile(command) + + expect(() => { + core.setOutput(`good stuff ${DELIMITER} bad stuff`, 'test') + }).toThrow( + `Unexpected input: name should not contain the delimiter "${DELIMITER}"` + ) + + const filePath = path.join(__dirname, `test/${command}`) + fs.unlinkSync(filePath) + }) + + it('setFailed sets the correct exit code and failure message', () => { + core.setFailed('Failure message') + expect(process.exitCode).toBe(core.ExitCode.Failure) + assertWriteCalls([`::error::Failure message${os.EOL}`]) + }) + + it('setFailed escapes the failure message', () => { + core.setFailed('Failure \r\n\nmessage\r') + expect(process.exitCode).toBe(core.ExitCode.Failure) + assertWriteCalls([`::error::Failure %0D%0A%0Amessage%0D${os.EOL}`]) + }) + + it('setFailed handles Error', () => { + const message = 'this is my error message' + core.setFailed(new Error(message)) + expect(process.exitCode).toBe(core.ExitCode.Failure) + assertWriteCalls([`::error::Error: ${message}${os.EOL}`]) + }) + + it('error sets the correct error message', () => { + core.error('Error message') + assertWriteCalls([`::error::Error message${os.EOL}`]) + }) + + it('error escapes the error message', () => { + core.error('Error message\r\n\n') + assertWriteCalls([`::error::Error message%0D%0A%0A${os.EOL}`]) + }) + + it('error handles an error object', () => { + const message = 'this is my error message' + core.error(new Error(message)) + assertWriteCalls([`::error::Error: ${message}${os.EOL}`]) + }) + + it('error handles parameters correctly', () => { + const message = 'this is my error message' + core.error(new Error(message), { + title: 'A title', + file: 'root/test.txt', + startColumn: 1, + endColumn: 2, + startLine: 5, + endLine: 5 + }) + assertWriteCalls([ + `::error title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}` + ]) + }) + + it('warning sets the correct message', () => { + core.warning('Warning') + assertWriteCalls([`::warning::Warning${os.EOL}`]) + }) + + it('warning escapes the message', () => { + core.warning('\r\nwarning\n') + assertWriteCalls([`::warning::%0D%0Awarning%0A${os.EOL}`]) + }) + + it('warning handles an error object', () => { + const message = 'this is my error message' + core.warning(new Error(message)) + assertWriteCalls([`::warning::Error: ${message}${os.EOL}`]) + }) + + it('warning handles parameters correctly', () => { + const message = 'this is my error message' + core.warning(new Error(message), { + title: 'A title', + file: 'root/test.txt', + startColumn: 1, + endColumn: 2, + startLine: 5, + endLine: 5 + }) + assertWriteCalls([ + `::warning title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}` + ]) + }) + + it('notice sets the correct message', () => { + core.notice('Notice') + assertWriteCalls([`::notice::Notice${os.EOL}`]) + }) + + it('notice escapes the message', () => { + core.notice('\r\nnotice\n') + assertWriteCalls([`::notice::%0D%0Anotice%0A${os.EOL}`]) + }) + + it('notice handles an error object', () => { + const message = 'this is my error message' + core.notice(new Error(message)) + assertWriteCalls([`::notice::Error: ${message}${os.EOL}`]) + }) + + it('notice handles parameters correctly', () => { + const message = 'this is my error message' + core.notice(new Error(message), { + title: 'A title', + file: 'root/test.txt', + startColumn: 1, + endColumn: 2, + startLine: 5, + endLine: 5 + }) + assertWriteCalls([ + `::notice title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}` + ]) + }) + + it('annotations map field names correctly', () => { + const commandProperties = toCommandProperties({ + title: 'A title', + file: 'root/test.txt', + startColumn: 1, + endColumn: 2, + startLine: 5, + endLine: 5 + }) + expect(commandProperties.title).toBe('A title') + expect(commandProperties.file).toBe('root/test.txt') + expect(commandProperties.col).toBe(1) + expect(commandProperties.endColumn).toBe(2) + expect(commandProperties.line).toBe(5) + expect(commandProperties.endLine).toBe(5) + + expect(commandProperties.startColumn).toBeUndefined() + expect(commandProperties.startLine).toBeUndefined() + }) + + it('startGroup starts a new group', () => { + core.startGroup('my-group') + assertWriteCalls([`::group::my-group${os.EOL}`]) + }) + + it('endGroup ends new group', () => { + core.endGroup() + assertWriteCalls([`::endgroup::${os.EOL}`]) + }) + + it('group wraps an async call in a group', async () => { + const result = await core.group('mygroup', async () => { + process.stdout.write('in my group\n') + return true + }) + expect(result).toBe(true) + assertWriteCalls([ + `::group::mygroup${os.EOL}`, + 'in my group\n', + `::endgroup::${os.EOL}` + ]) + }) + + it('debug sets the correct message', () => { + core.debug('Debug') + assertWriteCalls([`::debug::Debug${os.EOL}`]) + }) + + it('debug escapes the message', () => { + core.debug('\r\ndebug\n') + assertWriteCalls([`::debug::%0D%0Adebug%0A${os.EOL}`]) + }) + + it('legacy saveState produces the correct command', () => { + core.saveState('state_1', 'some value') + assertWriteCalls([`::save-state name=state_1::some value${os.EOL}`]) + }) + + it('legacy saveState handles numbers', () => { + core.saveState('state_1', 1) + assertWriteCalls([`::save-state name=state_1::1${os.EOL}`]) + }) + + it('legacy saveState handles bools', () => { + core.saveState('state_1', true) + assertWriteCalls([`::save-state name=state_1::true${os.EOL}`]) + }) + + it('saveState produces the correct command and saves the state', () => { + const command = 'STATE' + createFileCommandFile(command) + core.saveState('my state', 'out val') + verifyFileCommand( + command, + `my state<<${DELIMITER}${os.EOL}out val${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('saveState handles boolean inputs', () => { + const command = 'STATE' + createFileCommandFile(command) + core.saveState('my state', true) + verifyFileCommand( + command, + `my state<<${DELIMITER}${os.EOL}true${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('saveState handles number inputs', () => { + const command = 'STATE' + createFileCommandFile(command) + core.saveState('my state', 5) + verifyFileCommand( + command, + `my state<<${DELIMITER}${os.EOL}5${os.EOL}${DELIMITER}${os.EOL}` + ) + }) + + it('saveState does not allow delimiter as value', () => { + const command = 'STATE' + createFileCommandFile(command) + + expect(() => { + core.saveState('my state', `good stuff ${DELIMITER} bad stuff`) + }).toThrow( + `Unexpected input: value should not contain the delimiter "${DELIMITER}"` + ) + + const filePath = path.join(__dirname, `test/${command}`) + fs.unlinkSync(filePath) + }) + + it('saveState does not allow delimiter as name', () => { + const command = 'STATE' + createFileCommandFile(command) + + expect(() => { + core.saveState(`good stuff ${DELIMITER} bad stuff`, 'test') + }).toThrow( + `Unexpected input: name should not contain the delimiter "${DELIMITER}"` + ) + + const filePath = path.join(__dirname, `test/${command}`) + fs.unlinkSync(filePath) + }) + + it('getState gets wrapper action state', () => { + expect(core.getState('TEST_1')).toBe('state_val') + }) + + it('isDebug check debug state', () => { + const current = process.env['RUNNER_DEBUG'] + try { + delete process.env.RUNNER_DEBUG + expect(core.isDebug()).toBe(false) + + process.env['RUNNER_DEBUG'] = '1' + expect(core.isDebug()).toBe(true) + } finally { + process.env['RUNNER_DEBUG'] = current + } + }) + + it('setCommandEcho can enable echoing', () => { + core.setCommandEcho(true) + assertWriteCalls([`::echo::on${os.EOL}`]) + }) + + it('setCommandEcho can disable echoing', () => { + core.setCommandEcho(false) + assertWriteCalls([`::echo::off${os.EOL}`]) + }) +}) + +// Assert that process.stdout.write calls called only with the given arguments. +function assertWriteCalls(calls: string[]): void { + expect(process.stdout.write).toHaveBeenCalledTimes(calls.length) + + for (let i = 0; i < calls.length; i++) { + expect(process.stdout.write).toHaveBeenNthCalledWith(i + 1, calls[i]) + } +} + +function createFileCommandFile(command: string): void { + const filePath = path.join(__dirname, `test/${command}`) + process.env[`GITHUB_${command}`] = filePath + fs.appendFileSync(filePath, '', { + encoding: 'utf8' + }) +} + +function verifyFileCommand(command: string, expectedContents: string): void { + const filePath = path.join(__dirname, `test/${command}`) + const contents = fs.readFileSync(filePath, 'utf8') + try { + expect(contents).toEqual(expectedContents) + } finally { + fs.unlinkSync(filePath) + } +} + +function getTokenEndPoint(): string { + return 'https://vstoken.actions.githubusercontent.com/.well-known/openid-configuration' +} + +describe('oidc-client-tests', () => { + it('Get Http Client', async () => { + const http = new HttpClient('actions/oidc-client') + expect(http).toBeDefined() + }) + + it('HTTP get request to get token endpoint', async () => { + const http = new HttpClient('actions/oidc-client') + const res = await http.get(getTokenEndPoint()) + expect(res.message.statusCode).toBe(200) + }) +}) diff --git a/actions-toolkit/packages/core/__tests__/path-utils.test.ts b/actions-toolkit/packages/core/__tests__/path-utils.test.ts new file mode 100644 index 00000000..fda8a5e7 --- /dev/null +++ b/actions-toolkit/packages/core/__tests__/path-utils.test.ts @@ -0,0 +1,162 @@ +import * as path from 'path' + +import {toPlatformPath, toPosixPath, toWin32Path} from '../src/path-utils' + +describe('#toPosixPath', () => { + const cases: { + only?: boolean + name: string + input: string + expected: string + }[] = [ + { + name: 'empty string', + input: '', + expected: '' + }, + { + name: 'single value', + input: 'foo', + expected: 'foo' + }, + { + name: 'with posix relative', + input: 'foo/bar/baz', + expected: 'foo/bar/baz' + }, + { + name: 'with posix absolute', + input: '/foo/bar/baz', + expected: '/foo/bar/baz' + }, + { + name: 'with win32 relative', + input: 'foo\\bar\\baz', + expected: 'foo/bar/baz' + }, + { + name: 'with win32 absolute', + input: '\\foo\\bar\\baz', + expected: '/foo/bar/baz' + }, + { + name: 'with a mix', + input: '\\foo/bar/baz', + expected: '/foo/bar/baz' + } + ] + + for (const tc of cases) { + const fn = tc.only ? it.only : it + fn(tc.name, () => { + const result = toPosixPath(tc.input) + expect(result).toEqual(tc.expected) + }) + } +}) + +describe('#toWin32Path', () => { + const cases: { + only?: boolean + name: string + input: string + expected: string + }[] = [ + { + name: 'empty string', + input: '', + expected: '' + }, + { + name: 'single value', + input: 'foo', + expected: 'foo' + }, + { + name: 'with posix relative', + input: 'foo/bar/baz', + expected: 'foo\\bar\\baz' + }, + { + name: 'with posix absolute', + input: '/foo/bar/baz', + expected: '\\foo\\bar\\baz' + }, + { + name: 'with win32 relative', + input: 'foo\\bar\\baz', + expected: 'foo\\bar\\baz' + }, + { + name: 'with win32 absolute', + input: '\\foo\\bar\\baz', + expected: '\\foo\\bar\\baz' + }, + { + name: 'with a mix', + input: '\\foo/bar\\baz', + expected: '\\foo\\bar\\baz' + } + ] + + for (const tc of cases) { + const fn = tc.only ? it.only : it + fn(tc.name, () => { + const result = toWin32Path(tc.input) + expect(result).toEqual(tc.expected) + }) + } +}) + +describe('#toPlatformPath', () => { + const cases: { + only?: boolean + name: string + input: string + expected: string + }[] = [ + { + name: 'empty string', + input: '', + expected: '' + }, + { + name: 'single value', + input: 'foo', + expected: 'foo' + }, + { + name: 'with posix relative', + input: 'foo/bar/baz', + expected: path.join('foo', 'bar', 'baz') + }, + { + name: 'with posix absolute', + input: '/foo/bar/baz', + expected: path.join(path.sep, 'foo', 'bar', 'baz') + }, + { + name: 'with win32 relative', + input: 'foo\\bar\\baz', + expected: path.join('foo', 'bar', 'baz') + }, + { + name: 'with win32 absolute', + input: '\\foo\\bar\\baz', + expected: path.join(path.sep, 'foo', 'bar', 'baz') + }, + { + name: 'with a mix', + input: '\\foo/bar\\baz', + expected: path.join(path.sep, 'foo', 'bar', 'baz') + } + ] + + for (const tc of cases) { + const fn = tc.only ? it.only : it + fn(tc.name, () => { + const result = toPlatformPath(tc.input) + expect(result).toEqual(tc.expected) + }) + } +}) diff --git a/actions-toolkit/packages/core/__tests__/platform.test.ts b/actions-toolkit/packages/core/__tests__/platform.test.ts new file mode 100644 index 00000000..c80be330 --- /dev/null +++ b/actions-toolkit/packages/core/__tests__/platform.test.ts @@ -0,0 +1,29 @@ +import os from 'os' +import {platform} from '../src/core' + +describe('getInfo', () => { + it('returns the platform info', async () => { + const info = await platform.getDetails() + expect(info).toEqual({ + name: expect.any(String), + platform: expect.any(String), + arch: expect.any(String), + version: expect.any(String), + isWindows: expect.any(Boolean), + isMacOS: expect.any(Boolean), + isLinux: expect.any(Boolean) + }) + }) + + it('returns the platform info with the correct name', async () => { + const isWindows = os.platform() === 'win32' + const isMacOS = os.platform() === 'darwin' + const isLinux = os.platform() === 'linux' + + const info = await platform.getDetails() + expect(info.platform).toEqual(os.platform()) + expect(info.isWindows).toEqual(isWindows) + expect(info.isMacOS).toEqual(isMacOS) + expect(info.isLinux).toEqual(isLinux) + }) +}) diff --git a/actions-toolkit/packages/core/__tests__/summary.test.ts b/actions-toolkit/packages/core/__tests__/summary.test.ts new file mode 100644 index 00000000..b7798dee --- /dev/null +++ b/actions-toolkit/packages/core/__tests__/summary.test.ts @@ -0,0 +1,272 @@ +import * as fs from 'fs' +import * as os from 'os' +import path from 'path' +import {summary, SUMMARY_ENV_VAR} from '../src/summary' + +const testDirectoryPath = path.join(__dirname, 'test') +const testFilePath = path.join(testDirectoryPath, 'test-summary.md') + +async function assertSummary(expected: string): Promise { + const file = await fs.promises.readFile(testFilePath, {encoding: 'utf8'}) + expect(file).toEqual(expected) +} + +const fixtures = { + text: 'hello world 🌎', + code: `func fork() { + for { + go fork() + } +}`, + list: ['foo', 'bar', 'baz', '💣'], + table: [ + [ + { + data: 'foo', + header: true + }, + { + data: 'bar', + header: true + }, + { + data: 'baz', + header: true + }, + { + data: 'tall', + rowspan: '3' + } + ], + ['one', 'two', 'three'], + [ + { + data: 'wide', + colspan: '3' + } + ] + ], + details: { + label: 'open me', + content: '🎉 surprise' + }, + img: { + src: 'https://github.com/actions.png', + alt: 'actions logo', + options: { + width: '32', + height: '32' + } + }, + quote: { + text: 'Where the world builds software', + cite: 'https://github.com/about' + }, + link: { + text: 'GitHub', + href: 'https://github.com/' + } +} + +describe('@actions/core/src/summary', () => { + beforeEach(async () => { + process.env[SUMMARY_ENV_VAR] = testFilePath + await fs.promises.mkdir(testDirectoryPath, {recursive: true}) + await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'}) + summary.emptyBuffer() + }) + + afterAll(async () => { + await fs.promises.unlink(testFilePath) + }) + + it('throws if summary env var is undefined', async () => { + process.env[SUMMARY_ENV_VAR] = undefined + const write = summary.addRaw(fixtures.text).write() + + await expect(write).rejects.toThrow() + }) + + it('throws if summary file does not exist', async () => { + await fs.promises.unlink(testFilePath) + const write = summary.addRaw(fixtures.text).write() + + await expect(write).rejects.toThrow() + }) + + it('appends text to summary file', async () => { + await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'}) + await summary.addRaw(fixtures.text).write() + await assertSummary(`# ${fixtures.text}`) + }) + + it('overwrites text to summary file', async () => { + await fs.promises.writeFile(testFilePath, 'overwrite', {encoding: 'utf8'}) + await summary.addRaw(fixtures.text).write({overwrite: true}) + await assertSummary(fixtures.text) + }) + + it('appends text with EOL to summary file', async () => { + await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'}) + await summary.addRaw(fixtures.text, true).write() + await assertSummary(`# ${fixtures.text}${os.EOL}`) + }) + + it('chains appends text to summary file', async () => { + await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'}) + await summary + .addRaw(fixtures.text) + .addRaw(fixtures.text) + .addRaw(fixtures.text) + .write() + await assertSummary([fixtures.text, fixtures.text, fixtures.text].join('')) + }) + + it('empties buffer after write', async () => { + await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'}) + await summary.addRaw(fixtures.text).write() + await assertSummary(fixtures.text) + expect(summary.isEmptyBuffer()).toBe(true) + }) + + it('returns summary buffer as string', () => { + summary.addRaw(fixtures.text) + expect(summary.stringify()).toEqual(fixtures.text) + }) + + it('return correct values for isEmptyBuffer', () => { + summary.addRaw(fixtures.text) + expect(summary.isEmptyBuffer()).toBe(false) + + summary.emptyBuffer() + expect(summary.isEmptyBuffer()).toBe(true) + }) + + it('clears a buffer and summary file', async () => { + await fs.promises.writeFile(testFilePath, 'content', {encoding: 'utf8'}) + await summary.clear() + await assertSummary('') + expect(summary.isEmptyBuffer()).toBe(true) + }) + + it('adds EOL', async () => { + await summary.addRaw(fixtures.text).addEOL().write() + await assertSummary(fixtures.text + os.EOL) + }) + + it('adds a code block without language', async () => { + await summary.addCodeBlock(fixtures.code).write() + const expected = `
func fork() {\n  for {\n    go fork()\n  }\n}
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a code block with a language', async () => { + await summary.addCodeBlock(fixtures.code, 'go').write() + const expected = `
func fork() {\n  for {\n    go fork()\n  }\n}
${os.EOL}` + await assertSummary(expected) + }) + + it('adds an unordered list', async () => { + await summary.addList(fixtures.list).write() + const expected = `
  • foo
  • bar
  • baz
  • 💣
${os.EOL}` + await assertSummary(expected) + }) + + it('adds an ordered list', async () => { + await summary.addList(fixtures.list, true).write() + const expected = `
  1. foo
  2. bar
  3. baz
  4. 💣
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a table', async () => { + await summary.addTable(fixtures.table).write() + const expected = `
foobarbaztall
onetwothree
wide
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a details element', async () => { + await summary + .addDetails(fixtures.details.label, fixtures.details.content) + .write() + const expected = `
open me🎉 surprise
${os.EOL}` + await assertSummary(expected) + }) + + it('adds an image with alt text', async () => { + await summary.addImage(fixtures.img.src, fixtures.img.alt).write() + const expected = `actions logo${os.EOL}` + await assertSummary(expected) + }) + + it('adds an image with custom dimensions', async () => { + await summary + .addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options) + .write() + const expected = `actions logo${os.EOL}` + await assertSummary(expected) + }) + + it('adds an image with custom dimensions', async () => { + await summary + .addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options) + .write() + const expected = `actions logo${os.EOL}` + await assertSummary(expected) + }) + + it('adds headings h1...h6', async () => { + for (const i of [1, 2, 3, 4, 5, 6]) { + summary.addHeading('heading', i) + } + await summary.write() + const expected = `

heading

${os.EOL}

heading

${os.EOL}

heading

${os.EOL}

heading

${os.EOL}
heading
${os.EOL}
heading
${os.EOL}` + await assertSummary(expected) + }) + + it('adds h1 if heading level not specified', async () => { + await summary.addHeading('heading').write() + const expected = `

heading

${os.EOL}` + await assertSummary(expected) + }) + + it('uses h1 if heading level is garbage or out of range', async () => { + await summary + .addHeading('heading', 'foobar') + .addHeading('heading', 1337) + .addHeading('heading', -1) + .addHeading('heading', Infinity) + .write() + const expected = `

heading

${os.EOL}

heading

${os.EOL}

heading

${os.EOL}

heading

${os.EOL}` + await assertSummary(expected) + }) + + it('adds a separator', async () => { + await summary.addSeparator().write() + const expected = `
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a break', async () => { + await summary.addBreak().write() + const expected = `
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a quote', async () => { + await summary.addQuote(fixtures.quote.text).write() + const expected = `
Where the world builds software
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a quote with citation', async () => { + await summary.addQuote(fixtures.quote.text, fixtures.quote.cite).write() + const expected = `
Where the world builds software
${os.EOL}` + await assertSummary(expected) + }) + + it('adds a link with href', async () => { + await summary.addLink(fixtures.link.text, fixtures.link.href).write() + const expected = `GitHub${os.EOL}` + await assertSummary(expected) + }) +}) diff --git a/actions-toolkit/packages/core/package-lock.json b/actions-toolkit/packages/core/package-lock.json new file mode 100644 index 00000000..7b1cf7bb --- /dev/null +++ b/actions-toolkit/packages/core/package-lock.json @@ -0,0 +1,116 @@ +{ + "name": "@actions/core", + "version": "1.10.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/core", + "version": "1.10.1", + "license": "MIT", + "dependencies": { + "@actions/exec": "^1.1.1", + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@types/node": "^12.0.2", + "@types/uuid": "^8.3.4" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "node_modules/@types/node": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz", + "integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + } + }, + "dependencies": { + "@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "@types/node": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz", + "integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==", + "dev": true + }, + "@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } +} diff --git a/actions-toolkit/packages/core/package.json b/actions-toolkit/packages/core/package.json new file mode 100644 index 00000000..2eda27b5 --- /dev/null +++ b/actions-toolkit/packages/core/package.json @@ -0,0 +1,47 @@ +{ + "name": "@actions/core", + "version": "1.10.1", + "description": "Actions core lib", + "keywords": [ + "github", + "actions", + "core" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/core", + "license": "MIT", + "main": "lib/core.js", + "types": "lib/core.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/core" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc -p tsconfig.json" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/exec": "^1.1.1", + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@types/node": "^12.0.2", + "@types/uuid": "^8.3.4" + } +} \ No newline at end of file diff --git a/actions-toolkit/packages/core/src/command.ts b/actions-toolkit/packages/core/src/command.ts new file mode 100644 index 00000000..2796fce9 --- /dev/null +++ b/actions-toolkit/packages/core/src/command.ts @@ -0,0 +1,94 @@ +import * as os from 'os' +import {toCommandValue} from './utils' + +// For internal use, subject to change. + +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface CommandProperties { + [key: string]: any +} + +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +export function issueCommand( + command: string, + properties: CommandProperties, + message: any +): void { + const cmd = new Command(command, properties, message) + process.stdout.write(cmd.toString() + os.EOL) +} + +export function issue(name: string, message = ''): void { + issueCommand(name, {}, message) +} + +const CMD_STRING = '::' + +class Command { + private readonly command: string + private readonly message: string + private readonly properties: CommandProperties + + constructor(command: string, properties: CommandProperties, message: string) { + if (!command) { + command = 'missing.command' + } + + this.command = command + this.properties = properties + this.message = message + } + + toString(): string { + let cmdStr = CMD_STRING + this.command + + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' ' + let first = true + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key] + if (val) { + if (first) { + first = false + } else { + cmdStr += ',' + } + + cmdStr += `${key}=${escapeProperty(val)}` + } + } + } + } + + cmdStr += `${CMD_STRING}${escapeData(this.message)}` + return cmdStr + } +} + +function escapeData(s: any): string { + return toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') +} + +function escapeProperty(s: any): string { + return toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C') +} diff --git a/actions-toolkit/packages/core/src/core.ts b/actions-toolkit/packages/core/src/core.ts new file mode 100644 index 00000000..0a141693 --- /dev/null +++ b/actions-toolkit/packages/core/src/core.ts @@ -0,0 +1,393 @@ +import {issue, issueCommand} from './command' +import {issueFileCommand, prepareKeyValueMessage} from './file-command' +import {toCommandProperties, toCommandValue} from './utils' + +import * as os from 'os' +import * as path from 'path' + +import {OidcClient} from './oidc-utils' + +/** + * Interface for getInput options + */ +export interface InputOptions { + /** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */ + required?: boolean + + /** Optional. Whether leading/trailing whitespace will be trimmed for the input. Defaults to true */ + trimWhitespace?: boolean +} + +/** + * The code to exit an action + */ +export enum ExitCode { + /** + * A code indicating that the action was successful + */ + Success = 0, + + /** + * A code indicating that the action was a failure + */ + Failure = 1 +} + +/** + * Optional properties that can be sent with annotation commands (notice, error, and warning) + * See: https://docs.github.com/en/rest/reference/checks#create-a-check-run for more information about annotations. + */ +export interface AnnotationProperties { + /** + * A title for the annotation. + */ + title?: string + + /** + * The path of the file for which the annotation should be created. + */ + file?: string + + /** + * The start line for the annotation. + */ + startLine?: number + + /** + * The end line for the annotation. Defaults to `startLine` when `startLine` is provided. + */ + endLine?: number + + /** + * The start column for the annotation. Cannot be sent when `startLine` and `endLine` are different values. + */ + startColumn?: number + + /** + * The end column for the annotation. Cannot be sent when `startLine` and `endLine` are different values. + * Defaults to `startColumn` when `startColumn` is provided. + */ + endColumn?: number +} + +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- + +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function exportVariable(name: string, val: any): void { + const convertedVal = toCommandValue(val) + process.env[name] = convertedVal + + const filePath = process.env['GITHUB_ENV'] || '' + if (filePath) { + return issueFileCommand('ENV', prepareKeyValueMessage(name, val)) + } + + issueCommand('set-env', {name}, convertedVal) +} + +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +export function setSecret(secret: string): void { + issueCommand('add-mask', {}, secret) +} + +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +export function addPath(inputPath: string): void { + const filePath = process.env['GITHUB_PATH'] || '' + if (filePath) { + issueFileCommand('PATH', inputPath) + } else { + issueCommand('add-path', {}, inputPath) + } + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}` +} + +/** + * Gets the value of an input. + * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. + * Returns an empty string if the value is not defined. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +export function getInput(name: string, options?: InputOptions): string { + const val: string = + process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '' + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`) + } + + if (options && options.trimWhitespace === false) { + return val + } + + return val.trim() +} + +/** + * Gets the values of an multiline input. Each value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string[] + * + */ +export function getMultilineInput( + name: string, + options?: InputOptions +): string[] { + const inputs: string[] = getInput(name, options) + .split('\n') + .filter(x => x !== '') + + if (options && options.trimWhitespace === false) { + return inputs + } + + return inputs.map(input => input.trim()) +} + +/** + * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. + * Support boolean input list: `true | True | TRUE | false | False | FALSE` . + * The return value is also in boolean type. + * ref: https://yaml.org/spec/1.2/spec.html#id2804923 + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns boolean + */ +export function getBooleanInput(name: string, options?: InputOptions): boolean { + const trueValue = ['true', 'True', 'TRUE'] + const falseValue = ['false', 'False', 'FALSE'] + const val = getInput(name, options) + if (trueValue.includes(val)) return true + if (falseValue.includes(val)) return false + throw new TypeError( + `Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + + `Support boolean input list: \`true | True | TRUE | false | False | FALSE\`` + ) +} + +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function setOutput(name: string, value: any): void { + const filePath = process.env['GITHUB_OUTPUT'] || '' + if (filePath) { + return issueFileCommand('OUTPUT', prepareKeyValueMessage(name, value)) + } + + process.stdout.write(os.EOL) + issueCommand('set-output', {name}, toCommandValue(value)) +} + +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +export function setCommandEcho(enabled: boolean): void { + issue('echo', enabled ? 'on' : 'off') +} + +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- + +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +export function setFailed(message: string | Error): void { + process.exitCode = ExitCode.Failure + + error(message) +} + +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- + +/** + * Gets whether Actions Step Debug is on or not + */ +export function isDebug(): boolean { + return process.env['RUNNER_DEBUG'] === '1' +} + +/** + * Writes debug message to user log + * @param message debug message + */ +export function debug(message: string): void { + issueCommand('debug', {}, message) +} + +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +export function error( + message: string | Error, + properties: AnnotationProperties = {} +): void { + issueCommand( + 'error', + toCommandProperties(properties), + message instanceof Error ? message.toString() : message + ) +} + +/** + * Adds a warning issue + * @param message warning issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +export function warning( + message: string | Error, + properties: AnnotationProperties = {} +): void { + issueCommand( + 'warning', + toCommandProperties(properties), + message instanceof Error ? message.toString() : message + ) +} + +/** + * Adds a notice issue + * @param message notice issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +export function notice( + message: string | Error, + properties: AnnotationProperties = {} +): void { + issueCommand( + 'notice', + toCommandProperties(properties), + message instanceof Error ? message.toString() : message + ) +} + +/** + * Writes info to log with console.log. + * @param message info message + */ +export function info(message: string): void { + process.stdout.write(message + os.EOL) +} + +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +export function startGroup(name: string): void { + issue('group', name) +} + +/** + * End an output group. + */ +export function endGroup(): void { + issue('endgroup') +} + +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +export async function group(name: string, fn: () => Promise): Promise { + startGroup(name) + + let result: T + + try { + result = await fn() + } finally { + endGroup() + } + + return result +} + +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- + +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function saveState(name: string, value: any): void { + const filePath = process.env['GITHUB_STATE'] || '' + if (filePath) { + return issueFileCommand('STATE', prepareKeyValueMessage(name, value)) + } + + issueCommand('save-state', {name}, toCommandValue(value)) +} + +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +export function getState(name: string): string { + return process.env[`STATE_${name}`] || '' +} + +export async function getIDToken(aud?: string): Promise { + return await OidcClient.getIDToken(aud) +} + +/** + * Summary exports + */ +export {summary} from './summary' + +/** + * @deprecated use core.summary + */ +export {markdownSummary} from './summary' + +/** + * Path exports + */ +export {toPosixPath, toWin32Path, toPlatformPath} from './path-utils' + +/** + * Platform utilities exports + */ +export * as platform from './platform' diff --git a/actions-toolkit/packages/core/src/file-command.ts b/actions-toolkit/packages/core/src/file-command.ts new file mode 100644 index 00000000..832c2f0e --- /dev/null +++ b/actions-toolkit/packages/core/src/file-command.ts @@ -0,0 +1,47 @@ +// For internal use, subject to change. + +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as fs from 'fs' +import * as os from 'os' +import {v4 as uuidv4} from 'uuid' +import {toCommandValue} from './utils' + +export function issueFileCommand(command: string, message: any): void { + const filePath = process.env[`GITHUB_${command}`] + if (!filePath) { + throw new Error( + `Unable to find environment variable for file command ${command}` + ) + } + if (!fs.existsSync(filePath)) { + throw new Error(`Missing file at path: ${filePath}`) + } + + fs.appendFileSync(filePath, `${toCommandValue(message)}${os.EOL}`, { + encoding: 'utf8' + }) +} + +export function prepareKeyValueMessage(key: string, value: any): string { + const delimiter = `ghadelimiter_${uuidv4()}` + const convertedValue = toCommandValue(value) + + // These should realistically never happen, but just in case someone finds a + // way to exploit uuid generation let's not allow keys or values that contain + // the delimiter. + if (key.includes(delimiter)) { + throw new Error( + `Unexpected input: name should not contain the delimiter "${delimiter}"` + ) + } + + if (convertedValue.includes(delimiter)) { + throw new Error( + `Unexpected input: value should not contain the delimiter "${delimiter}"` + ) + } + + return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}` +} diff --git a/actions-toolkit/packages/core/src/oidc-utils.ts b/actions-toolkit/packages/core/src/oidc-utils.ts new file mode 100644 index 00000000..382fb13c --- /dev/null +++ b/actions-toolkit/packages/core/src/oidc-utils.ts @@ -0,0 +1,84 @@ +/* eslint-disable @typescript-eslint/no-extraneous-class */ +import * as actions_http_client from '@actions/http-client' +import {RequestOptions} from '@actions/http-client/lib/interfaces' +import {HttpClient} from '@actions/http-client' +import {BearerCredentialHandler} from '@actions/http-client/lib/auth' +import {debug, setSecret} from './core' +interface TokenResponse { + value?: string +} + +export class OidcClient { + private static createHttpClient( + allowRetry = true, + maxRetry = 10 + ): actions_http_client.HttpClient { + const requestOptions: RequestOptions = { + allowRetries: allowRetry, + maxRetries: maxRetry + } + + return new HttpClient( + 'actions/oidc-client', + [new BearerCredentialHandler(OidcClient.getRequestToken())], + requestOptions + ) + } + + private static getRequestToken(): string { + const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN'] + if (!token) { + throw new Error( + 'Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable' + ) + } + return token + } + + private static getIDTokenUrl(): string { + const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL'] + if (!runtimeUrl) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable') + } + return runtimeUrl + } + + private static async getCall(id_token_url: string): Promise { + const httpclient = OidcClient.createHttpClient() + + const res = await httpclient + .getJson(id_token_url) + .catch(error => { + throw new Error( + `Failed to get ID Token. \n + Error Code : ${error.statusCode}\n + Error Message: ${error.message}` + ) + }) + + const id_token = res.result?.value + if (!id_token) { + throw new Error('Response json body do not have ID Token field') + } + return id_token + } + + static async getIDToken(audience?: string): Promise { + try { + // New ID Token is requested from action service + let id_token_url: string = OidcClient.getIDTokenUrl() + if (audience) { + const encodedAudience = encodeURIComponent(audience) + id_token_url = `${id_token_url}&audience=${encodedAudience}` + } + + debug(`ID token url is ${id_token_url}`) + + const id_token = await OidcClient.getCall(id_token_url) + setSecret(id_token) + return id_token + } catch (error) { + throw new Error(`Error message: ${error.message}`) + } + } +} diff --git a/actions-toolkit/packages/core/src/path-utils.ts b/actions-toolkit/packages/core/src/path-utils.ts new file mode 100644 index 00000000..f3085377 --- /dev/null +++ b/actions-toolkit/packages/core/src/path-utils.ts @@ -0,0 +1,35 @@ +import * as path from 'path' + +/** + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. + * + * @param pth. Path to transform. + * @return string Posix path. + */ +export function toPosixPath(pth: string): string { + return pth.replace(/[\\]/g, '/') +} + +/** + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. + * + * @param pth. Path to transform. + * @return string Win32 path. + */ +export function toWin32Path(pth: string): string { + return pth.replace(/[/]/g, '\\') +} + +/** + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. + * + * @param pth The path to platformize. + * @return string The platform-specific path. + */ +export function toPlatformPath(pth: string): string { + return pth.replace(/[/\\]/g, path.sep) +} diff --git a/actions-toolkit/packages/core/src/platform.ts b/actions-toolkit/packages/core/src/platform.ts new file mode 100644 index 00000000..55fdee64 --- /dev/null +++ b/actions-toolkit/packages/core/src/platform.ts @@ -0,0 +1,87 @@ +import os from 'os' +import * as exec from '@actions/exec' + +const getWindowsInfo = async (): Promise<{name: string; version: string}> => { + const {stdout: version} = await exec.getExecOutput( + 'powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', + undefined, + { + silent: true + } + ) + + const {stdout: name} = await exec.getExecOutput( + 'powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', + undefined, + { + silent: true + } + ) + + return { + name: name.trim(), + version: version.trim() + } +} + +const getMacOsInfo = async (): Promise<{ + name: string + version: string +}> => { + const {stdout} = await exec.getExecOutput('sw_vers', undefined, { + silent: true + }) + + const version = stdout.match(/ProductVersion:\s*(.+)/)?.[1] ?? '' + const name = stdout.match(/ProductName:\s*(.+)/)?.[1] ?? '' + + return { + name, + version + } +} + +const getLinuxInfo = async (): Promise<{ + name: string + version: string +}> => { + const {stdout} = await exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], { + silent: true + }) + + const [name, version] = stdout.trim().split('\n') + + return { + name, + version + } +} + +export const platform = os.platform() +export const arch = os.arch() +export const isWindows = platform === 'win32' +export const isMacOS = platform === 'darwin' +export const isLinux = platform === 'linux' + +export async function getDetails(): Promise<{ + name: string + platform: string + arch: string + version: string + isWindows: boolean + isMacOS: boolean + isLinux: boolean +}> { + return { + ...(await (isWindows + ? getWindowsInfo() + : isMacOS + ? getMacOsInfo() + : getLinuxInfo())), + platform, + arch, + isWindows, + isMacOS, + isLinux + } +} diff --git a/actions-toolkit/packages/core/src/summary.ts b/actions-toolkit/packages/core/src/summary.ts new file mode 100644 index 00000000..015f2eea --- /dev/null +++ b/actions-toolkit/packages/core/src/summary.ts @@ -0,0 +1,363 @@ +import {EOL} from 'os' +import {constants, promises} from 'fs' +const {access, appendFile, writeFile} = promises + +export const SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY' +export const SUMMARY_DOCS_URL = + 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary' + +export type SummaryTableRow = (SummaryTableCell | string)[] + +export interface SummaryTableCell { + /** + * Cell content + */ + data: string + /** + * Render cell as header + * (optional) default: false + */ + header?: boolean + /** + * Number of columns the cell extends + * (optional) default: '1' + */ + colspan?: string + /** + * Number of rows the cell extends + * (optional) default: '1' + */ + rowspan?: string +} + +export interface SummaryImageOptions { + /** + * The width of the image in pixels. Must be an integer without a unit. + * (optional) + */ + width?: string + /** + * The height of the image in pixels. Must be an integer without a unit. + * (optional) + */ + height?: string +} + +export interface SummaryWriteOptions { + /** + * Replace all existing content in summary file with buffer contents + * (optional) default: false + */ + overwrite?: boolean +} + +class Summary { + private _buffer: string + private _filePath?: string + + constructor() { + this._buffer = '' + } + + /** + * Finds the summary file path from the environment, rejects if env var is not found or file does not exist + * Also checks r/w permissions. + * + * @returns step summary file path + */ + private async filePath(): Promise { + if (this._filePath) { + return this._filePath + } + + const pathFromEnv = process.env[SUMMARY_ENV_VAR] + if (!pathFromEnv) { + throw new Error( + `Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.` + ) + } + + try { + await access(pathFromEnv, constants.R_OK | constants.W_OK) + } catch { + throw new Error( + `Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.` + ) + } + + this._filePath = pathFromEnv + return this._filePath + } + + /** + * Wraps content in an HTML tag, adding any HTML attributes + * + * @param {string} tag HTML tag to wrap + * @param {string | null} content content within the tag + * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add + * + * @returns {string} content wrapped in HTML element + */ + private wrap( + tag: string, + content: string | null, + attrs: {[attribute: string]: string} = {} + ): string { + const htmlAttrs = Object.entries(attrs) + .map(([key, value]) => ` ${key}="${value}"`) + .join('') + + if (!content) { + return `<${tag}${htmlAttrs}>` + } + + return `<${tag}${htmlAttrs}>${content}` + } + + /** + * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. + * + * @param {SummaryWriteOptions} [options] (optional) options for write operation + * + * @returns {Promise} summary instance + */ + async write(options?: SummaryWriteOptions): Promise { + const overwrite = !!options?.overwrite + const filePath = await this.filePath() + const writeFunc = overwrite ? writeFile : appendFile + await writeFunc(filePath, this._buffer, {encoding: 'utf8'}) + return this.emptyBuffer() + } + + /** + * Clears the summary buffer and wipes the summary file + * + * @returns {Summary} summary instance + */ + async clear(): Promise { + return this.emptyBuffer().write({overwrite: true}) + } + + /** + * Returns the current summary buffer as a string + * + * @returns {string} string of summary buffer + */ + stringify(): string { + return this._buffer + } + + /** + * If the summary buffer is empty + * + * @returns {boolen} true if the buffer is empty + */ + isEmptyBuffer(): boolean { + return this._buffer.length === 0 + } + + /** + * Resets the summary buffer without writing to summary file + * + * @returns {Summary} summary instance + */ + emptyBuffer(): Summary { + this._buffer = '' + return this + } + + /** + * Adds raw text to the summary buffer + * + * @param {string} text content to add + * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) + * + * @returns {Summary} summary instance + */ + addRaw(text: string, addEOL = false): Summary { + this._buffer += text + return addEOL ? this.addEOL() : this + } + + /** + * Adds the operating system-specific end-of-line marker to the buffer + * + * @returns {Summary} summary instance + */ + addEOL(): Summary { + return this.addRaw(EOL) + } + + /** + * Adds an HTML codeblock to the summary buffer + * + * @param {string} code content to render within fenced code block + * @param {string} lang (optional) language to syntax highlight code + * + * @returns {Summary} summary instance + */ + addCodeBlock(code: string, lang?: string): Summary { + const attrs = { + ...(lang && {lang}) + } + const element = this.wrap('pre', this.wrap('code', code), attrs) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML list to the summary buffer + * + * @param {string[]} items list of items to render + * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) + * + * @returns {Summary} summary instance + */ + addList(items: string[], ordered = false): Summary { + const tag = ordered ? 'ol' : 'ul' + const listItems = items.map(item => this.wrap('li', item)).join('') + const element = this.wrap(tag, listItems) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML table to the summary buffer + * + * @param {SummaryTableCell[]} rows table rows + * + * @returns {Summary} summary instance + */ + addTable(rows: SummaryTableRow[]): Summary { + const tableBody = rows + .map(row => { + const cells = row + .map(cell => { + if (typeof cell === 'string') { + return this.wrap('td', cell) + } + + const {header, data, colspan, rowspan} = cell + const tag = header ? 'th' : 'td' + const attrs = { + ...(colspan && {colspan}), + ...(rowspan && {rowspan}) + } + + return this.wrap(tag, data, attrs) + }) + .join('') + + return this.wrap('tr', cells) + }) + .join('') + + const element = this.wrap('table', tableBody) + return this.addRaw(element).addEOL() + } + + /** + * Adds a collapsable HTML details element to the summary buffer + * + * @param {string} label text for the closed state + * @param {string} content collapsable content + * + * @returns {Summary} summary instance + */ + addDetails(label: string, content: string): Summary { + const element = this.wrap('details', this.wrap('summary', label) + content) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML image tag to the summary buffer + * + * @param {string} src path to the image you to embed + * @param {string} alt text description of the image + * @param {SummaryImageOptions} options (optional) addition image attributes + * + * @returns {Summary} summary instance + */ + addImage(src: string, alt: string, options?: SummaryImageOptions): Summary { + const {width, height} = options || {} + const attrs = { + ...(width && {width}), + ...(height && {height}) + } + + const element = this.wrap('img', null, {src, alt, ...attrs}) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML section heading element + * + * @param {string} text heading text + * @param {number | string} [level=1] (optional) the heading level, default: 1 + * + * @returns {Summary} summary instance + */ + addHeading(text: string, level?: number | string): Summary { + const tag = `h${level}` + const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) + ? tag + : 'h1' + const element = this.wrap(allowedTag, text) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML thematic break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addSeparator(): Summary { + const element = this.wrap('hr', null) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML line break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addBreak(): Summary { + const element = this.wrap('br', null) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML blockquote to the summary buffer + * + * @param {string} text quote text + * @param {string} cite (optional) citation url + * + * @returns {Summary} summary instance + */ + addQuote(text: string, cite?: string): Summary { + const attrs = { + ...(cite && {cite}) + } + const element = this.wrap('blockquote', text, attrs) + return this.addRaw(element).addEOL() + } + + /** + * Adds an HTML anchor tag to the summary buffer + * + * @param {string} text link text/content + * @param {string} href hyperlink + * + * @returns {Summary} summary instance + */ + addLink(text: string, href: string): Summary { + const element = this.wrap('a', text, {href}) + return this.addRaw(element).addEOL() + } +} + +const _summary = new Summary() + +/** + * @deprecated use `core.summary` + */ +export const markdownSummary = _summary +export const summary = _summary diff --git a/actions-toolkit/packages/core/src/utils.ts b/actions-toolkit/packages/core/src/utils.ts new file mode 100644 index 00000000..c43f3870 --- /dev/null +++ b/actions-toolkit/packages/core/src/utils.ts @@ -0,0 +1,41 @@ +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import {AnnotationProperties} from './core' +import {CommandProperties} from './command' + +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +export function toCommandValue(input: any): string { + if (input === null || input === undefined) { + return '' + } else if (typeof input === 'string' || input instanceof String) { + return input as string + } + return JSON.stringify(input) +} + +/** + * + * @param annotationProperties + * @returns The command properties to send with the actual annotation command + * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 + */ +export function toCommandProperties( + annotationProperties: AnnotationProperties +): CommandProperties { + if (!Object.keys(annotationProperties).length) { + return {} + } + + return { + title: annotationProperties.title, + file: annotationProperties.file, + line: annotationProperties.startLine, + endLine: annotationProperties.endLine, + col: annotationProperties.startColumn, + endColumn: annotationProperties.endColumn + } +} diff --git a/actions-toolkit/packages/core/tsconfig.json b/actions-toolkit/packages/core/tsconfig.json new file mode 100644 index 00000000..3bb20cf8 --- /dev/null +++ b/actions-toolkit/packages/core/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "declaration": true, + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/exec/LICENSE.md b/actions-toolkit/packages/exec/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/exec/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/exec/README.md b/actions-toolkit/packages/exec/README.md new file mode 100644 index 00000000..53a6bf52 --- /dev/null +++ b/actions-toolkit/packages/exec/README.md @@ -0,0 +1,57 @@ +# `@actions/exec` + +## Usage + +#### Basic + +You can use this package to execute tools in a cross platform way: + +```js +const exec = require('@actions/exec'); + +await exec.exec('node index.js'); +``` + +#### Args + +You can also pass in arg arrays: + +```js +const exec = require('@actions/exec'); + +await exec.exec('node', ['index.js', 'foo=bar']); +``` + +#### Output/options + +Capture output or specify [other options](https://github.com/actions/toolkit/blob/d9347d4ab99fd507c0b9104b2cf79fb44fcc827d/packages/exec/src/interfaces.ts#L5): + +```js +const exec = require('@actions/exec'); + +let myOutput = ''; +let myError = ''; + +const options = {}; +options.listeners = { + stdout: (data: Buffer) => { + myOutput += data.toString(); + }, + stderr: (data: Buffer) => { + myError += data.toString(); + } +}; +options.cwd = './lib'; + +await exec.exec('node', ['index.js', 'foo=bar'], options); +``` + +#### Exec tools not in the PATH + +You can specify the full path for tools not in the PATH: + +```js +const exec = require('@actions/exec'); + +await exec.exec('"/path/to/my-tool"', ['arg1']); +``` diff --git a/actions-toolkit/packages/exec/RELEASES.md b/actions-toolkit/packages/exec/RELEASES.md new file mode 100644 index 00000000..17cfac70 --- /dev/null +++ b/actions-toolkit/packages/exec/RELEASES.md @@ -0,0 +1,22 @@ +# @actions/exec Releases + +### 1.1.1 +- Update `lockfileVersion` to `v2` in `package-lock.json [#1024](https://github.com/actions/toolkit/pull/1024) + +### 1.1.0 + +- [Fix stdline dropping large output](https://github.com/actions/toolkit/pull/773) +- [Add getExecOutput function](https://github.com/actions/toolkit/pull/814) +- [Better error for bad cwd](https://github.com/actions/toolkit/pull/793) + +### 1.0.3 + +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + +### 1.0.2 + +- [Which before invoking tool](https://github.com/actions/toolkit/pull/220) + +### 1.0.0 + +- Initial release \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/exec.test.ts b/actions-toolkit/packages/exec/__tests__/exec.test.ts new file mode 100644 index 00000000..1f83ce4a --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/exec.test.ts @@ -0,0 +1,1214 @@ +import * as exec from '../src/exec' +import * as im from '../src/interfaces' + +import * as childProcess from 'child_process' +import * as fs from 'fs' +import * as os from 'os' +import * as path from 'path' +import * as stream from 'stream' +import * as io from '@actions/io' + +/* eslint-disable @typescript-eslint/unbound-method */ + +const IS_WINDOWS = process.platform === 'win32' + +let outstream: stream.Writable +let errstream: stream.Writable + +describe('@actions/exec', () => { + beforeAll(() => { + io.mkdirP(getTestTemp()) + outstream = fs.createWriteStream(path.join(getTestTemp(), 'my.log')) + errstream = fs.createWriteStream(path.join(getTestTemp(), 'myerr.log')) + }) + + beforeEach(() => { + outstream.write = jest.fn() + errstream.write = jest.fn() + }) + + it('Runs exec successfully with arguments split out', async () => { + const _testExecOptions = getExecOptions() + + let exitCode = 1 + let toolpath = '' + if (IS_WINDOWS) { + toolpath = await io.which('cmd', true) + exitCode = await exec.exec( + `"${toolpath}"`, + ['/c', 'echo', 'hello'], + _testExecOptions + ) + } else { + toolpath = await io.which('ls', true) + exitCode = await exec.exec( + `"${toolpath}"`, + ['-l', '-a'], + _testExecOptions + ) + } + + expect(exitCode).toBe(0) + if (IS_WINDOWS) { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} /c echo hello${os.EOL}` + ) + expect(outstream.write).toBeCalledWith(Buffer.from(`hello${os.EOL}`)) + } else { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} -l -a${os.EOL}` + ) + } + }) + + it('Runs exec successfully with arguments partially split out', async () => { + const _testExecOptions = getExecOptions() + + let exitCode = 1 + let toolpath = '' + if (IS_WINDOWS) { + toolpath = await io.which('cmd', true) + exitCode = await exec.exec( + `"${toolpath}" /c`, + ['echo', 'hello'], + _testExecOptions + ) + } else { + toolpath = await io.which('ls', true) + exitCode = await exec.exec(`"${toolpath}" -l`, ['-a'], _testExecOptions) + } + + expect(exitCode).toBe(0) + if (IS_WINDOWS) { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} /c echo hello${os.EOL}` + ) + expect(outstream.write).toBeCalledWith(Buffer.from(`hello${os.EOL}`)) + } else { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} -l -a${os.EOL}` + ) + } + }) + + it('Runs exec successfully with arguments as part of command line', async () => { + const _testExecOptions = getExecOptions() + + let exitCode = 1 + let toolpath = '' + if (IS_WINDOWS) { + toolpath = await io.which('cmd', true) + exitCode = await exec.exec( + `"${toolpath}" /c echo hello`, + [], + _testExecOptions + ) + } else { + toolpath = await io.which('ls', true) + exitCode = await exec.exec(`"${toolpath}" -l -a`, [], _testExecOptions) + } + + expect(exitCode).toBe(0) + if (IS_WINDOWS) { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} /c echo hello${os.EOL}` + ) + expect(outstream.write).toBeCalledWith(Buffer.from(`hello${os.EOL}`)) + } else { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} -l -a${os.EOL}` + ) + } + }) + + it('Runs exec successfully with command from PATH', async () => { + const execOptions = getExecOptions() + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + let exitCode = 1 + let tool: string + let args: string[] + if (IS_WINDOWS) { + tool = 'cmd' + args = ['/c', 'echo', 'hello'] + } else { + tool = 'sh' + args = ['-c', 'echo hello'] + } + + exitCode = await exec.exec(tool, args, execOptions) + + expect(exitCode).toBe(0) + const rootedTool = await io.which(tool, true) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${rootedTool} ${args.join(' ')}` + ) + expect(output.trim()).toBe(`hello`) + }) + + it('Exec fails with error on bad call', async () => { + const _testExecOptions = getExecOptions() + + let toolpath = '' + let args: string[] = [] + if (IS_WINDOWS) { + toolpath = await io.which('cmd', true) + args = ['/c', 'non-existent'] + } else { + toolpath = await io.which('ls', true) + args = ['-l', 'non-existent'] + } + + let failed = false + + await exec.exec(`"${toolpath}"`, args, _testExecOptions).catch(err => { + failed = true + expect(err.message).toContain( + `The process '${toolpath}' failed with exit code ` + ) + }) + + expect(failed).toBe(true) + if (IS_WINDOWS) { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} /c non-existent${os.EOL}` + ) + } else { + expect(outstream.write).toBeCalledWith( + `[command]${toolpath} -l non-existent${os.EOL}` + ) + } + }) + + it('Succeeds on stderr by default', async () => { + const scriptPath: string = path.join( + __dirname, + 'scripts', + 'stderroutput.js' + ) + const nodePath: string = await io.which('node', true) + + const _testExecOptions = getExecOptions() + + const exitCode = await exec.exec( + `"${nodePath}"`, + [scriptPath], + _testExecOptions + ) + + expect(exitCode).toBe(0) + expect(outstream.write).toBeCalledWith( + Buffer.from('this is output to stderr') + ) + }) + + it('Fails on stderr if specified', async () => { + const scriptPath: string = path.join( + __dirname, + 'scripts', + 'stderroutput.js' + ) + const nodePath: string = await io.which('node', true) + + const _testExecOptions = getExecOptions() + _testExecOptions.failOnStdErr = true + + let failed = false + await exec + .exec(`"${nodePath}"`, [scriptPath], _testExecOptions) + .catch(() => { + failed = true + }) + + expect(failed).toBe(true) + expect(errstream.write).toBeCalledWith( + Buffer.from('this is output to stderr') + ) + }) + + it('Fails when process fails to launch', async () => { + const nodePath: string = await io.which('node', true) + const _testExecOptions = getExecOptions() + _testExecOptions.cwd = path.join(__dirname, 'nosuchdir') + + let failed = false + await exec.exec(`"${nodePath}"`, [], _testExecOptions).catch(() => { + failed = true + }) + + expect(failed).toBe(true) + }) + + it('Handles output callbacks', async () => { + const stdErrPath: string = path.join( + __dirname, + 'scripts', + 'stderroutput.js' + ) + const stdOutPath: string = path.join( + __dirname, + 'scripts', + 'stdoutoutput.js' + ) + const nodePath: string = await io.which('node', true) + let stdoutCalled = false + let stderrCalled = false + + const _testExecOptions = getExecOptions() + _testExecOptions.listeners = { + stdout: (data: Buffer) => { + expect(data).toEqual(Buffer.from('this is output to stdout')) + stdoutCalled = true + }, + stderr: (data: Buffer) => { + expect(data).toEqual(Buffer.from('this is output to stderr')) + stderrCalled = true + } + } + + let exitCode = await exec.exec( + `"${nodePath}"`, + [stdOutPath], + _testExecOptions + ) + expect(exitCode).toBe(0) + exitCode = await exec.exec(`"${nodePath}"`, [stdErrPath], _testExecOptions) + expect(exitCode).toBe(0) + + expect(stdoutCalled).toBeTruthy() + expect(stderrCalled).toBeTruthy() + }) + + it('Handles large stdline', async () => { + const stdlinePath: string = path.join( + __dirname, + 'scripts', + 'stdlineoutput.js' + ) + const nodePath: string = await io.which('node', true) + + const _testExecOptions = getExecOptions() + let largeLine = '' + _testExecOptions.listeners = { + stdline: (line: string) => { + largeLine = line + } + } + + const exitCode = await exec.exec( + `"${nodePath}"`, + [stdlinePath], + _testExecOptions + ) + expect(exitCode).toBe(0) + expect(Buffer.byteLength(largeLine)).toEqual(2 ** 16 + 1) + }) + + it('Handles stdin shell', async () => { + let command: string + if (IS_WINDOWS) { + command = 'wait-for-input.cmd' + } else { + command = 'wait-for-input.sh' + } + + const waitForInput: string = path.join(__dirname, 'scripts', command) + + const _testExecOptions = getExecOptions() + + _testExecOptions.listeners = { + stdout: (data: Buffer) => { + expect(data).toEqual(Buffer.from(`this is my input${os.EOL}`)) + } + } + + _testExecOptions.input = Buffer.from('this is my input') + + const exitCode = await exec.exec(`"${waitForInput}"`, [], _testExecOptions) + expect(exitCode).toBe(0) + }) + + it('Handles stdin js', async () => { + const waitForInput: string = path.join( + __dirname, + 'scripts', + 'wait-for-input.js' + ) + + const _testExecOptions = getExecOptions() + + _testExecOptions.listeners = { + stdout: (data: Buffer) => { + expect(data).toEqual(Buffer.from(`this is my input`)) + } + } + + _testExecOptions.input = Buffer.from('this is my input') + + const nodePath = await io.which('node', true) + const exitCode = await exec.exec(nodePath, [waitForInput], _testExecOptions) + expect(exitCode).toBe(0) + }) + + it('Handles child process holding streams open', async function () { + const semaphorePath = path.join( + getTestTemp(), + 'child-process-semaphore.txt' + ) + fs.writeFileSync(semaphorePath, '') + + const nodePath = await io.which('node', true) + const scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js') + const debugList: string[] = [] + const _testExecOptions = getExecOptions() + _testExecOptions.delay = 500 + _testExecOptions.windowsVerbatimArguments = true + _testExecOptions.listeners = { + debug: (data: string) => { + debugList.push(data) + } + } + + let exitCode: number + if (IS_WINDOWS) { + const toolName: string = await io.which('cmd.exe', true) + const args = [ + '/D', // Disable execution of AutoRun commands from registry. + '/E:ON', // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + '/V:OFF', // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + '/S', // Will cause first and last quote after /C to be stripped. + '/C', + `"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}""` + ] + exitCode = await exec.exec(`"${toolName}"`, args, _testExecOptions) + } else { + const toolName: string = await io.which('bash', true) + const args = ['-c', `node '${scriptPath}' 'file=${semaphorePath}' &`] + + exitCode = await exec.exec(`"${toolName}"`, args, _testExecOptions) + } + + expect(exitCode).toBe(0) + expect( + debugList.filter(x => x.includes('STDIO streams did not close')).length + ).toBe(1) + + fs.unlinkSync(semaphorePath) + }, 10000) // this was timing out on some slower hosted macOS runs at default 5s + + it('Handles child process holding streams open and non-zero exit code', async function () { + const semaphorePath = path.join( + getTestTemp(), + 'child-process-semaphore.txt' + ) + fs.writeFileSync(semaphorePath, '') + + const nodePath = await io.which('node', true) + const scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js') + const debugList: string[] = [] + const _testExecOptions = getExecOptions() + _testExecOptions.delay = 500 + _testExecOptions.windowsVerbatimArguments = true + _testExecOptions.listeners = { + debug: (data: string) => { + debugList.push(data) + } + } + + let toolName: string + let args: string[] + if (IS_WINDOWS) { + toolName = await io.which('cmd.exe', true) + args = [ + '/D', // Disable execution of AutoRun commands from registry. + '/E:ON', // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + '/V:OFF', // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + '/S', // Will cause first and last quote after /C to be stripped. + '/C', + `"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & exit /b 123` + ] + } else { + toolName = await io.which('bash', true) + args = ['-c', `node '${scriptPath}' 'file=${semaphorePath}' & exit 123`] + } + + await exec + .exec(`"${toolName}"`, args, _testExecOptions) + .then(() => { + throw new Error('Should not have succeeded') + }) + .catch(err => { + expect( + err.message.indexOf('failed with exit code 123') + ).toBeGreaterThanOrEqual(0) + }) + + expect( + debugList.filter(x => x.includes('STDIO streams did not close')).length + ).toBe(1) + + fs.unlinkSync(semaphorePath) + }, 10000) // this was timing out on some slower hosted macOS runs at default 5s + + it('Handles child process holding streams open and stderr', async function () { + const semaphorePath = path.join( + getTestTemp(), + 'child-process-semaphore.txt' + ) + fs.writeFileSync(semaphorePath, '') + + const nodePath = await io.which('node', true) + const scriptPath = path.join(__dirname, 'scripts', 'wait-for-file.js') + const debugList: string[] = [] + const _testExecOptions = getExecOptions() + _testExecOptions.delay = 500 + _testExecOptions.failOnStdErr = true + _testExecOptions.windowsVerbatimArguments = true + _testExecOptions.listeners = { + debug: (data: string) => { + debugList.push(data) + } + } + + let toolName: string + let args: string[] + if (IS_WINDOWS) { + toolName = await io.which('cmd.exe', true) + args = [ + '/D', // Disable execution of AutoRun commands from registry. + '/E:ON', // Enable command extensions. Note, command extensions are enabled by default, unless disabled via registry. + '/V:OFF', // Disable delayed environment expansion. Note, delayed environment expansion is disabled by default, unless enabled via registry. + '/S', // Will cause first and last quote after /C to be stripped. + '/C', + `"start "" /B "${nodePath}" "${scriptPath}" "file=${semaphorePath}"" & echo hi 1>&2` + ] + } else { + toolName = await io.which('bash', true) + args = [ + '-c', + `node '${scriptPath}' 'file=${semaphorePath}' & echo hi 1>&2` + ] + } + + await exec + .exec(`"${toolName}"`, args, _testExecOptions) + .then(() => { + throw new Error('Should not have succeeded') + }) + .catch(err => { + expect( + err.message.indexOf( + 'failed because one or more lines were written to the STDERR stream' + ) + ).toBeGreaterThanOrEqual(0) + }) + + expect( + debugList.filter(x => x.includes('STDIO streams did not close')).length + ).toBe(1) + + fs.unlinkSync(semaphorePath) + }) + + it('Exec roots relative tool path using unrooted options.cwd', async () => { + let exitCode: number + let command: string + if (IS_WINDOWS) { + command = './print-args-cmd' // let ToolRunner resolve the extension + } else { + command = './print-args-sh.sh' + } + const execOptions = getExecOptions() + execOptions.cwd = 'scripts' + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const originalCwd = process.cwd() + try { + process.chdir(__dirname) + exitCode = await exec.exec(`${command} hello world`, [], execOptions) + } catch (err) { + process.chdir(originalCwd) + throw err + } + + expect(exitCode).toBe(0) + const toolPath = path.resolve( + __dirname, + execOptions.cwd, + `${command}${IS_WINDOWS ? '.cmd' : ''}` + ) + if (IS_WINDOWS) { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + } else { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${toolPath} hello world` + ) + } + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + + it('Exec roots throws friendly error when bad cwd is specified', async () => { + const execOptions = getExecOptions() + execOptions.cwd = 'nonexistent/path' + + await expect(exec.exec('ls', ['-all'], execOptions)).rejects.toThrowError( + `The cwd: ${execOptions.cwd} does not exist!` + ) + }) + + it('Exec roots does not throw when valid cwd is provided', async () => { + const execOptions = getExecOptions() + execOptions.cwd = './' + + await expect(exec.exec('ls', ['-all'], execOptions)).resolves.toBe(0) + }) + + it('Exec roots relative tool path using rooted options.cwd', async () => { + let command: string + if (IS_WINDOWS) { + command = './print-args-cmd' // let ToolRunner resolve the extension + } else { + command = './print-args-sh.sh' + } + const execOptions = getExecOptions() + execOptions.cwd = path.join(__dirname, 'scripts') + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const exitCode = await exec.exec(`${command} hello world`, [], execOptions) + + expect(exitCode).toBe(0) + const toolPath = path.resolve( + __dirname, + execOptions.cwd, + `${command}${IS_WINDOWS ? '.cmd' : ''}` + ) + if (IS_WINDOWS) { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + } else { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${toolPath} hello world` + ) + } + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + + it('Exec roots relative tool path using process.cwd', async () => { + let exitCode: number + let command: string + if (IS_WINDOWS) { + command = 'scripts/print-args-cmd' // let ToolRunner resolve the extension + } else { + command = 'scripts/print-args-sh.sh' + } + const execOptions = getExecOptions() + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const originalCwd = process.cwd() + try { + process.chdir(__dirname) + exitCode = await exec.exec(`${command} hello world`, [], execOptions) + } catch (err) { + process.chdir(originalCwd) + throw err + } + + expect(exitCode).toBe(0) + const toolPath = path.resolve( + __dirname, + `${command}${IS_WINDOWS ? '.cmd' : ''}` + ) + if (IS_WINDOWS) { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + } else { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${toolPath} hello world` + ) + } + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + + it('correctly outputs for getExecOutput', async () => { + const stdErrPath: string = path.join( + __dirname, + 'scripts', + 'stderroutput.js' + ) + const stdOutPath: string = path.join( + __dirname, + 'scripts', + 'stdoutoutput.js' + ) + const nodePath: string = await io.which('node', true) + + const {exitCode: exitCodeOut, stdout} = await exec.getExecOutput( + `"${nodePath}"`, + [stdOutPath], + getExecOptions() + ) + + expect(exitCodeOut).toBe(0) + expect(stdout).toBe('this is output to stdout') + + const {exitCode: exitCodeErr, stderr} = await exec.getExecOutput( + `"${nodePath}"`, + [stdErrPath], + getExecOptions() + ) + expect(exitCodeErr).toBe(0) + expect(stderr).toBe('this is output to stderr') + }) + + it('correctly outputs for getExecOutput with additional listeners', async () => { + const stdErrPath: string = path.join( + __dirname, + 'scripts', + 'stderroutput.js' + ) + const stdOutPath: string = path.join( + __dirname, + 'scripts', + 'stdoutoutput.js' + ) + + const nodePath: string = await io.which('node', true) + let listenerOut = '' + + const {exitCode: exitCodeOut, stdout} = await exec.getExecOutput( + `"${nodePath}"`, + [stdOutPath], + { + ...getExecOptions(), + listeners: { + stdout: data => { + listenerOut = data.toString() + } + } + } + ) + + expect(exitCodeOut).toBe(0) + expect(stdout).toBe('this is output to stdout') + expect(listenerOut).toBe('this is output to stdout') + + let listenerErr = '' + const {exitCode: exitCodeErr, stderr} = await exec.getExecOutput( + `"${nodePath}"`, + [stdErrPath], + { + ...getExecOptions(), + listeners: { + stderr: data => { + listenerErr = data.toString() + } + } + } + ) + expect(exitCodeErr).toBe(0) + expect(stderr).toBe('this is output to stderr') + expect(listenerErr).toBe('this is output to stderr') + }) + + it('correctly outputs for getExecOutput when total size exceeds buffer size', async () => { + const stdErrPath: string = path.join( + __dirname, + 'scripts', + 'stderroutput.js' + ) + const stdOutPath: string = path.join( + __dirname, + 'scripts', + 'stdoutoutputlarge.js' + ) + + const nodePath: string = await io.which('node', true) + let listenerOut = '' + + const {exitCode: exitCodeOut, stdout} = await exec.getExecOutput( + `"${nodePath}"`, + [stdOutPath], + { + ...getExecOptions(), + listeners: { + stdout: data => { + listenerOut += data.toString() + } + } + } + ) + + expect(exitCodeOut).toBe(0) + expect(Buffer.byteLength(stdout || '', 'utf8')).toBe(2 ** 25) + expect(Buffer.byteLength(listenerOut, 'utf8')).toBe(2 ** 25) + + let listenerErr = '' + const {exitCode: exitCodeErr, stderr} = await exec.getExecOutput( + `"${nodePath}"`, + [stdErrPath], + { + ...getExecOptions(), + listeners: { + stderr: data => { + listenerErr = data.toString() + } + } + } + ) + expect(exitCodeErr).toBe(0) + expect(stderr).toBe('this is output to stderr') + expect(listenerErr).toBe('this is output to stderr') + }) + + it('correctly outputs for getExecOutput with multi-byte characters', async () => { + const stdOutPath: string = path.join( + __dirname, + 'scripts', + 'stdoutputspecial.js' + ) + + const nodePath: string = await io.which('node', true) + let numStdOutBufferCalls = 0 + const {exitCode: exitCodeOut, stdout} = await exec.getExecOutput( + `"${nodePath}"`, + [stdOutPath], + { + ...getExecOptions(), + listeners: { + stdout: () => { + numStdOutBufferCalls += 1 + } + } + } + ) + + expect(exitCodeOut).toBe(0) + //one call for each half of the © character, ensuring it was actually split and not sent together + expect(numStdOutBufferCalls).toBe(2) + expect(stdout).toBe('©') + }) + + if (IS_WINDOWS) { + it('Exec roots relative tool path using process.cwd (Windows path separator)', async () => { + let exitCode: number + const command = 'scripts\\print-args-cmd' // let ToolRunner resolve the extension + const execOptions = getExecOptions() + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const originalCwd = process.cwd() + try { + process.chdir(__dirname) + exitCode = await exec.exec(`${command} hello world`, [], execOptions) + } catch (err) { + process.chdir(originalCwd) + throw err + } + + expect(exitCode).toBe(0) + const toolPath = path.resolve(__dirname, `${command}.cmd`) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + + // Win specific quoting tests + it('execs .exe with verbatim args (Windows)', async () => { + const exePath = process.env.ComSpec + const args: string[] = ['/c', 'echo', 'helloworld', 'hello:"world again"'] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + windowsVerbatimArguments: true, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const exitCode = await exec.exec(`"${exePath}"`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]"${exePath}" /c echo helloworld hello:"world again"` + ) + expect(output.trim()).toBe('helloworld hello:"world again"') + }) + + it('execs .exe with arg quoting (Windows)', async () => { + const exePath = process.env.ComSpec + const args: string[] = [ + '/c', + 'echo', + 'helloworld', + 'hello world', + 'hello:"world again"', + 'hello,world' + ] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const exitCode = await exec.exec(`"${exePath}"`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${exePath} /c echo` + + ` helloworld` + + ` "hello world"` + + ` "hello:\\"world again\\""` + + ` hello,world` + ) + expect(output.trim()).toBe( + 'helloworld' + + ' "hello world"' + + ' "hello:\\"world again\\""' + + ' hello,world' + ) + }) + + it('execs .exe with a space and with verbatim args (Windows)', async () => { + // this test validates the quoting that tool runner adds around the tool path + // when using the windowsVerbatimArguments option. otherwise the target process + // interprets the args as starting after the first space in the tool path. + const exePath = compileArgsExe('print args exe with spaces.exe') + const args: string[] = ['myarg1 myarg2'] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + windowsVerbatimArguments: true, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const exitCode = await exec.exec(`"${exePath}"`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]"${exePath}" myarg1 myarg2` + ) + expect(output.trim()).toBe("args[0]: 'myarg1'\r\nargs[1]: 'myarg2'") + }, 20000) // slower windows runs timeout, so upping timeout to 20s (from default of 5s) + + it('execs .cmd with a space and with verbatim args (Windows)', async () => { + // this test validates the quoting that tool runner adds around the script path. + // otherwise cmd.exe will not be able to resolve the path to the script. + const cmdPath = path.join( + __dirname, + 'scripts', + 'print args cmd with spaces.cmd' + ) + const args: string[] = ['arg1 arg2', 'arg3'] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + windowsVerbatimArguments: true, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const exitCode = await exec.exec(`"${cmdPath}"`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C ""${cmdPath}" arg1 arg2 arg3"` + ) + expect(output.trim()).toBe( + 'args[0]: "arg1"\r\nargs[1]: "arg2"\r\nargs[2]: "arg3"' + ) + }) + + it('execs .cmd with a space and with arg with space (Windows)', async () => { + // this test validates the command is wrapped in quotes (i.e. cmd.exe /S /C ""). + // otherwise the leading quote (around the script with space path) would be stripped + // and cmd.exe would not be able to resolve the script path. + const cmdPath = path.join( + __dirname, + 'scripts', + 'print args cmd with spaces.cmd' + ) + const args: string[] = ['my arg 1', 'my arg 2'] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const exitCode = await exec.exec(`"${cmdPath}"`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C ""${cmdPath}" "my arg 1" "my arg 2""` + ) + expect(output.trim()).toBe( + 'args[0]: "my arg 1"\r\n' + + 'args[1]: "my arg 2"' + ) + }) + + it('execs .cmd from path (Windows)', async () => { + // this test validates whether a .cmd is resolved from the PATH when the extension is not specified + const cmd = 'print-args-cmd' // note, not print-args-cmd.cmd + const cmdPath = path.join(__dirname, 'scripts', `${cmd}.cmd`) + const args: string[] = ['my arg 1', 'my arg 2'] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const originalPath = process.env['Path'] + try { + process.env['Path'] = `${originalPath};${path.dirname(cmdPath)}` + const exitCode = await exec.exec(`${cmd}`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${cmdPath} "my arg 1" "my arg 2""` + ) + expect(output.trim()).toBe( + 'args[0]: "my arg 1"\r\n' + + 'args[1]: "my arg 2"' + ) + } catch (err) { + process.env['Path'] = originalPath + throw err + } + }) + + it('execs .cmd with arg quoting (Windows)', async () => { + // this test validates .cmd quoting rules are applied, not the default libuv rules + const cmdPath = path.join( + __dirname, + 'scripts', + 'print args cmd with spaces.cmd' + ) + const args: string[] = [ + 'helloworld', + 'hello world', + 'hello\tworld', + 'hello&world', + 'hello(world', + 'hello)world', + 'hello[world', + 'hello]world', + 'hello{world', + 'hello}world', + 'hello^world', + 'hello=world', + 'hello;world', + 'hello!world', + "hello'world", + 'hello+world', + 'hello,world', + 'hello`world', + 'hello~world', + 'hello|world', + 'helloworld', + 'hello:"world again"', + 'hello world\\' + ] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const exitCode = await exec.exec(`"${cmdPath}"`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C ""${cmdPath}"` + + ` helloworld` + + ` "hello world"` + + ` "hello\tworld"` + + ` "hello&world"` + + ` "hello(world"` + + ` "hello)world"` + + ` "hello[world"` + + ` "hello]world"` + + ` "hello{world"` + + ` "hello}world"` + + ` "hello^world"` + + ` "hello=world"` + + ` "hello;world"` + + ` "hello!world"` + + ` "hello'world"` + + ` "hello+world"` + + ` "hello,world"` + + ` "hello\`world"` + + ` "hello~world"` + + ` "hello|world"` + + ` "helloworld"` + + ` "hello:""world again"""` + + ` "hello world\\\\"` + + `"` + ) + expect(output.trim()).toBe( + 'args[0]: "helloworld"\r\n' + + 'args[1]: "hello world"\r\n' + + 'args[2]: "hello\tworld"\r\n' + + 'args[3]: "hello&world"\r\n' + + 'args[4]: "hello(world"\r\n' + + 'args[5]: "hello)world"\r\n' + + 'args[6]: "hello[world"\r\n' + + 'args[7]: "hello]world"\r\n' + + 'args[8]: "hello{world"\r\n' + + 'args[9]: "hello}world"\r\n' + + 'args[10]: "hello^world"\r\n' + + 'args[11]: "hello=world"\r\n' + + 'args[12]: "hello;world"\r\n' + + 'args[13]: "hello!world"\r\n' + + 'args[14]: "hello\'world"\r\n' + + 'args[15]: "hello+world"\r\n' + + 'args[16]: "hello,world"\r\n' + + 'args[17]: "hello`world"\r\n' + + 'args[18]: "hello~world"\r\n' + + 'args[19]: "hello|world"\r\n' + + 'args[20]: "hello"\r\n' + + 'args[21]: "hello>world"\r\n' + + 'args[22]: "hello:world again"\r\n' + + 'args[23]: "hello world\\\\"' + ) + }) + } +}) + +function getTestTemp(): string { + return path.join(__dirname, '_temp') +} + +function getExecOptions(): im.ExecOptions { + return { + cwd: __dirname, + env: {}, + silent: false, + failOnStdErr: false, + ignoreReturnCode: false, + outStream: outstream, + errStream: errstream + } +} + +export class StringStream extends stream.Writable { + constructor() { + super() + stream.Writable.call(this) + } + + private contents = '' + + _write( + data: string | Buffer | Uint8Array, + encoding: string, + next: Function + ): void { + this.contents += data + next() + } + + getContents(): string { + return this.contents + } +} + +// function to compile a .NET program on Windows. +const compileExe = (sourceFileName: string, targetFileName: string): string => { + const directory = path.join(getTestTemp(), sourceFileName) + io.mkdirP(directory) + const exePath = path.join(directory, targetFileName) + // short-circuit if already compiled + try { + fs.statSync(exePath) + return exePath + } catch (err) { + if (err.code !== 'ENOENT') { + throw err + } + } + + const sourceFile = path.join(__dirname, 'scripts', sourceFileName) + const cscPath = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe' + fs.statSync(cscPath) + childProcess.execFileSync(cscPath, [ + '/target:exe', + `/out:${exePath}`, + sourceFile + ]) + + return exePath +} + +// function to compile a .NET program that prints the command line args. + +// the helper program is used to validate that command line args are passed correctly. + +const compileArgsExe = (targetFileName: string): string => { + return compileExe('print-args-exe.cs', targetFileName) +} diff --git a/actions-toolkit/packages/exec/__tests__/scripts/print args cmd with spaces.cmd b/actions-toolkit/packages/exec/__tests__/scripts/print args cmd with spaces.cmd new file mode 100644 index 00000000..7f3e4e66 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/print args cmd with spaces.cmd @@ -0,0 +1,12 @@ +@echo off +setlocal +set index=0 + +:check_arg +set arg=%1 +if not defined arg goto :eof +set "arg=%arg:"=%" +echo args[%index%]: "%arg%" +set /a index=%index%+1 +shift +goto check_arg \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/scripts/print-args-cmd.cmd b/actions-toolkit/packages/exec/__tests__/scripts/print-args-cmd.cmd new file mode 100644 index 00000000..7f3e4e66 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/print-args-cmd.cmd @@ -0,0 +1,12 @@ +@echo off +setlocal +set index=0 + +:check_arg +set arg=%1 +if not defined arg goto :eof +set "arg=%arg:"=%" +echo args[%index%]: "%arg%" +set /a index=%index%+1 +shift +goto check_arg \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/scripts/print-args-exe.cs b/actions-toolkit/packages/exec/__tests__/scripts/print-args-exe.cs new file mode 100644 index 00000000..e4934c1d --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/print-args-exe.cs @@ -0,0 +1,14 @@ +using System; +namespace PrintArgs +{ + public static class Program + { + public static void Main(string[] args) + { + for (int i = 0 ; i < args.Length ; i++) + { + Console.WriteLine("args[{0}]: '{1}'", i, args[i]); + } + } + } +} diff --git a/actions-toolkit/packages/exec/__tests__/scripts/print-args-sh.sh b/actions-toolkit/packages/exec/__tests__/scripts/print-args-sh.sh new file mode 100755 index 00000000..40f18cb2 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/print-args-sh.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# store arguments in a special array +args=("$@") +# get number of elements +ELEMENTS=${#args[@]} + +# echo each element +for (( i=0;i<$ELEMENTS;i++)); do + echo "args[$i]: \"${args[${i}]}\"" +done \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/scripts/stderroutput.js b/actions-toolkit/packages/exec/__tests__/scripts/stderroutput.js new file mode 100644 index 00000000..3ff0ac21 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/stderroutput.js @@ -0,0 +1 @@ +process.stderr.write('this is output to stderr'); \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/scripts/stdlineoutput.js b/actions-toolkit/packages/exec/__tests__/scripts/stdlineoutput.js new file mode 100644 index 00000000..1cea8276 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/stdlineoutput.js @@ -0,0 +1,5 @@ +//Default highWaterMark for readable stream buffers us 64K (2^16) +//so we go over that to get more than a buffer's worth +const os = require('os') + +process.stdout.write('a'.repeat(2**16 + 1) + os.EOL); diff --git a/actions-toolkit/packages/exec/__tests__/scripts/stdoutoutput.js b/actions-toolkit/packages/exec/__tests__/scripts/stdoutoutput.js new file mode 100644 index 00000000..d44a6a3d --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/stdoutoutput.js @@ -0,0 +1 @@ +process.stdout.write('this is output to stdout'); \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/scripts/stdoutoutputlarge.js b/actions-toolkit/packages/exec/__tests__/scripts/stdoutoutputlarge.js new file mode 100644 index 00000000..c7f7122d --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/stdoutoutputlarge.js @@ -0,0 +1,3 @@ +//Default highWaterMark for readable stream buffers us 64K (2^16) +//so we go over that to get more than a buffer's worth +process.stdout.write('a\n'.repeat(2**24)); diff --git a/actions-toolkit/packages/exec/__tests__/scripts/stdoutputspecial.js b/actions-toolkit/packages/exec/__tests__/scripts/stdoutputspecial.js new file mode 100644 index 00000000..6a641cd0 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/stdoutputspecial.js @@ -0,0 +1,8 @@ +//first half of © character +process.stdout.write(Buffer.from([0xC2]), (err) => { + //write in the callback so that the second byte is sent separately + setTimeout(() => { + process.stdout.write(Buffer.from([0xA9])) //second half of © character + }, 5000) + +}) diff --git a/actions-toolkit/packages/exec/__tests__/scripts/wait-for-file.js b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-file.js new file mode 100644 index 00000000..867d8e05 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-file.js @@ -0,0 +1,35 @@ +var fs = require('fs'); + +// get the command line args that use the format someArg=someValue +var args = {}; +process.argv.forEach(function (arg) { + var match = arg.match(/^(.+)=(.*)$/); + if (match) { + args[match[1]] = match[2]; + } +}); + +var state = { + file: args.file +}; + +if (!state.file) { + throw new Error('file is not specified'); +} + +state.checkFile = function (s) { + try { + fs.statSync(s.file); + } + catch (err) { + if (err.code == 'ENOENT') { + return; + } + + throw err; + } + + setTimeout(s.checkFile, 100, s); +}; + +state.checkFile(state); \ No newline at end of file diff --git a/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.cmd b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.cmd new file mode 100755 index 00000000..1ba3ec4b --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.cmd @@ -0,0 +1,3 @@ +@echo off +set /p var= +echo %var% diff --git a/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.js b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.js new file mode 100755 index 00000000..82b1e307 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.js @@ -0,0 +1,3 @@ +var fs = require('fs') +var data = fs.readFileSync(0, 'utf-8') +process.stdout.write(data) diff --git a/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.sh b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.sh new file mode 100755 index 00000000..1e8cd850 --- /dev/null +++ b/actions-toolkit/packages/exec/__tests__/scripts/wait-for-input.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +read var +echo $var diff --git a/actions-toolkit/packages/exec/package-lock.json b/actions-toolkit/packages/exec/package-lock.json new file mode 100644 index 00000000..38321784 --- /dev/null +++ b/actions-toolkit/packages/exec/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "@actions/exec", + "version": "1.1.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/exec", + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/io": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", + "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" + } + }, + "dependencies": { + "@actions/io": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", + "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" + } + } +} diff --git a/actions-toolkit/packages/exec/package.json b/actions-toolkit/packages/exec/package.json new file mode 100644 index 00000000..bc4d77a2 --- /dev/null +++ b/actions-toolkit/packages/exec/package.json @@ -0,0 +1,41 @@ +{ + "name": "@actions/exec", + "version": "1.1.1", + "description": "Actions exec lib", + "keywords": [ + "github", + "actions", + "exec" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/exec", + "license": "MIT", + "main": "lib/exec.js", + "types": "lib/exec.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/exec" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/io": "^1.0.1" + } +} diff --git a/actions-toolkit/packages/exec/src/exec.ts b/actions-toolkit/packages/exec/src/exec.ts new file mode 100644 index 00000000..2a67a912 --- /dev/null +++ b/actions-toolkit/packages/exec/src/exec.ts @@ -0,0 +1,90 @@ +import {StringDecoder} from 'string_decoder' +import {ExecOptions, ExecOutput, ExecListeners} from './interfaces' +import * as tr from './toolrunner' + +export {ExecOptions, ExecOutput, ExecListeners} + +/** + * Exec a command. + * Output will be streamed to the live console. + * Returns promise with return code + * + * @param commandLine command to execute (can include additional args). Must be correctly escaped. + * @param args optional arguments for tool. Escaping is handled by the lib. + * @param options optional exec options. See ExecOptions + * @returns Promise exit code + */ +export async function exec( + commandLine: string, + args?: string[], + options?: ExecOptions +): Promise { + const commandArgs = tr.argStringToArray(commandLine) + if (commandArgs.length === 0) { + throw new Error(`Parameter 'commandLine' cannot be null or empty.`) + } + // Path to tool to execute should be first arg + const toolPath = commandArgs[0] + args = commandArgs.slice(1).concat(args || []) + const runner: tr.ToolRunner = new tr.ToolRunner(toolPath, args, options) + return runner.exec() +} + +/** + * Exec a command and get the output. + * Output will be streamed to the live console. + * Returns promise with the exit code and collected stdout and stderr + * + * @param commandLine command to execute (can include additional args). Must be correctly escaped. + * @param args optional arguments for tool. Escaping is handled by the lib. + * @param options optional exec options. See ExecOptions + * @returns Promise exit code, stdout, and stderr + */ + +export async function getExecOutput( + commandLine: string, + args?: string[], + options?: ExecOptions +): Promise { + let stdout = '' + let stderr = '' + + //Using string decoder covers the case where a mult-byte character is split + const stdoutDecoder = new StringDecoder('utf8') + const stderrDecoder = new StringDecoder('utf8') + + const originalStdoutListener = options?.listeners?.stdout + const originalStdErrListener = options?.listeners?.stderr + + const stdErrListener = (data: Buffer): void => { + stderr += stderrDecoder.write(data) + if (originalStdErrListener) { + originalStdErrListener(data) + } + } + + const stdOutListener = (data: Buffer): void => { + stdout += stdoutDecoder.write(data) + if (originalStdoutListener) { + originalStdoutListener(data) + } + } + + const listeners: ExecListeners = { + ...options?.listeners, + stdout: stdOutListener, + stderr: stdErrListener + } + + const exitCode = await exec(commandLine, args, {...options, listeners}) + + //flush any remaining characters + stdout += stdoutDecoder.end() + stderr += stderrDecoder.end() + + return { + exitCode, + stdout, + stderr + } +} diff --git a/actions-toolkit/packages/exec/src/interfaces.ts b/actions-toolkit/packages/exec/src/interfaces.ts new file mode 100644 index 00000000..c86080c4 --- /dev/null +++ b/actions-toolkit/packages/exec/src/interfaces.ts @@ -0,0 +1,72 @@ +import * as stream from 'stream' +/** + * Interface for exec options + */ +export interface ExecOptions { + /** optional working directory. defaults to current */ + cwd?: string + + /** optional envvar dictionary. defaults to current process's env */ + env?: {[key: string]: string} + + /** optional. defaults to false */ + silent?: boolean + + /** optional out stream to use. Defaults to process.stdout */ + outStream?: stream.Writable + + /** optional err stream to use. Defaults to process.stderr */ + errStream?: stream.Writable + + /** optional. whether to skip quoting/escaping arguments if needed. defaults to false. */ + windowsVerbatimArguments?: boolean + + /** optional. whether to fail if output to stderr. defaults to false */ + failOnStdErr?: boolean + + /** optional. defaults to failing on non zero. ignore will not fail leaving it up to the caller */ + ignoreReturnCode?: boolean + + /** optional. How long in ms to wait for STDIO streams to close after the exit event of the process before terminating. defaults to 10000 */ + delay?: number + + /** optional. input to write to the process on STDIN. */ + input?: Buffer + + /** optional. Listeners for output. Callback functions that will be called on these events */ + listeners?: ExecListeners +} + +/** + * Interface for the output of getExecOutput() + */ +export interface ExecOutput { + /**The exit code of the process */ + exitCode: number + + /**The entire stdout of the process as a string */ + stdout: string + + /**The entire stderr of the process as a string */ + stderr: string +} + +/** + * The user defined listeners for an exec call + */ +export interface ExecListeners { + /** A call back for each buffer of stdout */ + stdout?: (data: Buffer) => void + + /** A call back for each buffer of stderr */ + stderr?: (data: Buffer) => void + + /** A call back for each line of stdout */ + stdline?: (data: string) => void + + /** A call back for each line of stderr */ + errline?: (data: string) => void + + /** A call back for each debug log */ + debug?: (data: string) => void +} diff --git a/actions-toolkit/packages/exec/src/toolrunner.ts b/actions-toolkit/packages/exec/src/toolrunner.ts new file mode 100644 index 00000000..a6ca34a2 --- /dev/null +++ b/actions-toolkit/packages/exec/src/toolrunner.ts @@ -0,0 +1,698 @@ +import * as os from 'os' +import * as events from 'events' +import * as child from 'child_process' +import * as path from 'path' +import * as stream from 'stream' +import * as im from './interfaces' +import * as io from '@actions/io' +import * as ioUtil from '@actions/io/lib/io-util' +import {setTimeout} from 'timers' + +/* eslint-disable @typescript-eslint/unbound-method */ + +const IS_WINDOWS = process.platform === 'win32' + +/* + * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way. + */ +export class ToolRunner extends events.EventEmitter { + constructor(toolPath: string, args?: string[], options?: im.ExecOptions) { + super() + + if (!toolPath) { + throw new Error("Parameter 'toolPath' cannot be null or empty.") + } + + this.toolPath = toolPath + this.args = args || [] + this.options = options || {} + } + + private toolPath: string + private args: string[] + private options: im.ExecOptions + + private _debug(message: string): void { + if (this.options.listeners && this.options.listeners.debug) { + this.options.listeners.debug(message) + } + } + + private _getCommandString( + options: im.ExecOptions, + noPrefix?: boolean + ): string { + const toolPath = this._getSpawnFileName() + const args = this._getSpawnArgs(options) + let cmd = noPrefix ? '' : '[command]' // omit prefix when piped to a second tool + if (IS_WINDOWS) { + // Windows + cmd file + if (this._isCmdFile()) { + cmd += toolPath + for (const a of args) { + cmd += ` ${a}` + } + } + // Windows + verbatim + else if (options.windowsVerbatimArguments) { + cmd += `"${toolPath}"` + for (const a of args) { + cmd += ` ${a}` + } + } + // Windows (regular) + else { + cmd += this._windowsQuoteCmdArg(toolPath) + for (const a of args) { + cmd += ` ${this._windowsQuoteCmdArg(a)}` + } + } + } else { + // OSX/Linux - this can likely be improved with some form of quoting. + // creating processes on Unix is fundamentally different than Windows. + // on Unix, execvp() takes an arg array. + cmd += toolPath + for (const a of args) { + cmd += ` ${a}` + } + } + + return cmd + } + + private _processLineBuffer( + data: Buffer, + strBuffer: string, + onLine: (line: string) => void + ): string { + try { + let s = strBuffer + data.toString() + let n = s.indexOf(os.EOL) + + while (n > -1) { + const line = s.substring(0, n) + onLine(line) + + // the rest of the string ... + s = s.substring(n + os.EOL.length) + n = s.indexOf(os.EOL) + } + + return s + } catch (err) { + // streaming lines to console is best effort. Don't fail a build. + this._debug(`error processing line. Failed with error ${err}`) + + return '' + } + } + + private _getSpawnFileName(): string { + if (IS_WINDOWS) { + if (this._isCmdFile()) { + return process.env['COMSPEC'] || 'cmd.exe' + } + } + + return this.toolPath + } + + private _getSpawnArgs(options: im.ExecOptions): string[] { + if (IS_WINDOWS) { + if (this._isCmdFile()) { + let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}` + for (const a of this.args) { + argline += ' ' + argline += options.windowsVerbatimArguments + ? a + : this._windowsQuoteCmdArg(a) + } + + argline += '"' + return [argline] + } + } + + return this.args + } + + private _endsWith(str: string, end: string): boolean { + return str.endsWith(end) + } + + private _isCmdFile(): boolean { + const upperToolPath: string = this.toolPath.toUpperCase() + return ( + this._endsWith(upperToolPath, '.CMD') || + this._endsWith(upperToolPath, '.BAT') + ) + } + + private _windowsQuoteCmdArg(arg: string): string { + // for .exe, apply the normal quoting rules that libuv applies + if (!this._isCmdFile()) { + return this._uvQuoteCmdArg(arg) + } + + // otherwise apply quoting rules specific to the cmd.exe command line parser. + // the libuv rules are generic and are not designed specifically for cmd.exe + // command line parser. + // + // for a detailed description of the cmd.exe command line parser, refer to + // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912 + + // need quotes for empty arg + if (!arg) { + return '""' + } + + // determine whether the arg needs to be quoted + const cmdSpecialChars = [ + ' ', + '\t', + '&', + '(', + ')', + '[', + ']', + '{', + '}', + '^', + '=', + ';', + '!', + "'", + '+', + ',', + '`', + '~', + '|', + '<', + '>', + '"' + ] + let needsQuotes = false + for (const char of arg) { + if (cmdSpecialChars.some(x => x === char)) { + needsQuotes = true + break + } + } + + // short-circuit if quotes not needed + if (!needsQuotes) { + return arg + } + + // the following quoting rules are very similar to the rules that by libuv applies. + // + // 1) wrap the string in quotes + // + // 2) double-up quotes - i.e. " => "" + // + // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately + // doesn't work well with a cmd.exe command line. + // + // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app. + // for example, the command line: + // foo.exe "myarg:""my val""" + // is parsed by a .NET console app into an arg array: + // [ "myarg:\"my val\"" ] + // which is the same end result when applying libuv quoting rules. although the actual + // command line from libuv quoting rules would look like: + // foo.exe "myarg:\"my val\"" + // + // 3) double-up slashes that precede a quote, + // e.g. hello \world => "hello \world" + // hello\"world => "hello\\""world" + // hello\\"world => "hello\\\\""world" + // hello world\ => "hello world\\" + // + // technically this is not required for a cmd.exe command line, or the batch argument parser. + // the reasons for including this as a .cmd quoting rule are: + // + // a) this is optimized for the scenario where the argument is passed from the .cmd file to an + // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule. + // + // b) it's what we've been doing previously (by deferring to node default behavior) and we + // haven't heard any complaints about that aspect. + // + // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be + // escaped when used on the command line directly - even though within a .cmd file % can be escaped + // by using %%. + // + // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts + // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing. + // + // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would + // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the + // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args + // to an external program. + // + // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file. + // % can be escaped within a .cmd file. + let reverse = '"' + let quoteHit = true + for (let i = arg.length; i > 0; i--) { + // walk the string in reverse + reverse += arg[i - 1] + if (quoteHit && arg[i - 1] === '\\') { + reverse += '\\' // double the slash + } else if (arg[i - 1] === '"') { + quoteHit = true + reverse += '"' // double the quote + } else { + quoteHit = false + } + } + + reverse += '"' + return reverse.split('').reverse().join('') + } + + private _uvQuoteCmdArg(arg: string): string { + // Tool runner wraps child_process.spawn() and needs to apply the same quoting as + // Node in certain cases where the undocumented spawn option windowsVerbatimArguments + // is used. + // + // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV, + // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details), + // pasting copyright notice from Node within this function: + // + // Copyright Joyent, Inc. and other Node contributors. All rights reserved. + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to + // deal in the Software without restriction, including without limitation the + // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + // sell copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + // IN THE SOFTWARE. + + if (!arg) { + // Need double quotation for empty argument + return '""' + } + + if (!arg.includes(' ') && !arg.includes('\t') && !arg.includes('"')) { + // No quotation needed + return arg + } + + if (!arg.includes('"') && !arg.includes('\\')) { + // No embedded double quotes or backslashes, so I can just wrap + // quote marks around the whole thing. + return `"${arg}"` + } + + // Expected input/output: + // input : hello"world + // output: "hello\"world" + // input : hello""world + // output: "hello\"\"world" + // input : hello\world + // output: hello\world + // input : hello\\world + // output: hello\\world + // input : hello\"world + // output: "hello\\\"world" + // input : hello\\"world + // output: "hello\\\\\"world" + // input : hello world\ + // output: "hello world\\" - note the comment in libuv actually reads "hello world\" + // but it appears the comment is wrong, it should be "hello world\\" + let reverse = '"' + let quoteHit = true + for (let i = arg.length; i > 0; i--) { + // walk the string in reverse + reverse += arg[i - 1] + if (quoteHit && arg[i - 1] === '\\') { + reverse += '\\' + } else if (arg[i - 1] === '"') { + quoteHit = true + reverse += '\\' + } else { + quoteHit = false + } + } + + reverse += '"' + return reverse.split('').reverse().join('') + } + + private _cloneExecOptions(options?: im.ExecOptions): im.ExecOptions { + options = options || {} + const result: im.ExecOptions = { + cwd: options.cwd || process.cwd(), + env: options.env || process.env, + silent: options.silent || false, + windowsVerbatimArguments: options.windowsVerbatimArguments || false, + failOnStdErr: options.failOnStdErr || false, + ignoreReturnCode: options.ignoreReturnCode || false, + delay: options.delay || 10000 + } + result.outStream = options.outStream || process.stdout + result.errStream = options.errStream || process.stderr + return result + } + + private _getSpawnOptions( + options: im.ExecOptions, + toolPath: string + ): child.SpawnOptions { + options = options || {} + const result = {} + result.cwd = options.cwd + result.env = options.env + result['windowsVerbatimArguments'] = + options.windowsVerbatimArguments || this._isCmdFile() + if (options.windowsVerbatimArguments) { + result.argv0 = `"${toolPath}"` + } + return result + } + + /** + * Exec a tool. + * Output will be streamed to the live console. + * Returns promise with return code + * + * @param tool path to tool to exec + * @param options optional exec options. See ExecOptions + * @returns number + */ + async exec(): Promise { + // root the tool path if it is unrooted and contains relative pathing + if ( + !ioUtil.isRooted(this.toolPath) && + (this.toolPath.includes('/') || + (IS_WINDOWS && this.toolPath.includes('\\'))) + ) { + // prefer options.cwd if it is specified, however options.cwd may also need to be rooted + this.toolPath = path.resolve( + process.cwd(), + this.options.cwd || process.cwd(), + this.toolPath + ) + } + + // if the tool is only a file name, then resolve it from the PATH + // otherwise verify it exists (add extension on Windows if necessary) + this.toolPath = await io.which(this.toolPath, true) + + return new Promise(async (resolve, reject) => { + this._debug(`exec tool: ${this.toolPath}`) + this._debug('arguments:') + for (const arg of this.args) { + this._debug(` ${arg}`) + } + + const optionsNonNull = this._cloneExecOptions(this.options) + if (!optionsNonNull.silent && optionsNonNull.outStream) { + optionsNonNull.outStream.write( + this._getCommandString(optionsNonNull) + os.EOL + ) + } + + const state = new ExecState(optionsNonNull, this.toolPath) + state.on('debug', (message: string) => { + this._debug(message) + }) + + if (this.options.cwd && !(await ioUtil.exists(this.options.cwd))) { + return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`)) + } + + const fileName = this._getSpawnFileName() + const cp = child.spawn( + fileName, + this._getSpawnArgs(optionsNonNull), + this._getSpawnOptions(this.options, fileName) + ) + + let stdbuffer = '' + if (cp.stdout) { + cp.stdout.on('data', (data: Buffer) => { + if (this.options.listeners && this.options.listeners.stdout) { + this.options.listeners.stdout(data) + } + + if (!optionsNonNull.silent && optionsNonNull.outStream) { + optionsNonNull.outStream.write(data) + } + + stdbuffer = this._processLineBuffer( + data, + stdbuffer, + (line: string) => { + if (this.options.listeners && this.options.listeners.stdline) { + this.options.listeners.stdline(line) + } + } + ) + }) + } + + let errbuffer = '' + if (cp.stderr) { + cp.stderr.on('data', (data: Buffer) => { + state.processStderr = true + if (this.options.listeners && this.options.listeners.stderr) { + this.options.listeners.stderr(data) + } + + if ( + !optionsNonNull.silent && + optionsNonNull.errStream && + optionsNonNull.outStream + ) { + const s = optionsNonNull.failOnStdErr + ? optionsNonNull.errStream + : optionsNonNull.outStream + s.write(data) + } + + errbuffer = this._processLineBuffer( + data, + errbuffer, + (line: string) => { + if (this.options.listeners && this.options.listeners.errline) { + this.options.listeners.errline(line) + } + } + ) + }) + } + + cp.on('error', (err: Error) => { + state.processError = err.message + state.processExited = true + state.processClosed = true + state.CheckComplete() + }) + + cp.on('exit', (code: number) => { + state.processExitCode = code + state.processExited = true + this._debug(`Exit code ${code} received from tool '${this.toolPath}'`) + state.CheckComplete() + }) + + cp.on('close', (code: number) => { + state.processExitCode = code + state.processExited = true + state.processClosed = true + this._debug(`STDIO streams have closed for tool '${this.toolPath}'`) + state.CheckComplete() + }) + + state.on('done', (error: Error, exitCode: number) => { + if (stdbuffer.length > 0) { + this.emit('stdline', stdbuffer) + } + + if (errbuffer.length > 0) { + this.emit('errline', errbuffer) + } + + cp.removeAllListeners() + + if (error) { + reject(error) + } else { + resolve(exitCode) + } + }) + + if (this.options.input) { + if (!cp.stdin) { + throw new Error('child process missing stdin') + } + + cp.stdin.end(this.options.input) + } + }) + } +} + +/** + * Convert an arg string to an array of args. Handles escaping + * + * @param argString string of arguments + * @returns string[] array of arguments + */ +export function argStringToArray(argString: string): string[] { + const args: string[] = [] + + let inQuotes = false + let escaped = false + let arg = '' + + function append(c: string): void { + // we only escape double quotes. + if (escaped && c !== '"') { + arg += '\\' + } + + arg += c + escaped = false + } + + for (let i = 0; i < argString.length; i++) { + const c = argString.charAt(i) + + if (c === '"') { + if (!escaped) { + inQuotes = !inQuotes + } else { + append(c) + } + continue + } + + if (c === '\\' && escaped) { + append(c) + continue + } + + if (c === '\\' && inQuotes) { + escaped = true + continue + } + + if (c === ' ' && !inQuotes) { + if (arg.length > 0) { + args.push(arg) + arg = '' + } + continue + } + + append(c) + } + + if (arg.length > 0) { + args.push(arg.trim()) + } + + return args +} + +class ExecState extends events.EventEmitter { + constructor(options: im.ExecOptions, toolPath: string) { + super() + + if (!toolPath) { + throw new Error('toolPath must not be empty') + } + + this.options = options + this.toolPath = toolPath + if (options.delay) { + this.delay = options.delay + } + } + + processClosed = false // tracks whether the process has exited and stdio is closed + processError = '' + processExitCode = 0 + processExited = false // tracks whether the process has exited + processStderr = false // tracks whether stderr was written to + private delay = 10000 // 10 seconds + private done = false + private options: im.ExecOptions + private timeout: NodeJS.Timeout | null = null + private toolPath: string + + CheckComplete(): void { + if (this.done) { + return + } + + if (this.processClosed) { + this._setResult() + } else if (this.processExited) { + this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this) + } + } + + private _debug(message: string): void { + this.emit('debug', message) + } + + private _setResult(): void { + // determine whether there is an error + let error: Error | undefined + if (this.processExited) { + if (this.processError) { + error = new Error( + `There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}` + ) + } else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) { + error = new Error( + `The process '${this.toolPath}' failed with exit code ${this.processExitCode}` + ) + } else if (this.processStderr && this.options.failOnStdErr) { + error = new Error( + `The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream` + ) + } + } + + // clear the timeout + if (this.timeout) { + clearTimeout(this.timeout) + this.timeout = null + } + + this.done = true + this.emit('done', error, this.processExitCode) + } + + private static HandleTimeout(state: ExecState): void { + if (state.done) { + return + } + + if (!state.processClosed && state.processExited) { + const message = `The STDIO streams did not close within ${ + state.delay / 1000 + } seconds of the exit event from process '${ + state.toolPath + }'. This may indicate a child process inherited the STDIO streams and has not yet exited.` + state._debug(message) + } + + state._setResult() + } +} diff --git a/actions-toolkit/packages/exec/tsconfig.json b/actions-toolkit/packages/exec/tsconfig.json new file mode 100644 index 00000000..a8b812a6 --- /dev/null +++ b/actions-toolkit/packages/exec/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/github/LICENSE.md b/actions-toolkit/packages/github/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/github/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/github/README.md b/actions-toolkit/packages/github/README.md new file mode 100644 index 00000000..30e6a68e --- /dev/null +++ b/actions-toolkit/packages/github/README.md @@ -0,0 +1,98 @@ +# `@actions/github` + +> A hydrated Octokit client. + +## Usage + +Returns an authenticated Octokit client that follows the machine [proxy settings](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners) and correctly sets GHES base urls. See https://octokit.github.io/rest.js for the API. + +```js +const github = require('@actions/github'); +const core = require('@actions/core'); + +async function run() { + // This should be a token with access to your repository scoped in as a secret. + // The YML workflow will need to set myToken with the GitHub Secret Token + // myToken: ${{ secrets.GITHUB_TOKEN }} + // https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret + const myToken = core.getInput('myToken'); + + const octokit = github.getOctokit(myToken) + + // You can also pass in additional options as a second parameter to getOctokit + // const octokit = github.getOctokit(myToken, {userAgent: "MyActionVersion1"}); + + const { data: pullRequest } = await octokit.rest.pulls.get({ + owner: 'octokit', + repo: 'rest.js', + pull_number: 123, + mediaType: { + format: 'diff' + } + }); + + console.log(pullRequest); +} + +run(); +``` + +You can also make GraphQL requests. See https://github.com/octokit/graphql.js for the API. + +```js +const result = await octokit.graphql(query, variables); +``` + +Finally, you can get the context of the current action: + +```js +const github = require('@actions/github'); + +const context = github.context; + +const newIssue = await octokit.rest.issues.create({ + ...context.repo, + title: 'New issue!', + body: 'Hello Universe!' +}); +``` + +## Webhook payload typescript definitions + +The npm module `@octokit/webhooks-definitions` provides type definitions for the response payloads. You can cast the payload to these types for better type information. + +First, install the npm module `npm install @octokit/webhooks-definitions` + +Then, assert the type based on the eventName +```ts +import * as core from '@actions/core' +import * as github from '@actions/github' +import {PushEvent} from '@octokit/webhooks-definitions/schema' + +if (github.context.eventName === 'push') { + const pushPayload = github.context.payload as PushEvent + core.info(`The head commit is: ${pushPayload.head_commit}`) +} +``` + +## Extending the Octokit instance +`@octokit/core` now supports the [plugin architecture](https://github.com/octokit/core.js#plugins). You can extend the GitHub instance using plugins. + +For example, using the `@octokit/plugin-enterprise-server` you can now access enterprise admin apis on GHES instances. + +```ts +import { GitHub, getOctokitOptions } from '@actions/github/lib/utils' +import { enterpriseServer220Admin } from '@octokit/plugin-enterprise-server' + +const octokit = GitHub.plugin(enterpriseServer220Admin) +// or override some of the default values as well +// const octokit = GitHub.plugin(enterpriseServer220Admin).defaults({userAgent: "MyNewUserAgent"}) + +const myToken = core.getInput('myToken'); +const myOctokit = new octokit(getOctokitOptions(token)) +// Create a new user +myOctokit.rest.enterpriseAdmin.createUser({ + login: "testuser", + email: "testuser@test.com", +}); +``` diff --git a/actions-toolkit/packages/github/RELEASES.md b/actions-toolkit/packages/github/RELEASES.md new file mode 100644 index 00000000..c0e1dc5a --- /dev/null +++ b/actions-toolkit/packages/github/RELEASES.md @@ -0,0 +1,69 @@ +# @actions/github Releases + +### 6.0.0 +- Support the latest Octokit in @actions/github [#1553](https://github.com/actions/toolkit/pull/1553) + - Drop support of NodeJS v14, v16 + +### 5.1.1 +- Export default octokit options [#1188](https://github.com/actions/toolkit/pull/1188) + +### 5.1.0 +- Add additionalPlugins parameter to getOctokit method [#1181](https://github.com/actions/toolkit/pull/1181) +- Dependency updates [#1180](https://github.com/actions/toolkit/pull/1180) + + +### 5.0.3 +- - Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087) + +### 5.0.2 +- Update to v2.0.0 of `@actions/http-client` + +### 5.0.1 +- [Update Octokit Dependencies](https://github.com/actions/toolkit/pull/1037) +### 5.0.0 +- [Update @actions/github to include latest octokit definitions](https://github.com/actions/toolkit/pull/783) +- [Add urls to context](https://github.com/actions/toolkit/pull/794) + +### 4.0.0 +- [Add execution state information to context](https://github.com/actions/toolkit/pull/499) +- [Update Octokit Dependencies with some api breaking changes](https://github.com/actions/toolkit/pull/498) + - The full list of api changes are [here](https://github.com/octokit/plugin-rest-endpoint-methods.js/releases/tag/v4.0.0) + - `GitHub.plugin()` no longer supports an array as first argument. Multiple args must be passed in instead. + +### 3.0.0 +- [Swap to @octokit/core and use plugins to leverage lastest octokit apis](https://github.com/actions/toolkit/pull/453) +- [Add comment field to payload context](https://github.com/actions/toolkit/pull/375) + +### 2.2.0 + +- [Support GHES: Use GITHUB_API_URL and GITHUB_GRAPHQL_URL to determine baseUrl](https://github.com/actions/toolkit/pull/449) + +### 2.1.1 + +- [Use import {Octokit}](https://github.com/actions/toolkit/pull/332) +- [Check proxy bypass before setting proxy agent](https://github.com/actions/toolkit/pull/320) + +### 2.1.0 + +- [Octokit client follows proxy settings](https://github.com/actions/toolkit/pull/314) +- [Fix issue number for pull request comment events](https://github.com/actions/toolkit/pull/311) + +### 2.0.1 + +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + +### 2.0.0 + +- Upgrade Octokit version to 4.x to include typescript types [#228](https://github.com/actions/toolkit/pull/228) + +### 1.1.0 + +- Accept Octokit.Options in the GitHub constructor [#113](https://github.com/actions/toolkit/pull/113) + +### 1.0.1 + +- Simplify WebPack configs by removing dynamic require - [#101](https://github.com/actions/toolkit/pull/101) + +### 1.0.0 + +- Initial release diff --git a/actions-toolkit/packages/github/__tests__/__snapshots__/lib.test.ts.snap b/actions-toolkit/packages/github/__tests__/__snapshots__/lib.test.ts.snap new file mode 100644 index 00000000..a7f3608c --- /dev/null +++ b/actions-toolkit/packages/github/__tests__/__snapshots__/lib.test.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`@actions/context return error for context.repo when repository doesn't exist 1`] = `"context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"`; diff --git a/actions-toolkit/packages/github/__tests__/github.proxy.test.ts b/actions-toolkit/packages/github/__tests__/github.proxy.test.ts new file mode 100644 index 00000000..318591e9 --- /dev/null +++ b/actions-toolkit/packages/github/__tests__/github.proxy.test.ts @@ -0,0 +1,109 @@ +import * as http from 'http' +import * as https from 'https' +import {createProxy} from 'proxy' + +// Default values are set when the module is imported, so we need to set proxy first. +const proxyUrl = 'http://127.0.0.1:8081' +const originalProxyUrl = process.env['https_proxy'] +process.env['https_proxy'] = proxyUrl +// eslint-disable-next-line import/first +import {getOctokit} from '../src/github' + +describe('@actions/github', () => { + let proxyConnects: string[] + let proxyServer: http.Server + let first = true + + beforeAll(async () => { + // Start proxy server + proxyServer = createProxy() + await new Promise(resolve => { + const port = Number(proxyUrl.split(':')[2]) + proxyServer.listen(port, () => resolve()) + }) + proxyServer.on('connect', req => { + proxyConnects.push(req.url ?? '') + }) + }) + + beforeEach(() => { + proxyConnects = [] + }) + + afterAll(async () => { + // Stop proxy server + await new Promise(resolve => { + proxyServer.once('close', () => resolve()) + proxyServer.close() + }) + + if (originalProxyUrl) { + process.env['https_proxy'] = originalProxyUrl + } + }) + + it('basic REST client with proxy', async () => { + const token = getToken() + if (!token) { + return + } + + const octokit = getOctokit(token) + const branch = await octokit.rest.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'main' + }) + expect(branch.data.name).toBe('main') + expect(proxyConnects).toEqual(['api.github.com:443']) + }) + + it('basic GraphQL client with proxy', async () => { + const token = getToken() + if (!token) { + return + } + process.env['https_proxy'] = proxyUrl + const octokit = getOctokit(token) + + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toEqual(['api.github.com:443']) + }) + + it('should only use default agent if one is not provided', async () => { + const token = getToken() + if (!token) { + return + } + + // Valid token + const octokit = getOctokit(token, { + request: { + agent: new https.Agent() + } + }) + const branch = await octokit.rest.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'main' + }) + expect(branch.data.name).toBe('main') + expect(proxyConnects).toHaveLength(0) + }) + + function getToken(): string { + const token = process.env['GITHUB_TOKEN'] || '' + if (!token && first) { + /* eslint-disable-next-line no-console */ + console.warn( + 'Skipping GitHub tests. Set $GITHUB_TOKEN to run REST client and GraphQL client tests' + ) + first = false + } + + return token + } +}) diff --git a/actions-toolkit/packages/github/__tests__/github.test.ts b/actions-toolkit/packages/github/__tests__/github.test.ts new file mode 100644 index 00000000..50fdc74a --- /dev/null +++ b/actions-toolkit/packages/github/__tests__/github.test.ts @@ -0,0 +1,156 @@ +import * as http from 'http' +import {createProxy} from 'proxy' +import {getOctokit} from '../src/github' +import {GitHub, getOctokitOptions} from '../src/utils' + +describe('@actions/github', () => { + const proxyUrl = 'http://127.0.0.1:8080' + const originalProxyUrl = process.env['https_proxy'] + let proxyConnects: string[] + let proxyServer: http.Server + let first = true + + beforeAll(async () => { + // Start proxy server + proxyServer = createProxy() + await new Promise(resolve => { + const port = Number(proxyUrl.split(':')[2]) + proxyServer.listen(port, () => resolve()) + }) + proxyServer.on('connect', req => { + proxyConnects.push(req.url ?? '') + }) + }) + + beforeEach(() => { + delete process.env['https_proxy'] + proxyConnects = [] + }) + + afterAll(async () => { + // Stop proxy server + await new Promise(resolve => { + proxyServer.once('close', () => resolve()) + proxyServer.close() + }) + + if (originalProxyUrl) { + process.env['https_proxy'] = originalProxyUrl + } + }) + + it('basic REST client', async () => { + const token = getToken() + if (!token) { + return + } + const octokit = new GitHub(getOctokitOptions(token)) + const branch = await octokit.rest.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'main' + }) + expect(branch.data.name).toBe('main') + expect(proxyConnects).toHaveLength(0) + }) + + it('basic getOctokit client', async () => { + const token = getToken() + if (!token) { + return + } + const octokit = getOctokit(token) + const branch = await octokit.rest.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'main' + }) + expect(branch.data.name).toBe('main') + expect(proxyConnects).toHaveLength(0) + }) + + it('basic REST client with custom auth', async () => { + const token = getToken() + if (!token) { + return + } + + // Valid token + let octokit = new GitHub({auth: `token ${token}`}) + const branch = await octokit.rest.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'main' + }) + expect(branch.data.name).toBe('main') + expect(proxyConnects).toHaveLength(0) + + // Invalid token + octokit = new GitHub({auth: `token asdf`}) + let failed = false + try { + await octokit.rest.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'main' + }) + } catch (err) { + failed = true + } + expect(failed).toBeTruthy() + }) + + it('basic GraphQL client', async () => { + const token = getToken() + if (!token) { + return + } + + const octokit = getOctokit(token) + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toHaveLength(0) + }) + + it('basic GraphQL client with custom auth', async () => { + const token = getToken() + if (!token) { + return + } + + // Valid token + let octokit = getOctokit(token) + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toHaveLength(0) + + // Invalid token + octokit = new GitHub({auth: `token asdf`}) + let failed = false + try { + await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + } catch (err) { + failed = true + } + expect(failed).toBeTruthy() + }) + + function getToken(): string { + const token = process.env['GITHUB_TOKEN'] || '' + if (!token && first) { + /* eslint-disable-next-line no-console */ + console.warn( + 'Skipping GitHub tests. Set $GITHUB_TOKEN to run REST client and GraphQL client tests' + ) + first = false + } + + return token + } +}) diff --git a/actions-toolkit/packages/github/__tests__/lib.test.ts b/actions-toolkit/packages/github/__tests__/lib.test.ts new file mode 100644 index 00000000..ea339adc --- /dev/null +++ b/actions-toolkit/packages/github/__tests__/lib.test.ts @@ -0,0 +1,81 @@ +import * as path from 'path' +import {Context} from '../src/context' + +/* eslint-disable @typescript-eslint/no-require-imports */ +/* eslint-disable @typescript-eslint/no-var-requires */ + +describe('@actions/context', () => { + let context: Context + + beforeEach(() => { + process.env.GITHUB_EVENT_PATH = path.join(__dirname, 'payload.json') + process.env.GITHUB_REPOSITORY = 'actions/toolkit' + context = new Context() + }) + + it('returns the payload object', () => { + expect(context.payload).toEqual(require('./payload.json')) + }) + + it('returns an empty payload if the GITHUB_EVENT_PATH environment variable is falsey', () => { + delete process.env.GITHUB_EVENT_PATH + + context = new Context() + expect(context.payload).toEqual({}) + }) + + it('returns attributes from the GITHUB_REPOSITORY', () => { + expect(context.repo).toEqual({owner: 'actions', repo: 'toolkit'}) + }) + + it('returns attributes from the repository payload', () => { + delete process.env.GITHUB_REPOSITORY + + context.payload.repository = { + name: 'test', + owner: {login: 'user'} + } + expect(context.repo).toEqual({owner: 'user', repo: 'test'}) + }) + + it("return error for context.repo when repository doesn't exist", () => { + delete process.env.GITHUB_REPOSITORY + + context.payload.repository = undefined + expect(() => context.repo).toThrowErrorMatchingSnapshot() + }) + + it('returns issue attributes from the repository', () => { + expect(context.issue).toEqual({ + owner: 'actions', + repo: 'toolkit', + number: 1 + }) + }) + + it('works with pull_request payloads', () => { + delete process.env.GITHUB_REPOSITORY + context.payload = { + pull_request: {number: 2}, + repository: {owner: {login: 'user'}, name: 'test'} + } + expect(context.issue).toEqual({ + number: 2, + owner: 'user', + repo: 'test' + }) + }) + + it('works with payload.number payloads', () => { + delete process.env.GITHUB_REPOSITORY + context.payload = { + number: 2, + repository: {owner: {login: 'user'}, name: 'test'} + } + expect(context.issue).toEqual({ + number: 2, + owner: 'user', + repo: 'test' + }) + }) +}) diff --git a/actions-toolkit/packages/github/__tests__/payload.json b/actions-toolkit/packages/github/__tests__/payload.json new file mode 100644 index 00000000..b7711b71 --- /dev/null +++ b/actions-toolkit/packages/github/__tests__/payload.json @@ -0,0 +1,15 @@ +{ + "action": "opened", + "repository": { + "owner": { + "login": "user" + }, + "name": "test" + }, + "issue": { + "number": 1 + }, + "sender": { + "type": "User" + } +} \ No newline at end of file diff --git a/actions-toolkit/packages/github/jest.config.js b/actions-toolkit/packages/github/jest.config.js new file mode 100644 index 00000000..ae4910cd --- /dev/null +++ b/actions-toolkit/packages/github/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + clearMocks: true, + moduleFileExtensions: ['js', 'ts'], + testEnvironment: 'node', + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.ts$': 'ts-jest' + }, + verbose: true +} \ No newline at end of file diff --git a/actions-toolkit/packages/github/package-lock.json b/actions-toolkit/packages/github/package-lock.json new file mode 100644 index 00000000..a33f8b41 --- /dev/null +++ b/actions-toolkit/packages/github/package-lock.json @@ -0,0 +1,640 @@ +{ + "name": "@actions/github", + "version": "6.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/github", + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "@actions/http-client": "^2.2.0", + "@octokit/core": "^5.0.1", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + }, + "devDependencies": { + "proxy": "^2.1.1" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz", + "integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz", + "integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz", + "integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==", + "dependencies": { + "@octokit/types": "^12.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "dependencies": { + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz", + "integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==", + "dependencies": { + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.0.0.tgz", + "integrity": "sha512-16VkwE2v6rXU+/gBsYC62M8lKWOphY5Lg4wpjYnVE9Zbu0J6IwiT5kILoj1YOB53XLmcJR+Nqp8DmifOPY4H3g==", + "dependencies": { + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/request": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz", + "integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==", + "dependencies": { + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "dependencies": { + "@octokit/types": "^12.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", + "dependencies": { + "@octokit/openapi-types": "^19.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/args": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "dev": true, + "dependencies": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/basic-auth-parser": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz", + "integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==", + "dev": true + }, + "node_modules/before-after-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", + "integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==" + }, + "node_modules/camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/proxy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz", + "integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==", + "dev": true, + "dependencies": { + "args": "^5.0.3", + "basic-auth-parser": "0.0.2-1", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/undici": { + "version": "5.25.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz", + "integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + }, + "dependencies": { + "@actions/http-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz", + "integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==", + "requires": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==" + }, + "@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==" + }, + "@octokit/core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz", + "integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==", + "requires": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz", + "integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==", + "requires": { + "@octokit/types": "^12.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "requires": { + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==" + }, + "@octokit/plugin-paginate-rest": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz", + "integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==", + "requires": { + "@octokit/types": "^12.0.0" + } + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.0.0.tgz", + "integrity": "sha512-16VkwE2v6rXU+/gBsYC62M8lKWOphY5Lg4wpjYnVE9Zbu0J6IwiT5kILoj1YOB53XLmcJR+Nqp8DmifOPY4H3g==", + "requires": { + "@octokit/types": "^12.0.0" + } + }, + "@octokit/request": { + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz", + "integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==", + "requires": { + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "requires": { + "@octokit/types": "^12.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/types": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", + "requires": { + "@octokit/openapi-types": "^19.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "args": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "dev": true, + "requires": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + } + }, + "basic-auth-parser": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz", + "integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==", + "dev": true + }, + "before-after-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz", + "integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw==" + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "dev": true + }, + "mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "proxy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz", + "integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==", + "dev": true, + "requires": { + "args": "^5.0.3", + "basic-auth-parser": "0.0.2-1", + "debug": "^4.3.4" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "undici": { + "version": "5.25.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz", + "integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==", + "requires": { + "@fastify/busboy": "^2.0.0" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + } +} diff --git a/actions-toolkit/packages/github/package.json b/actions-toolkit/packages/github/package.json new file mode 100644 index 00000000..20ae2302 --- /dev/null +++ b/actions-toolkit/packages/github/package.json @@ -0,0 +1,49 @@ +{ + "name": "@actions/github", + "version": "6.0.0", + "description": "Actions github lib", + "keywords": [ + "github", + "actions" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/github", + "license": "MIT", + "main": "lib/github.js", + "types": "lib/github.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/github" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "jest", + "build": "tsc", + "format": "prettier --write **/*.ts", + "format-check": "prettier --check **/*.ts", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/http-client": "^2.2.0", + "@octokit/core": "^5.0.1", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + }, + "devDependencies": { + "proxy": "^2.1.1" + } +} \ No newline at end of file diff --git a/actions-toolkit/packages/github/src/context.ts b/actions-toolkit/packages/github/src/context.ts new file mode 100644 index 00000000..691c9690 --- /dev/null +++ b/actions-toolkit/packages/github/src/context.ts @@ -0,0 +1,83 @@ +// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/main/src/context.ts +import {WebhookPayload} from './interfaces' +import {readFileSync, existsSync} from 'fs' +import {EOL} from 'os' + +export class Context { + /** + * Webhook payload object that triggered the workflow + */ + payload: WebhookPayload + + eventName: string + sha: string + ref: string + workflow: string + action: string + actor: string + job: string + runAttempt: number + runNumber: number + runId: number + apiUrl: string + serverUrl: string + graphqlUrl: string + + /** + * Hydrate the context from the environment + */ + constructor() { + this.payload = {} + if (process.env.GITHUB_EVENT_PATH) { + if (existsSync(process.env.GITHUB_EVENT_PATH)) { + this.payload = JSON.parse( + readFileSync(process.env.GITHUB_EVENT_PATH, {encoding: 'utf8'}) + ) + } else { + const path = process.env.GITHUB_EVENT_PATH + process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${EOL}`) + } + } + this.eventName = process.env.GITHUB_EVENT_NAME as string + this.sha = process.env.GITHUB_SHA as string + this.ref = process.env.GITHUB_REF as string + this.workflow = process.env.GITHUB_WORKFLOW as string + this.action = process.env.GITHUB_ACTION as string + this.actor = process.env.GITHUB_ACTOR as string + this.job = process.env.GITHUB_JOB as string + this.runAttempt = parseInt(process.env.GITHUB_RUN_ATTEMPT as string, 10) + this.runNumber = parseInt(process.env.GITHUB_RUN_NUMBER as string, 10) + this.runId = parseInt(process.env.GITHUB_RUN_ID as string, 10) + this.apiUrl = process.env.GITHUB_API_URL ?? `https://api.github.com` + this.serverUrl = process.env.GITHUB_SERVER_URL ?? `https://github.com` + this.graphqlUrl = + process.env.GITHUB_GRAPHQL_URL ?? `https://api.github.com/graphql` + } + + get issue(): {owner: string; repo: string; number: number} { + const payload = this.payload + + return { + ...this.repo, + number: (payload.issue || payload.pull_request || payload).number + } + } + + get repo(): {owner: string; repo: string} { + if (process.env.GITHUB_REPOSITORY) { + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/') + return {owner, repo} + } + + if (this.payload.repository) { + return { + owner: this.payload.repository.owner.login, + repo: this.payload.repository.name + } + } + + throw new Error( + "context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'" + ) + } +} diff --git a/actions-toolkit/packages/github/src/github.ts b/actions-toolkit/packages/github/src/github.ts new file mode 100644 index 00000000..b4ed7133 --- /dev/null +++ b/actions-toolkit/packages/github/src/github.ts @@ -0,0 +1,22 @@ +import * as Context from './context' +import {GitHub, getOctokitOptions} from './utils' + +// octokit + plugins +import {OctokitOptions, OctokitPlugin} from '@octokit/core/dist-types/types' + +export const context = new Context.Context() + +/** + * Returns a hydrated octokit ready to use for GitHub Actions + * + * @param token the repo PAT or GITHUB_TOKEN + * @param options other options to set + */ +export function getOctokit( + token: string, + options?: OctokitOptions, + ...additionalPlugins: OctokitPlugin[] +): InstanceType { + const GitHubWithPlugins = GitHub.plugin(...additionalPlugins) + return new GitHubWithPlugins(getOctokitOptions(token, options)) +} diff --git a/actions-toolkit/packages/github/src/interfaces.ts b/actions-toolkit/packages/github/src/interfaces.ts new file mode 100644 index 00000000..8ab08bfe --- /dev/null +++ b/actions-toolkit/packages/github/src/interfaces.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface PayloadRepository { + [key: string]: any + full_name?: string + name: string + owner: { + [key: string]: any + login: string + name?: string + } + html_url?: string +} + +export interface WebhookPayload { + [key: string]: any + repository?: PayloadRepository + issue?: { + [key: string]: any + number: number + html_url?: string + body?: string + } + pull_request?: { + [key: string]: any + number: number + html_url?: string + body?: string + } + sender?: { + [key: string]: any + type: string + } + action?: string + installation?: { + id: number + [key: string]: any + } + comment?: { + id: number + [key: string]: any + } +} diff --git a/actions-toolkit/packages/github/src/internal/utils.ts b/actions-toolkit/packages/github/src/internal/utils.ts new file mode 100644 index 00000000..2c520789 --- /dev/null +++ b/actions-toolkit/packages/github/src/internal/utils.ts @@ -0,0 +1,44 @@ +import * as http from 'http' +import * as httpClient from '@actions/http-client' +import {OctokitOptions} from '@octokit/core/dist-types/types' +import {ProxyAgent, fetch} from 'undici' + +export function getAuthString( + token: string, + options: OctokitOptions +): string | undefined { + if (!token && !options.auth) { + throw new Error('Parameter token or opts.auth is required') + } else if (token && options.auth) { + throw new Error('Parameters token and opts.auth may not both be specified') + } + + return typeof options.auth === 'string' ? options.auth : `token ${token}` +} + +export function getProxyAgent(destinationUrl: string): http.Agent { + const hc = new httpClient.HttpClient() + return hc.getAgent(destinationUrl) +} + +export function getProxyAgentDispatcher( + destinationUrl: string +): ProxyAgent | undefined { + const hc = new httpClient.HttpClient() + return hc.getAgentDispatcher(destinationUrl) +} + +export function getProxyFetch(destinationUrl): typeof fetch { + const httpDispatcher = getProxyAgentDispatcher(destinationUrl) + const proxyFetch: typeof fetch = async (url, opts) => { + return fetch(url, { + ...opts, + dispatcher: httpDispatcher + }) + } + return proxyFetch +} + +export function getApiBaseUrl(): string { + return process.env['GITHUB_API_URL'] || 'https://api.github.com' +} diff --git a/actions-toolkit/packages/github/src/utils.ts b/actions-toolkit/packages/github/src/utils.ts new file mode 100644 index 00000000..0c50d6ac --- /dev/null +++ b/actions-toolkit/packages/github/src/utils.ts @@ -0,0 +1,45 @@ +import * as Context from './context' +import * as Utils from './internal/utils' + +// octokit + plugins +import {Octokit} from '@octokit/core' +import {OctokitOptions} from '@octokit/core/dist-types/types' +import {restEndpointMethods} from '@octokit/plugin-rest-endpoint-methods' +import {paginateRest} from '@octokit/plugin-paginate-rest' + +export const context = new Context.Context() + +const baseUrl = Utils.getApiBaseUrl() +export const defaults: OctokitOptions = { + baseUrl, + request: { + agent: Utils.getProxyAgent(baseUrl), + fetch: Utils.getProxyFetch(baseUrl) + } +} + +export const GitHub = Octokit.plugin( + restEndpointMethods, + paginateRest +).defaults(defaults) + +/** + * Convience function to correctly format Octokit Options to pass into the constructor. + * + * @param token the repo PAT or GITHUB_TOKEN + * @param options other options to set + */ +export function getOctokitOptions( + token: string, + options?: OctokitOptions +): OctokitOptions { + const opts = Object.assign({}, options || {}) // Shallow clone - don't mutate the object provided by the caller + + // Auth + const auth = Utils.getAuthString(token, opts) + if (auth) { + opts.auth = auth + } + + return opts +} diff --git a/actions-toolkit/packages/github/tsconfig.json b/actions-toolkit/packages/github/tsconfig.json new file mode 100644 index 00000000..a8b812a6 --- /dev/null +++ b/actions-toolkit/packages/github/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/glob/LICENSE.md b/actions-toolkit/packages/glob/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/glob/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/glob/README.md b/actions-toolkit/packages/glob/README.md new file mode 100644 index 00000000..9575beaa --- /dev/null +++ b/actions-toolkit/packages/glob/README.md @@ -0,0 +1,113 @@ +# `@actions/glob` + +## Usage + +### Basic + +You can use this package to search for files matching glob patterns. + +Relative paths and absolute paths are both allowed. Relative paths are rooted against the current working directory. + +```js +const glob = require('@actions/glob'); + +const patterns = ['**/tar.gz', '**/tar.bz'] +const globber = await glob.create(patterns.join('\n')) +const files = await globber.glob() +``` + +### Opt out of following symbolic links + +```js +const glob = require('@actions/glob'); + +const globber = await glob.create('**', {followSymbolicLinks: false}) +const files = await globber.glob() +``` + +### Iterator + +When dealing with a large amount of results, consider iterating the results as they are returned: + +```js +const glob = require('@actions/glob'); + +const globber = await glob.create('**') +for await (const file of globber.globGenerator()) { + console.log(file) +} +``` + +## Recommended action inputs + +Glob follows symbolic links by default. Following is often appropriate unless deleting files. + +Users may want to opt-out from following symbolic links for other reasons. For example, +excessive amounts of symbolic links can create the appearance of very, very many files +and slow the search. + +When an action allows a user to specify input patterns, it is generally recommended to +allow users to opt-out from following symbolic links. + +Snippet from `action.yml`: + +```yaml +inputs: + files: + description: 'Files to print' + required: true + follow-symbolic-links: + description: 'Indicates whether to follow symbolic links' + default: true +``` + +And corresponding toolkit consumption: + +```js +const core = require('@actions/core') +const glob = require('@actions/glob') + +const globOptions = { + followSymbolicLinks: core.getInput('follow-symbolic-links').toUpper() !== 'FALSE' +} +const globber = glob.create(core.getInput('files'), globOptions) +for await (const file of globber.globGenerator()) { + console.log(file) +} +``` + +## Patterns + +### Glob behavior + +Patterns `*`, `?`, `[...]`, `**` (globstar) are supported. + +With the following behaviors: +- File names that begin with `.` may be included in the results +- Case insensitive on Windows +- Directory separator `/` and `\` both supported on Windows + +### Tilde expansion + +Supports basic tilde expansion, for current user HOME replacement only. + +Example: +- `~` may expand to /Users/johndoe +- `~/foo` may expand to /Users/johndoe/foo + +### Comments + +Patterns that begin with `#` are treated as comments. + +### Exclude patterns + +Leading `!` changes the meaning of an include pattern to exclude. + +Multiple leading `!` flips the meaning. + +### Escaping + +Wrapping special characters in `[]` can be used to escape literal glob characters +in a file name. For example the literal file name `hello[a-z]` can be escaped as `hello[[]a-z]`. + +On Linux/macOS `\` is also treated as an escape character. diff --git a/actions-toolkit/packages/glob/RELEASES.md b/actions-toolkit/packages/glob/RELEASES.md new file mode 100644 index 00000000..84aa06be --- /dev/null +++ b/actions-toolkit/packages/glob/RELEASES.md @@ -0,0 +1,26 @@ +# @actions/glob Releases + +### 0.4.0 +- Pass in the current workspace as a parameter to HashFiles [#1318](https://github.com/actions/toolkit/pull/1318) + +### 0.3.0 +- Added a `verbose` option to HashFiles [#1052](https://github.com/actions/toolkit/pull/1052/files) + +### 0.2.1 +- Update `lockfileVersion` to `v2` in `package-lock.json [#1023](https://github.com/actions/toolkit/pull/1023) + +### 0.2.0 +- [Added the hashFiles function to Glob](https://github.com/actions/toolkit/pull/830) +- [Added an option to filter out directories](https://github.com/actions/toolkit/pull/728) + +### 0.1.2 + +- [Fix bug where files were matched incorrectly](https://github.com/actions/toolkit/pull/805) + +### 0.1.1 + +- Update @actions/core version +### 0.1.0 + +- Initial release + diff --git a/actions-toolkit/packages/glob/__tests__/hash-files.test.ts b/actions-toolkit/packages/glob/__tests__/hash-files.test.ts new file mode 100644 index 00000000..51ed3ee6 --- /dev/null +++ b/actions-toolkit/packages/glob/__tests__/hash-files.test.ts @@ -0,0 +1,131 @@ +import * as io from '../../io/src/io' +import * as path from 'path' +import {hashFiles} from '../src/glob' +import {promises as fs} from 'fs' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * These test focus on the ability of globber to find files + * and not on the pattern matching aspect + */ +describe('globber', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('basic hashfiles test', async () => { + const root = path.join(getTestTemp(), 'basic-hashfiles') + await fs.mkdir(path.join(root), {recursive: true}) + await fs.writeFile(path.join(root, 'test.txt'), 'test file content') + const hash = await hashFiles(`${root}/*`) + expect(hash).toEqual( + 'd8a411e8f8643821bed189e627ff57151918aa554c00c10b31c693ab2dded273' + ) + }) + + it('basic hashfiles no match should return empty string', async () => { + const root = path.join(getTestTemp(), 'empty-hashfiles') + const hash = await hashFiles(`${root}/*`) + expect(hash).toEqual('') + }) + + it('followSymbolicLinks defaults to true', async () => { + const root = path.join( + getTestTemp(), + 'defaults-to-follow-symbolic-links-true' + ) + await fs.mkdir(path.join(root, 'realdir'), {recursive: true}) + await fs.writeFile( + path.join(root, 'realdir', 'file.txt'), + 'test file content' + ) + await createSymlinkDir( + path.join(root, 'realdir'), + path.join(root, 'symDir') + ) + const testPath = path.join(root, `symDir`) + const hash = await hashFiles(testPath) + expect(hash).toEqual( + 'd8a411e8f8643821bed189e627ff57151918aa554c00c10b31c693ab2dded273' + ) + }) + + const emptyDirectory = '' + it('followSymbolicLinks set to true', async () => { + const root = path.join(getTestTemp(), 'set-to-true') + await fs.mkdir(path.join(root, 'realdir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realdir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realdir'), + path.join(root, 'symDir') + ) + const testPath = path.join(root, `symDir`) + const hash = await hashFiles(testPath, emptyDirectory, { + followSymbolicLinks: true + }) + expect(hash).toEqual( + 'd8a411e8f8643821bed189e627ff57151918aa554c00c10b31c693ab2dded273' + ) + }) + + it('followSymbolicLinks set to false', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + // /symDir -> /folder-a + const root = path.join(getTestTemp(), 'set-to-false') + await fs.mkdir(path.join(root, 'realdir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realdir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realdir'), + path.join(root, 'symDir') + ) + const testPath = path.join(root, 'symdir') + const hash = await hashFiles(testPath, emptyDirectory, { + followSymbolicLinks: false + }) + expect(hash).toEqual('') + }) + + it('multipath test basic', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + // /symDir -> /folder-a + const root = path.join(getTestTemp(), 'set-to-false') + await fs.mkdir(path.join(root, 'dir1'), {recursive: true}) + await fs.mkdir(path.join(root, 'dir2'), {recursive: true}) + await fs.writeFile( + path.join(root, 'dir1', 'testfile1.txt'), + 'test file content' + ) + await fs.writeFile( + path.join(root, 'dir2', 'testfile2.txt'), + 'test file content' + ) + const testPath = `${path.join(root, 'dir1')}\n${path.join(root, 'dir2')}` + const hash = await hashFiles(testPath) + expect(hash).toEqual( + '4e911ea5824830b6a3ec096c7833d5af8381c189ffaa825c3503a5333a73eadc' + ) + }) +}) + +function getTestTemp(): string { + return path.join(__dirname, '_temp', 'hash_files') +} + +/** + * Creates a symlink directory on OSX/Linux, and a junction point directory on Windows. + * A symlink directory is not created on Windows since it requires an elevated context. + */ +async function createSymlinkDir(real: string, link: string): Promise { + if (IS_WINDOWS) { + await fs.symlink(real, link, 'junction') + } else { + await fs.symlink(real, link) + } +} diff --git a/actions-toolkit/packages/glob/__tests__/internal-globber.test.ts b/actions-toolkit/packages/glob/__tests__/internal-globber.test.ts new file mode 100644 index 00000000..4ae670fe --- /dev/null +++ b/actions-toolkit/packages/glob/__tests__/internal-globber.test.ts @@ -0,0 +1,897 @@ +import * as child from 'child_process' +import * as io from '../../io/src/io' +import * as os from 'os' +import * as path from 'path' +import {Globber, DefaultGlobber} from '../src/internal-globber' +import {GlobOptions} from '../src/internal-glob-options' +import {promises as fs} from 'fs' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * These test focus on the ability of globber to find files + * and not on the pattern matching aspect + */ +describe('globber', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('captures cwd', async () => { + // Create the following layout: + // first-cwd + // first-cwd/the-correct-file + // second-cwd + // second-cwd/the-wrong-file + const root = path.join(getTestTemp(), 'preserves-cwd') + await fs.mkdir(path.join(root, 'first-cwd'), {recursive: true}) + await fs.writeFile( + path.join(root, 'first-cwd', 'the-correct-file.txt'), + 'test file content' + ) + await fs.mkdir(path.join(root, 'second-cwd'), {recursive: true}) + await fs.writeFile( + path.join(root, 'second-cwd', 'the-wrong-file.txt'), + 'test file content' + ) + + const originalCwd = process.cwd() + try { + process.chdir(path.join(root, 'first-cwd')) + const globber = await DefaultGlobber.create('*') + process.chdir(path.join(root, 'second-cwd')) + expect(globber.getSearchPaths()).toEqual([path.join(root, 'first-cwd')]) + const itemPaths = await globber.glob() + expect(itemPaths).toEqual([ + path.join(root, 'first-cwd', 'the-correct-file.txt') + ]) + } finally { + process.chdir(originalCwd) + } + }) + + it('defaults to followSymbolicLinks=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + // /symDir -> /folder-a + const root = path.join( + getTestTemp(), + 'defaults-to-follow-symbolic-links-true' + ) + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'folder-a'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file'), + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('defaults to implicitDescendants=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + const root = path.join( + getTestTemp(), + 'defaults-to-implicit-descendants-true' + ) + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file') + ]) + }) + + it('defaults to matchDirectories=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + const root = path.join(getTestTemp(), 'defaults-to-match-directories-true') + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file') + ]) + }) + + it('does not match file with trailing slash when implicitDescendants=true', async () => { + // Create the following layout: + // + // /file + const root = path.join( + getTestTemp(), + 'defaults-to-implicit-descendants-true' + ) + + const filePath = path.join(root, 'file') + + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(filePath, 'test file content') + + const itemPaths = await glob(`${filePath}/`, {}) + expect(itemPaths).toEqual([]) + }) + + it('defaults to omitBrokenSymbolicLinks=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + // /symDir -> /no-such + const root = path.join( + getTestTemp(), + 'defaults-to-omit-broken-symbolic-links-true' + ) + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'no-such'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file') + ]) + }) + + it('detects cycle when followSymbolicLinks=true', async () => { + // Create the following layout: + // + // /file + // /symDir -> + const root = path.join(getTestTemp(), 'detects-cycle-when-follow-true') + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(path.join(root, 'file'), 'test file content') + await createSymlinkDir(root, path.join(root, 'symDir')) + + const itemPaths = await glob(root, {followSymbolicLinks: true}) + expect(itemPaths).toEqual([root, path.join(root, 'file')]) + }) + + it('detects deep cycle starting from middle when followSymbolicLinks=true', async () => { + // Create the following layout: + // + // /file-under-root + // /folder-a + // /folder-a/file-under-a + // /folder-a/folder-b + // /folder-a/folder-b/file-under-b + // /folder-a/folder-b/folder-c + // /folder-a/folder-b/folder-c/file-under-c + // /folder-a/folder-b/folder-c/sym-folder -> + const root = path.join( + getTestTemp(), + 'detects-deep-cycle-starting-from-middle-when-follow-true' + ) + await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), { + recursive: true + }) + await fs.writeFile( + path.join(root, 'file-under-root'), + 'test file under root contents' + ) + await fs.writeFile( + path.join(root, 'folder-a', 'file-under-a'), + 'test file under a contents' + ) + await fs.writeFile( + path.join(root, 'folder-a', 'folder-b', 'file-under-b'), + 'test file under b contents' + ) + await fs.writeFile( + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'file-under-c'), + 'test file under c contents' + ) + await createSymlinkDir( + root, + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'sym-folder') + ) + await fs.stat( + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'file-under-root' + ) + ) + + const itemPaths = await glob(path.join(root, 'folder-a', 'folder-b'), { + followSymbolicLinks: true + }) + expect(itemPaths).toEqual([ + path.join(root, 'folder-a', 'folder-b'), + path.join(root, 'folder-a', 'folder-b', 'file-under-b'), + path.join(root, 'folder-a', 'folder-b', 'folder-c'), + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'file-under-c'), + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'sym-folder'), + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'file-under-root' + ), + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'folder-a' + ), + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'folder-a', + 'file-under-a' + ) + ]) + await unlinkSymlinkDir(itemPaths) + }) + + it('detects cycle starting from symlink when followSymbolicLinks=true', async () => { + // Create the following layout: + // + // /file + // /symDir -> + const root: string = path.join( + getTestTemp(), + 'detects-cycle-starting-from-symlink-when-follow-true' + ) + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(path.join(root, 'file'), 'test file content') + await createSymlinkDir(root, path.join(root, 'symDir')) + await fs.stat(path.join(root, 'symDir')) + + const itemPaths = await glob(path.join(root, 'symDir'), { + followSymbolicLinks: true + }) + expect(itemPaths).toEqual([ + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('does not follow symlink when followSymbolicLinks=false', async () => { + // Create the following layout: + // + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join( + getTestTemp(), + 'does-not-follow-symlink-when-follow-false' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {followSymbolicLinks: false}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir') + ]) + }) + + it('does not follow symlink when search path is symlink and followSymbolicLinks=false', async () => { + // Create the following layout: + // realDir + // realDir/file + // symDir -> realDir + const root = path.join( + getTestTemp(), + 'does-not-follow-symlink-when-search-path-is-symlink-and-follow-false' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(path.join(root, 'symDir'), { + followSymbolicLinks: false + }) + expect(itemPaths).toEqual([path.join(root, 'symDir')]) + }) + + it('does not return broken symlink when follow-true and omit-true', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join( + getTestTemp(), + 'does-not-return-broken-symlink-when-follow-true-and-omit-true' + ) + await fs.mkdir(root, {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'brokenSym') + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {followSymbolicLinks: true}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('does not return broken symlink when search path is broken symlink and followSymbolicLinks=true', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'does-not-return-broken-symlink-when-search-path-is-broken-symlink-and-follow-true' + ) + await fs.mkdir(root, {recursive: true}) + const brokenSymPath = path.join(root, 'brokenSym') + await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) + await fs.lstat(brokenSymPath) + + const itemPaths = await glob(brokenSymPath, {followSymbolicLinks: true}) + expect(itemPaths).toEqual([]) + }) + + it('does not return directories when match directories false', async () => { + // Create the following layout: + // /file-1 + // /dir-1 + // /dir-1/file-2 + // /dir-1/dir-2 + // /dir-1/dir-2/file-3 + const root = path.join( + getTestTemp(), + 'does-not-return-directories-when-match-directories-false' + ) + await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true}) + await fs.writeFile(path.join(root, 'file-1'), '') + await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '') + await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '') + + const pattern = `${root}${path.sep}**` + expect( + await glob(pattern, { + matchDirectories: false + }) + ).toEqual([ + path.join(root, 'dir-1', 'dir-2', 'file-3'), + path.join(root, 'dir-1', 'file-2'), + path.join(root, 'file-1') + ]) + }) + + it('does not search paths that are not partial matches', async () => { + // Create the following layout: + // + // /realDir + // /realDir/nested + // /realDir/nested/file + // /realDir2 + // /realDir2/nested2 + // /realDir2/nested2/symDir -> /noSuch + const root = path.join( + getTestTemp(), + 'does-not-search-paths-that-are-not-partial-matches' + ) + await fs.mkdir(path.join(root, 'realDir', 'nested'), {recursive: true}) + await fs.writeFile( + path.join(root, 'realDir', 'nested', 'file'), + 'test file content' + ) + await fs.mkdir(path.join(root, 'realDir2', 'nested2'), {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'realDir2', 'nested2', 'symDir') + ) + + const options: GlobOptions = { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + } + + // Should throw + try { + await glob(`${root}/*Dir*/*nested*/*`, options) + throw new Error('should not reach here') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/i) + } + + // Not partial match + let itemPaths = await glob(`${root}/*Dir/*nested*/*`, options) + expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')]) + + // Not partial match + itemPaths = await glob(`${root}/*Dir*/*nested/*`, options) + expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')]) + }) + + it('does not throw for broken symlinks that are not matches or partial matches when followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => { + // Create the following layout: + // + // /realDir + // /realDir/file + // /symDir -> /noSuch + const root = path.join( + getTestTemp(), + 'does-not-throw-for-broken-symlinks-that-are-not-matches-or-partial-matches-when-follow-true-and-omit-false' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'symDir')) + + const options: GlobOptions = { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + } + + // Match should throw + try { + await glob(`${root}/*`, options) + throw new Error('should not reach here') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/i) + } + + // Partial match should throw + try { + await glob(`${root}/*/*`, options) + throw new Error('should not reach here') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/i) + } + + // Not match or partial match + const itemPaths = await glob(`${root}/*eal*/*`, options) + expect(itemPaths).toEqual([path.join(root, 'realDir', 'file')]) + }) + + it('follows symlink when follow-symbolic-links=true', async () => { + // Create the following layout: + // + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join(getTestTemp(), 'follows-symlink') + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {followSymbolicLinks: true}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('follows symlink when search path is symlink and follow-symbolic-links=true', async () => { + // Create the following layout: + // realDir + // realDir/file + // symDir -> realDir + const root = path.join( + getTestTemp(), + 'follows-symlink-when-search-path-is-symlink-and-follow-true' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(path.join(root, 'symDir'), { + followSymbolicLinks: true + }) + expect(itemPaths).toEqual([ + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('returns broken symlink when followSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join( + getTestTemp(), + 'returns-broken-symlink-when-follow-false' + ) + await fs.mkdir(root, {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'brokenSym') + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {followSymbolicLinks: false}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'brokenSym'), + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir') + ]) + }) + + it('returns broken symlink when search path is broken symlink and followSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'returns-broken-symlink-when-search-path-is-broken-symlink-and-follow-false' + ) + await fs.mkdir(root, {recursive: true}) + const brokenSymPath = path.join(root, 'brokenSym') + await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) + + const itemPaths = await glob(brokenSymPath, {followSymbolicLinks: false}) + expect(itemPaths).toEqual([brokenSymPath]) + }) + + it('returns depth first', async () => { + // Create the following layout: + // /a-file + // /b-folder + // /b-folder/a-file + // /b-folder/b-folder + // /b-folder/b-folder/file + // /b-folder/c-file + // /c-file + const root = path.join(getTestTemp(), 'returns-depth-first') + await fs.mkdir(path.join(root, 'b-folder', 'b-folder'), {recursive: true}) + await fs.writeFile(path.join(root, 'a-file'), 'test a-file content') + await fs.writeFile( + path.join(root, 'b-folder', 'a-file'), + 'test b-folder/a-file content' + ) + await fs.writeFile( + path.join(root, 'b-folder', 'b-folder', 'file'), + 'test b-folder/b-folder/file content' + ) + await fs.writeFile( + path.join(root, 'b-folder', 'c-file'), + 'test b-folder/c-file content' + ) + await fs.writeFile(path.join(root, 'c-file'), 'test c-file content') + + const itemPaths = await glob(root) + expect(itemPaths).toEqual([ + root, + path.join(root, 'a-file'), + path.join(root, 'b-folder'), + path.join(root, 'b-folder', 'a-file'), + path.join(root, 'b-folder', 'b-folder'), + path.join(root, 'b-folder', 'b-folder', 'file'), + path.join(root, 'b-folder', 'c-file'), + path.join(root, 'c-file') + ]) + }) + + it('returns descendants', async () => { + // Create the following layout: + // /file-1 + // /dir-1 + // /dir-1/file-2 + // /dir-1/dir-2 + // /dir-1/dir-2/file-3 + const root = path.join(getTestTemp(), 'returns-descendants') + await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true}) + await fs.writeFile(path.join(root, 'file-1'), '') + await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '') + await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '') + + // When pattern ends with `/**/` + let pattern = `${root}${path.sep}**${path.sep}` + expect( + await glob(pattern, { + implicitDescendants: false + }) + ).toHaveLength(3) // sanity check + expect(await glob(pattern)).toEqual([ + root, + path.join(root, 'dir-1'), + path.join(root, 'dir-1', 'dir-2'), + path.join(root, 'dir-1', 'dir-2', 'file-3'), + path.join(root, 'dir-1', 'file-2'), + path.join(root, 'file-1') + ]) + + // When pattern ends with something other than `/**/` + pattern = `${root}${path.sep}**${path.sep}dir-?` + expect( + await glob(pattern, { + implicitDescendants: false + }) + ).toHaveLength(2) // sanity check + expect(await glob(pattern)).toEqual([ + path.join(root, 'dir-1'), + path.join(root, 'dir-1', 'dir-2'), + path.join(root, 'dir-1', 'dir-2', 'file-3'), + path.join(root, 'dir-1', 'file-2') + ]) + }) + + it('returns directories only when trailing slash and implicit descendants false', async () => { + // Create the following layout: + // /file-1 + // /dir-1 + // /dir-1/file-2 + // /dir-1/dir-2 + // /dir-1/dir-2/file-3 + const root = path.join( + getTestTemp(), + 'returns-directories-only-when-trailing-slash-and-implicit-descendants-false' + ) + await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true}) + await fs.writeFile(path.join(root, 'file-1'), '') + await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '') + await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '') + + const pattern = `${root}${path.sep}**${path.sep}` + expect(await glob(pattern)).toHaveLength(6) // sanity check + expect( + await glob(pattern, { + implicitDescendants: false + }) + ).toEqual([ + root, + path.join(root, 'dir-1'), + path.join(root, 'dir-1', 'dir-2') + ]) + }) + + it('returns empty when search path does not exist', async () => { + const itemPaths = await glob(path.join(getTestTemp(), 'nosuch')) + expect(itemPaths).toEqual([]) + }) + + it('returns hidden files', async () => { + // Create the following layout: + // + // /.emptyFolder + // /.file + // /.folder + // /.folder/file + const root = path.join(getTestTemp(), 'returns-hidden-files') + await createHiddenDirectory(path.join(root, '.emptyFolder')) + await createHiddenDirectory(path.join(root, '.folder')) + await createHiddenFile(path.join(root, '.file'), 'test .file content') + await fs.writeFile( + path.join(root, '.folder', 'file'), + 'test .folder/file content' + ) + + const itemPaths = await glob(root) + expect(itemPaths).toEqual([ + root, + path.join(root, '.emptyFolder'), + path.join(root, '.file'), + path.join(root, '.folder'), + path.join(root, '.folder', 'file') + ]) + }) + + it('returns normalized paths', async () => { + // Create the following layout: + // /hello/world.txt + const root: string = path.join(getTestTemp(), 'returns-normalized-paths') + await fs.mkdir(path.join(root, 'hello'), {recursive: true}) + await fs.writeFile(path.join(root, 'hello', 'world.txt'), '') + + const itemPaths = await glob( + `${root}${path.sep}${path.sep}${path.sep}hello` + ) + expect(itemPaths).toEqual([ + path.join(root, 'hello'), + path.join(root, 'hello', 'world.txt') + ]) + }) + + it('skips comments', async () => { + const searchPaths = await getSearchPaths( + `#aaa/*${os.EOL}/foo/*${os.EOL}#bbb/*${os.EOL}/bar/*` + ) + const drive = IS_WINDOWS ? process.cwd().substr(0, 2) : '' + expect(searchPaths).toEqual([ + IS_WINDOWS ? `${drive}\\foo` : '/foo', + IS_WINDOWS ? `${drive}\\bar` : '/bar' + ]) + }) + + it('skips empty lines', async () => { + const searchPaths = await getSearchPaths( + `${os.EOL}${os.EOL}/foo/*${os.EOL}${os.EOL}/bar/*${os.EOL}/baz/**${os.EOL}` + ) + const drive = IS_WINDOWS ? process.cwd().substr(0, 2) : '' + expect(searchPaths).toEqual([ + IS_WINDOWS ? `${drive}\\foo` : '/foo', + IS_WINDOWS ? `${drive}\\bar` : '/bar', + IS_WINDOWS ? `${drive}\\baz` : '/baz' + ]) + }) + + it('throws when match broken symlink and followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'throws-when-match-broken-symlink-and-follow-true-and-omit-false' + ) + await fs.mkdir(root, {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'brokenSym') + ) + + try { + await glob(root, { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + }) + throw new Error('Expected tl.find to throw') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/) + } + }) + + it('throws when search path is broken symlink and followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'throws-when-search-path-is-broken-symlink-and-follow-true-and-omit-false' + ) + await fs.mkdir(root, {recursive: true}) + const brokenSymPath = path.join(root, 'brokenSym') + await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) + await fs.lstat(brokenSymPath) + + try { + await glob(brokenSymPath, { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + }) + throw new Error('Expected tl.find to throw') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/) + } + }) +}) + +async function createHiddenDirectory(dir: string): Promise { + if (!path.basename(dir).match(/^\./)) { + throw new Error(`Expected dir '${dir}' to start with '.'.`) + } + + await fs.mkdir(dir, {recursive: true}) + if (IS_WINDOWS) { + const result = child.spawnSync('attrib.exe', ['+H', dir]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error( + `Failed to set hidden attribute for directory '${dir}'. ${message}` + ) + } + } +} + +async function createHiddenFile(file: string, content: string): Promise { + if (!path.basename(file).match(/^\./)) { + throw new Error(`Expected dir '${file}' to start with '.'.`) + } + + await fs.mkdir(path.dirname(file), {recursive: true}) + await fs.writeFile(file, content) + + if (IS_WINDOWS) { + const result = child.spawnSync('attrib.exe', ['+H', file]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error( + `Failed to set hidden attribute for file '${file}'. ${message}` + ) + } + } +} + +function getTestTemp(): string { + return path.join(__dirname, '_temp', 'glob') +} +/** + * Deletes a symlink directory + */ +async function unlinkSymlinkDir(links: string[]): Promise { + for (const link of links) { + await fs.rm(link, {recursive: true, force: true}) + } +} + +/** + * Creates a symlink directory on OSX/Linux, and a junction point directory on Windows. + * A symlink directory is not created on Windows since it requires an elevated context. + */ +async function createSymlinkDir(real: string, link: string): Promise { + if (IS_WINDOWS) { + await fs.symlink(real, link, 'junction') + } else { + await fs.symlink(real, link) + } +} + +async function getSearchPaths(patterns: string): Promise { + const globber: Globber = await DefaultGlobber.create(patterns) + return globber.getSearchPaths() +} + +async function glob( + patterns: string, + options?: GlobOptions +): Promise { + const globber: Globber = await DefaultGlobber.create(patterns, options) + return await globber.glob() +} diff --git a/actions-toolkit/packages/glob/__tests__/internal-path-helper.test.ts b/actions-toolkit/packages/glob/__tests__/internal-path-helper.test.ts new file mode 100644 index 00000000..48a10da3 --- /dev/null +++ b/actions-toolkit/packages/glob/__tests__/internal-path-helper.test.ts @@ -0,0 +1,640 @@ +import * as pathHelper from '../src/internal-path-helper' + +const IS_WINDOWS = process.platform === 'win32' + +describe('path-helper', () => { + it('dirname interprets directory name from paths', () => { + assertDirectoryName('', '.') + assertDirectoryName('.', '.') + assertDirectoryName('..', '.') + assertDirectoryName('hello', '.') + assertDirectoryName('hello/', '.') + assertDirectoryName('hello/world', 'hello') + + if (IS_WINDOWS) { + // Removes redundant slashes + assertDirectoryName('C:\\\\hello\\\\\\world\\\\', 'C:\\hello') + assertDirectoryName('C://hello///world//', 'C:\\hello') + // Relative root: + assertDirectoryName('\\hello\\\\world\\\\again\\\\', '\\hello\\world') + assertDirectoryName('/hello///world//again//', '\\hello\\world') + // UNC: + assertDirectoryName('\\\\hello\\world\\again\\', '\\\\hello\\world') + assertDirectoryName( + '\\\\hello\\\\\\world\\\\again\\\\', + '\\\\hello\\world' + ) + assertDirectoryName( + '\\\\\\hello\\\\\\world\\\\again\\\\', + '\\\\hello\\world' + ) + assertDirectoryName( + '\\\\\\\\hello\\\\\\world\\\\again\\\\', + '\\\\hello\\world' + ) + assertDirectoryName('//hello///world//again//', '\\\\hello\\world') + assertDirectoryName('///hello///world//again//', '\\\\hello\\world') + assertDirectoryName('/////hello///world//again//', '\\\\hello\\world') + // Relative: + assertDirectoryName('hello\\world', 'hello') + + // Directory trimming + assertDirectoryName('a:/hello', 'a:\\') + assertDirectoryName('z:/hello', 'z:\\') + assertDirectoryName('A:/hello', 'A:\\') + assertDirectoryName('Z:/hello', 'Z:\\') + assertDirectoryName('C:/', 'C:\\') + assertDirectoryName('C:/hello', 'C:\\') + assertDirectoryName('C:/hello/', 'C:\\') + assertDirectoryName('C:/hello/world', 'C:\\hello') + assertDirectoryName('C:/hello/world/', 'C:\\hello') + assertDirectoryName('C:', 'C:') + assertDirectoryName('C:hello', 'C:') + assertDirectoryName('C:hello/', 'C:') + assertDirectoryName('C:hello/world', 'C:hello') + assertDirectoryName('C:hello/world/', 'C:hello') + assertDirectoryName('/', '\\') + assertDirectoryName('/hello', '\\') + assertDirectoryName('/hello/', '\\') + assertDirectoryName('/hello/world', '\\hello') + assertDirectoryName('/hello/world/', '\\hello') + assertDirectoryName('\\', '\\') + assertDirectoryName('\\hello', '\\') + assertDirectoryName('\\hello\\', '\\') + assertDirectoryName('\\hello\\world', '\\hello') + assertDirectoryName('\\hello\\world\\', '\\hello') + assertDirectoryName('//hello', '\\\\hello') + assertDirectoryName('//hello/', '\\\\hello') + assertDirectoryName('//hello/world', '\\\\hello\\world') + assertDirectoryName('//hello/world/', '\\\\hello\\world') + assertDirectoryName('\\\\hello', '\\\\hello') + assertDirectoryName('\\\\hello\\', '\\\\hello') + assertDirectoryName('\\\\hello\\world', '\\\\hello\\world') + assertDirectoryName('\\\\hello\\world\\', '\\\\hello\\world') + assertDirectoryName('//hello/world/again', '\\\\hello\\world') + assertDirectoryName('//hello/world/again/', '\\\\hello\\world') + assertDirectoryName('hello/world/', 'hello') + assertDirectoryName('hello/world/again', 'hello\\world') + assertDirectoryName('../../hello', '..\\..') + } else { + // Should not converts slashes + assertDirectoryName('/hello\\world', '/') + assertDirectoryName('/hello\\world/', '/') + assertDirectoryName('\\\\hello\\world\\again', '.') + assertDirectoryName('\\\\hello\\world/', '.') + assertDirectoryName('\\\\hello\\world/again', '\\\\hello\\world') + assertDirectoryName('hello\\world', '.') + assertDirectoryName('hello\\world/', '.') + + // Should remove redundant slashes (rooted paths; UNC format not special) + assertDirectoryName('//hello', '/') + assertDirectoryName('//hello/world', '/hello') + assertDirectoryName('//hello/world/', '/hello') + assertDirectoryName('//hello//world//', '/hello') + assertDirectoryName('///hello////world///', '/hello') + + // Should remove redundant slashes (relative paths) + assertDirectoryName('hello//world//again//', 'hello/world') + assertDirectoryName('hello///world///again///', 'hello/world') + + // Directory trimming (Windows drive root format not special) + assertDirectoryName('C:/', '.') + assertDirectoryName('C:/hello', 'C:') + assertDirectoryName('C:/hello/', 'C:') + assertDirectoryName('C:/hello/world', 'C:/hello') + assertDirectoryName('C:/hello/world/', 'C:/hello') + assertDirectoryName('C:', '.') + assertDirectoryName('C:hello', '.') + assertDirectoryName('C:hello/', '.') + assertDirectoryName('C:hello/world', 'C:hello') + assertDirectoryName('C:hello/world/', 'C:hello') + + // Directory trimming (rooted paths) + assertDirectoryName('/', '/') + assertDirectoryName('/hello', '/') + assertDirectoryName('/hello/', '/') + assertDirectoryName('/hello/world', '/hello') + assertDirectoryName('/hello/world/', '/hello') + + // Directory trimming (relative paths) + assertDirectoryName('hello/world/', 'hello') + assertDirectoryName('hello/world/again', 'hello/world') + assertDirectoryName('../../hello', '../..') + } + }) + + it('ensureAbsoluteRoot roots paths', () => { + if (IS_WINDOWS) { + const currentDrive = process.cwd().substr(0, 2) + expect(currentDrive.match(/^[A-Z]:$/i)).toBeTruthy() + const otherDrive = currentDrive.toUpperCase().startsWith('C') + ? 'D:' + : 'C:' + + // Preserves relative pathing + assertEnsureAbsoluteRoot('C:/foo', '.', `C:/foo\\.`) + assertEnsureAbsoluteRoot('C:/foo/..', 'bar', `C:/foo/..\\bar`) + assertEnsureAbsoluteRoot('C:/foo', 'bar/../baz', `C:/foo\\bar/../baz`) + + // Already rooted - drive root + assertEnsureAbsoluteRoot('D:\\', 'C:/', 'C:/') + assertEnsureAbsoluteRoot('D:\\', 'a:/hello', 'a:/hello') + assertEnsureAbsoluteRoot('D:\\', 'C:\\', 'C:\\') + assertEnsureAbsoluteRoot('D:\\', 'C:\\hello', 'C:\\hello') + + // Already rooted - relative current drive root + expect(process.cwd().length).toBeGreaterThan(3) // sanity check not drive root + assertEnsureAbsoluteRoot(`${otherDrive}\\`, currentDrive, process.cwd()) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + `${currentDrive}hello`, + `${process.cwd()}\\hello` + ) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + `${currentDrive}hello/world`, + `${process.cwd()}\\hello/world` + ) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + `${currentDrive}hello\\world`, + `${process.cwd()}\\hello\\world` + ) + + // Already rooted - relative other drive root + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + otherDrive, + `${otherDrive}\\` + ) + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + `${otherDrive}hello`, + `${otherDrive}\\hello` + ) + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + `${otherDrive}hello/world`, + `${otherDrive}\\hello/world` + ) + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + `${otherDrive}hello\\world`, + `${otherDrive}\\hello\\world` + ) + + // Already rooted - current drive root + assertEnsureAbsoluteRoot(`${otherDrive}\\`, '/', `${currentDrive}\\`) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + '/hello', + `${currentDrive}\\hello` + ) + assertEnsureAbsoluteRoot(`${otherDrive}\\`, '\\', `${currentDrive}\\`) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + '\\hello', + `${currentDrive}\\hello` + ) + + // Already rooted - UNC + assertEnsureAbsoluteRoot('D:\\', '//machine/share', '//machine/share') + assertEnsureAbsoluteRoot( + 'D:\\', + '\\\\machine\\share', + '\\\\machine\\share' + ) + + // Relative + assertEnsureAbsoluteRoot('D:/', 'hello', 'D:/hello') + assertEnsureAbsoluteRoot('D:/', 'hello/world', 'D:/hello/world') + assertEnsureAbsoluteRoot('D:\\', 'hello', 'D:\\hello') + assertEnsureAbsoluteRoot('D:\\', 'hello\\world', 'D:\\hello\\world') + assertEnsureAbsoluteRoot('D:/root', 'hello', 'D:/root\\hello') + assertEnsureAbsoluteRoot('D:/root', 'hello/world', 'D:/root\\hello/world') + assertEnsureAbsoluteRoot('D:\\root', 'hello', 'D:\\root\\hello') + assertEnsureAbsoluteRoot( + 'D:\\root', + 'hello\\world', + 'D:\\root\\hello\\world' + ) + assertEnsureAbsoluteRoot('D:/root/', 'hello', 'D:/root/hello') + assertEnsureAbsoluteRoot('D:/root/', 'hello/world', 'D:/root/hello/world') + assertEnsureAbsoluteRoot('D:\\root\\', 'hello', 'D:\\root\\hello') + assertEnsureAbsoluteRoot( + 'D:\\root\\', + 'hello\\world', + 'D:\\root\\hello\\world' + ) + } else { + // Preserves relative pathing + assertEnsureAbsoluteRoot('/foo', '.', `/foo/.`) + assertEnsureAbsoluteRoot('/foo/..', 'bar', `/foo/../bar`) + assertEnsureAbsoluteRoot('/foo', 'bar/../baz', `/foo/bar/../baz`) + + // Already rooted + assertEnsureAbsoluteRoot('/root', '/', '/') + assertEnsureAbsoluteRoot('/root', '/hello', '/hello') + assertEnsureAbsoluteRoot('/root', '/hello/world', '/hello/world') + + // Not already rooted - Windows style drive root + assertEnsureAbsoluteRoot('/root', 'C:/', '/root/C:/') + assertEnsureAbsoluteRoot('/root', 'C:/hello', '/root/C:/hello') + assertEnsureAbsoluteRoot('/root', 'C:\\', '/root/C:\\') + + // Not already rooted - Windows style relative drive root + assertEnsureAbsoluteRoot('/root', 'C:', '/root/C:') + assertEnsureAbsoluteRoot('/root', 'C:hello/world', '/root/C:hello/world') + + // Not already rooted - Windows style current drive root + assertEnsureAbsoluteRoot('/root', '\\', '/root/\\') + assertEnsureAbsoluteRoot( + '/root', + '\\hello\\world', + '/root/\\hello\\world' + ) + + // Not already rooted - Windows style UNC + assertEnsureAbsoluteRoot( + '/root', + '\\\\machine\\share', + '/root/\\\\machine\\share' + ) + + // Not already rooted - relative + assertEnsureAbsoluteRoot('/', 'hello', '/hello') + assertEnsureAbsoluteRoot('/', 'hello/world', '/hello/world') + assertEnsureAbsoluteRoot('/', 'hello\\world', '/hello\\world') + assertEnsureAbsoluteRoot('/root', 'hello', '/root/hello') + assertEnsureAbsoluteRoot('/root', 'hello/world', '/root/hello/world') + assertEnsureAbsoluteRoot('/root', 'hello\\world', '/root/hello\\world') + assertEnsureAbsoluteRoot('/root/', 'hello', '/root/hello') + assertEnsureAbsoluteRoot('/root/', 'hello/world', '/root/hello/world') + assertEnsureAbsoluteRoot('/root/', 'hello\\world', '/root/hello\\world') + assertEnsureAbsoluteRoot('/root\\', 'hello', '/root\\/hello') + assertEnsureAbsoluteRoot('/root\\', 'hello/world', '/root\\/hello/world') + assertEnsureAbsoluteRoot( + '/root\\', + 'hello\\world', + '/root\\/hello\\world' + ) + } + }) + + it('hasAbsoluteRoot detects absolute root', () => { + if (IS_WINDOWS) { + // Drive root + assertHasAbsoluteRoot('C:/', true) + assertHasAbsoluteRoot('a:/hello', true) + assertHasAbsoluteRoot('c:/hello', true) + assertHasAbsoluteRoot('z:/hello', true) + assertHasAbsoluteRoot('A:/hello', true) + assertHasAbsoluteRoot('C:/hello', true) + assertHasAbsoluteRoot('Z:/hello', true) + assertHasAbsoluteRoot('C:\\', true) + assertHasAbsoluteRoot('C:\\hello', true) + + // Relative drive root + assertHasAbsoluteRoot('C:', false) + assertHasAbsoluteRoot('C:hello', false) + assertHasAbsoluteRoot('C:hello/world', false) + assertHasAbsoluteRoot('C:hello\\world', false) + + // Current drive root + assertHasAbsoluteRoot('/', false) + assertHasAbsoluteRoot('/hello', false) + assertHasAbsoluteRoot('/hello/world', false) + assertHasAbsoluteRoot('\\', false) + assertHasAbsoluteRoot('\\hello', false) + assertHasAbsoluteRoot('\\hello\\world', false) + + // UNC + assertHasAbsoluteRoot('//machine/share', true) + assertHasAbsoluteRoot('//machine/share/', true) + assertHasAbsoluteRoot('//machine/share/hello', true) + assertHasAbsoluteRoot('\\\\machine\\share', true) + assertHasAbsoluteRoot('\\\\machine\\share\\', true) + assertHasAbsoluteRoot('\\\\machine\\share\\hello', true) + + // Relative + assertHasAbsoluteRoot('hello', false) + assertHasAbsoluteRoot('hello/world', false) + assertHasAbsoluteRoot('hello\\world', false) + } else { + // Root + assertHasAbsoluteRoot('/', true) + assertHasAbsoluteRoot('/hello', true) + assertHasAbsoluteRoot('/hello/world', true) + + // Windows style drive root - false on OSX/Linux + assertHasAbsoluteRoot('C:/', false) + assertHasAbsoluteRoot('a:/hello', false) + assertHasAbsoluteRoot('c:/hello', false) + assertHasAbsoluteRoot('z:/hello', false) + assertHasAbsoluteRoot('A:/hello', false) + assertHasAbsoluteRoot('C:/hello', false) + assertHasAbsoluteRoot('Z:/hello', false) + assertHasAbsoluteRoot('C:\\', false) + assertHasAbsoluteRoot('C:\\hello', false) + + // Windows style relative drive root - false on OSX/Linux + assertHasAbsoluteRoot('C:', false) + assertHasAbsoluteRoot('C:hello', false) + assertHasAbsoluteRoot('C:hello/world', false) + assertHasAbsoluteRoot('C:hello\\world', false) + + // Windows style current drive root - false on OSX/Linux + assertHasAbsoluteRoot('\\', false) + assertHasAbsoluteRoot('\\hello', false) + assertHasAbsoluteRoot('\\hello\\world', false) + + // Windows style UNC - false on OSX/Linux + assertHasAbsoluteRoot('\\\\machine\\share', false) + assertHasAbsoluteRoot('\\\\machine\\share\\', false) + assertHasAbsoluteRoot('\\\\machine\\share\\hello', false) + + // Relative + assertHasAbsoluteRoot('hello', false) + assertHasAbsoluteRoot('hello/world', false) + assertHasAbsoluteRoot('hello\\world', false) + } + }) + + it('hasRoot detects root', () => { + if (IS_WINDOWS) { + // Drive root + assertHasRoot('C:/', true) + assertHasRoot('a:/hello', true) + assertHasRoot('c:/hello', true) + assertHasRoot('z:/hello', true) + assertHasRoot('A:/hello', true) + assertHasRoot('C:/hello', true) + assertHasRoot('Z:/hello', true) + assertHasRoot('C:\\', true) + assertHasRoot('C:\\hello', true) + + // Relative drive root + assertHasRoot('C:', true) + assertHasRoot('C:hello', true) + assertHasRoot('C:hello/world', true) + assertHasRoot('C:hello\\world', true) + + // Current drive root + assertHasRoot('/', true) + assertHasRoot('/hello', true) + assertHasRoot('/hello/world', true) + assertHasRoot('\\', true) + assertHasRoot('\\hello', true) + assertHasRoot('\\hello\\world', true) + + // UNC + assertHasRoot('//machine/share', true) + assertHasRoot('//machine/share/', true) + assertHasRoot('//machine/share/hello', true) + assertHasRoot('\\\\machine\\share', true) + assertHasRoot('\\\\machine\\share\\', true) + assertHasRoot('\\\\machine\\share\\hello', true) + + // Relative + assertHasRoot('hello', false) + assertHasRoot('hello/world', false) + assertHasRoot('hello\\world', false) + } else { + // Root + assertHasRoot('/', true) + assertHasRoot('/hello', true) + assertHasRoot('/hello/world', true) + + // Windows style drive root - false on OSX/Linux + assertHasRoot('C:/', false) + assertHasRoot('a:/hello', false) + assertHasRoot('c:/hello', false) + assertHasRoot('z:/hello', false) + assertHasRoot('A:/hello', false) + assertHasRoot('C:/hello', false) + assertHasRoot('Z:/hello', false) + assertHasRoot('C:\\', false) + assertHasRoot('C:\\hello', false) + + // Windows style relative drive root - false on OSX/Linux + assertHasRoot('C:', false) + assertHasRoot('C:hello', false) + assertHasRoot('C:hello/world', false) + assertHasRoot('C:hello\\world', false) + + // Windows style current drive root - false on OSX/Linux + assertHasRoot('\\', false) + assertHasRoot('\\hello', false) + assertHasRoot('\\hello\\world', false) + + // Windows style UNC - false on OSX/Linux + assertHasRoot('\\\\machine\\share', false) + assertHasRoot('\\\\machine\\share\\', false) + assertHasRoot('\\\\machine\\share\\hello', false) + + // Relative + assertHasRoot('hello', false) + assertHasRoot('hello/world', false) + assertHasRoot('hello\\world', false) + } + }) + + it('normalizeSeparators normalizes slashes', () => { + if (IS_WINDOWS) { + // Drive-rooted + assertNormalizeSeparators('C:/', 'C:\\') + assertNormalizeSeparators('C:/hello', 'C:\\hello') + assertNormalizeSeparators('C:/hello/', 'C:\\hello\\') + assertNormalizeSeparators('C:\\', 'C:\\') + assertNormalizeSeparators('C:\\hello', 'C:\\hello') + assertNormalizeSeparators('C:', 'C:') + assertNormalizeSeparators('C:hello', 'C:hello') + assertNormalizeSeparators('C:hello/world', 'C:hello\\world') + assertNormalizeSeparators('C:hello\\world', 'C:hello\\world') + assertNormalizeSeparators('/', '\\') + assertNormalizeSeparators('/hello', '\\hello') + assertNormalizeSeparators('/hello/world', '\\hello\\world') + assertNormalizeSeparators('/hello//world', '\\hello\\world') + assertNormalizeSeparators('\\', '\\') + assertNormalizeSeparators('\\hello', '\\hello') + assertNormalizeSeparators('\\hello\\', '\\hello\\') + assertNormalizeSeparators('\\hello\\world', '\\hello\\world') + assertNormalizeSeparators('\\hello\\\\world', '\\hello\\world') + + // UNC + assertNormalizeSeparators('//machine/share', '\\\\machine\\share') + assertNormalizeSeparators('//machine/share/', '\\\\machine\\share\\') + assertNormalizeSeparators( + '//machine/share/hello', + '\\\\machine\\share\\hello' + ) + assertNormalizeSeparators('///machine/share', '\\\\machine\\share') + assertNormalizeSeparators('\\\\machine\\share', '\\\\machine\\share') + assertNormalizeSeparators('\\\\machine\\share\\', '\\\\machine\\share\\') + assertNormalizeSeparators( + '\\\\machine\\share\\hello', + '\\\\machine\\share\\hello' + ) + assertNormalizeSeparators('\\\\\\machine\\share', '\\\\machine\\share') + + // Relative + assertNormalizeSeparators('hello', 'hello') + assertNormalizeSeparators('hello/world', 'hello\\world') + assertNormalizeSeparators('hello//world', 'hello\\world') + assertNormalizeSeparators('hello\\world', 'hello\\world') + assertNormalizeSeparators('hello\\\\world', 'hello\\world') + } else { + // Rooted + assertNormalizeSeparators('/', '/') + assertNormalizeSeparators('/hello', '/hello') + assertNormalizeSeparators('/hello/world', '/hello/world') + assertNormalizeSeparators('//hello/world/', '/hello/world/') + + // Backslash not converted + assertNormalizeSeparators('C:\\', 'C:\\') + assertNormalizeSeparators('C:\\\\hello\\\\', 'C:\\\\hello\\\\') + assertNormalizeSeparators('\\', '\\') + assertNormalizeSeparators('\\hello', '\\hello') + assertNormalizeSeparators('\\hello\\world', '\\hello\\world') + assertNormalizeSeparators('hello\\world', 'hello\\world') + + // UNC not converted + assertNormalizeSeparators('\\\\machine\\share', '\\\\machine\\share') + + // UNC not preserved + assertNormalizeSeparators('//machine/share', '/machine/share') + + // Relative + assertNormalizeSeparators('hello', 'hello') + assertNormalizeSeparators('hello/////world', 'hello/world') + } + }) + + it('safeTrimTrailingSeparator safely trims trailing separator', () => { + assertSafeTrimTrailingSeparator('', '') + + if (IS_WINDOWS) { + // Removes redundant slashes + assertSafeTrimTrailingSeparator( + 'C:\\\\hello\\\\\\world\\\\', + 'C:\\hello\\world' + ) + assertSafeTrimTrailingSeparator('C://hello///world//', 'C:\\hello\\world') + // Relative root: + assertSafeTrimTrailingSeparator( + '\\hello\\\\world\\\\again\\\\', + '\\hello\\world\\again' + ) + assertSafeTrimTrailingSeparator( + '/hello///world//again//', + '\\hello\\world\\again' + ) + // UNC: + assertSafeTrimTrailingSeparator('\\\\hello\\world\\', '\\\\hello\\world') + assertSafeTrimTrailingSeparator( + '\\\\hello\\world\\\\', + '\\\\hello\\world' + ) + assertSafeTrimTrailingSeparator( + '\\\\hello\\\\\\world\\\\again\\', + '\\\\hello\\world\\again' + ) + assertSafeTrimTrailingSeparator('//hello/world/', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('//hello/world//', '\\\\hello\\world') + assertSafeTrimTrailingSeparator( + '//hello//world//again/', + '\\\\hello\\world\\again' + ) + // Relative: + assertSafeTrimTrailingSeparator('hello\\world\\', 'hello\\world') + + // Slash trimming + assertSafeTrimTrailingSeparator('a:/hello/', 'a:\\hello') + assertSafeTrimTrailingSeparator('z:/hello', 'z:\\hello') + assertSafeTrimTrailingSeparator('C:/', 'C:\\') + assertSafeTrimTrailingSeparator('C:\\', 'C:\\') + assertSafeTrimTrailingSeparator('C:/hello/world', 'C:\\hello\\world') + assertSafeTrimTrailingSeparator('C:/hello/world/', 'C:\\hello\\world') + assertSafeTrimTrailingSeparator('C:', 'C:') + assertSafeTrimTrailingSeparator('C:hello/', 'C:hello') + assertSafeTrimTrailingSeparator('/', '\\') + assertSafeTrimTrailingSeparator('/hello/', '\\hello') + assertSafeTrimTrailingSeparator('\\', '\\') + assertSafeTrimTrailingSeparator('\\hello\\', '\\hello') + assertSafeTrimTrailingSeparator('//hello/', '\\\\hello') + assertSafeTrimTrailingSeparator('//hello/world', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('//hello/world/', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('\\\\hello', '\\\\hello') + assertSafeTrimTrailingSeparator('\\\\hello\\', '\\\\hello') + assertSafeTrimTrailingSeparator('\\\\hello\\world', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('\\\\hello\\world\\', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('hello/world/', 'hello\\world') + assertSafeTrimTrailingSeparator('hello/', 'hello') + assertSafeTrimTrailingSeparator('../../', '..\\..') + } else { + // Should not converts slashes + assertSafeTrimTrailingSeparator('/hello\\world', '/hello\\world') + assertSafeTrimTrailingSeparator('/hello\\world/', '/hello\\world') + assertSafeTrimTrailingSeparator('\\\\hello\\world/', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('hello\\world/', 'hello\\world') + + // Should remove redundant slashes (rooted paths; UNC format not special) + assertSafeTrimTrailingSeparator('//hello', '/hello') + assertSafeTrimTrailingSeparator('//hello/world', '/hello/world') + assertSafeTrimTrailingSeparator('//hello/world/', '/hello/world') + assertSafeTrimTrailingSeparator('//hello//world//', '/hello/world') + assertSafeTrimTrailingSeparator('///hello////world///', '/hello/world') + + // Should remove redundant slashes (relative paths) + assertSafeTrimTrailingSeparator('hello//world//', 'hello/world') + assertSafeTrimTrailingSeparator('hello///world///', 'hello/world') + + // Slash trimming (Windows drive root format not special) + assertSafeTrimTrailingSeparator('C:/', 'C:') + assertSafeTrimTrailingSeparator('C:/hello', 'C:/hello') + assertSafeTrimTrailingSeparator('C:/hello/', 'C:/hello') + assertSafeTrimTrailingSeparator('C:hello/', 'C:hello') + + // Slash trimming (rooted paths) + assertSafeTrimTrailingSeparator('/', '/') + assertSafeTrimTrailingSeparator('/hello', '/hello') + assertSafeTrimTrailingSeparator('/hello/', '/hello') + assertSafeTrimTrailingSeparator('/hello/world/', '/hello/world') + + // Slash trimming (relative paths) + assertSafeTrimTrailingSeparator('hello/world/', 'hello/world') + assertSafeTrimTrailingSeparator('../../', '../..') + } + }) +}) + +function assertDirectoryName(itemPath: string, expected: string): void { + expect(pathHelper.dirname(itemPath)).toBe(expected) +} + +function assertEnsureAbsoluteRoot( + root: string, + itemPath: string, + expected: string +): void { + expect(pathHelper.ensureAbsoluteRoot(root, itemPath)).toBe(expected) +} + +function assertHasAbsoluteRoot(itemPath: string, expected: boolean): void { + expect(pathHelper.hasAbsoluteRoot(itemPath)).toBe(expected) +} + +function assertHasRoot(itemPath: string, expected: boolean): void { + expect(pathHelper.hasRoot(itemPath)).toBe(expected) +} + +function assertNormalizeSeparators(itemPath: string, expected: string): void { + expect(pathHelper.normalizeSeparators(itemPath)).toBe(expected) +} + +function assertSafeTrimTrailingSeparator( + itemPath: string, + expected: string +): void { + expect(pathHelper.safeTrimTrailingSeparator(itemPath)).toBe(expected) +} diff --git a/actions-toolkit/packages/glob/__tests__/internal-path.test.ts b/actions-toolkit/packages/glob/__tests__/internal-path.test.ts new file mode 100644 index 00000000..37090e49 --- /dev/null +++ b/actions-toolkit/packages/glob/__tests__/internal-path.test.ts @@ -0,0 +1,92 @@ +import * as path from 'path' +import {Path} from '../src/internal-path' + +const IS_WINDOWS = process.platform === 'win32' + +describe('path', () => { + it('constructs from rooted path', () => { + assertPath(`/`, `${path.sep}`, [path.sep]) + assertPath(`/foo`, `${path.sep}foo`, [path.sep, 'foo']) + if (IS_WINDOWS) { + assertPath(`C:\\foo`, `C:\\foo`, ['C:\\', 'foo']) + assertPath(`C:foo`, `C:foo`, ['C:', 'foo']) + assertPath(`\\\\foo\\bar\\baz`, `\\\\foo\\bar\\baz`, [ + '\\\\foo\\bar', + 'baz' + ]) + } + }) + + it('constructs from rooted segments', () => { + assertPath([`/`], `${path.sep}`, [path.sep]) + assertPath([`/`, `foo`], `${path.sep}foo`, [path.sep, 'foo']) + if (IS_WINDOWS) { + assertPath([`C:\\`, `foo`], `C:\\foo`, ['C:\\', 'foo']) + assertPath([`C:`, `foo`], `C:foo`, ['C:', 'foo']) + assertPath([`\\\\foo\\bar`, `baz`], `\\\\foo\\bar\\baz`, [ + '\\\\foo\\bar', + 'baz' + ]) + } + }) + + it('constructs from relative path', () => { + assertPath(`foo`, `foo`, ['foo']) + assertPath(`foo/bar`, `foo${path.sep}bar`, ['foo', 'bar']) + }) + + it('constructs from relative segments', () => { + assertPath([`foo`], `foo`, ['foo']) + assertPath([`foo`, `bar`], `foo${path.sep}bar`, ['foo', 'bar']) + }) + + it('normalizes slashes', () => { + assertPath( + `/foo///bar${path.sep}${path.sep}${path.sep}baz`, + `${path.sep}foo${path.sep}bar${path.sep}baz`, + [path.sep, 'foo', 'bar', 'baz'] + ) + }) + + it('preserves relative pathing', () => { + assertPath( + '/foo/../bar/./baz', + `${path.sep}foo${path.sep}..${path.sep}bar${path.sep}.${path.sep}baz`, + [path.sep, 'foo', '..', 'bar', '.', 'baz'] + ) + }) + + it('trims unnecessary trailing slash', () => { + assertPath('/', path.sep, [path.sep]) + assertPath('/foo/', `${path.sep}foo`, [path.sep, 'foo']) + assertPath('foo/', 'foo', ['foo']) + assertPath('foo/bar/', `foo${path.sep}bar`, ['foo', 'bar']) + if (IS_WINDOWS) { + assertPath('\\', '\\', ['\\']) + assertPath('C:\\', 'C:\\', ['C:\\']) + assertPath('C:\\foo\\', 'C:\\foo', ['C:\\', 'foo']) + assertPath('C:foo\\', 'C:foo', ['C:', 'foo']) + assertPath('\\\\computer\\share\\', '\\\\computer\\share', [ + '\\\\computer\\share' + ]) + assertPath('\\\\computer\\share\\foo', '\\\\computer\\share\\foo', [ + '\\\\computer\\share', + 'foo' + ]) + assertPath('\\\\computer\\share\\foo\\', '\\\\computer\\share\\foo', [ + '\\\\computer\\share', + 'foo' + ]) + } + }) +}) + +function assertPath( + itemPath: string | string[], + expectedPath: string, + expectedSegments: string[] +): void { + const actual = new Path(itemPath) + expect(actual.toString()).toBe(expectedPath) + expect(actual.segments).toEqual(expectedSegments) +} diff --git a/actions-toolkit/packages/glob/__tests__/internal-pattern-helper.test.ts b/actions-toolkit/packages/glob/__tests__/internal-pattern-helper.test.ts new file mode 100644 index 00000000..1d60f6b3 --- /dev/null +++ b/actions-toolkit/packages/glob/__tests__/internal-pattern-helper.test.ts @@ -0,0 +1,152 @@ +import * as path from 'path' +import * as patternHelper from '../src/internal-pattern-helper' +import {MatchKind} from '../src/internal-match-kind' +import {IS_WINDOWS} from '../../io/src/io-util' +import {Pattern} from '../src/internal-pattern' + +describe('pattern-helper', () => { + it('getSearchPaths omits negate search paths', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const patterns = [ + `${root}search1/foo/**`, + `${root}search2/bar/**`, + `!${root}search3/baz/**` + ].map(x => new Pattern(x)) + const searchPaths = patternHelper.getSearchPaths(patterns) + expect(searchPaths).toEqual([ + `${root}search1${path.sep}foo`, + `${root}search2${path.sep}bar` + ]) + }) + + it('getSearchPaths omits search path when ancestor is also a search path', () => { + if (IS_WINDOWS) { + const patterns = [ + 'C:\\Search1\\Foo\\**', + 'C:\\sEARCH1\\fOO\\bar\\**', + 'C:\\sEARCH1\\foo\\bar', + 'C:\\Search2\\**', + 'C:\\Search3\\Foo\\Bar\\**', + 'C:\\sEARCH3\\fOO\\bAR\\**' + ].map(x => new Pattern(x)) + const searchPaths = patternHelper.getSearchPaths(patterns) + expect(searchPaths).toEqual([ + 'C:\\Search1\\Foo', + 'C:\\Search2', + 'C:\\Search3\\Foo\\Bar' + ]) + } else { + const patterns = [ + '/search1/foo/**', + '/search1/foo/bar/**', + '/search2/foo/bar', + '/search2/**', + '/search3/foo/bar/**', + '/search3/foo/bar/**' + ].map(x => new Pattern(x)) + + const searchPaths = patternHelper.getSearchPaths(patterns) + expect(searchPaths).toEqual([ + '/search1/foo', + '/search2', + '/search3/foo/bar' + ]) + } + }) + + it('match supports interleaved exclude patterns', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const itemPaths = [ + `${root}solution1/proj1/proj1.proj`, + `${root}solution1/proj1/README.txt`, + `${root}solution1/proj2/proj2.proj`, + `${root}solution1/proj2/README.txt`, + `${root}solution1/solution1.sln`, + `${root}solution2/proj1/proj1.proj`, + `${root}solution2/proj1/README.txt`, + `${root}solution2/proj2/proj2.proj`, + `${root}solution2/proj2/README.txt`, + `${root}solution2/solution2.sln` + ] + const patterns = [ + `${root}**/*.proj`, // include all proj files + `${root}**/README.txt`, // include all README files + `!${root}**/solution2/**`, // exclude the solution 2 folder entirely + `${root}**/*.sln`, // include all sln files + `!${root}**/proj2/README.txt` // exclude proj2 README files + ].map(x => new Pattern(x)) + const matched = itemPaths.filter( + x => patternHelper.match(patterns, x) === MatchKind.All + ) + expect(matched).toEqual([ + `${root}solution1/proj1/proj1.proj`, + `${root}solution1/proj1/README.txt`, + `${root}solution1/proj2/proj2.proj`, + `${root}solution1/solution1.sln`, + `${root}solution2/solution2.sln` + ]) + }) + + it('match supports excluding directories', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const itemPaths = [ + root, + `${root}foo`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ] + const patterns = [ + `${root}foo/**`, // include all files and directories + `!${root}foo/**/` // exclude directories + ].map(x => new Pattern(x)) + const matchKinds = itemPaths.map(x => patternHelper.match(patterns, x)) + expect(matchKinds).toEqual([ + MatchKind.None, + MatchKind.File, + MatchKind.File, + MatchKind.File + ]) + }) + + it('match supports including directories only', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const itemPaths = [ + root, + `${root}foo/`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ] + const patterns = [ + `${root}foo/**/` // include directories only + ].map(x => new Pattern(x)) + const matchKinds = itemPaths.map(x => patternHelper.match(patterns, x)) + expect(matchKinds).toEqual([ + MatchKind.None, + MatchKind.Directory, + MatchKind.Directory, + MatchKind.Directory + ]) + }) + + it('partialMatch skips negate patterns', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const patterns = [ + `${root}search1/foo/**`, + `${root}search2/bar/**`, + `!${root}search2/bar/**`, + `!${root}search3/baz/**` + ].map(x => new Pattern(x)) + expect(patternHelper.partialMatch(patterns, `${root}search1`)).toBeTruthy() + expect( + patternHelper.partialMatch(patterns, `${root}search1/foo`) + ).toBeTruthy() + expect(patternHelper.partialMatch(patterns, `${root}search2`)).toBeTruthy() + expect( + patternHelper.partialMatch(patterns, `${root}search2/bar`) + ).toBeTruthy() + expect(patternHelper.partialMatch(patterns, `${root}search3`)).toBeFalsy() + expect( + patternHelper.partialMatch(patterns, `${root}search3/bar`) + ).toBeFalsy() + }) +}) diff --git a/actions-toolkit/packages/glob/__tests__/internal-pattern.test.ts b/actions-toolkit/packages/glob/__tests__/internal-pattern.test.ts new file mode 100644 index 00000000..8a9ecc85 --- /dev/null +++ b/actions-toolkit/packages/glob/__tests__/internal-pattern.test.ts @@ -0,0 +1,352 @@ +import * as io from '../../io/src/io' +import * as os from 'os' +import * as path from 'path' +import {MatchKind} from '../src/internal-match-kind' +import {promises as fs} from 'fs' +import {Pattern} from '../src/internal-pattern' + +const IS_WINDOWS = process.platform === 'win32' + +describe('pattern', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('counts leading negate markers', () => { + const actual = [ + '/initial-includes/*.txt', + '!!/hello/two-negate-markers.txt', + '!!!!/hello/four-negate-markers.txt', + '!/initial-includes/one-negate-markers.txt', + '!!!/initial-includes/three-negate-markers.txt' + ].map(x => new Pattern(x).negate) + expect(actual).toEqual([false, false, false, true, true]) + }) + + it('escapes homedir', async () => { + const home = path.join(getTestTemp(), 'home-with-[and]') + await fs.mkdir(home, {recursive: true}) + const pattern = new Pattern('~/m*', false, undefined, home) + + expect(pattern.searchPath).toBe(home) + expect(pattern.match(path.join(home, 'match'))).toBeTruthy() + expect(pattern.match(path.join(home, 'not-match'))).toBeFalsy() + }) + + it('escapes root', async () => { + const originalCwd = process.cwd() + const rootPath = path.join(getTestTemp(), 'cwd-with-[and]') + await fs.mkdir(rootPath, {recursive: true}) + try { + process.chdir(rootPath) + + // Relative + let pattern = new Pattern('m*') + expect(pattern.searchPath).toBe(rootPath) + expect(pattern.match(path.join(rootPath, 'match'))).toBeTruthy() + expect(pattern.match(path.join(rootPath, 'not-match'))).toBeFalsy() + + if (IS_WINDOWS) { + const currentDrive = process.cwd().substr(0, 2) + expect(currentDrive.match(/^[A-Z]:$/i)).toBeTruthy() + + // Relative current drive letter, e.g. C:m* + pattern = new Pattern(`${currentDrive}m*`) + expect(pattern.searchPath).toBe(rootPath) + expect(pattern.match(path.join(rootPath, 'match'))).toBeTruthy() + expect(pattern.match(path.join(rootPath, 'not-match'))).toBeFalsy() + + // Relative current drive, e.g. \path\to\cwd\m* + pattern = new Pattern( + `${Pattern.globEscape(process.cwd().substr(2))}\\m*` + ) + expect(pattern.searchPath).toBe(rootPath) + expect(pattern.match(path.join(rootPath, 'match'))).toBeTruthy() + expect(pattern.match(path.join(rootPath, 'not-match'))).toBeFalsy() + } + } finally { + process.chdir(originalCwd) + } + }) + + it('globstar matches immediately preceding directory', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}foo/bar/**`) + const actual = [ + root, + `${root}foo`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ].map(x => pattern.match(x)) + expect(actual).toEqual([ + MatchKind.None, + MatchKind.None, + MatchKind.All, + MatchKind.All + ]) + }) + + it('is case insensitive match on Windows', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}Foo/**/Baz`) + expect(pattern.match(`${root}Foo/Baz`)).toBe(MatchKind.All) + expect(pattern.match(`${root}Foo/bAZ`)).toBe( + IS_WINDOWS ? MatchKind.All : MatchKind.None + ) + expect(pattern.match(`${root}fOO/Baz`)).toBe( + IS_WINDOWS ? MatchKind.All : MatchKind.None + ) + expect(pattern.match(`${root}fOO/bar/bAZ`)).toBe( + IS_WINDOWS ? MatchKind.All : MatchKind.None + ) + }) + + it('is case insensitive partial match on Windows', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}Foo/Bar/**/Baz`) + expect(pattern.partialMatch(`${root}Foo`)).toBeTruthy() + expect(pattern.partialMatch(`${root}fOO`)).toBe(IS_WINDOWS ? true : false) + }) + + it('matches root', () => { + const pattern = new Pattern(IS_WINDOWS ? 'C:\\**' : '/**') + expect(pattern.match(IS_WINDOWS ? 'C:\\' : '/')).toBe(MatchKind.All) + }) + + it('partial matches root', () => { + if (IS_WINDOWS) { + let pattern = new Pattern('C:\\foo\\**') + expect(pattern.partialMatch('c:\\')).toBeTruthy() + pattern = new Pattern('c:\\foo\\**') + expect(pattern.partialMatch('C:\\')).toBeTruthy() + } else { + const pattern = new Pattern('/foo/**') + expect(pattern.partialMatch('/')).toBeTruthy() + } + }) + + it('replaces leading . segment', () => { + // Pattern is '.' + let pattern = new Pattern('.') + expect(pattern.match(process.cwd())).toBe(MatchKind.All) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBe(MatchKind.None) + + // Pattern is './foo' + pattern = new Pattern('./foo') + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(process.cwd(), 'bar'))).toBe(MatchKind.None) + + // Pattern is '.foo' + pattern = new Pattern('.foo') + expect(pattern.match(path.join(process.cwd(), '.foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBe(MatchKind.None) + expect(pattern.match(`${process.cwd()}foo`)).toBe(MatchKind.None) + }) + + it('replaces leading ~ segment', async () => { + const homedir = os.homedir() + expect(homedir).toBeTruthy() + await fs.stat(homedir) + + // Pattern is '~' + let pattern = new Pattern('~') + expect(pattern.match(homedir)).toBe(MatchKind.All) + expect(pattern.match(path.join(homedir, 'foo'))).toBe(MatchKind.None) + + // Pattern is '~/foo' + pattern = new Pattern('~/foo') + expect(pattern.match(path.join(homedir, 'foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(homedir, 'bar'))).toBe(MatchKind.None) + + // Pattern is '~foo' + pattern = new Pattern('~foo') + expect(pattern.match(path.join(process.cwd(), '~foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(homedir, 'foo'))).toBe(MatchKind.None) + expect(pattern.match(`${homedir}foo`)).toBe(MatchKind.None) + }) + + it('replaces leading relative root', () => { + if (IS_WINDOWS) { + const currentDrive = process.cwd().substr(0, 2) + expect(currentDrive.match(/^[A-Z]:$/i)).toBeTruthy() + const otherDrive = currentDrive.toUpperCase().startsWith('C') + ? 'D:' + : 'C:' + expect(process.cwd().length).toBeGreaterThan(3) // sanity check not drive root + + // Pattern is 'C:' + let pattern = new Pattern(currentDrive) + expect(pattern.match(process.cwd())).toBeTruthy() + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBeFalsy() + + // Pattern is 'C:foo' + pattern = new Pattern(`${currentDrive}foo`) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBeTruthy() + expect(pattern.match(path.join(process.cwd(), 'bar'))).toBeFalsy() + expect(pattern.match(`${currentDrive}\\foo`)).toBeFalsy() + + // Pattern is 'X:' + pattern = new Pattern(otherDrive) + expect(pattern.match(`${otherDrive}\\`)).toBeTruthy() + expect(pattern.match(`${otherDrive}\\foo`)).toBeFalsy() + + // Pattern is 'X:foo' + pattern = new Pattern(`${otherDrive}foo`) + expect(pattern.match(`${otherDrive}\\foo`)).toBeTruthy() + expect(pattern.match(`${otherDrive}\\bar`)).toBeFalsy() + + // Pattern is '\\path\\to\\cwd' + pattern = new Pattern(`${process.cwd().substr(2)}\\foo`) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBeTruthy() + expect(pattern.match(path.join(process.cwd(), 'bar'))).toBeFalsy() + } + }) + + it('roots exclude pattern', () => { + const patternStrings = ['!hello.txt', '!**/world.txt'] + const actual = patternStrings.map(x => new Pattern(x)) + const expected = patternStrings + .map(x => x.substr(1)) + .map(x => path.join(Pattern.globEscape(process.cwd()), x)) + .map(x => `!${x}`) + .map(x => new Pattern(x)) + expect(actual.map(x => x.negate)).toEqual([true, true]) + expect(actual.map(x => x.segments)).toEqual(expected.map(x => x.segments)) + }) + + it('roots include pattern', () => { + const patternStrings = ['hello.txt', '**/world.txt'] + const actual = patternStrings.map(x => new Pattern(x)) + const expected = patternStrings.map( + x => new Pattern(path.join(Pattern.globEscape(process.cwd()), x)) + ) + expect(actual.map(x => x.segments)).toEqual(expected.map(x => x.segments)) + }) + + it('sets trailing separator', () => { + expect(new Pattern(' foo ').trailingSeparator).toBeFalsy() + expect(new Pattern(' /foo ').trailingSeparator).toBeFalsy() + expect(new Pattern('! /foo ').trailingSeparator).toBeFalsy() + expect(new Pattern(' /foo/* ').trailingSeparator).toBeFalsy() + expect(new Pattern(' /foo/** ').trailingSeparator).toBeFalsy() + expect(new Pattern(' \\foo ').trailingSeparator).toBeFalsy() + expect(new Pattern('! \\foo ').trailingSeparator).toBeFalsy() + expect(new Pattern(' \\foo\\* ').trailingSeparator).toBeFalsy() + expect(new Pattern(' \\foo\\** ').trailingSeparator).toBeFalsy() + expect(new Pattern(' foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' /foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' C:/foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' C:foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' D:foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern('! /foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' /foo/*/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' /foo/**/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' foo\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern(' \\foo\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern('! \\foo\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern(' \\foo\\*\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern(' \\foo\\**\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + }) + + it('supports including directories only', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}foo/**/`) // trailing slash + const actual = [ + root, + `${root}foo/`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ].map(x => pattern.match(x)) + expect(pattern.trailingSeparator).toBeTruthy() + expect(actual).toEqual([ + MatchKind.None, + MatchKind.Directory, + MatchKind.Directory, + MatchKind.Directory + ]) + }) + + it('trims pattern', () => { + const pattern = new Pattern(' hello.txt ') + expect(pattern.segments.reverse()[0]).toBe('hello.txt') + }) + + it('trims whitespace after trimming negate markers', () => { + const pattern = new Pattern(' ! ! ! hello.txt ') + expect(pattern.negate).toBeTruthy() + expect(pattern.segments.reverse()[0]).toBe('hello.txt') + }) + + it('unescapes segments to narrow search path', () => { + // Positive + const root = IS_WINDOWS ? 'C:\\' : '/' + let pattern = new Pattern(`${root}foo/b[a]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}bar`) + expect(pattern.match(`${root}foo/bar/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[*]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b*r`) + expect(pattern.match(`${root}foo/b*r/baz`)).toBeTruthy() + expect(pattern.match(`${root}foo/bar/baz`)).toBeFalsy() + pattern = new Pattern(`${root}foo/b[?]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b?r`) + expect(pattern.match(`${root}foo/b?r/baz`)).toBeTruthy() + expect(pattern.match(`${root}foo/bar/baz`)).toBeFalsy() + pattern = new Pattern(`${root}foo/b[!]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b!r`) + expect(pattern.match(`${root}foo/b!r/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[[]ar/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b[ar`) + expect(pattern.match(`${root}foo/b[ar/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b[]r`) + expect(pattern.match(`${root}foo/b[]r/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b[r`) + expect(pattern.match(`${root}foo/b[r/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b]r`) + expect(pattern.match(`${root}foo/b]r/baz`)).toBeTruthy() + if (!IS_WINDOWS) { + pattern = new Pattern('/foo/b\\[a]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}b[a]r`) + expect(pattern.match('/foo/b[a]r/baz')).toBeTruthy() + pattern = new Pattern('/foo/b[\\!]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}b!r`) + expect(pattern.match('/foo/b!r/baz')).toBeTruthy() + pattern = new Pattern('/foo/b[\\]]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}b]r`) + expect(pattern.match('/foo/b]r/baz')).toBeTruthy() + pattern = new Pattern('/foo/b[\\a]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}bar`) + expect(pattern.match('/foo/bar/baz')).toBeTruthy() + } + + // Negative + pattern = new Pattern(`${root}foo/b[aA]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo`) + pattern = new Pattern(`${root}foo/b[!a]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo`) + if (IS_WINDOWS) { + pattern = new Pattern('C:/foo/b\\[a]r/b*') + expect(pattern.searchPath).toBe(`C:\\foo\\b\\ar`) + expect(pattern.match('C:/foo/b/ar/baz')).toBeTruthy() + pattern = new Pattern('C:/foo/b[\\!]r/b*') + expect(pattern.searchPath).toBe('C:\\foo\\b[\\!]r') + expect(pattern.match('C:/foo/b[undefined/!]r/baz')).toBeTruthy() // Note, "undefined" substr to accommodate a bug in Minimatch when nocase=true + } + }) +}) + +function getTestTemp(): string { + return path.join(__dirname, '_temp', 'internal-pattern') +} diff --git a/actions-toolkit/packages/glob/package-lock.json b/actions-toolkit/packages/glob/package-lock.json new file mode 100644 index 00000000..7b954259 --- /dev/null +++ b/actions-toolkit/packages/glob/package-lock.json @@ -0,0 +1,115 @@ +{ + "name": "@actions/glob", + "version": "0.4.0", + "lockfileVersion": 3, + "requires": true, + "description": "Actions glob lib", + "files": [ + "lib", + "!.DS_Store" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/glob", + "keywords": [ + "github", + "actions", + "glob" + ], + "license": "MIT", + "main": "lib/glob.js", + "preview": true, + "types": "lib/glob.d.ts", + "packages": { + "": { + "name": "@actions/glob", + "version": "0.4.0", + "license": "MIT", + "dependencies": { + "@actions/core": "^1.9.1", + "minimatch": "^3.0.4" + } + }, + "node_modules/@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + } + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "directory": "packages/glob", + "type": "git", + "url": "git+https://github.com/actions/toolkit.git" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + } +} diff --git a/actions-toolkit/packages/glob/package.json b/actions-toolkit/packages/glob/package.json new file mode 100644 index 00000000..62e6bd29 --- /dev/null +++ b/actions-toolkit/packages/glob/package.json @@ -0,0 +1,43 @@ +{ + "name": "@actions/glob", + "version": "0.4.0", + "preview": true, + "description": "Actions glob lib", + "keywords": [ + "github", + "actions", + "glob" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/glob", + "license": "MIT", + "main": "lib/glob.js", + "types": "lib/glob.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/glob" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.9.1", + "minimatch": "^3.0.4" + } +} diff --git a/actions-toolkit/packages/glob/src/glob.ts b/actions-toolkit/packages/glob/src/glob.ts new file mode 100644 index 00000000..3c68de8d --- /dev/null +++ b/actions-toolkit/packages/glob/src/glob.ts @@ -0,0 +1,41 @@ +import {Globber, DefaultGlobber} from './internal-globber' +import {GlobOptions} from './internal-glob-options' +import {HashFileOptions} from './internal-hash-file-options' +import {hashFiles as _hashFiles} from './internal-hash-files' + +export {Globber, GlobOptions} + +/** + * Constructs a globber + * + * @param patterns Patterns separated by newlines + * @param options Glob options + */ +export async function create( + patterns: string, + options?: GlobOptions +): Promise { + return await DefaultGlobber.create(patterns, options) +} + +/** + * Computes the sha256 hash of a glob + * + * @param patterns Patterns separated by newlines + * @param currentWorkspace Workspace used when matching files + * @param options Glob options + * @param verbose Enables verbose logging + */ +export async function hashFiles( + patterns: string, + currentWorkspace = '', + options?: HashFileOptions, + verbose: Boolean = false +): Promise { + let followSymbolicLinks = true + if (options && typeof options.followSymbolicLinks === 'boolean') { + followSymbolicLinks = options.followSymbolicLinks + } + const globber = await create(patterns, {followSymbolicLinks}) + return _hashFiles(globber, currentWorkspace, verbose) +} diff --git a/actions-toolkit/packages/glob/src/internal-glob-options-helper.ts b/actions-toolkit/packages/glob/src/internal-glob-options-helper.ts new file mode 100644 index 00000000..c798b165 --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-glob-options-helper.ts @@ -0,0 +1,38 @@ +import * as core from '@actions/core' +import {GlobOptions} from './internal-glob-options' + +/** + * Returns a copy with defaults filled in. + */ +export function getOptions(copy?: GlobOptions): GlobOptions { + const result: GlobOptions = { + followSymbolicLinks: true, + implicitDescendants: true, + matchDirectories: true, + omitBrokenSymbolicLinks: true + } + + if (copy) { + if (typeof copy.followSymbolicLinks === 'boolean') { + result.followSymbolicLinks = copy.followSymbolicLinks + core.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`) + } + + if (typeof copy.implicitDescendants === 'boolean') { + result.implicitDescendants = copy.implicitDescendants + core.debug(`implicitDescendants '${result.implicitDescendants}'`) + } + + if (typeof copy.matchDirectories === 'boolean') { + result.matchDirectories = copy.matchDirectories + core.debug(`matchDirectories '${result.matchDirectories}'`) + } + + if (typeof copy.omitBrokenSymbolicLinks === 'boolean') { + result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks + core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`) + } + } + + return result +} diff --git a/actions-toolkit/packages/glob/src/internal-glob-options.ts b/actions-toolkit/packages/glob/src/internal-glob-options.ts new file mode 100644 index 00000000..ac1e93f1 --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-glob-options.ts @@ -0,0 +1,39 @@ +/** + * Options to control globbing behavior + */ +export interface GlobOptions { + /** + * Indicates whether to follow symbolic links. Generally should set to false + * when deleting files. + * + * @default true + */ + followSymbolicLinks?: boolean + + /** + * Indicates whether directories that match a glob pattern, should implicitly + * cause all descendant paths to be matched. + * + * For example, given the directory `my-dir`, the following glob patterns + * would produce the same results: `my-dir/**`, `my-dir/`, `my-dir` + * + * @default true + */ + implicitDescendants?: boolean + + /** + * Indicates whether matching directories should be included in the + * result set. + * + * @default true + */ + matchDirectories?: boolean + + /** + * Indicates whether broken symbolic should be ignored and omitted from the + * result set. Otherwise an error will be thrown. + * + * @default true + */ + omitBrokenSymbolicLinks?: boolean +} diff --git a/actions-toolkit/packages/glob/src/internal-globber.ts b/actions-toolkit/packages/glob/src/internal-globber.ts new file mode 100644 index 00000000..3978d625 --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-globber.ts @@ -0,0 +1,243 @@ +import * as core from '@actions/core' +import * as fs from 'fs' +import * as globOptionsHelper from './internal-glob-options-helper' +import * as path from 'path' +import * as patternHelper from './internal-pattern-helper' +import {GlobOptions} from './internal-glob-options' +import {MatchKind} from './internal-match-kind' +import {Pattern} from './internal-pattern' +import {SearchState} from './internal-search-state' + +const IS_WINDOWS = process.platform === 'win32' + +export {GlobOptions} + +/** + * Used to match files and directories + */ +export interface Globber { + /** + * Returns the search path preceding the first glob segment, from each pattern. + * Duplicates and descendants of other paths are filtered out. + * + * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`. + * + * Example 2: The patterns `/foo/*` and `/foo/bar/*` returns `/foo`. + */ + getSearchPaths(): string[] + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + glob(): Promise + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + globGenerator(): AsyncGenerator +} + +export class DefaultGlobber implements Globber { + private readonly options: GlobOptions + private readonly patterns: Pattern[] = [] + private readonly searchPaths: string[] = [] + + private constructor(options?: GlobOptions) { + this.options = globOptionsHelper.getOptions(options) + } + + getSearchPaths(): string[] { + // Return a copy + return this.searchPaths.slice() + } + + async glob(): Promise { + const result: string[] = [] + for await (const itemPath of this.globGenerator()) { + result.push(itemPath) + } + return result + } + + async *globGenerator(): AsyncGenerator { + // Fill in defaults options + const options = globOptionsHelper.getOptions(this.options) + // Implicit descendants? + const patterns: Pattern[] = [] + for (const pattern of this.patterns) { + patterns.push(pattern) + if ( + options.implicitDescendants && + (pattern.trailingSeparator || + pattern.segments[pattern.segments.length - 1] !== '**') + ) { + patterns.push( + new Pattern(pattern.negate, true, pattern.segments.concat('**')) + ) + } + } + + // Push the search paths + + const stack: SearchState[] = [] + for (const searchPath of patternHelper.getSearchPaths(patterns)) { + core.debug(`Search path '${searchPath}'`) + + // Exists? + try { + // Intentionally using lstat. Detection for broken symlink + // will be performed later (if following symlinks). + await fs.promises.lstat(searchPath) + } catch (err) { + if (err.code === 'ENOENT') { + continue + } + throw err + } + + stack.unshift(new SearchState(searchPath, 1)) + } + + // Search + const traversalChain: string[] = [] // used to detect cycles + while (stack.length) { + // Pop + const item = stack.pop() as SearchState + + // Match? + const match = patternHelper.match(patterns, item.path) + const partialMatch = + !!match || patternHelper.partialMatch(patterns, item.path) + if (!match && !partialMatch) { + continue + } + + // Stat + const stats: fs.Stats | undefined = await DefaultGlobber.stat( + item, + options, + traversalChain + ) + + // Broken symlink, or symlink cycle detected, or no longer exists + if (!stats) { + continue + } + + // Directory + if (stats.isDirectory()) { + // Matched + if (match & MatchKind.Directory && options.matchDirectories) { + yield item.path + } + // Descend? + else if (!partialMatch) { + continue + } + + // Push the child items in reverse + const childLevel = item.level + 1 + const childItems = (await fs.promises.readdir(item.path)).map( + x => new SearchState(path.join(item.path, x), childLevel) + ) + stack.push(...childItems.reverse()) + } + // File + else if (match & MatchKind.File) { + yield item.path + } + } + } + + /** + * Constructs a DefaultGlobber + */ + static async create( + patterns: string, + options?: GlobOptions + ): Promise { + const result = new DefaultGlobber(options) + + if (IS_WINDOWS) { + patterns = patterns.replace(/\r\n/g, '\n') + patterns = patterns.replace(/\r/g, '\n') + } + + const lines = patterns.split('\n').map(x => x.trim()) + for (const line of lines) { + // Empty or comment + if (!line || line.startsWith('#')) { + continue + } + // Pattern + else { + result.patterns.push(new Pattern(line)) + } + } + + result.searchPaths.push(...patternHelper.getSearchPaths(result.patterns)) + + return result + } + + private static async stat( + item: SearchState, + options: GlobOptions, + traversalChain: string[] + ): Promise { + // Note: + // `stat` returns info about the target of a symlink (or symlink chain) + // `lstat` returns info about a symlink itself + let stats: fs.Stats + if (options.followSymbolicLinks) { + try { + // Use `stat` (following symlinks) + stats = await fs.promises.stat(item.path) + } catch (err) { + if (err.code === 'ENOENT') { + if (options.omitBrokenSymbolicLinks) { + core.debug(`Broken symlink '${item.path}'`) + return undefined + } + + throw new Error( + `No information found for the path '${item.path}'. This may indicate a broken symbolic link.` + ) + } + + throw err + } + } else { + // Use `lstat` (not following symlinks) + stats = await fs.promises.lstat(item.path) + } + + // Note, isDirectory() returns false for the lstat of a symlink + if (stats.isDirectory() && options.followSymbolicLinks) { + // Get the realpath + const realPath: string = await fs.promises.realpath(item.path) + + // Fixup the traversal chain to match the item level + while (traversalChain.length >= item.level) { + traversalChain.pop() + } + + // Test for a cycle + if (traversalChain.some((x: string) => x === realPath)) { + core.debug( + `Symlink cycle detected for path '${item.path}' and realpath '${realPath}'` + ) + return undefined + } + + // Update the traversal chain + traversalChain.push(realPath) + } + + return stats + } +} diff --git a/actions-toolkit/packages/glob/src/internal-hash-file-options.ts b/actions-toolkit/packages/glob/src/internal-hash-file-options.ts new file mode 100644 index 00000000..00f9bb5f --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-hash-file-options.ts @@ -0,0 +1,12 @@ +/** + * Options to control globbing behavior + */ +export interface HashFileOptions { + /** + * Indicates whether to follow symbolic links. Generally should set to false + * when deleting files. + * + * @default true + */ + followSymbolicLinks?: boolean +} diff --git a/actions-toolkit/packages/glob/src/internal-hash-files.ts b/actions-toolkit/packages/glob/src/internal-hash-files.ts new file mode 100644 index 00000000..d968db5e --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-hash-files.ts @@ -0,0 +1,49 @@ +import * as crypto from 'crypto' +import * as core from '@actions/core' +import * as fs from 'fs' +import * as stream from 'stream' +import * as util from 'util' +import * as path from 'path' +import {Globber} from './glob' + +export async function hashFiles( + globber: Globber, + currentWorkspace: string, + verbose: Boolean = false +): Promise { + const writeDelegate = verbose ? core.info : core.debug + let hasMatch = false + const githubWorkspace = currentWorkspace + ? currentWorkspace + : process.env['GITHUB_WORKSPACE'] ?? process.cwd() + const result = crypto.createHash('sha256') + let count = 0 + for await (const file of globber.globGenerator()) { + writeDelegate(file) + if (!file.startsWith(`${githubWorkspace}${path.sep}`)) { + writeDelegate(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`) + continue + } + if (fs.statSync(file).isDirectory()) { + writeDelegate(`Skip directory '${file}'.`) + continue + } + const hash = crypto.createHash('sha256') + const pipeline = util.promisify(stream.pipeline) + await pipeline(fs.createReadStream(file), hash) + result.write(hash.digest()) + count++ + if (!hasMatch) { + hasMatch = true + } + } + result.end() + + if (hasMatch) { + writeDelegate(`Found ${count} files to hash.`) + return result.digest('hex') + } else { + writeDelegate(`No matches found for glob`) + return '' + } +} diff --git a/actions-toolkit/packages/glob/src/internal-match-kind.ts b/actions-toolkit/packages/glob/src/internal-match-kind.ts new file mode 100644 index 00000000..75802da5 --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-match-kind.ts @@ -0,0 +1,16 @@ +/** + * Indicates whether a pattern matches a path + */ +export enum MatchKind { + /** Not matched */ + None = 0, + + /** Matched if the path is a directory */ + Directory = 1, + + /** Matched if the path is a regular file */ + File = 2, + + /** Matched */ + All = Directory | File +} diff --git a/actions-toolkit/packages/glob/src/internal-path-helper.ts b/actions-toolkit/packages/glob/src/internal-path-helper.ts new file mode 100644 index 00000000..0931bd2c --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-path-helper.ts @@ -0,0 +1,206 @@ +import * as path from 'path' +import assert from 'assert' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * Similar to path.dirname except normalizes the path separators and slightly better handling for Windows UNC paths. + * + * For example, on Linux/macOS: + * - `/ => /` + * - `/hello => /` + * + * For example, on Windows: + * - `C:\ => C:\` + * - `C:\hello => C:\` + * - `C: => C:` + * - `C:hello => C:` + * - `\ => \` + * - `\hello => \` + * - `\\hello => \\hello` + * - `\\hello\world => \\hello\world` + */ +export function dirname(p: string): string { + // Normalize slashes and trim unnecessary trailing slash + p = safeTrimTrailingSeparator(p) + + // Windows UNC root, e.g. \\hello or \\hello\world + if (IS_WINDOWS && /^\\\\[^\\]+(\\[^\\]+)?$/.test(p)) { + return p + } + + // Get dirname + let result = path.dirname(p) + + // Trim trailing slash for Windows UNC root, e.g. \\hello\world\ + if (IS_WINDOWS && /^\\\\[^\\]+\\[^\\]+\\$/.test(result)) { + result = safeTrimTrailingSeparator(result) + } + + return result +} + +/** + * Roots the path if not already rooted. On Windows, relative roots like `\` + * or `C:` are expanded based on the current working directory. + */ +export function ensureAbsoluteRoot(root: string, itemPath: string): string { + assert(root, `ensureAbsoluteRoot parameter 'root' must not be empty`) + assert(itemPath, `ensureAbsoluteRoot parameter 'itemPath' must not be empty`) + + // Already rooted + if (hasAbsoluteRoot(itemPath)) { + return itemPath + } + + // Windows + if (IS_WINDOWS) { + // Check for itemPath like C: or C:foo + if (itemPath.match(/^[A-Z]:[^\\/]|^[A-Z]:$/i)) { + let cwd = process.cwd() + assert( + cwd.match(/^[A-Z]:\\/i), + `Expected current directory to start with an absolute drive root. Actual '${cwd}'` + ) + + // Drive letter matches cwd? Expand to cwd + if (itemPath[0].toUpperCase() === cwd[0].toUpperCase()) { + // Drive only, e.g. C: + if (itemPath.length === 2) { + // Preserve specified drive letter case (upper or lower) + return `${itemPath[0]}:\\${cwd.substr(3)}` + } + // Drive + path, e.g. C:foo + else { + if (!cwd.endsWith('\\')) { + cwd += '\\' + } + // Preserve specified drive letter case (upper or lower) + return `${itemPath[0]}:\\${cwd.substr(3)}${itemPath.substr(2)}` + } + } + // Different drive + else { + return `${itemPath[0]}:\\${itemPath.substr(2)}` + } + } + // Check for itemPath like \ or \foo + else if (normalizeSeparators(itemPath).match(/^\\$|^\\[^\\]/)) { + const cwd = process.cwd() + assert( + cwd.match(/^[A-Z]:\\/i), + `Expected current directory to start with an absolute drive root. Actual '${cwd}'` + ) + + return `${cwd[0]}:\\${itemPath.substr(1)}` + } + } + + assert( + hasAbsoluteRoot(root), + `ensureAbsoluteRoot parameter 'root' must have an absolute root` + ) + + // Otherwise ensure root ends with a separator + if (root.endsWith('/') || (IS_WINDOWS && root.endsWith('\\'))) { + // Intentionally empty + } else { + // Append separator + root += path.sep + } + + return root + itemPath +} + +/** + * On Linux/macOS, true if path starts with `/`. On Windows, true for paths like: + * `\\hello\share` and `C:\hello` (and using alternate separator). + */ +export function hasAbsoluteRoot(itemPath: string): boolean { + assert(itemPath, `hasAbsoluteRoot parameter 'itemPath' must not be empty`) + + // Normalize separators + itemPath = normalizeSeparators(itemPath) + + // Windows + if (IS_WINDOWS) { + // E.g. \\hello\share or C:\hello + return itemPath.startsWith('\\\\') || /^[A-Z]:\\/i.test(itemPath) + } + + // E.g. /hello + return itemPath.startsWith('/') +} + +/** + * On Linux/macOS, true if path starts with `/`. On Windows, true for paths like: + * `\`, `\hello`, `\\hello\share`, `C:`, and `C:\hello` (and using alternate separator). + */ +export function hasRoot(itemPath: string): boolean { + assert(itemPath, `isRooted parameter 'itemPath' must not be empty`) + + // Normalize separators + itemPath = normalizeSeparators(itemPath) + + // Windows + if (IS_WINDOWS) { + // E.g. \ or \hello or \\hello + // E.g. C: or C:\hello + return itemPath.startsWith('\\') || /^[A-Z]:/i.test(itemPath) + } + + // E.g. /hello + return itemPath.startsWith('/') +} + +/** + * Removes redundant slashes and converts `/` to `\` on Windows + */ +export function normalizeSeparators(p: string): string { + p = p || '' + + // Windows + if (IS_WINDOWS) { + // Convert slashes on Windows + p = p.replace(/\//g, '\\') + + // Remove redundant slashes + const isUnc = /^\\\\+[^\\]/.test(p) // e.g. \\hello + return (isUnc ? '\\' : '') + p.replace(/\\\\+/g, '\\') // preserve leading \\ for UNC + } + + // Remove redundant slashes + return p.replace(/\/\/+/g, '/') +} + +/** + * Normalizes the path separators and trims the trailing separator (when safe). + * For example, `/foo/ => /foo` but `/ => /` + */ +export function safeTrimTrailingSeparator(p: string): string { + // Short-circuit if empty + if (!p) { + return '' + } + + // Normalize separators + p = normalizeSeparators(p) + + // No trailing slash + if (!p.endsWith(path.sep)) { + return p + } + + // Check '/' on Linux/macOS and '\' on Windows + if (p === path.sep) { + return p + } + + // On Windows check if drive root. E.g. C:\ + if (IS_WINDOWS && /^[A-Z]:\\$/i.test(p)) { + return p + } + + // Otherwise trim trailing slash + return p.substr(0, p.length - 1) +} diff --git a/actions-toolkit/packages/glob/src/internal-path.ts b/actions-toolkit/packages/glob/src/internal-path.ts new file mode 100644 index 00000000..47001d5a --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-path.ts @@ -0,0 +1,113 @@ +import * as path from 'path' +import * as pathHelper from './internal-path-helper' +import assert from 'assert' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * Helper class for parsing paths into segments + */ +export class Path { + segments: string[] = [] + + /** + * Constructs a Path + * @param itemPath Path or array of segments + */ + constructor(itemPath: string | string[]) { + // String + if (typeof itemPath === 'string') { + assert(itemPath, `Parameter 'itemPath' must not be empty`) + + // Normalize slashes and trim unnecessary trailing slash + itemPath = pathHelper.safeTrimTrailingSeparator(itemPath) + + // Not rooted + if (!pathHelper.hasRoot(itemPath)) { + this.segments = itemPath.split(path.sep) + } + // Rooted + else { + // Add all segments, while not at the root + let remaining = itemPath + let dir = pathHelper.dirname(remaining) + while (dir !== remaining) { + // Add the segment + const basename = path.basename(remaining) + this.segments.unshift(basename) + + // Truncate the last segment + remaining = dir + dir = pathHelper.dirname(remaining) + } + + // Remainder is the root + this.segments.unshift(remaining) + } + } + // Array + else { + // Must not be empty + assert( + itemPath.length > 0, + `Parameter 'itemPath' must not be an empty array` + ) + + // Each segment + for (let i = 0; i < itemPath.length; i++) { + let segment = itemPath[i] + + // Must not be empty + assert( + segment, + `Parameter 'itemPath' must not contain any empty segments` + ) + + // Normalize slashes + segment = pathHelper.normalizeSeparators(itemPath[i]) + + // Root segment + if (i === 0 && pathHelper.hasRoot(segment)) { + segment = pathHelper.safeTrimTrailingSeparator(segment) + assert( + segment === pathHelper.dirname(segment), + `Parameter 'itemPath' root segment contains information for multiple segments` + ) + this.segments.push(segment) + } + // All other segments + else { + // Must not contain slash + assert( + !segment.includes(path.sep), + `Parameter 'itemPath' contains unexpected path separators` + ) + this.segments.push(segment) + } + } + } + } + + /** + * Converts the path to it's string representation + */ + toString(): string { + // First segment + let result = this.segments[0] + + // All others + let skipSlash = + result.endsWith(path.sep) || (IS_WINDOWS && /^[A-Z]:$/i.test(result)) + for (let i = 1; i < this.segments.length; i++) { + if (skipSlash) { + skipSlash = false + } else { + result += path.sep + } + + result += this.segments[i] + } + + return result + } +} diff --git a/actions-toolkit/packages/glob/src/internal-pattern-helper.ts b/actions-toolkit/packages/glob/src/internal-pattern-helper.ts new file mode 100644 index 00000000..e0cf3a6a --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-pattern-helper.ts @@ -0,0 +1,81 @@ +import * as pathHelper from './internal-path-helper' +import {MatchKind} from './internal-match-kind' +import {Pattern} from './internal-pattern' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * Given an array of patterns, returns an array of paths to search. + * Duplicates and paths under other included paths are filtered out. + */ +export function getSearchPaths(patterns: Pattern[]): string[] { + // Ignore negate patterns + patterns = patterns.filter(x => !x.negate) + + // Create a map of all search paths + const searchPathMap: {[key: string]: string} = {} + for (const pattern of patterns) { + const key = IS_WINDOWS + ? pattern.searchPath.toUpperCase() + : pattern.searchPath + searchPathMap[key] = 'candidate' + } + + const result: string[] = [] + + for (const pattern of patterns) { + // Check if already included + const key = IS_WINDOWS + ? pattern.searchPath.toUpperCase() + : pattern.searchPath + if (searchPathMap[key] === 'included') { + continue + } + + // Check for an ancestor search path + let foundAncestor = false + let tempKey = key + let parent = pathHelper.dirname(tempKey) + while (parent !== tempKey) { + if (searchPathMap[parent]) { + foundAncestor = true + break + } + + tempKey = parent + parent = pathHelper.dirname(tempKey) + } + + // Include the search pattern in the result + if (!foundAncestor) { + result.push(pattern.searchPath) + searchPathMap[key] = 'included' + } + } + + return result +} + +/** + * Matches the patterns against the path + */ +export function match(patterns: Pattern[], itemPath: string): MatchKind { + let result: MatchKind = MatchKind.None + + for (const pattern of patterns) { + if (pattern.negate) { + result &= ~pattern.match(itemPath) + } else { + result |= pattern.match(itemPath) + } + } + + return result +} + +/** + * Checks whether to descend further into the directory + */ +export function partialMatch(patterns: Pattern[], itemPath: string): boolean { + return patterns.some(x => !x.negate && x.partialMatch(itemPath)) +} diff --git a/actions-toolkit/packages/glob/src/internal-pattern.ts b/actions-toolkit/packages/glob/src/internal-pattern.ts new file mode 100644 index 00000000..e1dbbda8 --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-pattern.ts @@ -0,0 +1,340 @@ +import * as os from 'os' +import * as path from 'path' +import * as pathHelper from './internal-path-helper' +import assert from 'assert' +import {Minimatch, IMinimatch, IOptions as IMinimatchOptions} from 'minimatch' +import {MatchKind} from './internal-match-kind' +import {Path} from './internal-path' + +const IS_WINDOWS = process.platform === 'win32' + +export class Pattern { + /** + * Indicates whether matches should be excluded from the result set + */ + readonly negate: boolean = false + + /** + * The directory to search. The literal path prior to the first glob segment. + */ + readonly searchPath: string + + /** + * The path/pattern segments. Note, only the first segment (the root directory) + * may contain a directory separator character. Use the trailingSeparator field + * to determine whether the pattern ended with a trailing slash. + */ + readonly segments: string[] + + /** + * Indicates the pattern should only match directories, not regular files. + */ + readonly trailingSeparator: boolean + + /** + * The Minimatch object used for matching + */ + private readonly minimatch: IMinimatch + + /** + * Used to workaround a limitation with Minimatch when determining a partial + * match and the path is a root directory. For example, when the pattern is + * `/foo/**` or `C:\foo\**` and the path is `/` or `C:\`. + */ + private readonly rootRegExp: RegExp + + /** + * Indicates that the pattern is implicitly added as opposed to user specified. + */ + private readonly isImplicitPattern: boolean + + constructor(pattern: string) + constructor( + pattern: string, + isImplicitPattern: boolean, + segments: undefined, + homedir: string + ) + constructor( + negate: boolean, + isImplicitPattern: boolean, + segments: string[], + homedir?: string + ) + constructor( + patternOrNegate: string | boolean, + isImplicitPattern = false, + segments?: string[], + homedir?: string + ) { + // Pattern overload + let pattern: string + if (typeof patternOrNegate === 'string') { + pattern = patternOrNegate.trim() + } + // Segments overload + else { + // Convert to pattern + segments = segments || [] + assert(segments.length, `Parameter 'segments' must not empty`) + const root = Pattern.getLiteral(segments[0]) + assert( + root && pathHelper.hasAbsoluteRoot(root), + `Parameter 'segments' first element must be a root path` + ) + pattern = new Path(segments).toString().trim() + if (patternOrNegate) { + pattern = `!${pattern}` + } + } + + // Negate + while (pattern.startsWith('!')) { + this.negate = !this.negate + pattern = pattern.substr(1).trim() + } + + // Normalize slashes and ensures absolute root + pattern = Pattern.fixupPattern(pattern, homedir) + + // Segments + this.segments = new Path(pattern).segments + + // Trailing slash indicates the pattern should only match directories, not regular files + this.trailingSeparator = pathHelper + .normalizeSeparators(pattern) + .endsWith(path.sep) + pattern = pathHelper.safeTrimTrailingSeparator(pattern) + + // Search path (literal path prior to the first glob segment) + let foundGlob = false + const searchSegments = this.segments + .map(x => Pattern.getLiteral(x)) + .filter(x => !foundGlob && !(foundGlob = x === '')) + this.searchPath = new Path(searchSegments).toString() + + // Root RegExp (required when determining partial match) + this.rootRegExp = new RegExp( + Pattern.regExpEscape(searchSegments[0]), + IS_WINDOWS ? 'i' : '' + ) + + this.isImplicitPattern = isImplicitPattern + + // Create minimatch + const minimatchOptions: IMinimatchOptions = { + dot: true, + nobrace: true, + nocase: IS_WINDOWS, + nocomment: true, + noext: true, + nonegate: true + } + pattern = IS_WINDOWS ? pattern.replace(/\\/g, '/') : pattern + this.minimatch = new Minimatch(pattern, minimatchOptions) + } + + /** + * Matches the pattern against the specified path + */ + match(itemPath: string): MatchKind { + // Last segment is globstar? + if (this.segments[this.segments.length - 1] === '**') { + // Normalize slashes + itemPath = pathHelper.normalizeSeparators(itemPath) + + // Append a trailing slash. Otherwise Minimatch will not match the directory immediately + // preceding the globstar. For example, given the pattern `/foo/**`, Minimatch returns + // false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk. + if (!itemPath.endsWith(path.sep) && this.isImplicitPattern === false) { + // Note, this is safe because the constructor ensures the pattern has an absolute root. + // For example, formats like C: and C:foo on Windows are resolved to an absolute root. + itemPath = `${itemPath}${path.sep}` + } + } else { + // Normalize slashes and trim unnecessary trailing slash + itemPath = pathHelper.safeTrimTrailingSeparator(itemPath) + } + + // Match + if (this.minimatch.match(itemPath)) { + return this.trailingSeparator ? MatchKind.Directory : MatchKind.All + } + + return MatchKind.None + } + + /** + * Indicates whether the pattern may match descendants of the specified path + */ + partialMatch(itemPath: string): boolean { + // Normalize slashes and trim unnecessary trailing slash + itemPath = pathHelper.safeTrimTrailingSeparator(itemPath) + + // matchOne does not handle root path correctly + if (pathHelper.dirname(itemPath) === itemPath) { + return this.rootRegExp.test(itemPath) + } + + return this.minimatch.matchOne( + itemPath.split(IS_WINDOWS ? /\\+/ : /\/+/), + this.minimatch.set[0], + true + ) + } + + /** + * Escapes glob patterns within a path + */ + static globEscape(s: string): string { + return (IS_WINDOWS ? s : s.replace(/\\/g, '\\\\')) // escape '\' on Linux/macOS + .replace(/(\[)(?=[^/]+\])/g, '[[]') // escape '[' when ']' follows within the path segment + .replace(/\?/g, '[?]') // escape '?' + .replace(/\*/g, '[*]') // escape '*' + } + + /** + * Normalizes slashes and ensures absolute root + */ + private static fixupPattern(pattern: string, homedir?: string): string { + // Empty + assert(pattern, 'pattern cannot be empty') + + // Must not contain `.` segment, unless first segment + // Must not contain `..` segment + const literalSegments = new Path(pattern).segments.map(x => + Pattern.getLiteral(x) + ) + assert( + literalSegments.every((x, i) => (x !== '.' || i === 0) && x !== '..'), + `Invalid pattern '${pattern}'. Relative pathing '.' and '..' is not allowed.` + ) + + // Must not contain globs in root, e.g. Windows UNC path \\foo\b*r + assert( + !pathHelper.hasRoot(pattern) || literalSegments[0], + `Invalid pattern '${pattern}'. Root segment must not contain globs.` + ) + + // Normalize slashes + pattern = pathHelper.normalizeSeparators(pattern) + + // Replace leading `.` segment + if (pattern === '.' || pattern.startsWith(`.${path.sep}`)) { + pattern = Pattern.globEscape(process.cwd()) + pattern.substr(1) + } + // Replace leading `~` segment + else if (pattern === '~' || pattern.startsWith(`~${path.sep}`)) { + homedir = homedir || os.homedir() + assert(homedir, 'Unable to determine HOME directory') + assert( + pathHelper.hasAbsoluteRoot(homedir), + `Expected HOME directory to be a rooted path. Actual '${homedir}'` + ) + pattern = Pattern.globEscape(homedir) + pattern.substr(1) + } + // Replace relative drive root, e.g. pattern is C: or C:foo + else if ( + IS_WINDOWS && + (pattern.match(/^[A-Z]:$/i) || pattern.match(/^[A-Z]:[^\\]/i)) + ) { + let root = pathHelper.ensureAbsoluteRoot( + 'C:\\dummy-root', + pattern.substr(0, 2) + ) + if (pattern.length > 2 && !root.endsWith('\\')) { + root += '\\' + } + pattern = Pattern.globEscape(root) + pattern.substr(2) + } + // Replace relative root, e.g. pattern is \ or \foo + else if (IS_WINDOWS && (pattern === '\\' || pattern.match(/^\\[^\\]/))) { + let root = pathHelper.ensureAbsoluteRoot('C:\\dummy-root', '\\') + if (!root.endsWith('\\')) { + root += '\\' + } + pattern = Pattern.globEscape(root) + pattern.substr(1) + } + // Otherwise ensure absolute root + else { + pattern = pathHelper.ensureAbsoluteRoot( + Pattern.globEscape(process.cwd()), + pattern + ) + } + + return pathHelper.normalizeSeparators(pattern) + } + + /** + * Attempts to unescape a pattern segment to create a literal path segment. + * Otherwise returns empty string. + */ + private static getLiteral(segment: string): string { + let literal = '' + for (let i = 0; i < segment.length; i++) { + const c = segment[i] + // Escape + if (c === '\\' && !IS_WINDOWS && i + 1 < segment.length) { + literal += segment[++i] + continue + } + // Wildcard + else if (c === '*' || c === '?') { + return '' + } + // Character set + else if (c === '[' && i + 1 < segment.length) { + let set = '' + let closed = -1 + for (let i2 = i + 1; i2 < segment.length; i2++) { + const c2 = segment[i2] + // Escape + if (c2 === '\\' && !IS_WINDOWS && i2 + 1 < segment.length) { + set += segment[++i2] + continue + } + // Closed + else if (c2 === ']') { + closed = i2 + break + } + // Otherwise + else { + set += c2 + } + } + + // Closed? + if (closed >= 0) { + // Cannot convert + if (set.length > 1) { + return '' + } + + // Convert to literal + if (set) { + literal += set + i = closed + continue + } + } + + // Otherwise fall thru + } + + // Append + literal += c + } + + return literal + } + + /** + * Escapes regexp special characters + * https://javascript.info/regexp-escaping + */ + private static regExpEscape(s: string): string { + return s.replace(/[[\\^$.|?*+()]/g, '\\$&') + } +} diff --git a/actions-toolkit/packages/glob/src/internal-search-state.ts b/actions-toolkit/packages/glob/src/internal-search-state.ts new file mode 100644 index 00000000..590db3d8 --- /dev/null +++ b/actions-toolkit/packages/glob/src/internal-search-state.ts @@ -0,0 +1,9 @@ +export class SearchState { + readonly path: string + readonly level: number + + constructor(path: string, level: number) { + this.path = path + this.level = level + } +} diff --git a/actions-toolkit/packages/glob/tsconfig.json b/actions-toolkit/packages/glob/tsconfig.json new file mode 100644 index 00000000..a8b812a6 --- /dev/null +++ b/actions-toolkit/packages/glob/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/http-client/.gitignore b/actions-toolkit/packages/http-client/.gitignore new file mode 100644 index 00000000..d481b576 --- /dev/null +++ b/actions-toolkit/packages/http-client/.gitignore @@ -0,0 +1,2 @@ +testoutput.txt +npm-debug.log diff --git a/actions-toolkit/packages/http-client/LICENSE b/actions-toolkit/packages/http-client/LICENSE new file mode 100644 index 00000000..5823a51c --- /dev/null +++ b/actions-toolkit/packages/http-client/LICENSE @@ -0,0 +1,21 @@ +Actions Http Client for Node.js + +Copyright (c) GitHub, Inc. + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/actions-toolkit/packages/http-client/README.md b/actions-toolkit/packages/http-client/README.md new file mode 100644 index 00000000..7e06adeb --- /dev/null +++ b/actions-toolkit/packages/http-client/README.md @@ -0,0 +1,73 @@ +# `@actions/http-client` + +A lightweight HTTP client optimized for building actions. + +## Features + + - HTTP client with TypeScript generics and async/await/Promises + - Typings included! + - [Proxy support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners) just works with actions and the runner + - Targets ES2019 (runner runs actions with node 12+). Only supported on node 12+. + - Basic, Bearer and PAT Support out of the box. Extensible handlers for others. + - Redirects supported + +Features and releases [here](./RELEASES.md) + +## Install + +``` +npm install @actions/http-client --save +``` + +## Samples + +See the [tests](./__tests__) for detailed examples. + +## Errors + +### HTTP + +The HTTP client does not throw unless truly exceptional. + +* A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body. +* Redirects (3xx) will be followed by default. + +See the [tests](./__tests__) for detailed examples. + +## Debugging + +To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible: + +```shell +export NODE_DEBUG=http +``` + +## Node support + +The http-client is built using the latest LTS version of Node 12. It may work on previous node LTS versions but it's tested and officially supported on Node12+. + +## Support and Versioning + +We follow semver and will hold compatibility between major versions and increment the minor version with new features and capabilities (while holding compat). + +## Contributing + +We welcome PRs. Please create an issue and if applicable, a design before proceeding with code. + +once: + +``` +npm install +``` + +To build: + +``` +npm run build +``` + +To run all tests: + +``` +npm test +``` diff --git a/actions-toolkit/packages/http-client/RELEASES.md b/actions-toolkit/packages/http-client/RELEASES.md new file mode 100644 index 00000000..be4ce085 --- /dev/null +++ b/actions-toolkit/packages/http-client/RELEASES.md @@ -0,0 +1,49 @@ +## Releases + +## 2.2.0 +- Add function to return proxy agent dispatcher for compatibility with latest octokit packages [#1547](https://github.com/actions/toolkit/pull/1547) + +## 2.1.1 +- Add `HttpClientResponse.readBodyBuffer` method to read from a response stream and return a buffer [#1475](https://github.com/actions/toolkit/pull/1475) + +## 2.1.0 +- Add support for `*` and subdomains in `no_proxy` [#1355](https://github.com/actions/toolkit/pull/1355) [#1223](https://github.com/actions/toolkit/pull/1223) +- Bypass proxy for loopback IP adresses [#1360](https://github.com/actions/toolkit/pull/1360)) + +## 2.0.1 +- Fix an issue with missing `tunnel` dependency [#1085](https://github.com/actions/toolkit/pull/1085) + +## 2.0.0 +- The package is now compiled with TypeScript's [`strict` compiler setting](https://www.typescriptlang.org/tsconfig#strict). To comply with stricter rules: + - Some exported types now include `| null` or `| undefined`, matching their actual behavior. + - Types implementing the method `RequestHandler.handleAuthentication()` now throw an `Error` rather than returning `null` if they do not support handling an HTTP 401 response. Callers can still use `canHandleAuthentication()` to determine if this handling is supported or not. + - Types using `any` have been scoped to more specific types. +- Following TypeScript's naming conventions, exported interfaces no longer begin with the prefix `I-`. +- Delete the `IHttpClientResponse` interface in favor of the `HttpClientResponse` class. +- Delete the `IHeaders` interface in favor of `http.OutgoingHttpHeaders`. +- The source code of the package was moved to build with [actions/toolkit](https://github.com/actions/toolkit). + +## 1.0.11 + +Contains a bug fix where proxy is defined without a user and password. see [PR here](https://github.com/actions/http-client/pull/42) + +## 1.0.9 +Throw HttpClientError instead of a generic Error from the \Json() helper methods when the server responds with a non-successful status code. + +## 1.0.8 +Fixed security issue where a redirect (e.g. 302) to another domain would pass headers. The fix was to strip the authorization header if the hostname was different. More [details in PR #27](https://github.com/actions/http-client/pull/27) + +## 1.0.7 +Update NPM dependencies and add 429 to the list of HttpCodes + +## 1.0.6 +Automatically sends Content-Type and Accept application/json headers for \Json() helper methods if not set in the client or parameters. + +## 1.0.5 +Adds \Json() helper methods for json over http scenarios. + +## 1.0.4 +Started to add \Json() helper methods. Do not use this release for that. Use >= 1.0.5 since there was an issue with types. + +## 1.0.1 to 1.0.3 +Adds proxy support. diff --git a/actions-toolkit/packages/http-client/__tests__/auth.test.ts b/actions-toolkit/packages/http-client/__tests__/auth.test.ts new file mode 100644 index 00000000..50d0a6a4 --- /dev/null +++ b/actions-toolkit/packages/http-client/__tests__/auth.test.ts @@ -0,0 +1,72 @@ +import * as httpm from '../lib' +import * as am from '../lib/auth' + +describe('auth', () => { + beforeEach(() => {}) + + afterEach(() => {}) + + it('does basic http get request with basic auth', async () => { + const bh: am.BasicCredentialHandler = new am.BasicCredentialHandler( + 'johndoe', + 'password' + ) + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ + bh + ]) + const res: httpm.HttpClientResponse = await http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + const auth: string = obj.headers.authorization + const creds: string = Buffer.from( + auth.substring('Basic '.length), + 'base64' + ).toString() + expect(creds).toBe('johndoe:password') + expect(obj.url).toBe('http://postman-echo.com/get') + }) + + it('does basic http get request with pat token auth', async () => { + const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs' + const ph: am.PersonalAccessTokenCredentialHandler = + new am.PersonalAccessTokenCredentialHandler(token) + + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ + ph + ]) + const res: httpm.HttpClientResponse = await http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + const auth: string = obj.headers.authorization + const creds: string = Buffer.from( + auth.substring('Basic '.length), + 'base64' + ).toString() + expect(creds).toBe(`PAT:${token}`) + expect(obj.url).toBe('http://postman-echo.com/get') + }) + + it('does basic http get request with pat token auth', async () => { + const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs' + const ph: am.BearerCredentialHandler = new am.BearerCredentialHandler(token) + + const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ + ph + ]) + const res: httpm.HttpClientResponse = await http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + const auth: string = obj.headers.authorization + expect(auth).toBe(`Bearer ${token}`) + expect(obj.url).toBe('http://postman-echo.com/get') + }) +}) diff --git a/actions-toolkit/packages/http-client/__tests__/basics.test.ts b/actions-toolkit/packages/http-client/__tests__/basics.test.ts new file mode 100644 index 00000000..1e715ce9 --- /dev/null +++ b/actions-toolkit/packages/http-client/__tests__/basics.test.ts @@ -0,0 +1,376 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as httpm from '..' +import * as path from 'path' +import * as fs from 'fs' + +const sampleFilePath: string = path.join(__dirname, 'testoutput.txt') + +interface HttpBinData { + url: string + data: any + json: any + headers: any + args?: any +} + +describe('basics', () => { + let _http: httpm.HttpClient + + beforeEach(() => { + _http = new httpm.HttpClient('http-client-tests') + }) + + afterEach(() => {}) + + it('constructs', () => { + const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests') + expect(http).toBeDefined() + }) + + // responses from httpbin return something like: + // { + // "args": {}, + // "headers": { + // "Connection": "close", + // "Host": "postman-echo.com", + // "user-agent": "typed-test-client-tests" + // }, + // "origin": "173.95.152.44", + // "url": "https://postman-echo.com/get" + // } + + it('does basic http get request', async () => { + const res: httpm.HttpClientResponse = await _http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('http://postman-echo.com/get') + expect(obj.headers['user-agent']).toBeTruthy() + }) + + it('does basic http get request with no user agent', async () => { + const http: httpm.HttpClient = new httpm.HttpClient() + const res: httpm.HttpClientResponse = await http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('http://postman-echo.com/get') + expect(obj.headers['user-agent']).toBeFalsy() + }) + + it('does basic https get request', async () => { + const res: httpm.HttpClientResponse = await _http.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('https://postman-echo.com/get') + }) + + it('does basic http get request with default headers', async () => { + const http: httpm.HttpClient = new httpm.HttpClient( + 'http-client-tests', + [], + { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + } + ) + const res: httpm.HttpClientResponse = await http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.headers.accept).toBe('application/json') + expect(obj.headers['content-type']).toBe('application/json') + expect(obj.url).toBe('http://postman-echo.com/get') + }) + + it('does basic http get request with merged headers', async () => { + const http: httpm.HttpClient = new httpm.HttpClient( + 'http-client-tests', + [], + { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + } + ) + const res: httpm.HttpClientResponse = await http.get( + 'http://postman-echo.com/get', + { + 'content-type': 'application/x-www-form-urlencoded' + } + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.headers.accept).toBe('application/json') + expect(obj.headers['content-type']).toBe( + 'application/x-www-form-urlencoded' + ) + expect(obj.url).toBe('http://postman-echo.com/get') + }) + + it('pipes a get request', async () => { + return new Promise(async resolve => { + const file = fs.createWriteStream(sampleFilePath) + ;(await _http.get('https://postman-echo.com/get')).message + .pipe(file) + .on('close', () => { + const body: string = fs.readFileSync(sampleFilePath).toString() + const obj = JSON.parse(body) + expect(obj.url).toBe('https://postman-echo.com/get') + resolve() + }) + }) + }) + + it('does basic get request with redirects', async () => { + const res: httpm.HttpClientResponse = await _http.get( + `https://postman-echo.com/redirect-to?url=${encodeURIComponent( + 'https://postman-echo.com/get' + )}` + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('https://postman-echo.com/get') + }) + + it('does basic get request with redirects (303)', async () => { + const res: httpm.HttpClientResponse = await _http.get( + `https://postman-echo.com/redirect-to?url=${encodeURIComponent( + 'https://postman-echo.com/get' + )}&status_code=303` + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('https://postman-echo.com/get') + }) + + it('returns 404 for not found get request on redirect', async () => { + const res: httpm.HttpClientResponse = await _http.get( + `https://postman-echo.com/redirect-to?url=${encodeURIComponent( + 'https://postman-echo.com/status/404' + )}&status_code=303` + ) + expect(res.message.statusCode).toBe(404) + await res.readBody() + }) + + it('does not follow redirects if disabled', async () => { + const http: httpm.HttpClient = new httpm.HttpClient( + 'typed-test-client-tests', + undefined, + {allowRedirects: false} + ) + const res: httpm.HttpClientResponse = await http.get( + `https://postman-echo.com/redirect-to?url=${encodeURIComponent( + 'https://postman-echo.com/get' + )}` + ) + expect(res.message.statusCode).toBe(302) + await res.readBody() + }) + + it('does not pass auth with diff hostname redirects', async () => { + const headers = { + accept: 'application/json', + authorization: 'shhh' + } + const res: httpm.HttpClientResponse = await _http.get( + `https://postman-echo.com/redirect-to?url=${encodeURIComponent( + 'https://www.postman-echo.com/get' + )}`, + headers + ) + + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + // httpbin "fixes" the casing + expect(obj.headers[httpm.Headers.Accept]).toBe('application/json') + expect(obj.headers['Authorization']).toBeUndefined() + expect(obj.headers['authorization']).toBeUndefined() + expect(obj.url).toBe('https://www.postman-echo.com/get') + }) + + it('does not pass Auth with diff hostname redirects', async () => { + const headers = { + Accept: 'application/json', + Authorization: 'shhh' + } + const res: httpm.HttpClientResponse = await _http.get( + `https://postman-echo.com/redirect-to?url=${encodeURIComponent( + 'https://www.postman-echo.com/get' + )}`, + headers + ) + + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + // httpbin "fixes" the casing + expect(obj.headers[httpm.Headers.Accept]).toBe('application/json') + expect(obj.headers['Authorization']).toBeUndefined() + expect(obj.headers['authorization']).toBeUndefined() + expect(obj.url).toBe('https://www.postman-echo.com/get') + }) + + it('does basic head request', async () => { + const res: httpm.HttpClientResponse = await _http.head( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + }) + + it('does basic http delete request', async () => { + const res: httpm.HttpClientResponse = await _http.del( + 'http://postman-echo.com/delete' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + JSON.parse(body) + }) + + it('does basic http post request', async () => { + const b = 'Hello World!' + const res: httpm.HttpClientResponse = await _http.post( + 'http://postman-echo.com/post', + b + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.data).toBe(b) + expect(obj.url).toBe('http://postman-echo.com/post') + }) + + it('does basic http patch request', async () => { + const b = 'Hello World!' + const res: httpm.HttpClientResponse = await _http.patch( + 'http://postman-echo.com/patch', + b + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.data).toBe(b) + expect(obj.url).toBe('http://postman-echo.com/patch') + }) + + it('does basic http options request', async () => { + const res: httpm.HttpClientResponse = await _http.options( + 'http://postman-echo.com' + ) + expect(res.message.statusCode).toBe(200) + await res.readBody() + }) + + it('returns 404 for not found get request', async () => { + const res: httpm.HttpClientResponse = await _http.get( + 'http://postman-echo.com/status/404' + ) + expect(res.message.statusCode).toBe(404) + await res.readBody() + }) + + it('gets a json object', async () => { + const jsonObj = await _http.getJson( + 'https://postman-echo.com/get' + ) + expect(jsonObj.statusCode).toBe(200) + expect(jsonObj.result).toBeDefined() + expect(jsonObj.result?.url).toBe('https://postman-echo.com/get') + expect(jsonObj.result?.headers[httpm.Headers.Accept]).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(jsonObj.headers['content-type']).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) + + it('getting a non existent json object returns null', async () => { + const jsonObj = await _http.getJson( + 'https://postman-echo.com/status/404' + ) + expect(jsonObj.statusCode).toBe(404) + expect(jsonObj.result).toBeNull() + }) + + it('posts a json object', async () => { + const res = {name: 'foo'} + const restRes = await _http.postJson( + 'https://postman-echo.com/post', + res + ) + expect(restRes.statusCode).toBe(200) + expect(restRes.result).toBeDefined() + expect(restRes.result?.url).toBe('https://postman-echo.com/post') + expect(restRes.result?.json.name).toBe('foo') + expect(restRes.result?.headers[httpm.Headers.Accept]).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(restRes.result?.headers['content-type']).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(restRes.headers['content-type']).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) + + it('puts a json object', async () => { + const res = {name: 'foo'} + const restRes = await _http.putJson( + 'https://postman-echo.com/put', + res + ) + expect(restRes.statusCode).toBe(200) + expect(restRes.result).toBeDefined() + expect(restRes.result?.url).toBe('https://postman-echo.com/put') + expect(restRes.result?.json.name).toBe('foo') + + expect(restRes.result?.headers[httpm.Headers.Accept]).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(restRes.result?.headers['content-type']).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(restRes.headers['content-type']).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) + + it('patch a json object', async () => { + const res = {name: 'foo'} + const restRes = await _http.patchJson( + 'https://postman-echo.com/patch', + res + ) + expect(restRes.statusCode).toBe(200) + expect(restRes.result).toBeDefined() + expect(restRes.result?.url).toBe('https://postman-echo.com/patch') + expect(restRes.result?.json.name).toBe('foo') + expect(restRes.result?.headers[httpm.Headers.Accept]).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(restRes.result?.headers['content-type']).toBe( + httpm.MediaTypes.ApplicationJson + ) + expect(restRes.headers['content-type']).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) +}) diff --git a/actions-toolkit/packages/http-client/__tests__/headers.test.ts b/actions-toolkit/packages/http-client/__tests__/headers.test.ts new file mode 100644 index 00000000..c1ca0ec3 --- /dev/null +++ b/actions-toolkit/packages/http-client/__tests__/headers.test.ts @@ -0,0 +1,119 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as httpm from '..' + +describe('headers', () => { + let _http: httpm.HttpClient + + beforeEach(() => { + _http = new httpm.HttpClient('http-client-tests') + }) + + it('preserves existing headers on getJson', async () => { + const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} + let jsonObj = await _http.getJson( + 'https://postman-echo.com/get', + additionalHeaders + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('foo') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + + const httpWithHeaders = new httpm.HttpClient() + httpWithHeaders.requestOptions = { + headers: { + [httpm.Headers.Accept]: 'baz' + } + } + jsonObj = await httpWithHeaders.getJson('https://postman-echo.com/get') + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('baz') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) + + it('preserves existing headers on postJson', async () => { + const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} + let jsonObj = await _http.postJson( + 'https://postman-echo.com/post', + {}, + additionalHeaders + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('foo') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + + const httpWithHeaders = new httpm.HttpClient() + httpWithHeaders.requestOptions = { + headers: { + [httpm.Headers.Accept]: 'baz' + } + } + jsonObj = await httpWithHeaders.postJson( + 'https://postman-echo.com/post', + {} + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('baz') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) + + it('preserves existing headers on putJson', async () => { + const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} + let jsonObj = await _http.putJson( + 'https://postman-echo.com/put', + {}, + additionalHeaders + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('foo') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + + const httpWithHeaders = new httpm.HttpClient() + httpWithHeaders.requestOptions = { + headers: { + [httpm.Headers.Accept]: 'baz' + } + } + jsonObj = await httpWithHeaders.putJson( + 'https://postman-echo.com/put', + {} + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('baz') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) + + it('preserves existing headers on patchJson', async () => { + const additionalHeaders = {[httpm.Headers.Accept]: 'foo'} + let jsonObj = await _http.patchJson( + 'https://postman-echo.com/patch', + {}, + additionalHeaders + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('foo') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + + const httpWithHeaders = new httpm.HttpClient() + httpWithHeaders.requestOptions = { + headers: { + [httpm.Headers.Accept]: 'baz' + } + } + jsonObj = await httpWithHeaders.patchJson( + 'https://postman-echo.com/patch', + {} + ) + expect(jsonObj.result.headers[httpm.Headers.Accept]).toBe('baz') + expect(jsonObj.headers[httpm.Headers.ContentType]).toContain( + httpm.MediaTypes.ApplicationJson + ) + }) +}) diff --git a/actions-toolkit/packages/http-client/__tests__/keepalive.test.ts b/actions-toolkit/packages/http-client/__tests__/keepalive.test.ts new file mode 100644 index 00000000..1faff5ff --- /dev/null +++ b/actions-toolkit/packages/http-client/__tests__/keepalive.test.ts @@ -0,0 +1,73 @@ +import * as httpm from '../lib' + +describe('basics', () => { + let _http: httpm.HttpClient + + beforeEach(() => { + _http = new httpm.HttpClient('http-client-tests', [], {keepAlive: true}) + }) + + afterEach(() => { + _http.dispose() + }) + + it('does basic http get request with keepAlive true', async () => { + const res: httpm.HttpClientResponse = await _http.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('http://postman-echo.com/get') + }) + + it('does basic head request with keepAlive true', async () => { + const res: httpm.HttpClientResponse = await _http.head( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + }) + + it('does basic http delete request with keepAlive true', async () => { + const res: httpm.HttpClientResponse = await _http.del( + 'http://postman-echo.com/delete' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + JSON.parse(body) + }) + + it('does basic http post request with keepAlive true', async () => { + const b = 'Hello World!' + const res: httpm.HttpClientResponse = await _http.post( + 'http://postman-echo.com/post', + b + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.data).toBe(b) + expect(obj.url).toBe('http://postman-echo.com/post') + }) + + it('does basic http patch request with keepAlive true', async () => { + const b = 'Hello World!' + const res: httpm.HttpClientResponse = await _http.patch( + 'http://postman-echo.com/patch', + b + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.data).toBe(b) + expect(obj.url).toBe('http://postman-echo.com/patch') + }) + + it('does basic http options request with keepAlive true', async () => { + const res: httpm.HttpClientResponse = await _http.options( + 'http://postman-echo.com' + ) + expect(res.message.statusCode).toBe(200) + await res.readBody() + }) +}) diff --git a/actions-toolkit/packages/http-client/__tests__/proxy.test.ts b/actions-toolkit/packages/http-client/__tests__/proxy.test.ts new file mode 100644 index 00000000..c921b4bc --- /dev/null +++ b/actions-toolkit/packages/http-client/__tests__/proxy.test.ts @@ -0,0 +1,316 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as http from 'http' +import * as httpm from '../lib/' +import * as pm from '../lib/proxy' +import {ProxyAgent} from 'undici' +// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports +const proxy = require('proxy') + +let _proxyConnects: string[] +let _proxyServer: http.Server +const _proxyUrl = 'http://127.0.0.1:8080' + +describe('proxy', () => { + beforeAll(async () => { + // Start proxy server + _proxyServer = proxy.createProxy() + await new Promise(resolve => { + const port = Number(_proxyUrl.split(':')[2]) + _proxyServer.listen(port, () => resolve()) + }) + _proxyServer.on('connect', req => { + _proxyConnects.push(req.url ?? '') + }) + }) + + beforeEach(() => { + _proxyConnects = [] + _clearVars() + }) + + afterEach(() => {}) + + afterAll(async () => { + _clearVars() + + // Stop proxy server + await new Promise(resolve => { + _proxyServer.once('close', () => resolve()) + _proxyServer.close() + }) + }) + + it('getProxyUrl does not return proxyUrl if variables not set', () => { + const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) + expect(proxyUrl).toBeUndefined() + }) + + it('getProxyUrl returns proxyUrl if https_proxy set for https url', () => { + process.env['https_proxy'] = 'https://myproxysvr' + const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) + expect(proxyUrl).toBeDefined() + }) + + it('getProxyUrl does not return proxyUrl if http_proxy set for https url', () => { + process.env['http_proxy'] = 'https://myproxysvr' + const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) + expect(proxyUrl).toBeUndefined() + }) + + it('getProxyUrl returns proxyUrl if http_proxy set for http url', () => { + process.env['http_proxy'] = 'http://myproxysvr' + const proxyUrl = pm.getProxyUrl(new URL('http://github.com')) + expect(proxyUrl).toBeDefined() + }) + + it('getProxyUrl does not return proxyUrl if https_proxy set and in no_proxy list', () => { + process.env['https_proxy'] = 'https://myproxysvr' + process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' + const proxyUrl = pm.getProxyUrl(new URL('https://myserver')) + expect(proxyUrl).toBeUndefined() + }) + + it('getProxyUrl returns proxyUrl if https_proxy set and not in no_proxy list', () => { + process.env['https_proxy'] = 'https://myproxysvr' + process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' + const proxyUrl = pm.getProxyUrl(new URL('https://github.com')) + expect(proxyUrl).toBeDefined() + }) + + it('getProxyUrl does not return proxyUrl if http_proxy set and in no_proxy list', () => { + process.env['http_proxy'] = 'http://myproxysvr' + process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' + const proxyUrl = pm.getProxyUrl(new URL('http://myserver')) + expect(proxyUrl).toBeUndefined() + }) + + it('getProxyUrl returns proxyUrl if http_proxy set and not in no_proxy list', () => { + process.env['http_proxy'] = 'http://myproxysvr' + process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' + const proxyUrl = pm.getProxyUrl(new URL('http://github.com')) + expect(proxyUrl).toBeDefined() + }) + + it('getProxyUrl returns proxyUrl if http_proxy has no protocol', () => { + process.env['http_proxy'] = 'myproxysvr' + const proxyUrl = pm.getProxyUrl(new URL('http://github.com')) + expect(proxyUrl?.toString()).toBe('http://myproxysvr/') + }) + + it('checkBypass returns true if host as no_proxy list', () => { + process.env['no_proxy'] = 'myserver' + const bypass = pm.checkBypass(new URL('https://myserver')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if host in no_proxy list', () => { + process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080' + const bypass = pm.checkBypass(new URL('https://myserver')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if host in no_proxy list with spaces', () => { + process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080' + const bypass = pm.checkBypass(new URL('https://myserver')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if host in no_proxy list with port', () => { + process.env['no_proxy'] = 'otherserver, myserver:8080 ,anotherserver' + const bypass = pm.checkBypass(new URL('https://myserver:8080')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if host with port in no_proxy list without port', () => { + process.env['no_proxy'] = 'otherserver, myserver ,anotherserver' + const bypass = pm.checkBypass(new URL('https://myserver:8080')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if host in no_proxy list with default https port', () => { + process.env['no_proxy'] = 'otherserver, myserver:443 ,anotherserver' + const bypass = pm.checkBypass(new URL('https://myserver')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if host in no_proxy list with default http port', () => { + process.env['no_proxy'] = 'otherserver, myserver:80 ,anotherserver' + const bypass = pm.checkBypass(new URL('http://myserver')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns false if host not in no_proxy list', () => { + process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080' + const bypass = pm.checkBypass(new URL('https://github.com')) + expect(bypass).toBeFalsy() + }) + + it('checkBypass returns false if empty no_proxy', () => { + process.env['no_proxy'] = '' + const bypass = pm.checkBypass(new URL('https://github.com')) + expect(bypass).toBeFalsy() + }) + + it('checkBypass returns true if host with subdomain in no_proxy', () => { + process.env['no_proxy'] = 'myserver.com' + const bypass = pm.checkBypass(new URL('https://sub.myserver.com')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns false if no_proxy is subdomain', () => { + process.env['no_proxy'] = 'myserver.com' + const bypass = pm.checkBypass(new URL('https://myserver.com.evil.org')) + expect(bypass).toBeFalsy() + }) + + it('checkBypass returns false if no_proxy is part of domain', () => { + process.env['no_proxy'] = 'myserver.com' + const bypass = pm.checkBypass(new URL('https://evilmyserver.com')) + expect(bypass).toBeFalsy() + }) + + // Do not strip leading dots as per https://github.com/actions/runner/blob/97195bad5870e2ad0915ebfef1616083aacf5818/docs/adrs/0263-proxy-support.md + it('checkBypass returns false if host with leading dot in no_proxy', () => { + process.env['no_proxy'] = '.myserver.com' + const bypass = pm.checkBypass(new URL('https://myserver.com')) + expect(bypass).toBeFalsy() + }) + + it('checkBypass returns true if host with subdomain in no_proxy defined with leading "."', () => { + process.env['no_proxy'] = '.myserver.com' + const bypass = pm.checkBypass(new URL('https://sub.myserver.com')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if no_proxy is "*"', () => { + process.env['no_proxy'] = '*' + const bypass = pm.checkBypass(new URL('https://anything.whatsoever.com')) + expect(bypass).toBeTruthy() + }) + + it('checkBypass returns true if no_proxy contains comma separated "*"', () => { + process.env['no_proxy'] = 'domain.com,* , example.com' + const bypass = pm.checkBypass(new URL('https://anything.whatsoever.com')) + expect(bypass).toBeTruthy() + }) + + it('HttpClient does basic http get request through proxy', async () => { + process.env['http_proxy'] = _proxyUrl + const httpClient = new httpm.HttpClient() + const res: httpm.HttpClientResponse = await httpClient.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('http://postman-echo.com/get') + expect(_proxyConnects).toEqual(['postman-echo.com:80']) + }) + + it('HttpClient does basic http get request when bypass proxy', async () => { + process.env['http_proxy'] = _proxyUrl + process.env['no_proxy'] = 'postman-echo.com' + const httpClient = new httpm.HttpClient() + const res: httpm.HttpClientResponse = await httpClient.get( + 'http://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('http://postman-echo.com/get') + expect(_proxyConnects).toHaveLength(0) + }) + + it('HttpClient does basic https get request through proxy', async () => { + process.env['https_proxy'] = _proxyUrl + const httpClient = new httpm.HttpClient() + const res: httpm.HttpClientResponse = await httpClient.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('https://postman-echo.com/get') + expect(_proxyConnects).toEqual(['postman-echo.com:443']) + }) + + it('HttpClient does basic https get request when bypass proxy', async () => { + process.env['https_proxy'] = _proxyUrl + process.env['no_proxy'] = 'postman-echo.com' + const httpClient = new httpm.HttpClient() + const res: httpm.HttpClientResponse = await httpClient.get( + 'https://postman-echo.com/get' + ) + expect(res.message.statusCode).toBe(200) + const body: string = await res.readBody() + const obj = JSON.parse(body) + expect(obj.url).toBe('https://postman-echo.com/get') + expect(_proxyConnects).toHaveLength(0) + }) + + it('HttpClient bypasses proxy for loopback addresses (localhost, ::1, 127.*)', async () => { + // setup a server listening on localhost:8091 + const server = http.createServer((request, response) => { + response.writeHead(200) + request.pipe(response) + }) + server.listen(8091) + try { + process.env['http_proxy'] = _proxyUrl + const httpClient = new httpm.HttpClient() + let res = await httpClient.get('http://localhost:8091') + expect(res.message.statusCode).toBe(200) + res = await httpClient.get('http://127.0.0.1:8091') + expect(res.message.statusCode).toBe(200) + + // no support for ipv6 for now + expect(httpClient.get('http://[::1]:8091')).rejects.toThrow() + + // proxy at _proxyUrl was ignored + expect(_proxyConnects).toEqual([]) + } finally { + server.close() + } + }) + + it('proxyAuth not set in tunnel agent when authentication is not provided', async () => { + process.env['https_proxy'] = 'http://127.0.0.1:8080' + const httpClient = new httpm.HttpClient() + const agent: any = httpClient.getAgent('https://some-url') + // eslint-disable-next-line no-console + console.log(agent) + expect(agent.proxyOptions.host).toBe('127.0.0.1') + expect(agent.proxyOptions.port).toBe('8080') + expect(agent.proxyOptions.proxyAuth).toBe(undefined) + }) + + it('proxyAuth is set in tunnel agent when authentication is provided', async () => { + process.env['https_proxy'] = 'http://user:password@127.0.0.1:8080' + const httpClient = new httpm.HttpClient() + const agent: any = httpClient.getAgent('https://some-url') + // eslint-disable-next-line no-console + console.log(agent) + expect(agent.proxyOptions.host).toBe('127.0.0.1') + expect(agent.proxyOptions.port).toBe('8080') + expect(agent.proxyOptions.proxyAuth).toBe('user:password') + }) + + it('ProxyAgent is returned when proxy setting are provided', async () => { + process.env['https_proxy'] = 'http://127.0.0.1:8080' + const httpClient = new httpm.HttpClient() + const agent = httpClient.getAgentDispatcher('https://some-url') + // eslint-disable-next-line no-console + console.log(agent) + expect(agent instanceof ProxyAgent).toBe(true) + }) +}) + +function _clearVars(): void { + delete process.env.http_proxy + delete process.env.HTTP_PROXY + delete process.env.https_proxy + delete process.env.HTTPS_PROXY + delete process.env.no_proxy + delete process.env.NO_PROXY +} diff --git a/actions-toolkit/packages/http-client/package-lock.json b/actions-toolkit/packages/http-client/package-lock.json new file mode 100644 index 00000000..dc7a9bd8 --- /dev/null +++ b/actions-toolkit/packages/http-client/package-lock.json @@ -0,0 +1,392 @@ +{ + "name": "@actions/http-client", + "version": "2.2.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/http-client", + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + }, + "devDependencies": { + "@types/node": "20.7.1", + "@types/proxy": "^1.0.1", + "@types/tunnel": "0.0.3", + "proxy": "^2.1.1" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@types/node": { + "version": "20.7.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz", + "integrity": "sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg==", + "dev": true + }, + "node_modules/@types/proxy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/proxy/-/proxy-1.0.2.tgz", + "integrity": "sha512-NDNsg7YuClVzEenn9SUButu43blypWvljGsIkDV7HI4N9apjrS0aeeMTUG0PYa71lD1AvIgvjkBagqHDiomDjA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/args": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "dev": true, + "dependencies": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/basic-auth-parser": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz", + "integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==", + "dev": true + }, + "node_modules/camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/proxy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz", + "integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==", + "dev": true, + "dependencies": { + "args": "^5.0.3", + "basic-auth-parser": "0.0.2-1", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/undici": { + "version": "5.25.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz", + "integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + } + }, + "dependencies": { + "@fastify/busboy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", + "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==" + }, + "@types/node": { + "version": "20.7.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz", + "integrity": "sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg==", + "dev": true + }, + "@types/proxy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/proxy/-/proxy-1.0.2.tgz", + "integrity": "sha512-NDNsg7YuClVzEenn9SUButu43blypWvljGsIkDV7HI4N9apjrS0aeeMTUG0PYa71lD1AvIgvjkBagqHDiomDjA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "args": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz", + "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==", + "dev": true, + "requires": { + "camelcase": "5.0.0", + "chalk": "2.4.2", + "leven": "2.1.0", + "mri": "1.1.4" + } + }, + "basic-auth-parser": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz", + "integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==", + "dev": true + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "dev": true + }, + "mri": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz", + "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "proxy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz", + "integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==", + "dev": true, + "requires": { + "args": "^5.0.3", + "basic-auth-parser": "0.0.2-1", + "debug": "^4.3.4" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "undici": { + "version": "5.25.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz", + "integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==", + "requires": { + "@fastify/busboy": "^2.0.0" + } + } + } +} diff --git a/actions-toolkit/packages/http-client/package.json b/actions-toolkit/packages/http-client/package.json new file mode 100644 index 00000000..ed134372 --- /dev/null +++ b/actions-toolkit/packages/http-client/package.json @@ -0,0 +1,51 @@ +{ + "name": "@actions/http-client", + "version": "2.2.0", + "description": "Actions Http Client", + "keywords": [ + "github", + "actions", + "http" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/http-client", + "license": "MIT", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "!.DS_Store" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/http-client" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "build": "tsc", + "format": "prettier --write **/*.ts", + "format-check": "prettier --check **/*.ts", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "devDependencies": { + "@types/node": "20.7.1", + "@types/tunnel": "0.0.3", + "proxy": "^2.1.1", + "@types/proxy": "^1.0.1" + }, + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } +} diff --git a/actions-toolkit/packages/http-client/src/auth.ts b/actions-toolkit/packages/http-client/src/auth.ts new file mode 100644 index 00000000..58938ac4 --- /dev/null +++ b/actions-toolkit/packages/http-client/src/auth.ts @@ -0,0 +1,87 @@ +import * as http from 'http' +import * as ifm from './interfaces' +import {HttpClientResponse} from './index' + +export class BasicCredentialHandler implements ifm.RequestHandler { + username: string + password: string + + constructor(username: string, password: string) { + this.username = username + this.password = password + } + + prepareRequest(options: http.RequestOptions): void { + if (!options.headers) { + throw Error('The request has no headers') + } + options.headers['Authorization'] = `Basic ${Buffer.from( + `${this.username}:${this.password}` + ).toString('base64')}` + } + + // This handler cannot handle 401 + canHandleAuthentication(): boolean { + return false + } + + async handleAuthentication(): Promise { + throw new Error('not implemented') + } +} + +export class BearerCredentialHandler implements ifm.RequestHandler { + token: string + + constructor(token: string) { + this.token = token + } + + // currently implements pre-authorization + // TODO: support preAuth = false where it hooks on 401 + prepareRequest(options: http.RequestOptions): void { + if (!options.headers) { + throw Error('The request has no headers') + } + options.headers['Authorization'] = `Bearer ${this.token}` + } + + // This handler cannot handle 401 + canHandleAuthentication(): boolean { + return false + } + + async handleAuthentication(): Promise { + throw new Error('not implemented') + } +} + +export class PersonalAccessTokenCredentialHandler + implements ifm.RequestHandler +{ + token: string + + constructor(token: string) { + this.token = token + } + + // currently implements pre-authorization + // TODO: support preAuth = false where it hooks on 401 + prepareRequest(options: http.RequestOptions): void { + if (!options.headers) { + throw Error('The request has no headers') + } + options.headers['Authorization'] = `Basic ${Buffer.from( + `PAT:${this.token}` + ).toString('base64')}` + } + + // This handler cannot handle 401 + canHandleAuthentication(): boolean { + return false + } + + async handleAuthentication(): Promise { + throw new Error('not implemented') + } +} diff --git a/actions-toolkit/packages/http-client/src/index.ts b/actions-toolkit/packages/http-client/src/index.ts new file mode 100644 index 00000000..f1170ea0 --- /dev/null +++ b/actions-toolkit/packages/http-client/src/index.ts @@ -0,0 +1,834 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as http from 'http' +import * as https from 'https' +import * as ifm from './interfaces' +import * as net from 'net' +import * as pm from './proxy' +import * as tunnel from 'tunnel' +import {ProxyAgent} from 'undici' + +export enum HttpCodes { + OK = 200, + MultipleChoices = 300, + MovedPermanently = 301, + ResourceMoved = 302, + SeeOther = 303, + NotModified = 304, + UseProxy = 305, + SwitchProxy = 306, + TemporaryRedirect = 307, + PermanentRedirect = 308, + BadRequest = 400, + Unauthorized = 401, + PaymentRequired = 402, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + NotAcceptable = 406, + ProxyAuthenticationRequired = 407, + RequestTimeout = 408, + Conflict = 409, + Gone = 410, + TooManyRequests = 429, + InternalServerError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + GatewayTimeout = 504 +} + +export enum Headers { + Accept = 'accept', + ContentType = 'content-type' +} + +export enum MediaTypes { + ApplicationJson = 'application/json' +} + +/** + * Returns the proxy URL, depending upon the supplied url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ +export function getProxyUrl(serverUrl: string): string { + const proxyUrl = pm.getProxyUrl(new URL(serverUrl)) + return proxyUrl ? proxyUrl.href : '' +} + +const HttpRedirectCodes: number[] = [ + HttpCodes.MovedPermanently, + HttpCodes.ResourceMoved, + HttpCodes.SeeOther, + HttpCodes.TemporaryRedirect, + HttpCodes.PermanentRedirect +] +const HttpResponseRetryCodes: number[] = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout +] +const RetryableHttpVerbs: string[] = ['OPTIONS', 'GET', 'DELETE', 'HEAD'] +const ExponentialBackoffCeiling = 10 +const ExponentialBackoffTimeSlice = 5 + +export class HttpClientError extends Error { + constructor(message: string, statusCode: number) { + super(message) + this.name = 'HttpClientError' + this.statusCode = statusCode + Object.setPrototypeOf(this, HttpClientError.prototype) + } + + statusCode: number + result?: any +} + +export class HttpClientResponse { + constructor(message: http.IncomingMessage) { + this.message = message + } + + message: http.IncomingMessage + async readBody(): Promise { + return new Promise(async resolve => { + let output = Buffer.alloc(0) + + this.message.on('data', (chunk: Buffer) => { + output = Buffer.concat([output, chunk]) + }) + + this.message.on('end', () => { + resolve(output.toString()) + }) + }) + } + + async readBodyBuffer?(): Promise { + return new Promise(async resolve => { + const chunks: Buffer[] = [] + + this.message.on('data', (chunk: Buffer) => { + chunks.push(chunk) + }) + + this.message.on('end', () => { + resolve(Buffer.concat(chunks)) + }) + }) + } +} + +export function isHttps(requestUrl: string): boolean { + const parsedUrl: URL = new URL(requestUrl) + return parsedUrl.protocol === 'https:' +} + +export class HttpClient { + userAgent: string | undefined + handlers: ifm.RequestHandler[] + requestOptions: ifm.RequestOptions | undefined + + private _ignoreSslError = false + private _socketTimeout: number | undefined + private _allowRedirects = true + private _allowRedirectDowngrade = false + private _maxRedirects = 50 + private _allowRetries = false + private _maxRetries = 1 + private _agent: any + private _proxyAgent: any + private _proxyAgentDispatcher: any + private _keepAlive = false + private _disposed = false + + constructor( + userAgent?: string, + handlers?: ifm.RequestHandler[], + requestOptions?: ifm.RequestOptions + ) { + this.userAgent = userAgent + this.handlers = handlers || [] + this.requestOptions = requestOptions + if (requestOptions) { + if (requestOptions.ignoreSslError != null) { + this._ignoreSslError = requestOptions.ignoreSslError + } + + this._socketTimeout = requestOptions.socketTimeout + + if (requestOptions.allowRedirects != null) { + this._allowRedirects = requestOptions.allowRedirects + } + + if (requestOptions.allowRedirectDowngrade != null) { + this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade + } + + if (requestOptions.maxRedirects != null) { + this._maxRedirects = Math.max(requestOptions.maxRedirects, 0) + } + + if (requestOptions.keepAlive != null) { + this._keepAlive = requestOptions.keepAlive + } + + if (requestOptions.allowRetries != null) { + this._allowRetries = requestOptions.allowRetries + } + + if (requestOptions.maxRetries != null) { + this._maxRetries = requestOptions.maxRetries + } + } + } + + async options( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}) + } + + async get( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('GET', requestUrl, null, additionalHeaders || {}) + } + + async del( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('DELETE', requestUrl, null, additionalHeaders || {}) + } + + async post( + requestUrl: string, + data: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('POST', requestUrl, data, additionalHeaders || {}) + } + + async patch( + requestUrl: string, + data: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('PATCH', requestUrl, data, additionalHeaders || {}) + } + + async put( + requestUrl: string, + data: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('PUT', requestUrl, data, additionalHeaders || {}) + } + + async head( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request('HEAD', requestUrl, null, additionalHeaders || {}) + } + + async sendStream( + verb: string, + requestUrl: string, + stream: NodeJS.ReadableStream, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise { + return this.request(verb, requestUrl, stream, additionalHeaders) + } + + /** + * Gets a typed object from an endpoint + * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise + */ + async getJson( + requestUrl: string, + additionalHeaders: http.OutgoingHttpHeaders = {} + ): Promise> { + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.Accept, + MediaTypes.ApplicationJson + ) + const res: HttpClientResponse = await this.get( + requestUrl, + additionalHeaders + ) + return this._processResponse(res, this.requestOptions) + } + + async postJson( + requestUrl: string, + obj: any, + additionalHeaders: http.OutgoingHttpHeaders = {} + ): Promise> { + const data: string = JSON.stringify(obj, null, 2) + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.Accept, + MediaTypes.ApplicationJson + ) + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.ContentType, + MediaTypes.ApplicationJson + ) + const res: HttpClientResponse = await this.post( + requestUrl, + data, + additionalHeaders + ) + return this._processResponse(res, this.requestOptions) + } + + async putJson( + requestUrl: string, + obj: any, + additionalHeaders: http.OutgoingHttpHeaders = {} + ): Promise> { + const data: string = JSON.stringify(obj, null, 2) + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.Accept, + MediaTypes.ApplicationJson + ) + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.ContentType, + MediaTypes.ApplicationJson + ) + const res: HttpClientResponse = await this.put( + requestUrl, + data, + additionalHeaders + ) + return this._processResponse(res, this.requestOptions) + } + + async patchJson( + requestUrl: string, + obj: any, + additionalHeaders: http.OutgoingHttpHeaders = {} + ): Promise> { + const data: string = JSON.stringify(obj, null, 2) + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.Accept, + MediaTypes.ApplicationJson + ) + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader( + additionalHeaders, + Headers.ContentType, + MediaTypes.ApplicationJson + ) + const res: HttpClientResponse = await this.patch( + requestUrl, + data, + additionalHeaders + ) + return this._processResponse(res, this.requestOptions) + } + + /** + * Makes a raw http request. + * All other methods such as get, post, patch, and request ultimately call this. + * Prefer get, del, post and patch + */ + async request( + verb: string, + requestUrl: string, + data: string | NodeJS.ReadableStream | null, + headers?: http.OutgoingHttpHeaders + ): Promise { + if (this._disposed) { + throw new Error('Client has already been disposed.') + } + + const parsedUrl = new URL(requestUrl) + let info: ifm.RequestInfo = this._prepareRequest(verb, parsedUrl, headers) + + // Only perform retries on reads since writes may not be idempotent. + const maxTries: number = + this._allowRetries && RetryableHttpVerbs.includes(verb) + ? this._maxRetries + 1 + : 1 + let numTries = 0 + + let response: HttpClientResponse | undefined + do { + response = await this.requestRaw(info, data) + + // Check if it's an authentication challenge + if ( + response && + response.message && + response.message.statusCode === HttpCodes.Unauthorized + ) { + let authenticationHandler: ifm.RequestHandler | undefined + + for (const handler of this.handlers) { + if (handler.canHandleAuthentication(response)) { + authenticationHandler = handler + break + } + } + + if (authenticationHandler) { + return authenticationHandler.handleAuthentication(this, info, data) + } else { + // We have received an unauthorized response but have no handlers to handle it. + // Let the response return to the caller. + return response + } + } + + let redirectsRemaining: number = this._maxRedirects + while ( + response.message.statusCode && + HttpRedirectCodes.includes(response.message.statusCode) && + this._allowRedirects && + redirectsRemaining > 0 + ) { + const redirectUrl: string | undefined = + response.message.headers['location'] + if (!redirectUrl) { + // if there's no location to redirect to, we won't + break + } + const parsedRedirectUrl = new URL(redirectUrl) + if ( + parsedUrl.protocol === 'https:' && + parsedUrl.protocol !== parsedRedirectUrl.protocol && + !this._allowRedirectDowngrade + ) { + throw new Error( + 'Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.' + ) + } + + // we need to finish reading the response before reassigning response + // which will leak the open socket. + await response.readBody() + + // strip authorization header if redirected to a different hostname + if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { + for (const header in headers) { + // header names are case insensitive + if (header.toLowerCase() === 'authorization') { + delete headers[header] + } + } + } + + // let's make the request with the new redirectUrl + info = this._prepareRequest(verb, parsedRedirectUrl, headers) + response = await this.requestRaw(info, data) + redirectsRemaining-- + } + + if ( + !response.message.statusCode || + !HttpResponseRetryCodes.includes(response.message.statusCode) + ) { + // If not a retry code, return immediately instead of retrying + return response + } + + numTries += 1 + + if (numTries < maxTries) { + await response.readBody() + await this._performExponentialBackoff(numTries) + } + } while (numTries < maxTries) + + return response + } + + /** + * Needs to be called if keepAlive is set to true in request options. + */ + dispose(): void { + if (this._agent) { + this._agent.destroy() + } + + this._disposed = true + } + + /** + * Raw request. + * @param info + * @param data + */ + async requestRaw( + info: ifm.RequestInfo, + data: string | NodeJS.ReadableStream | null + ): Promise { + return new Promise((resolve, reject) => { + function callbackForResult(err?: Error, res?: HttpClientResponse): void { + if (err) { + reject(err) + } else if (!res) { + // If `err` is not passed, then `res` must be passed. + reject(new Error('Unknown error')) + } else { + resolve(res) + } + } + + this.requestRawWithCallback(info, data, callbackForResult) + }) + } + + /** + * Raw request with callback. + * @param info + * @param data + * @param onResult + */ + requestRawWithCallback( + info: ifm.RequestInfo, + data: string | NodeJS.ReadableStream | null, + onResult: (err?: Error, res?: HttpClientResponse) => void + ): void { + if (typeof data === 'string') { + if (!info.options.headers) { + info.options.headers = {} + } + info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8') + } + + let callbackCalled = false + function handleResult(err?: Error, res?: HttpClientResponse): void { + if (!callbackCalled) { + callbackCalled = true + onResult(err, res) + } + } + + const req: http.ClientRequest = info.httpModule.request( + info.options, + (msg: http.IncomingMessage) => { + const res: HttpClientResponse = new HttpClientResponse(msg) + handleResult(undefined, res) + } + ) + + let socket: net.Socket + req.on('socket', sock => { + socket = sock + }) + + // If we ever get disconnected, we want the socket to timeout eventually + req.setTimeout(this._socketTimeout || 3 * 60000, () => { + if (socket) { + socket.end() + } + handleResult(new Error(`Request timeout: ${info.options.path}`)) + }) + + req.on('error', function (err) { + // err has statusCode property + // res should have headers + handleResult(err) + }) + + if (data && typeof data === 'string') { + req.write(data, 'utf8') + } + + if (data && typeof data !== 'string') { + data.on('close', function () { + req.end() + }) + + data.pipe(req) + } else { + req.end() + } + } + + /** + * Gets an http agent. This function is useful when you need an http agent that handles + * routing through a proxy server - depending upon the url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ + getAgent(serverUrl: string): http.Agent { + const parsedUrl = new URL(serverUrl) + return this._getAgent(parsedUrl) + } + + getAgentDispatcher(serverUrl: string): ProxyAgent | undefined { + const parsedUrl = new URL(serverUrl) + const proxyUrl = pm.getProxyUrl(parsedUrl) + const useProxy = proxyUrl && proxyUrl.hostname + if (!useProxy) { + return + } + + return this._getProxyAgentDispatcher(parsedUrl, proxyUrl) + } + + private _prepareRequest( + method: string, + requestUrl: URL, + headers?: http.OutgoingHttpHeaders + ): ifm.RequestInfo { + const info: ifm.RequestInfo = {} + + info.parsedUrl = requestUrl + const usingSsl: boolean = info.parsedUrl.protocol === 'https:' + info.httpModule = usingSsl ? https : http + const defaultPort: number = usingSsl ? 443 : 80 + + info.options = {} + info.options.host = info.parsedUrl.hostname + info.options.port = info.parsedUrl.port + ? parseInt(info.parsedUrl.port) + : defaultPort + info.options.path = + (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '') + info.options.method = method + info.options.headers = this._mergeHeaders(headers) + if (this.userAgent != null) { + info.options.headers['user-agent'] = this.userAgent + } + + info.options.agent = this._getAgent(info.parsedUrl) + + // gives handlers an opportunity to participate + if (this.handlers) { + for (const handler of this.handlers) { + handler.prepareRequest(info.options) + } + } + + return info + } + + private _mergeHeaders( + headers?: http.OutgoingHttpHeaders + ): http.OutgoingHttpHeaders { + if (this.requestOptions && this.requestOptions.headers) { + return Object.assign( + {}, + lowercaseKeys(this.requestOptions.headers), + lowercaseKeys(headers || {}) + ) + } + + return lowercaseKeys(headers || {}) + } + + private _getExistingOrDefaultHeader( + additionalHeaders: http.OutgoingHttpHeaders, + header: string, + _default: string + ): string | number | string[] { + let clientHeader: string | undefined + if (this.requestOptions && this.requestOptions.headers) { + clientHeader = lowercaseKeys(this.requestOptions.headers)[header] + } + return additionalHeaders[header] || clientHeader || _default + } + + private _getAgent(parsedUrl: URL): http.Agent { + let agent + const proxyUrl = pm.getProxyUrl(parsedUrl) + const useProxy = proxyUrl && proxyUrl.hostname + + if (this._keepAlive && useProxy) { + agent = this._proxyAgent + } + + if (this._keepAlive && !useProxy) { + agent = this._agent + } + + // if agent is already assigned use that agent. + if (agent) { + return agent + } + + const usingSsl = parsedUrl.protocol === 'https:' + let maxSockets = 100 + if (this.requestOptions) { + maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets + } + + // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. + if (proxyUrl && proxyUrl.hostname) { + const agentOptions = { + maxSockets, + keepAlive: this._keepAlive, + proxy: { + ...((proxyUrl.username || proxyUrl.password) && { + proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` + }), + host: proxyUrl.hostname, + port: proxyUrl.port + } + } + + let tunnelAgent: Function + const overHttps = proxyUrl.protocol === 'https:' + if (usingSsl) { + tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp + } else { + tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp + } + + agent = tunnelAgent(agentOptions) + this._proxyAgent = agent + } + + // if reusing agent across request and tunneling agent isn't assigned create a new agent + if (this._keepAlive && !agent) { + const options = {keepAlive: this._keepAlive, maxSockets} + agent = usingSsl ? new https.Agent(options) : new http.Agent(options) + this._agent = agent + } + + // if not using private agent and tunnel agent isn't setup then use global agent + if (!agent) { + agent = usingSsl ? https.globalAgent : http.globalAgent + } + + if (usingSsl && this._ignoreSslError) { + // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process + // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options + // we have to cast it to any and change it directly + agent.options = Object.assign(agent.options || {}, { + rejectUnauthorized: false + }) + } + + return agent + } + + private _getProxyAgentDispatcher(parsedUrl: URL, proxyUrl: URL): ProxyAgent { + let proxyAgent + + if (this._keepAlive) { + proxyAgent = this._proxyAgentDispatcher + } + + // if agent is already assigned use that agent. + if (proxyAgent) { + return proxyAgent + } + + const usingSsl = parsedUrl.protocol === 'https:' + proxyAgent = new ProxyAgent({ + uri: proxyUrl.href, + pipelining: !this._keepAlive ? 0 : 1, + ...((proxyUrl.username || proxyUrl.password) && { + token: `${proxyUrl.username}:${proxyUrl.password}` + }) + }) + this._proxyAgentDispatcher = proxyAgent + + if (usingSsl && this._ignoreSslError) { + // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process + // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options + // we have to cast it to any and change it directly + proxyAgent.options = Object.assign(proxyAgent.options.requestTls || {}, { + rejectUnauthorized: false + }) + } + + return proxyAgent + } + + private async _performExponentialBackoff(retryNumber: number): Promise { + retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber) + const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber) + return new Promise(resolve => setTimeout(() => resolve(), ms)) + } + + private async _processResponse( + res: HttpClientResponse, + options?: ifm.RequestOptions + ): Promise> { + return new Promise>(async (resolve, reject) => { + const statusCode = res.message.statusCode || 0 + + const response: ifm.TypedResponse = { + statusCode, + result: null, + headers: {} + } + + // not found leads to null obj returned + if (statusCode === HttpCodes.NotFound) { + resolve(response) + } + + // get the result from the body + + function dateTimeDeserializer(key: any, value: any): any { + if (typeof value === 'string') { + const a = new Date(value) + if (!isNaN(a.valueOf())) { + return a + } + } + + return value + } + + let obj: any + let contents: string | undefined + + try { + contents = await res.readBody() + if (contents && contents.length > 0) { + if (options && options.deserializeDates) { + obj = JSON.parse(contents, dateTimeDeserializer) + } else { + obj = JSON.parse(contents) + } + + response.result = obj + } + + response.headers = res.message.headers + } catch (err) { + // Invalid resource (contents not json); leaving result obj null + } + + // note that 3xx redirects are handled by the http layer. + if (statusCode > 299) { + let msg: string + + // if exception/error in body, attempt to get better error + if (obj && obj.message) { + msg = obj.message + } else if (contents && contents.length > 0) { + // it may be the case that the exception is in the body message as string + msg = contents + } else { + msg = `Failed request: (${statusCode})` + } + + const err = new HttpClientError(msg, statusCode) + err.result = response.result + + reject(err) + } else { + resolve(response) + } + }) + } +} + +const lowercaseKeys = (obj: {[index: string]: any}): any => + Object.keys(obj).reduce((c: any, k) => ((c[k.toLowerCase()] = obj[k]), c), {}) diff --git a/actions-toolkit/packages/http-client/src/interfaces.ts b/actions-toolkit/packages/http-client/src/interfaces.ts new file mode 100644 index 00000000..96b0fec7 --- /dev/null +++ b/actions-toolkit/packages/http-client/src/interfaces.ts @@ -0,0 +1,91 @@ +import * as http from 'http' +import * as https from 'https' +import {HttpClientResponse} from './index' + +export interface HttpClient { + options( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + get( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + del( + requestUrl: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + post( + requestUrl: string, + data: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + patch( + requestUrl: string, + data: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + put( + requestUrl: string, + data: string, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + sendStream( + verb: string, + requestUrl: string, + stream: NodeJS.ReadableStream, + additionalHeaders?: http.OutgoingHttpHeaders + ): Promise + request( + verb: string, + requestUrl: string, + data: string | NodeJS.ReadableStream, + headers: http.OutgoingHttpHeaders + ): Promise + requestRaw( + info: RequestInfo, + data: string | NodeJS.ReadableStream + ): Promise + requestRawWithCallback( + info: RequestInfo, + data: string | NodeJS.ReadableStream, + onResult: (err?: Error, res?: HttpClientResponse) => void + ): void +} + +export interface RequestHandler { + prepareRequest(options: http.RequestOptions): void + canHandleAuthentication(response: HttpClientResponse): boolean + handleAuthentication( + httpClient: HttpClient, + requestInfo: RequestInfo, + data: string | NodeJS.ReadableStream | null + ): Promise +} + +export interface RequestInfo { + options: http.RequestOptions + parsedUrl: URL + httpModule: typeof http | typeof https +} + +export interface RequestOptions { + headers?: http.OutgoingHttpHeaders + socketTimeout?: number + ignoreSslError?: boolean + allowRedirects?: boolean + allowRedirectDowngrade?: boolean + maxRedirects?: number + maxSockets?: number + keepAlive?: boolean + deserializeDates?: boolean + // Allows retries only on Read operations (since writes may not be idempotent) + allowRetries?: boolean + maxRetries?: number +} + +export interface TypedResponse { + statusCode: number + result: T | null + headers: http.IncomingHttpHeaders +} diff --git a/actions-toolkit/packages/http-client/src/proxy.ts b/actions-toolkit/packages/http-client/src/proxy.ts new file mode 100644 index 00000000..32afce6a --- /dev/null +++ b/actions-toolkit/packages/http-client/src/proxy.ts @@ -0,0 +1,89 @@ +export function getProxyUrl(reqUrl: URL): URL | undefined { + const usingSsl = reqUrl.protocol === 'https:' + + if (checkBypass(reqUrl)) { + return undefined + } + + const proxyVar = (() => { + if (usingSsl) { + return process.env['https_proxy'] || process.env['HTTPS_PROXY'] + } else { + return process.env['http_proxy'] || process.env['HTTP_PROXY'] + } + })() + + if (proxyVar) { + try { + return new URL(proxyVar) + } catch { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`) + } + } else { + return undefined + } +} + +export function checkBypass(reqUrl: URL): boolean { + if (!reqUrl.hostname) { + return false + } + + const reqHost = reqUrl.hostname + if (isLoopbackAddress(reqHost)) { + return true + } + + const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '' + if (!noProxy) { + return false + } + + // Determine the request port + let reqPort: number | undefined + if (reqUrl.port) { + reqPort = Number(reqUrl.port) + } else if (reqUrl.protocol === 'http:') { + reqPort = 80 + } else if (reqUrl.protocol === 'https:') { + reqPort = 443 + } + + // Format the request hostname and hostname with port + const upperReqHosts = [reqUrl.hostname.toUpperCase()] + if (typeof reqPort === 'number') { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`) + } + + // Compare request host against noproxy + for (const upperNoProxyItem of noProxy + .split(',') + .map(x => x.trim().toUpperCase()) + .filter(x => x)) { + if ( + upperNoProxyItem === '*' || + upperReqHosts.some( + x => + x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)) + ) + ) { + return true + } + } + + return false +} + +function isLoopbackAddress(host: string): boolean { + const hostLower = host.toLowerCase() + return ( + hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]') + ) +} diff --git a/actions-toolkit/packages/http-client/tsconfig.json b/actions-toolkit/packages/http-client/tsconfig.json new file mode 100644 index 00000000..222ca415 --- /dev/null +++ b/actions-toolkit/packages/http-client/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "moduleResolution": "node", + "declaration": true, + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/io/LICENSE.md b/actions-toolkit/packages/io/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/io/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/io/README.md b/actions-toolkit/packages/io/README.md new file mode 100644 index 00000000..9aadf2f9 --- /dev/null +++ b/actions-toolkit/packages/io/README.md @@ -0,0 +1,53 @@ +# `@actions/io` + +> Core functions for cli filesystem scenarios + +## Usage + +#### mkdir -p + +Recursively make a directory. Follows rules specified in [man mkdir](https://linux.die.net/man/1/mkdir) with the `-p` option specified: + +```js +const io = require('@actions/io'); + +await io.mkdirP('path/to/make'); +``` + +#### cp/mv + +Copy or move files or folders. Follows rules specified in [man cp](https://linux.die.net/man/1/cp) and [man mv](https://linux.die.net/man/1/mv): + +```js +const io = require('@actions/io'); + +// Recursive must be true for directories +const options = { recursive: true, force: false } + +await io.cp('path/to/directory', 'path/to/dest', options); +await io.mv('path/to/file', 'path/to/dest'); +``` + +#### rm -rf + +Remove a file or folder recursively. Follows rules specified in [man rm](https://linux.die.net/man/1/rm) with the `-r` and `-f` rules specified. + +```js +const io = require('@actions/io'); + +await io.rmRF('path/to/directory'); +await io.rmRF('path/to/file'); +``` + +#### which + +Get the path to a tool and resolves via paths. Follows the rules specified in [man which](https://linux.die.net/man/1/which). + +```js +const exec = require('@actions/exec'); +const io = require('@actions/io'); + +const pythonPath: string = await io.which('python', true) + +await exec.exec(`"${pythonPath}"`, ['main.py']); +``` diff --git a/actions-toolkit/packages/io/RELEASES.md b/actions-toolkit/packages/io/RELEASES.md new file mode 100644 index 00000000..e2f94d16 --- /dev/null +++ b/actions-toolkit/packages/io/RELEASES.md @@ -0,0 +1,22 @@ +# @actions/io Releases + +### 1.1.3 +- Replace `child_process.exec` with `fs.rm` in `rmRF` for all OS implementations [#1373](https://github.com/actions/toolkit/pull/1373) + +### 1.1.2 +- Update `lockfileVersion` to `v2` in `package-lock.json [#1020](https://github.com/actions/toolkit/pull/1020) + +### 1.1.1 +- [Fixed a bug where we incorrectly escaped paths for rmrf](https://github.com/actions/toolkit/pull/828) + +### 1.1.0 + +- Add `findInPath` method to locate all matching executables in the system path + +### 1.0.2 + +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + +### 1.0.0 + +- Initial release diff --git a/actions-toolkit/packages/io/__tests__/io.test.ts b/actions-toolkit/packages/io/__tests__/io.test.ts new file mode 100644 index 00000000..c05be13f --- /dev/null +++ b/actions-toolkit/packages/io/__tests__/io.test.ts @@ -0,0 +1,1566 @@ +import * as child from 'child_process' +import {promises as fs} from 'fs' +import * as os from 'os' +import * as path from 'path' +import * as io from '../src/io' +import * as ioUtil from '../src/io-util' + +describe('cp', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('copies file with no flags', async () => { + const root = path.join(getTestTemp(), 'cp_with_no_flags') + const sourceFile = path.join(root, 'cp_source') + const targetFile = path.join(root, 'cp_target') + await io.mkdirP(root) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + + await io.cp(sourceFile, targetFile) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) + + it('copies file using -f', async () => { + const root: string = path.join(path.join(__dirname, '_temp'), 'cp_with_-f') + const sourceFile: string = path.join(root, 'cp_source') + const targetFile: string = path.join(root, 'cp_target') + await io.mkdirP(root) + await fs.writeFile(sourceFile, 'test file content') + + await io.cp(sourceFile, targetFile, {recursive: false, force: true}) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) + + it('copies file into directory', async () => { + const root: string = path.join( + path.join(__dirname, '_temp'), + 'cp_file_to_directory' + ) + const sourceFile: string = path.join(root, 'cp_source') + const targetDirectory: string = path.join(root, 'cp_target') + const targetFile: string = path.join(targetDirectory, 'cp_source') + await io.mkdirP(targetDirectory) + await fs.writeFile(sourceFile, 'test file content') + + await io.cp(sourceFile, targetDirectory, {recursive: false, force: true}) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) + + it('try copying to existing file with -n', async () => { + const root: string = path.join(getTestTemp(), 'cp_to_existing') + const sourceFile: string = path.join(root, 'cp_source') + const targetFile: string = path.join(root, 'cp_target') + await io.mkdirP(root) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await fs.writeFile(targetFile, 'correct content', {encoding: 'utf8'}) + await io.cp(sourceFile, targetFile, {recursive: false, force: false}) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'correct content' + ) + }) + + it('copies directory into existing destination with -r', async () => { + const root: string = path.join(getTestTemp(), 'cp_with_-r_existing_dest') + const sourceFolder: string = path.join(root, 'cp_source') + const sourceFile: string = path.join(sourceFolder, 'cp_source_file') + + const targetFolder: string = path.join(root, 'cp_target') + const targetFile: string = path.join( + targetFolder, + 'cp_source', + 'cp_source_file' + ) + await io.mkdirP(sourceFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await io.mkdirP(targetFolder) + await io.cp(sourceFolder, targetFolder, {recursive: true}) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) + + it('copies directory into existing destination with -r without copying source directory', async () => { + const root: string = path.join( + getTestTemp(), + 'cp_with_-r_existing_dest_no_source_dir' + ) + const sourceFolder: string = path.join(root, 'cp_source') + const sourceFile: string = path.join(sourceFolder, 'cp_source_file') + + const targetFolder: string = path.join(root, 'cp_target') + const targetFile: string = path.join(targetFolder, 'cp_source_file') + await io.mkdirP(sourceFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await io.mkdirP(targetFolder) + await io.cp(sourceFolder, targetFolder, { + recursive: true, + copySourceDirectory: false + }) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) + + it('copies directory into non-existing destination with -r', async () => { + const root: string = path.join(getTestTemp(), 'cp_with_-r_nonexistent_dest') + const sourceFolder: string = path.join(root, 'cp_source') + const sourceFile: string = path.join(sourceFolder, 'cp_source_file') + + const targetFolder: string = path.join(root, 'cp_target') + const targetFile: string = path.join(targetFolder, 'cp_source_file') + await io.mkdirP(sourceFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await io.cp(sourceFolder, targetFolder, {recursive: true}) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) + + it('tries to copy directory without -r', async () => { + const root: string = path.join(getTestTemp(), 'cp_without_-r') + const sourceFolder: string = path.join(root, 'cp_source') + const sourceFile: string = path.join(sourceFolder, 'cp_source_file') + + const targetFolder: string = path.join(root, 'cp_target') + const targetFile: string = path.join( + targetFolder, + 'cp_source', + 'cp_source_file' + ) + await io.mkdirP(sourceFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + + let thrown = false + try { + await io.cp(sourceFolder, targetFolder) + } catch (err) { + thrown = true + } + expect(thrown).toBe(true) + await assertNotExists(targetFile) + }) + + it('Copies symlinks correctly', async () => { + // create the following layout + // sourceFolder + // sourceFolder/nested + // sourceFolder/nested/sourceFile + // sourceFolder/symlinkDirectory -> sourceFile + const root: string = path.join(getTestTemp(), 'cp_with_-r_symlinks') + const sourceFolder: string = path.join(root, 'cp_source') + const nestedFolder: string = path.join(sourceFolder, 'nested') + const sourceFile: string = path.join(nestedFolder, 'cp_source_file') + const symlinkDirectory: string = path.join(sourceFolder, 'symlinkDirectory') + + const targetFolder: string = path.join(root, 'cp_target') + const targetFile: string = path.join( + targetFolder, + 'nested', + 'cp_source_file' + ) + const symlinkTargetPath: string = path.join( + targetFolder, + 'symlinkDirectory', + 'cp_source_file' + ) + await io.mkdirP(sourceFolder) + await io.mkdirP(nestedFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await createSymlinkDir(nestedFolder, symlinkDirectory) + await io.cp(sourceFolder, targetFolder, {recursive: true}) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + expect(await fs.readFile(symlinkTargetPath, {encoding: 'utf8'})).toBe( + 'test file content' + ) + }) +}) + +describe('mv', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('moves file with no flags', async () => { + const root = path.join(getTestTemp(), ' mv_with_no_flags') + const sourceFile = path.join(root, ' mv_source') + const targetFile = path.join(root, ' mv_target') + await io.mkdirP(root) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + + await io.mv(sourceFile, targetFile) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + await assertNotExists(sourceFile) + }) + + it('moves file using -f', async () => { + const root: string = path.join(path.join(__dirname, '_temp'), ' mv_with_-f') + const sourceFile: string = path.join(root, ' mv_source') + const targetFile: string = path.join(root, ' mv_target') + await io.mkdirP(root) + await fs.writeFile(sourceFile, 'test file content') + + await io.mv(sourceFile, targetFile) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + + await assertNotExists(sourceFile) + }) + + it('try moving to existing file with -n', async () => { + const root: string = path.join(getTestTemp(), ' mv_to_existing') + const sourceFile: string = path.join(root, ' mv_source') + const targetFile: string = path.join(root, ' mv_target') + await io.mkdirP(root) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await fs.writeFile(targetFile, 'correct content', {encoding: 'utf8'}) + let failed = false + try { + await io.mv(sourceFile, targetFile, {force: false}) + } catch { + failed = true + } + expect(failed).toBe(true) + + expect(await fs.readFile(sourceFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'correct content' + ) + }) + + it('moves directory into existing destination', async () => { + const root: string = path.join(getTestTemp(), ' mv_with_-r_existing_dest') + const sourceFolder: string = path.join(root, ' mv_source') + const sourceFile: string = path.join(sourceFolder, ' mv_source_file') + + const targetFolder: string = path.join(root, ' mv_target') + const targetFile: string = path.join( + targetFolder, + ' mv_source', + ' mv_source_file' + ) + await io.mkdirP(sourceFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await io.mkdirP(targetFolder) + await io.mv(sourceFolder, targetFolder) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + await assertNotExists(sourceFile) + }) + + it('moves directory into non-existing destination', async () => { + const root: string = path.join( + getTestTemp(), + ' mv_with_-r_nonexistent_dest' + ) + const sourceFolder: string = path.join(root, ' mv_source') + const sourceFile: string = path.join(sourceFolder, ' mv_source_file') + + const targetFolder: string = path.join(root, ' mv_target') + const targetFile: string = path.join(targetFolder, ' mv_source_file') + await io.mkdirP(sourceFolder) + await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'}) + await io.mv(sourceFolder, targetFolder) + + expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + await assertNotExists(sourceFile) + }) +}) + +describe('rmRF', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('removes single folder with rmRF', async () => { + const testPath = path.join(getTestTemp(), 'testFolder') + + await io.mkdirP(testPath) + await assertExists(testPath) + + await io.rmRF(testPath) + await assertNotExists(testPath) + }) + + it('removes recursive folders with rmRF', async () => { + const testPath = path.join(getTestTemp(), 'testDir1') + const testPath2 = path.join(testPath, 'testDir2') + await io.mkdirP(testPath2) + + await assertExists(testPath) + await assertExists(testPath2) + + await io.rmRF(testPath) + await assertNotExists(testPath) + await assertNotExists(testPath2) + }) + + it('removes folder with locked file with rmRF', async () => { + const testPath = path.join(getTestTemp(), 'testFolder') + await io.mkdirP(testPath) + await assertExists(testPath) + + // can't remove folder with locked file on windows + const filePath = path.join(testPath, 'file.txt') + await fs.appendFile(filePath, 'some data') + await assertExists(filePath) + + // For windows we need to explicitly set an exclusive lock flag, because by default Node will open the file with the 'Delete' FileShare flag. + // See the exclusive lock windows flag definition: + // https://github.com/nodejs/node/blob/c2e4b1fa9ad0b744616c4e4c13a5017772a630c4/deps/uv/src/win/fs.c#L499-L513 + const fd = await fs.open( + filePath, + fs.constants.O_RDONLY | ioUtil.UV_FS_O_EXLOCK + ) + if (ioUtil.IS_WINDOWS) { + // On Windows, we expect an error due to an lstat call implementation in the underlying libuv code. + // See https://github.com/libuv/libuv/issues/3267 is resolved + await expect(async () => io.rmRF(testPath)).rejects.toThrow('EBUSY') + } else { + await io.rmRF(testPath) + + await assertNotExists(testPath) + } + await fd.close() + await io.rmRF(testPath) + await assertNotExists(testPath) + }) + + it('removes folder that does not exist with rmRF', async () => { + const testPath = path.join(getTestTemp(), 'testFolder') + await assertNotExists(testPath) + + await io.rmRF(testPath) + await assertNotExists(testPath) + }) + + it('removes file with rmRF', async () => { + const file: string = path.join(getTestTemp(), 'rmRF_file') + await fs.writeFile(file, 'test file content') + await assertExists(file) + await io.rmRF(file) + await assertNotExists(file) + }) + + it('removes hidden folder with rmRF', async () => { + const directory: string = path.join(getTestTemp(), '.rmRF_directory') + await createHiddenDirectory(directory) + await assertExists(directory) + await io.rmRF(directory) + await assertNotExists(directory) + }) + + it('removes hidden file with rmRF', async () => { + const file: string = path.join(getTestTemp(), '.rmRF_file') + await fs.writeFile(file, 'test file content') + await assertExists(file) + await io.rmRF(file) + await assertNotExists(file) + }) + + // creating a symlink to a file on Windows requires elevated + if (os.platform() !== 'win32') { + it('removes symlink file with rmRF', async () => { + // create the following layout: + // real_file + // symlink_file -> real_file + const root: string = path.join(getTestTemp(), 'rmRF_sym_file_test') + const realFile: string = path.join(root, 'real_file') + const symlinkFile: string = path.join(root, 'symlink_file') + await io.mkdirP(root) + await fs.writeFile(realFile, 'test file content') + await fs.symlink(realFile, symlinkFile) + expect(await fs.readFile(symlinkFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + + await io.rmRF(symlinkFile) + await assertExists(realFile) + await assertNotExists(symlinkFile) + }) + + it('removes symlink file with missing source using rmRF', async () => { + // create the following layout: + // real_file + // symlink_file -> real_file + const root: string = path.join( + getTestTemp(), + 'rmRF_sym_file_missing_source_test' + ) + const realFile: string = path.join(root, 'real_file') + const symlinkFile: string = path.join(root, 'symlink_file') + await io.mkdirP(root) + await fs.writeFile(realFile, 'test file content') + await fs.symlink(realFile, symlinkFile) + expect(await fs.readFile(symlinkFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + + // remove the real file + await fs.unlink(realFile) + expect((await fs.lstat(symlinkFile)).isSymbolicLink()).toBe(true) + + // remove the symlink file + await io.rmRF(symlinkFile) + let errcode = '' + try { + await fs.lstat(symlinkFile) + } catch (err) { + errcode = err.code + } + + expect(errcode).toBe('ENOENT') + }) + + it('removes symlink level 2 file with rmRF', async () => { + // create the following layout: + // real_file + // symlink_file -> real_file + // symlink_level_2_file -> symlink_file + const root: string = path.join( + getTestTemp(), + 'rmRF_sym_level_2_file_test' + ) + const realFile: string = path.join(root, 'real_file') + const symlinkFile: string = path.join(root, 'symlink_file') + const symlinkLevel2File: string = path.join(root, 'symlink_level_2_file') + await io.mkdirP(root) + await fs.writeFile(realFile, 'test file content') + await fs.symlink(realFile, symlinkFile) + await fs.symlink(symlinkFile, symlinkLevel2File) + expect(await fs.readFile(symlinkLevel2File, {encoding: 'utf8'})).toBe( + 'test file content' + ) + + await io.rmRF(symlinkLevel2File) + await assertExists(realFile) + await assertExists(symlinkFile) + await assertNotExists(symlinkLevel2File) + }) + + it('removes nested symlink file with rmRF', async () => { + // create the following layout: + // real_directory + // real_directory/real_file + // outer_directory + // outer_directory/symlink_file -> real_file + const root: string = path.join(getTestTemp(), 'rmRF_sym_nest_file_test') + const realDirectory: string = path.join(root, 'real_directory') + const realFile: string = path.join(root, 'real_directory', 'real_file') + const outerDirectory: string = path.join(root, 'outer_directory') + const symlinkFile: string = path.join( + root, + 'outer_directory', + 'symlink_file' + ) + await io.mkdirP(realDirectory) + await fs.writeFile(realFile, 'test file content') + await io.mkdirP(outerDirectory) + await fs.symlink(realFile, symlinkFile) + expect(await fs.readFile(symlinkFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + + await io.rmRF(outerDirectory) + await assertExists(realDirectory) + await assertExists(realFile) + await assertNotExists(symlinkFile) + await assertNotExists(outerDirectory) + }) + + it('removes deeply nested symlink file with rmRF', async () => { + // create the following layout: + // real_directory + // real_directory/real_file + // outer_directory + // outer_directory/nested_directory + // outer_directory/nested_directory/symlink_file -> real_file + const root: string = path.join( + getTestTemp(), + 'rmRF_sym_deep_nest_file_test' + ) + const realDirectory: string = path.join(root, 'real_directory') + const realFile: string = path.join(root, 'real_directory', 'real_file') + const outerDirectory: string = path.join(root, 'outer_directory') + const nestedDirectory: string = path.join( + root, + 'outer_directory', + 'nested_directory' + ) + const symlinkFile: string = path.join( + root, + 'outer_directory', + 'nested_directory', + 'symlink_file' + ) + await io.mkdirP(realDirectory) + await fs.writeFile(realFile, 'test file content') + await io.mkdirP(nestedDirectory) + await fs.symlink(realFile, symlinkFile) + expect(await fs.readFile(symlinkFile, {encoding: 'utf8'})).toBe( + 'test file content' + ) + + await io.rmRF(outerDirectory) + await assertExists(realDirectory) + await assertExists(realFile) + await assertNotExists(symlinkFile) + await assertNotExists(outerDirectory) + }) + } else { + it('correctly escapes % on windows', async () => { + const root: string = path.join(getTestTemp(), 'rmRF_escape_test_win') + const directory: string = path.join(root, '%test%') + await io.mkdirP(root) + await io.mkdirP(directory) + const oldEnv = process.env['test'] + process.env['test'] = 'thisshouldnotresolve' + + await io.rmRF(directory) + await assertNotExists(directory) + process.env['test'] = oldEnv + }) + + it('Should throw for invalid characters', async () => { + const root: string = path.join(getTestTemp(), 'rmRF_invalidChar_Windows') + const errorString = + 'File path must not contain `*`, `"`, `<`, `>` or `|` on Windows' + await expect(io.rmRF(path.join(root, '"'))).rejects.toHaveProperty( + 'message', + errorString + ) + await expect(io.rmRF(path.join(root, '<'))).rejects.toHaveProperty( + 'message', + errorString + ) + await expect(io.rmRF(path.join(root, '>'))).rejects.toHaveProperty( + 'message', + errorString + ) + await expect(io.rmRF(path.join(root, '|'))).rejects.toHaveProperty( + 'message', + errorString + ) + await expect(io.rmRF(path.join(root, '*'))).rejects.toHaveProperty( + 'message', + errorString + ) + }) + } + + it('removes symlink folder with missing source using rmRF', async () => { + // create the following layout: + // real_directory + // real_directory/real_file + // symlink_directory -> real_directory + const root: string = path.join(getTestTemp(), 'rmRF_sym_dir_miss_src_test') + const realDirectory: string = path.join(root, 'real_directory') + const realFile: string = path.join(root, 'real_directory', 'real_file') + const symlinkDirectory: string = path.join(root, 'symlink_directory') + await io.mkdirP(realDirectory) + await fs.writeFile(realFile, 'test file content') + await createSymlinkDir(realDirectory, symlinkDirectory) + await assertExists(symlinkDirectory) + + // remove the real directory + await fs.unlink(realFile) + await fs.rmdir(realDirectory) + let errcode = '' + try { + await fs.stat(symlinkDirectory) + } catch (err) { + errcode = err.code + } + + expect(errcode).toBe('ENOENT') + + // lstat shouldn't throw + await fs.lstat(symlinkDirectory) + + // remove the symlink directory + await io.rmRF(symlinkDirectory) + errcode = '' + try { + await fs.lstat(symlinkDirectory) + } catch (err) { + errcode = err.code + } + + expect(errcode).toBe('ENOENT') + }) + + it('removes symlink level 2 folder with rmRF', async () => { + // create the following layout: + // real_directory + // real_directory/real_file + // symlink_directory -> real_directory + // symlink_level_2_directory -> symlink_directory + const root: string = path.join( + getTestTemp(), + 'rmRF_sym_level_2_directory_test' + ) + const realDirectory: string = path.join(root, 'real_directory') + const realFile: string = path.join(realDirectory, 'real_file') + const symlinkDirectory: string = path.join(root, 'symlink_directory') + const symlinkLevel2Directory: string = path.join( + root, + 'symlink_level_2_directory' + ) + await io.mkdirP(realDirectory) + await fs.writeFile(realFile, 'test file content') + await createSymlinkDir(realDirectory, symlinkDirectory) + await createSymlinkDir(symlinkDirectory, symlinkLevel2Directory) + expect( + await fs.readFile(path.join(symlinkDirectory, 'real_file'), { + encoding: 'utf8' + }) + ).toBe('test file content') + if (os.platform() === 'win32') { + expect(await fs.readlink(symlinkLevel2Directory)).toBe( + `${symlinkDirectory}\\` + ) + } else { + expect(await fs.readlink(symlinkLevel2Directory)).toBe(symlinkDirectory) + } + + await io.rmRF(symlinkLevel2Directory) + await assertExists(path.join(symlinkDirectory, 'real_file')) + await assertNotExists(symlinkLevel2Directory) + }) + + it('removes nested symlink folder with rmRF', async () => { + // create the following layout: + // real_directory + // real_directory/real_file + // outer_directory + // outer_directory/symlink_directory -> real_directory + const root: string = path.join(getTestTemp(), 'rmRF_sym_nest_dir_test') + const realDirectory: string = path.join(root, 'real_directory') + const realFile: string = path.join(root, 'real_directory', 'real_file') + const outerDirectory: string = path.join(root, 'outer_directory') + const symlinkDirectory: string = path.join( + root, + 'outer_directory', + 'symlink_directory' + ) + await io.mkdirP(realDirectory) + await fs.writeFile(realFile, 'test file content') + await io.mkdirP(outerDirectory) + await createSymlinkDir(realDirectory, symlinkDirectory) + await assertExists(path.join(symlinkDirectory, 'real_file')) + + await io.rmRF(outerDirectory) + await assertExists(realDirectory) + await assertExists(realFile) + await assertNotExists(symlinkDirectory) + await assertNotExists(outerDirectory) + }) + + it('removes deeply nested symlink folder with rmRF', async () => { + // create the following layout: + // real_directory + // real_directory/real_file + // outer_directory + // outer_directory/nested_directory + // outer_directory/nested_directory/symlink_directory -> real_directory + const root: string = path.join(getTestTemp(), 'rmRF_sym_deep_nest_dir_test') + const realDirectory: string = path.join(root, 'real_directory') + const realFile: string = path.join(root, 'real_directory', 'real_file') + const outerDirectory: string = path.join(root, 'outer_directory') + const nestedDirectory: string = path.join( + root, + 'outer_directory', + 'nested_directory' + ) + const symlinkDirectory: string = path.join( + root, + 'outer_directory', + 'nested_directory', + 'symlink_directory' + ) + await io.mkdirP(realDirectory) + await fs.writeFile(realFile, 'test file content') + await io.mkdirP(nestedDirectory) + await createSymlinkDir(realDirectory, symlinkDirectory) + await assertExists(path.join(symlinkDirectory, 'real_file')) + + await io.rmRF(outerDirectory) + await assertExists(realDirectory) + await assertExists(realFile) + await assertNotExists(symlinkDirectory) + await assertNotExists(outerDirectory) + }) + + it('removes hidden file with rmRF', async () => { + const file: string = path.join(getTestTemp(), '.rmRF_file') + await io.mkdirP(path.dirname(file)) + await createHiddenFile(file, 'test file content') + await assertExists(file) + await io.rmRF(file) + await assertNotExists(file) + }) +}) + +describe('mkdirP', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('fails when called with an empty path', async () => { + expect.assertions(1) + + try { + await io.mkdirP('') + } catch (err) { + expect(err.message).toEqual('a path argument must be provided') + } + }) + + it('creates folder', async () => { + const testPath = path.join(getTestTemp(), 'mkdirTest') + await io.mkdirP(testPath) + + await assertExists(testPath) + }) + + it('creates nested folders with mkdirP', async () => { + const testPath = path.join(getTestTemp(), 'mkdir1', 'mkdir2') + await io.mkdirP(testPath) + + await assertExists(testPath) + }) + + it('fails if mkdirP with illegal chars', async () => { + const testPath = path.join(getTestTemp(), 'mkdir\0') + let worked = false + try { + await io.mkdirP(testPath) + worked = true + } catch (err) { + await expect(fs.stat(testPath)).rejects.toHaveProperty( + 'code', + 'ERR_INVALID_ARG_VALUE' + ) + } + + expect(worked).toBe(false) + }) + + it('fails if mkdirP with conflicting file path', async () => { + const testPath = path.join(getTestTemp(), 'mkdirP_conflicting_file_path') + await io.mkdirP(getTestTemp()) + await fs.writeFile(testPath, '') + let worked: boolean + try { + await io.mkdirP(testPath) + worked = true + } catch (err) { + worked = false + } + + expect(worked).toBe(false) + }) + + it('fails if mkdirP with conflicting parent file path', async () => { + const testPath = path.join( + getTestTemp(), + 'mkdirP_conflicting_parent_file_path', + 'dir' + ) + await io.mkdirP(getTestTemp()) + await fs.writeFile(path.dirname(testPath), '') + let worked: boolean + try { + await io.mkdirP(testPath) + worked = true + } catch (err) { + worked = false + } + + expect(worked).toBe(false) + }) + + it('no-ops if mkdirP directory exists', async () => { + const testPath = path.join(getTestTemp(), 'mkdirP_dir_exists') + await io.mkdirP(testPath) + await assertExists(testPath) + + // Calling again shouldn't throw + await io.mkdirP(testPath) + await assertExists(testPath) + }) + + it('no-ops if mkdirP with symlink directory', async () => { + // create the following layout: + // real_dir + // real_dir/file.txt + // symlink_dir -> real_dir + const rootPath = path.join(getTestTemp(), 'mkdirP_symlink_dir') + const realDirPath = path.join(rootPath, 'real_dir') + const realFilePath = path.join(realDirPath, 'file.txt') + const symlinkDirPath = path.join(rootPath, 'symlink_dir') + await io.mkdirP(getTestTemp()) + await fs.mkdir(rootPath) + await fs.mkdir(realDirPath) + await fs.writeFile(realFilePath, 'test real_dir/file.txt content') + await createSymlinkDir(realDirPath, symlinkDirPath) + + await io.mkdirP(symlinkDirPath) + + // the file in the real directory should still be accessible via the symlink + expect((await fs.lstat(symlinkDirPath)).isSymbolicLink()).toBe(true) + expect( + (await fs.stat(path.join(symlinkDirPath, 'file.txt'))).isFile() + ).toBe(true) + }) + + it('no-ops if mkdirP with parent symlink directory', async () => { + // create the following layout: + // real_dir + // real_dir/file.txt + // symlink_dir -> real_dir + const rootPath = path.join(getTestTemp(), 'mkdirP_parent_symlink_dir') + const realDirPath = path.join(rootPath, 'real_dir') + const realFilePath = path.join(realDirPath, 'file.txt') + const symlinkDirPath = path.join(rootPath, 'symlink_dir') + await io.mkdirP(getTestTemp()) + await fs.mkdir(rootPath) + await fs.mkdir(realDirPath) + await fs.writeFile(realFilePath, 'test real_dir/file.txt content') + await createSymlinkDir(realDirPath, symlinkDirPath) + + const subDirPath = path.join(symlinkDirPath, 'sub_dir') + await io.mkdirP(subDirPath) + + // the subdirectory should be accessible via the real directory + expect( + (await fs.lstat(path.join(realDirPath, 'sub_dir'))).isDirectory() + ).toBe(true) + }) +}) + +describe('which', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('which() finds file name', async () => { + // create a executable file + const testPath = path.join(getTestTemp(), 'which-finds-file-name') + await io.mkdirP(testPath) + let fileName = 'Which-Test-File' + if (process.platform === 'win32') { + fileName += '.exe' + } + + const filePath = path.join(testPath, fileName) + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '+x') + } + + const originalPath = process.env['PATH'] + try { + // update the PATH + process.env['PATH'] = `${process.env['PATH']}${path.delimiter}${testPath}` + + // exact file name + expect(await io.which(fileName)).toBe(filePath) + expect(await io.which(fileName, false)).toBe(filePath) + expect(await io.which(fileName, true)).toBe(filePath) + + if (process.platform === 'win32') { + // not case sensitive on windows + expect(await io.which('which-test-file.exe')).toBe( + path.join(testPath, 'which-test-file.exe') + ) + expect(await io.which('WHICH-TEST-FILE.EXE')).toBe( + path.join(testPath, 'WHICH-TEST-FILE.EXE') + ) + expect(await io.which('WHICH-TEST-FILE.EXE', false)).toBe( + path.join(testPath, 'WHICH-TEST-FILE.EXE') + ) + expect(await io.which('WHICH-TEST-FILE.EXE', true)).toBe( + path.join(testPath, 'WHICH-TEST-FILE.EXE') + ) + + // without extension + expect(await io.which('which-test-file')).toBe(filePath) + expect(await io.which('which-test-file', false)).toBe(filePath) + expect(await io.which('which-test-file', true)).toBe(filePath) + } else if (process.platform === 'darwin') { + // not case sensitive on Mac + expect(await io.which(fileName.toUpperCase())).toBe( + path.join(testPath, fileName.toUpperCase()) + ) + expect(await io.which(fileName.toUpperCase(), false)).toBe( + path.join(testPath, fileName.toUpperCase()) + ) + expect(await io.which(fileName.toUpperCase(), true)).toBe( + path.join(testPath, fileName.toUpperCase()) + ) + } else { + // case sensitive on Linux + expect(await io.which(fileName.toUpperCase())).toBe('') + } + } finally { + process.env['PATH'] = originalPath + } + }) + + it('which() not found', async () => { + expect(await io.which('which-test-no-such-file')).toBe('') + expect(await io.which('which-test-no-such-file', false)).toBe('') + await expect( + io.which('which-test-no-such-file', true) + ).rejects.toBeDefined() + }) + + it('which() searches path in order', async () => { + // create a chcp.com/bash override file + const testPath = path.join(getTestTemp(), 'which-searches-path-in-order') + await io.mkdirP(testPath) + let fileName + if (process.platform === 'win32') { + fileName = 'chcp.com' + } else { + fileName = 'bash' + } + + const filePath = path.join(testPath, fileName) + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '+x') + } + + const originalPath = process.env['PATH'] + try { + // sanity - regular chcp.com/bash should be found + const originalWhich = await io.which(fileName) + expect(!!(originalWhich || '')).toBe(true) + + // modify PATH + process.env['PATH'] = [testPath, process.env.PATH].join(path.delimiter) + + // override chcp.com/bash should be found + expect(await io.which(fileName)).toBe(filePath) + } finally { + process.env['PATH'] = originalPath + } + }) + + it('which() requires executable', async () => { + // create a non-executable file + // on Windows, should not end in valid PATHEXT + // on Mac/Linux should not have executable bit + const testPath = path.join(getTestTemp(), 'which-requires-executable') + await io.mkdirP(testPath) + let fileName = 'Which-Test-File' + if (process.platform === 'win32') { + fileName += '.abc' // not a valid PATHEXT + } + + const filePath = path.join(testPath, fileName) + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '-x') + } + + const originalPath = process.env['PATH'] + try { + // modify PATH + process.env['PATH'] = [process.env['PATH'], testPath].join(path.delimiter) + + // should not be found + expect(await io.which(fileName)).toBe('') + } finally { + process.env['PATH'] = originalPath + } + }) + + // which permissions tests + it('which() finds executable with different permissions', async () => { + await findsExecutableWithScopedPermissions('u=rwx,g=r,o=r') + await findsExecutableWithScopedPermissions('u=rw,g=rx,o=r') + await findsExecutableWithScopedPermissions('u=rw,g=r,o=rx') + }) + + it('which() ignores directory match', async () => { + // create a directory + const testPath = path.join(getTestTemp(), 'which-ignores-directory-match') + let dirPath = path.join(testPath, 'Which-Test-Dir') + if (process.platform === 'win32') { + dirPath += '.exe' + } + + await io.mkdirP(dirPath) + if (process.platform !== 'win32') { + chmod(dirPath, '+x') + } + + const originalPath = process.env['PATH'] + try { + // modify PATH + process.env['PATH'] = [process.env['PATH'], testPath].join(path.delimiter) + + // should not be found + expect(await io.which(path.basename(dirPath))).toBe('') + } finally { + process.env['PATH'] = originalPath + } + }) + + it('which() allows rooted path', async () => { + // create an executable file + const testPath = path.join(getTestTemp(), 'which-allows-rooted-path') + await io.mkdirP(testPath) + let filePath = path.join(testPath, 'Which-Test-File') + if (process.platform === 'win32') { + filePath += '.exe' + } + + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '+x') + } + + // which the full path + expect(await io.which(filePath)).toBe(filePath) + expect(await io.which(filePath, false)).toBe(filePath) + expect(await io.which(filePath, true)).toBe(filePath) + }) + + it('which() requires rooted path to be executable', async () => { + // create a non-executable file + // on Windows, should not end in valid PATHEXT + // on Mac/Linux, should not have executable bit + const testPath = path.join( + getTestTemp(), + 'which-requires-rooted-path-to-be-executable' + ) + await io.mkdirP(testPath) + let filePath = path.join(testPath, 'Which-Test-File') + if (process.platform === 'win32') { + filePath += '.abc' // not a valid PATHEXT + } + + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '-x') + } + + // should not be found + expect(await io.which(filePath)).toBe('') + expect(await io.which(filePath, false)).toBe('') + let failed = false + try { + await io.which(filePath, true) + } catch (err) { + failed = true + } + + expect(failed).toBe(true) + }) + + it('which() requires rooted path to be a file', async () => { + // create a dir + const testPath = path.join( + getTestTemp(), + 'which-requires-rooted-path-to-be-executable' + ) + let dirPath = path.join(testPath, 'Which-Test-Dir') + if (process.platform === 'win32') { + dirPath += '.exe' + } + + await io.mkdirP(dirPath) + if (process.platform !== 'win32') { + chmod(dirPath, '+x') + } + + // should not be found + expect(await io.which(dirPath)).toBe('') + expect(await io.which(dirPath, false)).toBe('') + let failed = false + try { + await io.which(dirPath, true) + } catch (err) { + failed = true + } + + expect(failed).toBe(true) + }) + + it('which() requires rooted path to exist', async () => { + let filePath = path.join(__dirname, 'no-such-file') + if (process.platform === 'win32') { + filePath += '.exe' + } + + expect(await io.which(filePath)).toBe('') + expect(await io.which(filePath, false)).toBe('') + let failed = false + try { + await io.which(filePath, true) + } catch (err) { + failed = true + } + + expect(failed).toBe(true) + }) + + it('which() does not allow separators', async () => { + // create an executable file + const testDirName = 'which-does-not-allow-separators' + const testPath = path.join(getTestTemp(), testDirName) + await io.mkdirP(testPath) + let fileName = 'Which-Test-File' + if (process.platform === 'win32') { + fileName += '.exe' + } + + const filePath = path.join(testPath, fileName) + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '+x') + } + + const originalPath = process.env['PATH'] + try { + // modify PATH + process.env['PATH'] = [process.env['PATH'], testPath].join(path.delimiter) + + // which "dir/file", should not be found + expect(await io.which(`${testDirName}/${fileName}`)).toBe('') + + // on Windows, also try "dir\file" + if (process.platform === 'win32') { + expect(await io.which(`${testDirName}\\${fileName}`)).toBe('') + } + } finally { + process.env['PATH'] = originalPath + } + }) + + if (process.platform === 'win32') { + it('which() resolves actual case file name when extension is applied', async () => { + const comspec: string = process.env['ComSpec'] || '' + expect(!!comspec).toBe(true) + expect(await io.which('CmD.eXe')).toBe( + path.join(path.dirname(comspec), 'CmD.eXe') + ) + expect(await io.which('CmD')).toBe(comspec) + }) + + it('which() appends ext on windows', async () => { + // create executable files + const testPath = path.join(getTestTemp(), 'which-appends-ext-on-windows') + await io.mkdirP(testPath) + // PATHEXT=.COM;.EXE;.BAT;.CMD... + const files: {[key: string]: string} = { + 'which-test-file-1': path.join(testPath, 'which-test-file-1.com'), + 'which-test-file-2': path.join(testPath, 'which-test-file-2.exe'), + 'which-test-file-3': path.join(testPath, 'which-test-file-3.bat'), + 'which-test-file-4': path.join(testPath, 'which-test-file-4.cmd'), + 'which-test-file-5.txt': path.join( + testPath, + 'which-test-file-5.txt.com' + ) + } + for (const fileName of Object.keys(files)) { + await fs.writeFile(files[fileName], '') + } + + const originalPath = process.env['PATH'] + try { + // modify PATH + process.env[ + 'PATH' + ] = `${process.env['PATH']}${path.delimiter}${testPath}` + + // find each file + for (const fileName of Object.keys(files)) { + expect(await io.which(fileName)).toBe(files[fileName]) + } + } finally { + process.env['PATH'] = originalPath + } + }) + + it('which() appends ext on windows when rooted', async () => { + // create executable files + const testPath = path.join( + getTestTemp(), + 'which-appends-ext-on-windows-when-rooted' + ) + await io.mkdirP(testPath) + // PATHEXT=.COM;.EXE;.BAT;.CMD... + const files: {[key: string]: string} = {} + files[path.join(testPath, 'which-test-file-1')] = path.join( + testPath, + 'which-test-file-1.com' + ) + files[path.join(testPath, 'which-test-file-2')] = path.join( + testPath, + 'which-test-file-2.exe' + ) + files[path.join(testPath, 'which-test-file-3')] = path.join( + testPath, + 'which-test-file-3.bat' + ) + files[path.join(testPath, 'which-test-file-4')] = path.join( + testPath, + 'which-test-file-4.cmd' + ) + files[path.join(testPath, 'which-test-file-5.txt')] = path.join( + testPath, + 'which-test-file-5.txt.com' + ) + for (const fileName of Object.keys(files)) { + await fs.writeFile(files[fileName], '') + } + + // find each file + for (const fileName of Object.keys(files)) { + expect(await io.which(fileName)).toBe(files[fileName]) + } + }) + + it('which() prefer exact match on windows', async () => { + // create two executable files: + // which-test-file.bat + // which-test-file.bat.exe + // + // verify "which-test-file.bat" returns that file, and not "which-test-file.bat.exe" + // + // preference, within the same dir, should be given to the exact match (even though + // .EXE is defined with higher preference than .BAT in PATHEXT (PATHEXT=.COM;.EXE;.BAT;.CMD...) + const testPath = path.join( + getTestTemp(), + 'which-prefer-exact-match-on-windows' + ) + await io.mkdirP(testPath) + const fileName = 'which-test-file.bat' + const expectedFilePath = path.join(testPath, fileName) + const notExpectedFilePath = path.join(testPath, `${fileName}.exe`) + await fs.writeFile(expectedFilePath, '') + await fs.writeFile(notExpectedFilePath, '') + const originalPath = process.env['PATH'] + try { + process.env[ + 'PATH' + ] = `${process.env['PATH']}${path.delimiter}${testPath}` + expect(await io.which(fileName)).toBe(expectedFilePath) + } finally { + process.env['PATH'] = originalPath + } + }) + + it('which() prefer exact match on windows when rooted', async () => { + // create two executable files: + // which-test-file.bat + // which-test-file.bat.exe + // + // verify "which-test-file.bat" returns that file, and not "which-test-file.bat.exe" + // + // preference, within the same dir, should be given to the exact match (even though + // .EXE is defined with higher preference than .BAT in PATHEXT (PATHEXT=.COM;.EXE;.BAT;.CMD...) + const testPath = path.join( + getTestTemp(), + 'which-prefer-exact-match-on-windows-when-rooted' + ) + await io.mkdirP(testPath) + const fileName = 'which-test-file.bat' + const expectedFilePath = path.join(testPath, fileName) + const notExpectedFilePath = path.join(testPath, `${fileName}.exe`) + await fs.writeFile(expectedFilePath, '') + await fs.writeFile(notExpectedFilePath, '') + expect(await io.which(path.join(testPath, fileName))).toBe( + expectedFilePath + ) + }) + + it('which() searches ext in order', async () => { + const testPath = path.join(getTestTemp(), 'which-searches-ext-in-order') + + // create a directory for testing .COM order preference + // PATHEXT=.COM;.EXE;.BAT;.CMD... + const fileNameWithoutExtension = 'which-test-file' + const comTestPath = path.join(testPath, 'com-test') + await io.mkdirP(comTestPath) + await fs.writeFile( + path.join(comTestPath, `${fileNameWithoutExtension}.com`), + '' + ) + await fs.writeFile( + path.join(comTestPath, `${fileNameWithoutExtension}.exe`), + '' + ) + await fs.writeFile( + path.join(comTestPath, `${fileNameWithoutExtension}.bat`), + '' + ) + await fs.writeFile( + path.join(comTestPath, `${fileNameWithoutExtension}.cmd`), + '' + ) + + // create a directory for testing .EXE order preference + // PATHEXT=.COM;.EXE;.BAT;.CMD... + const exeTestPath = path.join(testPath, 'exe-test') + await io.mkdirP(exeTestPath) + await fs.writeFile( + path.join(exeTestPath, `${fileNameWithoutExtension}.exe`), + '' + ) + await fs.writeFile( + path.join(exeTestPath, `${fileNameWithoutExtension}.bat`), + '' + ) + await fs.writeFile( + path.join(exeTestPath, `${fileNameWithoutExtension}.cmd`), + '' + ) + + // create a directory for testing .BAT order preference + // PATHEXT=.COM;.EXE;.BAT;.CMD... + const batTestPath = path.join(testPath, 'bat-test') + await io.mkdirP(batTestPath) + await fs.writeFile( + path.join(batTestPath, `${fileNameWithoutExtension}.bat`), + '' + ) + await fs.writeFile( + path.join(batTestPath, `${fileNameWithoutExtension}.cmd`), + '' + ) + + // create a directory for testing .CMD + const cmdTestPath = path.join(testPath, 'cmd-test') + await io.mkdirP(cmdTestPath) + const cmdFilePath = path.join( + cmdTestPath, + `${fileNameWithoutExtension}.cmd` + ) + await fs.writeFile(cmdFilePath, '') + + const originalPath = process.env['PATH'] + try { + // test .COM + process.env['PATH'] = `${comTestPath}${path.delimiter}${originalPath}` + expect(await io.which(fileNameWithoutExtension)).toBe( + path.join(comTestPath, `${fileNameWithoutExtension}.com`) + ) + + // test .EXE + process.env['PATH'] = `${exeTestPath}${path.delimiter}${originalPath}` + expect(await io.which(fileNameWithoutExtension)).toBe( + path.join(exeTestPath, `${fileNameWithoutExtension}.exe`) + ) + + // test .BAT + process.env['PATH'] = `${batTestPath}${path.delimiter}${originalPath}` + expect(await io.which(fileNameWithoutExtension)).toBe( + path.join(batTestPath, `${fileNameWithoutExtension}.bat`) + ) + + // test .CMD + process.env['PATH'] = `${cmdTestPath}${path.delimiter}${originalPath}` + expect(await io.which(fileNameWithoutExtension)).toBe( + path.join(cmdTestPath, `${fileNameWithoutExtension}.cmd`) + ) + } finally { + process.env['PATH'] = originalPath + } + }) + } +}) + +describe('findInPath', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('findInPath() not found', async () => { + expect(await io.findInPath('findInPath-test-no-such-file')).toEqual([]) + }) + + it('findInPath() finds file names', async () => { + // create executable files + let fileName = 'FindInPath-Test-File' + if (process.platform === 'win32') { + fileName += '.exe' + } + + const testPaths = ['1', '2', '3'].map(count => + path.join(getTestTemp(), `findInPath-finds-file-names-${count}`) + ) + for (const testPath of testPaths) { + await io.mkdirP(testPath) + } + + const filePaths = testPaths.map(testPath => path.join(testPath, fileName)) + for (const filePath of filePaths) { + await fs.writeFile(filePath, '') + if (process.platform !== 'win32') { + chmod(filePath, '+x') + } + } + + const originalPath = process.env['PATH'] + try { + // update the PATH + for (const testPath of testPaths) { + process.env[ + 'PATH' + ] = `${process.env['PATH']}${path.delimiter}${testPath}` + } + // exact file names + expect(await io.findInPath(fileName)).toEqual(filePaths) + } finally { + process.env['PATH'] = originalPath + } + }) +}) + +async function findsExecutableWithScopedPermissions( + chmodOptions: string +): Promise { + // create a executable file + const testPath = path.join(getTestTemp(), 'which-finds-file-name') + await io.mkdirP(testPath) + const fileName = 'Which-Test-File' + if (process.platform === 'win32') { + return + } + + const filePath = path.join(testPath, fileName) + await fs.writeFile(filePath, '') + chmod(filePath, chmodOptions) + + const originalPath = process.env['PATH'] + try { + // update the PATH + process.env['PATH'] = `${process.env['PATH']}${path.delimiter}${testPath}` + + // exact file name + expect(await io.which(fileName)).toBe(filePath) + expect(await io.which(fileName, false)).toBe(filePath) + expect(await io.which(fileName, true)).toBe(filePath) + + if (process.platform === 'darwin') { + // not case sensitive on Mac + expect(await io.which(fileName.toUpperCase())).toBe( + path.join(testPath, fileName.toUpperCase()) + ) + expect(await io.which(fileName.toUpperCase(), false)).toBe( + path.join(testPath, fileName.toUpperCase()) + ) + expect(await io.which(fileName.toUpperCase(), true)).toBe( + path.join(testPath, fileName.toUpperCase()) + ) + } else { + // case sensitive on Linux + expect(await io.which(fileName.toUpperCase())).toBe('') + } + } finally { + process.env['PATH'] = originalPath + } +} + +// Assert that a file exists +async function assertExists(filePath: string): Promise { + expect(await fs.stat(filePath)).toBeDefined() +} + +// Assert that reading a file raises an ENOENT error (does not exist) +async function assertNotExists(filePath: string): Promise { + await expect(fs.stat(filePath)).rejects.toHaveProperty('code', 'ENOENT') +} + +function chmod(file: string, mode: string): void { + const result = child.spawnSync('chmod', [mode, file]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error(`Command failed: "chmod ${mode} ${file}". ${message}`) + } +} + +async function createHiddenDirectory(dir: string): Promise { + if (!path.basename(dir).match(/^\./)) { + throw new Error(`Expected dir '${dir}' to start with '.'.`) + } + + await io.mkdirP(dir) + if (os.platform() === 'win32') { + const result = child.spawnSync('attrib.exe', ['+H', dir]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error( + `Failed to set hidden attribute for directory '${dir}'. ${message}` + ) + } + } +} + +async function createHiddenFile(file: string, content: string): Promise { + if (!path.basename(file).match(/^\./)) { + throw new Error(`Expected dir '${file}' to start with '.'.`) + } + + await io.mkdirP(path.dirname(file)) + await fs.writeFile(file, content) + + if (os.platform() === 'win32') { + const result = child.spawnSync('attrib.exe', ['+H', file]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error( + `Failed to set hidden attribute for file '${file}'. ${message}` + ) + } + } +} + +function getTestTemp(): string { + return path.join(__dirname, '_temp') +} + +/** + * Creates a symlink directory on OSX/Linux, and a junction point directory on Windows. + * A symlink directory is not created on Windows since it requires an elevated context. + */ +async function createSymlinkDir(real: string, link: string): Promise { + if (os.platform() === 'win32') { + await fs.symlink(real, link, 'junction') + } else { + await fs.symlink(real, link) + } +} diff --git a/actions-toolkit/packages/io/package-lock.json b/actions-toolkit/packages/io/package-lock.json new file mode 100644 index 00000000..e7152dca --- /dev/null +++ b/actions-toolkit/packages/io/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "@actions/io", + "version": "1.1.3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/io", + "version": "1.1.3", + "license": "MIT" + } + } +} diff --git a/actions-toolkit/packages/io/package.json b/actions-toolkit/packages/io/package.json new file mode 100644 index 00000000..e8c8d8fb --- /dev/null +++ b/actions-toolkit/packages/io/package.json @@ -0,0 +1,37 @@ +{ + "name": "@actions/io", + "version": "1.1.3", + "description": "Actions io lib", + "keywords": [ + "github", + "actions", + "io" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/io", + "license": "MIT", + "main": "lib/io.js", + "types": "lib/io.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/io" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + } +} diff --git a/actions-toolkit/packages/io/src/io-util.ts b/actions-toolkit/packages/io/src/io-util.ts new file mode 100644 index 00000000..fd3a3e17 --- /dev/null +++ b/actions-toolkit/packages/io/src/io-util.ts @@ -0,0 +1,182 @@ +import * as fs from 'fs' +import * as path from 'path' + +export const { + chmod, + copyFile, + lstat, + mkdir, + open, + readdir, + readlink, + rename, + rm, + rmdir, + stat, + symlink, + unlink +} = fs.promises +// export const {open} = 'fs' +export const IS_WINDOWS = process.platform === 'win32' +// See https://github.com/nodejs/node/blob/d0153aee367422d0858105abec186da4dff0a0c5/deps/uv/include/uv/win.h#L691 +export const UV_FS_O_EXLOCK = 0x10000000 +export const READONLY = fs.constants.O_RDONLY + +export async function exists(fsPath: string): Promise { + try { + await stat(fsPath) + } catch (err) { + if (err.code === 'ENOENT') { + return false + } + + throw err + } + + return true +} + +export async function isDirectory( + fsPath: string, + useStat = false +): Promise { + const stats = useStat ? await stat(fsPath) : await lstat(fsPath) + return stats.isDirectory() +} + +/** + * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like: + * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases). + */ +export function isRooted(p: string): boolean { + p = normalizeSeparators(p) + if (!p) { + throw new Error('isRooted() parameter "p" cannot be empty') + } + + if (IS_WINDOWS) { + return ( + p.startsWith('\\') || /^[A-Z]:/i.test(p) // e.g. \ or \hello or \\hello + ) // e.g. C: or C:\hello + } + + return p.startsWith('/') +} + +/** + * Best effort attempt to determine whether a file exists and is executable. + * @param filePath file path to check + * @param extensions additional file extensions to try + * @return if file exists and is executable, returns the file path. otherwise empty string. + */ +export async function tryGetExecutablePath( + filePath: string, + extensions: string[] +): Promise { + let stats: fs.Stats | undefined = undefined + try { + // test file exists + stats = await stat(filePath) + } catch (err) { + if (err.code !== 'ENOENT') { + // eslint-disable-next-line no-console + console.log( + `Unexpected error attempting to determine if executable file exists '${filePath}': ${err}` + ) + } + } + if (stats && stats.isFile()) { + if (IS_WINDOWS) { + // on Windows, test for valid extension + const upperExt = path.extname(filePath).toUpperCase() + if (extensions.some(validExt => validExt.toUpperCase() === upperExt)) { + return filePath + } + } else { + if (isUnixExecutable(stats)) { + return filePath + } + } + } + + // try each extension + const originalFilePath = filePath + for (const extension of extensions) { + filePath = originalFilePath + extension + + stats = undefined + try { + stats = await stat(filePath) + } catch (err) { + if (err.code !== 'ENOENT') { + // eslint-disable-next-line no-console + console.log( + `Unexpected error attempting to determine if executable file exists '${filePath}': ${err}` + ) + } + } + + if (stats && stats.isFile()) { + if (IS_WINDOWS) { + // preserve the case of the actual file (since an extension was appended) + try { + const directory = path.dirname(filePath) + const upperName = path.basename(filePath).toUpperCase() + for (const actualName of await readdir(directory)) { + if (upperName === actualName.toUpperCase()) { + filePath = path.join(directory, actualName) + break + } + } + } catch (err) { + // eslint-disable-next-line no-console + console.log( + `Unexpected error attempting to determine the actual case of the file '${filePath}': ${err}` + ) + } + + return filePath + } else { + if (isUnixExecutable(stats)) { + return filePath + } + } + } + } + + return '' +} + +function normalizeSeparators(p: string): string { + p = p || '' + if (IS_WINDOWS) { + // convert slashes on Windows + p = p.replace(/\//g, '\\') + + // remove redundant slashes + return p.replace(/\\\\+/g, '\\') + } + + // remove redundant slashes + return p.replace(/\/\/+/g, '/') +} + +// on Mac/Linux, test the execute bit +// R W X R W X R W X +// 256 128 64 32 16 8 4 2 1 +function isUnixExecutable(stats: fs.Stats): boolean { + return ( + (stats.mode & 1) > 0 || + ((stats.mode & 8) > 0 && + process.getgid !== undefined && + stats.gid === process.getgid()) || + ((stats.mode & 64) > 0 && + process.getuid !== undefined && + stats.uid === process.getuid()) + ) +} + +// Get the path of cmd.exe in windows +export function getCmdPath(): string { + return process.env['COMSPEC'] ?? `cmd.exe` +} diff --git a/actions-toolkit/packages/io/src/io.ts b/actions-toolkit/packages/io/src/io.ts new file mode 100644 index 00000000..bbd8efd3 --- /dev/null +++ b/actions-toolkit/packages/io/src/io.ts @@ -0,0 +1,327 @@ +import {ok} from 'assert' +import * as path from 'path' +import * as ioUtil from './io-util' + +/** + * Interface for cp/mv options + */ +export interface CopyOptions { + /** Optional. Whether to recursively copy all subdirectories. Defaults to false */ + recursive?: boolean + /** Optional. Whether to overwrite existing files in the destination. Defaults to true */ + force?: boolean + /** Optional. Whether to copy the source directory along with all the files. Only takes effect when recursive=true and copying a directory. Default is true*/ + copySourceDirectory?: boolean +} + +/** + * Interface for cp/mv options + */ +export interface MoveOptions { + /** Optional. Whether to overwrite existing files in the destination. Defaults to true */ + force?: boolean +} + +/** + * Copies a file or folder. + * Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js + * + * @param source source path + * @param dest destination path + * @param options optional. See CopyOptions. + */ +export async function cp( + source: string, + dest: string, + options: CopyOptions = {} +): Promise { + const {force, recursive, copySourceDirectory} = readCopyOptions(options) + + const destStat = (await ioUtil.exists(dest)) ? await ioUtil.stat(dest) : null + // Dest is an existing file, but not forcing + if (destStat && destStat.isFile() && !force) { + return + } + + // If dest is an existing directory, should copy inside. + const newDest: string = + destStat && destStat.isDirectory() && copySourceDirectory + ? path.join(dest, path.basename(source)) + : dest + + if (!(await ioUtil.exists(source))) { + throw new Error(`no such file or directory: ${source}`) + } + const sourceStat = await ioUtil.stat(source) + + if (sourceStat.isDirectory()) { + if (!recursive) { + throw new Error( + `Failed to copy. ${source} is a directory, but tried to copy without recursive flag.` + ) + } else { + await cpDirRecursive(source, newDest, 0, force) + } + } else { + if (path.relative(source, newDest) === '') { + // a file cannot be copied to itself + throw new Error(`'${newDest}' and '${source}' are the same file`) + } + + await copyFile(source, newDest, force) + } +} + +/** + * Moves a path. + * + * @param source source path + * @param dest destination path + * @param options optional. See MoveOptions. + */ +export async function mv( + source: string, + dest: string, + options: MoveOptions = {} +): Promise { + if (await ioUtil.exists(dest)) { + let destExists = true + if (await ioUtil.isDirectory(dest)) { + // If dest is directory copy src into dest + dest = path.join(dest, path.basename(source)) + destExists = await ioUtil.exists(dest) + } + + if (destExists) { + if (options.force == null || options.force) { + await rmRF(dest) + } else { + throw new Error('Destination already exists') + } + } + } + await mkdirP(path.dirname(dest)) + await ioUtil.rename(source, dest) +} + +/** + * Remove a path recursively with force + * + * @param inputPath path to remove + */ +export async function rmRF(inputPath: string): Promise { + if (ioUtil.IS_WINDOWS) { + // Check for invalid characters + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + if (/[*"<>|]/.test(inputPath)) { + throw new Error( + 'File path must not contain `*`, `"`, `<`, `>` or `|` on Windows' + ) + } + } + try { + // note if path does not exist, error is silent + await ioUtil.rm(inputPath, { + force: true, + maxRetries: 3, + recursive: true, + retryDelay: 300 + }) + } catch (err) { + throw new Error(`File was unable to be removed ${err}`) + } +} + +/** + * Make a directory. Creates the full path with folders in between + * Will throw if it fails + * + * @param fsPath path to create + * @returns Promise + */ +export async function mkdirP(fsPath: string): Promise { + ok(fsPath, 'a path argument must be provided') + await ioUtil.mkdir(fsPath, {recursive: true}) +} + +/** + * Returns path of a tool had the tool actually been invoked. Resolves via paths. + * If you check and the tool does not exist, it will throw. + * + * @param tool name of the tool + * @param check whether to check if tool exists + * @returns Promise path to tool + */ +export async function which(tool: string, check?: boolean): Promise { + if (!tool) { + throw new Error("parameter 'tool' is required") + } + + // recursive when check=true + if (check) { + const result: string = await which(tool, false) + + if (!result) { + if (ioUtil.IS_WINDOWS) { + throw new Error( + `Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also verify the file has a valid extension for an executable file.` + ) + } else { + throw new Error( + `Unable to locate executable file: ${tool}. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable.` + ) + } + } + + return result + } + + const matches: string[] = await findInPath(tool) + + if (matches && matches.length > 0) { + return matches[0] + } + + return '' +} + +/** + * Returns a list of all occurrences of the given tool on the system path. + * + * @returns Promise the paths of the tool + */ +export async function findInPath(tool: string): Promise { + if (!tool) { + throw new Error("parameter 'tool' is required") + } + + // build the list of extensions to try + const extensions: string[] = [] + if (ioUtil.IS_WINDOWS && process.env['PATHEXT']) { + for (const extension of process.env['PATHEXT'].split(path.delimiter)) { + if (extension) { + extensions.push(extension) + } + } + } + + // if it's rooted, return it if exists. otherwise return empty. + if (ioUtil.isRooted(tool)) { + const filePath: string = await ioUtil.tryGetExecutablePath(tool, extensions) + + if (filePath) { + return [filePath] + } + + return [] + } + + // if any path separators, return empty + if (tool.includes(path.sep)) { + return [] + } + + // build the list of directories + // + // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, + // it feels like we should not do this. Checking the current directory seems like more of a use + // case of a shell, and the which() function exposed by the toolkit should strive for consistency + // across platforms. + const directories: string[] = [] + + if (process.env.PATH) { + for (const p of process.env.PATH.split(path.delimiter)) { + if (p) { + directories.push(p) + } + } + } + + // find all matches + const matches: string[] = [] + + for (const directory of directories) { + const filePath = await ioUtil.tryGetExecutablePath( + path.join(directory, tool), + extensions + ) + if (filePath) { + matches.push(filePath) + } + } + + return matches +} + +function readCopyOptions(options: CopyOptions): Required { + const force = options.force == null ? true : options.force + const recursive = Boolean(options.recursive) + const copySourceDirectory = + options.copySourceDirectory == null + ? true + : Boolean(options.copySourceDirectory) + return {force, recursive, copySourceDirectory} +} + +async function cpDirRecursive( + sourceDir: string, + destDir: string, + currentDepth: number, + force: boolean +): Promise { + // Ensure there is not a run away recursive copy + if (currentDepth >= 255) return + currentDepth++ + + await mkdirP(destDir) + + const files: string[] = await ioUtil.readdir(sourceDir) + + for (const fileName of files) { + const srcFile = `${sourceDir}/${fileName}` + const destFile = `${destDir}/${fileName}` + const srcFileStat = await ioUtil.lstat(srcFile) + + if (srcFileStat.isDirectory()) { + // Recurse + await cpDirRecursive(srcFile, destFile, currentDepth, force) + } else { + await copyFile(srcFile, destFile, force) + } + } + + // Change the mode for the newly created directory + await ioUtil.chmod(destDir, (await ioUtil.stat(sourceDir)).mode) +} + +// Buffered file copy +async function copyFile( + srcFile: string, + destFile: string, + force: boolean +): Promise { + if ((await ioUtil.lstat(srcFile)).isSymbolicLink()) { + // unlink/re-link it + try { + await ioUtil.lstat(destFile) + await ioUtil.unlink(destFile) + } catch (e) { + // Try to override file permission + if (e.code === 'EPERM') { + await ioUtil.chmod(destFile, '0666') + await ioUtil.unlink(destFile) + } + // other errors = it doesn't exist, no work to do + } + + // Copy over symlink + const symlinkFull: string = await ioUtil.readlink(srcFile) + await ioUtil.symlink( + symlinkFull, + destFile, + ioUtil.IS_WINDOWS ? 'junction' : null + ) + } else if (!(await ioUtil.exists(destFile)) || force) { + await ioUtil.copyFile(srcFile, destFile) + } +} diff --git a/actions-toolkit/packages/io/tsconfig.json b/actions-toolkit/packages/io/tsconfig.json new file mode 100644 index 00000000..a8b812a6 --- /dev/null +++ b/actions-toolkit/packages/io/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/LICENSE.md b/actions-toolkit/packages/tool-cache/LICENSE.md new file mode 100644 index 00000000..dbae2edb --- /dev/null +++ b/actions-toolkit/packages/tool-cache/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright 2019 GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/README.md b/actions-toolkit/packages/tool-cache/README.md new file mode 100644 index 00000000..d3da0c35 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/README.md @@ -0,0 +1,86 @@ +# `@actions/tool-cache` + +> Functions necessary for downloading and caching tools. + +## Usage + +#### Download + +You can use this to download tools (or other files) from a download URL: + +```js +const tc = require('@actions/tool-cache'); + +const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz'); +``` + +#### Extract + +These can then be extracted in platform specific ways: + +```js +const tc = require('@actions/tool-cache'); + +if (process.platform === 'win32') { + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip'); + const node12ExtractedFolder = await tc.extractZip(node12Path, 'path/to/extract/to'); + + // Or alternately + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z'); + const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to'); +} +else if (process.platform === 'darwin') { + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0.pkg'); + const node12ExtractedFolder = await tc.extractXar(node12Path, 'path/to/extract/to'); +} +else { + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz'); + const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to'); +} +``` + +#### Cache + +Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for self-hosted runners. + +You'll often want to add it to the path as part of this step: + +```js +const tc = require('@actions/tool-cache'); +const core = require('@actions/core'); + +const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz'); +const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to'); + +const cachedPath = await tc.cacheDir(node12ExtractedFolder, 'node', '12.7.0'); +core.addPath(cachedPath); +``` + +You can also cache files for reuse. + +```js +const tc = require('@actions/tool-cache'); + +const cachedPath = await tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0'); +``` + +#### Find + +Finally, you can find directories and files you've previously cached: + +```js +const tc = require('@actions/tool-cache'); +const core = require('@actions/core'); + +const nodeDirectory = tc.find('node', '12.x', 'x64'); +core.addPath(nodeDirectory); +``` + +You can even find all cached versions of a tool: + +```js +const tc = require('@actions/tool-cache'); + +const allNodeVersions = tc.findAllVersions('node'); +console.log(`Versions of node available: ${allNodeVersions}`); +``` diff --git a/actions-toolkit/packages/tool-cache/RELEASES.md b/actions-toolkit/packages/tool-cache/RELEASES.md new file mode 100644 index 00000000..9fdd4898 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/RELEASES.md @@ -0,0 +1,69 @@ +# @actions/tool-cache Releases + +### 2.0.1 +- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087) + +### 2.0.0 +- Update to v2.0.0 of `@actions/http-client` +- The type of the `headers` parameter in the exported function `downloadTool` has been narrowed from `{ [header: string]: any }` to `{ [header: string]: number | string | string[] | undefined; }` (that is, `http.OutgoingHttpHeaders`). + This is strictly a compile-time change for TypeScript consumers. Previous attempts to use a header value of a type other than those now accepted would have resulted in an error at run time. + +### 1.7.2 +- Update `lockfileVersion` to `v2` in `package-lock.json [#1025](https://github.com/actions/toolkit/pull/1025) + +### 1.7.1 +- [Fallback to os-releases file to get linux version](https://github.com/actions/toolkit/pull/594) +- [Update to latest @actions/io verison](https://github.com/actions/toolkit/pull/838) + +### 1.7.0 +- [Allow arbirtary headers when downloading tools to the tc](https://github.com/actions/toolkit/pull/530) +- [Export `isExplicitVersion` and `evaluateVersions` functions](https://github.com/actions/toolkit/pull/796) +- [Force overwrite on default when extracted compressed files](https://github.com/actions/toolkit/pull/807) + +### 1.6.1 +- [Update @actions/core version](https://github.com/actions/toolkit/pull/636) + +### 1.6.0 +- [Add extractXar function to extract XAR files](https://github.com/actions/toolkit/pull/207) + +### 1.3.5 + +- [Check if tool path exists before executing](https://github.com/actions/toolkit/pull/385) +- [Make extract functions quiet by default](https://github.com/actions/toolkit/pull/206) + +### 1.3.4 + +- [Update the http-client to 1.0.8 which had a security fix](https://github.com/actions/toolkit/pull/429) + +Here is [the security issue](https://github.com/actions/http-client/pull/27) that was fixed in the http-client 1.0.8 release + +### 1.3.3 + +- [Update downloadTool to only retry 500s and 408 and 429](https://github.com/actions/toolkit/pull/373) + +### 1.3.2 + +- [Update downloadTool with better error handling and retries](https://github.com/actions/toolkit/pull/369) + +### 1.3.1 + +- [Increase http-client min version](https://github.com/actions/toolkit/pull/314) + +### 1.3.0 + +- [Uses @actions/http-client](https://github.com/actions/http-client) + +### 1.2.0 + +- [Overload downloadTool to accept destination path](https://github.com/actions/toolkit/pull/257) +- [Fix `extractTar` on Windows](https://github.com/actions/toolkit/pull/264) +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + +### 1.1.2 + +- [Use zip and unzip from PATH](https://github.com/actions/toolkit/pull/161) +- [Support custom flags for `extractTar`](https://github.com/actions/toolkit/pull/48) + +### 1.0.0 + +- Initial release \ No newline at end of file diff --git "a/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" "b/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" new file mode 100644 index 00000000..5803ea15 --- /dev/null +++ "b/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" @@ -0,0 +1 @@ +file-with--character.txt \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/file.txt b/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/file.txt new file mode 100644 index 00000000..f2da98ad --- /dev/null +++ b/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/file.txt @@ -0,0 +1 @@ +file.txt contents \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt b/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt new file mode 100644 index 00000000..37922426 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt @@ -0,0 +1 @@ +folder/nested-file.txt contents \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/__tests__/data/test.7z b/actions-toolkit/packages/tool-cache/__tests__/data/test.7z new file mode 100644 index 00000000..7f385fc7 Binary files /dev/null and b/actions-toolkit/packages/tool-cache/__tests__/data/test.7z differ diff --git a/actions-toolkit/packages/tool-cache/__tests__/data/test.tar.gz b/actions-toolkit/packages/tool-cache/__tests__/data/test.tar.gz new file mode 100644 index 00000000..ea74cafe Binary files /dev/null and b/actions-toolkit/packages/tool-cache/__tests__/data/test.tar.gz differ diff --git a/actions-toolkit/packages/tool-cache/__tests__/data/test.tar.xz b/actions-toolkit/packages/tool-cache/__tests__/data/test.tar.xz new file mode 100644 index 00000000..5993e1fd Binary files /dev/null and b/actions-toolkit/packages/tool-cache/__tests__/data/test.tar.xz differ diff --git a/actions-toolkit/packages/tool-cache/__tests__/data/versions-manifest.json b/actions-toolkit/packages/tool-cache/__tests__/data/versions-manifest.json new file mode 100644 index 00000000..0c45b81a --- /dev/null +++ b/actions-toolkit/packages/tool-cache/__tests__/data/versions-manifest.json @@ -0,0 +1,86 @@ +[ + { + "version": "3.0.1", + "stable": false, + "release_url": "https://github.com/actions/sometool/releases/tag/3.0.1-20200402.6", + "files": [ + { + "filename": "sometool-3.0.1-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/3.0.1-20200402.6/sometool-1.2.3-linux-x64.tar.gz" + } + ] + }, + { + "version": "2.0.2", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6", + "files": [ + { + "filename": "sometool-2.0.2-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x64.tar.gz" + }, + { + "filename": "sometool-2.0.2-linux-x32.tar.gz", + "arch": "x32", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x32.tar.gz" + }, + { + "filename": "sometool-2.0.2-linux-x32.tar.gz", + "arch": "x64", + "platform": "windows", + "download_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x32.tar.gz" + } + ] + }, + { + "version": "1.2.4", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6", + "files": [ + { + "filename": "sometool-1.2.4-ubuntu1804-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "platform_version": "18.04", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-ubuntu1804-x64.tar.gz" + }, + { + "filename": "sometool-1.2.4-darwin1015-x64.tar.gz", + "arch": "x64", + "platform": "darwin", + "platform_version": "10.15", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-darwin1015-x64.tar.gz" + } + ] + }, + { + "version": "1.2.3", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6", + "files": [ + { + "filename": "sometool-1.2.3-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x64.tar.gz" + }, + { + "filename": "sometool-1.2.3-linux-x32.tar.gz", + "arch": "x32", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x32.tar.gz" + }, + { + "filename": "sometool-1.2.3-linux-x32.zip", + "arch": "x64", + "platform": "windows", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x32.zip" + } + ] + } +] \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/__tests__/manifest.test.ts b/actions-toolkit/packages/tool-cache/__tests__/manifest.test.ts new file mode 100644 index 00000000..37146b89 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/__tests__/manifest.test.ts @@ -0,0 +1,314 @@ +import * as tc from '../src/tool-cache' +import * as mm from '../src/manifest' // --> OFF + +// needs to be require for core node modules to be mocked +// eslint-disable-next-line @typescript-eslint/no-require-imports +import osm = require('os') +// eslint-disable-next-line @typescript-eslint/no-require-imports +import cp = require('child_process') +//import {coerce} from 'semver' + +// we fetch the manifest file from main of a repo +const owner = 'actions' +const repo = 'some-tool' +const fakeToken = 'notrealtoken' + +// just loading data and require handles BOMs etc. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const manifestData = require('./data/versions-manifest.json') + +describe('@actions/tool-cache-manifest', () => { + let os: {platform: string; arch: string} + + let getSpy: jest.SpyInstance + let platSpy: jest.SpyInstance + let archSpy: jest.SpyInstance + let execSpy: jest.SpyInstance + let readLsbSpy: jest.SpyInstance + + beforeEach(() => { + // node + os = {platform: '', arch: ''} + platSpy = jest.spyOn(osm, 'platform') + + platSpy.mockImplementation(() => os.platform) + archSpy = jest.spyOn(osm, 'arch') + archSpy.mockImplementation(() => os.arch) + + execSpy = jest.spyOn(cp, 'execSync') + readLsbSpy = jest.spyOn(mm, '_readLinuxVersionFile') + + getSpy = jest.spyOn(tc, 'getManifestFromRepo') + getSpy.mockImplementation(() => manifestData) + }) + + afterEach(() => { + jest.resetAllMocks() + jest.clearAllMocks() + //jest.restoreAllMocks(); + }) + + afterAll(async () => {}, 100000) + + it('can query versions', async () => { + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + + expect(manifest).toBeDefined() + const l: number = manifest ? manifest.length : 0 + expect(l).toBe(4) + }) + + it('can match stable major version for linux x64', async () => { + os.platform = 'linux' + os.arch = 'x64' + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '2.x', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('2.0.2') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-2.0.2-linux-x64.tar.gz') + }) + + it('can match stable exact version for linux x64', async () => { + os.platform = 'linux' + os.arch = 'x64' + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.3', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.3') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.3-linux-x64.tar.gz') + }) + + it('can match with linux platform version spec from lsb-release', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=18.04 + DISTRIB_CODENAME=bionic + DISTRIB_DESCRIPTION=Ubuntu 18.04.4 LTS` + }) + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.4') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-ubuntu1804-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.4-ubuntu1804-x64.tar.gz') + }) + + it('can match with linux platform version spec from os-release', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `NAME="Ubuntu" + VERSION="18.04.5 LTS (Bionic Beaver)" + ID=ubuntu + ID_LIKE=debian + PRETTY_NAME="Ubuntu 18.04.5 LTS" + VERSION_ID="18.04" + HOME_URL="https://www.ubuntu.com/" + SUPPORT_URL="https://help.ubuntu.com/" + BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" + PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" + VERSION_CODENAME=bionic + UBUNTU_CODENAME=bionic` + }) + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.4') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-ubuntu1804-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.4-ubuntu1804-x64.tar.gz') + }) + + it('can match with darwin platform version spec', async () => { + os.platform = 'darwin' + os.arch = 'x64' + + execSpy.mockImplementation(() => '10.15.1') + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.4') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('darwin') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-darwin1015-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.4-darwin1015-x64.tar.gz') + }) + + it('does not match with unmatched linux platform version spec', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=16.04 + DISTRIB_CODENAME=xenial + DISTRIB_DESCRIPTION=Ubuntu 16.04.4 LTS` + }) + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeUndefined() + }) + + it('does not match with unmatched darwin platform version spec', async () => { + os.platform = 'darwin' + os.arch = 'x64' + + execSpy.mockImplementation(() => '10.14.6') + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeUndefined() + }) + + it('can get version from lsb on ubuntu-18.04', async () => { + os.platform = 'linux' + os.arch = 'x64' + + //existsSpy.mockImplementation(() => true) + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=18.04 + DISTRIB_CODENAME=bionic + DISTRIB_DESCRIPTION=Ubuntu 18.04.4 LTS` + }) + + const version = mm._getOsVersion() + + expect(osm.platform()).toBe('linux') + expect(version).toBe('18.04') + }) + + it('can get version from lsb on ubuntu-16.04', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=16.04 + DISTRIB_CODENAME=xenial + DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"` + }) + + const version = mm._getOsVersion() + + expect(osm.platform()).toBe('linux') + expect(version).toBe('16.04') + }) + + // sw_vers -productVersion + it('can get version on macOS', async () => { + os.platform = 'darwin' + os.arch = 'x64' + + execSpy.mockImplementation(() => '10.14.6') + + const version = mm._getOsVersion() + + expect(osm.platform()).toBe('darwin') + expect(version).toBe('10.14.6') + }) +}) diff --git a/actions-toolkit/packages/tool-cache/__tests__/retry-helper.test.ts b/actions-toolkit/packages/tool-cache/__tests__/retry-helper.test.ts new file mode 100644 index 00000000..7b110db3 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/__tests__/retry-helper.test.ts @@ -0,0 +1,125 @@ +import * as core from '@actions/core' +import {RetryHelper} from '../src/retry-helper' + +let info: string[] +let retryHelper: RetryHelper + +describe('retry-helper tests', () => { + beforeAll(() => { + // Mock @actions/core info() + jest.spyOn(core, 'info').mockImplementation((message: string) => { + info.push(message) + }) + + retryHelper = new RetryHelper(3, 0, 0) + }) + + beforeEach(() => { + // Reset info + info = [] + }) + + afterAll(() => { + // Restore + jest.restoreAllMocks() + }) + + it('first attempt succeeds', async () => { + const actual = await retryHelper.execute(async () => { + return 'some result' + }) + expect(actual).toBe('some result') + expect(info).toHaveLength(0) + }) + + it('second attempt succeeds', async () => { + let attempts = 0 + const actual = await retryHelper.execute(async () => { + if (++attempts === 1) { + throw new Error('some error') + } + + return Promise.resolve('some result') + }) + expect(attempts).toBe(2) + expect(actual).toBe('some result') + expect(info).toHaveLength(2) + expect(info[0]).toBe('some error') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + }) + + it('third attempt succeeds', async () => { + let attempts = 0 + const actual = await retryHelper.execute(async () => { + if (++attempts < 3) { + throw new Error(`some error ${attempts}`) + } + + return Promise.resolve('some result') + }) + expect(attempts).toBe(3) + expect(actual).toBe('some result') + expect(info).toHaveLength(4) + expect(info[0]).toBe('some error 1') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + expect(info[2]).toBe('some error 2') + expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) + }) + + it('all attempts fail', async () => { + let attempts = 0 + let error: Error = null as unknown as Error + try { + await retryHelper.execute(() => { + throw new Error(`some error ${++attempts}`) + }) + } catch (err) { + error = err + } + expect(error.message).toBe('some error 3') + expect(attempts).toBe(3) + expect(info).toHaveLength(4) + expect(info[0]).toBe('some error 1') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + expect(info[2]).toBe('some error 2') + expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) + }) + + it('checks retryable after first attempt', async () => { + let attempts = 0 + let error: Error = null as unknown as Error + try { + await retryHelper.execute( + async () => { + throw new Error(`some error ${++attempts}`) + }, + () => false + ) + } catch (err) { + error = err + } + expect(error.message).toBe('some error 1') + expect(attempts).toBe(1) + expect(info).toHaveLength(0) + }) + + it('checks retryable after second attempt', async () => { + let attempts = 0 + let error: Error = null as unknown as Error + try { + await retryHelper.execute( + async () => { + throw new Error(`some error ${++attempts}`) + }, + (e: Error) => e.message === 'some error 1' + ) + } catch (err) { + error = err + } + expect(error.message).toBe('some error 2') + expect(attempts).toBe(2) + expect(info).toHaveLength(2) + expect(info[0]).toBe('some error 1') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + }) +}) diff --git a/actions-toolkit/packages/tool-cache/__tests__/tool-cache.test.ts b/actions-toolkit/packages/tool-cache/__tests__/tool-cache.test.ts new file mode 100644 index 00000000..4712ab5f --- /dev/null +++ b/actions-toolkit/packages/tool-cache/__tests__/tool-cache.test.ts @@ -0,0 +1,896 @@ +import * as fs from 'fs' +import * as path from 'path' +import * as io from '@actions/io' +import * as exec from '@actions/exec' +import * as stream from 'stream' +import nock from 'nock' + +const cachePath = path.join(__dirname, 'CACHE') +const tempPath = path.join(__dirname, 'TEMP') +// Set temp and tool directories before importing (used to set global state) +process.env['RUNNER_TEMP'] = tempPath +process.env['RUNNER_TOOL_CACHE'] = cachePath + +// eslint-disable-next-line import/first +import * as tc from '../src/tool-cache' + +const IS_WINDOWS = process.platform === 'win32' +const IS_MAC = process.platform === 'darwin' + +describe('@actions/tool-cache', function () { + beforeAll(function () { + nock('http://example.com').persist().get('/bytes/35').reply(200, { + username: 'abc', + password: 'def' + }) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', 0) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 0) + }) + + beforeEach(async function () { + await io.rmRF(cachePath) + await io.rmRF(tempPath) + await io.mkdirP(cachePath) + await io.mkdirP(tempPath) + }) + + afterEach(function () { + setResponseMessageFactory(undefined) + }) + + afterAll(async function () { + await io.rmRF(tempPath) + await io.rmRF(cachePath) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', undefined) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', undefined) + }) + + it('downloads a 35 byte file', async () => { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35' + ) + + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + }) + + it('downloads a 35 byte file (dest)', async () => { + const dest = 'test-download-file' + try { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35', + dest + ) + + expect(downPath).toEqual(dest) + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + } finally { + try { + await fs.promises.unlink(dest) + } catch { + // intentionally empty + } + } + }) + + it('downloads a 35 byte file (dest requires mkdirp)', async () => { + const dest = 'test-download-dir/test-download-file' + try { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35', + dest + ) + + expect(downPath).toEqual(dest) + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + } finally { + try { + await fs.promises.unlink(dest) + await fs.promises.rmdir(path.dirname(dest)) + } catch { + // intentionally empty + } + } + }) + + it('downloads a 35 byte file after a redirect', async () => { + nock('http://example.com') + .persist() + .get('/redirect-to') + .reply(303, undefined, { + location: 'http://example.com/bytes/35' + }) + + const downPath: string = await tc.downloadTool( + 'http://example.com/redirect-to' + ) + + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + }) + + it('handles error from response message stream', async () => { + nock('http://example.com') + .persist() + .get('/error-from-response-message-stream') + .reply(200, {}) + + setResponseMessageFactory(() => { + const readStream = new stream.Readable() + readStream._read = () => { + readStream.destroy(new Error('uh oh')) + } + return readStream + }) + + let error = new Error('unexpected') + try { + await tc.downloadTool( + 'http://example.com/error-from-response-message-stream' + ) + } catch (err) { + error = err + } + + expect(error).not.toBeUndefined() + expect(error.message).toBe('uh oh') + }) + + it('retries error from response message stream', async () => { + nock('http://example.com') + .persist() + .get('/retries-error-from-response-message-stream') + .reply(200, {}) + + let attempt = 1 + setResponseMessageFactory(() => { + const readStream = new stream.Readable() + let failsafe = 0 + readStream._read = () => { + // First attempt fails + if (attempt++ === 1) { + readStream.destroy(new Error('uh oh')) + return + } + + // Write some data + if (failsafe++ === 0) { + readStream.push(''.padEnd(35)) + readStream.push(null) // no more data + } + } + + return readStream + }) + + const downPath = await tc.downloadTool( + 'http://example.com/retries-error-from-response-message-stream' + ) + + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + }) + + it('has status code in exception dictionary for HTTP error code responses', async () => { + nock('http://example.com').persist().get('/bytes/bad').reply(400, { + username: 'bad', + password: 'file' + }) + + expect.assertions(2) + + try { + const errorCodeUrl = 'http://example.com/bytes/bad' + await tc.downloadTool(errorCodeUrl) + } catch (err) { + expect(err.toString()).toContain('Unexpected HTTP response: 400') + expect(err['httpStatusCode']).toBe(400) + } + }) + + it('works with redirect code 302', async function () { + nock('http://example.com') + .persist() + .get('/redirect-to') + .reply(302, undefined, { + location: 'http://example.com/bytes/35' + }) + + const downPath: string = await tc.downloadTool( + 'http://example.com/redirect-to' + ) + + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + }) + + it('installs a binary tool and finds it', async () => { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35' + ) + + expect(fs.existsSync(downPath)).toBeTruthy() + + await tc.cacheFile(downPath, 'foo', 'foo', '1.1.0') + + const toolPath: string = tc.find('foo', '1.1.0') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + + const binaryPath: string = path.join(toolPath, 'foo') + expect(fs.existsSync(binaryPath)).toBeTruthy() + }) + + if (IS_WINDOWS) { + it('installs a 7z and finds it', async () => { + const tempDir = path.join(__dirname, 'test-install-7z') + try { + await io.mkdirP(tempDir) + + // copy the 7z file to the test dir + const _7zFile: string = path.join(tempDir, 'test.7z') + await io.cp(path.join(__dirname, 'data', 'test.7z'), _7zFile) + + const destDir = path.join(tempDir, 'destination') + await io.mkdirP(destDir) + fs.writeFileSync(path.join(destDir, 'file.txt'), 'overwriteMe') + + // extract/cache + const extPath: string = await tc.extract7z(_7zFile) + await tc.cacheDir(extPath, 'my-7z-contents', '1.1.0') + const toolPath: string = tc.find('my-7z-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect(fs.readFileSync(path.join(toolPath, 'file.txt'), 'utf8')).toBe( + 'file.txt contents' + ) + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + } + }) + + it('extracts a 7z to a directory that does not exist', async () => { + const tempDir = path.join(__dirname, 'test-install-7z') + const destDir = path.join(tempDir, 'not-exist') + try { + await io.mkdirP(tempDir) + + // copy the 7z file to the test dir + const _7zFile: string = path.join(tempDir, 'test.7z') + await io.cp(path.join(__dirname, 'data', 'test.7z'), _7zFile) + + // extract/cache + const extPath: string = await tc.extract7z(_7zFile, destDir) + await tc.cacheDir(extPath, 'my-7z-contents', '1.1.0') + const toolPath: string = tc.find('my-7z-contents', '1.1.0') + + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + } + }) + + it('extract 7z using custom 7z tool', async function () { + const tempDir = path.join( + __dirname, + 'test-extract-7z-using-custom-7z-tool' + ) + try { + await io.mkdirP(tempDir) + // create mock7zr.cmd + const mock7zrPath: string = path.join(tempDir, 'mock7zr.cmd') + fs.writeFileSync( + mock7zrPath, + [ + 'echo %* > "%~dp0mock7zr-args.txt"', + `"${path.join( + __dirname, + '..', + 'scripts', + 'externals', + '7zdec.exe' + )}" x %5` + ].join('\r\n') + ) + + // copy the 7z file to the test dir + const _7zFile: string = path.join(tempDir, 'test.7z') + await io.cp(path.join(__dirname, 'data', 'test.7z'), _7zFile) + + // extract + const extPath: string = await tc.extract7z( + _7zFile, + undefined, + mock7zrPath + ) + + expect(fs.existsSync(extPath)).toBeTruthy() + expect( + fs.existsSync(path.join(tempDir, 'mock7zr-args.txt')) + ).toBeTruthy() + expect( + fs + .readFileSync(path.join(tempDir, 'mock7zr-args.txt')) + .toString() + .trim() + ).toBe(`x -bb0 -bd -sccUTF-8 ${_7zFile}`) + expect(fs.existsSync(path.join(extPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(extPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(extPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + } + }) + it.each(['pwsh', 'powershell'])( + 'unzip properly fails with bad path (%s)', + async powershellTool => { + const originalPath = process.env['PATH'] + try { + if (powershellTool === 'powershell' && IS_WINDOWS) { + //remove pwsh from PATH temporarily to test fallback case + process.env['PATH'] = removePWSHFromPath(process.env['PATH']) + } + + await expect(tc.extractZip('badPath')).rejects.toThrow() + } finally { + process.env['PATH'] = originalPath + } + } + ) + } else if (IS_MAC) { + it('extract .xar', async () => { + const tempDir = path.join(tempPath, 'test-install.xar') + const sourcePath = path.join(__dirname, 'data', 'archive-content') + const targetPath = path.join(tempDir, 'test.xar') + await io.mkdirP(tempDir) + + // Create test archive + const xarPath = await io.which('xar', true) + await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], { + cwd: sourcePath + }) + + const destDir = path.join(tempDir, 'destination') + await io.mkdirP(destDir) + fs.writeFileSync(path.join(destDir, 'file.txt'), 'overwriteMe') + + // extract/cache + const extPath: string = await tc.extractXar(targetPath, destDir, ['-x']) + await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0') + const toolPath: string = tc.find('my-xar-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect(fs.readFileSync(path.join(toolPath, 'file.txt'), 'utf8')).toBe( + 'file.txt contents' + ) + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) + + it('extract .xar to a directory that does not exist', async () => { + const tempDir = path.join(tempPath, 'test-install.xar') + const sourcePath = path.join(__dirname, 'data', 'archive-content') + const targetPath = path.join(tempDir, 'test.xar') + await io.mkdirP(tempDir) + + const destDir = path.join(tempDir, 'not-exist') + + // Create test archive + const xarPath = await io.which('xar', true) + await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], { + cwd: sourcePath + }) + + // extract/cache + const extPath: string = await tc.extractXar(targetPath, destDir, '-x') + await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0') + const toolPath: string = tc.find('my-xar-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) + + it('extract .xar without flags', async () => { + const tempDir = path.join(tempPath, 'test-install.xar') + const sourcePath = path.join(__dirname, 'data', 'archive-content') + const targetPath = path.join(tempDir, 'test.xar') + await io.mkdirP(tempDir) + + // Create test archive + const xarPath = await io.which('xar', true) + await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], { + cwd: sourcePath + }) + + // extract/cache + const extPath: string = await tc.extractXar(targetPath, undefined) + await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0') + const toolPath: string = tc.find('my-xar-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) + } + + it('extract .tar.gz', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.gz') + + await io.mkdirP(tempDir) + + // copy the .tar.gz file to the test dir + const _tgzFile: string = path.join(tempDir, 'test.tar.gz') + await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) + + //Create file to overwrite + const destDir = path.join(tempDir, 'extract-dest') + await io.rmRF(destDir) + await io.mkdirP(destDir) + fs.writeFileSync(path.join(destDir, 'file.txt'), 'overwriteMe') + + // extract/cache + const extPath: string = await tc.extractTar(_tgzFile, destDir) + await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') + const toolPath: string = tc.find('my-tgz-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect(fs.readFileSync(path.join(toolPath, 'file.txt'), 'utf8')).toBe( + 'file.txt contents' + ) + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync(path.join(toolPath, 'folder', 'nested-file.txt'), 'utf8') + ).toBe('folder/nested-file.txt contents') + }) + + it('extract .tar.gz to a directory that does not exist', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.gz') + const destDir = path.join(tempDir, 'not-exist') + + await io.mkdirP(tempDir) + + // copy the .tar.gz file to the test dir + const _tgzFile: string = path.join(tempDir, 'test.tar.gz') + await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) + + // extract/cache + const extPath: string = await tc.extractTar(_tgzFile, destDir) + await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') + const toolPath: string = tc.find('my-tgz-contents', '1.1.0') + + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect(fs.readFileSync(path.join(toolPath, 'file.txt'), 'utf8')).toBe( + 'file.txt contents' + ) + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync(path.join(toolPath, 'folder', 'nested-file.txt'), 'utf8') + ).toBe('folder/nested-file.txt contents') + }) + + it('extract .tar.xz', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.xz') + + await io.mkdirP(tempDir) + + // copy the .tar.gz file to the test dir + const _txzFile: string = path.join(tempDir, 'test.tar.xz') + await io.cp(path.join(__dirname, 'data', 'test.tar.xz'), _txzFile) + + //Create file to overwrite + const destDir = path.join(tempDir, 'extract-dest') + await io.rmRF(destDir) + await io.mkdirP(destDir) + fs.writeFileSync(path.join(destDir, 'file.txt'), 'overwriteMe') + + // extract/cache + const extPath: string = await tc.extractTar(_txzFile, destDir, 'x') + await tc.cacheDir(extPath, 'my-txz-contents', '1.1.0') + const toolPath: string = tc.find('my-txz-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'bar.txt'))).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'foo', 'hello.txt'))).toBeTruthy() + expect( + fs.readFileSync(path.join(toolPath, 'foo', 'hello.txt'), 'utf8') + ).toBe('foo/hello: world') + }) + + it.each(['pwsh', 'powershell'])( + 'installs a zip and finds it (%s)', + async powershellTool => { + const tempDir = path.join(__dirname, 'test-install-zip') + const originalPath = process.env['PATH'] + try { + await io.mkdirP(tempDir) + + // stage the layout for a zip file: + // file.txt + // folder/nested-file.txt + const stagingDir = path.join(tempDir, 'zip-staging') + await io.mkdirP(path.join(stagingDir, 'folder')) + fs.writeFileSync(path.join(stagingDir, 'file.txt'), '') + fs.writeFileSync(path.join(stagingDir, 'folder', 'nested-file.txt'), '') + + // create the zip + const zipFile = path.join(tempDir, 'test.zip') + await io.rmRF(zipFile) + if (IS_WINDOWS) { + const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes + const escapedZipFile = zipFile.replace(/'/g, "''") + const powershellPath = + (await io.which('pwsh', false)) || + (await io.which('powershell', true)) + const args = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + `$ErrorActionPreference = 'Stop' ; Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::CreateFromDirectory('${escapedStagingPath}', '${escapedZipFile}')` + ] + await exec.exec(`"${powershellPath}"`, args) + } else { + const zipPath: string = await io.which('zip', true) + await exec.exec(`"${zipPath}`, [zipFile, '-r', '.'], { + cwd: stagingDir + }) + } + + if (powershellTool === 'powershell' && IS_WINDOWS) { + //remove pwsh from PATH temporarily to test fallback case + process.env['PATH'] = removePWSHFromPath(process.env['PATH']) + } + + const extPath: string = await tc.extractZip(zipFile) + await tc.cacheDir(extPath, 'foo', '1.1.0') + const toolPath: string = tc.find('foo', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + process.env['PATH'] = originalPath + } + } + ) + + it('installs a zip and extracts it to specified directory', async function () { + const tempDir = path.join(__dirname, 'test-install-zip') + try { + await io.mkdirP(tempDir) + + // stage the layout for a zip file: + // file.txt + // folder/nested-file.txt + const stagingDir = path.join(tempDir, 'zip-staging') + await io.mkdirP(path.join(stagingDir, 'folder')) + fs.writeFileSync(path.join(stagingDir, 'file.txt'), 'originalText') + fs.writeFileSync(path.join(stagingDir, 'folder', 'nested-file.txt'), '') + + // create the zip + const zipFile = path.join(tempDir, 'test.zip') + await io.rmRF(zipFile) + if (IS_WINDOWS) { + const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes + const escapedZipFile = zipFile.replace(/'/g, "''") + const powershellPath = + (await io.which('pwsh', false)) || + (await io.which('powershell', true)) + const args = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + `$ErrorActionPreference = 'Stop' ; Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::CreateFromDirectory('${escapedStagingPath}', '${escapedZipFile}')` + ] + await exec.exec(`"${powershellPath}"`, args) + } else { + const zipPath: string = await io.which('zip', true) + await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir}) + } + + const destDir = path.join(__dirname, 'unzip-dest') + await io.rmRF(destDir) + await io.mkdirP(destDir) + try { + fs.writeFileSync(path.join(destDir, 'file.txt'), 'overwriteMe') + const extPath: string = await tc.extractZip(zipFile, destDir) + await tc.cacheDir(extPath, 'foo', '1.1.0') + const toolPath: string = tc.find('foo', '1.1.0') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect(fs.readFileSync(path.join(toolPath, 'file.txt'), 'utf8')).toBe( + 'originalText' + ) + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(destDir) + } + } finally { + await io.rmRF(tempDir) + } + }) + + it('extract zip to a directory that does not exist', async function () { + const tempDir = path.join(__dirname, 'test-install-zip') + try { + await io.mkdirP(tempDir) + + // stage the layout for a zip file: + // file.txt + // folder/nested-file.txt + const stagingDir = path.join(tempDir, 'zip-staging') + await io.mkdirP(path.join(stagingDir, 'folder')) + fs.writeFileSync(path.join(stagingDir, 'file.txt'), '') + fs.writeFileSync(path.join(stagingDir, 'folder', 'nested-file.txt'), '') + + // create the zip + const zipFile = path.join(tempDir, 'test.zip') + await io.rmRF(zipFile) + if (IS_WINDOWS) { + const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes + const escapedZipFile = zipFile.replace(/'/g, "''") + const powershellPath = + (await io.which('pwsh', false)) || + (await io.which('powershell', true)) + const args = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + `$ErrorActionPreference = 'Stop' ; Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::CreateFromDirectory('${escapedStagingPath}', '${escapedZipFile}')` + ] + await exec.exec(`"${powershellPath}"`, args) + } else { + const zipPath: string = await io.which('zip', true) + await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir}) + } + + const destDir = path.join(tempDir, 'not-exist') + + const extPath: string = await tc.extractZip(zipFile, destDir) + await tc.cacheDir(extPath, 'foo', '1.1.0') + const toolPath: string = tc.find('foo', '1.1.0') + + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + } + }) + + it('works with a 502 temporary failure', async function () { + nock('http://example.com').get('/temp502').twice().reply(502, undefined) + nock('http://example.com').get('/temp502').reply(200, undefined) + + const statusCodeUrl = 'http://example.com/temp502' + await tc.downloadTool(statusCodeUrl) + }) + + it("doesn't retry 502s more than 3 times", async function () { + nock('http://example.com').get('/perm502').times(3).reply(502, undefined) + + expect.assertions(1) + + try { + const statusCodeUrl = 'http://example.com/perm502' + await tc.downloadTool(statusCodeUrl) + } catch (err) { + expect(err.toString()).toContain('502') + } + }) + + it('retries 429s', async function () { + nock('http://example.com') + .get('/too-many-requests-429') + .times(2) + .reply(429, undefined) + nock('http://example.com') + .get('/too-many-requests-429') + .reply(500, undefined) + + try { + const statusCodeUrl = 'http://example.com/too-many-requests-429' + await tc.downloadTool(statusCodeUrl) + } catch (err) { + expect(err.toString()).toContain('500') + } + }) + + it("doesn't retry 404", async function () { + nock('http://example.com').get('/not-found-404').reply(404, undefined) + nock('http://example.com').get('/not-found-404').reply(500, undefined) + + try { + const statusCodeUrl = 'http://example.com/not-found-404' + await tc.downloadTool(statusCodeUrl) + } catch (err) { + expect(err.toString()).toContain('404') + } + }) + + it('supports authorization headers', async function () { + nock('http://example.com', { + reqheaders: { + authorization: 'token abc123' + } + }) + .get('/some-file-that-needs-authorization') + .reply(200, undefined) + + await tc.downloadTool( + 'http://example.com/some-file-that-needs-authorization', + undefined, + 'token abc123' + ) + }) + + it('supports custom headers', async function () { + nock('http://example.com', { + reqheaders: { + accept: 'application/octet-stream' + } + }) + .get('/some-file-that-needs-headers') + .reply(200, undefined) + + await tc.downloadTool( + 'http://example.com/some-file-that-needs-headers', + undefined, + undefined, + { + accept: 'application/octet-stream' + } + ) + }) + + it('supports authorization and custom headers', async function () { + nock('http://example.com', { + reqheaders: { + accept: 'application/octet-stream', + authorization: 'token abc123' + } + }) + .get('/some-file-that-needs-authorization-and-headers') + .reply(200, undefined) + + await tc.downloadTool( + 'http://example.com/some-file-that-needs-authorization-and-headers', + undefined, + 'token abc123', + { + accept: 'application/octet-stream' + } + ) + }) +}) + +/** + * Sets up a mock response body for downloadTool. This function works around a limitation with + * nock when the response stream emits an error. + */ +function setResponseMessageFactory( + factory: (() => stream.Readable) | undefined +): void { + setGlobal('TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', factory) +} + +/** + * Sets a global variable + */ +function setGlobal(key: string, value: T | undefined): void { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const g = global as any + /* eslint-enable @typescript-eslint/no-explicit-any */ + if (value === undefined) { + delete g[key] + } else { + g[key] = value + } +} + +function removePWSHFromPath(pathEnv: string | undefined): string { + return (pathEnv || '') + .split(';') + .filter(segment => { + return !segment.startsWith(`C:\\Program Files\\PowerShell`) + }) + .join(';') +} diff --git a/actions-toolkit/packages/tool-cache/package-lock.json b/actions-toolkit/packages/tool-cache/package-lock.json new file mode 100644 index 00000000..d431aa44 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/package-lock.json @@ -0,0 +1,309 @@ +{ + "name": "@actions/tool-cache", + "version": "2.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@actions/tool-cache", + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.0", + "@actions/http-client": "^2.0.1", + "@actions/io": "^1.1.1", + "semver": "^6.1.0", + "uuid": "^3.3.2" + }, + "devDependencies": { + "@types/nock": "^11.1.0", + "@types/semver": "^6.0.0", + "@types/uuid": "^3.4.4", + "nock": "^13.2.9" + } + }, + "node_modules/@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "dependencies": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + } + }, + "node_modules/@actions/core/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "dependencies": { + "tunnel": "^0.0.6" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "node_modules/@types/nock": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz", + "integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==", + "deprecated": "This is a stub types definition. nock provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "nock": "*" + } + }, + "node_modules/@types/node": { + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.0.tgz", + "integrity": "sha512-vqcj1MVm2Sla4PpMfYKh1MyDN4D2f/mPIZD7RdAGqEsbE+JxfeqQHHVbRDQ0Nqn8i73gJa1HQ1Pu3+nH4Q0Yiw==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.1.tgz", + "integrity": "sha512-ffCdcrEE5h8DqVxinQjo+2d1q+FV5z7iNtPofw3JsrltSoSVlOGaW0rY8XxtO9XukdTn8TaCGWmk2VFGhI70mg==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", + "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nock": { + "version": "13.2.9", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", + "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + } + }, + "dependencies": { + "@actions/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", + "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", + "requires": { + "@actions/http-client": "^2.0.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/http-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", + "requires": { + "tunnel": "^0.0.6" + } + }, + "@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, + "@types/nock": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz", + "integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==", + "dev": true, + "requires": { + "nock": "*" + } + }, + "@types/node": { + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.0.tgz", + "integrity": "sha512-vqcj1MVm2Sla4PpMfYKh1MyDN4D2f/mPIZD7RdAGqEsbE+JxfeqQHHVbRDQ0Nqn8i73gJa1HQ1Pu3+nH4Q0Yiw==", + "dev": true + }, + "@types/semver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.1.tgz", + "integrity": "sha512-ffCdcrEE5h8DqVxinQjo+2d1q+FV5z7iNtPofw3JsrltSoSVlOGaW0rY8XxtO9XukdTn8TaCGWmk2VFGhI70mg==", + "dev": true + }, + "@types/uuid": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", + "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nock": { + "version": "13.2.9", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", + "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + } + }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } +} diff --git a/actions-toolkit/packages/tool-cache/package.json b/actions-toolkit/packages/tool-cache/package.json new file mode 100644 index 00000000..7a05399a --- /dev/null +++ b/actions-toolkit/packages/tool-cache/package.json @@ -0,0 +1,52 @@ +{ + "name": "@actions/tool-cache", + "version": "2.0.1", + "description": "Actions tool-cache lib", + "keywords": [ + "github", + "actions", + "exec" + ], + "homepage": "https://github.com/actions/toolkit/tree/main/packages/tool-cache", + "license": "MIT", + "main": "lib/tool-cache.js", + "types": "lib/tool-cache.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib", + "scripts" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/tool-cache" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.2.6", + "@actions/exec": "^1.0.0", + "@actions/http-client": "^2.0.1", + "@actions/io": "^1.1.1", + "semver": "^6.1.0", + "uuid": "^3.3.2" + }, + "devDependencies": { + "@types/nock": "^11.1.0", + "@types/semver": "^6.0.0", + "@types/uuid": "^3.4.4", + "nock": "^13.2.9" + } +} diff --git a/actions-toolkit/packages/tool-cache/scripts/Invoke-7zdec.ps1 b/actions-toolkit/packages/tool-cache/scripts/Invoke-7zdec.ps1 new file mode 100644 index 00000000..8b39bb4d --- /dev/null +++ b/actions-toolkit/packages/tool-cache/scripts/Invoke-7zdec.ps1 @@ -0,0 +1,60 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string]$Source, + + [Parameter(Mandatory = $true)] + [string]$Target) + +# This script translates the output from 7zdec into UTF8. Node has limited +# built-in support for encodings. +# +# 7zdec uses the system default code page. The system default code page varies +# depending on the locale configuration. On an en-US box, the system default code +# page is Windows-1252. +# +# Note, on a typical en-US box, testing with the 'ç' character is a good way to +# determine whether data is passed correctly between processes. This is because +# the 'ç' character has a different code point across each of the common encodings +# on a typical en-US box, i.e. +# 1) the default console-output code page (IBM437) +# 2) the system default code page (i.e. CP_ACP) (Windows-1252) +# 3) UTF8 + +$ErrorActionPreference = 'Stop' + +# Redefine the wrapper over STDOUT to use UTF8. Node expects UTF8 by default. +$stdout = [System.Console]::OpenStandardOutput() +$utf8 = New-Object System.Text.UTF8Encoding($false) # do not emit BOM +$writer = New-Object System.IO.StreamWriter($stdout, $utf8) +[System.Console]::SetOut($writer) + +# All subsequent output must be written using [System.Console]::WriteLine(). In +# PowerShell 4, Write-Host and Out-Default do not consider the updated stream writer. + +Set-Location -LiteralPath $Target + +# Print the ##command. +$_7zdec = Join-Path -Path "$PSScriptRoot" -ChildPath "externals/7zdec.exe" +[System.Console]::WriteLine("##[command]$_7zdec x `"$Source`"") + +# The $OutputEncoding variable instructs PowerShell how to interpret the output +# from the external command. +$OutputEncoding = [System.Text.Encoding]::Default + +# Note, the output from 7zdec.exe needs to be iterated over. Otherwise PowerShell.exe +# will launch the external command in such a way that it inherits the streams. +& $_7zdec x $Source 2>&1 | + ForEach-Object { + if ($_ -is [System.Management.Automation.ErrorRecord]) { + [System.Console]::WriteLine($_.Exception.Message) + } + else { + [System.Console]::WriteLine($_) + } + } +[System.Console]::WriteLine("##[debug]7zdec.exe exit code '$LASTEXITCODE'") +[System.Console]::Out.Flush() +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} \ No newline at end of file diff --git a/actions-toolkit/packages/tool-cache/scripts/externals/7zdec.exe b/actions-toolkit/packages/tool-cache/scripts/externals/7zdec.exe new file mode 100644 index 00000000..1106aa0e Binary files /dev/null and b/actions-toolkit/packages/tool-cache/scripts/externals/7zdec.exe differ diff --git a/actions-toolkit/packages/tool-cache/src/manifest.ts b/actions-toolkit/packages/tool-cache/src/manifest.ts new file mode 100644 index 00000000..7e0808f1 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/src/manifest.ts @@ -0,0 +1,165 @@ +import * as semver from 'semver' +import {debug} from '@actions/core' + +// needs to be require for core node modules to be mocked +/* eslint @typescript-eslint/no-require-imports: 0 */ + +import os = require('os') +import cp = require('child_process') +import fs = require('fs') + +/* +NOTE: versions must be sorted descending by version in the manifest + this library short circuits on first semver spec match + + platform_version is an optional filter and can be a semver spec or range +[ + { + "version": "1.2.3", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6", + "files": [ + { + "filename": "sometool-1.2.3-linux-x64.zip", + "arch": "x64", + "platform": "linux", + "platform_version": "18.04" + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x64.zip" + }, + ... + ] + }, + ... +] +*/ + +export interface IToolReleaseFile { + filename: string + // 'aix', 'darwin', 'freebsd', 'linux', 'openbsd', + // 'sunos', and 'win32' + // platform_version is an optional semver filter + // TODO: do we need distribution (e.g. ubuntu). + // not adding yet but might need someday. + // right now, 16.04 and 18.04 work + platform: string + platform_version?: string + + // 'arm', 'arm64', 'ia32', 'mips', 'mipsel', + // 'ppc', 'ppc64', 's390', 's390x', + // 'x32', and 'x64'. + arch: string + + download_url: string +} + +export interface IToolRelease { + version: string + stable: boolean + release_url: string + files: IToolReleaseFile[] +} + +export async function _findMatch( + versionSpec: string, + stable: boolean, + candidates: IToolRelease[], + archFilter: string +): Promise { + const platFilter = os.platform() + + let result: IToolRelease | undefined + let match: IToolRelease | undefined + + let file: IToolReleaseFile | undefined + for (const candidate of candidates) { + const version = candidate.version + + debug(`check ${version} satisfies ${versionSpec}`) + if ( + semver.satisfies(version, versionSpec) && + (!stable || candidate.stable === stable) + ) { + file = candidate.files.find(item => { + debug( + `${item.arch}===${archFilter} && ${item.platform}===${platFilter}` + ) + + let chk = item.arch === archFilter && item.platform === platFilter + if (chk && item.platform_version) { + const osVersion = module.exports._getOsVersion() + + if (osVersion === item.platform_version) { + chk = true + } else { + chk = semver.satisfies(osVersion, item.platform_version) + } + } + + return chk + }) + + if (file) { + debug(`matched ${candidate.version}`) + match = candidate + break + } + } + } + + if (match && file) { + // clone since we're mutating the file list to be only the file that matches + result = Object.assign({}, match) + result.files = [file] + } + + return result +} + +export function _getOsVersion(): string { + // TODO: add windows and other linux, arm variants + // right now filtering on version is only an ubuntu and macos scenario for tools we build for hosted (python) + const plat = os.platform() + let version = '' + + if (plat === 'darwin') { + version = cp.execSync('sw_vers -productVersion').toString() + } else if (plat === 'linux') { + // lsb_release process not in some containers, readfile + // Run cat /etc/lsb-release + // DISTRIB_ID=Ubuntu + // DISTRIB_RELEASE=18.04 + // DISTRIB_CODENAME=bionic + // DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS" + const lsbContents = module.exports._readLinuxVersionFile() + if (lsbContents) { + const lines = lsbContents.split('\n') + for (const line of lines) { + const parts = line.split('=') + if ( + parts.length === 2 && + (parts[0].trim() === 'VERSION_ID' || + parts[0].trim() === 'DISTRIB_RELEASE') + ) { + version = parts[1].trim().replace(/^"/, '').replace(/"$/, '') + break + } + } + } + } + + return version +} + +export function _readLinuxVersionFile(): string { + const lsbReleaseFile = '/etc/lsb-release' + const osReleaseFile = '/etc/os-release' + let contents = '' + + if (fs.existsSync(lsbReleaseFile)) { + contents = fs.readFileSync(lsbReleaseFile).toString() + } else if (fs.existsSync(osReleaseFile)) { + contents = fs.readFileSync(osReleaseFile).toString() + } + + return contents +} diff --git a/actions-toolkit/packages/tool-cache/src/retry-helper.ts b/actions-toolkit/packages/tool-cache/src/retry-helper.ts new file mode 100644 index 00000000..16f8bb7e --- /dev/null +++ b/actions-toolkit/packages/tool-cache/src/retry-helper.ts @@ -0,0 +1,62 @@ +import * as core from '@actions/core' + +/** + * Internal class for retries + */ +export class RetryHelper { + private maxAttempts: number + private minSeconds: number + private maxSeconds: number + + constructor(maxAttempts: number, minSeconds: number, maxSeconds: number) { + if (maxAttempts < 1) { + throw new Error('max attempts should be greater than or equal to 1') + } + + this.maxAttempts = maxAttempts + this.minSeconds = Math.floor(minSeconds) + this.maxSeconds = Math.floor(maxSeconds) + if (this.minSeconds > this.maxSeconds) { + throw new Error('min seconds should be less than or equal to max seconds') + } + } + + async execute( + action: () => Promise, + isRetryable?: (e: Error) => boolean + ): Promise { + let attempt = 1 + while (attempt < this.maxAttempts) { + // Try + try { + return await action() + } catch (err) { + if (isRetryable && !isRetryable(err)) { + throw err + } + + core.info(err.message) + } + + // Sleep + const seconds = this.getSleepAmount() + core.info(`Waiting ${seconds} seconds before trying again`) + await this.sleep(seconds) + attempt++ + } + + // Last attempt + return await action() + } + + private getSleepAmount(): number { + return ( + Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) + + this.minSeconds + ) + } + + private async sleep(seconds: number): Promise { + return new Promise(resolve => setTimeout(resolve, seconds * 1000)) + } +} diff --git a/actions-toolkit/packages/tool-cache/src/tool-cache.ts b/actions-toolkit/packages/tool-cache/src/tool-cache.ts new file mode 100644 index 00000000..694d1252 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/src/tool-cache.ts @@ -0,0 +1,777 @@ +import * as core from '@actions/core' +import * as io from '@actions/io' +import * as fs from 'fs' +import * as mm from './manifest' +import * as os from 'os' +import * as path from 'path' +import * as httpm from '@actions/http-client' +import * as semver from 'semver' +import * as stream from 'stream' +import * as util from 'util' +import {ok} from 'assert' +import {OutgoingHttpHeaders} from 'http' +import uuidV4 from 'uuid/v4' +import {exec} from '@actions/exec/lib/exec' +import {ExecOptions} from '@actions/exec/lib/interfaces' +import {RetryHelper} from './retry-helper' + +export class HTTPError extends Error { + constructor(readonly httpStatusCode: number | undefined) { + super(`Unexpected HTTP response: ${httpStatusCode}`) + Object.setPrototypeOf(this, new.target.prototype) + } +} + +const IS_WINDOWS = process.platform === 'win32' +const IS_MAC = process.platform === 'darwin' +const userAgent = 'actions/tool-cache' + +/** + * Download a tool from an url and stream it into a file + * + * @param url url of tool to download + * @param dest path to download tool + * @param auth authorization header + * @param headers other headers + * @returns path to downloaded tool + */ +export async function downloadTool( + url: string, + dest?: string, + auth?: string, + headers?: OutgoingHttpHeaders +): Promise { + dest = dest || path.join(_getTempDirectory(), uuidV4()) + await io.mkdirP(path.dirname(dest)) + core.debug(`Downloading ${url}`) + core.debug(`Destination ${dest}`) + + const maxAttempts = 3 + const minSeconds = _getGlobal( + 'TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', + 10 + ) + const maxSeconds = _getGlobal( + 'TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', + 20 + ) + const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds) + return await retryHelper.execute( + async () => { + return await downloadToolAttempt(url, dest || '', auth, headers) + }, + (err: Error) => { + if (err instanceof HTTPError && err.httpStatusCode) { + // Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests + if ( + err.httpStatusCode < 500 && + err.httpStatusCode !== 408 && + err.httpStatusCode !== 429 + ) { + return false + } + } + + // Otherwise retry + return true + } + ) +} + +async function downloadToolAttempt( + url: string, + dest: string, + auth?: string, + headers?: OutgoingHttpHeaders +): Promise { + if (fs.existsSync(dest)) { + throw new Error(`Destination file path ${dest} already exists`) + } + + // Get the response headers + const http = new httpm.HttpClient(userAgent, [], { + allowRetries: false + }) + + if (auth) { + core.debug('set auth') + if (headers === undefined) { + headers = {} + } + headers.authorization = auth + } + + const response: httpm.HttpClientResponse = await http.get(url, headers) + if (response.message.statusCode !== 200) { + const err = new HTTPError(response.message.statusCode) + core.debug( + `Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})` + ) + throw err + } + + // Download the response body + const pipeline = util.promisify(stream.pipeline) + const responseMessageFactory = _getGlobal<() => stream.Readable>( + 'TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', + () => response.message + ) + const readStream = responseMessageFactory() + let succeeded = false + try { + await pipeline(readStream, fs.createWriteStream(dest)) + core.debug('download complete') + succeeded = true + return dest + } finally { + // Error, delete dest before retry + if (!succeeded) { + core.debug('download failed') + try { + await io.rmRF(dest) + } catch (err) { + core.debug(`Failed to delete '${dest}'. ${err.message}`) + } + } + } +} + +/** + * Extract a .7z file + * + * @param file path to the .7z file + * @param dest destination directory. Optional. + * @param _7zPath path to 7zr.exe. Optional, for long path support. Most .7z archives do not have this + * problem. If your .7z archive contains very long paths, you can pass the path to 7zr.exe which will + * gracefully handle long paths. By default 7zdec.exe is used because it is a very small program and is + * bundled with the tool lib. However it does not support long paths. 7zr.exe is the reduced command line + * interface, it is smaller than the full command line interface, and it does support long paths. At the + * time of this writing, it is freely available from the LZMA SDK that is available on the 7zip website. + * Be sure to check the current license agreement. If 7zr.exe is bundled with your action, then the path + * to 7zr.exe can be pass to this function. + * @returns path to the destination directory + */ +export async function extract7z( + file: string, + dest?: string, + _7zPath?: string +): Promise { + ok(IS_WINDOWS, 'extract7z() not supported on current OS') + ok(file, 'parameter "file" is required') + + dest = await _createExtractFolder(dest) + + const originalCwd = process.cwd() + process.chdir(dest) + if (_7zPath) { + try { + const logLevel = core.isDebug() ? '-bb1' : '-bb0' + const args: string[] = [ + 'x', // eXtract files with full paths + logLevel, // -bb[0-3] : set output log level + '-bd', // disable progress indicator + '-sccUTF-8', // set charset for for console input/output + file + ] + const options: ExecOptions = { + silent: true + } + await exec(`"${_7zPath}"`, args, options) + } finally { + process.chdir(originalCwd) + } + } else { + const escapedScript = path + .join(__dirname, '..', 'scripts', 'Invoke-7zdec.ps1') + .replace(/'/g, "''") + .replace(/"|\n|\r/g, '') // double-up single quotes, remove double quotes and newlines + const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, '') + const escapedTarget = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '') + const command = `& '${escapedScript}' -Source '${escapedFile}' -Target '${escapedTarget}'` + const args: string[] = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + command + ] + const options: ExecOptions = { + silent: true + } + try { + const powershellPath: string = await io.which('powershell', true) + await exec(`"${powershellPath}"`, args, options) + } finally { + process.chdir(originalCwd) + } + } + + return dest +} + +/** + * Extract a compressed tar archive + * + * @param file path to the tar + * @param dest destination directory. Optional. + * @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional. + * @returns path to the destination directory + */ +export async function extractTar( + file: string, + dest?: string, + flags: string | string[] = 'xz' +): Promise { + if (!file) { + throw new Error("parameter 'file' is required") + } + + // Create dest + dest = await _createExtractFolder(dest) + + // Determine whether GNU tar + core.debug('Checking tar --version') + let versionOutput = '' + await exec('tar --version', [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data: Buffer) => (versionOutput += data.toString()), + stderr: (data: Buffer) => (versionOutput += data.toString()) + } + }) + core.debug(versionOutput.trim()) + const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR') + + // Initialize args + let args: string[] + if (flags instanceof Array) { + args = flags + } else { + args = [flags] + } + + if (core.isDebug() && !flags.includes('v')) { + args.push('-v') + } + + let destArg = dest + let fileArg = file + if (IS_WINDOWS && isGnuTar) { + args.push('--force-local') + destArg = dest.replace(/\\/g, '/') + + // Technically only the dest needs to have `/` but for aesthetic consistency + // convert slashes in the file arg too. + fileArg = file.replace(/\\/g, '/') + } + + if (isGnuTar) { + // Suppress warnings when using GNU tar to extract archives created by BSD tar + args.push('--warning=no-unknown-keyword') + args.push('--overwrite') + } + + args.push('-C', destArg, '-f', fileArg) + await exec(`tar`, args) + + return dest +} + +/** + * Extract a xar compatible archive + * + * @param file path to the archive + * @param dest destination directory. Optional. + * @param flags flags for the xar. Optional. + * @returns path to the destination directory + */ +export async function extractXar( + file: string, + dest?: string, + flags: string | string[] = [] +): Promise { + ok(IS_MAC, 'extractXar() not supported on current OS') + ok(file, 'parameter "file" is required') + + dest = await _createExtractFolder(dest) + + let args: string[] + if (flags instanceof Array) { + args = flags + } else { + args = [flags] + } + + args.push('-x', '-C', dest, '-f', file) + + if (core.isDebug()) { + args.push('-v') + } + + const xarPath: string = await io.which('xar', true) + await exec(`"${xarPath}"`, _unique(args)) + + return dest +} + +/** + * Extract a zip + * + * @param file path to the zip + * @param dest destination directory. Optional. + * @returns path to the destination directory + */ +export async function extractZip(file: string, dest?: string): Promise { + if (!file) { + throw new Error("parameter 'file' is required") + } + + dest = await _createExtractFolder(dest) + + if (IS_WINDOWS) { + await extractZipWin(file, dest) + } else { + await extractZipNix(file, dest) + } + + return dest +} + +async function extractZipWin(file: string, dest: string): Promise { + // build the powershell command + const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, '') // double-up single quotes, remove double quotes and newlines + const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, '') + const pwshPath = await io.which('pwsh', false) + + //To match the file overwrite behavior on nix systems, we use the overwrite = true flag for ExtractToDirectory + //and the -Force flag for Expand-Archive as a fallback + if (pwshPath) { + //attempt to use pwsh with ExtractToDirectory, if this fails attempt Expand-Archive + const pwshCommand = [ + `$ErrorActionPreference = 'Stop' ;`, + `try { Add-Type -AssemblyName System.IO.Compression.ZipFile } catch { } ;`, + `try { [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}', $true) }`, + `catch { if (($_.Exception.GetType().FullName -eq 'System.Management.Automation.MethodException') -or ($_.Exception.GetType().FullName -eq 'System.Management.Automation.RuntimeException') ){ Expand-Archive -LiteralPath '${escapedFile}' -DestinationPath '${escapedDest}' -Force } else { throw $_ } } ;` + ].join(' ') + + const args = [ + '-NoLogo', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + pwshCommand + ] + + core.debug(`Using pwsh at path: ${pwshPath}`) + await exec(`"${pwshPath}"`, args) + } else { + const powershellCommand = [ + `$ErrorActionPreference = 'Stop' ;`, + `try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ;`, + `if ((Get-Command -Name Expand-Archive -Module Microsoft.PowerShell.Archive -ErrorAction Ignore)) { Expand-Archive -LiteralPath '${escapedFile}' -DestinationPath '${escapedDest}' -Force }`, + `else {[System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}', $true) }` + ].join(' ') + + const args = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + powershellCommand + ] + + const powershellPath = await io.which('powershell', true) + core.debug(`Using powershell at path: ${powershellPath}`) + + await exec(`"${powershellPath}"`, args) + } +} + +async function extractZipNix(file: string, dest: string): Promise { + const unzipPath = await io.which('unzip', true) + const args = [file] + if (!core.isDebug()) { + args.unshift('-q') + } + args.unshift('-o') //overwrite with -o, otherwise a prompt is shown which freezes the run + await exec(`"${unzipPath}"`, args, {cwd: dest}) +} + +/** + * Caches a directory and installs it into the tool cacheDir + * + * @param sourceDir the directory to cache into tools + * @param tool tool name + * @param version version of the tool. semver format + * @param arch architecture of the tool. Optional. Defaults to machine architecture + */ +export async function cacheDir( + sourceDir: string, + tool: string, + version: string, + arch?: string +): Promise { + version = semver.clean(version) || version + arch = arch || os.arch() + core.debug(`Caching tool ${tool} ${version} ${arch}`) + + core.debug(`source dir: ${sourceDir}`) + if (!fs.statSync(sourceDir).isDirectory()) { + throw new Error('sourceDir is not a directory') + } + + // Create the tool dir + const destPath: string = await _createToolPath(tool, version, arch) + // copy each child item. do not move. move can fail on Windows + // due to anti-virus software having an open handle on a file. + for (const itemName of fs.readdirSync(sourceDir)) { + const s = path.join(sourceDir, itemName) + await io.cp(s, destPath, {recursive: true}) + } + + // write .complete + _completeToolPath(tool, version, arch) + + return destPath +} + +/** + * Caches a downloaded file (GUID) and installs it + * into the tool cache with a given targetName + * + * @param sourceFile the file to cache into tools. Typically a result of downloadTool which is a guid. + * @param targetFile the name of the file name in the tools directory + * @param tool tool name + * @param version version of the tool. semver format + * @param arch architecture of the tool. Optional. Defaults to machine architecture + */ +export async function cacheFile( + sourceFile: string, + targetFile: string, + tool: string, + version: string, + arch?: string +): Promise { + version = semver.clean(version) || version + arch = arch || os.arch() + core.debug(`Caching tool ${tool} ${version} ${arch}`) + + core.debug(`source file: ${sourceFile}`) + if (!fs.statSync(sourceFile).isFile()) { + throw new Error('sourceFile is not a file') + } + + // create the tool dir + const destFolder: string = await _createToolPath(tool, version, arch) + + // copy instead of move. move can fail on Windows due to + // anti-virus software having an open handle on a file. + const destPath: string = path.join(destFolder, targetFile) + core.debug(`destination file ${destPath}`) + await io.cp(sourceFile, destPath) + + // write .complete + _completeToolPath(tool, version, arch) + + return destFolder +} + +/** + * Finds the path to a tool version in the local installed tool cache + * + * @param toolName name of the tool + * @param versionSpec version of the tool + * @param arch optional arch. defaults to arch of computer + */ +export function find( + toolName: string, + versionSpec: string, + arch?: string +): string { + if (!toolName) { + throw new Error('toolName parameter is required') + } + + if (!versionSpec) { + throw new Error('versionSpec parameter is required') + } + + arch = arch || os.arch() + + // attempt to resolve an explicit version + if (!isExplicitVersion(versionSpec)) { + const localVersions: string[] = findAllVersions(toolName, arch) + const match = evaluateVersions(localVersions, versionSpec) + versionSpec = match + } + + // check for the explicit version in the cache + let toolPath = '' + if (versionSpec) { + versionSpec = semver.clean(versionSpec) || '' + const cachePath = path.join( + _getCacheDirectory(), + toolName, + versionSpec, + arch + ) + core.debug(`checking cache: ${cachePath}`) + if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) { + core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`) + toolPath = cachePath + } else { + core.debug('not found') + } + } + return toolPath +} + +/** + * Finds the paths to all versions of a tool that are installed in the local tool cache + * + * @param toolName name of the tool + * @param arch optional arch. defaults to arch of computer + */ +export function findAllVersions(toolName: string, arch?: string): string[] { + const versions: string[] = [] + + arch = arch || os.arch() + const toolPath = path.join(_getCacheDirectory(), toolName) + + if (fs.existsSync(toolPath)) { + const children: string[] = fs.readdirSync(toolPath) + for (const child of children) { + if (isExplicitVersion(child)) { + const fullPath = path.join(toolPath, child, arch || '') + if (fs.existsSync(fullPath) && fs.existsSync(`${fullPath}.complete`)) { + versions.push(child) + } + } + } + } + + return versions +} + +// versions-manifest +// +// typical pattern of a setup-* action that supports JIT would be: +// 1. resolve semver against local cache +// +// 2. if no match, download +// a. query versions manifest to match +// b. if no match, fall back to source if exists (tool distribution) +// c. with download url, download, install and preprent path + +export type IToolRelease = mm.IToolRelease +export type IToolReleaseFile = mm.IToolReleaseFile + +interface GitHubTreeItem { + path: string + size: string + url: string +} + +interface GitHubTree { + tree: GitHubTreeItem[] + truncated: boolean +} + +export async function getManifestFromRepo( + owner: string, + repo: string, + auth?: string, + branch = 'master' +): Promise { + let releases: IToolRelease[] = [] + const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}` + + const http: httpm.HttpClient = new httpm.HttpClient('tool-cache') + const headers: OutgoingHttpHeaders = {} + if (auth) { + core.debug('set auth') + headers.authorization = auth + } + + const response = await http.getJson(treeUrl, headers) + if (!response.result) { + return releases + } + + let manifestUrl = '' + for (const item of response.result.tree) { + if (item.path === 'versions-manifest.json') { + manifestUrl = item.url + break + } + } + + headers['accept'] = 'application/vnd.github.VERSION.raw' + let versionsRaw = await (await http.get(manifestUrl, headers)).readBody() + + if (versionsRaw) { + // shouldn't be needed but protects against invalid json saved with BOM + versionsRaw = versionsRaw.replace(/^\uFEFF/, '') + try { + releases = JSON.parse(versionsRaw) + } catch { + core.debug('Invalid json') + } + } + + return releases +} + +export async function findFromManifest( + versionSpec: string, + stable: boolean, + manifest: IToolRelease[], + archFilter: string = os.arch() +): Promise { + // wrap the internal impl + const match: mm.IToolRelease | undefined = await mm._findMatch( + versionSpec, + stable, + manifest, + archFilter + ) + + return match +} + +async function _createExtractFolder(dest?: string): Promise { + if (!dest) { + // create a temp dir + dest = path.join(_getTempDirectory(), uuidV4()) + } + await io.mkdirP(dest) + return dest +} + +async function _createToolPath( + tool: string, + version: string, + arch?: string +): Promise { + const folderPath = path.join( + _getCacheDirectory(), + tool, + semver.clean(version) || version, + arch || '' + ) + core.debug(`destination ${folderPath}`) + const markerPath = `${folderPath}.complete` + await io.rmRF(folderPath) + await io.rmRF(markerPath) + await io.mkdirP(folderPath) + return folderPath +} + +function _completeToolPath(tool: string, version: string, arch?: string): void { + const folderPath = path.join( + _getCacheDirectory(), + tool, + semver.clean(version) || version, + arch || '' + ) + const markerPath = `${folderPath}.complete` + fs.writeFileSync(markerPath, '') + core.debug('finished caching tool') +} + +/** + * Check if version string is explicit + * + * @param versionSpec version string to check + */ +export function isExplicitVersion(versionSpec: string): boolean { + const c = semver.clean(versionSpec) || '' + core.debug(`isExplicit: ${c}`) + + const valid = semver.valid(c) != null + core.debug(`explicit? ${valid}`) + + return valid +} + +/** + * Get the highest satisfiying semantic version in `versions` which satisfies `versionSpec` + * + * @param versions array of versions to evaluate + * @param versionSpec semantic version spec to satisfy + */ + +export function evaluateVersions( + versions: string[], + versionSpec: string +): string { + let version = '' + core.debug(`evaluating ${versions.length} versions`) + versions = versions.sort((a, b) => { + if (semver.gt(a, b)) { + return 1 + } + return -1 + }) + for (let i = versions.length - 1; i >= 0; i--) { + const potential: string = versions[i] + const satisfied: boolean = semver.satisfies(potential, versionSpec) + if (satisfied) { + version = potential + break + } + } + + if (version) { + core.debug(`matched: ${version}`) + } else { + core.debug('match not found') + } + + return version +} + +/** + * Gets RUNNER_TOOL_CACHE + */ +function _getCacheDirectory(): string { + const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || '' + ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined') + return cacheDirectory +} + +/** + * Gets RUNNER_TEMP + */ +function _getTempDirectory(): string { + const tempDirectory = process.env['RUNNER_TEMP'] || '' + ok(tempDirectory, 'Expected RUNNER_TEMP to be defined') + return tempDirectory +} + +/** + * Gets a global variable + */ +function _getGlobal(key: string, defaultValue: T): T { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const value = (global as any)[key] as T | undefined + /* eslint-enable @typescript-eslint/no-explicit-any */ + return value !== undefined ? value : defaultValue +} + +/** + * Returns an array of unique values. + * @param values Values to make unique. + */ +function _unique(values: T[]): T[] { + return Array.from(new Set(values)) +} diff --git a/actions-toolkit/packages/tool-cache/tsconfig.json b/actions-toolkit/packages/tool-cache/tsconfig.json new file mode 100644 index 00000000..a8b812a6 --- /dev/null +++ b/actions-toolkit/packages/tool-cache/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file diff --git a/actions-toolkit/res/at-logo.png b/actions-toolkit/res/at-logo.png new file mode 100644 index 00000000..872d1375 Binary files /dev/null and b/actions-toolkit/res/at-logo.png differ diff --git a/actions-toolkit/scripts/audit-allow-list b/actions-toolkit/scripts/audit-allow-list new file mode 100755 index 00000000..7ff67226 --- /dev/null +++ b/actions-toolkit/scripts/audit-allow-list @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +// NEEDS TO BE UPDATED TO WORK ON NODE 16 BECAUSE NPM AUDIT --JSON OUTPUT CHANGED +// THE AUDIT WORKFLOW IS ONLY RUN ON PRS, BUT IT CAN BE IGNORED AND A RELEASE CAN BE CREATED NONETHELESS +// @fhammerl @rentziass + +/* +This script takes the output of npm audit --json from stdin +and writes a filtered version to stdout. +The filtered version will have the entries listed in `AUDIT_ALLOW_LIST` removed. +Specifically, each property of `vulnerabilities` in the input is matched by name in the allow list. + +Sample output of `npm audit --json` (NPM v6): + +{ + "actions": [ + { + "action": "review", + "module": "trim-newlines", + "resolves": [ + { + "id": 1753, + "path": "lerna>@lerna/publish>@lerna/version>@lerna/conventional-commits>conventional-changelog-core>get-pkg-repo>meow>trim-newlines", + "dev": true, + "optional": false, + "bundled": false + } + ] + } + ], + // Other properties ... +} + + +The reason we have this script is that there may be low-severity or unexploitable vulnerabilities +that have not yet been fixed in newer versions of the package. + +Note: if we update to NPM v7, we will have to change this script because the `npm audit` output will be different. +See commit 935647112d96fa5cf82e61314f7135376d24f291 in https://github.com/actions/toolkit/pull/846. +*/ + +'use strict' +const fs = require('fs') + +const USAGE = "Usage: npm audit --json | scripts/audit-allow-list" + +// To add entires to the allow list: +// - Run `npm audit --json` +// - Copy `path` from each `actions[k].resolves` you want to allow +// - Fill in the `advisoryUrl` and `justification` (these are just for documentation) +const AUDIT_ALLOW_LIST = [ + { + path: "lerna>@lerna/publish>@lerna/version>@lerna/conventional-commits>conventional-changelog-core>get-pkg-repo>meow>trim-newlines", + advisoryUrl: "https://www.npmjs.com/advisories/1753", + justification: "dependency of lerna (dev only); low severity" + }, + { + path: "lerna>@lerna/version>@lerna/conventional-commits>conventional-changelog-core>get-pkg-repo>meow>trim-newlines", + advisoryUrl: "https://www.npmjs.com/advisories/1753", + justification: "dependency of lerna (dev only); low severity" + }, + { + path: "lerna>@lerna/version>@lerna/github-client>git-url-parse>git-up>parse-url", + advisoryUrl: "https://github.com/advisories/GHSA-j9fq-vwqv-2fm2", + justification: "dependency of lerna; moderate severity" + }, + { + path: "lerna>@lerna/publish>@lerna/version>@lerna/github-client>git-url-parse>git-up>parse-url", + advisoryUrl: "https://github.com/advisories/GHSA-j9fq-vwqv-2fm2", + justification: "dependency of lerna; moderate severity" + } +] + +/** + * @param audits - JavaScript object matching the schema of `npm audit --json` + * @param allowedPaths - List of dependency paths to exclude from the audit +*/ +function filterVulnerabilities(audits, allowedPaths) { + const vulnerabilities = audits.actions.flatMap(x => x.resolves) + return vulnerabilities.filter(x => !allowedPaths.includes(x.path)) +} + +const input = fs.readFileSync("/dev/stdin", "utf-8") +if (input === "") { + console.error(USAGE) + process.exit(1) +} + +const audits = JSON.parse(input) +const allowedPaths = AUDIT_ALLOW_LIST.map(x => x.path) +// This function assumes `audits` has the right structure. +// Just let the error terminate the process if the input doesn't match the schema. +const remainingVulnerabilities = filterVulnerabilities(audits, allowedPaths) + +// `npm audit` will return exit code 1 if it finds vulnerabilities. +// This script should do the same. +const numVulnerabilities = remainingVulnerabilities.length +if (numVulnerabilities > 0) { + const pluralized = numVulnerabilities === 1 ? "y" : "ies" + console.log(`Found ${numVulnerabilities} unrecognized vulnerabilit${pluralized} from \`npm audit\`:`) + console.log(JSON.stringify(remainingVulnerabilities, null, 2)) + process.exit(1) +} diff --git a/actions-toolkit/scripts/create-package b/actions-toolkit/scripts/create-package new file mode 100755 index 00000000..29d07feb --- /dev/null +++ b/actions-toolkit/scripts/create-package @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -eo pipefail + +name=$1 + +if [[ -z "$name" ]]; then + >&2 echo "Usage: npm run new-package [name]" + exit 1 +fi + +npx lerna create @actions/$name +cp packages/core/tsconfig.json packages/$name/tsconfig.json \ No newline at end of file diff --git a/actions-toolkit/tsconfig.eslint.json b/actions-toolkit/tsconfig.eslint.json new file mode 100644 index 00000000..d18952e8 --- /dev/null +++ b/actions-toolkit/tsconfig.eslint.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/actions-toolkit/tsconfig.json b/actions-toolkit/tsconfig.json new file mode 100644 index 00000000..7d74f05f --- /dev/null +++ b/actions-toolkit/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "module": "commonjs", + "strict": true, + "declaration": true, + "target": "es6", + "sourceMap": true, + "noImplicitAny": false, + "baseUrl": "./", + "paths": { + "@actions/core": [ + "packages/core" + ], + "@actions/http-client": [ + "packages/http-client" + ], + }, + "useUnknownInCatchVariables": false + }, + "exclude": [ + "node_modules", + "**/*.test.ts", + "**/__mocks__/*" + ] +} \ No newline at end of file diff --git a/cache_nix_action/translate_table.py b/cache_nix_action/translate_table.py new file mode 100644 index 00000000..dad7225d --- /dev/null +++ b/cache_nix_action/translate_table.py @@ -0,0 +1,98 @@ +import sys +import yaml + +from lxml.etree import tostring, fromstring +from lxml.builder import E +import markdown +from pathlib import Path + + +def convert_to_table(attrs, is_inputs=True): + columns = ["name", "description"] + + if is_inputs: + columns += ["default", "required"] + + root = E.table( + E.tr(*[E.th(c) for c in columns]), + ) + + for key, value in attrs.items(): + key_pretty = f"`{key}`" + description = value.get("description", "") + columns = [key_pretty, description] + + if is_inputs: + required = ( + "`true`" + if (required := value.get("required", "`false`")) == True + else required + ) + + default = ( + f"`{default}`" + if (default := value.get("default")) + else "`''`" + if default == "" + else "" + ) + + columns += [ + default, + required, + ] + + root.append( + E.tr( + *[ + E.td(fromstring(f"
{markdown.markdown(x)}
")) + for x in columns + ] + ) + ) + + return ( + tostring( + root, + encoding="UTF-16", + xml_declaration=False, + ) + .decode("UTF-16") + .replace("\n", "") + ) + + +def main(): + actions = [ + ["save/action.yml", False], + ["restore/action.yml", True], + ["action.yml", True], + ] + + for path, has_outputs in actions: + dir = Path(path).parent + readme_md = dir / "README.md" + with open(path, "r") as action: + action_yaml = yaml.safe_load(action.read()) + + with open(readme_md, "r") as readme: + readme_lines = readme.readlines() + + for i, line in enumerate(readme_lines): + for attr, heading, is_inputs in [ + ["inputs", "Inputs", True], + ["outputs", "Outputs", False], + ]: + if line.find(f"### {heading}") != -1 and not ( + heading == "Outputs" and not has_outputs + ): + readme_lines[i + 2] = convert_to_table( + action_yaml.get(attr), is_inputs=is_inputs + ) + "\n" + + with open(readme_md, "w") as readme: + readme.writelines(readme_lines) + + +if __name__ == "__main__": + main() diff --git a/dist/restore-only/index.js b/dist/restore-only/index.js index bdbb1a09..dd8331d8 100644 --- a/dist/restore-only/index.js +++ b/dist/restore-only/index.js @@ -1,1815 +1,886 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 7799: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +/***/ 1898: +/***/ ((module, exports) => { -"use strict"; +exports = module.exports = SemVer -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const cacheHttpClient = __importStar(__nccwpck_require__(8245)); -const tar_1 = __nccwpck_require__(6490); -class ValidationError extends Error { - constructor(message) { - super(message); - this.name = 'ValidationError'; - Object.setPrototypeOf(this, ValidationError.prototype); - } -} -exports.ValidationError = ValidationError; -class ReserveCacheError extends Error { - constructor(message) { - super(message); - this.name = 'ReserveCacheError'; - Object.setPrototypeOf(this, ReserveCacheError.prototype); - } -} -exports.ReserveCacheError = ReserveCacheError; -function checkPaths(paths) { - if (!paths || paths.length === 0) { - throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); - } -} -function checkKey(key) { - if (key.length > 512) { - throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); - } - const regex = /^[^,]*$/; - if (!regex.test(key)) { - throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); - } -} -/** - * isFeatureAvailable to check the presence of Actions cache service - * - * @returns boolean return true if Actions cache service feature is available, otherwise false - */ -function isFeatureAvailable() { - return !!process.env['ACTIONS_CACHE_URL']; -} -exports.isFeatureAvailable = isFeatureAvailable; -/** - * Restores cache from keys - * - * @param paths a list of file paths to restore from the cache - * @param primaryKey an explicit key for restoring the cache - * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key - * @param downloadOptions cache download options - * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform - * @returns string returns the key for the cache hit, otherwise returns undefined - */ -function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - restoreKeys = restoreKeys || []; - const keys = [primaryKey, ...restoreKeys]; - core.debug('Resolved Keys:'); - core.debug(JSON.stringify(keys)); - if (keys.length > 10) { - throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); - } - for (const key of keys) { - checkKey(key); - } - const compressionMethod = yield utils.getCompressionMethod(); - let archivePath = ''; - try { - // path are needed to compute version - const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { - compressionMethod, - enableCrossOsArchive - }); - if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { - // Cache not found - return undefined; - } - if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core.info('Lookup only - skipping download'); - return cacheEntry.cacheKey; - } - archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - // Download the cache from the cache entry - yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core.info('Cache restored successfully'); - return cacheEntry.cacheKey; - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else { - // Supress all non-validation cache related errors because caching should be optional - core.warning(`Failed to restore: ${error.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return undefined; - }); -} -exports.restoreCache = restoreCache; -/** - * Saves a list of files with the specified key - * - * @param paths a list of file paths to be cached - * @param key an explicit key for restoring the cache - * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform - * @param options cache upload options - * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails - */ -function saveCache(paths, key, options, enableCrossOsArchive = false) { - var _a, _b, _c, _d, _e; - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - checkKey(key); - const compressionMethod = yield utils.getCompressionMethod(); - let cacheId = -1; - const cachePaths = yield utils.resolvePaths(paths); - core.debug('Cache Paths:'); - core.debug(`${JSON.stringify(cachePaths)}`); - if (cachePaths.length === 0) { - throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); - } - const archiveFolder = yield utils.createTempDirectory(); - const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - try { - yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.debug(`File Size: ${archiveFileSize}`); - // For GHES, this check will take place in ReserveCache API with enterprise file size limit - if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { - throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); - } - core.debug('Reserving Cache'); - const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { - compressionMethod, - enableCrossOsArchive, - cacheSize: archiveFileSize - }); - if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { - cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; - } - else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { - throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); - } - else { - throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); - } - core.debug(`Saving Cache (ID: ${cacheId})`); - yield cacheHttpClient.saveCache(cacheId, archivePath, options); - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else if (typedError.name === ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); - } - else { - core.warning(`Failed to save: ${typedError.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return cacheId; - }); +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} } -exports.saveCache = saveCache; -//# sourceMappingURL=cache.js.map -/***/ }), +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' -/***/ 8245: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 -"use strict"; +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const auth_1 = __nccwpck_require__(5526); -const crypto = __importStar(__nccwpck_require__(6113)); -const fs = __importStar(__nccwpck_require__(7147)); -const url_1 = __nccwpck_require__(7310); -const utils = __importStar(__nccwpck_require__(1518)); -const downloadUtils_1 = __nccwpck_require__(5500); -const options_1 = __nccwpck_require__(6215); -const requestUtils_1 = __nccwpck_require__(3981); -const versionSalt = '1.0'; -function getCacheApiUrl(resource) { - const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; - if (!baseUrl) { - throw new Error('Cache Service Url not found, unable to restore cache.'); - } - const url = `${baseUrl}_apis/artifactcache/${resource}`; - core.debug(`Resource Url: ${url}`); - return url; -} -function createAcceptHeader(type, apiVersion) { - return `${type};api-version=${apiVersion}`; -} -function getRequestOptions() { - const requestOptions = { - headers: { - Accept: createAcceptHeader('application/json', '6.0-preview.1') - } - }; - return requestOptions; -} -function createHttpClient() { - const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; - const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); - return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); -} -function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { - const components = paths; - // Add compression method to cache version to restore - // compressed cache as per compression method - if (compressionMethod) { - components.push(compressionMethod); - } - // Only check for windows platforms if enableCrossOsArchive is false - if (process.platform === 'win32' && !enableCrossOsArchive) { - components.push('windows-only'); - } - // Add salt to cache version to support breaking changes in cache entry - components.push(versionSalt); - return crypto - .createHash('sha256') - .update(components.join('|')) - .digest('hex'); -} -exports.getCacheVersion = getCacheVersion; -function getCacheEntry(keys, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - // Cache not found - if (response.statusCode === 204) { - // List cache for primary key only if cache miss occurs - if (core.isDebug()) { - yield printCachesListForDiagnostics(keys[0], httpClient, version); - } - return null; - } - if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { - throw new Error(`Cache service responded with ${response.statusCode}`); - } - const cacheResult = response.result; - const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; - if (!cacheDownloadUrl) { - // Cache achiveLocation not found. This should never happen, and hence bail out. - throw new Error('Cache not found.'); - } - core.setSecret(cacheDownloadUrl); - core.debug(`Cache Result:`); - core.debug(JSON.stringify(cacheResult)); - return cacheResult; - }); -} -exports.getCacheEntry = getCacheEntry; -function printCachesListForDiagnostics(key, httpClient, version) { - return __awaiter(this, void 0, void 0, function* () { - const resource = `caches?key=${encodeURIComponent(key)}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - if (response.statusCode === 200) { - const cacheListResult = response.result; - const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; - if (totalCount && totalCount > 0) { - core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); - for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); - } - } - } - }); -} -function downloadCache(archiveLocation, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const archiveUrl = new url_1.URL(archiveLocation); - const downloadOptions = (0, options_1.getDownloadOptions)(options); - if (downloadOptions.useAzureSdk && - archiveUrl.hostname.endsWith('.blob.core.windows.net')) { - // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. - yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); - } - else { - // Otherwise, download using the Actions http-client. - yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); - } - }); -} -exports.downloadCache = downloadCache; -// Reserve Cache -function reserveCache(key, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const reserveCacheRequest = { - key, - version, - cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize - }; - const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); - })); - return response; - }); -} -exports.reserveCache = reserveCache; -function getContentRange(start, end) { - // Format: `bytes start-end/filesize - // start and end are inclusive - // filesize can be * - // For a 200 byte chunk starting at byte 0: - // Content-Range: bytes 0-199/* - return `bytes ${start}-${end}/*`; -} -function uploadChunk(httpClient, resourceUrl, openStream, start, end) { - return __awaiter(this, void 0, void 0, function* () { - core.debug(`Uploading chunk of size ${end - - start + - 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); - const additionalHeaders = { - 'Content-Type': 'application/octet-stream', - 'Content-Range': getContentRange(start, end) - }; - const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { - return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); - })); - if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { - throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); - } - }); -} -function uploadFile(httpClient, cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - // Upload Chunks - const fileSize = utils.getArchiveFileSizeInBytes(archivePath); - const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs.openSync(archivePath, 'r'); - const uploadOptions = (0, options_1.getUploadOptions)(options); - const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); - const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); - const parallelUploads = [...new Array(concurrency).keys()]; - core.debug('Awaiting all uploads'); - let offset = 0; - try { - yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { - while (offset < fileSize) { - const chunkSize = Math.min(fileSize - offset, maxChunkSize); - const start = offset; - const end = offset + chunkSize - 1; - offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs - .createReadStream(archivePath, { - fd, - start, - end, - autoClose: false - }) - .on('error', error => { - throw new Error(`Cache upload failed because file read failed with ${error.message}`); - }), start, end); - } - }))); - } - finally { - fs.closeSync(fd); - } - return; - }); -} -function commitCache(httpClient, cacheId, filesize) { - return __awaiter(this, void 0, void 0, function* () { - const commitCacheRequest = { size: filesize }; - return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); - })); - }); -} -function saveCache(cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - core.debug('Upload cache'); - yield uploadFile(httpClient, cacheId, archivePath, options); - // Commit Cache - core.debug('Commiting cache'); - const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); - const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); - if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { - throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); - } - core.info('Cache saved successfully'); - }); +var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + +// The actual regexps go on exports.re +var re = exports.re = [] +var safeRe = exports.safeRe = [] +var src = exports.src = [] +var t = exports.tokens = {} +var R = 0 + +function tok (n) { + t[n] = R++ } -exports.saveCache = saveCache; -//# sourceMappingURL=cacheHttpClient.js.map -/***/ }), +var LETTERDASHNUMBER = '[a-zA-Z0-9-]' -/***/ 1518: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +var safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] -"use strict"; +function makeSafeRe (value) { + for (var i = 0; i < safeRegexReplacements.length; i++) { + var token = safeRegexReplacements[i][0] + var max = safeRegexReplacements[i][1] + value = value + .split(token + '*').join(token + '{0,' + max + '}') + .split(token + '+').join(token + '{1,' + max + '}') + } + return value +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const exec = __importStar(__nccwpck_require__(1514)); -const glob = __importStar(__nccwpck_require__(8090)); -const io = __importStar(__nccwpck_require__(7436)); -const fs = __importStar(__nccwpck_require__(7147)); -const path = __importStar(__nccwpck_require__(1017)); -const semver = __importStar(__nccwpck_require__(3771)); -const util = __importStar(__nccwpck_require__(3837)); -const uuid_1 = __nccwpck_require__(2155); -const constants_1 = __nccwpck_require__(8840); -// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 -function createTempDirectory() { - return __awaiter(this, void 0, void 0, function* () { - const IS_WINDOWS = process.platform === 'win32'; - let tempDirectory = process.env['RUNNER_TEMP'] || ''; - if (!tempDirectory) { - let baseLocation; - if (IS_WINDOWS) { - // On Windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } - else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } - else { - baseLocation = '/home'; - } - } - tempDirectory = path.join(baseLocation, 'actions', 'temp'); - } - const dest = path.join(tempDirectory, (0, uuid_1.v4)()); - yield io.mkdirP(dest); - return dest; - }); -} -exports.createTempDirectory = createTempDirectory; -function getArchiveFileSizeInBytes(filePath) { - return fs.statSync(filePath).size; -} -exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; -function resolvePaths(patterns) { - var e_1, _a; - var _b; - return __awaiter(this, void 0, void 0, function* () { - const paths = []; - const workspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); - const globber = yield glob.create(patterns.join('\n'), { - implicitDescendants: false - }); - try { - for (var _c = __asyncValues(globber.globGenerator()), _d; _d = yield _c.next(), !_d.done;) { - const file = _d.value; - const relativeFile = path - .relative(workspace, file) - .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); - core.debug(`Matched: ${relativeFile}`); - // Paths are made relative so the tar entries are all relative to the root of the workspace. - if (relativeFile === '') { - // path.relative returns empty string if workspace and file are equal - paths.push('.'); - } - else { - paths.push(`${relativeFile}`); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } - return paths; - }); -} -exports.resolvePaths = resolvePaths; -function unlinkFile(filePath) { - return __awaiter(this, void 0, void 0, function* () { - return util.promisify(fs.unlink)(filePath); - }); -} -exports.unlinkFile = unlinkFile; -function getVersion(app, additionalArgs = []) { - return __awaiter(this, void 0, void 0, function* () { - let versionOutput = ''; - additionalArgs.push('--version'); - core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); - try { - yield exec.exec(`${app}`, additionalArgs, { - ignoreReturnCode: true, - silent: true, - listeners: { - stdout: (data) => (versionOutput += data.toString()), - stderr: (data) => (versionOutput += data.toString()) - } - }); - } - catch (err) { - core.debug(err.message); - } - versionOutput = versionOutput.trim(); - core.debug(versionOutput); - return versionOutput; - }); +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +tok('NUMERICIDENTIFIER') +src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' +tok('NUMERICIDENTIFIERLOOSE') +src[t.NUMERICIDENTIFIERLOOSE] = '\\d+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +tok('NONNUMERICIDENTIFIER') +src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*' + +// ## Main Version +// Three dot-separated numeric identifiers. + +tok('MAINVERSION') +src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')' + +tok('MAINVERSIONLOOSE') +src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +tok('PRERELEASEIDENTIFIER') +src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +tok('PRERELEASEIDENTIFIERLOOSE') +src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +tok('PRERELEASE') +src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' + +tok('PRERELEASELOOSE') +src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +tok('BUILDIDENTIFIER') +src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+' + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +tok('BUILD') +src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + + '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +tok('FULL') +tok('FULLPLAIN') +src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + + src[t.PRERELEASE] + '?' + + src[t.BUILD] + '?' + +src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +tok('LOOSEPLAIN') +src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + + src[t.PRERELEASELOOSE] + '?' + + src[t.BUILD] + '?' + +tok('LOOSE') +src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + +tok('GTLT') +src[t.GTLT] = '((?:<|>)?=?)' + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +tok('XRANGEIDENTIFIERLOOSE') +src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +tok('XRANGEIDENTIFIER') +src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + +tok('XRANGEPLAIN') +src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:' + src[t.PRERELEASE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGEPLAINLOOSE') +src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[t.PRERELEASELOOSE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGE') +src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' +tok('XRANGELOOSE') +src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +tok('COERCE') +src[t.COERCE] = '(^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' +tok('COERCERTL') +re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') +safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g') + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +tok('LONETILDE') +src[t.LONETILDE] = '(?:~>?)' + +tok('TILDETRIM') +src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' +re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') +safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g') +var tildeTrimReplace = '$1~' + +tok('TILDE') +src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' +tok('TILDELOOSE') +src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +tok('LONECARET') +src[t.LONECARET] = '(?:\\^)' + +tok('CARETTRIM') +src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' +re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') +safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g') +var caretTrimReplace = '$1^' + +tok('CARET') +src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' +tok('CARETLOOSE') +src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +tok('COMPARATORLOOSE') +src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' +tok('COMPARATOR') +src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +tok('COMPARATORTRIM') +src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + + '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' + +// this one has to use the /g flag +re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') +safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +tok('HYPHENRANGE') +src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAIN] + ')' + + '\\s*$' + +tok('HYPHENRANGELOOSE') +src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s*$' + +// Star ranges basically just allow anything at all. +tok('STAR') +src[t.STAR] = '(<|>)?=?\\s*\\*' + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + safeRe[i] = new RegExp(makeSafeRe(src[i])) + } } -// Use zstandard if possible to maximize cache performance -function getCompressionMethod() { - return __awaiter(this, void 0, void 0, function* () { - const versionOutput = yield getVersion('zstd', ['--quiet']); - const version = semver.clean(versionOutput); - core.debug(`zstd version: ${version}`); - if (versionOutput === '') { - return constants_1.CompressionMethod.Gzip; - } - else { - return constants_1.CompressionMethod.ZstdWithoutLong; - } - }); + +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } } -exports.getCompressionMethod = getCompressionMethod; -function getCacheFileName(compressionMethod) { - return compressionMethod === constants_1.CompressionMethod.Gzip - ? constants_1.CacheFilename.Gzip - : constants_1.CacheFilename.Zstd; + +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null } -exports.getCacheFileName = getCacheFileName; -function getGnuTarPathOnWindows() { - return __awaiter(this, void 0, void 0, function* () { - if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { - return constants_1.GnuTarPathOnWindows; - } - const versionOutput = yield getVersion('tar'); - return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; - }); + +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null } -exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; -function assertDefined(name, value) { - if (value === undefined) { - throw Error(`Expected ${name} but value was undefiend`); + +exports.SemVer = SemVer + +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } - return value; -} -exports.assertDefined = assertDefined; -function isGhes() { - const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; -} -exports.isGhes = isGhes; -//# sourceMappingURL=cacheUtils.js.map + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } -/***/ }), + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } -/***/ 8840: -/***/ ((__unused_webpack_module, exports) => { + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } -"use strict"; + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; -var CacheFilename; -(function (CacheFilename) { - CacheFilename["Gzip"] = "cache.tgz"; - CacheFilename["Zstd"] = "cache.tzst"; -})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); -var CompressionMethod; -(function (CompressionMethod) { - CompressionMethod["Gzip"] = "gzip"; - // Long range mode was added to zstd in v1.3.2. - // This enum is for earlier version of zstd that does not have --long support - CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; - CompressionMethod["Zstd"] = "zstd"; -})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); -var ArchiveToolType; -(function (ArchiveToolType) { - ArchiveToolType["GNU"] = "gnu"; - ArchiveToolType["BSD"] = "bsd"; -})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); -// The default number of retry attempts. -exports.DefaultRetryAttempts = 2; -// The default delay in milliseconds between retry attempts. -exports.DefaultRetryDelay = 5000; -// Socket timeout in milliseconds during download. If no traffic is received -// over the socket during this period, the socket is destroyed and the download -// is aborted. -exports.SocketTimeout = 5000; -// The default path of GNUtar on hosted Windows runners -exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; -// The default path of BSDtar on hosted Windows runners -exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; -exports.TarFilename = 'cache.tar'; -exports.ManifestFilename = 'manifest.txt'; -//# sourceMappingURL=constants.js.map + var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]) -/***/ }), + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } -/***/ 5500: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + this.raw = version -"use strict"; + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.downloadCacheStorageSDK = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const storage_blob_1 = __nccwpck_require__(4100); -const buffer = __importStar(__nccwpck_require__(4300)); -const fs = __importStar(__nccwpck_require__(7147)); -const stream = __importStar(__nccwpck_require__(2781)); -const util = __importStar(__nccwpck_require__(3837)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const requestUtils_1 = __nccwpck_require__(3981); -const abort_controller_1 = __nccwpck_require__(2557); -/** - * Pipes the body of a HTTP response to a stream - * - * @param response the HTTP response - * @param output the writable stream - */ -function pipeResponseToStream(response, output) { - return __awaiter(this, void 0, void 0, function* () { - const pipeline = util.promisify(stream.pipeline); - yield pipeline(response.message, output); - }); -} -/** - * Class for tracking the download state and displaying stats. - */ -class DownloadProgress { - constructor(contentLength) { - this.contentLength = contentLength; - this.segmentIndex = 0; - this.segmentSize = 0; - this.segmentOffset = 0; - this.receivedBytes = 0; - this.displayedComplete = false; - this.startTime = Date.now(); - } - /** - * Progress to the next segment. Only call this method when the previous segment - * is complete. - * - * @param segmentSize the length of the next segment - */ - nextSegment(segmentSize) { - this.segmentOffset = this.segmentOffset + this.segmentSize; - this.segmentIndex = this.segmentIndex + 1; - this.segmentSize = segmentSize; - this.receivedBytes = 0; - core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); - } - /** - * Sets the number of bytes received for the current segment. - * - * @param receivedBytes the number of bytes received - */ - setReceivedBytes(receivedBytes) { - this.receivedBytes = receivedBytes; - } - /** - * Returns the total number of bytes transferred. - */ - getTransferredBytes() { - return this.segmentOffset + this.receivedBytes; - } - /** - * Returns true if the download is complete. - */ - isDone() { - return this.getTransferredBytes() === this.contentLength; - } - /** - * Prints the current download stats. Once the download completes, this will print one - * last line and then stop. - */ - display() { - if (this.displayedComplete) { - return; - } - const transferredBytes = this.segmentOffset + this.receivedBytes; - const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); - const elapsedTime = Date.now() - this.startTime; - const downloadSpeed = (transferredBytes / - (1024 * 1024) / - (elapsedTime / 1000)).toFixed(1); - core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); - if (this.isDone()) { - this.displayedComplete = true; - } - } - /** - * Returns a function used to handle TransferProgressEvents. - */ - onProgress() { - return (progress) => { - this.setReceivedBytes(progress.loadedBytes); - }; - } - /** - * Starts the timer that displays the stats. - * - * @param delayInMs the delay between each write - */ - startDisplayTimer(delayInMs = 1000) { - const displayCallback = () => { - this.display(); - if (!this.isDone()) { - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - }; - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - /** - * Stops the timer that displays the stats. As this typically indicates the download - * is complete, this will display one last line, unless the last line has already - * been written. - */ - stopDisplayTimer() { - if (this.timeoutHandle) { - clearTimeout(this.timeoutHandle); - this.timeoutHandle = undefined; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num } - this.display(); - } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() } -exports.DownloadProgress = DownloadProgress; -/** - * Download the cache using the Actions toolkit http-client - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - */ -function downloadCacheHttpClient(archiveLocation, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - const writeStream = fs.createWriteStream(archivePath); - const httpClient = new http_client_1.HttpClient('actions/cache'); - const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); - // Abort download if no traffic received over the socket. - downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { - downloadResponse.message.destroy(); - core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); - }); - yield pipeResponseToStream(downloadResponse, writeStream); - // Validate download size. - const contentLengthHeader = downloadResponse.message.headers['content-length']; - if (contentLengthHeader) { - const expectedLength = parseInt(contentLengthHeader); - const actualLength = utils.getArchiveFileSizeInBytes(archivePath); - if (actualLength !== expectedLength) { - throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); - } - } - else { - core.debug('Unable to validate download, no Content-Length header'); - } - }); + +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version } -exports.downloadCacheHttpClient = downloadCacheHttpClient; -/** - * Download the cache using the Azure Storage SDK. Only call this method if the - * URL points to an Azure Storage endpoint. - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - * @param options the download options with the defaults set - */ -function downloadCacheStorageSDK(archiveLocation, archivePath, options) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { - retryOptions: { - // Override the timeout used when downloading each 4 MB chunk - // The default is 2 min / MB, which is way too slow - tryTimeoutInMs: options.timeoutInMs - } - }); - const properties = yield client.getProperties(); - const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; - if (contentLength < 0) { - // We should never hit this condition, but just in case fall back to downloading the - // file as one large stream - core.debug('Unable to determine content length, downloading file with http-client...'); - yield downloadCacheHttpClient(archiveLocation, archivePath); - } - else { - // Use downloadToBuffer for faster downloads, since internally it splits the - // file into 4 MB chunks which can then be parallelized and retried independently - // - // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB - // on 64-bit systems), split the download into multiple segments - // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. - // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast - const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); - const downloadProgress = new DownloadProgress(contentLength); - const fd = fs.openSync(archivePath, 'w'); - try { - downloadProgress.startDisplayTimer(); - const controller = new abort_controller_1.AbortController(); - const abortSignal = controller.signal; - while (!downloadProgress.isDone()) { - const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; - const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); - downloadProgress.nextSegment(segmentSize); - const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { - abortSignal, - concurrency: options.downloadConcurrency, - onProgress: downloadProgress.onProgress() - })); - if (result === 'timeout') { - controller.abort(); - throw new Error('Aborting cache download as the download time exceeded the timeout.'); - } - else if (Buffer.isBuffer(result)) { - fs.writeFileSync(fd, result); - } - } - } - finally { - downloadProgress.stopDisplayTimer(); - fs.closeSync(fd); - } - } - }); + +SemVer.prototype.toString = function () { + return this.version } -exports.downloadCacheStorageSDK = downloadCacheStorageSDK; -const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { - let timeoutHandle; - const timeoutPromise = new Promise(resolve => { - timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); - }); - return Promise.race([promise, timeoutPromise]).then(result => { - clearTimeout(timeoutHandle); - return result; - }); -}); -//# sourceMappingURL=downloadUtils.js.map -/***/ }), +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -/***/ 3981: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + return this.compareMain(other) || this.comparePre(other) +} -"use strict"; +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const constants_1 = __nccwpck_require__(8840); -function isSuccessStatusCode(statusCode) { - if (!statusCode) { - return false; - } - return statusCode >= 200 && statusCode < 300; + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) } -exports.isSuccessStatusCode = isSuccessStatusCode; -function isServerErrorStatusCode(statusCode) { - if (!statusCode) { - return true; + +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - return statusCode >= 500; + } while (++i) } -exports.isServerErrorStatusCode = isServerErrorStatusCode; -function isRetryableStatusCode(statusCode) { - if (!statusCode) { - return false; + +SemVer.prototype.compareBuild = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + var i = 0 + do { + var a = this.build[i] + var b = other.build[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - const retryableStatusCodes = [ - http_client_1.HttpCodes.BadGateway, - http_client_1.HttpCodes.ServiceUnavailable, - http_client_1.HttpCodes.GatewayTimeout - ]; - return retryableStatusCodes.includes(statusCode); -} -exports.isRetryableStatusCode = isRetryableStatusCode; -function sleep(milliseconds) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise(resolve => setTimeout(resolve, milliseconds)); - }); -} -function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { - return __awaiter(this, void 0, void 0, function* () { - let errorMessage = ''; - let attempt = 1; - while (attempt <= maxAttempts) { - let response = undefined; - let statusCode = undefined; - let isRetryable = false; - try { - response = yield method(); - } - catch (error) { - if (onError) { - response = onError(error); - } - isRetryable = true; - errorMessage = error.message; - } - if (response) { - statusCode = getStatusCode(response); - if (!isServerErrorStatusCode(statusCode)) { - return response; - } - } - if (statusCode) { - isRetryable = isRetryableStatusCode(statusCode); - errorMessage = `Cache service responded with ${statusCode}`; - } - core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); - if (!isRetryable) { - core.debug(`${name} - Error is not retryable`); - break; - } - yield sleep(delay); - attempt++; - } - throw Error(`${name} failed: ${errorMessage}`); - }); -} -exports.retry = retry; -function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, - // If the error object contains the statusCode property, extract it and return - // an TypedResponse so it can be processed by the retry logic. - (error) => { - if (error instanceof http_client_1.HttpClientError) { - return { - statusCode: error.statusCode, - result: null, - headers: {}, - error - }; - } - else { - return undefined; - } - }); - }); -} -exports.retryTypedResponse = retryTypedResponse; -function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); - }); + } while (++i) } -exports.retryHttpClientResponse = retryHttpClientResponse; -//# sourceMappingURL=requestUtils.js.map - -/***/ }), -/***/ 6490: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.createTar = exports.extractTar = exports.listTar = void 0; -const exec_1 = __nccwpck_require__(1514); -const io = __importStar(__nccwpck_require__(7436)); -const fs_1 = __nccwpck_require__(7147); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const IS_WINDOWS = process.platform === 'win32'; -// Returns tar path and type: BSD or GNU -function getTarPath() { - return __awaiter(this, void 0, void 0, function* () { - switch (process.platform) { - case 'win32': { - const gnuTar = yield utils.getGnuTarPathOnWindows(); - const systemTar = constants_1.SystemTarPathOnWindows; - if (gnuTar) { - // Use GNUtar as default on windows - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else if ((0, fs_1.existsSync)(systemTar)) { - return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; - } - break; - } - case 'darwin': { - const gnuTar = yield io.which('gtar', false); - if (gnuTar) { - // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else { - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.BSD - }; - } - } - default: - break; - } - // Default assumption is GNU tar is present in path - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.GNU - }; - }); -} -// Return arguments for tar as per tarPath, compressionMethod, method type and os -function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - const args = [`"${tarPath.path}"`]; - const cacheFileName = utils.getCacheFileName(compressionMethod); - const tarFile = 'cache.tar'; - const workingDirectory = getWorkingDirectory(); - // Speficic args for BSD tar on windows for workaround - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - // Method specific args - switch (type) { - case 'create': - args.push('--posix', '-cf', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); - break; - case 'extract': - args.push('-xf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); - break; - case 'list': - args.push('-tf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); - break; - } - // Platform specific args - if (tarPath.type === constants_1.ArchiveToolType.GNU) { - switch (process.platform) { - case 'win32': - args.push('--force-local'); - break; - case 'darwin': - args.push('--delay-directory-restore'); - break; - } - } - return args; - }); -} -// Returns commands to run tar and compression program -function getCommands(compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - let args; - const tarPath = yield getTarPath(); - const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); - const compressionArgs = type !== 'create' - ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) - : yield getCompressionProgram(tarPath, compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - if (BSD_TAR_ZSTD && type !== 'create') { - args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; - } - else { - args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; - } - if (BSD_TAR_ZSTD) { - return args; - } - return [args.join(' ')]; - }); -} -function getWorkingDirectory() { - var _a; - return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); -} -// Common function for extractTar and listTar to get the compression method -function getDecompressionProgram(tarPath, compressionMethod, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - // -d: Decompress. - // unzstd is equivalent to 'zstd -d' - // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. - // Using 30 here because we also support 32-bit self-hosted runners. - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --long=30 --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; - default: - return ['-z']; + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } } - }); -} -// Used for creating the archive -// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. -// zstdmt is equivalent to 'zstd -T0' -// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. -// Using 30 here because we also support 32-bit self-hosted runners. -// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. -function getCompressionProgram(tarPath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const cacheFileName = utils.getCacheFileName(compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --long=30 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; - default: - return ['-z']; + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) } - }); -} -// Executes all commands as separate processes -function execCommands(commands, cwd) { - return __awaiter(this, void 0, void 0, function* () { - for (const command of commands) { - try { - yield (0, exec_1.exec)(command, undefined, { - cwd, - env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) - }); - } - catch (error) { - throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); - } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] } - }); -} -// List the contents of a tar -function listTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const commands = yield getCommands(compressionMethod, 'list', archivePath); - yield execCommands(commands); - }); -} -exports.listTar = listTar; -// Extract a tar -function extractTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Create directory to extract tar into - const workingDirectory = getWorkingDirectory(); - yield io.mkdirP(workingDirectory); - const commands = yield getCommands(compressionMethod, 'extract', archivePath); - yield execCommands(commands); - }); -} -exports.extractTar = extractTar; -// Create a tar -function createTar(archiveFolder, sourceDirectories, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Write source directories to manifest.txt to avoid command length limits - (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); - const commands = yield getCommands(compressionMethod, 'create'); - yield execCommands(commands, archiveFolder); - }); -} -exports.createTar = createTar; -//# sourceMappingURL=tar.js.map + } + break -/***/ }), + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} -/***/ 6215: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } -"use strict"; + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getDownloadOptions = exports.getUploadOptions = void 0; -const core = __importStar(__nccwpck_require__(2186)); -/** - * Returns a copy of the upload options with defaults filled in. - * - * @param copy the original upload options - */ -function getUploadOptions(copy) { - const result = { - uploadConcurrency: 4, - uploadChunkSize: 32 * 1024 * 1024 - }; - if (copy) { - if (typeof copy.uploadConcurrency === 'number') { - result.uploadConcurrency = copy.uploadConcurrency; - } - if (typeof copy.uploadChunkSize === 'number') { - result.uploadChunkSize = copy.uploadChunkSize; - } +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' } - core.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core.debug(`Upload chunk size: ${result.uploadChunkSize}`); - return result; -} -exports.getUploadOptions = getUploadOptions; -/** - * Returns a copy of the download options with defaults filled in. - * - * @param copy the original download options - */ -function getDownloadOptions(copy) { - const result = { - useAzureSdk: true, - downloadConcurrency: 8, - timeoutInMs: 30000, - segmentTimeoutInMs: 600000, - lookupOnly: false - }; - if (copy) { - if (typeof copy.useAzureSdk === 'boolean') { - result.useAzureSdk = copy.useAzureSdk; - } - if (typeof copy.downloadConcurrency === 'number') { - result.downloadConcurrency = copy.downloadConcurrency; - } - if (typeof copy.timeoutInMs === 'number') { - result.timeoutInMs = copy.timeoutInMs; - } - if (typeof copy.segmentTimeoutInMs === 'number') { - result.segmentTimeoutInMs = copy.segmentTimeoutInMs; - } - if (typeof copy.lookupOnly === 'boolean') { - result.lookupOnly = copy.lookupOnly; + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key } + } } - const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; - if (segmentDownloadTimeoutMins && - !isNaN(Number(segmentDownloadTimeoutMins)) && - isFinite(Number(segmentDownloadTimeoutMins))) { - result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; - } - core.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core.debug(`Download concurrency: ${result.downloadConcurrency}`); - core.debug(`Request timeout (ms): ${result.timeoutInMs}`); - core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); - core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - core.debug(`Lookup only: ${result.lookupOnly}`); - return result; + return defaultResult // may be undefined + } } -exports.getDownloadOptions = getDownloadOptions; -//# sourceMappingURL=options.js.map - -/***/ }), -/***/ 3771: -/***/ ((module, exports) => { +exports.compareIdentifiers = compareIdentifiers -exports = module.exports = SemVer +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) + if (anum && bnum) { + a = +a + b = +b } -} else { - debug = function () {} -} - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var t = exports.tokens = {} -var R = 0 +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} -function tok (n) { - t[n] = R++ +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor } -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -tok('NUMERICIDENTIFIER') -src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' -tok('NUMERICIDENTIFIERLOOSE') -src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -tok('NONNUMERICIDENTIFIER') -src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - -// ## Main Version -// Three dot-separated numeric identifiers. +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} -tok('MAINVERSION') -src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')' +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} -tok('MAINVERSIONLOOSE') -src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. +exports.compareBuild = compareBuild +function compareBuild (a, b, loose) { + var versionA = new SemVer(a, loose) + var versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} -tok('PRERELEASEIDENTIFIER') -src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} -tok('PRERELEASEIDENTIFIERLOOSE') -src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(a, b, loose) + }) +} -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(b, a, loose) + }) +} -tok('PRERELEASE') -src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} -tok('PRERELEASELOOSE') -src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} -tok('BUILDIDENTIFIER') -src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} -tok('BUILD') -src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + - '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b -tok('FULL') -tok('FULLPLAIN') -src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + - src[t.PRERELEASE] + '?' + - src[t.BUILD] + '?' + case '': + case '=': + case '==': + return eq(a, b, loose) -src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + case '!=': + return neq(a, b, loose) -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -tok('LOOSEPLAIN') -src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + - src[t.PRERELEASELOOSE] + '?' + - src[t.BUILD] + '?' + case '>': + return gt(a, b, loose) -tok('LOOSE') -src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + case '>=': + return gte(a, b, loose) -tok('GTLT') -src[t.GTLT] = '((?:<|>)?=?)' + case '<': + return lt(a, b, loose) -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -tok('XRANGEIDENTIFIERLOOSE') -src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -tok('XRANGEIDENTIFIER') -src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + case '<=': + return lte(a, b, loose) -tok('XRANGEPLAIN') -src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:' + src[t.PRERELEASE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' + default: + throw new TypeError('Invalid operator: ' + op) + } +} -tok('XRANGEPLAINLOOSE') -src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[t.PRERELEASELOOSE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -tok('XRANGE') -src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' -tok('XRANGELOOSE') -src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -tok('COERCE') -src[t.COERCE] = '(^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' -tok('COERCERTL') -re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } -// Tilde ranges. -// Meaning is "reasonably at or greater than" -tok('LONETILDE') -src[t.LONETILDE] = '(?:~>?)' + comp = comp.trim().split(/\s+/).join(' ') + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) -tok('TILDETRIM') -src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' -re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') -var tildeTrimReplace = '$1~' + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } -tok('TILDE') -src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' -tok('TILDELOOSE') -src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + debug('comp', this) +} -// Caret ranges. -// Meaning is "at least and backwards compatible with" -tok('LONECARET') -src[t.LONECARET] = '(?:\\^)' +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var m = comp.match(r) -tok('CARETTRIM') -src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' -re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') -var caretTrimReplace = '$1^' + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } -tok('CARET') -src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' -tok('CARETLOOSE') -src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } -// A simple gt/lt/eq thing, or just "" to indicate "any version" -tok('COMPARATORLOOSE') -src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' -tok('COMPARATOR') -src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -tok('COMPARATORTRIM') -src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + - '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' +Comparator.prototype.toString = function () { + return this.value +} -// this one has to use the /g flag -re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -tok('HYPHENRANGE') -src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAIN] + ')' + - '\\s*$' + if (this.semver === ANY || version === ANY) { + return true + } -tok('HYPHENRANGELOOSE') -src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s*$' + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } -// Star ranges basically just allow anything at all. -tok('STAR') -src[t.STAR] = '(<|>)?=?\\s*\\*' + return cmp(version, this.operator, this.semver, this.options) +} -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') } -} -exports.parse = parse -function parse (version, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, @@ -1817,985 +888,409 @@ function parse (version, options) { } } - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - var r = options.loose ? re[t.LOOSE] : re[t.FULL] - if (!r.test(version)) { - return null - } + var rangeTmp - try { - return new SemVer(version, options) - } catch (er) { - return null + if (this.operator === '') { + if (this.value === '') { + return true + } + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) } -} -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan } -exports.SemVer = SemVer - -function SemVer (version, options) { +exports.Range = Range +function Range (range, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, includePrerelease: false } } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version + + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range } else { - version = version.version + return new Range(range.raw, options) } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) } - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + if (range instanceof Comparator) { + return new Range(range.value, options) } - if (!(this instanceof SemVer)) { - return new SemVer(version, options) + if (!(this instanceof Range)) { + return new Range(range, options) } - debug('SemVer', version, options) this.options = options this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease - var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) - - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } - - this.raw = version - - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. + this.raw = range + .trim() + .split(/\s+/) + .join(' ') - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } + // First, split based on boolean or || + this.set = this.raw.split('||').map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + this.raw) } - this.build = m[5] ? m[5].split('.') : [] this.format() } -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range } -SemVer.prototype.toString = function () { - return this.version +Range.prototype.toString = function () { + return this.range } -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, safeRe[t.COMPARATORTRIM]) - return this.compareMain(other) || this.comparePre(other) -} + // `~ 1.2.3` => `~1.2.3` + range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace) -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // `^ 1.2.3` => `^1.2.3` + range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace) - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} + // normalize spaces + range = range.split(/\s+/).join(' ') -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // At this point, the range is completely trimmed and + // ready to be split into comparators. - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 + var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return set } -SemVer.prototype.compareBuild = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') } - var i = 0 - do { - var a = this.build[i] - var b = other.build[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return this.set.some(function (thisComparators) { + return ( + isSatisfiable(thisComparators, options) && + range.set.some(function (rangeComparators) { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every(function (thisComparator) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) } -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break - - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} +// take a set of comparators and determine whether there +// exists a version which can satisfy it +function isSatisfiable (comparators, options) { + var result = true + var remainingComparators = comparators.slice() + var testComparator = remainingComparators.pop() -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } + while (result && remainingComparators.length) { + result = remainingComparators.every(function (otherComparator) { + return testComparator.intersects(otherComparator, options) + }) - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null + testComparator = remainingComparators.pop() } -} -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } + return result } -exports.compareIdentifiers = compareIdentifiers - -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) - - if (anum && bnum) { - a = +a - b = +b - } - - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) } -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp } -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' } -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') } -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} +function replaceTilde (comp, options) { + var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) + debug('tilde return', ret) + return ret + }) } -exports.compareBuild = compareBuild -function compareBuild (a, b, loose) { - var versionA = new SemVer(a, loose) - var versionB = new SemVer(b, loose) - return versionA.compare(versionB) || versionA.compareBuild(versionB) +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') } -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(a, b, loose) - }) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(b, a, loose) + debug('caret return', ret) + return ret }) } -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') } -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} + if (gtlt === '=' && anyX) { + gtlt = '' + } -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b + ret = gtlt + M + '.' + m + '.' + p + pr + } else if (xm) { + ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr + } else if (xp) { + ret = '>=' + M + '.' + m + '.0' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + pr + } - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b + debug('xRange return', ret) - case '': - case '=': - case '==': - return eq(a, b, loose) + return ret + }) +} - case '!=': - return neq(a, b, loose) - - case '>': - return gt(a, b, loose) - - case '>=': - return gte(a, b, loose) - - case '<': - return lt(a, b, loose) - - case '<=': - return lte(a, b, loose) - - default: - throw new TypeError('Invalid operator: ' + op) - } -} - -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } - - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } - - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } - - debug('comp', this) -} - -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var m = comp.match(r) - - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } - - this.operator = m[1] !== undefined ? m[1] : '' - if (this.operator === '=') { - this.operator = '' - } - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} - -Comparator.prototype.toString = function () { - return this.value -} - -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) - - if (this.semver === ANY || version === ANY) { - return true - } - - if (typeof version === 'string') { - try { - version = new SemVer(version, this.options) - } catch (er) { - return false - } - } - - return cmp(version, this.operator, this.semver, this.options) -} - -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } - - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - var rangeTmp - - if (this.operator === '') { - if (this.value === '') { - return true - } - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - if (comp.value === '') { - return true - } - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} - -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease - - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } - - this.format() -} - -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} - -Range.prototype.toString = function () { - return this.range -} - -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[t.COMPARATORTRIM]) - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[t.CARETTRIM], caretTrimReplace) - - // normalize spaces - range = range.split(/\s+/).join(' ') - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) - - return set -} - -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } - - return this.set.some(function (thisComparators) { - return ( - isSatisfiable(thisComparators, options) && - range.set.some(function (rangeComparators) { - return ( - isSatisfiable(rangeComparators, options) && - thisComparators.every(function (thisComparator) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - ) - }) - ) - }) -} - -// take a set of comparators and determine whether there -// exists a version which can satisfy it -function isSatisfiable (comparators, options) { - var result = true - var remainingComparators = comparators.slice() - var testComparator = remainingComparators.pop() - - while (result && remainingComparators.length) { - result = remainingComparators.every(function (otherComparator) { - return testComparator.intersects(otherComparator, options) - }) - - testComparator = remainingComparators.pop() - } - - return result -} - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} - -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} - -function replaceTilde (comp, options) { - var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - - debug('tilde return', ret) - return ret - }) -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} - -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } - - debug('caret return', ret) - return ret - }) -} - -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} - -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp - - if (gtlt === '=' && anyX) { - gtlt = '' - } - - // if we're including prereleases in the match, then we need - // to fix this to -0, the lowest possible prerelease value - pr = options.includePrerelease ? '-0' : '' - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0-0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } - - ret = gtlt + M + '.' + m + '.' + p + pr - } else if (xm) { - ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr - } else if (xp) { - ret = '>=' + M + '.' + m + '.0' + pr + - ' <' + M + '.' + (+m + 1) + '.0' + pr - } - - debug('xRange return', ret) - - return ret - }) -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') -} +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(safeRe[t.STAR], '') +} // This function is passed to string.replace(re[t.HYPHENRANGE]) // M, m, patch, prerelease, build @@ -3120,7 +1615,7 @@ function coerce (version, options) { var match = null if (!options.rtl) { - match = version.match(re[t.COERCE]) + match = version.match(safeRe[t.COERCE]) } else { // Find the right-most coercible string that does not share // a terminus with a more left-ward coercible string. @@ -3131,17 +1626,17 @@ function coerce (version, options) { // Stop when we get a match that ends at the string end, since no // coercible string can be more right-ward without the same terminus. var next - while ((next = re[t.COERCERTL].exec(version)) && + while ((next = safeRe[t.COERCERTL].exec(version)) && (!match || match.index + match[0].length !== version.length) ) { if (!match || next.index + next[0].length !== match.index + match[0].length) { match = next } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length } // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 + safeRe[t.COERCERTL].lastIndex = -1 } if (match === null) { @@ -7026,6 +5521,19 @@ class HttpClientResponse { })); }); } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } } exports.HttpClientResponse = HttpClientResponse; function isHttps(requestUrl) { @@ -7530,7 +6038,13 @@ function getProxyUrl(reqUrl) { } })(); if (proxyVar) { - return new URL(proxyVar); + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } } else { return undefined; @@ -7541,6 +6055,10 @@ function checkBypass(reqUrl) { if (!reqUrl.hostname) { return false; } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; if (!noProxy) { return false; @@ -7566,13 +6084,24 @@ function checkBypass(reqUrl) { .split(',') .map(x => x.trim().toUpperCase()) .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { + if (upperNoProxyItem === '*' || + upperReqHosts.some(x => x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)))) { return true; } } return false; } exports.checkBypass = checkBypass; +function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return (hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]')); +} //# sourceMappingURL=proxy.js.map /***/ }), @@ -48965,7 +47494,7 @@ function state(list, sortMethod) /***/ }), -/***/ 7942: +/***/ 834: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { var abort = __nccwpck_require__(1700) @@ -49006,7 +47535,7 @@ function terminator(callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -49080,7 +47609,7 @@ function serial(list, iterator, callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -62310,123 +60839,1799 @@ function wrappy (fn, cb) { } else if (node.sysID) { r += ' SYSTEM "' + node.sysID + '"'; } - if (node.nData) { - r += ' NDATA ' + node.nData; + if (node.nData) { + r += ' NDATA ' + node.nData; + } + } + options.state = WriterState.CloseTag; + r += options.spaceBeforeSlash + '>' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + }; + + XMLWriterBase.prototype.dtdNotation = function(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + }; + + XMLWriterBase.prototype.openNode = function(node, options, level) {}; + + XMLWriterBase.prototype.closeNode = function(node, options, level) {}; + + XMLWriterBase.prototype.openAttribute = function(att, options, level) {}; + + XMLWriterBase.prototype.closeAttribute = function(att, options, level) {}; + + return XMLWriterBase; + + })(); + +}).call(this); + + +/***/ }), + +/***/ 2958: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +// Generated by CoffeeScript 1.12.7 +(function() { + var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; + + ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; + + XMLDOMImplementation = __nccwpck_require__(8310); + + XMLDocument = __nccwpck_require__(3730); + + XMLDocumentCB = __nccwpck_require__(7356); + + XMLStringWriter = __nccwpck_require__(5913); + + XMLStreamWriter = __nccwpck_require__(8601); + + NodeType = __nccwpck_require__(9267); + + WriterState = __nccwpck_require__(9766); + + module.exports.create = function(name, xmldec, doctype, options) { + var doc, root; + if (name == null) { + throw new Error("Root element needs a name."); + } + options = assign({}, xmldec, doctype, options); + doc = new XMLDocument(options); + root = doc.element(name); + if (!options.headless) { + doc.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + doc.dtd(options); + } + } + return root; + }; + + module.exports.begin = function(options, onData, onEnd) { + var ref1; + if (isFunction(options)) { + ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; + options = {}; + } + if (onData) { + return new XMLDocumentCB(options, onData, onEnd); + } else { + return new XMLDocument(options); + } + }; + + module.exports.stringWriter = function(options) { + return new XMLStringWriter(options); + }; + + module.exports.streamWriter = function(stream, options) { + return new XMLStreamWriter(stream, options); + }; + + module.exports.implementation = new XMLDOMImplementation(); + + module.exports.nodeType = NodeType; + + module.exports.writerState = WriterState; + +}).call(this); + + +/***/ }), + +/***/ 7942: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const cacheHttpClient = __importStar(__nccwpck_require__(7417)); +const tar_1 = __nccwpck_require__(7672); +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = 'ValidationError'; + Object.setPrototypeOf(this, ValidationError.prototype); + } +} +exports.ValidationError = ValidationError; +class ReserveCacheError extends Error { + constructor(message) { + super(message); + this.name = 'ReserveCacheError'; + Object.setPrototypeOf(this, ReserveCacheError.prototype); + } +} +exports.ReserveCacheError = ReserveCacheError; +function checkPaths(paths) { + if (!paths || paths.length === 0) { + throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); + } +} +function checkKey(key) { + if (key.length > 512) { + throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); + } + const regex = /^[^,]*$/; + if (!regex.test(key)) { + throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); + } +} +/** + * isFeatureAvailable to check the presence of Actions cache service + * + * @returns boolean return true if Actions cache service feature is available, otherwise false + */ +function isFeatureAvailable() { + return !!process.env['ACTIONS_CACHE_URL']; +} +exports.isFeatureAvailable = isFeatureAvailable; +/** + * Restores cache from keys + * + * @param paths a list of file paths to restore from the cache + * @param primaryKey an explicit key for restoring the cache + * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @param downloadOptions cache download options + * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform + * @returns string returns the key for the cache hit, otherwise returns undefined + */ +function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + restoreKeys = restoreKeys || []; + const keys = [primaryKey, ...restoreKeys]; + core.debug('Resolved Keys:'); + core.debug(JSON.stringify(keys)); + if (keys.length > 10) { + throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); + } + for (const key of keys) { + checkKey(key); + } + const compressionMethod = yield utils.getCompressionMethod(); + let archivePath = ''; + try { + // path are needed to compute version + const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod, + enableCrossOsArchive + }); + if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { + // Cache not found + return undefined; + } + if (options === null || options === void 0 ? void 0 : options.lookupOnly) { + core.info('Lookup only - skipping download'); + return cacheEntry.cacheKey; + } + archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + // Download the cache from the cache entry + yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + yield (0, tar_1.extractTar)(archivePath, compressionMethod, extraTarArgs); + core.info('Cache restored successfully'); + return cacheEntry.cacheKey; + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else { + // Supress all non-validation cache related errors because caching should be optional + core.warning(`Failed to restore: ${error.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return undefined; + }); +} +exports.restoreCache = restoreCache; +/** + * Saves a list of files with the specified key + * + * @param paths a list of file paths to be cached + * @param key an explicit key for restoring the cache + * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform + * @param options cache upload options + * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails + */ +function saveCache(paths, key, options, enableCrossOsArchive = false) { + var _a, _b, _c, _d, _e; + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + checkKey(key); + const compressionMethod = yield utils.getCompressionMethod(); + let cacheId = -1; + const cachePaths = yield utils.resolvePaths(paths); + core.debug('Cache Paths:'); + core.debug(`${JSON.stringify(cachePaths)}`); + if (cachePaths.length === 0) { + throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); + } + const archiveFolder = yield utils.createTempDirectory(); + const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + try { + yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.debug(`File Size: ${archiveFileSize}`); + // For GHES, this check will take place in ReserveCache API with enterprise file size limit + if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { + throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); + } + core.debug('Reserving Cache'); + const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { + compressionMethod, + enableCrossOsArchive, + cacheSize: archiveFileSize + }); + if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { + cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; + } + else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { + throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); + } + else { + throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); + } + core.debug(`Saving Cache (ID: ${cacheId})`); + yield cacheHttpClient.saveCache(cacheId, archivePath, options); + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else if (typedError.name === ReserveCacheError.name) { + core.info(`Failed to save: ${typedError.message}`); + } + else { + core.warning(`Failed to save: ${typedError.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return cacheId; + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 7417: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const auth_1 = __nccwpck_require__(5526); +const crypto = __importStar(__nccwpck_require__(6113)); +const fs = __importStar(__nccwpck_require__(7147)); +const url_1 = __nccwpck_require__(7310); +const utils = __importStar(__nccwpck_require__(4799)); +const downloadUtils_1 = __nccwpck_require__(8432); +const options_1 = __nccwpck_require__(3128); +const requestUtils_1 = __nccwpck_require__(204); +const versionSalt = '1.0'; +function getCacheApiUrl(resource) { + const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; + if (!baseUrl) { + throw new Error('Cache Service Url not found, unable to restore cache.'); + } + const url = `${baseUrl}_apis/artifactcache/${resource}`; + core.debug(`Resource Url: ${url}`); + return url; +} +function createAcceptHeader(type, apiVersion) { + return `${type};api-version=${apiVersion}`; +} +function getRequestOptions() { + const requestOptions = { + headers: { + Accept: createAcceptHeader('application/json', '6.0-preview.1') + } + }; + return requestOptions; +} +function createHttpClient() { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; + const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); + return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); +} +function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { + const components = paths; + // Add compression method to cache version to restore + // compressed cache as per compression method + if (compressionMethod) { + components.push(compressionMethod); + } + // Only check for windows platforms if enableCrossOsArchive is false + if (process.platform === 'win32' && !enableCrossOsArchive) { + components.push('windows-only'); + } + // Add salt to cache version to support breaking changes in cache entry + components.push(versionSalt); + return crypto.createHash('sha256').update(components.join('|')).digest('hex'); +} +exports.getCacheVersion = getCacheVersion; +function getCacheEntry(keys, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + // Cache not found + if (response.statusCode === 204) { + // List cache for primary key only if cache miss occurs + if (core.isDebug()) { + yield printCachesListForDiagnostics(keys[0], httpClient, version); + } + return null; + } + if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { + throw new Error(`Cache service responded with ${response.statusCode}`); + } + const cacheResult = response.result; + const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; + if (!cacheDownloadUrl) { + // Cache achiveLocation not found. This should never happen, and hence bail out. + throw new Error('Cache not found.'); + } + core.setSecret(cacheDownloadUrl); + core.debug(`Cache Result:`); + core.debug(JSON.stringify(cacheResult)); + return cacheResult; + }); +} +exports.getCacheEntry = getCacheEntry; +function printCachesListForDiagnostics(key, httpClient, version) { + return __awaiter(this, void 0, void 0, function* () { + const resource = `caches?key=${encodeURIComponent(key)}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + if (response.statusCode === 200) { + const cacheListResult = response.result; + const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; + if (totalCount && totalCount > 0) { + core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); + for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { + core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + } + } + } + }); +} +function downloadCache(archiveLocation, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const archiveUrl = new url_1.URL(archiveLocation); + const downloadOptions = (0, options_1.getDownloadOptions)(options); + if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) { + if (downloadOptions.useAzureSdk) { + // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. + yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); + } + else if (downloadOptions.concurrentBlobDownloads) { + // Use concurrent implementation with HttpClient to work around blob SDK issue + yield (0, downloadUtils_1.downloadCacheHttpClientConcurrent)(archiveLocation, archivePath, downloadOptions); + } + else { + // Otherwise, download using the Actions http-client. + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + } + else { + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + }); +} +exports.downloadCache = downloadCache; +// Reserve Cache +function reserveCache(key, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const reserveCacheRequest = { + key, + version, + cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize + }; + const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); + })); + return response; + }); +} +exports.reserveCache = reserveCache; +function getContentRange(start, end) { + // Format: `bytes start-end/filesize + // start and end are inclusive + // filesize can be * + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/* + return `bytes ${start}-${end}/*`; +} +function uploadChunk(httpClient, resourceUrl, openStream, start, end) { + return __awaiter(this, void 0, void 0, function* () { + core.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + const additionalHeaders = { + 'Content-Type': 'application/octet-stream', + 'Content-Range': getContentRange(start, end) + }; + const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { + return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); + })); + if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { + throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); + } + }); +} +function uploadFile(httpClient, cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + // Upload Chunks + const fileSize = utils.getArchiveFileSizeInBytes(archivePath); + const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); + const fd = fs.openSync(archivePath, 'r'); + const uploadOptions = (0, options_1.getUploadOptions)(options); + const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); + const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); + const parallelUploads = [...new Array(concurrency).keys()]; + core.debug('Awaiting all uploads'); + let offset = 0; + try { + yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, maxChunkSize); + const start = offset; + const end = offset + chunkSize - 1; + offset += maxChunkSize; + yield uploadChunk(httpClient, resourceUrl, () => fs + .createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + .on('error', error => { + throw new Error(`Cache upload failed because file read failed with ${error.message}`); + }), start, end); + } + }))); + } + finally { + fs.closeSync(fd); + } + return; + }); +} +function commitCache(httpClient, cacheId, filesize) { + return __awaiter(this, void 0, void 0, function* () { + const commitCacheRequest = { size: filesize }; + return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); + })); + }); +} +function saveCache(cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + core.debug('Upload cache'); + yield uploadFile(httpClient, cacheId, archivePath, options); + // Commit Cache + core.debug('Commiting cache'); + const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); + if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { + throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); + } + core.info('Cache saved successfully'); + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 4799: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const exec = __importStar(__nccwpck_require__(1514)); +const glob = __importStar(__nccwpck_require__(8090)); +const io = __importStar(__nccwpck_require__(7436)); +const fs = __importStar(__nccwpck_require__(7147)); +const path = __importStar(__nccwpck_require__(1017)); +const semver = __importStar(__nccwpck_require__(1898)); +const util = __importStar(__nccwpck_require__(3837)); +const uuid_1 = __nccwpck_require__(2155); +const constants_1 = __nccwpck_require__(3608); +// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 +function createTempDirectory() { + return __awaiter(this, void 0, void 0, function* () { + const IS_WINDOWS = process.platform === 'win32'; + let tempDirectory = process.env['RUNNER_TEMP'] || ''; + if (!tempDirectory) { + let baseLocation; + if (IS_WINDOWS) { + // On Windows use the USERPROFILE env variable + baseLocation = process.env['USERPROFILE'] || 'C:\\'; + } + else { + if (process.platform === 'darwin') { + baseLocation = '/Users'; + } + else { + baseLocation = '/home'; + } + } + tempDirectory = path.join(baseLocation, 'actions', 'temp'); + } + const dest = path.join(tempDirectory, (0, uuid_1.v4)()); + yield io.mkdirP(dest); + return dest; + }); +} +exports.createTempDirectory = createTempDirectory; +function getArchiveFileSizeInBytes(filePath) { + return fs.statSync(filePath).size; +} +exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; +function resolvePaths(patterns) { + var _a, e_1, _b, _c; + var _d; + return __awaiter(this, void 0, void 0, function* () { + const paths = []; + const workspace = (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); + const globber = yield glob.create(patterns.join('\n'), { + implicitDescendants: false + }); + try { + for (var _e = true, _f = __asyncValues(globber.globGenerator()), _g; _g = yield _f.next(), _a = _g.done, !_a;) { + _c = _g.value; + _e = false; + try { + const file = _c; + const relativeFile = path + .relative(workspace, file) + .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); + core.debug(`Matched: ${relativeFile}`); + // Paths are made relative so the tar entries are all relative to the root of the workspace. + if (relativeFile === '') { + // path.relative returns empty string if workspace and file are equal + paths.push('.'); + } + else { + paths.push(`${relativeFile}`); + } + } + finally { + _e = true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_e && !_a && (_b = _f.return)) yield _b.call(_f); + } + finally { if (e_1) throw e_1.error; } + } + return paths; + }); +} +exports.resolvePaths = resolvePaths; +function unlinkFile(filePath) { + return __awaiter(this, void 0, void 0, function* () { + return util.promisify(fs.unlink)(filePath); + }); +} +exports.unlinkFile = unlinkFile; +function getVersion(app, additionalArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let versionOutput = ''; + additionalArgs.push('--version'); + core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); + try { + yield exec.exec(`${app}`, additionalArgs, { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + } + catch (err) { + core.debug(err.message); + } + versionOutput = versionOutput.trim(); + core.debug(versionOutput); + return versionOutput; + }); +} +// Use zstandard if possible to maximize cache performance +function getCompressionMethod() { + return __awaiter(this, void 0, void 0, function* () { + const versionOutput = yield getVersion('zstd', ['--quiet']); + const version = semver.clean(versionOutput); + core.debug(`zstd version: ${version}`); + if (versionOutput === '') { + return constants_1.CompressionMethod.Gzip; + } + else { + return constants_1.CompressionMethod.ZstdWithoutLong; + } + }); +} +exports.getCompressionMethod = getCompressionMethod; +function getCacheFileName(compressionMethod) { + return compressionMethod === constants_1.CompressionMethod.Gzip + ? constants_1.CacheFilename.Gzip + : constants_1.CacheFilename.Zstd; +} +exports.getCacheFileName = getCacheFileName; +function getGnuTarPathOnWindows() { + return __awaiter(this, void 0, void 0, function* () { + if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { + return constants_1.GnuTarPathOnWindows; + } + const versionOutput = yield getVersion('tar'); + return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; + }); +} +exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; +function assertDefined(name, value) { + if (value === undefined) { + throw Error(`Expected ${name} but value was undefiend`); + } + return value; +} +exports.assertDefined = assertDefined; +function isGhes() { + const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; +} +exports.isGhes = isGhes; + + +/***/ }), + +/***/ 3608: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; +var CacheFilename; +(function (CacheFilename) { + CacheFilename["Gzip"] = "cache.tgz"; + CacheFilename["Zstd"] = "cache.tzst"; +})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); +var CompressionMethod; +(function (CompressionMethod) { + CompressionMethod["Gzip"] = "gzip"; + // Long range mode was added to zstd in v1.3.2. + // This enum is for earlier version of zstd that does not have --long support + CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; + CompressionMethod["Zstd"] = "zstd"; +})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); +var ArchiveToolType; +(function (ArchiveToolType) { + ArchiveToolType["GNU"] = "gnu"; + ArchiveToolType["BSD"] = "bsd"; +})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); +// The default number of retry attempts. +exports.DefaultRetryAttempts = 2; +// The default delay in milliseconds between retry attempts. +exports.DefaultRetryDelay = 5000; +// Socket timeout in milliseconds during download. If no traffic is received +// over the socket during this period, the socket is destroyed and the download +// is aborted. +exports.SocketTimeout = 5000; +// The default path of GNUtar on hosted Windows runners +exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; +// The default path of BSDtar on hosted Windows runners +exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; +exports.TarFilename = 'cache.tar'; +exports.ManifestFilename = 'manifest.txt'; + + +/***/ }), + +/***/ 8432: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.downloadCacheStorageSDK = exports.downloadCacheHttpClientConcurrent = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const storage_blob_1 = __nccwpck_require__(4100); +const buffer = __importStar(__nccwpck_require__(4300)); +const fs = __importStar(__nccwpck_require__(7147)); +const stream = __importStar(__nccwpck_require__(2781)); +const util = __importStar(__nccwpck_require__(3837)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const requestUtils_1 = __nccwpck_require__(204); +const abort_controller_1 = __nccwpck_require__(2557); +/** + * Pipes the body of a HTTP response to a stream + * + * @param response the HTTP response + * @param output the writable stream + */ +function pipeResponseToStream(response, output) { + return __awaiter(this, void 0, void 0, function* () { + const pipeline = util.promisify(stream.pipeline); + yield pipeline(response.message, output); + }); +} +/** + * Class for tracking the download state and displaying stats. + */ +class DownloadProgress { + constructor(contentLength) { + this.contentLength = contentLength; + this.segmentIndex = 0; + this.segmentSize = 0; + this.segmentOffset = 0; + this.receivedBytes = 0; + this.displayedComplete = false; + this.startTime = Date.now(); + } + /** + * Progress to the next segment. Only call this method when the previous segment + * is complete. + * + * @param segmentSize the length of the next segment + */ + nextSegment(segmentSize) { + this.segmentOffset = this.segmentOffset + this.segmentSize; + this.segmentIndex = this.segmentIndex + 1; + this.segmentSize = segmentSize; + this.receivedBytes = 0; + core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + } + /** + * Sets the number of bytes received for the current segment. + * + * @param receivedBytes the number of bytes received + */ + setReceivedBytes(receivedBytes) { + this.receivedBytes = receivedBytes; + } + /** + * Returns the total number of bytes transferred. + */ + getTransferredBytes() { + return this.segmentOffset + this.receivedBytes; + } + /** + * Returns true if the download is complete. + */ + isDone() { + return this.getTransferredBytes() === this.contentLength; + } + /** + * Prints the current download stats. Once the download completes, this will print one + * last line and then stop. + */ + display() { + if (this.displayedComplete) { + return; + } + const transferredBytes = this.segmentOffset + this.receivedBytes; + const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); + const elapsedTime = Date.now() - this.startTime; + const downloadSpeed = (transferredBytes / + (1024 * 1024) / + (elapsedTime / 1000)).toFixed(1); + core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + if (this.isDone()) { + this.displayedComplete = true; + } + } + /** + * Returns a function used to handle TransferProgressEvents. + */ + onProgress() { + return (progress) => { + this.setReceivedBytes(progress.loadedBytes); + }; + } + /** + * Starts the timer that displays the stats. + * + * @param delayInMs the delay between each write + */ + startDisplayTimer(delayInMs = 1000) { + const displayCallback = () => { + this.display(); + if (!this.isDone()) { + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + }; + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + /** + * Stops the timer that displays the stats. As this typically indicates the download + * is complete, this will display one last line, unless the last line has already + * been written. + */ + stopDisplayTimer() { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle); + this.timeoutHandle = undefined; + } + this.display(); + } +} +exports.DownloadProgress = DownloadProgress; +/** + * Download the cache using the Actions toolkit http-client + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClient(archiveLocation, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + const writeStream = fs.createWriteStream(archivePath); + const httpClient = new http_client_1.HttpClient('actions/cache'); + const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { + downloadResponse.message.destroy(); + core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); + }); + yield pipeResponseToStream(downloadResponse, writeStream); + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length']; + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader); + const actualLength = utils.getArchiveFileSizeInBytes(archivePath); + if (actualLength !== expectedLength) { + throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); + } + } + else { + core.debug('Unable to validate download, no Content-Length header'); + } + }); +} +exports.downloadCacheHttpClient = downloadCacheHttpClient; +/** + * Download the cache using the Actions toolkit http-client concurrently + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const archiveDescriptor = yield fs.promises.open(archivePath, 'w'); + const httpClient = new http_client_1.HttpClient('actions/cache', undefined, { + socketTimeout: options.timeoutInMs, + keepAlive: true + }); + try { + const res = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCacheMetadata', () => __awaiter(this, void 0, void 0, function* () { return yield httpClient.request('HEAD', archiveLocation, null, {}); })); + const lengthHeader = res.message.headers['content-length']; + if (lengthHeader === undefined || lengthHeader === null) { + throw new Error('Content-Length not found on blob response'); + } + const length = parseInt(lengthHeader); + if (Number.isNaN(length)) { + throw new Error(`Could not interpret Content-Length: ${length}`); + } + const downloads = []; + const blockSize = 4 * 1024 * 1024; + for (let offset = 0; offset < length; offset += blockSize) { + const count = Math.min(blockSize, length - offset); + downloads.push({ + offset, + promiseGetter: () => __awaiter(this, void 0, void 0, function* () { + return yield downloadSegmentRetry(httpClient, archiveLocation, offset, count); + }) + }); + } + // reverse to use .pop instead of .shift + downloads.reverse(); + let actives = 0; + let bytesDownloaded = 0; + const progress = new DownloadProgress(length); + progress.startDisplayTimer(); + const progressFn = progress.onProgress(); + const activeDownloads = []; + let nextDownload; + const waitAndWrite = () => __awaiter(this, void 0, void 0, function* () { + const segment = yield Promise.race(Object.values(activeDownloads)); + yield archiveDescriptor.write(segment.buffer, 0, segment.count, segment.offset); + actives--; + delete activeDownloads[segment.offset]; + bytesDownloaded += segment.count; + progressFn({ loadedBytes: bytesDownloaded }); + }); + while ((nextDownload = downloads.pop())) { + activeDownloads[nextDownload.offset] = nextDownload.promiseGetter(); + actives++; + if (actives >= ((_a = options.downloadConcurrency) !== null && _a !== void 0 ? _a : 10)) { + yield waitAndWrite(); + } + } + while (actives > 0) { + yield waitAndWrite(); + } + } + finally { + httpClient.dispose(); + yield archiveDescriptor.close(); + } + }); +} +exports.downloadCacheHttpClientConcurrent = downloadCacheHttpClientConcurrent; +function downloadSegmentRetry(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const retries = 5; + let failures = 0; + while (true) { + try { + const timeout = 30000; + const result = yield promiseWithTimeout(timeout, downloadSegment(httpClient, archiveLocation, offset, count)); + if (typeof result === 'string') { + throw new Error('downloadSegmentRetry failed due to timeout'); + } + return result; + } + catch (err) { + if (failures >= retries) { + throw err; + } + failures++; + } + } + }); +} +function downloadSegment(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const partRes = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCachePart', () => __awaiter(this, void 0, void 0, function* () { + return yield httpClient.get(archiveLocation, { + Range: `bytes=${offset}-${offset + count - 1}` + }); + })); + if (!partRes.readBodyBuffer) { + throw new Error('Expected HttpClientResponse to implement readBodyBuffer'); + } + return { + offset, + count, + buffer: yield partRes.readBodyBuffer() + }; + }); +} +/** + * Download the cache using the Azure Storage SDK. Only call this method if the + * URL points to an Azure Storage endpoint. + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + * @param options the download options with the defaults set + */ +function downloadCacheStorageSDK(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { + retryOptions: { + // Override the timeout used when downloading each 4 MB chunk + // The default is 2 min / MB, which is way too slow + tryTimeoutInMs: options.timeoutInMs + } + }); + const properties = yield client.getProperties(); + const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; + if (contentLength < 0) { + // We should never hit this condition, but just in case fall back to downloading the + // file as one large stream + core.debug('Unable to determine content length, downloading file with http-client...'); + yield downloadCacheHttpClient(archiveLocation, archivePath); + } + else { + // Use downloadToBuffer for faster downloads, since internally it splits the + // file into 4 MB chunks which can then be parallelized and retried independently + // + // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB + // on 64-bit systems), split the download into multiple segments + // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. + // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast + const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); + const downloadProgress = new DownloadProgress(contentLength); + const fd = fs.openSync(archivePath, 'w'); + try { + downloadProgress.startDisplayTimer(); + const controller = new abort_controller_1.AbortController(); + const abortSignal = controller.signal; + while (!downloadProgress.isDone()) { + const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; + const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); + downloadProgress.nextSegment(segmentSize); + const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { + abortSignal, + concurrency: options.downloadConcurrency, + onProgress: downloadProgress.onProgress() + })); + if (result === 'timeout') { + controller.abort(); + throw new Error('Aborting cache download as the download time exceeded the timeout.'); + } + else if (Buffer.isBuffer(result)) { + fs.writeFileSync(fd, result); + } + } + } + finally { + downloadProgress.stopDisplayTimer(); + fs.closeSync(fd); + } + } + }); +} +exports.downloadCacheStorageSDK = downloadCacheStorageSDK; +const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { + let timeoutHandle; + const timeoutPromise = new Promise(resolve => { + timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); + }); + return Promise.race([promise, timeoutPromise]).then(result => { + clearTimeout(timeoutHandle); + return result; + }); +}); + + +/***/ }), + +/***/ 204: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const constants_1 = __nccwpck_require__(3608); +function isSuccessStatusCode(statusCode) { + if (!statusCode) { + return false; + } + return statusCode >= 200 && statusCode < 300; +} +exports.isSuccessStatusCode = isSuccessStatusCode; +function isServerErrorStatusCode(statusCode) { + if (!statusCode) { + return true; + } + return statusCode >= 500; +} +exports.isServerErrorStatusCode = isServerErrorStatusCode; +function isRetryableStatusCode(statusCode) { + if (!statusCode) { + return false; + } + const retryableStatusCodes = [ + http_client_1.HttpCodes.BadGateway, + http_client_1.HttpCodes.ServiceUnavailable, + http_client_1.HttpCodes.GatewayTimeout + ]; + return retryableStatusCodes.includes(statusCode); +} +exports.isRetryableStatusCode = isRetryableStatusCode; +function sleep(milliseconds) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => setTimeout(resolve, milliseconds)); + }); +} +function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { + return __awaiter(this, void 0, void 0, function* () { + let errorMessage = ''; + let attempt = 1; + while (attempt <= maxAttempts) { + let response = undefined; + let statusCode = undefined; + let isRetryable = false; + try { + response = yield method(); + } + catch (error) { + if (onError) { + response = onError(error); + } + isRetryable = true; + errorMessage = error.message; + } + if (response) { + statusCode = getStatusCode(response); + if (!isServerErrorStatusCode(statusCode)) { + return response; + } + } + if (statusCode) { + isRetryable = isRetryableStatusCode(statusCode); + errorMessage = `Cache service responded with ${statusCode}`; + } + core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`); + break; + } + yield sleep(delay); + attempt++; + } + throw Error(`${name} failed: ${errorMessage}`); + }); +} +exports.retry = retry; +function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, + // If the error object contains the statusCode property, extract it and return + // an TypedResponse so it can be processed by the retry logic. + (error) => { + if (error instanceof http_client_1.HttpClientError) { + return { + statusCode: error.statusCode, + result: null, + headers: {}, + error + }; + } + else { + return undefined; + } + }); + }); +} +exports.retryTypedResponse = retryTypedResponse; +function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); + }); +} +exports.retryHttpClientResponse = retryHttpClientResponse; + + +/***/ }), + +/***/ 7672: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.createTar = exports.extractTar = exports.listTar = void 0; +const exec_1 = __nccwpck_require__(1514); +const io = __importStar(__nccwpck_require__(7436)); +const fs_1 = __nccwpck_require__(7147); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const IS_WINDOWS = process.platform === 'win32'; +// Returns tar path and type: BSD or GNU +function getTarPath() { + return __awaiter(this, void 0, void 0, function* () { + switch (process.platform) { + case 'win32': { + const gnuTar = yield utils.getGnuTarPathOnWindows(); + const systemTar = constants_1.SystemTarPathOnWindows; + if (gnuTar) { + // Use GNUtar as default on windows + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else if ((0, fs_1.existsSync)(systemTar)) { + return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; + } + break; + } + case 'darwin': { + const gnuTar = yield io.which('gtar', false); + if (gnuTar) { + // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else { + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.BSD + }; + } + } + default: + break; + } + // Default assumption is GNU tar is present in path + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.GNU + }; + }); +} +// Return arguments for tar as per tarPath, compressionMethod, method type and os +function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { + return __awaiter(this, void 0, void 0, function* () { + const args = [`"${tarPath.path}"`]; + const cacheFileName = utils.getCacheFileName(compressionMethod); + const tarFile = 'cache.tar'; + const workingDirectory = getWorkingDirectory(); + // Speficic args for BSD tar on windows for workaround + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + // Method specific args + switch (type) { + case 'create': + args.push('--posix', '-cf', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); + break; + case 'extract': + args.push('-xf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); + break; + case 'list': + args.push('-tf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); + break; + } + // Platform specific args + if (tarPath.type === constants_1.ArchiveToolType.GNU) { + switch (process.platform) { + case 'win32': + args.push('--force-local'); + break; + case 'darwin': + args.push('--delay-directory-restore'); + break; + } + } + return args; + }); +} +// Returns commands to run tar and compression program +function getCommands(compressionMethod, type, archivePath = '', tarExtraArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let args; + const tarPath = yield getTarPath(); + const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); + tarArgs.push(...tarExtraArgs); + const compressionArgs = type !== 'create' + ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) + : yield getCompressionProgram(tarPath, compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + if (BSD_TAR_ZSTD && type !== 'create') { + args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; + } + else { + args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; + } + if (BSD_TAR_ZSTD) { + return args; + } + return [args.join(' ')]; + }); +} +function getWorkingDirectory() { + var _a; + return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); +} +// Common function for extractTar and listTar to get the compression method +function getDecompressionProgram(tarPath, compressionMethod, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + // -d: Decompress. + // unzstd is equivalent to 'zstd -d' + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --long=30 --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; + default: + return ['-z']; + } + }); +} +// Used for creating the archive +// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. +// zstdmt is equivalent to 'zstd -T0' +// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. +// Using 30 here because we also support 32-bit self-hosted runners. +// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. +function getCompressionProgram(tarPath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const cacheFileName = utils.getCacheFileName(compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --long=30 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; + default: + return ['-z']; + } + }); +} +// Executes all commands as separate processes +function execCommands(commands, cwd) { + return __awaiter(this, void 0, void 0, function* () { + for (const command of commands) { + try { + yield (0, exec_1.exec)(command, undefined, { + cwd, + env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) + }); + } + catch (error) { + throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); + } } - } - options.state = WriterState.CloseTag; - r += options.spaceBeforeSlash + '>' + this.endline(node, options, level); - options.state = WriterState.None; - this.closeNode(node, options, level); - return r; - }; - - XMLWriterBase.prototype.dtdNotation = function(node, options, level) { - var r; - this.openNode(node, options, level); - options.state = WriterState.OpenTag; - r = this.indent(node, options, level) + '' + this.endline(node, options, level); - options.state = WriterState.None; - this.closeNode(node, options, level); - return r; - }; - - XMLWriterBase.prototype.openNode = function(node, options, level) {}; - - XMLWriterBase.prototype.closeNode = function(node, options, level) {}; - - XMLWriterBase.prototype.openAttribute = function(att, options, level) {}; - - XMLWriterBase.prototype.closeAttribute = function(att, options, level) {}; - - return XMLWriterBase; - - })(); - -}).call(this); + }); +} +// List the contents of a tar +function listTar(archivePath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const commands = yield getCommands(compressionMethod, 'list', archivePath); + yield execCommands(commands); + }); +} +exports.listTar = listTar; +// Extract a tar +function extractTar(archivePath, compressionMethod, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + // Create directory to extract tar into + const workingDirectory = getWorkingDirectory(); + yield io.mkdirP(workingDirectory); + const commands = yield getCommands(compressionMethod, 'extract', archivePath, extraTarArgs); + yield execCommands(commands); + }); +} +exports.extractTar = extractTar; +// Create a tar +function createTar(archiveFolder, sourceDirectories, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + // Write source directories to manifest.txt to avoid command length limits + (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); + const commands = yield getCommands(compressionMethod, 'create'); + yield execCommands(commands, archiveFolder); + }); +} +exports.createTar = createTar; /***/ }), -/***/ 2958: -/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { - -// Generated by CoffeeScript 1.12.7 -(function() { - var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; - - ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; - - XMLDOMImplementation = __nccwpck_require__(8310); - - XMLDocument = __nccwpck_require__(3730); - - XMLDocumentCB = __nccwpck_require__(7356); - - XMLStringWriter = __nccwpck_require__(5913); - - XMLStreamWriter = __nccwpck_require__(8601); - - NodeType = __nccwpck_require__(9267); +/***/ 3128: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - WriterState = __nccwpck_require__(9766); +"use strict"; - module.exports.create = function(name, xmldec, doctype, options) { - var doc, root; - if (name == null) { - throw new Error("Root element needs a name."); +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; } - options = assign({}, xmldec, doctype, options); - doc = new XMLDocument(options); - root = doc.element(name); - if (!options.headless) { - doc.declaration(options); - if ((options.pubID != null) || (options.sysID != null)) { - doc.dtd(options); - } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getDownloadOptions = exports.getUploadOptions = void 0; +const core = __importStar(__nccwpck_require__(2186)); +/** + * Returns a copy of the upload options with defaults filled in. + * + * @param copy the original upload options + */ +function getUploadOptions(copy) { + const result = { + uploadConcurrency: 4, + uploadChunkSize: 32 * 1024 * 1024 + }; + if (copy) { + if (typeof copy.uploadConcurrency === 'number') { + result.uploadConcurrency = copy.uploadConcurrency; + } + if (typeof copy.uploadChunkSize === 'number') { + result.uploadChunkSize = copy.uploadChunkSize; + } } - return root; - }; - - module.exports.begin = function(options, onData, onEnd) { - var ref1; - if (isFunction(options)) { - ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; - options = {}; + core.debug(`Upload concurrency: ${result.uploadConcurrency}`); + core.debug(`Upload chunk size: ${result.uploadChunkSize}`); + return result; +} +exports.getUploadOptions = getUploadOptions; +/** + * Returns a copy of the download options with defaults filled in. + * + * @param copy the original download options + */ +function getDownloadOptions(copy) { + const result = { + useAzureSdk: false, + concurrentBlobDownloads: true, + downloadConcurrency: 8, + timeoutInMs: 30000, + segmentTimeoutInMs: 600000, + lookupOnly: false + }; + if (copy) { + if (typeof copy.useAzureSdk === 'boolean') { + result.useAzureSdk = copy.useAzureSdk; + } + if (typeof copy.concurrentBlobDownloads === 'boolean') { + result.concurrentBlobDownloads = copy.concurrentBlobDownloads; + } + if (typeof copy.downloadConcurrency === 'number') { + result.downloadConcurrency = copy.downloadConcurrency; + } + if (typeof copy.timeoutInMs === 'number') { + result.timeoutInMs = copy.timeoutInMs; + } + if (typeof copy.segmentTimeoutInMs === 'number') { + result.segmentTimeoutInMs = copy.segmentTimeoutInMs; + } + if (typeof copy.lookupOnly === 'boolean') { + result.lookupOnly = copy.lookupOnly; + } } - if (onData) { - return new XMLDocumentCB(options, onData, onEnd); - } else { - return new XMLDocument(options); + const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; + if (segmentDownloadTimeoutMins && + !isNaN(Number(segmentDownloadTimeoutMins)) && + isFinite(Number(segmentDownloadTimeoutMins))) { + result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; } - }; - - module.exports.stringWriter = function(options) { - return new XMLStringWriter(options); - }; - - module.exports.streamWriter = function(stream, options) { - return new XMLStreamWriter(stream, options); - }; - - module.exports.implementation = new XMLDOMImplementation(); - - module.exports.nodeType = NodeType; - - module.exports.writerState = WriterState; - -}).call(this); + core.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core.debug(`Download concurrency: ${result.downloadConcurrency}`); + core.debug(`Request timeout (ms): ${result.timeoutInMs}`); + core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); + core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core.debug(`Lookup only: ${result.lookupOnly}`); + return result; +} +exports.getDownloadOptions = getDownloadOptions; /***/ }), @@ -62440,35 +62645,40 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; var Inputs; (function (Inputs) { - Inputs["Key"] = "key"; - Inputs["Path"] = "path"; - Inputs["RestoreKeys"] = "restore-keys"; - Inputs["UploadChunkSize"] = "upload-chunk-size"; - Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; - Inputs["RestoreKeyHit"] = "restore-key-hit"; - Inputs["ExtraRestoreKeys"] = "extra-restore-keys"; - Inputs["GCMacos"] = "gc-macos"; + Inputs["PrimaryKey"] = "primary-key"; + Inputs["RestorePrefixesFirstMatch"] = "restore-prefixes-first-match"; + Inputs["RestorePrefixesAllMatches"] = "restore-prefixes-all-matches"; + Inputs["SkipRestoreOnHitPrimaryKey"] = "skip-restore-on-primary-key-hit"; + Inputs["FailOn"] = "fail-on"; + Inputs["Nix"] = "nix"; + Inputs["Save"] = "save"; + Inputs["Paths"] = "paths"; + Inputs["PathsMacos"] = "paths-macos"; + Inputs["PathsLinux"] = "paths-linux"; + Inputs["GCMaxStoreSize"] = "gc-max-store-size"; Inputs["GCMaxStoreSizeMacos"] = "gc-max-store-size-macos"; - Inputs["GCLinux"] = "gc-linux"; Inputs["GCMaxStoreSizeLinux"] = "gc-max-store-size-linux"; - Inputs["Token"] = "token"; Inputs["Purge"] = "purge"; - Inputs["PurgeKeys"] = "purge-keys"; - Inputs["PurgeAccessed"] = "purge-accessed"; - Inputs["PurgeAccessedMaxAge"] = "purge-accessed-max-age"; + Inputs["PurgeOverwrite"] = "purge-overwrite"; + Inputs["PurgePrefixes"] = "purge-prefixes"; + Inputs["PurgeLastAccessed"] = "purge-last-accessed"; Inputs["PurgeCreated"] = "purge-created"; - Inputs["PurgeCreatedMaxAge"] = "purge-created-max-age"; // Input for cache, save action + Inputs["UploadChunkSize"] = "upload-chunk-size"; + Inputs["Token"] = "token"; // Input for cache, save actions })(Inputs = exports.Inputs || (exports.Inputs = {})); var Outputs; (function (Outputs) { - Outputs["CacheHit"] = "cache-hit"; - Outputs["CachePrimaryKey"] = "cache-primary-key"; - Outputs["CacheMatchedKey"] = "cache-matched-key"; // Output from restore action + Outputs["PrimaryKey"] = "primary-key"; + Outputs["Hit"] = "hit"; + Outputs["HitPrimaryKey"] = "hit-primary-key"; + Outputs["HitFirstMatch"] = "hit-first-match"; + Outputs["RestoredKey"] = "restored-key"; + Outputs["RestoredKeys"] = "restored-keys"; // Output from cache, restore actions })(Outputs = exports.Outputs || (exports.Outputs = {})); var State; (function (State) { - State["CachePrimaryKey"] = "CACHE_KEY"; - State["CacheMatchedKey"] = "CACHE_RESULT"; + State["CachePrimaryKey"] = "CACHE_PRIMARY_KEY"; + State["CacheRestoredKey"] = "CACHE_RESTORED_KEY"; })(State = exports.State || (exports.State = {})); var Events; (function (Events) { @@ -62481,7 +62691,7 @@ exports.RefKey = "GITHUB_REF"; /***/ }), -/***/ 5084: +/***/ 7063: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62509,54 +62719,63 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.restoreExtraCaches = void 0; +exports.token = exports.uploadChunkSize = exports.purgeCreatedMaxAge = exports.purgeLastAccessed = exports.purgePrefixes = exports.purgeOverwrite = exports.purge = exports.gcMaxStoreSize = exports.paths = exports.save = exports.nix = exports.failOn = exports.skipRestoreOnHitPrimaryKey = exports.restorePrefixesAllMatches = exports.restorePrefixesFirstMatch = exports.primaryKey = void 0; const core = __importStar(__nccwpck_require__(2186)); const constants_1 = __nccwpck_require__(9042); -const utils = __importStar(__nccwpck_require__(6850)); -function restoreExtraCaches() { - return __awaiter(this, void 0, void 0, function* () { - const extraRestoreKeys = utils.getInputAsArray(constants_1.Inputs.ExtraRestoreKeys); - if (extraRestoreKeys.length == 0) { - return; - } - const token = core.getInput(constants_1.Inputs.Token, { required: true }); - utils.info(` - Restoring extra caches with keys: - ${utils.stringify(extraRestoreKeys)} - `); - const results = yield utils.getCachesByKeys(token, extraRestoreKeys); - utils.info(` - Found ${results.length} cache(s): - ${utils.stringify(results)} - `); - const cachePaths = utils.paths; - results.forEach((cache) => __awaiter(this, void 0, void 0, function* () { - utils.info(`Restoring a cache with the key ${cache.key}`); - if (cache.key !== undefined) { - const restoreKey = yield utils.getCacheKey({ - paths: cachePaths, - primaryKey: cache.key, - restoreKeys: [], - lookupOnly: false - }); - if (restoreKey) { - utils.info(`Restored a cache with the key ${cache.key}`); - } - } - })); - }); -} -exports.restoreExtraCaches = restoreExtraCaches; +const utils = __importStar(__nccwpck_require__(9378)); +exports.primaryKey = core.getInput(constants_1.Inputs.PrimaryKey, { required: true }); +exports.restorePrefixesFirstMatch = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesFirstMatch); +exports.restorePrefixesAllMatches = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesAllMatches); +exports.skipRestoreOnHitPrimaryKey = utils.getInputAsBool(constants_1.Inputs.SkipRestoreOnHitPrimaryKey); +exports.failOn = (() => { + var _a; + const failOnRaw = (_a = new RegExp("^(primary|first-match)\\.(miss|not-restored)$") + .exec(core.getInput(constants_1.Inputs.FailOn))) === null || _a === void 0 ? void 0 : _a.slice(1); + if (!failOnRaw) { + return; + } + const [keyType, result] = failOnRaw; + if ((keyType == "primary" || keyType == "first-match") && + (result == "miss" || result == "not-restored")) { + return { keyType, result }; + } +})(); +exports.nix = utils.getInputAsBool(constants_1.Inputs.Nix); +exports.save = utils.getInputAsBool(constants_1.Inputs.Save); +exports.paths = (exports.nix ? ["/nix/", "~/.cache/nix", "~root/.cache/nix"] : []).concat((() => { + const paths = utils.getInputAsArray(constants_1.Inputs.Paths); + const pathsPlatform = utils.getInputAsArray(utils.isLinux ? constants_1.Inputs.PathsLinux : constants_1.Inputs.PathsMacos); + if (pathsPlatform.length > 0) { + return pathsPlatform; + } + else + return paths; +})()); +exports.gcMaxStoreSize = exports.nix + ? (() => { + const gcMaxStoreSize = utils.getInputAsInt(constants_1.Inputs.GCMaxStoreSize); + const gcMaxStoreSizePlatform = utils.getInputAsInt(utils.isLinux + ? constants_1.Inputs.GCMaxStoreSizeLinux + : constants_1.Inputs.GCMaxStoreSizeMacos); + return gcMaxStoreSizePlatform + ? gcMaxStoreSizePlatform + : gcMaxStoreSize; + })() + : undefined; +exports.purge = utils.getInputAsBool(constants_1.Inputs.Purge); +exports.purgeOverwrite = (() => { + const purgeOverwrite = core.getInput(constants_1.Inputs.PurgeOverwrite); + if (!(purgeOverwrite == "always" || purgeOverwrite == "never")) { + return "default"; + } + return purgeOverwrite; +})(); +exports.purgePrefixes = utils.getInputAsArray(constants_1.Inputs.PurgePrefixes); +exports.purgeLastAccessed = utils.getInputAsInt(constants_1.Inputs.PurgeLastAccessed); +exports.purgeCreatedMaxAge = utils.getInputAsInt(constants_1.Inputs.PurgeCreated); +exports.uploadChunkSize = utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) || 32 * 1024 * 1024; +exports.token = core.getInput(constants_1.Inputs.Token, { required: true }); /***/ }), @@ -62599,131 +62818,155 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.restoreWithKey = void 0; +exports.restoreRun = exports.restoreOnlyRun = exports.restoreImpl = void 0; const core = __importStar(__nccwpck_require__(2186)); const constants_1 = __nccwpck_require__(9042); -const restoreExtraCaches_1 = __nccwpck_require__(5084); -const utils = __importStar(__nccwpck_require__(6850)); -function restoreWithKey(key, paths) { - return __awaiter(this, void 0, void 0, function* () { - utils.info(`Restoring a cache with the key "${key}".`); - utils.info(`::group::Logs are hidden. Errors are due to attempts to overwrite read-only paths.`); - yield utils.getCacheKey({ - paths, - primaryKey: key, - restoreKeys: [], - lookupOnly: false - }); - utils.info(`::endgroup::`); - utils.info(`Finished restoring the cache.`); - }); -} -exports.restoreWithKey = restoreWithKey; +const inputs = __importStar(__nccwpck_require__(7063)); +const stateProvider_1 = __nccwpck_require__(1527); +const utils = __importStar(__nccwpck_require__(4427)); +const restore = __importStar(__nccwpck_require__(8486)); function restoreImpl(stateProvider) { + var _a, _b, _c, _d, _e, _f, _g; return __awaiter(this, void 0, void 0, function* () { try { + core.setOutput(constants_1.Outputs.Hit, false); + core.setOutput(constants_1.Outputs.HitPrimaryKey, false); + core.setOutput(constants_1.Outputs.HitFirstMatch, false); + core.setOutput(constants_1.Outputs.RestoredKey, false); + core.setOutput(constants_1.Outputs.RestoredKeys, []); if (!utils.isCacheFeatureAvailable()) { - core.setOutput(constants_1.Outputs.CacheHit, "false"); return; } // Validate inputs, this can cause task failure if (!utils.isValidEvent()) { throw new Error(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`); } - const primaryKey = core.getInput(constants_1.Inputs.Key, { required: true }); - stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey); - const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys); - const cachePaths = utils.paths; - const failOnCacheMiss = utils.getInputAsBool(constants_1.Inputs.FailOnCacheMiss); - const restoreKeyHit = utils.getInputAsBool(constants_1.Inputs.RestoreKeyHit); - utils.info(`Searching for a cache with the key "${primaryKey}".`); - let cacheKey = yield utils.getCacheKey({ - paths: cachePaths, - primaryKey, - restoreKeys: [], - lookupOnly: true - }); - if (cacheKey) { - yield restoreWithKey(cacheKey, cachePaths); + let restoredKey; + let lookedUpKey; + const restoredKeys = []; + const errorNot = (message) => new Error(` + No cache with the given key ${message}. + Exiting as the input "${constants_1.Inputs.FailOn}" is set. + `); + const errorNotFound = errorNot("was found"); + const errorNotRestored = errorNot("could be restored"); + { + const primaryKey = inputs.primaryKey; + stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey); + utils.info(`Searching for a cache with the key "${primaryKey}".`); + lookedUpKey = yield utils.restoreCache({ + primaryKey, + restoreKeys: [], + lookupOnly: true + }); + if (!lookedUpKey) { + if (((_a = inputs.failOn) === null || _a === void 0 ? void 0 : _a.keyType) == "primary" && + ((_b = inputs.failOn) === null || _b === void 0 ? void 0 : _b.result) == "miss") { + throw errorNotFound; + } + else { + utils.info(`Could not find a cache.`); + } + } + if (lookedUpKey && utils.isExactKeyMatch(primaryKey, lookedUpKey)) { + utils.info(`Found a cache with the given "${constants_1.Inputs.PrimaryKey}".`); + core.setOutput(constants_1.Outputs.HitPrimaryKey, true); + if (!inputs.skipRestoreOnHitPrimaryKey) { + restoredKey = yield restore.restoreCache(primaryKey); + if (restoredKey) { + restoredKeys.push(...[restoredKey]); + } + else if (((_c = inputs.failOn) === null || _c === void 0 ? void 0 : _c.keyType) == "primary" && + ((_d = inputs.failOn) === null || _d === void 0 ? void 0 : _d.result) == "not-restored") { + throw errorNotRestored; + } + } + } } - else { + if (inputs.restorePrefixesFirstMatch.length > 0 && + !restoredKey && + !(inputs.skipRestoreOnHitPrimaryKey && lookedUpKey)) { utils.info(` - No cache with the given primary key found. - Searching for a cache using restore keys: - ${JSON.stringify(restoreKeys)} + Searching for a cache using the "${constants_1.Inputs.RestorePrefixesFirstMatch}": + ${JSON.stringify(inputs.restorePrefixesFirstMatch)} `); - const restoreKey = yield utils.getCacheKey({ - paths: cachePaths, + const foundKey = yield utils.restoreCache({ primaryKey: "", - restoreKeys, + restoreKeys: inputs.restorePrefixesFirstMatch, lookupOnly: true }); - if (restoreKey) { - yield restoreWithKey(restoreKey, cachePaths); + if (!foundKey) { + if (((_e = inputs.failOn) === null || _e === void 0 ? void 0 : _e.keyType) == "first-match" && + inputs.failOn.result == "miss") { + throw errorNotFound; + } + else { + utils.info(`Could not find a cache.`); + } } - if (restoreKeyHit) { - cacheKey = restoreKey; + if (foundKey) { + utils.info(`Found a cache using the "${constants_1.Inputs.RestorePrefixesFirstMatch}".`); + core.setOutput(constants_1.Outputs.HitFirstMatch, true); + restoredKey = yield restore.restoreCache(foundKey); + if (restoredKey) { + restoredKeys.push(...[restoredKey]); + } + else if (((_f = inputs.failOn) === null || _f === void 0 ? void 0 : _f.keyType) == "first-match" && + ((_g = inputs.failOn) === null || _g === void 0 ? void 0 : _g.result) == "not-restored") { + throw errorNotRestored; + } } } - if (!cacheKey) { - if (failOnCacheMiss) { - throw new Error(` - Failed to restore a cache with the key "${primaryKey}". - Exiting as ${constants_1.Inputs.FailOnCacheMiss} is set. - `); - } - utils.info(` - Cache not found for input keys: - ${utils.stringify([primaryKey, ...restoreKeys])} - `); - yield (0, restoreExtraCaches_1.restoreExtraCaches)(); - return; + if (!(inputs.skipRestoreOnHitPrimaryKey && lookedUpKey)) { + restoredKeys.push(...(yield restore.restoreCaches())); } + restoredKey || (restoredKey = ""); // Store the matched cache key in states - stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey); - const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey) || restoreKeyHit; - core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString()); - utils.info(`Cache restored from key: "${cacheKey}"`); - yield (0, restoreExtraCaches_1.restoreExtraCaches)(); - return cacheKey; + stateProvider.setState(constants_1.State.CacheRestoredKey, restoredKey); + core.setOutput(constants_1.Outputs.Hit, true); + core.setOutput(constants_1.Outputs.RestoredKey, restoredKey); + core.setOutput(constants_1.Outputs.RestoredKeys, restoredKeys); + return restoredKey; } catch (error) { core.setFailed(error.message); } }); } -exports["default"] = restoreImpl; - - -/***/ }), - -/***/ 5743: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); +exports.restoreImpl = restoreImpl; +function run(stateProvider, earlyExit) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield restoreImpl(stateProvider); + } + catch (err) { + console.error(err); + if (earlyExit) { + process.exit(1); + } + } + // node will stay alive if any promises are not resolved, + // which is a possibility if HTTP requests are dangling + // due to retries or timeouts. We know that if we got here + // that all promises that we care about have successfully + // resolved, so simply exit with success. + if (earlyExit) { + process.exit(0); + } }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const restoreImpl_1 = __importDefault(__nccwpck_require__(2357)); -const stateProvider_1 = __nccwpck_require__(1527); -function run() { +} +function restoreOnlyRun(earlyExit) { + return __awaiter(this, void 0, void 0, function* () { + yield run(new stateProvider_1.NullStateProvider(), earlyExit); + }); +} +exports.restoreOnlyRun = restoreOnlyRun; +function restoreRun(earlyExit) { return __awaiter(this, void 0, void 0, function* () { - yield (0, restoreImpl_1.default)(new stateProvider_1.NullStateProvider()); + yield run(new stateProvider_1.StateProvider(), earlyExit); }); } -run(); -exports["default"] = run; +exports.restoreRun = restoreRun; /***/ }), @@ -62768,7 +63011,7 @@ class StateProviderBase { this.getState = (key) => ""; } getCacheState() { - const cacheKey = this.getState(constants_1.State.CacheMatchedKey); + const cacheKey = this.getState(constants_1.State.CacheRestoredKey); if (cacheKey) { core.debug(`Cache state/key: ${cacheKey}`); return cacheKey; @@ -62788,8 +63031,8 @@ class NullStateProvider extends StateProviderBase { constructor() { super(...arguments); this.stateToOutputMap = new Map([ - [constants_1.State.CacheMatchedKey, constants_1.Outputs.CacheMatchedKey], - [constants_1.State.CachePrimaryKey, constants_1.Outputs.CachePrimaryKey] + [constants_1.State.CacheRestoredKey, constants_1.Outputs.RestoredKey], + [constants_1.State.CachePrimaryKey, constants_1.Outputs.PrimaryKey] ]); this.setState = (key, value) => { core.setOutput(this.stateToOutputMap.get(key), value); @@ -62803,7 +63046,7 @@ exports.NullStateProvider = NullStateProvider; /***/ }), -/***/ 6850: +/***/ 4427: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62844,12 +63087,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.filterCachesByTime = exports.getCachesByKeys = exports.getCacheKey = exports.paths = exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; -const cache = __importStar(__nccwpck_require__(7799)); +exports.warning = exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.getCachesByKeys = exports.restoreCache = exports.isCacheFeatureAvailable = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; +const cache = __importStar(__nccwpck_require__(7942)); const core = __importStar(__nccwpck_require__(2186)); const github = __importStar(__nccwpck_require__(5438)); const dedent_1 = __importDefault(__nccwpck_require__(5281)); const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); function isGhes() { const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com"); return ghUrl.hostname.toUpperCase() !== "GITHUB.COM"; @@ -62864,7 +63108,7 @@ function isExactKeyMatch(key, cacheKey) { exports.isExactKeyMatch = isExactKeyMatch; function logWarning(message) { const warningPrefix = "[warning]"; - core.info(`${warningPrefix}${message}`); + core.warning(`${warningPrefix} ${message}`); } exports.logWarning = logWarning; function logError(message) { @@ -62873,58 +63117,39 @@ function logError(message) { } exports.logError = logError; // Cache token authorized for all events that are tied to a ref -// See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context +// See GitHub Context https://docs.github.com/en/actions/learn-github-actions/contexts function isValidEvent() { return constants_1.RefKey in process.env && Boolean(process.env[constants_1.RefKey]); } exports.isValidEvent = isValidEvent; -function getInputAsArray(name, options) { - return core - .getInput(name, options) - .split("\n") - .map(s => s.replace(/^!\s+/, "!").trim()) - .filter(x => x !== ""); -} -exports.getInputAsArray = getInputAsArray; -function getInputAsInt(name, options) { - const value = parseInt(core.getInput(name, options)); - if (isNaN(value) || value < 0) { - return undefined; - } - return value; -} -exports.getInputAsInt = getInputAsInt; -function getInputAsBool(name, options) { - const result = core.getInput(name, options); - return result.toLowerCase() === "true"; -} -exports.getInputAsBool = getInputAsBool; function isCacheFeatureAvailable() { if (cache.isFeatureAvailable()) { return true; } if (isGhes()) { - logWarning(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. + logError(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`); return false; } - logWarning("An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions."); + logError(` + Actions cache service is unavailable. + Please check https://www.githubstatus.com/ for any ongoing issue in Actions. + `); return false; } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; -exports.paths = ["/nix/", "~/.cache/nix", "~root/.cache/nix"]; -function getCacheKey({ paths, primaryKey, restoreKeys, lookupOnly }) { +function restoreCache({ primaryKey, restoreKeys, lookupOnly }) { return __awaiter(this, void 0, void 0, function* () { return yield cache.restoreCache( // https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 - paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false); + inputs.paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false, ["--skip-old-files"]); }); } -exports.getCacheKey = getCacheKey; -function getCachesByKeys(token, keys) { +exports.restoreCache = restoreCache; +function getCachesByKeys(keys) { return __awaiter(this, void 0, void 0, function* () { const caches = []; - const octokit = github.getOctokit(token); + const octokit = github.getOctokit(inputs.token); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; for (let page = 1; page <= 500; page += 1) { @@ -62939,32 +63164,22 @@ function getCachesByKeys(token, keys) { if (cachesRequest.actions_caches.length == 0) { break; } - caches.push(...cachesRequest.actions_caches); + if (isExactKeyMatch(inputs.primaryKey, key)) { + caches.push(...cachesRequest.actions_caches); + } } } return caches; }); } exports.getCachesByKeys = getCachesByKeys; -const filterCachesByTime = ({ caches, doUseLastAccessedTime, maxDate }) => caches.filter(cache => { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - return atDate < maxDate; - } - else - return false; -}); -exports.filterCachesByTime = filterCachesByTime; -const mkMessageWrongValue = (input, value) => `Wrong value for the input '${input}': ${value}`; +const mkMessageWrongValue = (input, value) => `Wrong value for the input "${input}": ${value}`; exports.mkMessageWrongValue = mkMessageWrongValue; function getMaxDate({ doUseLastAccessedTime, time }) { const inputMaxAge = doUseLastAccessedTime - ? constants_1.Inputs.PurgeAccessedMaxAge - : constants_1.Inputs.PurgeCreatedMaxAge; - const maxAge = core.getInput(inputMaxAge, { required: false }); + ? constants_1.Inputs.PurgeLastAccessed + : constants_1.Inputs.PurgeCreated; + const maxAge = core.getInput(inputMaxAge); const maxDate = new Date(time - Number.parseInt(maxAge) * 1000); if (maxDate === null) { throw new Error((0, exports.mkMessageWrongValue)(inputMaxAge, maxAge)); @@ -62974,10 +63189,161 @@ function getMaxDate({ doUseLastAccessedTime, time }) { exports.getMaxDate = getMaxDate; const stringify = (value) => JSON.stringify(value, null, 2); exports.stringify = stringify; -const info = (message) => { - core.info(dedent_1.default.withOptions({})(message)); -}; +const myDedent = dedent_1.default.withOptions({}); +const info = (message) => core.info(myDedent(message)); exports.info = info; +const warning = (message) => core.warning(myDedent(message)); +exports.warning = warning; + + +/***/ }), + +/***/ 9378: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isLinux = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = void 0; +const core = __importStar(__nccwpck_require__(2186)); +function getInputAsArray(name, options) { + return core + .getInput(name, options) + .split("\n") + .map(s => s.replace(/^!\s+/, "!").trim()) + .filter(x => x !== ""); +} +exports.getInputAsArray = getInputAsArray; +function getInputAsInt(name, options) { + const value = parseInt(core.getInput(name, options)); + if (isNaN(value) || value < 0) { + return undefined; + } + return value; +} +exports.getInputAsInt = getInputAsInt; +function getInputAsBool(name, options) { + const result = core.getInput(name, options); + return result.toLowerCase() === "true"; +} +exports.getInputAsBool = getInputAsBool; +exports.isLinux = process.platform === "linux"; + + +/***/ }), + +/***/ 8486: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.restoreCaches = exports.restoreCache = void 0; +const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +function restoreCache(key) { + return __awaiter(this, void 0, void 0, function* () { + utils.info(`Restoring a cache with the key "${key}".`); + const cacheKey = yield utils.restoreCache({ + primaryKey: key, + restoreKeys: [], + lookupOnly: false + }); + if (cacheKey) { + utils.info(`Finished restoring the cache.`); + return cacheKey; + } + else { + utils.info(`Failed to restore the cache.`); + } + }); +} +exports.restoreCache = restoreCache; +function restoreCaches() { + return __awaiter(this, void 0, void 0, function* () { + const restoredCaches = []; + if (inputs.restorePrefixesAllMatches.length == 0) { + return restoredCaches; + } + utils.info(` + Searching for caches using the "${constants_1.Inputs.RestorePrefixesAllMatches}": + + ${utils.stringify(inputs.restorePrefixesAllMatches)} + `); + const caches = yield utils.getCachesByKeys(inputs.restorePrefixesAllMatches); + utils.info(` + Found ${caches.length} cache(s): + + ${utils.stringify(caches)} + `); + for (const cache of caches) { + if (cache.key) { + const cacheKey = yield restoreCache(cache.key); + if (cacheKey) { + restoredCaches.push(...[cacheKey]); + } + } + } + return restoredCaches; + }); +} +exports.restoreCaches = restoreCaches; /***/ }), @@ -63196,12 +63562,18 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; /******/ /************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module is referenced by other modules so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(5743); -/******/ module.exports = __webpack_exports__; -/******/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +var exports = __webpack_exports__; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +const restoreImpl_1 = __nccwpck_require__(2357); +(0, restoreImpl_1.restoreOnlyRun)(true); + +})(); + +module.exports = __webpack_exports__; /******/ })() ; \ No newline at end of file diff --git a/dist/restore/index.js b/dist/restore/index.js index 9a61fa8e..0855e7eb 100644 --- a/dist/restore/index.js +++ b/dist/restore/index.js @@ -1,1815 +1,886 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 7799: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +/***/ 1898: +/***/ ((module, exports) => { -"use strict"; +exports = module.exports = SemVer -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const cacheHttpClient = __importStar(__nccwpck_require__(8245)); -const tar_1 = __nccwpck_require__(6490); -class ValidationError extends Error { - constructor(message) { - super(message); - this.name = 'ValidationError'; - Object.setPrototypeOf(this, ValidationError.prototype); - } -} -exports.ValidationError = ValidationError; -class ReserveCacheError extends Error { - constructor(message) { - super(message); - this.name = 'ReserveCacheError'; - Object.setPrototypeOf(this, ReserveCacheError.prototype); - } -} -exports.ReserveCacheError = ReserveCacheError; -function checkPaths(paths) { - if (!paths || paths.length === 0) { - throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); - } -} -function checkKey(key) { - if (key.length > 512) { - throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); - } - const regex = /^[^,]*$/; - if (!regex.test(key)) { - throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); - } -} -/** - * isFeatureAvailable to check the presence of Actions cache service - * - * @returns boolean return true if Actions cache service feature is available, otherwise false - */ -function isFeatureAvailable() { - return !!process.env['ACTIONS_CACHE_URL']; -} -exports.isFeatureAvailable = isFeatureAvailable; -/** - * Restores cache from keys - * - * @param paths a list of file paths to restore from the cache - * @param primaryKey an explicit key for restoring the cache - * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key - * @param downloadOptions cache download options - * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform - * @returns string returns the key for the cache hit, otherwise returns undefined - */ -function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - restoreKeys = restoreKeys || []; - const keys = [primaryKey, ...restoreKeys]; - core.debug('Resolved Keys:'); - core.debug(JSON.stringify(keys)); - if (keys.length > 10) { - throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); - } - for (const key of keys) { - checkKey(key); - } - const compressionMethod = yield utils.getCompressionMethod(); - let archivePath = ''; - try { - // path are needed to compute version - const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { - compressionMethod, - enableCrossOsArchive - }); - if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { - // Cache not found - return undefined; - } - if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core.info('Lookup only - skipping download'); - return cacheEntry.cacheKey; - } - archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - // Download the cache from the cache entry - yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core.info('Cache restored successfully'); - return cacheEntry.cacheKey; - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else { - // Supress all non-validation cache related errors because caching should be optional - core.warning(`Failed to restore: ${error.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return undefined; - }); -} -exports.restoreCache = restoreCache; -/** - * Saves a list of files with the specified key - * - * @param paths a list of file paths to be cached - * @param key an explicit key for restoring the cache - * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform - * @param options cache upload options - * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails - */ -function saveCache(paths, key, options, enableCrossOsArchive = false) { - var _a, _b, _c, _d, _e; - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - checkKey(key); - const compressionMethod = yield utils.getCompressionMethod(); - let cacheId = -1; - const cachePaths = yield utils.resolvePaths(paths); - core.debug('Cache Paths:'); - core.debug(`${JSON.stringify(cachePaths)}`); - if (cachePaths.length === 0) { - throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); - } - const archiveFolder = yield utils.createTempDirectory(); - const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - try { - yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.debug(`File Size: ${archiveFileSize}`); - // For GHES, this check will take place in ReserveCache API with enterprise file size limit - if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { - throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); - } - core.debug('Reserving Cache'); - const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { - compressionMethod, - enableCrossOsArchive, - cacheSize: archiveFileSize - }); - if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { - cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; - } - else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { - throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); - } - else { - throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); - } - core.debug(`Saving Cache (ID: ${cacheId})`); - yield cacheHttpClient.saveCache(cacheId, archivePath, options); - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else if (typedError.name === ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); - } - else { - core.warning(`Failed to save: ${typedError.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return cacheId; - }); +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} } -exports.saveCache = saveCache; -//# sourceMappingURL=cache.js.map -/***/ }), +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' -/***/ 8245: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 -"use strict"; +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const auth_1 = __nccwpck_require__(5526); -const crypto = __importStar(__nccwpck_require__(6113)); -const fs = __importStar(__nccwpck_require__(7147)); -const url_1 = __nccwpck_require__(7310); -const utils = __importStar(__nccwpck_require__(1518)); -const downloadUtils_1 = __nccwpck_require__(5500); -const options_1 = __nccwpck_require__(6215); -const requestUtils_1 = __nccwpck_require__(3981); -const versionSalt = '1.0'; -function getCacheApiUrl(resource) { - const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; - if (!baseUrl) { - throw new Error('Cache Service Url not found, unable to restore cache.'); - } - const url = `${baseUrl}_apis/artifactcache/${resource}`; - core.debug(`Resource Url: ${url}`); - return url; -} -function createAcceptHeader(type, apiVersion) { - return `${type};api-version=${apiVersion}`; -} -function getRequestOptions() { - const requestOptions = { - headers: { - Accept: createAcceptHeader('application/json', '6.0-preview.1') - } - }; - return requestOptions; -} -function createHttpClient() { - const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; - const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); - return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); -} -function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { - const components = paths; - // Add compression method to cache version to restore - // compressed cache as per compression method - if (compressionMethod) { - components.push(compressionMethod); - } - // Only check for windows platforms if enableCrossOsArchive is false - if (process.platform === 'win32' && !enableCrossOsArchive) { - components.push('windows-only'); - } - // Add salt to cache version to support breaking changes in cache entry - components.push(versionSalt); - return crypto - .createHash('sha256') - .update(components.join('|')) - .digest('hex'); -} -exports.getCacheVersion = getCacheVersion; -function getCacheEntry(keys, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - // Cache not found - if (response.statusCode === 204) { - // List cache for primary key only if cache miss occurs - if (core.isDebug()) { - yield printCachesListForDiagnostics(keys[0], httpClient, version); - } - return null; - } - if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { - throw new Error(`Cache service responded with ${response.statusCode}`); - } - const cacheResult = response.result; - const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; - if (!cacheDownloadUrl) { - // Cache achiveLocation not found. This should never happen, and hence bail out. - throw new Error('Cache not found.'); - } - core.setSecret(cacheDownloadUrl); - core.debug(`Cache Result:`); - core.debug(JSON.stringify(cacheResult)); - return cacheResult; - }); -} -exports.getCacheEntry = getCacheEntry; -function printCachesListForDiagnostics(key, httpClient, version) { - return __awaiter(this, void 0, void 0, function* () { - const resource = `caches?key=${encodeURIComponent(key)}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - if (response.statusCode === 200) { - const cacheListResult = response.result; - const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; - if (totalCount && totalCount > 0) { - core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); - for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); - } - } - } - }); -} -function downloadCache(archiveLocation, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const archiveUrl = new url_1.URL(archiveLocation); - const downloadOptions = (0, options_1.getDownloadOptions)(options); - if (downloadOptions.useAzureSdk && - archiveUrl.hostname.endsWith('.blob.core.windows.net')) { - // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. - yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); - } - else { - // Otherwise, download using the Actions http-client. - yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); - } - }); -} -exports.downloadCache = downloadCache; -// Reserve Cache -function reserveCache(key, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const reserveCacheRequest = { - key, - version, - cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize - }; - const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); - })); - return response; - }); -} -exports.reserveCache = reserveCache; -function getContentRange(start, end) { - // Format: `bytes start-end/filesize - // start and end are inclusive - // filesize can be * - // For a 200 byte chunk starting at byte 0: - // Content-Range: bytes 0-199/* - return `bytes ${start}-${end}/*`; -} -function uploadChunk(httpClient, resourceUrl, openStream, start, end) { - return __awaiter(this, void 0, void 0, function* () { - core.debug(`Uploading chunk of size ${end - - start + - 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); - const additionalHeaders = { - 'Content-Type': 'application/octet-stream', - 'Content-Range': getContentRange(start, end) - }; - const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { - return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); - })); - if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { - throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); - } - }); -} -function uploadFile(httpClient, cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - // Upload Chunks - const fileSize = utils.getArchiveFileSizeInBytes(archivePath); - const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs.openSync(archivePath, 'r'); - const uploadOptions = (0, options_1.getUploadOptions)(options); - const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); - const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); - const parallelUploads = [...new Array(concurrency).keys()]; - core.debug('Awaiting all uploads'); - let offset = 0; - try { - yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { - while (offset < fileSize) { - const chunkSize = Math.min(fileSize - offset, maxChunkSize); - const start = offset; - const end = offset + chunkSize - 1; - offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs - .createReadStream(archivePath, { - fd, - start, - end, - autoClose: false - }) - .on('error', error => { - throw new Error(`Cache upload failed because file read failed with ${error.message}`); - }), start, end); - } - }))); - } - finally { - fs.closeSync(fd); - } - return; - }); -} -function commitCache(httpClient, cacheId, filesize) { - return __awaiter(this, void 0, void 0, function* () { - const commitCacheRequest = { size: filesize }; - return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); - })); - }); -} -function saveCache(cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - core.debug('Upload cache'); - yield uploadFile(httpClient, cacheId, archivePath, options); - // Commit Cache - core.debug('Commiting cache'); - const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); - const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); - if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { - throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); - } - core.info('Cache saved successfully'); - }); +var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + +// The actual regexps go on exports.re +var re = exports.re = [] +var safeRe = exports.safeRe = [] +var src = exports.src = [] +var t = exports.tokens = {} +var R = 0 + +function tok (n) { + t[n] = R++ } -exports.saveCache = saveCache; -//# sourceMappingURL=cacheHttpClient.js.map -/***/ }), +var LETTERDASHNUMBER = '[a-zA-Z0-9-]' -/***/ 1518: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +var safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] -"use strict"; +function makeSafeRe (value) { + for (var i = 0; i < safeRegexReplacements.length; i++) { + var token = safeRegexReplacements[i][0] + var max = safeRegexReplacements[i][1] + value = value + .split(token + '*').join(token + '{0,' + max + '}') + .split(token + '+').join(token + '{1,' + max + '}') + } + return value +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const exec = __importStar(__nccwpck_require__(1514)); -const glob = __importStar(__nccwpck_require__(8090)); -const io = __importStar(__nccwpck_require__(7436)); -const fs = __importStar(__nccwpck_require__(7147)); -const path = __importStar(__nccwpck_require__(1017)); -const semver = __importStar(__nccwpck_require__(3771)); -const util = __importStar(__nccwpck_require__(3837)); -const uuid_1 = __nccwpck_require__(2155); -const constants_1 = __nccwpck_require__(8840); -// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 -function createTempDirectory() { - return __awaiter(this, void 0, void 0, function* () { - const IS_WINDOWS = process.platform === 'win32'; - let tempDirectory = process.env['RUNNER_TEMP'] || ''; - if (!tempDirectory) { - let baseLocation; - if (IS_WINDOWS) { - // On Windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } - else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } - else { - baseLocation = '/home'; - } - } - tempDirectory = path.join(baseLocation, 'actions', 'temp'); - } - const dest = path.join(tempDirectory, (0, uuid_1.v4)()); - yield io.mkdirP(dest); - return dest; - }); -} -exports.createTempDirectory = createTempDirectory; -function getArchiveFileSizeInBytes(filePath) { - return fs.statSync(filePath).size; -} -exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; -function resolvePaths(patterns) { - var e_1, _a; - var _b; - return __awaiter(this, void 0, void 0, function* () { - const paths = []; - const workspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); - const globber = yield glob.create(patterns.join('\n'), { - implicitDescendants: false - }); - try { - for (var _c = __asyncValues(globber.globGenerator()), _d; _d = yield _c.next(), !_d.done;) { - const file = _d.value; - const relativeFile = path - .relative(workspace, file) - .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); - core.debug(`Matched: ${relativeFile}`); - // Paths are made relative so the tar entries are all relative to the root of the workspace. - if (relativeFile === '') { - // path.relative returns empty string if workspace and file are equal - paths.push('.'); - } - else { - paths.push(`${relativeFile}`); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } - return paths; - }); -} -exports.resolvePaths = resolvePaths; -function unlinkFile(filePath) { - return __awaiter(this, void 0, void 0, function* () { - return util.promisify(fs.unlink)(filePath); - }); -} -exports.unlinkFile = unlinkFile; -function getVersion(app, additionalArgs = []) { - return __awaiter(this, void 0, void 0, function* () { - let versionOutput = ''; - additionalArgs.push('--version'); - core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); - try { - yield exec.exec(`${app}`, additionalArgs, { - ignoreReturnCode: true, - silent: true, - listeners: { - stdout: (data) => (versionOutput += data.toString()), - stderr: (data) => (versionOutput += data.toString()) - } - }); - } - catch (err) { - core.debug(err.message); - } - versionOutput = versionOutput.trim(); - core.debug(versionOutput); - return versionOutput; - }); +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +tok('NUMERICIDENTIFIER') +src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' +tok('NUMERICIDENTIFIERLOOSE') +src[t.NUMERICIDENTIFIERLOOSE] = '\\d+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +tok('NONNUMERICIDENTIFIER') +src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*' + +// ## Main Version +// Three dot-separated numeric identifiers. + +tok('MAINVERSION') +src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')' + +tok('MAINVERSIONLOOSE') +src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +tok('PRERELEASEIDENTIFIER') +src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +tok('PRERELEASEIDENTIFIERLOOSE') +src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +tok('PRERELEASE') +src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' + +tok('PRERELEASELOOSE') +src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +tok('BUILDIDENTIFIER') +src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+' + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +tok('BUILD') +src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + + '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +tok('FULL') +tok('FULLPLAIN') +src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + + src[t.PRERELEASE] + '?' + + src[t.BUILD] + '?' + +src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +tok('LOOSEPLAIN') +src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + + src[t.PRERELEASELOOSE] + '?' + + src[t.BUILD] + '?' + +tok('LOOSE') +src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + +tok('GTLT') +src[t.GTLT] = '((?:<|>)?=?)' + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +tok('XRANGEIDENTIFIERLOOSE') +src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +tok('XRANGEIDENTIFIER') +src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + +tok('XRANGEPLAIN') +src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:' + src[t.PRERELEASE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGEPLAINLOOSE') +src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[t.PRERELEASELOOSE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGE') +src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' +tok('XRANGELOOSE') +src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +tok('COERCE') +src[t.COERCE] = '(^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' +tok('COERCERTL') +re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') +safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g') + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +tok('LONETILDE') +src[t.LONETILDE] = '(?:~>?)' + +tok('TILDETRIM') +src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' +re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') +safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g') +var tildeTrimReplace = '$1~' + +tok('TILDE') +src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' +tok('TILDELOOSE') +src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +tok('LONECARET') +src[t.LONECARET] = '(?:\\^)' + +tok('CARETTRIM') +src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' +re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') +safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g') +var caretTrimReplace = '$1^' + +tok('CARET') +src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' +tok('CARETLOOSE') +src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +tok('COMPARATORLOOSE') +src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' +tok('COMPARATOR') +src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +tok('COMPARATORTRIM') +src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + + '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' + +// this one has to use the /g flag +re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') +safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +tok('HYPHENRANGE') +src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAIN] + ')' + + '\\s*$' + +tok('HYPHENRANGELOOSE') +src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s*$' + +// Star ranges basically just allow anything at all. +tok('STAR') +src[t.STAR] = '(<|>)?=?\\s*\\*' + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + safeRe[i] = new RegExp(makeSafeRe(src[i])) + } } -// Use zstandard if possible to maximize cache performance -function getCompressionMethod() { - return __awaiter(this, void 0, void 0, function* () { - const versionOutput = yield getVersion('zstd', ['--quiet']); - const version = semver.clean(versionOutput); - core.debug(`zstd version: ${version}`); - if (versionOutput === '') { - return constants_1.CompressionMethod.Gzip; - } - else { - return constants_1.CompressionMethod.ZstdWithoutLong; - } - }); + +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } } -exports.getCompressionMethod = getCompressionMethod; -function getCacheFileName(compressionMethod) { - return compressionMethod === constants_1.CompressionMethod.Gzip - ? constants_1.CacheFilename.Gzip - : constants_1.CacheFilename.Zstd; + +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null } -exports.getCacheFileName = getCacheFileName; -function getGnuTarPathOnWindows() { - return __awaiter(this, void 0, void 0, function* () { - if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { - return constants_1.GnuTarPathOnWindows; - } - const versionOutput = yield getVersion('tar'); - return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; - }); + +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null } -exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; -function assertDefined(name, value) { - if (value === undefined) { - throw Error(`Expected ${name} but value was undefiend`); + +exports.SemVer = SemVer + +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } - return value; -} -exports.assertDefined = assertDefined; -function isGhes() { - const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; -} -exports.isGhes = isGhes; -//# sourceMappingURL=cacheUtils.js.map + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } -/***/ }), + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } -/***/ 8840: -/***/ ((__unused_webpack_module, exports) => { + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } -"use strict"; + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; -var CacheFilename; -(function (CacheFilename) { - CacheFilename["Gzip"] = "cache.tgz"; - CacheFilename["Zstd"] = "cache.tzst"; -})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); -var CompressionMethod; -(function (CompressionMethod) { - CompressionMethod["Gzip"] = "gzip"; - // Long range mode was added to zstd in v1.3.2. - // This enum is for earlier version of zstd that does not have --long support - CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; - CompressionMethod["Zstd"] = "zstd"; -})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); -var ArchiveToolType; -(function (ArchiveToolType) { - ArchiveToolType["GNU"] = "gnu"; - ArchiveToolType["BSD"] = "bsd"; -})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); -// The default number of retry attempts. -exports.DefaultRetryAttempts = 2; -// The default delay in milliseconds between retry attempts. -exports.DefaultRetryDelay = 5000; -// Socket timeout in milliseconds during download. If no traffic is received -// over the socket during this period, the socket is destroyed and the download -// is aborted. -exports.SocketTimeout = 5000; -// The default path of GNUtar on hosted Windows runners -exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; -// The default path of BSDtar on hosted Windows runners -exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; -exports.TarFilename = 'cache.tar'; -exports.ManifestFilename = 'manifest.txt'; -//# sourceMappingURL=constants.js.map + var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]) -/***/ }), + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } -/***/ 5500: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + this.raw = version -"use strict"; + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.downloadCacheStorageSDK = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const storage_blob_1 = __nccwpck_require__(4100); -const buffer = __importStar(__nccwpck_require__(4300)); -const fs = __importStar(__nccwpck_require__(7147)); -const stream = __importStar(__nccwpck_require__(2781)); -const util = __importStar(__nccwpck_require__(3837)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const requestUtils_1 = __nccwpck_require__(3981); -const abort_controller_1 = __nccwpck_require__(2557); -/** - * Pipes the body of a HTTP response to a stream - * - * @param response the HTTP response - * @param output the writable stream - */ -function pipeResponseToStream(response, output) { - return __awaiter(this, void 0, void 0, function* () { - const pipeline = util.promisify(stream.pipeline); - yield pipeline(response.message, output); - }); -} -/** - * Class for tracking the download state and displaying stats. - */ -class DownloadProgress { - constructor(contentLength) { - this.contentLength = contentLength; - this.segmentIndex = 0; - this.segmentSize = 0; - this.segmentOffset = 0; - this.receivedBytes = 0; - this.displayedComplete = false; - this.startTime = Date.now(); - } - /** - * Progress to the next segment. Only call this method when the previous segment - * is complete. - * - * @param segmentSize the length of the next segment - */ - nextSegment(segmentSize) { - this.segmentOffset = this.segmentOffset + this.segmentSize; - this.segmentIndex = this.segmentIndex + 1; - this.segmentSize = segmentSize; - this.receivedBytes = 0; - core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); - } - /** - * Sets the number of bytes received for the current segment. - * - * @param receivedBytes the number of bytes received - */ - setReceivedBytes(receivedBytes) { - this.receivedBytes = receivedBytes; - } - /** - * Returns the total number of bytes transferred. - */ - getTransferredBytes() { - return this.segmentOffset + this.receivedBytes; - } - /** - * Returns true if the download is complete. - */ - isDone() { - return this.getTransferredBytes() === this.contentLength; - } - /** - * Prints the current download stats. Once the download completes, this will print one - * last line and then stop. - */ - display() { - if (this.displayedComplete) { - return; - } - const transferredBytes = this.segmentOffset + this.receivedBytes; - const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); - const elapsedTime = Date.now() - this.startTime; - const downloadSpeed = (transferredBytes / - (1024 * 1024) / - (elapsedTime / 1000)).toFixed(1); - core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); - if (this.isDone()) { - this.displayedComplete = true; - } - } - /** - * Returns a function used to handle TransferProgressEvents. - */ - onProgress() { - return (progress) => { - this.setReceivedBytes(progress.loadedBytes); - }; - } - /** - * Starts the timer that displays the stats. - * - * @param delayInMs the delay between each write - */ - startDisplayTimer(delayInMs = 1000) { - const displayCallback = () => { - this.display(); - if (!this.isDone()) { - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - }; - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - /** - * Stops the timer that displays the stats. As this typically indicates the download - * is complete, this will display one last line, unless the last line has already - * been written. - */ - stopDisplayTimer() { - if (this.timeoutHandle) { - clearTimeout(this.timeoutHandle); - this.timeoutHandle = undefined; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num } - this.display(); - } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() } -exports.DownloadProgress = DownloadProgress; -/** - * Download the cache using the Actions toolkit http-client - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - */ -function downloadCacheHttpClient(archiveLocation, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - const writeStream = fs.createWriteStream(archivePath); - const httpClient = new http_client_1.HttpClient('actions/cache'); - const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); - // Abort download if no traffic received over the socket. - downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { - downloadResponse.message.destroy(); - core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); - }); - yield pipeResponseToStream(downloadResponse, writeStream); - // Validate download size. - const contentLengthHeader = downloadResponse.message.headers['content-length']; - if (contentLengthHeader) { - const expectedLength = parseInt(contentLengthHeader); - const actualLength = utils.getArchiveFileSizeInBytes(archivePath); - if (actualLength !== expectedLength) { - throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); - } - } - else { - core.debug('Unable to validate download, no Content-Length header'); - } - }); + +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version } -exports.downloadCacheHttpClient = downloadCacheHttpClient; -/** - * Download the cache using the Azure Storage SDK. Only call this method if the - * URL points to an Azure Storage endpoint. - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - * @param options the download options with the defaults set - */ -function downloadCacheStorageSDK(archiveLocation, archivePath, options) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { - retryOptions: { - // Override the timeout used when downloading each 4 MB chunk - // The default is 2 min / MB, which is way too slow - tryTimeoutInMs: options.timeoutInMs - } - }); - const properties = yield client.getProperties(); - const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; - if (contentLength < 0) { - // We should never hit this condition, but just in case fall back to downloading the - // file as one large stream - core.debug('Unable to determine content length, downloading file with http-client...'); - yield downloadCacheHttpClient(archiveLocation, archivePath); - } - else { - // Use downloadToBuffer for faster downloads, since internally it splits the - // file into 4 MB chunks which can then be parallelized and retried independently - // - // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB - // on 64-bit systems), split the download into multiple segments - // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. - // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast - const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); - const downloadProgress = new DownloadProgress(contentLength); - const fd = fs.openSync(archivePath, 'w'); - try { - downloadProgress.startDisplayTimer(); - const controller = new abort_controller_1.AbortController(); - const abortSignal = controller.signal; - while (!downloadProgress.isDone()) { - const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; - const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); - downloadProgress.nextSegment(segmentSize); - const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { - abortSignal, - concurrency: options.downloadConcurrency, - onProgress: downloadProgress.onProgress() - })); - if (result === 'timeout') { - controller.abort(); - throw new Error('Aborting cache download as the download time exceeded the timeout.'); - } - else if (Buffer.isBuffer(result)) { - fs.writeFileSync(fd, result); - } - } - } - finally { - downloadProgress.stopDisplayTimer(); - fs.closeSync(fd); - } - } - }); + +SemVer.prototype.toString = function () { + return this.version } -exports.downloadCacheStorageSDK = downloadCacheStorageSDK; -const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { - let timeoutHandle; - const timeoutPromise = new Promise(resolve => { - timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); - }); - return Promise.race([promise, timeoutPromise]).then(result => { - clearTimeout(timeoutHandle); - return result; - }); -}); -//# sourceMappingURL=downloadUtils.js.map -/***/ }), +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -/***/ 3981: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + return this.compareMain(other) || this.comparePre(other) +} -"use strict"; +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const constants_1 = __nccwpck_require__(8840); -function isSuccessStatusCode(statusCode) { - if (!statusCode) { - return false; - } - return statusCode >= 200 && statusCode < 300; + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) } -exports.isSuccessStatusCode = isSuccessStatusCode; -function isServerErrorStatusCode(statusCode) { - if (!statusCode) { - return true; + +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - return statusCode >= 500; + } while (++i) } -exports.isServerErrorStatusCode = isServerErrorStatusCode; -function isRetryableStatusCode(statusCode) { - if (!statusCode) { - return false; + +SemVer.prototype.compareBuild = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + var i = 0 + do { + var a = this.build[i] + var b = other.build[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - const retryableStatusCodes = [ - http_client_1.HttpCodes.BadGateway, - http_client_1.HttpCodes.ServiceUnavailable, - http_client_1.HttpCodes.GatewayTimeout - ]; - return retryableStatusCodes.includes(statusCode); -} -exports.isRetryableStatusCode = isRetryableStatusCode; -function sleep(milliseconds) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise(resolve => setTimeout(resolve, milliseconds)); - }); -} -function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { - return __awaiter(this, void 0, void 0, function* () { - let errorMessage = ''; - let attempt = 1; - while (attempt <= maxAttempts) { - let response = undefined; - let statusCode = undefined; - let isRetryable = false; - try { - response = yield method(); - } - catch (error) { - if (onError) { - response = onError(error); - } - isRetryable = true; - errorMessage = error.message; - } - if (response) { - statusCode = getStatusCode(response); - if (!isServerErrorStatusCode(statusCode)) { - return response; - } - } - if (statusCode) { - isRetryable = isRetryableStatusCode(statusCode); - errorMessage = `Cache service responded with ${statusCode}`; - } - core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); - if (!isRetryable) { - core.debug(`${name} - Error is not retryable`); - break; - } - yield sleep(delay); - attempt++; - } - throw Error(`${name} failed: ${errorMessage}`); - }); -} -exports.retry = retry; -function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, - // If the error object contains the statusCode property, extract it and return - // an TypedResponse so it can be processed by the retry logic. - (error) => { - if (error instanceof http_client_1.HttpClientError) { - return { - statusCode: error.statusCode, - result: null, - headers: {}, - error - }; - } - else { - return undefined; - } - }); - }); -} -exports.retryTypedResponse = retryTypedResponse; -function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); - }); + } while (++i) } -exports.retryHttpClientResponse = retryHttpClientResponse; -//# sourceMappingURL=requestUtils.js.map - -/***/ }), -/***/ 6490: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.createTar = exports.extractTar = exports.listTar = void 0; -const exec_1 = __nccwpck_require__(1514); -const io = __importStar(__nccwpck_require__(7436)); -const fs_1 = __nccwpck_require__(7147); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const IS_WINDOWS = process.platform === 'win32'; -// Returns tar path and type: BSD or GNU -function getTarPath() { - return __awaiter(this, void 0, void 0, function* () { - switch (process.platform) { - case 'win32': { - const gnuTar = yield utils.getGnuTarPathOnWindows(); - const systemTar = constants_1.SystemTarPathOnWindows; - if (gnuTar) { - // Use GNUtar as default on windows - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else if ((0, fs_1.existsSync)(systemTar)) { - return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; - } - break; - } - case 'darwin': { - const gnuTar = yield io.which('gtar', false); - if (gnuTar) { - // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else { - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.BSD - }; - } - } - default: - break; - } - // Default assumption is GNU tar is present in path - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.GNU - }; - }); -} -// Return arguments for tar as per tarPath, compressionMethod, method type and os -function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - const args = [`"${tarPath.path}"`]; - const cacheFileName = utils.getCacheFileName(compressionMethod); - const tarFile = 'cache.tar'; - const workingDirectory = getWorkingDirectory(); - // Speficic args for BSD tar on windows for workaround - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - // Method specific args - switch (type) { - case 'create': - args.push('--posix', '-cf', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); - break; - case 'extract': - args.push('-xf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); - break; - case 'list': - args.push('-tf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); - break; - } - // Platform specific args - if (tarPath.type === constants_1.ArchiveToolType.GNU) { - switch (process.platform) { - case 'win32': - args.push('--force-local'); - break; - case 'darwin': - args.push('--delay-directory-restore'); - break; - } - } - return args; - }); -} -// Returns commands to run tar and compression program -function getCommands(compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - let args; - const tarPath = yield getTarPath(); - const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); - const compressionArgs = type !== 'create' - ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) - : yield getCompressionProgram(tarPath, compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - if (BSD_TAR_ZSTD && type !== 'create') { - args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; - } - else { - args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; - } - if (BSD_TAR_ZSTD) { - return args; - } - return [args.join(' ')]; - }); -} -function getWorkingDirectory() { - var _a; - return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); -} -// Common function for extractTar and listTar to get the compression method -function getDecompressionProgram(tarPath, compressionMethod, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - // -d: Decompress. - // unzstd is equivalent to 'zstd -d' - // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. - // Using 30 here because we also support 32-bit self-hosted runners. - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --long=30 --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; - default: - return ['-z']; + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } } - }); -} -// Used for creating the archive -// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. -// zstdmt is equivalent to 'zstd -T0' -// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. -// Using 30 here because we also support 32-bit self-hosted runners. -// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. -function getCompressionProgram(tarPath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const cacheFileName = utils.getCacheFileName(compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --long=30 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; - default: - return ['-z']; + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) } - }); -} -// Executes all commands as separate processes -function execCommands(commands, cwd) { - return __awaiter(this, void 0, void 0, function* () { - for (const command of commands) { - try { - yield (0, exec_1.exec)(command, undefined, { - cwd, - env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) - }); - } - catch (error) { - throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); - } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] } - }); -} -// List the contents of a tar -function listTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const commands = yield getCommands(compressionMethod, 'list', archivePath); - yield execCommands(commands); - }); -} -exports.listTar = listTar; -// Extract a tar -function extractTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Create directory to extract tar into - const workingDirectory = getWorkingDirectory(); - yield io.mkdirP(workingDirectory); - const commands = yield getCommands(compressionMethod, 'extract', archivePath); - yield execCommands(commands); - }); -} -exports.extractTar = extractTar; -// Create a tar -function createTar(archiveFolder, sourceDirectories, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Write source directories to manifest.txt to avoid command length limits - (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); - const commands = yield getCommands(compressionMethod, 'create'); - yield execCommands(commands, archiveFolder); - }); -} -exports.createTar = createTar; -//# sourceMappingURL=tar.js.map + } + break -/***/ }), + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} -/***/ 6215: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } -"use strict"; + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getDownloadOptions = exports.getUploadOptions = void 0; -const core = __importStar(__nccwpck_require__(2186)); -/** - * Returns a copy of the upload options with defaults filled in. - * - * @param copy the original upload options - */ -function getUploadOptions(copy) { - const result = { - uploadConcurrency: 4, - uploadChunkSize: 32 * 1024 * 1024 - }; - if (copy) { - if (typeof copy.uploadConcurrency === 'number') { - result.uploadConcurrency = copy.uploadConcurrency; - } - if (typeof copy.uploadChunkSize === 'number') { - result.uploadChunkSize = copy.uploadChunkSize; - } +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' } - core.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core.debug(`Upload chunk size: ${result.uploadChunkSize}`); - return result; -} -exports.getUploadOptions = getUploadOptions; -/** - * Returns a copy of the download options with defaults filled in. - * - * @param copy the original download options - */ -function getDownloadOptions(copy) { - const result = { - useAzureSdk: true, - downloadConcurrency: 8, - timeoutInMs: 30000, - segmentTimeoutInMs: 600000, - lookupOnly: false - }; - if (copy) { - if (typeof copy.useAzureSdk === 'boolean') { - result.useAzureSdk = copy.useAzureSdk; - } - if (typeof copy.downloadConcurrency === 'number') { - result.downloadConcurrency = copy.downloadConcurrency; - } - if (typeof copy.timeoutInMs === 'number') { - result.timeoutInMs = copy.timeoutInMs; - } - if (typeof copy.segmentTimeoutInMs === 'number') { - result.segmentTimeoutInMs = copy.segmentTimeoutInMs; - } - if (typeof copy.lookupOnly === 'boolean') { - result.lookupOnly = copy.lookupOnly; + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key } + } } - const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; - if (segmentDownloadTimeoutMins && - !isNaN(Number(segmentDownloadTimeoutMins)) && - isFinite(Number(segmentDownloadTimeoutMins))) { - result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; - } - core.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core.debug(`Download concurrency: ${result.downloadConcurrency}`); - core.debug(`Request timeout (ms): ${result.timeoutInMs}`); - core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); - core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - core.debug(`Lookup only: ${result.lookupOnly}`); - return result; + return defaultResult // may be undefined + } } -exports.getDownloadOptions = getDownloadOptions; -//# sourceMappingURL=options.js.map - -/***/ }), -/***/ 3771: -/***/ ((module, exports) => { +exports.compareIdentifiers = compareIdentifiers -exports = module.exports = SemVer +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) + if (anum && bnum) { + a = +a + b = +b } -} else { - debug = function () {} -} - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var t = exports.tokens = {} -var R = 0 +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} -function tok (n) { - t[n] = R++ +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor } -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -tok('NUMERICIDENTIFIER') -src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' -tok('NUMERICIDENTIFIERLOOSE') -src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -tok('NONNUMERICIDENTIFIER') -src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - -// ## Main Version -// Three dot-separated numeric identifiers. +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} -tok('MAINVERSION') -src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')' +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} -tok('MAINVERSIONLOOSE') -src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. +exports.compareBuild = compareBuild +function compareBuild (a, b, loose) { + var versionA = new SemVer(a, loose) + var versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} -tok('PRERELEASEIDENTIFIER') -src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} -tok('PRERELEASEIDENTIFIERLOOSE') -src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(a, b, loose) + }) +} -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(b, a, loose) + }) +} -tok('PRERELEASE') -src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} -tok('PRERELEASELOOSE') -src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} -tok('BUILDIDENTIFIER') -src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} -tok('BUILD') -src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + - '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b -tok('FULL') -tok('FULLPLAIN') -src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + - src[t.PRERELEASE] + '?' + - src[t.BUILD] + '?' + case '': + case '=': + case '==': + return eq(a, b, loose) -src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + case '!=': + return neq(a, b, loose) -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -tok('LOOSEPLAIN') -src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + - src[t.PRERELEASELOOSE] + '?' + - src[t.BUILD] + '?' + case '>': + return gt(a, b, loose) -tok('LOOSE') -src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + case '>=': + return gte(a, b, loose) -tok('GTLT') -src[t.GTLT] = '((?:<|>)?=?)' + case '<': + return lt(a, b, loose) -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -tok('XRANGEIDENTIFIERLOOSE') -src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -tok('XRANGEIDENTIFIER') -src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + case '<=': + return lte(a, b, loose) -tok('XRANGEPLAIN') -src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:' + src[t.PRERELEASE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' + default: + throw new TypeError('Invalid operator: ' + op) + } +} -tok('XRANGEPLAINLOOSE') -src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[t.PRERELEASELOOSE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -tok('XRANGE') -src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' -tok('XRANGELOOSE') -src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -tok('COERCE') -src[t.COERCE] = '(^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' -tok('COERCERTL') -re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } -// Tilde ranges. -// Meaning is "reasonably at or greater than" -tok('LONETILDE') -src[t.LONETILDE] = '(?:~>?)' + comp = comp.trim().split(/\s+/).join(' ') + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) -tok('TILDETRIM') -src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' -re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') -var tildeTrimReplace = '$1~' + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } -tok('TILDE') -src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' -tok('TILDELOOSE') -src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + debug('comp', this) +} -// Caret ranges. -// Meaning is "at least and backwards compatible with" -tok('LONECARET') -src[t.LONECARET] = '(?:\\^)' +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var m = comp.match(r) -tok('CARETTRIM') -src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' -re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') -var caretTrimReplace = '$1^' + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } -tok('CARET') -src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' -tok('CARETLOOSE') -src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } -// A simple gt/lt/eq thing, or just "" to indicate "any version" -tok('COMPARATORLOOSE') -src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' -tok('COMPARATOR') -src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -tok('COMPARATORTRIM') -src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + - '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' +Comparator.prototype.toString = function () { + return this.value +} -// this one has to use the /g flag -re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -tok('HYPHENRANGE') -src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAIN] + ')' + - '\\s*$' + if (this.semver === ANY || version === ANY) { + return true + } -tok('HYPHENRANGELOOSE') -src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s*$' + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } -// Star ranges basically just allow anything at all. -tok('STAR') -src[t.STAR] = '(<|>)?=?\\s*\\*' + return cmp(version, this.operator, this.semver, this.options) +} -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') } -} -exports.parse = parse -function parse (version, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, @@ -1817,985 +888,409 @@ function parse (version, options) { } } - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - var r = options.loose ? re[t.LOOSE] : re[t.FULL] - if (!r.test(version)) { - return null - } + var rangeTmp - try { - return new SemVer(version, options) - } catch (er) { - return null + if (this.operator === '') { + if (this.value === '') { + return true + } + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) } -} -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan } -exports.SemVer = SemVer - -function SemVer (version, options) { +exports.Range = Range +function Range (range, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, includePrerelease: false } } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version + + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range } else { - version = version.version + return new Range(range.raw, options) } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) } - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + if (range instanceof Comparator) { + return new Range(range.value, options) } - if (!(this instanceof SemVer)) { - return new SemVer(version, options) + if (!(this instanceof Range)) { + return new Range(range, options) } - debug('SemVer', version, options) this.options = options this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease - var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) - - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } - - this.raw = version - - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. + this.raw = range + .trim() + .split(/\s+/) + .join(' ') - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } + // First, split based on boolean or || + this.set = this.raw.split('||').map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + this.raw) } - this.build = m[5] ? m[5].split('.') : [] this.format() } -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range } -SemVer.prototype.toString = function () { - return this.version +Range.prototype.toString = function () { + return this.range } -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, safeRe[t.COMPARATORTRIM]) - return this.compareMain(other) || this.comparePre(other) -} + // `~ 1.2.3` => `~1.2.3` + range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace) -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // `^ 1.2.3` => `^1.2.3` + range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace) - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} + // normalize spaces + range = range.split(/\s+/).join(' ') -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // At this point, the range is completely trimmed and + // ready to be split into comparators. - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 + var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return set } -SemVer.prototype.compareBuild = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') } - var i = 0 - do { - var a = this.build[i] - var b = other.build[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return this.set.some(function (thisComparators) { + return ( + isSatisfiable(thisComparators, options) && + range.set.some(function (rangeComparators) { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every(function (thisComparator) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) } -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break - - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} +// take a set of comparators and determine whether there +// exists a version which can satisfy it +function isSatisfiable (comparators, options) { + var result = true + var remainingComparators = comparators.slice() + var testComparator = remainingComparators.pop() -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } + while (result && remainingComparators.length) { + result = remainingComparators.every(function (otherComparator) { + return testComparator.intersects(otherComparator, options) + }) - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null + testComparator = remainingComparators.pop() } -} -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } + return result } -exports.compareIdentifiers = compareIdentifiers - -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) - - if (anum && bnum) { - a = +a - b = +b - } - - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) } -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp } -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' } -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') } -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} +function replaceTilde (comp, options) { + var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) + debug('tilde return', ret) + return ret + }) } -exports.compareBuild = compareBuild -function compareBuild (a, b, loose) { - var versionA = new SemVer(a, loose) - var versionB = new SemVer(b, loose) - return versionA.compare(versionB) || versionA.compareBuild(versionB) +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') } -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(a, b, loose) - }) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(b, a, loose) + debug('caret return', ret) + return ret }) } -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') } -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} + if (gtlt === '=' && anyX) { + gtlt = '' + } -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b + ret = gtlt + M + '.' + m + '.' + p + pr + } else if (xm) { + ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr + } else if (xp) { + ret = '>=' + M + '.' + m + '.0' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + pr + } - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b + debug('xRange return', ret) - case '': - case '=': - case '==': - return eq(a, b, loose) + return ret + }) +} - case '!=': - return neq(a, b, loose) - - case '>': - return gt(a, b, loose) - - case '>=': - return gte(a, b, loose) - - case '<': - return lt(a, b, loose) - - case '<=': - return lte(a, b, loose) - - default: - throw new TypeError('Invalid operator: ' + op) - } -} - -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } - - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } - - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } - - debug('comp', this) -} - -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var m = comp.match(r) - - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } - - this.operator = m[1] !== undefined ? m[1] : '' - if (this.operator === '=') { - this.operator = '' - } - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} - -Comparator.prototype.toString = function () { - return this.value -} - -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) - - if (this.semver === ANY || version === ANY) { - return true - } - - if (typeof version === 'string') { - try { - version = new SemVer(version, this.options) - } catch (er) { - return false - } - } - - return cmp(version, this.operator, this.semver, this.options) -} - -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } - - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - var rangeTmp - - if (this.operator === '') { - if (this.value === '') { - return true - } - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - if (comp.value === '') { - return true - } - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} - -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease - - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } - - this.format() -} - -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} - -Range.prototype.toString = function () { - return this.range -} - -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[t.COMPARATORTRIM]) - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[t.CARETTRIM], caretTrimReplace) - - // normalize spaces - range = range.split(/\s+/).join(' ') - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) - - return set -} - -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } - - return this.set.some(function (thisComparators) { - return ( - isSatisfiable(thisComparators, options) && - range.set.some(function (rangeComparators) { - return ( - isSatisfiable(rangeComparators, options) && - thisComparators.every(function (thisComparator) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - ) - }) - ) - }) -} - -// take a set of comparators and determine whether there -// exists a version which can satisfy it -function isSatisfiable (comparators, options) { - var result = true - var remainingComparators = comparators.slice() - var testComparator = remainingComparators.pop() - - while (result && remainingComparators.length) { - result = remainingComparators.every(function (otherComparator) { - return testComparator.intersects(otherComparator, options) - }) - - testComparator = remainingComparators.pop() - } - - return result -} - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} - -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} - -function replaceTilde (comp, options) { - var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - - debug('tilde return', ret) - return ret - }) -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} - -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } - - debug('caret return', ret) - return ret - }) -} - -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} - -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp - - if (gtlt === '=' && anyX) { - gtlt = '' - } - - // if we're including prereleases in the match, then we need - // to fix this to -0, the lowest possible prerelease value - pr = options.includePrerelease ? '-0' : '' - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0-0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } - - ret = gtlt + M + '.' + m + '.' + p + pr - } else if (xm) { - ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr - } else if (xp) { - ret = '>=' + M + '.' + m + '.0' + pr + - ' <' + M + '.' + (+m + 1) + '.0' + pr - } - - debug('xRange return', ret) - - return ret - }) -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') -} +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(safeRe[t.STAR], '') +} // This function is passed to string.replace(re[t.HYPHENRANGE]) // M, m, patch, prerelease, build @@ -3120,7 +1615,7 @@ function coerce (version, options) { var match = null if (!options.rtl) { - match = version.match(re[t.COERCE]) + match = version.match(safeRe[t.COERCE]) } else { // Find the right-most coercible string that does not share // a terminus with a more left-ward coercible string. @@ -3131,17 +1626,17 @@ function coerce (version, options) { // Stop when we get a match that ends at the string end, since no // coercible string can be more right-ward without the same terminus. var next - while ((next = re[t.COERCERTL].exec(version)) && + while ((next = safeRe[t.COERCERTL].exec(version)) && (!match || match.index + match[0].length !== version.length) ) { if (!match || next.index + next[0].length !== match.index + match[0].length) { match = next } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length } // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 + safeRe[t.COERCERTL].lastIndex = -1 } if (match === null) { @@ -7026,6 +5521,19 @@ class HttpClientResponse { })); }); } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } } exports.HttpClientResponse = HttpClientResponse; function isHttps(requestUrl) { @@ -7530,7 +6038,13 @@ function getProxyUrl(reqUrl) { } })(); if (proxyVar) { - return new URL(proxyVar); + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } } else { return undefined; @@ -7541,6 +6055,10 @@ function checkBypass(reqUrl) { if (!reqUrl.hostname) { return false; } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; if (!noProxy) { return false; @@ -7566,13 +6084,24 @@ function checkBypass(reqUrl) { .split(',') .map(x => x.trim().toUpperCase()) .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { + if (upperNoProxyItem === '*' || + upperReqHosts.some(x => x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)))) { return true; } } return false; } exports.checkBypass = checkBypass; +function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return (hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]')); +} //# sourceMappingURL=proxy.js.map /***/ }), @@ -48965,7 +47494,7 @@ function state(list, sortMethod) /***/ }), -/***/ 7942: +/***/ 834: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { var abort = __nccwpck_require__(1700) @@ -49006,7 +47535,7 @@ function terminator(callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -49080,7 +47609,7 @@ function serial(list, iterator, callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -62310,123 +60839,1799 @@ function wrappy (fn, cb) { } else if (node.sysID) { r += ' SYSTEM "' + node.sysID + '"'; } - if (node.nData) { - r += ' NDATA ' + node.nData; + if (node.nData) { + r += ' NDATA ' + node.nData; + } + } + options.state = WriterState.CloseTag; + r += options.spaceBeforeSlash + '>' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + }; + + XMLWriterBase.prototype.dtdNotation = function(node, options, level) { + var r; + this.openNode(node, options, level); + options.state = WriterState.OpenTag; + r = this.indent(node, options, level) + '' + this.endline(node, options, level); + options.state = WriterState.None; + this.closeNode(node, options, level); + return r; + }; + + XMLWriterBase.prototype.openNode = function(node, options, level) {}; + + XMLWriterBase.prototype.closeNode = function(node, options, level) {}; + + XMLWriterBase.prototype.openAttribute = function(att, options, level) {}; + + XMLWriterBase.prototype.closeAttribute = function(att, options, level) {}; + + return XMLWriterBase; + + })(); + +}).call(this); + + +/***/ }), + +/***/ 2958: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +// Generated by CoffeeScript 1.12.7 +(function() { + var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; + + ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; + + XMLDOMImplementation = __nccwpck_require__(8310); + + XMLDocument = __nccwpck_require__(3730); + + XMLDocumentCB = __nccwpck_require__(7356); + + XMLStringWriter = __nccwpck_require__(5913); + + XMLStreamWriter = __nccwpck_require__(8601); + + NodeType = __nccwpck_require__(9267); + + WriterState = __nccwpck_require__(9766); + + module.exports.create = function(name, xmldec, doctype, options) { + var doc, root; + if (name == null) { + throw new Error("Root element needs a name."); + } + options = assign({}, xmldec, doctype, options); + doc = new XMLDocument(options); + root = doc.element(name); + if (!options.headless) { + doc.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + doc.dtd(options); + } + } + return root; + }; + + module.exports.begin = function(options, onData, onEnd) { + var ref1; + if (isFunction(options)) { + ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; + options = {}; + } + if (onData) { + return new XMLDocumentCB(options, onData, onEnd); + } else { + return new XMLDocument(options); + } + }; + + module.exports.stringWriter = function(options) { + return new XMLStringWriter(options); + }; + + module.exports.streamWriter = function(stream, options) { + return new XMLStreamWriter(stream, options); + }; + + module.exports.implementation = new XMLDOMImplementation(); + + module.exports.nodeType = NodeType; + + module.exports.writerState = WriterState; + +}).call(this); + + +/***/ }), + +/***/ 7942: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const cacheHttpClient = __importStar(__nccwpck_require__(7417)); +const tar_1 = __nccwpck_require__(7672); +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = 'ValidationError'; + Object.setPrototypeOf(this, ValidationError.prototype); + } +} +exports.ValidationError = ValidationError; +class ReserveCacheError extends Error { + constructor(message) { + super(message); + this.name = 'ReserveCacheError'; + Object.setPrototypeOf(this, ReserveCacheError.prototype); + } +} +exports.ReserveCacheError = ReserveCacheError; +function checkPaths(paths) { + if (!paths || paths.length === 0) { + throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); + } +} +function checkKey(key) { + if (key.length > 512) { + throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); + } + const regex = /^[^,]*$/; + if (!regex.test(key)) { + throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); + } +} +/** + * isFeatureAvailable to check the presence of Actions cache service + * + * @returns boolean return true if Actions cache service feature is available, otherwise false + */ +function isFeatureAvailable() { + return !!process.env['ACTIONS_CACHE_URL']; +} +exports.isFeatureAvailable = isFeatureAvailable; +/** + * Restores cache from keys + * + * @param paths a list of file paths to restore from the cache + * @param primaryKey an explicit key for restoring the cache + * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @param downloadOptions cache download options + * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform + * @returns string returns the key for the cache hit, otherwise returns undefined + */ +function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + restoreKeys = restoreKeys || []; + const keys = [primaryKey, ...restoreKeys]; + core.debug('Resolved Keys:'); + core.debug(JSON.stringify(keys)); + if (keys.length > 10) { + throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); + } + for (const key of keys) { + checkKey(key); + } + const compressionMethod = yield utils.getCompressionMethod(); + let archivePath = ''; + try { + // path are needed to compute version + const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod, + enableCrossOsArchive + }); + if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { + // Cache not found + return undefined; + } + if (options === null || options === void 0 ? void 0 : options.lookupOnly) { + core.info('Lookup only - skipping download'); + return cacheEntry.cacheKey; + } + archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + // Download the cache from the cache entry + yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + yield (0, tar_1.extractTar)(archivePath, compressionMethod, extraTarArgs); + core.info('Cache restored successfully'); + return cacheEntry.cacheKey; + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else { + // Supress all non-validation cache related errors because caching should be optional + core.warning(`Failed to restore: ${error.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return undefined; + }); +} +exports.restoreCache = restoreCache; +/** + * Saves a list of files with the specified key + * + * @param paths a list of file paths to be cached + * @param key an explicit key for restoring the cache + * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform + * @param options cache upload options + * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails + */ +function saveCache(paths, key, options, enableCrossOsArchive = false) { + var _a, _b, _c, _d, _e; + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + checkKey(key); + const compressionMethod = yield utils.getCompressionMethod(); + let cacheId = -1; + const cachePaths = yield utils.resolvePaths(paths); + core.debug('Cache Paths:'); + core.debug(`${JSON.stringify(cachePaths)}`); + if (cachePaths.length === 0) { + throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); + } + const archiveFolder = yield utils.createTempDirectory(); + const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + try { + yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.debug(`File Size: ${archiveFileSize}`); + // For GHES, this check will take place in ReserveCache API with enterprise file size limit + if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { + throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); + } + core.debug('Reserving Cache'); + const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { + compressionMethod, + enableCrossOsArchive, + cacheSize: archiveFileSize + }); + if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { + cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; + } + else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { + throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); + } + else { + throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); + } + core.debug(`Saving Cache (ID: ${cacheId})`); + yield cacheHttpClient.saveCache(cacheId, archivePath, options); + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else if (typedError.name === ReserveCacheError.name) { + core.info(`Failed to save: ${typedError.message}`); + } + else { + core.warning(`Failed to save: ${typedError.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return cacheId; + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 7417: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const auth_1 = __nccwpck_require__(5526); +const crypto = __importStar(__nccwpck_require__(6113)); +const fs = __importStar(__nccwpck_require__(7147)); +const url_1 = __nccwpck_require__(7310); +const utils = __importStar(__nccwpck_require__(4799)); +const downloadUtils_1 = __nccwpck_require__(8432); +const options_1 = __nccwpck_require__(3128); +const requestUtils_1 = __nccwpck_require__(204); +const versionSalt = '1.0'; +function getCacheApiUrl(resource) { + const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; + if (!baseUrl) { + throw new Error('Cache Service Url not found, unable to restore cache.'); + } + const url = `${baseUrl}_apis/artifactcache/${resource}`; + core.debug(`Resource Url: ${url}`); + return url; +} +function createAcceptHeader(type, apiVersion) { + return `${type};api-version=${apiVersion}`; +} +function getRequestOptions() { + const requestOptions = { + headers: { + Accept: createAcceptHeader('application/json', '6.0-preview.1') + } + }; + return requestOptions; +} +function createHttpClient() { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; + const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); + return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); +} +function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { + const components = paths; + // Add compression method to cache version to restore + // compressed cache as per compression method + if (compressionMethod) { + components.push(compressionMethod); + } + // Only check for windows platforms if enableCrossOsArchive is false + if (process.platform === 'win32' && !enableCrossOsArchive) { + components.push('windows-only'); + } + // Add salt to cache version to support breaking changes in cache entry + components.push(versionSalt); + return crypto.createHash('sha256').update(components.join('|')).digest('hex'); +} +exports.getCacheVersion = getCacheVersion; +function getCacheEntry(keys, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + // Cache not found + if (response.statusCode === 204) { + // List cache for primary key only if cache miss occurs + if (core.isDebug()) { + yield printCachesListForDiagnostics(keys[0], httpClient, version); + } + return null; + } + if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { + throw new Error(`Cache service responded with ${response.statusCode}`); + } + const cacheResult = response.result; + const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; + if (!cacheDownloadUrl) { + // Cache achiveLocation not found. This should never happen, and hence bail out. + throw new Error('Cache not found.'); + } + core.setSecret(cacheDownloadUrl); + core.debug(`Cache Result:`); + core.debug(JSON.stringify(cacheResult)); + return cacheResult; + }); +} +exports.getCacheEntry = getCacheEntry; +function printCachesListForDiagnostics(key, httpClient, version) { + return __awaiter(this, void 0, void 0, function* () { + const resource = `caches?key=${encodeURIComponent(key)}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + if (response.statusCode === 200) { + const cacheListResult = response.result; + const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; + if (totalCount && totalCount > 0) { + core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); + for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { + core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + } + } + } + }); +} +function downloadCache(archiveLocation, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const archiveUrl = new url_1.URL(archiveLocation); + const downloadOptions = (0, options_1.getDownloadOptions)(options); + if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) { + if (downloadOptions.useAzureSdk) { + // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. + yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); + } + else if (downloadOptions.concurrentBlobDownloads) { + // Use concurrent implementation with HttpClient to work around blob SDK issue + yield (0, downloadUtils_1.downloadCacheHttpClientConcurrent)(archiveLocation, archivePath, downloadOptions); + } + else { + // Otherwise, download using the Actions http-client. + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + } + else { + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + }); +} +exports.downloadCache = downloadCache; +// Reserve Cache +function reserveCache(key, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const reserveCacheRequest = { + key, + version, + cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize + }; + const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); + })); + return response; + }); +} +exports.reserveCache = reserveCache; +function getContentRange(start, end) { + // Format: `bytes start-end/filesize + // start and end are inclusive + // filesize can be * + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/* + return `bytes ${start}-${end}/*`; +} +function uploadChunk(httpClient, resourceUrl, openStream, start, end) { + return __awaiter(this, void 0, void 0, function* () { + core.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + const additionalHeaders = { + 'Content-Type': 'application/octet-stream', + 'Content-Range': getContentRange(start, end) + }; + const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { + return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); + })); + if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { + throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); + } + }); +} +function uploadFile(httpClient, cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + // Upload Chunks + const fileSize = utils.getArchiveFileSizeInBytes(archivePath); + const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); + const fd = fs.openSync(archivePath, 'r'); + const uploadOptions = (0, options_1.getUploadOptions)(options); + const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); + const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); + const parallelUploads = [...new Array(concurrency).keys()]; + core.debug('Awaiting all uploads'); + let offset = 0; + try { + yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, maxChunkSize); + const start = offset; + const end = offset + chunkSize - 1; + offset += maxChunkSize; + yield uploadChunk(httpClient, resourceUrl, () => fs + .createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + .on('error', error => { + throw new Error(`Cache upload failed because file read failed with ${error.message}`); + }), start, end); + } + }))); + } + finally { + fs.closeSync(fd); + } + return; + }); +} +function commitCache(httpClient, cacheId, filesize) { + return __awaiter(this, void 0, void 0, function* () { + const commitCacheRequest = { size: filesize }; + return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); + })); + }); +} +function saveCache(cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + core.debug('Upload cache'); + yield uploadFile(httpClient, cacheId, archivePath, options); + // Commit Cache + core.debug('Commiting cache'); + const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); + if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { + throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); + } + core.info('Cache saved successfully'); + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 4799: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const exec = __importStar(__nccwpck_require__(1514)); +const glob = __importStar(__nccwpck_require__(8090)); +const io = __importStar(__nccwpck_require__(7436)); +const fs = __importStar(__nccwpck_require__(7147)); +const path = __importStar(__nccwpck_require__(1017)); +const semver = __importStar(__nccwpck_require__(1898)); +const util = __importStar(__nccwpck_require__(3837)); +const uuid_1 = __nccwpck_require__(2155); +const constants_1 = __nccwpck_require__(3608); +// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 +function createTempDirectory() { + return __awaiter(this, void 0, void 0, function* () { + const IS_WINDOWS = process.platform === 'win32'; + let tempDirectory = process.env['RUNNER_TEMP'] || ''; + if (!tempDirectory) { + let baseLocation; + if (IS_WINDOWS) { + // On Windows use the USERPROFILE env variable + baseLocation = process.env['USERPROFILE'] || 'C:\\'; + } + else { + if (process.platform === 'darwin') { + baseLocation = '/Users'; + } + else { + baseLocation = '/home'; + } + } + tempDirectory = path.join(baseLocation, 'actions', 'temp'); + } + const dest = path.join(tempDirectory, (0, uuid_1.v4)()); + yield io.mkdirP(dest); + return dest; + }); +} +exports.createTempDirectory = createTempDirectory; +function getArchiveFileSizeInBytes(filePath) { + return fs.statSync(filePath).size; +} +exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; +function resolvePaths(patterns) { + var _a, e_1, _b, _c; + var _d; + return __awaiter(this, void 0, void 0, function* () { + const paths = []; + const workspace = (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); + const globber = yield glob.create(patterns.join('\n'), { + implicitDescendants: false + }); + try { + for (var _e = true, _f = __asyncValues(globber.globGenerator()), _g; _g = yield _f.next(), _a = _g.done, !_a;) { + _c = _g.value; + _e = false; + try { + const file = _c; + const relativeFile = path + .relative(workspace, file) + .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); + core.debug(`Matched: ${relativeFile}`); + // Paths are made relative so the tar entries are all relative to the root of the workspace. + if (relativeFile === '') { + // path.relative returns empty string if workspace and file are equal + paths.push('.'); + } + else { + paths.push(`${relativeFile}`); + } + } + finally { + _e = true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_e && !_a && (_b = _f.return)) yield _b.call(_f); + } + finally { if (e_1) throw e_1.error; } + } + return paths; + }); +} +exports.resolvePaths = resolvePaths; +function unlinkFile(filePath) { + return __awaiter(this, void 0, void 0, function* () { + return util.promisify(fs.unlink)(filePath); + }); +} +exports.unlinkFile = unlinkFile; +function getVersion(app, additionalArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let versionOutput = ''; + additionalArgs.push('--version'); + core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); + try { + yield exec.exec(`${app}`, additionalArgs, { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + } + catch (err) { + core.debug(err.message); + } + versionOutput = versionOutput.trim(); + core.debug(versionOutput); + return versionOutput; + }); +} +// Use zstandard if possible to maximize cache performance +function getCompressionMethod() { + return __awaiter(this, void 0, void 0, function* () { + const versionOutput = yield getVersion('zstd', ['--quiet']); + const version = semver.clean(versionOutput); + core.debug(`zstd version: ${version}`); + if (versionOutput === '') { + return constants_1.CompressionMethod.Gzip; + } + else { + return constants_1.CompressionMethod.ZstdWithoutLong; + } + }); +} +exports.getCompressionMethod = getCompressionMethod; +function getCacheFileName(compressionMethod) { + return compressionMethod === constants_1.CompressionMethod.Gzip + ? constants_1.CacheFilename.Gzip + : constants_1.CacheFilename.Zstd; +} +exports.getCacheFileName = getCacheFileName; +function getGnuTarPathOnWindows() { + return __awaiter(this, void 0, void 0, function* () { + if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { + return constants_1.GnuTarPathOnWindows; + } + const versionOutput = yield getVersion('tar'); + return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; + }); +} +exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; +function assertDefined(name, value) { + if (value === undefined) { + throw Error(`Expected ${name} but value was undefiend`); + } + return value; +} +exports.assertDefined = assertDefined; +function isGhes() { + const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; +} +exports.isGhes = isGhes; + + +/***/ }), + +/***/ 3608: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; +var CacheFilename; +(function (CacheFilename) { + CacheFilename["Gzip"] = "cache.tgz"; + CacheFilename["Zstd"] = "cache.tzst"; +})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); +var CompressionMethod; +(function (CompressionMethod) { + CompressionMethod["Gzip"] = "gzip"; + // Long range mode was added to zstd in v1.3.2. + // This enum is for earlier version of zstd that does not have --long support + CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; + CompressionMethod["Zstd"] = "zstd"; +})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); +var ArchiveToolType; +(function (ArchiveToolType) { + ArchiveToolType["GNU"] = "gnu"; + ArchiveToolType["BSD"] = "bsd"; +})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); +// The default number of retry attempts. +exports.DefaultRetryAttempts = 2; +// The default delay in milliseconds between retry attempts. +exports.DefaultRetryDelay = 5000; +// Socket timeout in milliseconds during download. If no traffic is received +// over the socket during this period, the socket is destroyed and the download +// is aborted. +exports.SocketTimeout = 5000; +// The default path of GNUtar on hosted Windows runners +exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; +// The default path of BSDtar on hosted Windows runners +exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; +exports.TarFilename = 'cache.tar'; +exports.ManifestFilename = 'manifest.txt'; + + +/***/ }), + +/***/ 8432: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.downloadCacheStorageSDK = exports.downloadCacheHttpClientConcurrent = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const storage_blob_1 = __nccwpck_require__(4100); +const buffer = __importStar(__nccwpck_require__(4300)); +const fs = __importStar(__nccwpck_require__(7147)); +const stream = __importStar(__nccwpck_require__(2781)); +const util = __importStar(__nccwpck_require__(3837)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const requestUtils_1 = __nccwpck_require__(204); +const abort_controller_1 = __nccwpck_require__(2557); +/** + * Pipes the body of a HTTP response to a stream + * + * @param response the HTTP response + * @param output the writable stream + */ +function pipeResponseToStream(response, output) { + return __awaiter(this, void 0, void 0, function* () { + const pipeline = util.promisify(stream.pipeline); + yield pipeline(response.message, output); + }); +} +/** + * Class for tracking the download state and displaying stats. + */ +class DownloadProgress { + constructor(contentLength) { + this.contentLength = contentLength; + this.segmentIndex = 0; + this.segmentSize = 0; + this.segmentOffset = 0; + this.receivedBytes = 0; + this.displayedComplete = false; + this.startTime = Date.now(); + } + /** + * Progress to the next segment. Only call this method when the previous segment + * is complete. + * + * @param segmentSize the length of the next segment + */ + nextSegment(segmentSize) { + this.segmentOffset = this.segmentOffset + this.segmentSize; + this.segmentIndex = this.segmentIndex + 1; + this.segmentSize = segmentSize; + this.receivedBytes = 0; + core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + } + /** + * Sets the number of bytes received for the current segment. + * + * @param receivedBytes the number of bytes received + */ + setReceivedBytes(receivedBytes) { + this.receivedBytes = receivedBytes; + } + /** + * Returns the total number of bytes transferred. + */ + getTransferredBytes() { + return this.segmentOffset + this.receivedBytes; + } + /** + * Returns true if the download is complete. + */ + isDone() { + return this.getTransferredBytes() === this.contentLength; + } + /** + * Prints the current download stats. Once the download completes, this will print one + * last line and then stop. + */ + display() { + if (this.displayedComplete) { + return; + } + const transferredBytes = this.segmentOffset + this.receivedBytes; + const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); + const elapsedTime = Date.now() - this.startTime; + const downloadSpeed = (transferredBytes / + (1024 * 1024) / + (elapsedTime / 1000)).toFixed(1); + core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + if (this.isDone()) { + this.displayedComplete = true; + } + } + /** + * Returns a function used to handle TransferProgressEvents. + */ + onProgress() { + return (progress) => { + this.setReceivedBytes(progress.loadedBytes); + }; + } + /** + * Starts the timer that displays the stats. + * + * @param delayInMs the delay between each write + */ + startDisplayTimer(delayInMs = 1000) { + const displayCallback = () => { + this.display(); + if (!this.isDone()) { + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + }; + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + /** + * Stops the timer that displays the stats. As this typically indicates the download + * is complete, this will display one last line, unless the last line has already + * been written. + */ + stopDisplayTimer() { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle); + this.timeoutHandle = undefined; + } + this.display(); + } +} +exports.DownloadProgress = DownloadProgress; +/** + * Download the cache using the Actions toolkit http-client + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClient(archiveLocation, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + const writeStream = fs.createWriteStream(archivePath); + const httpClient = new http_client_1.HttpClient('actions/cache'); + const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { + downloadResponse.message.destroy(); + core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); + }); + yield pipeResponseToStream(downloadResponse, writeStream); + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length']; + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader); + const actualLength = utils.getArchiveFileSizeInBytes(archivePath); + if (actualLength !== expectedLength) { + throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); + } + } + else { + core.debug('Unable to validate download, no Content-Length header'); + } + }); +} +exports.downloadCacheHttpClient = downloadCacheHttpClient; +/** + * Download the cache using the Actions toolkit http-client concurrently + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const archiveDescriptor = yield fs.promises.open(archivePath, 'w'); + const httpClient = new http_client_1.HttpClient('actions/cache', undefined, { + socketTimeout: options.timeoutInMs, + keepAlive: true + }); + try { + const res = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCacheMetadata', () => __awaiter(this, void 0, void 0, function* () { return yield httpClient.request('HEAD', archiveLocation, null, {}); })); + const lengthHeader = res.message.headers['content-length']; + if (lengthHeader === undefined || lengthHeader === null) { + throw new Error('Content-Length not found on blob response'); + } + const length = parseInt(lengthHeader); + if (Number.isNaN(length)) { + throw new Error(`Could not interpret Content-Length: ${length}`); + } + const downloads = []; + const blockSize = 4 * 1024 * 1024; + for (let offset = 0; offset < length; offset += blockSize) { + const count = Math.min(blockSize, length - offset); + downloads.push({ + offset, + promiseGetter: () => __awaiter(this, void 0, void 0, function* () { + return yield downloadSegmentRetry(httpClient, archiveLocation, offset, count); + }) + }); + } + // reverse to use .pop instead of .shift + downloads.reverse(); + let actives = 0; + let bytesDownloaded = 0; + const progress = new DownloadProgress(length); + progress.startDisplayTimer(); + const progressFn = progress.onProgress(); + const activeDownloads = []; + let nextDownload; + const waitAndWrite = () => __awaiter(this, void 0, void 0, function* () { + const segment = yield Promise.race(Object.values(activeDownloads)); + yield archiveDescriptor.write(segment.buffer, 0, segment.count, segment.offset); + actives--; + delete activeDownloads[segment.offset]; + bytesDownloaded += segment.count; + progressFn({ loadedBytes: bytesDownloaded }); + }); + while ((nextDownload = downloads.pop())) { + activeDownloads[nextDownload.offset] = nextDownload.promiseGetter(); + actives++; + if (actives >= ((_a = options.downloadConcurrency) !== null && _a !== void 0 ? _a : 10)) { + yield waitAndWrite(); + } + } + while (actives > 0) { + yield waitAndWrite(); + } + } + finally { + httpClient.dispose(); + yield archiveDescriptor.close(); + } + }); +} +exports.downloadCacheHttpClientConcurrent = downloadCacheHttpClientConcurrent; +function downloadSegmentRetry(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const retries = 5; + let failures = 0; + while (true) { + try { + const timeout = 30000; + const result = yield promiseWithTimeout(timeout, downloadSegment(httpClient, archiveLocation, offset, count)); + if (typeof result === 'string') { + throw new Error('downloadSegmentRetry failed due to timeout'); + } + return result; + } + catch (err) { + if (failures >= retries) { + throw err; + } + failures++; + } + } + }); +} +function downloadSegment(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const partRes = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCachePart', () => __awaiter(this, void 0, void 0, function* () { + return yield httpClient.get(archiveLocation, { + Range: `bytes=${offset}-${offset + count - 1}` + }); + })); + if (!partRes.readBodyBuffer) { + throw new Error('Expected HttpClientResponse to implement readBodyBuffer'); + } + return { + offset, + count, + buffer: yield partRes.readBodyBuffer() + }; + }); +} +/** + * Download the cache using the Azure Storage SDK. Only call this method if the + * URL points to an Azure Storage endpoint. + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + * @param options the download options with the defaults set + */ +function downloadCacheStorageSDK(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { + retryOptions: { + // Override the timeout used when downloading each 4 MB chunk + // The default is 2 min / MB, which is way too slow + tryTimeoutInMs: options.timeoutInMs + } + }); + const properties = yield client.getProperties(); + const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; + if (contentLength < 0) { + // We should never hit this condition, but just in case fall back to downloading the + // file as one large stream + core.debug('Unable to determine content length, downloading file with http-client...'); + yield downloadCacheHttpClient(archiveLocation, archivePath); + } + else { + // Use downloadToBuffer for faster downloads, since internally it splits the + // file into 4 MB chunks which can then be parallelized and retried independently + // + // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB + // on 64-bit systems), split the download into multiple segments + // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. + // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast + const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); + const downloadProgress = new DownloadProgress(contentLength); + const fd = fs.openSync(archivePath, 'w'); + try { + downloadProgress.startDisplayTimer(); + const controller = new abort_controller_1.AbortController(); + const abortSignal = controller.signal; + while (!downloadProgress.isDone()) { + const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; + const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); + downloadProgress.nextSegment(segmentSize); + const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { + abortSignal, + concurrency: options.downloadConcurrency, + onProgress: downloadProgress.onProgress() + })); + if (result === 'timeout') { + controller.abort(); + throw new Error('Aborting cache download as the download time exceeded the timeout.'); + } + else if (Buffer.isBuffer(result)) { + fs.writeFileSync(fd, result); + } + } + } + finally { + downloadProgress.stopDisplayTimer(); + fs.closeSync(fd); + } + } + }); +} +exports.downloadCacheStorageSDK = downloadCacheStorageSDK; +const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { + let timeoutHandle; + const timeoutPromise = new Promise(resolve => { + timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); + }); + return Promise.race([promise, timeoutPromise]).then(result => { + clearTimeout(timeoutHandle); + return result; + }); +}); + + +/***/ }), + +/***/ 204: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const constants_1 = __nccwpck_require__(3608); +function isSuccessStatusCode(statusCode) { + if (!statusCode) { + return false; + } + return statusCode >= 200 && statusCode < 300; +} +exports.isSuccessStatusCode = isSuccessStatusCode; +function isServerErrorStatusCode(statusCode) { + if (!statusCode) { + return true; + } + return statusCode >= 500; +} +exports.isServerErrorStatusCode = isServerErrorStatusCode; +function isRetryableStatusCode(statusCode) { + if (!statusCode) { + return false; + } + const retryableStatusCodes = [ + http_client_1.HttpCodes.BadGateway, + http_client_1.HttpCodes.ServiceUnavailable, + http_client_1.HttpCodes.GatewayTimeout + ]; + return retryableStatusCodes.includes(statusCode); +} +exports.isRetryableStatusCode = isRetryableStatusCode; +function sleep(milliseconds) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => setTimeout(resolve, milliseconds)); + }); +} +function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { + return __awaiter(this, void 0, void 0, function* () { + let errorMessage = ''; + let attempt = 1; + while (attempt <= maxAttempts) { + let response = undefined; + let statusCode = undefined; + let isRetryable = false; + try { + response = yield method(); + } + catch (error) { + if (onError) { + response = onError(error); + } + isRetryable = true; + errorMessage = error.message; + } + if (response) { + statusCode = getStatusCode(response); + if (!isServerErrorStatusCode(statusCode)) { + return response; + } + } + if (statusCode) { + isRetryable = isRetryableStatusCode(statusCode); + errorMessage = `Cache service responded with ${statusCode}`; + } + core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`); + break; + } + yield sleep(delay); + attempt++; + } + throw Error(`${name} failed: ${errorMessage}`); + }); +} +exports.retry = retry; +function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, + // If the error object contains the statusCode property, extract it and return + // an TypedResponse so it can be processed by the retry logic. + (error) => { + if (error instanceof http_client_1.HttpClientError) { + return { + statusCode: error.statusCode, + result: null, + headers: {}, + error + }; + } + else { + return undefined; + } + }); + }); +} +exports.retryTypedResponse = retryTypedResponse; +function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); + }); +} +exports.retryHttpClientResponse = retryHttpClientResponse; + + +/***/ }), + +/***/ 7672: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.createTar = exports.extractTar = exports.listTar = void 0; +const exec_1 = __nccwpck_require__(1514); +const io = __importStar(__nccwpck_require__(7436)); +const fs_1 = __nccwpck_require__(7147); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const IS_WINDOWS = process.platform === 'win32'; +// Returns tar path and type: BSD or GNU +function getTarPath() { + return __awaiter(this, void 0, void 0, function* () { + switch (process.platform) { + case 'win32': { + const gnuTar = yield utils.getGnuTarPathOnWindows(); + const systemTar = constants_1.SystemTarPathOnWindows; + if (gnuTar) { + // Use GNUtar as default on windows + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else if ((0, fs_1.existsSync)(systemTar)) { + return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; + } + break; + } + case 'darwin': { + const gnuTar = yield io.which('gtar', false); + if (gnuTar) { + // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else { + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.BSD + }; + } + } + default: + break; + } + // Default assumption is GNU tar is present in path + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.GNU + }; + }); +} +// Return arguments for tar as per tarPath, compressionMethod, method type and os +function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { + return __awaiter(this, void 0, void 0, function* () { + const args = [`"${tarPath.path}"`]; + const cacheFileName = utils.getCacheFileName(compressionMethod); + const tarFile = 'cache.tar'; + const workingDirectory = getWorkingDirectory(); + // Speficic args for BSD tar on windows for workaround + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + // Method specific args + switch (type) { + case 'create': + args.push('--posix', '-cf', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); + break; + case 'extract': + args.push('-xf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); + break; + case 'list': + args.push('-tf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); + break; + } + // Platform specific args + if (tarPath.type === constants_1.ArchiveToolType.GNU) { + switch (process.platform) { + case 'win32': + args.push('--force-local'); + break; + case 'darwin': + args.push('--delay-directory-restore'); + break; + } + } + return args; + }); +} +// Returns commands to run tar and compression program +function getCommands(compressionMethod, type, archivePath = '', tarExtraArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let args; + const tarPath = yield getTarPath(); + const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); + tarArgs.push(...tarExtraArgs); + const compressionArgs = type !== 'create' + ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) + : yield getCompressionProgram(tarPath, compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + if (BSD_TAR_ZSTD && type !== 'create') { + args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; + } + else { + args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; + } + if (BSD_TAR_ZSTD) { + return args; + } + return [args.join(' ')]; + }); +} +function getWorkingDirectory() { + var _a; + return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); +} +// Common function for extractTar and listTar to get the compression method +function getDecompressionProgram(tarPath, compressionMethod, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + // -d: Decompress. + // unzstd is equivalent to 'zstd -d' + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --long=30 --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; + default: + return ['-z']; + } + }); +} +// Used for creating the archive +// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. +// zstdmt is equivalent to 'zstd -T0' +// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. +// Using 30 here because we also support 32-bit self-hosted runners. +// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. +function getCompressionProgram(tarPath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const cacheFileName = utils.getCacheFileName(compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --long=30 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; + default: + return ['-z']; + } + }); +} +// Executes all commands as separate processes +function execCommands(commands, cwd) { + return __awaiter(this, void 0, void 0, function* () { + for (const command of commands) { + try { + yield (0, exec_1.exec)(command, undefined, { + cwd, + env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) + }); + } + catch (error) { + throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); + } } - } - options.state = WriterState.CloseTag; - r += options.spaceBeforeSlash + '>' + this.endline(node, options, level); - options.state = WriterState.None; - this.closeNode(node, options, level); - return r; - }; - - XMLWriterBase.prototype.dtdNotation = function(node, options, level) { - var r; - this.openNode(node, options, level); - options.state = WriterState.OpenTag; - r = this.indent(node, options, level) + '' + this.endline(node, options, level); - options.state = WriterState.None; - this.closeNode(node, options, level); - return r; - }; - - XMLWriterBase.prototype.openNode = function(node, options, level) {}; - - XMLWriterBase.prototype.closeNode = function(node, options, level) {}; - - XMLWriterBase.prototype.openAttribute = function(att, options, level) {}; - - XMLWriterBase.prototype.closeAttribute = function(att, options, level) {}; - - return XMLWriterBase; - - })(); - -}).call(this); + }); +} +// List the contents of a tar +function listTar(archivePath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const commands = yield getCommands(compressionMethod, 'list', archivePath); + yield execCommands(commands); + }); +} +exports.listTar = listTar; +// Extract a tar +function extractTar(archivePath, compressionMethod, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + // Create directory to extract tar into + const workingDirectory = getWorkingDirectory(); + yield io.mkdirP(workingDirectory); + const commands = yield getCommands(compressionMethod, 'extract', archivePath, extraTarArgs); + yield execCommands(commands); + }); +} +exports.extractTar = extractTar; +// Create a tar +function createTar(archiveFolder, sourceDirectories, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + // Write source directories to manifest.txt to avoid command length limits + (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); + const commands = yield getCommands(compressionMethod, 'create'); + yield execCommands(commands, archiveFolder); + }); +} +exports.createTar = createTar; /***/ }), -/***/ 2958: -/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { - -// Generated by CoffeeScript 1.12.7 -(function() { - var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; - - ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; - - XMLDOMImplementation = __nccwpck_require__(8310); - - XMLDocument = __nccwpck_require__(3730); - - XMLDocumentCB = __nccwpck_require__(7356); - - XMLStringWriter = __nccwpck_require__(5913); - - XMLStreamWriter = __nccwpck_require__(8601); - - NodeType = __nccwpck_require__(9267); +/***/ 3128: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - WriterState = __nccwpck_require__(9766); +"use strict"; - module.exports.create = function(name, xmldec, doctype, options) { - var doc, root; - if (name == null) { - throw new Error("Root element needs a name."); +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; } - options = assign({}, xmldec, doctype, options); - doc = new XMLDocument(options); - root = doc.element(name); - if (!options.headless) { - doc.declaration(options); - if ((options.pubID != null) || (options.sysID != null)) { - doc.dtd(options); - } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getDownloadOptions = exports.getUploadOptions = void 0; +const core = __importStar(__nccwpck_require__(2186)); +/** + * Returns a copy of the upload options with defaults filled in. + * + * @param copy the original upload options + */ +function getUploadOptions(copy) { + const result = { + uploadConcurrency: 4, + uploadChunkSize: 32 * 1024 * 1024 + }; + if (copy) { + if (typeof copy.uploadConcurrency === 'number') { + result.uploadConcurrency = copy.uploadConcurrency; + } + if (typeof copy.uploadChunkSize === 'number') { + result.uploadChunkSize = copy.uploadChunkSize; + } } - return root; - }; - - module.exports.begin = function(options, onData, onEnd) { - var ref1; - if (isFunction(options)) { - ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; - options = {}; + core.debug(`Upload concurrency: ${result.uploadConcurrency}`); + core.debug(`Upload chunk size: ${result.uploadChunkSize}`); + return result; +} +exports.getUploadOptions = getUploadOptions; +/** + * Returns a copy of the download options with defaults filled in. + * + * @param copy the original download options + */ +function getDownloadOptions(copy) { + const result = { + useAzureSdk: false, + concurrentBlobDownloads: true, + downloadConcurrency: 8, + timeoutInMs: 30000, + segmentTimeoutInMs: 600000, + lookupOnly: false + }; + if (copy) { + if (typeof copy.useAzureSdk === 'boolean') { + result.useAzureSdk = copy.useAzureSdk; + } + if (typeof copy.concurrentBlobDownloads === 'boolean') { + result.concurrentBlobDownloads = copy.concurrentBlobDownloads; + } + if (typeof copy.downloadConcurrency === 'number') { + result.downloadConcurrency = copy.downloadConcurrency; + } + if (typeof copy.timeoutInMs === 'number') { + result.timeoutInMs = copy.timeoutInMs; + } + if (typeof copy.segmentTimeoutInMs === 'number') { + result.segmentTimeoutInMs = copy.segmentTimeoutInMs; + } + if (typeof copy.lookupOnly === 'boolean') { + result.lookupOnly = copy.lookupOnly; + } } - if (onData) { - return new XMLDocumentCB(options, onData, onEnd); - } else { - return new XMLDocument(options); + const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; + if (segmentDownloadTimeoutMins && + !isNaN(Number(segmentDownloadTimeoutMins)) && + isFinite(Number(segmentDownloadTimeoutMins))) { + result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; } - }; - - module.exports.stringWriter = function(options) { - return new XMLStringWriter(options); - }; - - module.exports.streamWriter = function(stream, options) { - return new XMLStreamWriter(stream, options); - }; - - module.exports.implementation = new XMLDOMImplementation(); - - module.exports.nodeType = NodeType; - - module.exports.writerState = WriterState; - -}).call(this); + core.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core.debug(`Download concurrency: ${result.downloadConcurrency}`); + core.debug(`Request timeout (ms): ${result.timeoutInMs}`); + core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); + core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core.debug(`Lookup only: ${result.lookupOnly}`); + return result; +} +exports.getDownloadOptions = getDownloadOptions; /***/ }), @@ -62440,35 +62645,40 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; var Inputs; (function (Inputs) { - Inputs["Key"] = "key"; - Inputs["Path"] = "path"; - Inputs["RestoreKeys"] = "restore-keys"; - Inputs["UploadChunkSize"] = "upload-chunk-size"; - Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; - Inputs["RestoreKeyHit"] = "restore-key-hit"; - Inputs["ExtraRestoreKeys"] = "extra-restore-keys"; - Inputs["GCMacos"] = "gc-macos"; + Inputs["PrimaryKey"] = "primary-key"; + Inputs["RestorePrefixesFirstMatch"] = "restore-prefixes-first-match"; + Inputs["RestorePrefixesAllMatches"] = "restore-prefixes-all-matches"; + Inputs["SkipRestoreOnHitPrimaryKey"] = "skip-restore-on-primary-key-hit"; + Inputs["FailOn"] = "fail-on"; + Inputs["Nix"] = "nix"; + Inputs["Save"] = "save"; + Inputs["Paths"] = "paths"; + Inputs["PathsMacos"] = "paths-macos"; + Inputs["PathsLinux"] = "paths-linux"; + Inputs["GCMaxStoreSize"] = "gc-max-store-size"; Inputs["GCMaxStoreSizeMacos"] = "gc-max-store-size-macos"; - Inputs["GCLinux"] = "gc-linux"; Inputs["GCMaxStoreSizeLinux"] = "gc-max-store-size-linux"; - Inputs["Token"] = "token"; Inputs["Purge"] = "purge"; - Inputs["PurgeKeys"] = "purge-keys"; - Inputs["PurgeAccessed"] = "purge-accessed"; - Inputs["PurgeAccessedMaxAge"] = "purge-accessed-max-age"; + Inputs["PurgeOverwrite"] = "purge-overwrite"; + Inputs["PurgePrefixes"] = "purge-prefixes"; + Inputs["PurgeLastAccessed"] = "purge-last-accessed"; Inputs["PurgeCreated"] = "purge-created"; - Inputs["PurgeCreatedMaxAge"] = "purge-created-max-age"; // Input for cache, save action + Inputs["UploadChunkSize"] = "upload-chunk-size"; + Inputs["Token"] = "token"; // Input for cache, save actions })(Inputs = exports.Inputs || (exports.Inputs = {})); var Outputs; (function (Outputs) { - Outputs["CacheHit"] = "cache-hit"; - Outputs["CachePrimaryKey"] = "cache-primary-key"; - Outputs["CacheMatchedKey"] = "cache-matched-key"; // Output from restore action + Outputs["PrimaryKey"] = "primary-key"; + Outputs["Hit"] = "hit"; + Outputs["HitPrimaryKey"] = "hit-primary-key"; + Outputs["HitFirstMatch"] = "hit-first-match"; + Outputs["RestoredKey"] = "restored-key"; + Outputs["RestoredKeys"] = "restored-keys"; // Output from cache, restore actions })(Outputs = exports.Outputs || (exports.Outputs = {})); var State; (function (State) { - State["CachePrimaryKey"] = "CACHE_KEY"; - State["CacheMatchedKey"] = "CACHE_RESULT"; + State["CachePrimaryKey"] = "CACHE_PRIMARY_KEY"; + State["CacheRestoredKey"] = "CACHE_RESTORED_KEY"; })(State = exports.State || (exports.State = {})); var Events; (function (Events) { @@ -62481,38 +62691,7 @@ exports.RefKey = "GITHUB_REF"; /***/ }), -/***/ 4095: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const restoreImpl_1 = __importDefault(__nccwpck_require__(2357)); -const stateProvider_1 = __nccwpck_require__(1527); -function run() { - return __awaiter(this, void 0, void 0, function* () { - yield (0, restoreImpl_1.default)(new stateProvider_1.StateProvider()); - }); -} -run(); -exports["default"] = run; - - -/***/ }), - -/***/ 5084: +/***/ 7063: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62540,54 +62719,63 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.restoreExtraCaches = void 0; +exports.token = exports.uploadChunkSize = exports.purgeCreatedMaxAge = exports.purgeLastAccessed = exports.purgePrefixes = exports.purgeOverwrite = exports.purge = exports.gcMaxStoreSize = exports.paths = exports.save = exports.nix = exports.failOn = exports.skipRestoreOnHitPrimaryKey = exports.restorePrefixesAllMatches = exports.restorePrefixesFirstMatch = exports.primaryKey = void 0; const core = __importStar(__nccwpck_require__(2186)); const constants_1 = __nccwpck_require__(9042); -const utils = __importStar(__nccwpck_require__(6850)); -function restoreExtraCaches() { - return __awaiter(this, void 0, void 0, function* () { - const extraRestoreKeys = utils.getInputAsArray(constants_1.Inputs.ExtraRestoreKeys); - if (extraRestoreKeys.length == 0) { - return; - } - const token = core.getInput(constants_1.Inputs.Token, { required: true }); - utils.info(` - Restoring extra caches with keys: - ${utils.stringify(extraRestoreKeys)} - `); - const results = yield utils.getCachesByKeys(token, extraRestoreKeys); - utils.info(` - Found ${results.length} cache(s): - ${utils.stringify(results)} - `); - const cachePaths = utils.paths; - results.forEach((cache) => __awaiter(this, void 0, void 0, function* () { - utils.info(`Restoring a cache with the key ${cache.key}`); - if (cache.key !== undefined) { - const restoreKey = yield utils.getCacheKey({ - paths: cachePaths, - primaryKey: cache.key, - restoreKeys: [], - lookupOnly: false - }); - if (restoreKey) { - utils.info(`Restored a cache with the key ${cache.key}`); - } - } - })); - }); -} -exports.restoreExtraCaches = restoreExtraCaches; +const utils = __importStar(__nccwpck_require__(9378)); +exports.primaryKey = core.getInput(constants_1.Inputs.PrimaryKey, { required: true }); +exports.restorePrefixesFirstMatch = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesFirstMatch); +exports.restorePrefixesAllMatches = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesAllMatches); +exports.skipRestoreOnHitPrimaryKey = utils.getInputAsBool(constants_1.Inputs.SkipRestoreOnHitPrimaryKey); +exports.failOn = (() => { + var _a; + const failOnRaw = (_a = new RegExp("^(primary|first-match)\\.(miss|not-restored)$") + .exec(core.getInput(constants_1.Inputs.FailOn))) === null || _a === void 0 ? void 0 : _a.slice(1); + if (!failOnRaw) { + return; + } + const [keyType, result] = failOnRaw; + if ((keyType == "primary" || keyType == "first-match") && + (result == "miss" || result == "not-restored")) { + return { keyType, result }; + } +})(); +exports.nix = utils.getInputAsBool(constants_1.Inputs.Nix); +exports.save = utils.getInputAsBool(constants_1.Inputs.Save); +exports.paths = (exports.nix ? ["/nix/", "~/.cache/nix", "~root/.cache/nix"] : []).concat((() => { + const paths = utils.getInputAsArray(constants_1.Inputs.Paths); + const pathsPlatform = utils.getInputAsArray(utils.isLinux ? constants_1.Inputs.PathsLinux : constants_1.Inputs.PathsMacos); + if (pathsPlatform.length > 0) { + return pathsPlatform; + } + else + return paths; +})()); +exports.gcMaxStoreSize = exports.nix + ? (() => { + const gcMaxStoreSize = utils.getInputAsInt(constants_1.Inputs.GCMaxStoreSize); + const gcMaxStoreSizePlatform = utils.getInputAsInt(utils.isLinux + ? constants_1.Inputs.GCMaxStoreSizeLinux + : constants_1.Inputs.GCMaxStoreSizeMacos); + return gcMaxStoreSizePlatform + ? gcMaxStoreSizePlatform + : gcMaxStoreSize; + })() + : undefined; +exports.purge = utils.getInputAsBool(constants_1.Inputs.Purge); +exports.purgeOverwrite = (() => { + const purgeOverwrite = core.getInput(constants_1.Inputs.PurgeOverwrite); + if (!(purgeOverwrite == "always" || purgeOverwrite == "never")) { + return "default"; + } + return purgeOverwrite; +})(); +exports.purgePrefixes = utils.getInputAsArray(constants_1.Inputs.PurgePrefixes); +exports.purgeLastAccessed = utils.getInputAsInt(constants_1.Inputs.PurgeLastAccessed); +exports.purgeCreatedMaxAge = utils.getInputAsInt(constants_1.Inputs.PurgeCreated); +exports.uploadChunkSize = utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) || 32 * 1024 * 1024; +exports.token = core.getInput(constants_1.Inputs.Token, { required: true }); /***/ }), @@ -62630,100 +62818,155 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.restoreWithKey = void 0; +exports.restoreRun = exports.restoreOnlyRun = exports.restoreImpl = void 0; const core = __importStar(__nccwpck_require__(2186)); const constants_1 = __nccwpck_require__(9042); -const restoreExtraCaches_1 = __nccwpck_require__(5084); -const utils = __importStar(__nccwpck_require__(6850)); -function restoreWithKey(key, paths) { - return __awaiter(this, void 0, void 0, function* () { - utils.info(`Restoring a cache with the key "${key}".`); - utils.info(`::group::Logs are hidden. Errors are due to attempts to overwrite read-only paths.`); - yield utils.getCacheKey({ - paths, - primaryKey: key, - restoreKeys: [], - lookupOnly: false - }); - utils.info(`::endgroup::`); - utils.info(`Finished restoring the cache.`); - }); -} -exports.restoreWithKey = restoreWithKey; +const inputs = __importStar(__nccwpck_require__(7063)); +const stateProvider_1 = __nccwpck_require__(1527); +const utils = __importStar(__nccwpck_require__(4427)); +const restore = __importStar(__nccwpck_require__(8486)); function restoreImpl(stateProvider) { + var _a, _b, _c, _d, _e, _f, _g; return __awaiter(this, void 0, void 0, function* () { try { + core.setOutput(constants_1.Outputs.Hit, false); + core.setOutput(constants_1.Outputs.HitPrimaryKey, false); + core.setOutput(constants_1.Outputs.HitFirstMatch, false); + core.setOutput(constants_1.Outputs.RestoredKey, false); + core.setOutput(constants_1.Outputs.RestoredKeys, []); if (!utils.isCacheFeatureAvailable()) { - core.setOutput(constants_1.Outputs.CacheHit, "false"); return; } // Validate inputs, this can cause task failure if (!utils.isValidEvent()) { throw new Error(`Event Validation Error: The event type ${process.env[constants_1.Events.Key]} is not supported because it's not tied to a branch or tag ref.`); } - const primaryKey = core.getInput(constants_1.Inputs.Key, { required: true }); - stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey); - const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys); - const cachePaths = utils.paths; - const failOnCacheMiss = utils.getInputAsBool(constants_1.Inputs.FailOnCacheMiss); - const restoreKeyHit = utils.getInputAsBool(constants_1.Inputs.RestoreKeyHit); - utils.info(`Searching for a cache with the key "${primaryKey}".`); - let cacheKey = yield utils.getCacheKey({ - paths: cachePaths, - primaryKey, - restoreKeys: [], - lookupOnly: true - }); - if (cacheKey) { - yield restoreWithKey(cacheKey, cachePaths); + let restoredKey; + let lookedUpKey; + const restoredKeys = []; + const errorNot = (message) => new Error(` + No cache with the given key ${message}. + Exiting as the input "${constants_1.Inputs.FailOn}" is set. + `); + const errorNotFound = errorNot("was found"); + const errorNotRestored = errorNot("could be restored"); + { + const primaryKey = inputs.primaryKey; + stateProvider.setState(constants_1.State.CachePrimaryKey, primaryKey); + utils.info(`Searching for a cache with the key "${primaryKey}".`); + lookedUpKey = yield utils.restoreCache({ + primaryKey, + restoreKeys: [], + lookupOnly: true + }); + if (!lookedUpKey) { + if (((_a = inputs.failOn) === null || _a === void 0 ? void 0 : _a.keyType) == "primary" && + ((_b = inputs.failOn) === null || _b === void 0 ? void 0 : _b.result) == "miss") { + throw errorNotFound; + } + else { + utils.info(`Could not find a cache.`); + } + } + if (lookedUpKey && utils.isExactKeyMatch(primaryKey, lookedUpKey)) { + utils.info(`Found a cache with the given "${constants_1.Inputs.PrimaryKey}".`); + core.setOutput(constants_1.Outputs.HitPrimaryKey, true); + if (!inputs.skipRestoreOnHitPrimaryKey) { + restoredKey = yield restore.restoreCache(primaryKey); + if (restoredKey) { + restoredKeys.push(...[restoredKey]); + } + else if (((_c = inputs.failOn) === null || _c === void 0 ? void 0 : _c.keyType) == "primary" && + ((_d = inputs.failOn) === null || _d === void 0 ? void 0 : _d.result) == "not-restored") { + throw errorNotRestored; + } + } + } } - else { + if (inputs.restorePrefixesFirstMatch.length > 0 && + !restoredKey && + !(inputs.skipRestoreOnHitPrimaryKey && lookedUpKey)) { utils.info(` - No cache with the given primary key found. - Searching for a cache using restore keys: - ${JSON.stringify(restoreKeys)} + Searching for a cache using the "${constants_1.Inputs.RestorePrefixesFirstMatch}": + ${JSON.stringify(inputs.restorePrefixesFirstMatch)} `); - const restoreKey = yield utils.getCacheKey({ - paths: cachePaths, + const foundKey = yield utils.restoreCache({ primaryKey: "", - restoreKeys, + restoreKeys: inputs.restorePrefixesFirstMatch, lookupOnly: true }); - if (restoreKey) { - yield restoreWithKey(restoreKey, cachePaths); + if (!foundKey) { + if (((_e = inputs.failOn) === null || _e === void 0 ? void 0 : _e.keyType) == "first-match" && + inputs.failOn.result == "miss") { + throw errorNotFound; + } + else { + utils.info(`Could not find a cache.`); + } } - if (restoreKeyHit) { - cacheKey = restoreKey; + if (foundKey) { + utils.info(`Found a cache using the "${constants_1.Inputs.RestorePrefixesFirstMatch}".`); + core.setOutput(constants_1.Outputs.HitFirstMatch, true); + restoredKey = yield restore.restoreCache(foundKey); + if (restoredKey) { + restoredKeys.push(...[restoredKey]); + } + else if (((_f = inputs.failOn) === null || _f === void 0 ? void 0 : _f.keyType) == "first-match" && + ((_g = inputs.failOn) === null || _g === void 0 ? void 0 : _g.result) == "not-restored") { + throw errorNotRestored; + } } } - if (!cacheKey) { - if (failOnCacheMiss) { - throw new Error(` - Failed to restore a cache with the key "${primaryKey}". - Exiting as ${constants_1.Inputs.FailOnCacheMiss} is set. - `); - } - utils.info(` - Cache not found for input keys: - ${utils.stringify([primaryKey, ...restoreKeys])} - `); - yield (0, restoreExtraCaches_1.restoreExtraCaches)(); - return; + if (!(inputs.skipRestoreOnHitPrimaryKey && lookedUpKey)) { + restoredKeys.push(...(yield restore.restoreCaches())); } + restoredKey || (restoredKey = ""); // Store the matched cache key in states - stateProvider.setState(constants_1.State.CacheMatchedKey, cacheKey); - const isExactKeyMatch = utils.isExactKeyMatch(core.getInput(constants_1.Inputs.Key, { required: true }), cacheKey) || restoreKeyHit; - core.setOutput(constants_1.Outputs.CacheHit, isExactKeyMatch.toString()); - utils.info(`Cache restored from key: "${cacheKey}"`); - yield (0, restoreExtraCaches_1.restoreExtraCaches)(); - return cacheKey; + stateProvider.setState(constants_1.State.CacheRestoredKey, restoredKey); + core.setOutput(constants_1.Outputs.Hit, true); + core.setOutput(constants_1.Outputs.RestoredKey, restoredKey); + core.setOutput(constants_1.Outputs.RestoredKeys, restoredKeys); + return restoredKey; } catch (error) { core.setFailed(error.message); } }); } -exports["default"] = restoreImpl; +exports.restoreImpl = restoreImpl; +function run(stateProvider, earlyExit) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield restoreImpl(stateProvider); + } + catch (err) { + console.error(err); + if (earlyExit) { + process.exit(1); + } + } + // node will stay alive if any promises are not resolved, + // which is a possibility if HTTP requests are dangling + // due to retries or timeouts. We know that if we got here + // that all promises that we care about have successfully + // resolved, so simply exit with success. + if (earlyExit) { + process.exit(0); + } + }); +} +function restoreOnlyRun(earlyExit) { + return __awaiter(this, void 0, void 0, function* () { + yield run(new stateProvider_1.NullStateProvider(), earlyExit); + }); +} +exports.restoreOnlyRun = restoreOnlyRun; +function restoreRun(earlyExit) { + return __awaiter(this, void 0, void 0, function* () { + yield run(new stateProvider_1.StateProvider(), earlyExit); + }); +} +exports.restoreRun = restoreRun; /***/ }), @@ -62768,7 +63011,7 @@ class StateProviderBase { this.getState = (key) => ""; } getCacheState() { - const cacheKey = this.getState(constants_1.State.CacheMatchedKey); + const cacheKey = this.getState(constants_1.State.CacheRestoredKey); if (cacheKey) { core.debug(`Cache state/key: ${cacheKey}`); return cacheKey; @@ -62788,8 +63031,8 @@ class NullStateProvider extends StateProviderBase { constructor() { super(...arguments); this.stateToOutputMap = new Map([ - [constants_1.State.CacheMatchedKey, constants_1.Outputs.CacheMatchedKey], - [constants_1.State.CachePrimaryKey, constants_1.Outputs.CachePrimaryKey] + [constants_1.State.CacheRestoredKey, constants_1.Outputs.RestoredKey], + [constants_1.State.CachePrimaryKey, constants_1.Outputs.PrimaryKey] ]); this.setState = (key, value) => { core.setOutput(this.stateToOutputMap.get(key), value); @@ -62803,7 +63046,7 @@ exports.NullStateProvider = NullStateProvider; /***/ }), -/***/ 6850: +/***/ 4427: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62844,12 +63087,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.filterCachesByTime = exports.getCachesByKeys = exports.getCacheKey = exports.paths = exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; -const cache = __importStar(__nccwpck_require__(7799)); +exports.warning = exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.getCachesByKeys = exports.restoreCache = exports.isCacheFeatureAvailable = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; +const cache = __importStar(__nccwpck_require__(7942)); const core = __importStar(__nccwpck_require__(2186)); const github = __importStar(__nccwpck_require__(5438)); const dedent_1 = __importDefault(__nccwpck_require__(5281)); const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); function isGhes() { const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com"); return ghUrl.hostname.toUpperCase() !== "GITHUB.COM"; @@ -62864,7 +63108,7 @@ function isExactKeyMatch(key, cacheKey) { exports.isExactKeyMatch = isExactKeyMatch; function logWarning(message) { const warningPrefix = "[warning]"; - core.info(`${warningPrefix}${message}`); + core.warning(`${warningPrefix} ${message}`); } exports.logWarning = logWarning; function logError(message) { @@ -62873,58 +63117,39 @@ function logError(message) { } exports.logError = logError; // Cache token authorized for all events that are tied to a ref -// See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context +// See GitHub Context https://docs.github.com/en/actions/learn-github-actions/contexts function isValidEvent() { return constants_1.RefKey in process.env && Boolean(process.env[constants_1.RefKey]); } exports.isValidEvent = isValidEvent; -function getInputAsArray(name, options) { - return core - .getInput(name, options) - .split("\n") - .map(s => s.replace(/^!\s+/, "!").trim()) - .filter(x => x !== ""); -} -exports.getInputAsArray = getInputAsArray; -function getInputAsInt(name, options) { - const value = parseInt(core.getInput(name, options)); - if (isNaN(value) || value < 0) { - return undefined; - } - return value; -} -exports.getInputAsInt = getInputAsInt; -function getInputAsBool(name, options) { - const result = core.getInput(name, options); - return result.toLowerCase() === "true"; -} -exports.getInputAsBool = getInputAsBool; function isCacheFeatureAvailable() { if (cache.isFeatureAvailable()) { return true; } if (isGhes()) { - logWarning(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. + logError(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`); return false; } - logWarning("An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions."); + logError(` + Actions cache service is unavailable. + Please check https://www.githubstatus.com/ for any ongoing issue in Actions. + `); return false; } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; -exports.paths = ["/nix/", "~/.cache/nix", "~root/.cache/nix"]; -function getCacheKey({ paths, primaryKey, restoreKeys, lookupOnly }) { +function restoreCache({ primaryKey, restoreKeys, lookupOnly }) { return __awaiter(this, void 0, void 0, function* () { return yield cache.restoreCache( // https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 - paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false); + inputs.paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false, ["--skip-old-files"]); }); } -exports.getCacheKey = getCacheKey; -function getCachesByKeys(token, keys) { +exports.restoreCache = restoreCache; +function getCachesByKeys(keys) { return __awaiter(this, void 0, void 0, function* () { const caches = []; - const octokit = github.getOctokit(token); + const octokit = github.getOctokit(inputs.token); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; for (let page = 1; page <= 500; page += 1) { @@ -62939,32 +63164,22 @@ function getCachesByKeys(token, keys) { if (cachesRequest.actions_caches.length == 0) { break; } - caches.push(...cachesRequest.actions_caches); + if (isExactKeyMatch(inputs.primaryKey, key)) { + caches.push(...cachesRequest.actions_caches); + } } } return caches; }); } exports.getCachesByKeys = getCachesByKeys; -const filterCachesByTime = ({ caches, doUseLastAccessedTime, maxDate }) => caches.filter(cache => { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - return atDate < maxDate; - } - else - return false; -}); -exports.filterCachesByTime = filterCachesByTime; -const mkMessageWrongValue = (input, value) => `Wrong value for the input '${input}': ${value}`; +const mkMessageWrongValue = (input, value) => `Wrong value for the input "${input}": ${value}`; exports.mkMessageWrongValue = mkMessageWrongValue; function getMaxDate({ doUseLastAccessedTime, time }) { const inputMaxAge = doUseLastAccessedTime - ? constants_1.Inputs.PurgeAccessedMaxAge - : constants_1.Inputs.PurgeCreatedMaxAge; - const maxAge = core.getInput(inputMaxAge, { required: false }); + ? constants_1.Inputs.PurgeLastAccessed + : constants_1.Inputs.PurgeCreated; + const maxAge = core.getInput(inputMaxAge); const maxDate = new Date(time - Number.parseInt(maxAge) * 1000); if (maxDate === null) { throw new Error((0, exports.mkMessageWrongValue)(inputMaxAge, maxAge)); @@ -62974,10 +63189,161 @@ function getMaxDate({ doUseLastAccessedTime, time }) { exports.getMaxDate = getMaxDate; const stringify = (value) => JSON.stringify(value, null, 2); exports.stringify = stringify; -const info = (message) => { - core.info(dedent_1.default.withOptions({})(message)); -}; +const myDedent = dedent_1.default.withOptions({}); +const info = (message) => core.info(myDedent(message)); exports.info = info; +const warning = (message) => core.warning(myDedent(message)); +exports.warning = warning; + + +/***/ }), + +/***/ 9378: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isLinux = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = void 0; +const core = __importStar(__nccwpck_require__(2186)); +function getInputAsArray(name, options) { + return core + .getInput(name, options) + .split("\n") + .map(s => s.replace(/^!\s+/, "!").trim()) + .filter(x => x !== ""); +} +exports.getInputAsArray = getInputAsArray; +function getInputAsInt(name, options) { + const value = parseInt(core.getInput(name, options)); + if (isNaN(value) || value < 0) { + return undefined; + } + return value; +} +exports.getInputAsInt = getInputAsInt; +function getInputAsBool(name, options) { + const result = core.getInput(name, options); + return result.toLowerCase() === "true"; +} +exports.getInputAsBool = getInputAsBool; +exports.isLinux = process.platform === "linux"; + + +/***/ }), + +/***/ 8486: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.restoreCaches = exports.restoreCache = void 0; +const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +function restoreCache(key) { + return __awaiter(this, void 0, void 0, function* () { + utils.info(`Restoring a cache with the key "${key}".`); + const cacheKey = yield utils.restoreCache({ + primaryKey: key, + restoreKeys: [], + lookupOnly: false + }); + if (cacheKey) { + utils.info(`Finished restoring the cache.`); + return cacheKey; + } + else { + utils.info(`Failed to restore the cache.`); + } + }); +} +exports.restoreCache = restoreCache; +function restoreCaches() { + return __awaiter(this, void 0, void 0, function* () { + const restoredCaches = []; + if (inputs.restorePrefixesAllMatches.length == 0) { + return restoredCaches; + } + utils.info(` + Searching for caches using the "${constants_1.Inputs.RestorePrefixesAllMatches}": + + ${utils.stringify(inputs.restorePrefixesAllMatches)} + `); + const caches = yield utils.getCachesByKeys(inputs.restorePrefixesAllMatches); + utils.info(` + Found ${caches.length} cache(s): + + ${utils.stringify(caches)} + `); + for (const cache of caches) { + if (cache.key) { + const cacheKey = yield restoreCache(cache.key); + if (cacheKey) { + restoredCaches.push(...[cacheKey]); + } + } + } + return restoredCaches; + }); +} +exports.restoreCaches = restoreCaches; /***/ }), @@ -63196,12 +63562,18 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; /******/ /************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module is referenced by other modules so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(4095); -/******/ module.exports = __webpack_exports__; -/******/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { +"use strict"; +var exports = __webpack_exports__; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +const restoreImpl_1 = __nccwpck_require__(2357); +(0, restoreImpl_1.restoreRun)(true); + +})(); + +module.exports = __webpack_exports__; /******/ })() ; \ No newline at end of file diff --git a/dist/save-only/index.js b/dist/save-only/index.js index 9e655da3..2676c9b8 100644 --- a/dist/save-only/index.js +++ b/dist/save-only/index.js @@ -1,1815 +1,886 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 7799: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +/***/ 1898: +/***/ ((module, exports) => { -"use strict"; +exports = module.exports = SemVer -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const cacheHttpClient = __importStar(__nccwpck_require__(8245)); -const tar_1 = __nccwpck_require__(6490); -class ValidationError extends Error { - constructor(message) { - super(message); - this.name = 'ValidationError'; - Object.setPrototypeOf(this, ValidationError.prototype); - } -} -exports.ValidationError = ValidationError; -class ReserveCacheError extends Error { - constructor(message) { - super(message); - this.name = 'ReserveCacheError'; - Object.setPrototypeOf(this, ReserveCacheError.prototype); - } -} -exports.ReserveCacheError = ReserveCacheError; -function checkPaths(paths) { - if (!paths || paths.length === 0) { - throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); - } -} -function checkKey(key) { - if (key.length > 512) { - throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); - } - const regex = /^[^,]*$/; - if (!regex.test(key)) { - throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); - } -} -/** - * isFeatureAvailable to check the presence of Actions cache service - * - * @returns boolean return true if Actions cache service feature is available, otherwise false - */ -function isFeatureAvailable() { - return !!process.env['ACTIONS_CACHE_URL']; -} -exports.isFeatureAvailable = isFeatureAvailable; -/** - * Restores cache from keys - * - * @param paths a list of file paths to restore from the cache - * @param primaryKey an explicit key for restoring the cache - * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key - * @param downloadOptions cache download options - * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform - * @returns string returns the key for the cache hit, otherwise returns undefined - */ -function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - restoreKeys = restoreKeys || []; - const keys = [primaryKey, ...restoreKeys]; - core.debug('Resolved Keys:'); - core.debug(JSON.stringify(keys)); - if (keys.length > 10) { - throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); - } - for (const key of keys) { - checkKey(key); - } - const compressionMethod = yield utils.getCompressionMethod(); - let archivePath = ''; - try { - // path are needed to compute version - const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { - compressionMethod, - enableCrossOsArchive - }); - if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { - // Cache not found - return undefined; - } - if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core.info('Lookup only - skipping download'); - return cacheEntry.cacheKey; - } - archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - // Download the cache from the cache entry - yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core.info('Cache restored successfully'); - return cacheEntry.cacheKey; - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else { - // Supress all non-validation cache related errors because caching should be optional - core.warning(`Failed to restore: ${error.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return undefined; - }); -} -exports.restoreCache = restoreCache; -/** - * Saves a list of files with the specified key - * - * @param paths a list of file paths to be cached - * @param key an explicit key for restoring the cache - * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform - * @param options cache upload options - * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails - */ -function saveCache(paths, key, options, enableCrossOsArchive = false) { - var _a, _b, _c, _d, _e; - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - checkKey(key); - const compressionMethod = yield utils.getCompressionMethod(); - let cacheId = -1; - const cachePaths = yield utils.resolvePaths(paths); - core.debug('Cache Paths:'); - core.debug(`${JSON.stringify(cachePaths)}`); - if (cachePaths.length === 0) { - throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); - } - const archiveFolder = yield utils.createTempDirectory(); - const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - try { - yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.debug(`File Size: ${archiveFileSize}`); - // For GHES, this check will take place in ReserveCache API with enterprise file size limit - if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { - throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); - } - core.debug('Reserving Cache'); - const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { - compressionMethod, - enableCrossOsArchive, - cacheSize: archiveFileSize - }); - if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { - cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; - } - else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { - throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); - } - else { - throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); - } - core.debug(`Saving Cache (ID: ${cacheId})`); - yield cacheHttpClient.saveCache(cacheId, archivePath, options); - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else if (typedError.name === ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); - } - else { - core.warning(`Failed to save: ${typedError.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return cacheId; - }); +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} } -exports.saveCache = saveCache; -//# sourceMappingURL=cache.js.map -/***/ }), +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' -/***/ 8245: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 -"use strict"; +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const auth_1 = __nccwpck_require__(5526); -const crypto = __importStar(__nccwpck_require__(6113)); -const fs = __importStar(__nccwpck_require__(7147)); -const url_1 = __nccwpck_require__(7310); -const utils = __importStar(__nccwpck_require__(1518)); -const downloadUtils_1 = __nccwpck_require__(5500); -const options_1 = __nccwpck_require__(6215); -const requestUtils_1 = __nccwpck_require__(3981); -const versionSalt = '1.0'; -function getCacheApiUrl(resource) { - const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; - if (!baseUrl) { - throw new Error('Cache Service Url not found, unable to restore cache.'); - } - const url = `${baseUrl}_apis/artifactcache/${resource}`; - core.debug(`Resource Url: ${url}`); - return url; -} -function createAcceptHeader(type, apiVersion) { - return `${type};api-version=${apiVersion}`; -} -function getRequestOptions() { - const requestOptions = { - headers: { - Accept: createAcceptHeader('application/json', '6.0-preview.1') - } - }; - return requestOptions; -} -function createHttpClient() { - const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; - const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); - return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); -} -function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { - const components = paths; - // Add compression method to cache version to restore - // compressed cache as per compression method - if (compressionMethod) { - components.push(compressionMethod); - } - // Only check for windows platforms if enableCrossOsArchive is false - if (process.platform === 'win32' && !enableCrossOsArchive) { - components.push('windows-only'); - } - // Add salt to cache version to support breaking changes in cache entry - components.push(versionSalt); - return crypto - .createHash('sha256') - .update(components.join('|')) - .digest('hex'); -} -exports.getCacheVersion = getCacheVersion; -function getCacheEntry(keys, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - // Cache not found - if (response.statusCode === 204) { - // List cache for primary key only if cache miss occurs - if (core.isDebug()) { - yield printCachesListForDiagnostics(keys[0], httpClient, version); - } - return null; - } - if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { - throw new Error(`Cache service responded with ${response.statusCode}`); - } - const cacheResult = response.result; - const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; - if (!cacheDownloadUrl) { - // Cache achiveLocation not found. This should never happen, and hence bail out. - throw new Error('Cache not found.'); - } - core.setSecret(cacheDownloadUrl); - core.debug(`Cache Result:`); - core.debug(JSON.stringify(cacheResult)); - return cacheResult; - }); -} -exports.getCacheEntry = getCacheEntry; -function printCachesListForDiagnostics(key, httpClient, version) { - return __awaiter(this, void 0, void 0, function* () { - const resource = `caches?key=${encodeURIComponent(key)}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - if (response.statusCode === 200) { - const cacheListResult = response.result; - const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; - if (totalCount && totalCount > 0) { - core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); - for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); - } - } - } - }); -} -function downloadCache(archiveLocation, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const archiveUrl = new url_1.URL(archiveLocation); - const downloadOptions = (0, options_1.getDownloadOptions)(options); - if (downloadOptions.useAzureSdk && - archiveUrl.hostname.endsWith('.blob.core.windows.net')) { - // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. - yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); - } - else { - // Otherwise, download using the Actions http-client. - yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); - } - }); -} -exports.downloadCache = downloadCache; -// Reserve Cache -function reserveCache(key, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const reserveCacheRequest = { - key, - version, - cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize - }; - const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); - })); - return response; - }); -} -exports.reserveCache = reserveCache; -function getContentRange(start, end) { - // Format: `bytes start-end/filesize - // start and end are inclusive - // filesize can be * - // For a 200 byte chunk starting at byte 0: - // Content-Range: bytes 0-199/* - return `bytes ${start}-${end}/*`; -} -function uploadChunk(httpClient, resourceUrl, openStream, start, end) { - return __awaiter(this, void 0, void 0, function* () { - core.debug(`Uploading chunk of size ${end - - start + - 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); - const additionalHeaders = { - 'Content-Type': 'application/octet-stream', - 'Content-Range': getContentRange(start, end) - }; - const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { - return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); - })); - if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { - throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); - } - }); -} -function uploadFile(httpClient, cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - // Upload Chunks - const fileSize = utils.getArchiveFileSizeInBytes(archivePath); - const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs.openSync(archivePath, 'r'); - const uploadOptions = (0, options_1.getUploadOptions)(options); - const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); - const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); - const parallelUploads = [...new Array(concurrency).keys()]; - core.debug('Awaiting all uploads'); - let offset = 0; - try { - yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { - while (offset < fileSize) { - const chunkSize = Math.min(fileSize - offset, maxChunkSize); - const start = offset; - const end = offset + chunkSize - 1; - offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs - .createReadStream(archivePath, { - fd, - start, - end, - autoClose: false - }) - .on('error', error => { - throw new Error(`Cache upload failed because file read failed with ${error.message}`); - }), start, end); - } - }))); - } - finally { - fs.closeSync(fd); - } - return; - }); -} -function commitCache(httpClient, cacheId, filesize) { - return __awaiter(this, void 0, void 0, function* () { - const commitCacheRequest = { size: filesize }; - return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); - })); - }); -} -function saveCache(cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - core.debug('Upload cache'); - yield uploadFile(httpClient, cacheId, archivePath, options); - // Commit Cache - core.debug('Commiting cache'); - const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); - const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); - if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { - throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); - } - core.info('Cache saved successfully'); - }); +var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + +// The actual regexps go on exports.re +var re = exports.re = [] +var safeRe = exports.safeRe = [] +var src = exports.src = [] +var t = exports.tokens = {} +var R = 0 + +function tok (n) { + t[n] = R++ } -exports.saveCache = saveCache; -//# sourceMappingURL=cacheHttpClient.js.map -/***/ }), +var LETTERDASHNUMBER = '[a-zA-Z0-9-]' -/***/ 1518: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +var safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] -"use strict"; +function makeSafeRe (value) { + for (var i = 0; i < safeRegexReplacements.length; i++) { + var token = safeRegexReplacements[i][0] + var max = safeRegexReplacements[i][1] + value = value + .split(token + '*').join(token + '{0,' + max + '}') + .split(token + '+').join(token + '{1,' + max + '}') + } + return value +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const exec = __importStar(__nccwpck_require__(1514)); -const glob = __importStar(__nccwpck_require__(8090)); -const io = __importStar(__nccwpck_require__(7436)); -const fs = __importStar(__nccwpck_require__(7147)); -const path = __importStar(__nccwpck_require__(1017)); -const semver = __importStar(__nccwpck_require__(3771)); -const util = __importStar(__nccwpck_require__(3837)); -const uuid_1 = __nccwpck_require__(2155); -const constants_1 = __nccwpck_require__(8840); -// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 -function createTempDirectory() { - return __awaiter(this, void 0, void 0, function* () { - const IS_WINDOWS = process.platform === 'win32'; - let tempDirectory = process.env['RUNNER_TEMP'] || ''; - if (!tempDirectory) { - let baseLocation; - if (IS_WINDOWS) { - // On Windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } - else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } - else { - baseLocation = '/home'; - } - } - tempDirectory = path.join(baseLocation, 'actions', 'temp'); - } - const dest = path.join(tempDirectory, (0, uuid_1.v4)()); - yield io.mkdirP(dest); - return dest; - }); -} -exports.createTempDirectory = createTempDirectory; -function getArchiveFileSizeInBytes(filePath) { - return fs.statSync(filePath).size; -} -exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; -function resolvePaths(patterns) { - var e_1, _a; - var _b; - return __awaiter(this, void 0, void 0, function* () { - const paths = []; - const workspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); - const globber = yield glob.create(patterns.join('\n'), { - implicitDescendants: false - }); - try { - for (var _c = __asyncValues(globber.globGenerator()), _d; _d = yield _c.next(), !_d.done;) { - const file = _d.value; - const relativeFile = path - .relative(workspace, file) - .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); - core.debug(`Matched: ${relativeFile}`); - // Paths are made relative so the tar entries are all relative to the root of the workspace. - if (relativeFile === '') { - // path.relative returns empty string if workspace and file are equal - paths.push('.'); - } - else { - paths.push(`${relativeFile}`); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } - return paths; - }); -} -exports.resolvePaths = resolvePaths; -function unlinkFile(filePath) { - return __awaiter(this, void 0, void 0, function* () { - return util.promisify(fs.unlink)(filePath); - }); -} -exports.unlinkFile = unlinkFile; -function getVersion(app, additionalArgs = []) { - return __awaiter(this, void 0, void 0, function* () { - let versionOutput = ''; - additionalArgs.push('--version'); - core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); - try { - yield exec.exec(`${app}`, additionalArgs, { - ignoreReturnCode: true, - silent: true, - listeners: { - stdout: (data) => (versionOutput += data.toString()), - stderr: (data) => (versionOutput += data.toString()) - } - }); - } - catch (err) { - core.debug(err.message); - } - versionOutput = versionOutput.trim(); - core.debug(versionOutput); - return versionOutput; - }); +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +tok('NUMERICIDENTIFIER') +src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' +tok('NUMERICIDENTIFIERLOOSE') +src[t.NUMERICIDENTIFIERLOOSE] = '\\d+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +tok('NONNUMERICIDENTIFIER') +src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*' + +// ## Main Version +// Three dot-separated numeric identifiers. + +tok('MAINVERSION') +src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')' + +tok('MAINVERSIONLOOSE') +src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +tok('PRERELEASEIDENTIFIER') +src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +tok('PRERELEASEIDENTIFIERLOOSE') +src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +tok('PRERELEASE') +src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' + +tok('PRERELEASELOOSE') +src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +tok('BUILDIDENTIFIER') +src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+' + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +tok('BUILD') +src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + + '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +tok('FULL') +tok('FULLPLAIN') +src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + + src[t.PRERELEASE] + '?' + + src[t.BUILD] + '?' + +src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +tok('LOOSEPLAIN') +src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + + src[t.PRERELEASELOOSE] + '?' + + src[t.BUILD] + '?' + +tok('LOOSE') +src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + +tok('GTLT') +src[t.GTLT] = '((?:<|>)?=?)' + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +tok('XRANGEIDENTIFIERLOOSE') +src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +tok('XRANGEIDENTIFIER') +src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + +tok('XRANGEPLAIN') +src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:' + src[t.PRERELEASE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGEPLAINLOOSE') +src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[t.PRERELEASELOOSE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGE') +src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' +tok('XRANGELOOSE') +src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +tok('COERCE') +src[t.COERCE] = '(^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' +tok('COERCERTL') +re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') +safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g') + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +tok('LONETILDE') +src[t.LONETILDE] = '(?:~>?)' + +tok('TILDETRIM') +src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' +re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') +safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g') +var tildeTrimReplace = '$1~' + +tok('TILDE') +src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' +tok('TILDELOOSE') +src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +tok('LONECARET') +src[t.LONECARET] = '(?:\\^)' + +tok('CARETTRIM') +src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' +re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') +safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g') +var caretTrimReplace = '$1^' + +tok('CARET') +src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' +tok('CARETLOOSE') +src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +tok('COMPARATORLOOSE') +src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' +tok('COMPARATOR') +src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +tok('COMPARATORTRIM') +src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + + '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' + +// this one has to use the /g flag +re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') +safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +tok('HYPHENRANGE') +src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAIN] + ')' + + '\\s*$' + +tok('HYPHENRANGELOOSE') +src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s*$' + +// Star ranges basically just allow anything at all. +tok('STAR') +src[t.STAR] = '(<|>)?=?\\s*\\*' + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + safeRe[i] = new RegExp(makeSafeRe(src[i])) + } } -// Use zstandard if possible to maximize cache performance -function getCompressionMethod() { - return __awaiter(this, void 0, void 0, function* () { - const versionOutput = yield getVersion('zstd', ['--quiet']); - const version = semver.clean(versionOutput); - core.debug(`zstd version: ${version}`); - if (versionOutput === '') { - return constants_1.CompressionMethod.Gzip; - } - else { - return constants_1.CompressionMethod.ZstdWithoutLong; - } - }); + +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } } -exports.getCompressionMethod = getCompressionMethod; -function getCacheFileName(compressionMethod) { - return compressionMethod === constants_1.CompressionMethod.Gzip - ? constants_1.CacheFilename.Gzip - : constants_1.CacheFilename.Zstd; + +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null } -exports.getCacheFileName = getCacheFileName; -function getGnuTarPathOnWindows() { - return __awaiter(this, void 0, void 0, function* () { - if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { - return constants_1.GnuTarPathOnWindows; - } - const versionOutput = yield getVersion('tar'); - return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; - }); + +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null } -exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; -function assertDefined(name, value) { - if (value === undefined) { - throw Error(`Expected ${name} but value was undefiend`); + +exports.SemVer = SemVer + +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } - return value; -} -exports.assertDefined = assertDefined; -function isGhes() { - const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; -} -exports.isGhes = isGhes; -//# sourceMappingURL=cacheUtils.js.map + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } -/***/ }), + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } -/***/ 8840: -/***/ ((__unused_webpack_module, exports) => { + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } -"use strict"; + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; -var CacheFilename; -(function (CacheFilename) { - CacheFilename["Gzip"] = "cache.tgz"; - CacheFilename["Zstd"] = "cache.tzst"; -})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); -var CompressionMethod; -(function (CompressionMethod) { - CompressionMethod["Gzip"] = "gzip"; - // Long range mode was added to zstd in v1.3.2. - // This enum is for earlier version of zstd that does not have --long support - CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; - CompressionMethod["Zstd"] = "zstd"; -})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); -var ArchiveToolType; -(function (ArchiveToolType) { - ArchiveToolType["GNU"] = "gnu"; - ArchiveToolType["BSD"] = "bsd"; -})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); -// The default number of retry attempts. -exports.DefaultRetryAttempts = 2; -// The default delay in milliseconds between retry attempts. -exports.DefaultRetryDelay = 5000; -// Socket timeout in milliseconds during download. If no traffic is received -// over the socket during this period, the socket is destroyed and the download -// is aborted. -exports.SocketTimeout = 5000; -// The default path of GNUtar on hosted Windows runners -exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; -// The default path of BSDtar on hosted Windows runners -exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; -exports.TarFilename = 'cache.tar'; -exports.ManifestFilename = 'manifest.txt'; -//# sourceMappingURL=constants.js.map + var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]) -/***/ }), + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } -/***/ 5500: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + this.raw = version -"use strict"; + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.downloadCacheStorageSDK = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const storage_blob_1 = __nccwpck_require__(4100); -const buffer = __importStar(__nccwpck_require__(4300)); -const fs = __importStar(__nccwpck_require__(7147)); -const stream = __importStar(__nccwpck_require__(2781)); -const util = __importStar(__nccwpck_require__(3837)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const requestUtils_1 = __nccwpck_require__(3981); -const abort_controller_1 = __nccwpck_require__(2557); -/** - * Pipes the body of a HTTP response to a stream - * - * @param response the HTTP response - * @param output the writable stream - */ -function pipeResponseToStream(response, output) { - return __awaiter(this, void 0, void 0, function* () { - const pipeline = util.promisify(stream.pipeline); - yield pipeline(response.message, output); - }); -} -/** - * Class for tracking the download state and displaying stats. - */ -class DownloadProgress { - constructor(contentLength) { - this.contentLength = contentLength; - this.segmentIndex = 0; - this.segmentSize = 0; - this.segmentOffset = 0; - this.receivedBytes = 0; - this.displayedComplete = false; - this.startTime = Date.now(); - } - /** - * Progress to the next segment. Only call this method when the previous segment - * is complete. - * - * @param segmentSize the length of the next segment - */ - nextSegment(segmentSize) { - this.segmentOffset = this.segmentOffset + this.segmentSize; - this.segmentIndex = this.segmentIndex + 1; - this.segmentSize = segmentSize; - this.receivedBytes = 0; - core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); - } - /** - * Sets the number of bytes received for the current segment. - * - * @param receivedBytes the number of bytes received - */ - setReceivedBytes(receivedBytes) { - this.receivedBytes = receivedBytes; - } - /** - * Returns the total number of bytes transferred. - */ - getTransferredBytes() { - return this.segmentOffset + this.receivedBytes; - } - /** - * Returns true if the download is complete. - */ - isDone() { - return this.getTransferredBytes() === this.contentLength; - } - /** - * Prints the current download stats. Once the download completes, this will print one - * last line and then stop. - */ - display() { - if (this.displayedComplete) { - return; - } - const transferredBytes = this.segmentOffset + this.receivedBytes; - const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); - const elapsedTime = Date.now() - this.startTime; - const downloadSpeed = (transferredBytes / - (1024 * 1024) / - (elapsedTime / 1000)).toFixed(1); - core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); - if (this.isDone()) { - this.displayedComplete = true; - } - } - /** - * Returns a function used to handle TransferProgressEvents. - */ - onProgress() { - return (progress) => { - this.setReceivedBytes(progress.loadedBytes); - }; - } - /** - * Starts the timer that displays the stats. - * - * @param delayInMs the delay between each write - */ - startDisplayTimer(delayInMs = 1000) { - const displayCallback = () => { - this.display(); - if (!this.isDone()) { - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - }; - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - /** - * Stops the timer that displays the stats. As this typically indicates the download - * is complete, this will display one last line, unless the last line has already - * been written. - */ - stopDisplayTimer() { - if (this.timeoutHandle) { - clearTimeout(this.timeoutHandle); - this.timeoutHandle = undefined; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num } - this.display(); - } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() } -exports.DownloadProgress = DownloadProgress; -/** - * Download the cache using the Actions toolkit http-client - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - */ -function downloadCacheHttpClient(archiveLocation, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - const writeStream = fs.createWriteStream(archivePath); - const httpClient = new http_client_1.HttpClient('actions/cache'); - const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); - // Abort download if no traffic received over the socket. - downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { - downloadResponse.message.destroy(); - core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); - }); - yield pipeResponseToStream(downloadResponse, writeStream); - // Validate download size. - const contentLengthHeader = downloadResponse.message.headers['content-length']; - if (contentLengthHeader) { - const expectedLength = parseInt(contentLengthHeader); - const actualLength = utils.getArchiveFileSizeInBytes(archivePath); - if (actualLength !== expectedLength) { - throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); - } - } - else { - core.debug('Unable to validate download, no Content-Length header'); - } - }); + +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version } -exports.downloadCacheHttpClient = downloadCacheHttpClient; -/** - * Download the cache using the Azure Storage SDK. Only call this method if the - * URL points to an Azure Storage endpoint. - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - * @param options the download options with the defaults set - */ -function downloadCacheStorageSDK(archiveLocation, archivePath, options) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { - retryOptions: { - // Override the timeout used when downloading each 4 MB chunk - // The default is 2 min / MB, which is way too slow - tryTimeoutInMs: options.timeoutInMs - } - }); - const properties = yield client.getProperties(); - const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; - if (contentLength < 0) { - // We should never hit this condition, but just in case fall back to downloading the - // file as one large stream - core.debug('Unable to determine content length, downloading file with http-client...'); - yield downloadCacheHttpClient(archiveLocation, archivePath); - } - else { - // Use downloadToBuffer for faster downloads, since internally it splits the - // file into 4 MB chunks which can then be parallelized and retried independently - // - // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB - // on 64-bit systems), split the download into multiple segments - // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. - // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast - const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); - const downloadProgress = new DownloadProgress(contentLength); - const fd = fs.openSync(archivePath, 'w'); - try { - downloadProgress.startDisplayTimer(); - const controller = new abort_controller_1.AbortController(); - const abortSignal = controller.signal; - while (!downloadProgress.isDone()) { - const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; - const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); - downloadProgress.nextSegment(segmentSize); - const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { - abortSignal, - concurrency: options.downloadConcurrency, - onProgress: downloadProgress.onProgress() - })); - if (result === 'timeout') { - controller.abort(); - throw new Error('Aborting cache download as the download time exceeded the timeout.'); - } - else if (Buffer.isBuffer(result)) { - fs.writeFileSync(fd, result); - } - } - } - finally { - downloadProgress.stopDisplayTimer(); - fs.closeSync(fd); - } - } - }); + +SemVer.prototype.toString = function () { + return this.version } -exports.downloadCacheStorageSDK = downloadCacheStorageSDK; -const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { - let timeoutHandle; - const timeoutPromise = new Promise(resolve => { - timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); - }); - return Promise.race([promise, timeoutPromise]).then(result => { - clearTimeout(timeoutHandle); - return result; - }); -}); -//# sourceMappingURL=downloadUtils.js.map -/***/ }), +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -/***/ 3981: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + return this.compareMain(other) || this.comparePre(other) +} -"use strict"; +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const constants_1 = __nccwpck_require__(8840); -function isSuccessStatusCode(statusCode) { - if (!statusCode) { - return false; - } - return statusCode >= 200 && statusCode < 300; + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) } -exports.isSuccessStatusCode = isSuccessStatusCode; -function isServerErrorStatusCode(statusCode) { - if (!statusCode) { - return true; + +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - return statusCode >= 500; + } while (++i) } -exports.isServerErrorStatusCode = isServerErrorStatusCode; -function isRetryableStatusCode(statusCode) { - if (!statusCode) { - return false; + +SemVer.prototype.compareBuild = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + var i = 0 + do { + var a = this.build[i] + var b = other.build[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - const retryableStatusCodes = [ - http_client_1.HttpCodes.BadGateway, - http_client_1.HttpCodes.ServiceUnavailable, - http_client_1.HttpCodes.GatewayTimeout - ]; - return retryableStatusCodes.includes(statusCode); -} -exports.isRetryableStatusCode = isRetryableStatusCode; -function sleep(milliseconds) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise(resolve => setTimeout(resolve, milliseconds)); - }); -} -function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { - return __awaiter(this, void 0, void 0, function* () { - let errorMessage = ''; - let attempt = 1; - while (attempt <= maxAttempts) { - let response = undefined; - let statusCode = undefined; - let isRetryable = false; - try { - response = yield method(); - } - catch (error) { - if (onError) { - response = onError(error); - } - isRetryable = true; - errorMessage = error.message; - } - if (response) { - statusCode = getStatusCode(response); - if (!isServerErrorStatusCode(statusCode)) { - return response; - } - } - if (statusCode) { - isRetryable = isRetryableStatusCode(statusCode); - errorMessage = `Cache service responded with ${statusCode}`; - } - core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); - if (!isRetryable) { - core.debug(`${name} - Error is not retryable`); - break; - } - yield sleep(delay); - attempt++; - } - throw Error(`${name} failed: ${errorMessage}`); - }); -} -exports.retry = retry; -function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, - // If the error object contains the statusCode property, extract it and return - // an TypedResponse so it can be processed by the retry logic. - (error) => { - if (error instanceof http_client_1.HttpClientError) { - return { - statusCode: error.statusCode, - result: null, - headers: {}, - error - }; - } - else { - return undefined; - } - }); - }); -} -exports.retryTypedResponse = retryTypedResponse; -function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); - }); + } while (++i) } -exports.retryHttpClientResponse = retryHttpClientResponse; -//# sourceMappingURL=requestUtils.js.map - -/***/ }), -/***/ 6490: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.createTar = exports.extractTar = exports.listTar = void 0; -const exec_1 = __nccwpck_require__(1514); -const io = __importStar(__nccwpck_require__(7436)); -const fs_1 = __nccwpck_require__(7147); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const IS_WINDOWS = process.platform === 'win32'; -// Returns tar path and type: BSD or GNU -function getTarPath() { - return __awaiter(this, void 0, void 0, function* () { - switch (process.platform) { - case 'win32': { - const gnuTar = yield utils.getGnuTarPathOnWindows(); - const systemTar = constants_1.SystemTarPathOnWindows; - if (gnuTar) { - // Use GNUtar as default on windows - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else if ((0, fs_1.existsSync)(systemTar)) { - return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; - } - break; - } - case 'darwin': { - const gnuTar = yield io.which('gtar', false); - if (gnuTar) { - // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else { - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.BSD - }; - } - } - default: - break; - } - // Default assumption is GNU tar is present in path - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.GNU - }; - }); -} -// Return arguments for tar as per tarPath, compressionMethod, method type and os -function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - const args = [`"${tarPath.path}"`]; - const cacheFileName = utils.getCacheFileName(compressionMethod); - const tarFile = 'cache.tar'; - const workingDirectory = getWorkingDirectory(); - // Speficic args for BSD tar on windows for workaround - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - // Method specific args - switch (type) { - case 'create': - args.push('--posix', '-cf', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); - break; - case 'extract': - args.push('-xf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); - break; - case 'list': - args.push('-tf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); - break; - } - // Platform specific args - if (tarPath.type === constants_1.ArchiveToolType.GNU) { - switch (process.platform) { - case 'win32': - args.push('--force-local'); - break; - case 'darwin': - args.push('--delay-directory-restore'); - break; - } - } - return args; - }); -} -// Returns commands to run tar and compression program -function getCommands(compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - let args; - const tarPath = yield getTarPath(); - const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); - const compressionArgs = type !== 'create' - ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) - : yield getCompressionProgram(tarPath, compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - if (BSD_TAR_ZSTD && type !== 'create') { - args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; - } - else { - args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; - } - if (BSD_TAR_ZSTD) { - return args; - } - return [args.join(' ')]; - }); -} -function getWorkingDirectory() { - var _a; - return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); -} -// Common function for extractTar and listTar to get the compression method -function getDecompressionProgram(tarPath, compressionMethod, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - // -d: Decompress. - // unzstd is equivalent to 'zstd -d' - // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. - // Using 30 here because we also support 32-bit self-hosted runners. - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --long=30 --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; - default: - return ['-z']; + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } } - }); -} -// Used for creating the archive -// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. -// zstdmt is equivalent to 'zstd -T0' -// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. -// Using 30 here because we also support 32-bit self-hosted runners. -// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. -function getCompressionProgram(tarPath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const cacheFileName = utils.getCacheFileName(compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --long=30 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; - default: - return ['-z']; + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) } - }); -} -// Executes all commands as separate processes -function execCommands(commands, cwd) { - return __awaiter(this, void 0, void 0, function* () { - for (const command of commands) { - try { - yield (0, exec_1.exec)(command, undefined, { - cwd, - env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) - }); - } - catch (error) { - throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); - } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] } - }); -} -// List the contents of a tar -function listTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const commands = yield getCommands(compressionMethod, 'list', archivePath); - yield execCommands(commands); - }); -} -exports.listTar = listTar; -// Extract a tar -function extractTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Create directory to extract tar into - const workingDirectory = getWorkingDirectory(); - yield io.mkdirP(workingDirectory); - const commands = yield getCommands(compressionMethod, 'extract', archivePath); - yield execCommands(commands); - }); -} -exports.extractTar = extractTar; -// Create a tar -function createTar(archiveFolder, sourceDirectories, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Write source directories to manifest.txt to avoid command length limits - (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); - const commands = yield getCommands(compressionMethod, 'create'); - yield execCommands(commands, archiveFolder); - }); -} -exports.createTar = createTar; -//# sourceMappingURL=tar.js.map + } + break -/***/ }), + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} -/***/ 6215: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } -"use strict"; + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getDownloadOptions = exports.getUploadOptions = void 0; -const core = __importStar(__nccwpck_require__(2186)); -/** - * Returns a copy of the upload options with defaults filled in. - * - * @param copy the original upload options - */ -function getUploadOptions(copy) { - const result = { - uploadConcurrency: 4, - uploadChunkSize: 32 * 1024 * 1024 - }; - if (copy) { - if (typeof copy.uploadConcurrency === 'number') { - result.uploadConcurrency = copy.uploadConcurrency; - } - if (typeof copy.uploadChunkSize === 'number') { - result.uploadChunkSize = copy.uploadChunkSize; - } +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' } - core.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core.debug(`Upload chunk size: ${result.uploadChunkSize}`); - return result; -} -exports.getUploadOptions = getUploadOptions; -/** - * Returns a copy of the download options with defaults filled in. - * - * @param copy the original download options - */ -function getDownloadOptions(copy) { - const result = { - useAzureSdk: true, - downloadConcurrency: 8, - timeoutInMs: 30000, - segmentTimeoutInMs: 600000, - lookupOnly: false - }; - if (copy) { - if (typeof copy.useAzureSdk === 'boolean') { - result.useAzureSdk = copy.useAzureSdk; - } - if (typeof copy.downloadConcurrency === 'number') { - result.downloadConcurrency = copy.downloadConcurrency; - } - if (typeof copy.timeoutInMs === 'number') { - result.timeoutInMs = copy.timeoutInMs; - } - if (typeof copy.segmentTimeoutInMs === 'number') { - result.segmentTimeoutInMs = copy.segmentTimeoutInMs; - } - if (typeof copy.lookupOnly === 'boolean') { - result.lookupOnly = copy.lookupOnly; + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key } + } } - const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; - if (segmentDownloadTimeoutMins && - !isNaN(Number(segmentDownloadTimeoutMins)) && - isFinite(Number(segmentDownloadTimeoutMins))) { - result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; - } - core.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core.debug(`Download concurrency: ${result.downloadConcurrency}`); - core.debug(`Request timeout (ms): ${result.timeoutInMs}`); - core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); - core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - core.debug(`Lookup only: ${result.lookupOnly}`); - return result; + return defaultResult // may be undefined + } } -exports.getDownloadOptions = getDownloadOptions; -//# sourceMappingURL=options.js.map - -/***/ }), -/***/ 3771: -/***/ ((module, exports) => { +exports.compareIdentifiers = compareIdentifiers -exports = module.exports = SemVer +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) + if (anum && bnum) { + a = +a + b = +b } -} else { - debug = function () {} -} - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var t = exports.tokens = {} -var R = 0 +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} -function tok (n) { - t[n] = R++ +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor } -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -tok('NUMERICIDENTIFIER') -src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' -tok('NUMERICIDENTIFIERLOOSE') -src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -tok('NONNUMERICIDENTIFIER') -src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - -// ## Main Version -// Three dot-separated numeric identifiers. +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} -tok('MAINVERSION') -src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')' +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} -tok('MAINVERSIONLOOSE') -src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. +exports.compareBuild = compareBuild +function compareBuild (a, b, loose) { + var versionA = new SemVer(a, loose) + var versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} -tok('PRERELEASEIDENTIFIER') -src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} -tok('PRERELEASEIDENTIFIERLOOSE') -src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(a, b, loose) + }) +} -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(b, a, loose) + }) +} -tok('PRERELEASE') -src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} -tok('PRERELEASELOOSE') -src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} -tok('BUILDIDENTIFIER') -src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} -tok('BUILD') -src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + - '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b -tok('FULL') -tok('FULLPLAIN') -src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + - src[t.PRERELEASE] + '?' + - src[t.BUILD] + '?' + case '': + case '=': + case '==': + return eq(a, b, loose) -src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + case '!=': + return neq(a, b, loose) -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -tok('LOOSEPLAIN') -src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + - src[t.PRERELEASELOOSE] + '?' + - src[t.BUILD] + '?' + case '>': + return gt(a, b, loose) -tok('LOOSE') -src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + case '>=': + return gte(a, b, loose) -tok('GTLT') -src[t.GTLT] = '((?:<|>)?=?)' + case '<': + return lt(a, b, loose) -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -tok('XRANGEIDENTIFIERLOOSE') -src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -tok('XRANGEIDENTIFIER') -src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + case '<=': + return lte(a, b, loose) -tok('XRANGEPLAIN') -src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:' + src[t.PRERELEASE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' + default: + throw new TypeError('Invalid operator: ' + op) + } +} -tok('XRANGEPLAINLOOSE') -src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[t.PRERELEASELOOSE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -tok('XRANGE') -src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' -tok('XRANGELOOSE') -src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -tok('COERCE') -src[t.COERCE] = '(^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' -tok('COERCERTL') -re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } -// Tilde ranges. -// Meaning is "reasonably at or greater than" -tok('LONETILDE') -src[t.LONETILDE] = '(?:~>?)' + comp = comp.trim().split(/\s+/).join(' ') + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) -tok('TILDETRIM') -src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' -re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') -var tildeTrimReplace = '$1~' + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } -tok('TILDE') -src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' -tok('TILDELOOSE') -src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + debug('comp', this) +} -// Caret ranges. -// Meaning is "at least and backwards compatible with" -tok('LONECARET') -src[t.LONECARET] = '(?:\\^)' +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var m = comp.match(r) -tok('CARETTRIM') -src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' -re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') -var caretTrimReplace = '$1^' + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } -tok('CARET') -src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' -tok('CARETLOOSE') -src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } -// A simple gt/lt/eq thing, or just "" to indicate "any version" -tok('COMPARATORLOOSE') -src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' -tok('COMPARATOR') -src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -tok('COMPARATORTRIM') -src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + - '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' +Comparator.prototype.toString = function () { + return this.value +} -// this one has to use the /g flag -re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -tok('HYPHENRANGE') -src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAIN] + ')' + - '\\s*$' + if (this.semver === ANY || version === ANY) { + return true + } -tok('HYPHENRANGELOOSE') -src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s*$' + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } -// Star ranges basically just allow anything at all. -tok('STAR') -src[t.STAR] = '(<|>)?=?\\s*\\*' + return cmp(version, this.operator, this.semver, this.options) +} -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') } -} -exports.parse = parse -function parse (version, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, @@ -1817,985 +888,409 @@ function parse (version, options) { } } - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - var r = options.loose ? re[t.LOOSE] : re[t.FULL] - if (!r.test(version)) { - return null - } + var rangeTmp - try { - return new SemVer(version, options) - } catch (er) { - return null + if (this.operator === '') { + if (this.value === '') { + return true + } + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) } -} -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan } -exports.SemVer = SemVer - -function SemVer (version, options) { +exports.Range = Range +function Range (range, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, includePrerelease: false } } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version + + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range } else { - version = version.version + return new Range(range.raw, options) } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) } - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + if (range instanceof Comparator) { + return new Range(range.value, options) } - if (!(this instanceof SemVer)) { - return new SemVer(version, options) + if (!(this instanceof Range)) { + return new Range(range, options) } - debug('SemVer', version, options) this.options = options this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease - var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) - - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } - - this.raw = version - - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. + this.raw = range + .trim() + .split(/\s+/) + .join(' ') - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } + // First, split based on boolean or || + this.set = this.raw.split('||').map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + this.raw) } - this.build = m[5] ? m[5].split('.') : [] this.format() } -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range } -SemVer.prototype.toString = function () { - return this.version +Range.prototype.toString = function () { + return this.range } -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, safeRe[t.COMPARATORTRIM]) - return this.compareMain(other) || this.comparePre(other) -} + // `~ 1.2.3` => `~1.2.3` + range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace) -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // `^ 1.2.3` => `^1.2.3` + range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace) - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} + // normalize spaces + range = range.split(/\s+/).join(' ') -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // At this point, the range is completely trimmed and + // ready to be split into comparators. - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 + var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return set } -SemVer.prototype.compareBuild = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') } - var i = 0 - do { - var a = this.build[i] - var b = other.build[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return this.set.some(function (thisComparators) { + return ( + isSatisfiable(thisComparators, options) && + range.set.some(function (rangeComparators) { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every(function (thisComparator) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) } -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break - - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} +// take a set of comparators and determine whether there +// exists a version which can satisfy it +function isSatisfiable (comparators, options) { + var result = true + var remainingComparators = comparators.slice() + var testComparator = remainingComparators.pop() -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } + while (result && remainingComparators.length) { + result = remainingComparators.every(function (otherComparator) { + return testComparator.intersects(otherComparator, options) + }) - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null + testComparator = remainingComparators.pop() } -} -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } + return result } -exports.compareIdentifiers = compareIdentifiers - -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) - - if (anum && bnum) { - a = +a - b = +b - } - - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) } -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp } -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' } -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') } -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} +function replaceTilde (comp, options) { + var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) + debug('tilde return', ret) + return ret + }) } -exports.compareBuild = compareBuild -function compareBuild (a, b, loose) { - var versionA = new SemVer(a, loose) - var versionB = new SemVer(b, loose) - return versionA.compare(versionB) || versionA.compareBuild(versionB) +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') } -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(a, b, loose) - }) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(b, a, loose) + debug('caret return', ret) + return ret }) } -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') } -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} + if (gtlt === '=' && anyX) { + gtlt = '' + } -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b + ret = gtlt + M + '.' + m + '.' + p + pr + } else if (xm) { + ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr + } else if (xp) { + ret = '>=' + M + '.' + m + '.0' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + pr + } - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b + debug('xRange return', ret) - case '': - case '=': - case '==': - return eq(a, b, loose) + return ret + }) +} - case '!=': - return neq(a, b, loose) - - case '>': - return gt(a, b, loose) - - case '>=': - return gte(a, b, loose) - - case '<': - return lt(a, b, loose) - - case '<=': - return lte(a, b, loose) - - default: - throw new TypeError('Invalid operator: ' + op) - } -} - -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } - - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } - - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } - - debug('comp', this) -} - -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var m = comp.match(r) - - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } - - this.operator = m[1] !== undefined ? m[1] : '' - if (this.operator === '=') { - this.operator = '' - } - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} - -Comparator.prototype.toString = function () { - return this.value -} - -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) - - if (this.semver === ANY || version === ANY) { - return true - } - - if (typeof version === 'string') { - try { - version = new SemVer(version, this.options) - } catch (er) { - return false - } - } - - return cmp(version, this.operator, this.semver, this.options) -} - -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } - - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - var rangeTmp - - if (this.operator === '') { - if (this.value === '') { - return true - } - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - if (comp.value === '') { - return true - } - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} - -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease - - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } - - this.format() -} - -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} - -Range.prototype.toString = function () { - return this.range -} - -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[t.COMPARATORTRIM]) - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[t.CARETTRIM], caretTrimReplace) - - // normalize spaces - range = range.split(/\s+/).join(' ') - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) - - return set -} - -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } - - return this.set.some(function (thisComparators) { - return ( - isSatisfiable(thisComparators, options) && - range.set.some(function (rangeComparators) { - return ( - isSatisfiable(rangeComparators, options) && - thisComparators.every(function (thisComparator) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - ) - }) - ) - }) -} - -// take a set of comparators and determine whether there -// exists a version which can satisfy it -function isSatisfiable (comparators, options) { - var result = true - var remainingComparators = comparators.slice() - var testComparator = remainingComparators.pop() - - while (result && remainingComparators.length) { - result = remainingComparators.every(function (otherComparator) { - return testComparator.intersects(otherComparator, options) - }) - - testComparator = remainingComparators.pop() - } - - return result -} - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} - -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} - -function replaceTilde (comp, options) { - var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - - debug('tilde return', ret) - return ret - }) -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} - -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } - - debug('caret return', ret) - return ret - }) -} - -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} - -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp - - if (gtlt === '=' && anyX) { - gtlt = '' - } - - // if we're including prereleases in the match, then we need - // to fix this to -0, the lowest possible prerelease value - pr = options.includePrerelease ? '-0' : '' - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0-0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } - - ret = gtlt + M + '.' + m + '.' + p + pr - } else if (xm) { - ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr - } else if (xp) { - ret = '>=' + M + '.' + m + '.0' + pr + - ' <' + M + '.' + (+m + 1) + '.0' + pr - } - - debug('xRange return', ret) - - return ret - }) -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') -} +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(safeRe[t.STAR], '') +} // This function is passed to string.replace(re[t.HYPHENRANGE]) // M, m, patch, prerelease, build @@ -3120,7 +1615,7 @@ function coerce (version, options) { var match = null if (!options.rtl) { - match = version.match(re[t.COERCE]) + match = version.match(safeRe[t.COERCE]) } else { // Find the right-most coercible string that does not share // a terminus with a more left-ward coercible string. @@ -3131,17 +1626,17 @@ function coerce (version, options) { // Stop when we get a match that ends at the string end, since no // coercible string can be more right-ward without the same terminus. var next - while ((next = re[t.COERCERTL].exec(version)) && + while ((next = safeRe[t.COERCERTL].exec(version)) && (!match || match.index + match[0].length !== version.length) ) { if (!match || next.index + next[0].length !== match.index + match[0].length) { match = next } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length } // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 + safeRe[t.COERCERTL].lastIndex = -1 } if (match === null) { @@ -7026,6 +5521,19 @@ class HttpClientResponse { })); }); } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } } exports.HttpClientResponse = HttpClientResponse; function isHttps(requestUrl) { @@ -7530,7 +6038,13 @@ function getProxyUrl(reqUrl) { } })(); if (proxyVar) { - return new URL(proxyVar); + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } } else { return undefined; @@ -7541,6 +6055,10 @@ function checkBypass(reqUrl) { if (!reqUrl.hostname) { return false; } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; if (!noProxy) { return false; @@ -7566,13 +6084,24 @@ function checkBypass(reqUrl) { .split(',') .map(x => x.trim().toUpperCase()) .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { + if (upperNoProxyItem === '*' || + upperReqHosts.some(x => x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)))) { return true; } } return false; } exports.checkBypass = checkBypass; +function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return (hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]')); +} //# sourceMappingURL=proxy.js.map /***/ }), @@ -48965,7 +47494,7 @@ function state(list, sortMethod) /***/ }), -/***/ 7942: +/***/ 834: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { var abort = __nccwpck_require__(1700) @@ -49006,7 +47535,7 @@ function terminator(callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -49080,7 +47609,7 @@ function serial(list, iterator, callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -62359,74 +60888,1750 @@ function wrappy (fn, cb) { /***/ }), -/***/ 2958: -/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { - -// Generated by CoffeeScript 1.12.7 -(function() { - var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; - - ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; - - XMLDOMImplementation = __nccwpck_require__(8310); - - XMLDocument = __nccwpck_require__(3730); - - XMLDocumentCB = __nccwpck_require__(7356); - - XMLStringWriter = __nccwpck_require__(5913); - - XMLStreamWriter = __nccwpck_require__(8601); - - NodeType = __nccwpck_require__(9267); +/***/ 2958: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +// Generated by CoffeeScript 1.12.7 +(function() { + var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; + + ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; + + XMLDOMImplementation = __nccwpck_require__(8310); + + XMLDocument = __nccwpck_require__(3730); + + XMLDocumentCB = __nccwpck_require__(7356); + + XMLStringWriter = __nccwpck_require__(5913); + + XMLStreamWriter = __nccwpck_require__(8601); + + NodeType = __nccwpck_require__(9267); + + WriterState = __nccwpck_require__(9766); + + module.exports.create = function(name, xmldec, doctype, options) { + var doc, root; + if (name == null) { + throw new Error("Root element needs a name."); + } + options = assign({}, xmldec, doctype, options); + doc = new XMLDocument(options); + root = doc.element(name); + if (!options.headless) { + doc.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + doc.dtd(options); + } + } + return root; + }; + + module.exports.begin = function(options, onData, onEnd) { + var ref1; + if (isFunction(options)) { + ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; + options = {}; + } + if (onData) { + return new XMLDocumentCB(options, onData, onEnd); + } else { + return new XMLDocument(options); + } + }; + + module.exports.stringWriter = function(options) { + return new XMLStringWriter(options); + }; + + module.exports.streamWriter = function(stream, options) { + return new XMLStreamWriter(stream, options); + }; + + module.exports.implementation = new XMLDOMImplementation(); + + module.exports.nodeType = NodeType; + + module.exports.writerState = WriterState; + +}).call(this); + + +/***/ }), + +/***/ 7942: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const cacheHttpClient = __importStar(__nccwpck_require__(7417)); +const tar_1 = __nccwpck_require__(7672); +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = 'ValidationError'; + Object.setPrototypeOf(this, ValidationError.prototype); + } +} +exports.ValidationError = ValidationError; +class ReserveCacheError extends Error { + constructor(message) { + super(message); + this.name = 'ReserveCacheError'; + Object.setPrototypeOf(this, ReserveCacheError.prototype); + } +} +exports.ReserveCacheError = ReserveCacheError; +function checkPaths(paths) { + if (!paths || paths.length === 0) { + throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); + } +} +function checkKey(key) { + if (key.length > 512) { + throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); + } + const regex = /^[^,]*$/; + if (!regex.test(key)) { + throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); + } +} +/** + * isFeatureAvailable to check the presence of Actions cache service + * + * @returns boolean return true if Actions cache service feature is available, otherwise false + */ +function isFeatureAvailable() { + return !!process.env['ACTIONS_CACHE_URL']; +} +exports.isFeatureAvailable = isFeatureAvailable; +/** + * Restores cache from keys + * + * @param paths a list of file paths to restore from the cache + * @param primaryKey an explicit key for restoring the cache + * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @param downloadOptions cache download options + * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform + * @returns string returns the key for the cache hit, otherwise returns undefined + */ +function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + restoreKeys = restoreKeys || []; + const keys = [primaryKey, ...restoreKeys]; + core.debug('Resolved Keys:'); + core.debug(JSON.stringify(keys)); + if (keys.length > 10) { + throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); + } + for (const key of keys) { + checkKey(key); + } + const compressionMethod = yield utils.getCompressionMethod(); + let archivePath = ''; + try { + // path are needed to compute version + const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod, + enableCrossOsArchive + }); + if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { + // Cache not found + return undefined; + } + if (options === null || options === void 0 ? void 0 : options.lookupOnly) { + core.info('Lookup only - skipping download'); + return cacheEntry.cacheKey; + } + archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + // Download the cache from the cache entry + yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + yield (0, tar_1.extractTar)(archivePath, compressionMethod, extraTarArgs); + core.info('Cache restored successfully'); + return cacheEntry.cacheKey; + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else { + // Supress all non-validation cache related errors because caching should be optional + core.warning(`Failed to restore: ${error.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return undefined; + }); +} +exports.restoreCache = restoreCache; +/** + * Saves a list of files with the specified key + * + * @param paths a list of file paths to be cached + * @param key an explicit key for restoring the cache + * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform + * @param options cache upload options + * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails + */ +function saveCache(paths, key, options, enableCrossOsArchive = false) { + var _a, _b, _c, _d, _e; + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + checkKey(key); + const compressionMethod = yield utils.getCompressionMethod(); + let cacheId = -1; + const cachePaths = yield utils.resolvePaths(paths); + core.debug('Cache Paths:'); + core.debug(`${JSON.stringify(cachePaths)}`); + if (cachePaths.length === 0) { + throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); + } + const archiveFolder = yield utils.createTempDirectory(); + const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + try { + yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.debug(`File Size: ${archiveFileSize}`); + // For GHES, this check will take place in ReserveCache API with enterprise file size limit + if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { + throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); + } + core.debug('Reserving Cache'); + const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { + compressionMethod, + enableCrossOsArchive, + cacheSize: archiveFileSize + }); + if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { + cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; + } + else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { + throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); + } + else { + throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); + } + core.debug(`Saving Cache (ID: ${cacheId})`); + yield cacheHttpClient.saveCache(cacheId, archivePath, options); + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else if (typedError.name === ReserveCacheError.name) { + core.info(`Failed to save: ${typedError.message}`); + } + else { + core.warning(`Failed to save: ${typedError.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return cacheId; + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 7417: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const auth_1 = __nccwpck_require__(5526); +const crypto = __importStar(__nccwpck_require__(6113)); +const fs = __importStar(__nccwpck_require__(7147)); +const url_1 = __nccwpck_require__(7310); +const utils = __importStar(__nccwpck_require__(4799)); +const downloadUtils_1 = __nccwpck_require__(8432); +const options_1 = __nccwpck_require__(3128); +const requestUtils_1 = __nccwpck_require__(204); +const versionSalt = '1.0'; +function getCacheApiUrl(resource) { + const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; + if (!baseUrl) { + throw new Error('Cache Service Url not found, unable to restore cache.'); + } + const url = `${baseUrl}_apis/artifactcache/${resource}`; + core.debug(`Resource Url: ${url}`); + return url; +} +function createAcceptHeader(type, apiVersion) { + return `${type};api-version=${apiVersion}`; +} +function getRequestOptions() { + const requestOptions = { + headers: { + Accept: createAcceptHeader('application/json', '6.0-preview.1') + } + }; + return requestOptions; +} +function createHttpClient() { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; + const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); + return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); +} +function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { + const components = paths; + // Add compression method to cache version to restore + // compressed cache as per compression method + if (compressionMethod) { + components.push(compressionMethod); + } + // Only check for windows platforms if enableCrossOsArchive is false + if (process.platform === 'win32' && !enableCrossOsArchive) { + components.push('windows-only'); + } + // Add salt to cache version to support breaking changes in cache entry + components.push(versionSalt); + return crypto.createHash('sha256').update(components.join('|')).digest('hex'); +} +exports.getCacheVersion = getCacheVersion; +function getCacheEntry(keys, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + // Cache not found + if (response.statusCode === 204) { + // List cache for primary key only if cache miss occurs + if (core.isDebug()) { + yield printCachesListForDiagnostics(keys[0], httpClient, version); + } + return null; + } + if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { + throw new Error(`Cache service responded with ${response.statusCode}`); + } + const cacheResult = response.result; + const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; + if (!cacheDownloadUrl) { + // Cache achiveLocation not found. This should never happen, and hence bail out. + throw new Error('Cache not found.'); + } + core.setSecret(cacheDownloadUrl); + core.debug(`Cache Result:`); + core.debug(JSON.stringify(cacheResult)); + return cacheResult; + }); +} +exports.getCacheEntry = getCacheEntry; +function printCachesListForDiagnostics(key, httpClient, version) { + return __awaiter(this, void 0, void 0, function* () { + const resource = `caches?key=${encodeURIComponent(key)}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + if (response.statusCode === 200) { + const cacheListResult = response.result; + const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; + if (totalCount && totalCount > 0) { + core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); + for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { + core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + } + } + } + }); +} +function downloadCache(archiveLocation, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const archiveUrl = new url_1.URL(archiveLocation); + const downloadOptions = (0, options_1.getDownloadOptions)(options); + if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) { + if (downloadOptions.useAzureSdk) { + // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. + yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); + } + else if (downloadOptions.concurrentBlobDownloads) { + // Use concurrent implementation with HttpClient to work around blob SDK issue + yield (0, downloadUtils_1.downloadCacheHttpClientConcurrent)(archiveLocation, archivePath, downloadOptions); + } + else { + // Otherwise, download using the Actions http-client. + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + } + else { + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + }); +} +exports.downloadCache = downloadCache; +// Reserve Cache +function reserveCache(key, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const reserveCacheRequest = { + key, + version, + cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize + }; + const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); + })); + return response; + }); +} +exports.reserveCache = reserveCache; +function getContentRange(start, end) { + // Format: `bytes start-end/filesize + // start and end are inclusive + // filesize can be * + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/* + return `bytes ${start}-${end}/*`; +} +function uploadChunk(httpClient, resourceUrl, openStream, start, end) { + return __awaiter(this, void 0, void 0, function* () { + core.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + const additionalHeaders = { + 'Content-Type': 'application/octet-stream', + 'Content-Range': getContentRange(start, end) + }; + const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { + return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); + })); + if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { + throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); + } + }); +} +function uploadFile(httpClient, cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + // Upload Chunks + const fileSize = utils.getArchiveFileSizeInBytes(archivePath); + const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); + const fd = fs.openSync(archivePath, 'r'); + const uploadOptions = (0, options_1.getUploadOptions)(options); + const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); + const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); + const parallelUploads = [...new Array(concurrency).keys()]; + core.debug('Awaiting all uploads'); + let offset = 0; + try { + yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, maxChunkSize); + const start = offset; + const end = offset + chunkSize - 1; + offset += maxChunkSize; + yield uploadChunk(httpClient, resourceUrl, () => fs + .createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + .on('error', error => { + throw new Error(`Cache upload failed because file read failed with ${error.message}`); + }), start, end); + } + }))); + } + finally { + fs.closeSync(fd); + } + return; + }); +} +function commitCache(httpClient, cacheId, filesize) { + return __awaiter(this, void 0, void 0, function* () { + const commitCacheRequest = { size: filesize }; + return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); + })); + }); +} +function saveCache(cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + core.debug('Upload cache'); + yield uploadFile(httpClient, cacheId, archivePath, options); + // Commit Cache + core.debug('Commiting cache'); + const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); + if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { + throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); + } + core.info('Cache saved successfully'); + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 4799: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const exec = __importStar(__nccwpck_require__(1514)); +const glob = __importStar(__nccwpck_require__(8090)); +const io = __importStar(__nccwpck_require__(7436)); +const fs = __importStar(__nccwpck_require__(7147)); +const path = __importStar(__nccwpck_require__(1017)); +const semver = __importStar(__nccwpck_require__(1898)); +const util = __importStar(__nccwpck_require__(3837)); +const uuid_1 = __nccwpck_require__(2155); +const constants_1 = __nccwpck_require__(3608); +// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 +function createTempDirectory() { + return __awaiter(this, void 0, void 0, function* () { + const IS_WINDOWS = process.platform === 'win32'; + let tempDirectory = process.env['RUNNER_TEMP'] || ''; + if (!tempDirectory) { + let baseLocation; + if (IS_WINDOWS) { + // On Windows use the USERPROFILE env variable + baseLocation = process.env['USERPROFILE'] || 'C:\\'; + } + else { + if (process.platform === 'darwin') { + baseLocation = '/Users'; + } + else { + baseLocation = '/home'; + } + } + tempDirectory = path.join(baseLocation, 'actions', 'temp'); + } + const dest = path.join(tempDirectory, (0, uuid_1.v4)()); + yield io.mkdirP(dest); + return dest; + }); +} +exports.createTempDirectory = createTempDirectory; +function getArchiveFileSizeInBytes(filePath) { + return fs.statSync(filePath).size; +} +exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; +function resolvePaths(patterns) { + var _a, e_1, _b, _c; + var _d; + return __awaiter(this, void 0, void 0, function* () { + const paths = []; + const workspace = (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); + const globber = yield glob.create(patterns.join('\n'), { + implicitDescendants: false + }); + try { + for (var _e = true, _f = __asyncValues(globber.globGenerator()), _g; _g = yield _f.next(), _a = _g.done, !_a;) { + _c = _g.value; + _e = false; + try { + const file = _c; + const relativeFile = path + .relative(workspace, file) + .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); + core.debug(`Matched: ${relativeFile}`); + // Paths are made relative so the tar entries are all relative to the root of the workspace. + if (relativeFile === '') { + // path.relative returns empty string if workspace and file are equal + paths.push('.'); + } + else { + paths.push(`${relativeFile}`); + } + } + finally { + _e = true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_e && !_a && (_b = _f.return)) yield _b.call(_f); + } + finally { if (e_1) throw e_1.error; } + } + return paths; + }); +} +exports.resolvePaths = resolvePaths; +function unlinkFile(filePath) { + return __awaiter(this, void 0, void 0, function* () { + return util.promisify(fs.unlink)(filePath); + }); +} +exports.unlinkFile = unlinkFile; +function getVersion(app, additionalArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let versionOutput = ''; + additionalArgs.push('--version'); + core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); + try { + yield exec.exec(`${app}`, additionalArgs, { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + } + catch (err) { + core.debug(err.message); + } + versionOutput = versionOutput.trim(); + core.debug(versionOutput); + return versionOutput; + }); +} +// Use zstandard if possible to maximize cache performance +function getCompressionMethod() { + return __awaiter(this, void 0, void 0, function* () { + const versionOutput = yield getVersion('zstd', ['--quiet']); + const version = semver.clean(versionOutput); + core.debug(`zstd version: ${version}`); + if (versionOutput === '') { + return constants_1.CompressionMethod.Gzip; + } + else { + return constants_1.CompressionMethod.ZstdWithoutLong; + } + }); +} +exports.getCompressionMethod = getCompressionMethod; +function getCacheFileName(compressionMethod) { + return compressionMethod === constants_1.CompressionMethod.Gzip + ? constants_1.CacheFilename.Gzip + : constants_1.CacheFilename.Zstd; +} +exports.getCacheFileName = getCacheFileName; +function getGnuTarPathOnWindows() { + return __awaiter(this, void 0, void 0, function* () { + if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { + return constants_1.GnuTarPathOnWindows; + } + const versionOutput = yield getVersion('tar'); + return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; + }); +} +exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; +function assertDefined(name, value) { + if (value === undefined) { + throw Error(`Expected ${name} but value was undefiend`); + } + return value; +} +exports.assertDefined = assertDefined; +function isGhes() { + const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; +} +exports.isGhes = isGhes; + + +/***/ }), + +/***/ 3608: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; +var CacheFilename; +(function (CacheFilename) { + CacheFilename["Gzip"] = "cache.tgz"; + CacheFilename["Zstd"] = "cache.tzst"; +})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); +var CompressionMethod; +(function (CompressionMethod) { + CompressionMethod["Gzip"] = "gzip"; + // Long range mode was added to zstd in v1.3.2. + // This enum is for earlier version of zstd that does not have --long support + CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; + CompressionMethod["Zstd"] = "zstd"; +})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); +var ArchiveToolType; +(function (ArchiveToolType) { + ArchiveToolType["GNU"] = "gnu"; + ArchiveToolType["BSD"] = "bsd"; +})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); +// The default number of retry attempts. +exports.DefaultRetryAttempts = 2; +// The default delay in milliseconds between retry attempts. +exports.DefaultRetryDelay = 5000; +// Socket timeout in milliseconds during download. If no traffic is received +// over the socket during this period, the socket is destroyed and the download +// is aborted. +exports.SocketTimeout = 5000; +// The default path of GNUtar on hosted Windows runners +exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; +// The default path of BSDtar on hosted Windows runners +exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; +exports.TarFilename = 'cache.tar'; +exports.ManifestFilename = 'manifest.txt'; + + +/***/ }), + +/***/ 8432: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.downloadCacheStorageSDK = exports.downloadCacheHttpClientConcurrent = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const storage_blob_1 = __nccwpck_require__(4100); +const buffer = __importStar(__nccwpck_require__(4300)); +const fs = __importStar(__nccwpck_require__(7147)); +const stream = __importStar(__nccwpck_require__(2781)); +const util = __importStar(__nccwpck_require__(3837)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const requestUtils_1 = __nccwpck_require__(204); +const abort_controller_1 = __nccwpck_require__(2557); +/** + * Pipes the body of a HTTP response to a stream + * + * @param response the HTTP response + * @param output the writable stream + */ +function pipeResponseToStream(response, output) { + return __awaiter(this, void 0, void 0, function* () { + const pipeline = util.promisify(stream.pipeline); + yield pipeline(response.message, output); + }); +} +/** + * Class for tracking the download state and displaying stats. + */ +class DownloadProgress { + constructor(contentLength) { + this.contentLength = contentLength; + this.segmentIndex = 0; + this.segmentSize = 0; + this.segmentOffset = 0; + this.receivedBytes = 0; + this.displayedComplete = false; + this.startTime = Date.now(); + } + /** + * Progress to the next segment. Only call this method when the previous segment + * is complete. + * + * @param segmentSize the length of the next segment + */ + nextSegment(segmentSize) { + this.segmentOffset = this.segmentOffset + this.segmentSize; + this.segmentIndex = this.segmentIndex + 1; + this.segmentSize = segmentSize; + this.receivedBytes = 0; + core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + } + /** + * Sets the number of bytes received for the current segment. + * + * @param receivedBytes the number of bytes received + */ + setReceivedBytes(receivedBytes) { + this.receivedBytes = receivedBytes; + } + /** + * Returns the total number of bytes transferred. + */ + getTransferredBytes() { + return this.segmentOffset + this.receivedBytes; + } + /** + * Returns true if the download is complete. + */ + isDone() { + return this.getTransferredBytes() === this.contentLength; + } + /** + * Prints the current download stats. Once the download completes, this will print one + * last line and then stop. + */ + display() { + if (this.displayedComplete) { + return; + } + const transferredBytes = this.segmentOffset + this.receivedBytes; + const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); + const elapsedTime = Date.now() - this.startTime; + const downloadSpeed = (transferredBytes / + (1024 * 1024) / + (elapsedTime / 1000)).toFixed(1); + core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + if (this.isDone()) { + this.displayedComplete = true; + } + } + /** + * Returns a function used to handle TransferProgressEvents. + */ + onProgress() { + return (progress) => { + this.setReceivedBytes(progress.loadedBytes); + }; + } + /** + * Starts the timer that displays the stats. + * + * @param delayInMs the delay between each write + */ + startDisplayTimer(delayInMs = 1000) { + const displayCallback = () => { + this.display(); + if (!this.isDone()) { + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + }; + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + /** + * Stops the timer that displays the stats. As this typically indicates the download + * is complete, this will display one last line, unless the last line has already + * been written. + */ + stopDisplayTimer() { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle); + this.timeoutHandle = undefined; + } + this.display(); + } +} +exports.DownloadProgress = DownloadProgress; +/** + * Download the cache using the Actions toolkit http-client + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClient(archiveLocation, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + const writeStream = fs.createWriteStream(archivePath); + const httpClient = new http_client_1.HttpClient('actions/cache'); + const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { + downloadResponse.message.destroy(); + core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); + }); + yield pipeResponseToStream(downloadResponse, writeStream); + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length']; + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader); + const actualLength = utils.getArchiveFileSizeInBytes(archivePath); + if (actualLength !== expectedLength) { + throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); + } + } + else { + core.debug('Unable to validate download, no Content-Length header'); + } + }); +} +exports.downloadCacheHttpClient = downloadCacheHttpClient; +/** + * Download the cache using the Actions toolkit http-client concurrently + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const archiveDescriptor = yield fs.promises.open(archivePath, 'w'); + const httpClient = new http_client_1.HttpClient('actions/cache', undefined, { + socketTimeout: options.timeoutInMs, + keepAlive: true + }); + try { + const res = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCacheMetadata', () => __awaiter(this, void 0, void 0, function* () { return yield httpClient.request('HEAD', archiveLocation, null, {}); })); + const lengthHeader = res.message.headers['content-length']; + if (lengthHeader === undefined || lengthHeader === null) { + throw new Error('Content-Length not found on blob response'); + } + const length = parseInt(lengthHeader); + if (Number.isNaN(length)) { + throw new Error(`Could not interpret Content-Length: ${length}`); + } + const downloads = []; + const blockSize = 4 * 1024 * 1024; + for (let offset = 0; offset < length; offset += blockSize) { + const count = Math.min(blockSize, length - offset); + downloads.push({ + offset, + promiseGetter: () => __awaiter(this, void 0, void 0, function* () { + return yield downloadSegmentRetry(httpClient, archiveLocation, offset, count); + }) + }); + } + // reverse to use .pop instead of .shift + downloads.reverse(); + let actives = 0; + let bytesDownloaded = 0; + const progress = new DownloadProgress(length); + progress.startDisplayTimer(); + const progressFn = progress.onProgress(); + const activeDownloads = []; + let nextDownload; + const waitAndWrite = () => __awaiter(this, void 0, void 0, function* () { + const segment = yield Promise.race(Object.values(activeDownloads)); + yield archiveDescriptor.write(segment.buffer, 0, segment.count, segment.offset); + actives--; + delete activeDownloads[segment.offset]; + bytesDownloaded += segment.count; + progressFn({ loadedBytes: bytesDownloaded }); + }); + while ((nextDownload = downloads.pop())) { + activeDownloads[nextDownload.offset] = nextDownload.promiseGetter(); + actives++; + if (actives >= ((_a = options.downloadConcurrency) !== null && _a !== void 0 ? _a : 10)) { + yield waitAndWrite(); + } + } + while (actives > 0) { + yield waitAndWrite(); + } + } + finally { + httpClient.dispose(); + yield archiveDescriptor.close(); + } + }); +} +exports.downloadCacheHttpClientConcurrent = downloadCacheHttpClientConcurrent; +function downloadSegmentRetry(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const retries = 5; + let failures = 0; + while (true) { + try { + const timeout = 30000; + const result = yield promiseWithTimeout(timeout, downloadSegment(httpClient, archiveLocation, offset, count)); + if (typeof result === 'string') { + throw new Error('downloadSegmentRetry failed due to timeout'); + } + return result; + } + catch (err) { + if (failures >= retries) { + throw err; + } + failures++; + } + } + }); +} +function downloadSegment(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const partRes = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCachePart', () => __awaiter(this, void 0, void 0, function* () { + return yield httpClient.get(archiveLocation, { + Range: `bytes=${offset}-${offset + count - 1}` + }); + })); + if (!partRes.readBodyBuffer) { + throw new Error('Expected HttpClientResponse to implement readBodyBuffer'); + } + return { + offset, + count, + buffer: yield partRes.readBodyBuffer() + }; + }); +} +/** + * Download the cache using the Azure Storage SDK. Only call this method if the + * URL points to an Azure Storage endpoint. + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + * @param options the download options with the defaults set + */ +function downloadCacheStorageSDK(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { + retryOptions: { + // Override the timeout used when downloading each 4 MB chunk + // The default is 2 min / MB, which is way too slow + tryTimeoutInMs: options.timeoutInMs + } + }); + const properties = yield client.getProperties(); + const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; + if (contentLength < 0) { + // We should never hit this condition, but just in case fall back to downloading the + // file as one large stream + core.debug('Unable to determine content length, downloading file with http-client...'); + yield downloadCacheHttpClient(archiveLocation, archivePath); + } + else { + // Use downloadToBuffer for faster downloads, since internally it splits the + // file into 4 MB chunks which can then be parallelized and retried independently + // + // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB + // on 64-bit systems), split the download into multiple segments + // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. + // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast + const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); + const downloadProgress = new DownloadProgress(contentLength); + const fd = fs.openSync(archivePath, 'w'); + try { + downloadProgress.startDisplayTimer(); + const controller = new abort_controller_1.AbortController(); + const abortSignal = controller.signal; + while (!downloadProgress.isDone()) { + const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; + const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); + downloadProgress.nextSegment(segmentSize); + const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { + abortSignal, + concurrency: options.downloadConcurrency, + onProgress: downloadProgress.onProgress() + })); + if (result === 'timeout') { + controller.abort(); + throw new Error('Aborting cache download as the download time exceeded the timeout.'); + } + else if (Buffer.isBuffer(result)) { + fs.writeFileSync(fd, result); + } + } + } + finally { + downloadProgress.stopDisplayTimer(); + fs.closeSync(fd); + } + } + }); +} +exports.downloadCacheStorageSDK = downloadCacheStorageSDK; +const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { + let timeoutHandle; + const timeoutPromise = new Promise(resolve => { + timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); + }); + return Promise.race([promise, timeoutPromise]).then(result => { + clearTimeout(timeoutHandle); + return result; + }); +}); + + +/***/ }), + +/***/ 204: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const constants_1 = __nccwpck_require__(3608); +function isSuccessStatusCode(statusCode) { + if (!statusCode) { + return false; + } + return statusCode >= 200 && statusCode < 300; +} +exports.isSuccessStatusCode = isSuccessStatusCode; +function isServerErrorStatusCode(statusCode) { + if (!statusCode) { + return true; + } + return statusCode >= 500; +} +exports.isServerErrorStatusCode = isServerErrorStatusCode; +function isRetryableStatusCode(statusCode) { + if (!statusCode) { + return false; + } + const retryableStatusCodes = [ + http_client_1.HttpCodes.BadGateway, + http_client_1.HttpCodes.ServiceUnavailable, + http_client_1.HttpCodes.GatewayTimeout + ]; + return retryableStatusCodes.includes(statusCode); +} +exports.isRetryableStatusCode = isRetryableStatusCode; +function sleep(milliseconds) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => setTimeout(resolve, milliseconds)); + }); +} +function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { + return __awaiter(this, void 0, void 0, function* () { + let errorMessage = ''; + let attempt = 1; + while (attempt <= maxAttempts) { + let response = undefined; + let statusCode = undefined; + let isRetryable = false; + try { + response = yield method(); + } + catch (error) { + if (onError) { + response = onError(error); + } + isRetryable = true; + errorMessage = error.message; + } + if (response) { + statusCode = getStatusCode(response); + if (!isServerErrorStatusCode(statusCode)) { + return response; + } + } + if (statusCode) { + isRetryable = isRetryableStatusCode(statusCode); + errorMessage = `Cache service responded with ${statusCode}`; + } + core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`); + break; + } + yield sleep(delay); + attempt++; + } + throw Error(`${name} failed: ${errorMessage}`); + }); +} +exports.retry = retry; +function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, + // If the error object contains the statusCode property, extract it and return + // an TypedResponse so it can be processed by the retry logic. + (error) => { + if (error instanceof http_client_1.HttpClientError) { + return { + statusCode: error.statusCode, + result: null, + headers: {}, + error + }; + } + else { + return undefined; + } + }); + }); +} +exports.retryTypedResponse = retryTypedResponse; +function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); + }); +} +exports.retryHttpClientResponse = retryHttpClientResponse; + + +/***/ }), + +/***/ 7672: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.createTar = exports.extractTar = exports.listTar = void 0; +const exec_1 = __nccwpck_require__(1514); +const io = __importStar(__nccwpck_require__(7436)); +const fs_1 = __nccwpck_require__(7147); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const IS_WINDOWS = process.platform === 'win32'; +// Returns tar path and type: BSD or GNU +function getTarPath() { + return __awaiter(this, void 0, void 0, function* () { + switch (process.platform) { + case 'win32': { + const gnuTar = yield utils.getGnuTarPathOnWindows(); + const systemTar = constants_1.SystemTarPathOnWindows; + if (gnuTar) { + // Use GNUtar as default on windows + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else if ((0, fs_1.existsSync)(systemTar)) { + return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; + } + break; + } + case 'darwin': { + const gnuTar = yield io.which('gtar', false); + if (gnuTar) { + // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else { + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.BSD + }; + } + } + default: + break; + } + // Default assumption is GNU tar is present in path + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.GNU + }; + }); +} +// Return arguments for tar as per tarPath, compressionMethod, method type and os +function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { + return __awaiter(this, void 0, void 0, function* () { + const args = [`"${tarPath.path}"`]; + const cacheFileName = utils.getCacheFileName(compressionMethod); + const tarFile = 'cache.tar'; + const workingDirectory = getWorkingDirectory(); + // Speficic args for BSD tar on windows for workaround + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + // Method specific args + switch (type) { + case 'create': + args.push('--posix', '-cf', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); + break; + case 'extract': + args.push('-xf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); + break; + case 'list': + args.push('-tf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); + break; + } + // Platform specific args + if (tarPath.type === constants_1.ArchiveToolType.GNU) { + switch (process.platform) { + case 'win32': + args.push('--force-local'); + break; + case 'darwin': + args.push('--delay-directory-restore'); + break; + } + } + return args; + }); +} +// Returns commands to run tar and compression program +function getCommands(compressionMethod, type, archivePath = '', tarExtraArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let args; + const tarPath = yield getTarPath(); + const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); + tarArgs.push(...tarExtraArgs); + const compressionArgs = type !== 'create' + ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) + : yield getCompressionProgram(tarPath, compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + if (BSD_TAR_ZSTD && type !== 'create') { + args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; + } + else { + args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; + } + if (BSD_TAR_ZSTD) { + return args; + } + return [args.join(' ')]; + }); +} +function getWorkingDirectory() { + var _a; + return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); +} +// Common function for extractTar and listTar to get the compression method +function getDecompressionProgram(tarPath, compressionMethod, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + // -d: Decompress. + // unzstd is equivalent to 'zstd -d' + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --long=30 --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; + default: + return ['-z']; + } + }); +} +// Used for creating the archive +// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. +// zstdmt is equivalent to 'zstd -T0' +// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. +// Using 30 here because we also support 32-bit self-hosted runners. +// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. +function getCompressionProgram(tarPath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const cacheFileName = utils.getCacheFileName(compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --long=30 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; + default: + return ['-z']; + } + }); +} +// Executes all commands as separate processes +function execCommands(commands, cwd) { + return __awaiter(this, void 0, void 0, function* () { + for (const command of commands) { + try { + yield (0, exec_1.exec)(command, undefined, { + cwd, + env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) + }); + } + catch (error) { + throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); + } + } + }); +} +// List the contents of a tar +function listTar(archivePath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const commands = yield getCommands(compressionMethod, 'list', archivePath); + yield execCommands(commands); + }); +} +exports.listTar = listTar; +// Extract a tar +function extractTar(archivePath, compressionMethod, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + // Create directory to extract tar into + const workingDirectory = getWorkingDirectory(); + yield io.mkdirP(workingDirectory); + const commands = yield getCommands(compressionMethod, 'extract', archivePath, extraTarArgs); + yield execCommands(commands); + }); +} +exports.extractTar = extractTar; +// Create a tar +function createTar(archiveFolder, sourceDirectories, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + // Write source directories to manifest.txt to avoid command length limits + (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); + const commands = yield getCommands(compressionMethod, 'create'); + yield execCommands(commands, archiveFolder); + }); +} +exports.createTar = createTar; + + +/***/ }), + +/***/ 3128: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - WriterState = __nccwpck_require__(9766); +"use strict"; - module.exports.create = function(name, xmldec, doctype, options) { - var doc, root; - if (name == null) { - throw new Error("Root element needs a name."); +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; } - options = assign({}, xmldec, doctype, options); - doc = new XMLDocument(options); - root = doc.element(name); - if (!options.headless) { - doc.declaration(options); - if ((options.pubID != null) || (options.sysID != null)) { - doc.dtd(options); - } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getDownloadOptions = exports.getUploadOptions = void 0; +const core = __importStar(__nccwpck_require__(2186)); +/** + * Returns a copy of the upload options with defaults filled in. + * + * @param copy the original upload options + */ +function getUploadOptions(copy) { + const result = { + uploadConcurrency: 4, + uploadChunkSize: 32 * 1024 * 1024 + }; + if (copy) { + if (typeof copy.uploadConcurrency === 'number') { + result.uploadConcurrency = copy.uploadConcurrency; + } + if (typeof copy.uploadChunkSize === 'number') { + result.uploadChunkSize = copy.uploadChunkSize; + } } - return root; - }; - - module.exports.begin = function(options, onData, onEnd) { - var ref1; - if (isFunction(options)) { - ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; - options = {}; + core.debug(`Upload concurrency: ${result.uploadConcurrency}`); + core.debug(`Upload chunk size: ${result.uploadChunkSize}`); + return result; +} +exports.getUploadOptions = getUploadOptions; +/** + * Returns a copy of the download options with defaults filled in. + * + * @param copy the original download options + */ +function getDownloadOptions(copy) { + const result = { + useAzureSdk: false, + concurrentBlobDownloads: true, + downloadConcurrency: 8, + timeoutInMs: 30000, + segmentTimeoutInMs: 600000, + lookupOnly: false + }; + if (copy) { + if (typeof copy.useAzureSdk === 'boolean') { + result.useAzureSdk = copy.useAzureSdk; + } + if (typeof copy.concurrentBlobDownloads === 'boolean') { + result.concurrentBlobDownloads = copy.concurrentBlobDownloads; + } + if (typeof copy.downloadConcurrency === 'number') { + result.downloadConcurrency = copy.downloadConcurrency; + } + if (typeof copy.timeoutInMs === 'number') { + result.timeoutInMs = copy.timeoutInMs; + } + if (typeof copy.segmentTimeoutInMs === 'number') { + result.segmentTimeoutInMs = copy.segmentTimeoutInMs; + } + if (typeof copy.lookupOnly === 'boolean') { + result.lookupOnly = copy.lookupOnly; + } } - if (onData) { - return new XMLDocumentCB(options, onData, onEnd); - } else { - return new XMLDocument(options); + const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; + if (segmentDownloadTimeoutMins && + !isNaN(Number(segmentDownloadTimeoutMins)) && + isFinite(Number(segmentDownloadTimeoutMins))) { + result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; } - }; - - module.exports.stringWriter = function(options) { - return new XMLStringWriter(options); - }; - - module.exports.streamWriter = function(stream, options) { - return new XMLStreamWriter(stream, options); - }; - - module.exports.implementation = new XMLDOMImplementation(); - - module.exports.nodeType = NodeType; - - module.exports.writerState = WriterState; - -}).call(this); + core.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core.debug(`Download concurrency: ${result.downloadConcurrency}`); + core.debug(`Request timeout (ms): ${result.timeoutInMs}`); + core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); + core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core.debug(`Lookup only: ${result.lookupOnly}`); + return result; +} +exports.getDownloadOptions = getDownloadOptions; /***/ }), @@ -62440,35 +62645,40 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; var Inputs; (function (Inputs) { - Inputs["Key"] = "key"; - Inputs["Path"] = "path"; - Inputs["RestoreKeys"] = "restore-keys"; - Inputs["UploadChunkSize"] = "upload-chunk-size"; - Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; - Inputs["RestoreKeyHit"] = "restore-key-hit"; - Inputs["ExtraRestoreKeys"] = "extra-restore-keys"; - Inputs["GCMacos"] = "gc-macos"; + Inputs["PrimaryKey"] = "primary-key"; + Inputs["RestorePrefixesFirstMatch"] = "restore-prefixes-first-match"; + Inputs["RestorePrefixesAllMatches"] = "restore-prefixes-all-matches"; + Inputs["SkipRestoreOnHitPrimaryKey"] = "skip-restore-on-primary-key-hit"; + Inputs["FailOn"] = "fail-on"; + Inputs["Nix"] = "nix"; + Inputs["Save"] = "save"; + Inputs["Paths"] = "paths"; + Inputs["PathsMacos"] = "paths-macos"; + Inputs["PathsLinux"] = "paths-linux"; + Inputs["GCMaxStoreSize"] = "gc-max-store-size"; Inputs["GCMaxStoreSizeMacos"] = "gc-max-store-size-macos"; - Inputs["GCLinux"] = "gc-linux"; Inputs["GCMaxStoreSizeLinux"] = "gc-max-store-size-linux"; - Inputs["Token"] = "token"; Inputs["Purge"] = "purge"; - Inputs["PurgeKeys"] = "purge-keys"; - Inputs["PurgeAccessed"] = "purge-accessed"; - Inputs["PurgeAccessedMaxAge"] = "purge-accessed-max-age"; + Inputs["PurgeOverwrite"] = "purge-overwrite"; + Inputs["PurgePrefixes"] = "purge-prefixes"; + Inputs["PurgeLastAccessed"] = "purge-last-accessed"; Inputs["PurgeCreated"] = "purge-created"; - Inputs["PurgeCreatedMaxAge"] = "purge-created-max-age"; // Input for cache, save action + Inputs["UploadChunkSize"] = "upload-chunk-size"; + Inputs["Token"] = "token"; // Input for cache, save actions })(Inputs = exports.Inputs || (exports.Inputs = {})); var Outputs; (function (Outputs) { - Outputs["CacheHit"] = "cache-hit"; - Outputs["CachePrimaryKey"] = "cache-primary-key"; - Outputs["CacheMatchedKey"] = "cache-matched-key"; // Output from restore action + Outputs["PrimaryKey"] = "primary-key"; + Outputs["Hit"] = "hit"; + Outputs["HitPrimaryKey"] = "hit-primary-key"; + Outputs["HitFirstMatch"] = "hit-first-match"; + Outputs["RestoredKey"] = "restored-key"; + Outputs["RestoredKeys"] = "restored-keys"; // Output from cache, restore actions })(Outputs = exports.Outputs || (exports.Outputs = {})); var State; (function (State) { - State["CachePrimaryKey"] = "CACHE_KEY"; - State["CacheMatchedKey"] = "CACHE_RESULT"; + State["CachePrimaryKey"] = "CACHE_PRIMARY_KEY"; + State["CacheRestoredKey"] = "CACHE_RESTORED_KEY"; })(State = exports.State || (exports.State = {})); var Events; (function (Events) { @@ -62481,82 +62691,7 @@ exports.RefKey = "GITHUB_REF"; /***/ }), -/***/ 2193: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.collectGarbage = void 0; -const exec_1 = __nccwpck_require__(1514); -const constants_1 = __nccwpck_require__(9042); -const utils = __importStar(__nccwpck_require__(6850)); -function collectGarbage() { - return __awaiter(this, void 0, void 0, function* () { - utils.info("Collecting garbage"); - yield (0, exec_1.exec)("bash", ["-c", "sudo rm -rf /nix/.[!.]* /nix/..?*"]); - const gcEnabled = utils.getInputAsBool(process.platform == "darwin" ? constants_1.Inputs.GCMacos : constants_1.Inputs.GCLinux); - if (gcEnabled) { - const maxStoreSize = utils.getInputAsInt(process.platform == "darwin" - ? constants_1.Inputs.GCMaxStoreSizeMacos - : constants_1.Inputs.GCMaxStoreSizeLinux, { required: true }); - yield (0, exec_1.exec)("bash", [ - "-c", - ` - STORE_SIZE="$(nix path-info --json --all | jq 'map(.narSize) | add')" - printf "Current store size in bytes: $STORE_SIZE\\n" - - MAX_STORE_SIZE=${maxStoreSize} - - if (( STORE_SIZE > MAX_STORE_SIZE )); then - (( R1 = STORE_SIZE - MAX_STORE_SIZE )) - (( R2 = R1 > 0 ? R1 : 0 )) - printf "Max bytes to free: $R2\\n" - nix store gc --max "$R2" - fi - ` - ]); - } - }); -} -exports.collectGarbage = collectGarbage; - - -/***/ }), - -/***/ 6231: +/***/ 7063: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62584,121 +62719,63 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.purgeCaches = void 0; +exports.token = exports.uploadChunkSize = exports.purgeCreatedMaxAge = exports.purgeLastAccessed = exports.purgePrefixes = exports.purgeOverwrite = exports.purge = exports.gcMaxStoreSize = exports.paths = exports.save = exports.nix = exports.failOn = exports.skipRestoreOnHitPrimaryKey = exports.restorePrefixesAllMatches = exports.restorePrefixesFirstMatch = exports.primaryKey = void 0; const core = __importStar(__nccwpck_require__(2186)); -const github = __importStar(__nccwpck_require__(5438)); const constants_1 = __nccwpck_require__(9042); -const utils = __importStar(__nccwpck_require__(6850)); -function purgeByTime({ doUseLastAccessedTime, keys, lookupOnly, time }) { - return __awaiter(this, void 0, void 0, function* () { - const verb = doUseLastAccessedTime ? "last accessed" : "created"; - const maxDate = utils.getMaxDate({ doUseLastAccessedTime, time }); - utils.info(` - ${lookupOnly ? "Searching for" : "Purging"} caches ${verb} before ${maxDate.toISOString()} and having keys: - ${utils.stringify(keys)} - `); - const token = core.getInput(constants_1.Inputs.Token, { required: true }); - const caches = utils.filterCachesByTime({ - caches: yield utils.getCachesByKeys(token, keys), - doUseLastAccessedTime, - maxDate - }); - utils.info(` - Found ${caches.length} cache(s): - ${utils.stringify(caches)} - `); - if (lookupOnly) { - return caches; - } - const octokit = github.getOctokit(token); - caches.forEach((cache) => __awaiter(this, void 0, void 0, function* () { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - const atDatePretty = atDate.toISOString(); - if (atDate < maxDate) { - utils.info(`Deleting the cache having the key '${cache.key}' and ${verb} at ${atDatePretty}`); - try { - yield octokit.rest.actions.deleteActionsCacheById({ - per_page: 100, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - cache_id: cache.id - }); - } - catch (error) { - utils.info(` - Failed to delete cache ${cache.key} - - ${error} - `); - } - } - else { - utils.info(`Skipping the cache having the key '${cache.key}' and ${verb} at ${atDatePretty}`); - } - } - })); - return []; - }); -} -function purge({ key, lookupOnly, time }) { - return __awaiter(this, void 0, void 0, function* () { - const accessed = core.getInput(constants_1.Inputs.PurgeAccessed) === "true"; - const created = core.getInput(constants_1.Inputs.PurgeCreated) === "true"; - let purgeKeys = utils.getInputAsArray(constants_1.Inputs.PurgeKeys); - if (purgeKeys.length == 0) { - purgeKeys.push(...[key]); - } - purgeKeys = purgeKeys.filter(key => key.trim().length > 0); - const results = []; - if (accessed || created) { - if (accessed) { - results.push(...(yield purgeByTime({ - doUseLastAccessedTime: true, - keys: purgeKeys, - lookupOnly, - time - }))); - } - if (created) { - results.push(...(yield purgeByTime({ - doUseLastAccessedTime: false, - keys: purgeKeys, - lookupOnly, - time - }))); - } - } - else { - core.warning("Either `accessed` or `created` input should be `true`."); - } - return results; - }); -} -function purgeCaches({ key, lookupOnly, time }) { - return __awaiter(this, void 0, void 0, function* () { - const purgeEnabled = utils.getInputAsBool(constants_1.Inputs.Purge); - const results = []; - if (purgeEnabled) { - results.push(...(yield purge({ key, lookupOnly, time }))); - } - return results; - }); -} -exports.purgeCaches = purgeCaches; +const utils = __importStar(__nccwpck_require__(9378)); +exports.primaryKey = core.getInput(constants_1.Inputs.PrimaryKey, { required: true }); +exports.restorePrefixesFirstMatch = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesFirstMatch); +exports.restorePrefixesAllMatches = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesAllMatches); +exports.skipRestoreOnHitPrimaryKey = utils.getInputAsBool(constants_1.Inputs.SkipRestoreOnHitPrimaryKey); +exports.failOn = (() => { + var _a; + const failOnRaw = (_a = new RegExp("^(primary|first-match)\\.(miss|not-restored)$") + .exec(core.getInput(constants_1.Inputs.FailOn))) === null || _a === void 0 ? void 0 : _a.slice(1); + if (!failOnRaw) { + return; + } + const [keyType, result] = failOnRaw; + if ((keyType == "primary" || keyType == "first-match") && + (result == "miss" || result == "not-restored")) { + return { keyType, result }; + } +})(); +exports.nix = utils.getInputAsBool(constants_1.Inputs.Nix); +exports.save = utils.getInputAsBool(constants_1.Inputs.Save); +exports.paths = (exports.nix ? ["/nix/", "~/.cache/nix", "~root/.cache/nix"] : []).concat((() => { + const paths = utils.getInputAsArray(constants_1.Inputs.Paths); + const pathsPlatform = utils.getInputAsArray(utils.isLinux ? constants_1.Inputs.PathsLinux : constants_1.Inputs.PathsMacos); + if (pathsPlatform.length > 0) { + return pathsPlatform; + } + else + return paths; +})()); +exports.gcMaxStoreSize = exports.nix + ? (() => { + const gcMaxStoreSize = utils.getInputAsInt(constants_1.Inputs.GCMaxStoreSize); + const gcMaxStoreSizePlatform = utils.getInputAsInt(utils.isLinux + ? constants_1.Inputs.GCMaxStoreSizeLinux + : constants_1.Inputs.GCMaxStoreSizeMacos); + return gcMaxStoreSizePlatform + ? gcMaxStoreSizePlatform + : gcMaxStoreSize; + })() + : undefined; +exports.purge = utils.getInputAsBool(constants_1.Inputs.Purge); +exports.purgeOverwrite = (() => { + const purgeOverwrite = core.getInput(constants_1.Inputs.PurgeOverwrite); + if (!(purgeOverwrite == "always" || purgeOverwrite == "never")) { + return "default"; + } + return purgeOverwrite; +})(); +exports.purgePrefixes = utils.getInputAsArray(constants_1.Inputs.PurgePrefixes); +exports.purgeLastAccessed = utils.getInputAsInt(constants_1.Inputs.PurgeLastAccessed); +exports.purgeCreatedMaxAge = utils.getInputAsInt(constants_1.Inputs.PurgeCreated); +exports.uploadChunkSize = utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) || 32 * 1024 * 1024; +exports.token = core.getInput(constants_1.Inputs.Token, { required: true }); /***/ }), @@ -62741,21 +62818,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -const cache = __importStar(__nccwpck_require__(7799)); +const cache = __importStar(__nccwpck_require__(7942)); const core = __importStar(__nccwpck_require__(2186)); -const github_1 = __nccwpck_require__(5438); -const github = __importStar(__nccwpck_require__(5438)); const constants_1 = __nccwpck_require__(9042); -const gc_1 = __nccwpck_require__(2193); -const purge_1 = __nccwpck_require__(6231); -const utils = __importStar(__nccwpck_require__(6850)); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +const collectGarbage_1 = __nccwpck_require__(9311); +const purge_1 = __nccwpck_require__(7612); // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to // throw an uncaught exception. Instead of failing this action, just warn. process.on("uncaughtException", e => utils.logError(e.message)); function saveImpl(stateProvider) { return __awaiter(this, void 0, void 0, function* () { - let cacheId = -1; + const cacheId = -1; + const time = Date.now(); try { if (!utils.isCacheFeatureAvailable()) { return; @@ -62765,57 +62842,55 @@ function saveImpl(stateProvider) { } // If restore has stored a primary key in state, reuse that // Else re-evaluate from inputs - const primaryKey = stateProvider.getState(constants_1.State.CachePrimaryKey) || - core.getInput(constants_1.Inputs.Key); - if (!primaryKey || primaryKey === "") { - throw new Error(` - Primary cache key not found. - You may want to specify it in your workflow file. - See "with.key" field at the step where you use this action. - `); - } - const cachePaths = utils.paths; - const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys); - const restoredKey = yield utils.getCacheKey({ - paths: cachePaths, - primaryKey, - restoreKeys, - lookupOnly: true - }); - const time = Date.now(); - if (utils.isExactKeyMatch(primaryKey, restoredKey)) { - utils.info(`Cache hit occurred on the primary key "${primaryKey}".`); - const caches = yield (0, purge_1.purgeCaches)({ - key: primaryKey, - lookupOnly: true, - time + const primaryKey = stateProvider.getState(constants_1.State.CachePrimaryKey) || inputs.primaryKey; + if (inputs.purge) { + if (inputs.purgeOverwrite == "always") { + yield (0, purge_1.purgeCacheByKey)(primaryKey, `Purging the cache with the key "${primaryKey}" because of "${constants_1.Inputs.PurgeOverwrite}: always".`); + } + else { + yield (0, purge_1.purgeCachesByTime)({ + primaryKey, + time, + prefixes: [] + }); + } + } + // Save a cache using the primary key + { + utils.info(`Searching for a cache using the primary key "${primaryKey}".`); + const foundKey = yield utils.restoreCache({ + primaryKey, + restoreKeys: [], + lookupOnly: true }); - if (caches.map(cache => cache.key).includes(primaryKey)) { - utils.info(`Purging the cache with the key "${primaryKey}".`); - const token = core.getInput(constants_1.Inputs.Token, { required: true }); - const octokit = (0, github_1.getOctokit)(token); - octokit.rest.actions.deleteActionsCacheByKey({ - per_page: 100, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - key: primaryKey, - ref: github.context.ref + if (utils.isExactKeyMatch(primaryKey, foundKey)) { + utils.info(` + Cache hit occurred on the "${constants_1.Inputs.PrimaryKey}". + Not saving a new cache. + `); + } + else if (inputs.save) { + yield (0, collectGarbage_1.collectGarbage)(); + utils.info(`Saving a new cache with the key "${primaryKey}".`); + // can throw + yield cache.saveCache(inputs.paths, primaryKey, { + uploadChunkSize: inputs.uploadChunkSize }); + utils.info(`Saved a new cache.`); } else { - utils.info(`The cache with the key "${primaryKey}" won't be purged. Not saving a new cache.`); - yield (0, purge_1.purgeCaches)({ key: primaryKey, lookupOnly: false, time }); - return; + `Not saving a new cache because of "${constants_1.Inputs.Save}: false"`; } } - yield (0, gc_1.collectGarbage)(); - utils.info(`Saving a new cache with the key "${primaryKey}".`); - cacheId = yield cache.saveCache(cachePaths, primaryKey, { - uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) - }); - if (cacheId != -1) { - utils.info(`Cache saved with the key "${primaryKey}".`); - yield (0, purge_1.purgeCaches)({ key: primaryKey, lookupOnly: false, time }); + // Purge other caches + // This runs last so that in case of cache saving errors + // the action can be re-run with other caches + if (inputs.purge) { + yield (0, purge_1.purgeCachesByTime)({ + primaryKey, + time, + prefixes: inputs.purgePrefixes + }); } } catch (error) { @@ -62927,7 +63002,7 @@ class StateProviderBase { this.getState = (key) => ""; } getCacheState() { - const cacheKey = this.getState(constants_1.State.CacheMatchedKey); + const cacheKey = this.getState(constants_1.State.CacheRestoredKey); if (cacheKey) { core.debug(`Cache state/key: ${cacheKey}`); return cacheKey; @@ -62947,8 +63022,8 @@ class NullStateProvider extends StateProviderBase { constructor() { super(...arguments); this.stateToOutputMap = new Map([ - [constants_1.State.CacheMatchedKey, constants_1.Outputs.CacheMatchedKey], - [constants_1.State.CachePrimaryKey, constants_1.Outputs.CachePrimaryKey] + [constants_1.State.CacheRestoredKey, constants_1.Outputs.RestoredKey], + [constants_1.State.CachePrimaryKey, constants_1.Outputs.PrimaryKey] ]); this.setState = (key, value) => { core.setOutput(this.stateToOutputMap.get(key), value); @@ -62962,7 +63037,7 @@ exports.NullStateProvider = NullStateProvider; /***/ }), -/***/ 6850: +/***/ 4427: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -63003,12 +63078,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.filterCachesByTime = exports.getCachesByKeys = exports.getCacheKey = exports.paths = exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; -const cache = __importStar(__nccwpck_require__(7799)); +exports.warning = exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.getCachesByKeys = exports.restoreCache = exports.isCacheFeatureAvailable = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; +const cache = __importStar(__nccwpck_require__(7942)); const core = __importStar(__nccwpck_require__(2186)); const github = __importStar(__nccwpck_require__(5438)); const dedent_1 = __importDefault(__nccwpck_require__(5281)); const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); function isGhes() { const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com"); return ghUrl.hostname.toUpperCase() !== "GITHUB.COM"; @@ -63023,7 +63099,7 @@ function isExactKeyMatch(key, cacheKey) { exports.isExactKeyMatch = isExactKeyMatch; function logWarning(message) { const warningPrefix = "[warning]"; - core.info(`${warningPrefix}${message}`); + core.warning(`${warningPrefix} ${message}`); } exports.logWarning = logWarning; function logError(message) { @@ -63032,58 +63108,39 @@ function logError(message) { } exports.logError = logError; // Cache token authorized for all events that are tied to a ref -// See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context +// See GitHub Context https://docs.github.com/en/actions/learn-github-actions/contexts function isValidEvent() { return constants_1.RefKey in process.env && Boolean(process.env[constants_1.RefKey]); } exports.isValidEvent = isValidEvent; -function getInputAsArray(name, options) { - return core - .getInput(name, options) - .split("\n") - .map(s => s.replace(/^!\s+/, "!").trim()) - .filter(x => x !== ""); -} -exports.getInputAsArray = getInputAsArray; -function getInputAsInt(name, options) { - const value = parseInt(core.getInput(name, options)); - if (isNaN(value) || value < 0) { - return undefined; - } - return value; -} -exports.getInputAsInt = getInputAsInt; -function getInputAsBool(name, options) { - const result = core.getInput(name, options); - return result.toLowerCase() === "true"; -} -exports.getInputAsBool = getInputAsBool; function isCacheFeatureAvailable() { if (cache.isFeatureAvailable()) { return true; } if (isGhes()) { - logWarning(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. + logError(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`); return false; } - logWarning("An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions."); + logError(` + Actions cache service is unavailable. + Please check https://www.githubstatus.com/ for any ongoing issue in Actions. + `); return false; } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; -exports.paths = ["/nix/", "~/.cache/nix", "~root/.cache/nix"]; -function getCacheKey({ paths, primaryKey, restoreKeys, lookupOnly }) { +function restoreCache({ primaryKey, restoreKeys, lookupOnly }) { return __awaiter(this, void 0, void 0, function* () { return yield cache.restoreCache( // https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 - paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false); + inputs.paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false, ["--skip-old-files"]); }); } -exports.getCacheKey = getCacheKey; -function getCachesByKeys(token, keys) { +exports.restoreCache = restoreCache; +function getCachesByKeys(keys) { return __awaiter(this, void 0, void 0, function* () { const caches = []; - const octokit = github.getOctokit(token); + const octokit = github.getOctokit(inputs.token); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; for (let page = 1; page <= 500; page += 1) { @@ -63098,32 +63155,22 @@ function getCachesByKeys(token, keys) { if (cachesRequest.actions_caches.length == 0) { break; } - caches.push(...cachesRequest.actions_caches); + if (isExactKeyMatch(inputs.primaryKey, key)) { + caches.push(...cachesRequest.actions_caches); + } } } return caches; }); } exports.getCachesByKeys = getCachesByKeys; -const filterCachesByTime = ({ caches, doUseLastAccessedTime, maxDate }) => caches.filter(cache => { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - return atDate < maxDate; - } - else - return false; -}); -exports.filterCachesByTime = filterCachesByTime; -const mkMessageWrongValue = (input, value) => `Wrong value for the input '${input}': ${value}`; +const mkMessageWrongValue = (input, value) => `Wrong value for the input "${input}": ${value}`; exports.mkMessageWrongValue = mkMessageWrongValue; function getMaxDate({ doUseLastAccessedTime, time }) { const inputMaxAge = doUseLastAccessedTime - ? constants_1.Inputs.PurgeAccessedMaxAge - : constants_1.Inputs.PurgeCreatedMaxAge; - const maxAge = core.getInput(inputMaxAge, { required: false }); + ? constants_1.Inputs.PurgeLastAccessed + : constants_1.Inputs.PurgeCreated; + const maxAge = core.getInput(inputMaxAge); const maxDate = new Date(time - Number.parseInt(maxAge) * 1000); if (maxDate === null) { throw new Error((0, exports.mkMessageWrongValue)(inputMaxAge, maxAge)); @@ -63133,10 +63180,287 @@ function getMaxDate({ doUseLastAccessedTime, time }) { exports.getMaxDate = getMaxDate; const stringify = (value) => JSON.stringify(value, null, 2); exports.stringify = stringify; -const info = (message) => { - core.info(dedent_1.default.withOptions({})(message)); -}; +const myDedent = dedent_1.default.withOptions({}); +const info = (message) => core.info(myDedent(message)); exports.info = info; +const warning = (message) => core.warning(myDedent(message)); +exports.warning = warning; + + +/***/ }), + +/***/ 9311: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.collectGarbage = void 0; +const exec_1 = __nccwpck_require__(1514); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +function collectGarbage() { + return __awaiter(this, void 0, void 0, function* () { + if (inputs.gcMaxStoreSize) { + utils.info("Collecting garbage."); + const printStoreSize = ` + STORE_SIZE="$(nix path-info --json --all | jq 'map(.narSize) | add')" + printf "Current store size in bytes: $STORE_SIZE\\n" + `; + const run = (command) => __awaiter(this, void 0, void 0, function* () { return yield (0, exec_1.exec)("bash", ["-c", command]); }); + yield run(printStoreSize); + yield run(` + sudo rm -rf /nix/.[!.]* /nix/..?* + + MAX_STORE_SIZE=${inputs.gcMaxStoreSize} + + if (( STORE_SIZE > MAX_STORE_SIZE )); then + (( R1 = STORE_SIZE - MAX_STORE_SIZE )) + (( R2 = R1 > 0 ? R1 : 0 )) + printf "Max bytes to free: $R2\\n" + nix store gc --max "$R2" + fi + `); + utils.info(`Finished collecting garbage.`); + yield run(printStoreSize); + } + }); +} +exports.collectGarbage = collectGarbage; + + +/***/ }), + +/***/ 9378: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isLinux = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = void 0; +const core = __importStar(__nccwpck_require__(2186)); +function getInputAsArray(name, options) { + return core + .getInput(name, options) + .split("\n") + .map(s => s.replace(/^!\s+/, "!").trim()) + .filter(x => x !== ""); +} +exports.getInputAsArray = getInputAsArray; +function getInputAsInt(name, options) { + const value = parseInt(core.getInput(name, options)); + if (isNaN(value) || value < 0) { + return undefined; + } + return value; +} +exports.getInputAsInt = getInputAsInt; +function getInputAsBool(name, options) { + const result = core.getInput(name, options); + return result.toLowerCase() === "true"; +} +exports.getInputAsBool = getInputAsBool; +exports.isLinux = process.platform === "linux"; + + +/***/ }), + +/***/ 7612: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.purgeCachesByTime = exports.filterCachesByTime = exports.purgeCacheByKey = void 0; +const github = __importStar(__nccwpck_require__(5438)); +const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +function purgeCacheByKey(key, message) { + return __awaiter(this, void 0, void 0, function* () { + try { + utils.info(message || ""); + const octokit = github.getOctokit(inputs.token); + yield octokit.rest.actions.deleteActionsCacheByKey({ + per_page: 100, + owner: github.context.repo.owner, + repo: github.context.repo.repo, + key, + ref: github.context.ref + }); + } + catch (error) { + utils.info(` + Failed to delete the cache. + + ${error} + `); + } + }); +} +exports.purgeCacheByKey = purgeCacheByKey; +const filterCachesByTime = ({ caches, doUseLastAccessedTime, maxDate }) => caches.filter(cache => { + const at = doUseLastAccessedTime + ? cache.last_accessed_at + : cache.created_at; + if (at && cache.key) { + const atDate = new Date(at); + return atDate < maxDate; + } + else + return false; +}); +exports.filterCachesByTime = filterCachesByTime; +function purgeByTime({ primaryKey, doUseLastAccessedTime, prefixes, time }) { + return __awaiter(this, void 0, void 0, function* () { + const verb = doUseLastAccessedTime ? "last accessed" : "created"; + const maxDate = utils.getMaxDate({ doUseLastAccessedTime, time }); + let caches = []; + if (prefixes.length > 0) { + utils.info(` + Purging cache(s) ${verb} before ${maxDate.toISOString()} and having key prefixes: + + ${utils.stringify(prefixes)} + `); + caches = (0, exports.filterCachesByTime)({ + caches: yield utils.getCachesByKeys(prefixes), + doUseLastAccessedTime, + maxDate + }); + } + else { + utils.info(`Purging cache(s) ${verb} before ${maxDate.toISOString()} and having the key "${primaryKey}".`); + caches = (0, exports.filterCachesByTime)({ + caches: yield utils.getCachesByKeys([primaryKey]), + doUseLastAccessedTime, + maxDate + }).filter(x => utils.isExactKeyMatch(primaryKey, x.key)); + } + if (inputs.purgeOverwrite == "never") { + `The cache with the key "${primaryKey}" will be skipped because of "${constants_1.Inputs.PurgeOverwrite}: never".`; + caches.filter(x => !utils.isExactKeyMatch(primaryKey, x.key)); + } + utils.info(` + Found ${caches.length} cache(s): + + ${utils.stringify(caches)} + `); + for (const cache of caches) { + const at = doUseLastAccessedTime + ? cache.last_accessed_at + : cache.created_at; + if (at && cache.key) { + const atDate = new Date(at); + const atDatePretty = atDate.toISOString(); + yield purgeCacheByKey(cache.key, `Purging the cache ${verb} at ${atDatePretty} and having the key "${cache.key}".`); + } + } + utils.info(`Finished purging cache(s).`); + }); +} +function purgeCachesByTime({ primaryKey, time, prefixes }) { + return __awaiter(this, void 0, void 0, function* () { + // TODO https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 + for (const flag of [true, false]) { + if (flag ? inputs.purgeLastAccessed : inputs.purgeCreatedMaxAge) { + yield purgeByTime({ + primaryKey, + doUseLastAccessedTime: flag, + prefixes: prefixes.slice(), + time + }); + } + } + }); +} +exports.purgeCachesByTime = purgeCachesByTime; /***/ }), diff --git a/dist/save/index.js b/dist/save/index.js index 8d9d0b8a..e28aeeb4 100644 --- a/dist/save/index.js +++ b/dist/save/index.js @@ -1,1815 +1,886 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 7799: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +/***/ 1898: +/***/ ((module, exports) => { -"use strict"; +exports = module.exports = SemVer -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const cacheHttpClient = __importStar(__nccwpck_require__(8245)); -const tar_1 = __nccwpck_require__(6490); -class ValidationError extends Error { - constructor(message) { - super(message); - this.name = 'ValidationError'; - Object.setPrototypeOf(this, ValidationError.prototype); - } -} -exports.ValidationError = ValidationError; -class ReserveCacheError extends Error { - constructor(message) { - super(message); - this.name = 'ReserveCacheError'; - Object.setPrototypeOf(this, ReserveCacheError.prototype); - } -} -exports.ReserveCacheError = ReserveCacheError; -function checkPaths(paths) { - if (!paths || paths.length === 0) { - throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); - } -} -function checkKey(key) { - if (key.length > 512) { - throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); - } - const regex = /^[^,]*$/; - if (!regex.test(key)) { - throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); - } -} -/** - * isFeatureAvailable to check the presence of Actions cache service - * - * @returns boolean return true if Actions cache service feature is available, otherwise false - */ -function isFeatureAvailable() { - return !!process.env['ACTIONS_CACHE_URL']; -} -exports.isFeatureAvailable = isFeatureAvailable; -/** - * Restores cache from keys - * - * @param paths a list of file paths to restore from the cache - * @param primaryKey an explicit key for restoring the cache - * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key - * @param downloadOptions cache download options - * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform - * @returns string returns the key for the cache hit, otherwise returns undefined - */ -function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - restoreKeys = restoreKeys || []; - const keys = [primaryKey, ...restoreKeys]; - core.debug('Resolved Keys:'); - core.debug(JSON.stringify(keys)); - if (keys.length > 10) { - throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); - } - for (const key of keys) { - checkKey(key); - } - const compressionMethod = yield utils.getCompressionMethod(); - let archivePath = ''; - try { - // path are needed to compute version - const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { - compressionMethod, - enableCrossOsArchive - }); - if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { - // Cache not found - return undefined; - } - if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - core.info('Lookup only - skipping download'); - return cacheEntry.cacheKey; - } - archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - // Download the cache from the cache entry - yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - yield (0, tar_1.extractTar)(archivePath, compressionMethod); - core.info('Cache restored successfully'); - return cacheEntry.cacheKey; - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else { - // Supress all non-validation cache related errors because caching should be optional - core.warning(`Failed to restore: ${error.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return undefined; - }); -} -exports.restoreCache = restoreCache; -/** - * Saves a list of files with the specified key - * - * @param paths a list of file paths to be cached - * @param key an explicit key for restoring the cache - * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform - * @param options cache upload options - * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails - */ -function saveCache(paths, key, options, enableCrossOsArchive = false) { - var _a, _b, _c, _d, _e; - return __awaiter(this, void 0, void 0, function* () { - checkPaths(paths); - checkKey(key); - const compressionMethod = yield utils.getCompressionMethod(); - let cacheId = -1; - const cachePaths = yield utils.resolvePaths(paths); - core.debug('Cache Paths:'); - core.debug(`${JSON.stringify(cachePaths)}`); - if (cachePaths.length === 0) { - throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); - } - const archiveFolder = yield utils.createTempDirectory(); - const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); - try { - yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); - if (core.isDebug()) { - yield (0, tar_1.listTar)(archivePath, compressionMethod); - } - const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.debug(`File Size: ${archiveFileSize}`); - // For GHES, this check will take place in ReserveCache API with enterprise file size limit - if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { - throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); - } - core.debug('Reserving Cache'); - const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { - compressionMethod, - enableCrossOsArchive, - cacheSize: archiveFileSize - }); - if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { - cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; - } - else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { - throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); - } - else { - throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); - } - core.debug(`Saving Cache (ID: ${cacheId})`); - yield cacheHttpClient.saveCache(cacheId, archivePath, options); - } - catch (error) { - const typedError = error; - if (typedError.name === ValidationError.name) { - throw error; - } - else if (typedError.name === ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); - } - else { - core.warning(`Failed to save: ${typedError.message}`); - } - } - finally { - // Try to delete the archive to save space - try { - yield utils.unlinkFile(archivePath); - } - catch (error) { - core.debug(`Failed to delete archive: ${error}`); - } - } - return cacheId; - }); +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} } -exports.saveCache = saveCache; -//# sourceMappingURL=cache.js.map -/***/ }), +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' -/***/ 8245: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 -"use strict"; +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const auth_1 = __nccwpck_require__(5526); -const crypto = __importStar(__nccwpck_require__(6113)); -const fs = __importStar(__nccwpck_require__(7147)); -const url_1 = __nccwpck_require__(7310); -const utils = __importStar(__nccwpck_require__(1518)); -const downloadUtils_1 = __nccwpck_require__(5500); -const options_1 = __nccwpck_require__(6215); -const requestUtils_1 = __nccwpck_require__(3981); -const versionSalt = '1.0'; -function getCacheApiUrl(resource) { - const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; - if (!baseUrl) { - throw new Error('Cache Service Url not found, unable to restore cache.'); - } - const url = `${baseUrl}_apis/artifactcache/${resource}`; - core.debug(`Resource Url: ${url}`); - return url; -} -function createAcceptHeader(type, apiVersion) { - return `${type};api-version=${apiVersion}`; -} -function getRequestOptions() { - const requestOptions = { - headers: { - Accept: createAcceptHeader('application/json', '6.0-preview.1') - } - }; - return requestOptions; -} -function createHttpClient() { - const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; - const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); - return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); -} -function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { - const components = paths; - // Add compression method to cache version to restore - // compressed cache as per compression method - if (compressionMethod) { - components.push(compressionMethod); - } - // Only check for windows platforms if enableCrossOsArchive is false - if (process.platform === 'win32' && !enableCrossOsArchive) { - components.push('windows-only'); - } - // Add salt to cache version to support breaking changes in cache entry - components.push(versionSalt); - return crypto - .createHash('sha256') - .update(components.join('|')) - .digest('hex'); -} -exports.getCacheVersion = getCacheVersion; -function getCacheEntry(keys, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - // Cache not found - if (response.statusCode === 204) { - // List cache for primary key only if cache miss occurs - if (core.isDebug()) { - yield printCachesListForDiagnostics(keys[0], httpClient, version); - } - return null; - } - if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { - throw new Error(`Cache service responded with ${response.statusCode}`); - } - const cacheResult = response.result; - const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; - if (!cacheDownloadUrl) { - // Cache achiveLocation not found. This should never happen, and hence bail out. - throw new Error('Cache not found.'); - } - core.setSecret(cacheDownloadUrl); - core.debug(`Cache Result:`); - core.debug(JSON.stringify(cacheResult)); - return cacheResult; - }); -} -exports.getCacheEntry = getCacheEntry; -function printCachesListForDiagnostics(key, httpClient, version) { - return __awaiter(this, void 0, void 0, function* () { - const resource = `caches?key=${encodeURIComponent(key)}`; - const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); - if (response.statusCode === 200) { - const cacheListResult = response.result; - const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; - if (totalCount && totalCount > 0) { - core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); - for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); - } - } - } - }); -} -function downloadCache(archiveLocation, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const archiveUrl = new url_1.URL(archiveLocation); - const downloadOptions = (0, options_1.getDownloadOptions)(options); - if (downloadOptions.useAzureSdk && - archiveUrl.hostname.endsWith('.blob.core.windows.net')) { - // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. - yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); - } - else { - // Otherwise, download using the Actions http-client. - yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); - } - }); -} -exports.downloadCache = downloadCache; -// Reserve Cache -function reserveCache(key, paths, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); - const reserveCacheRequest = { - key, - version, - cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize - }; - const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); - })); - return response; - }); -} -exports.reserveCache = reserveCache; -function getContentRange(start, end) { - // Format: `bytes start-end/filesize - // start and end are inclusive - // filesize can be * - // For a 200 byte chunk starting at byte 0: - // Content-Range: bytes 0-199/* - return `bytes ${start}-${end}/*`; -} -function uploadChunk(httpClient, resourceUrl, openStream, start, end) { - return __awaiter(this, void 0, void 0, function* () { - core.debug(`Uploading chunk of size ${end - - start + - 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); - const additionalHeaders = { - 'Content-Type': 'application/octet-stream', - 'Content-Range': getContentRange(start, end) - }; - const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { - return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); - })); - if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { - throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); - } - }); -} -function uploadFile(httpClient, cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - // Upload Chunks - const fileSize = utils.getArchiveFileSizeInBytes(archivePath); - const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs.openSync(archivePath, 'r'); - const uploadOptions = (0, options_1.getUploadOptions)(options); - const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); - const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); - const parallelUploads = [...new Array(concurrency).keys()]; - core.debug('Awaiting all uploads'); - let offset = 0; - try { - yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { - while (offset < fileSize) { - const chunkSize = Math.min(fileSize - offset, maxChunkSize); - const start = offset; - const end = offset + chunkSize - 1; - offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs - .createReadStream(archivePath, { - fd, - start, - end, - autoClose: false - }) - .on('error', error => { - throw new Error(`Cache upload failed because file read failed with ${error.message}`); - }), start, end); - } - }))); - } - finally { - fs.closeSync(fd); - } - return; - }); -} -function commitCache(httpClient, cacheId, filesize) { - return __awaiter(this, void 0, void 0, function* () { - const commitCacheRequest = { size: filesize }; - return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { - return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); - })); - }); -} -function saveCache(cacheId, archivePath, options) { - return __awaiter(this, void 0, void 0, function* () { - const httpClient = createHttpClient(); - core.debug('Upload cache'); - yield uploadFile(httpClient, cacheId, archivePath, options); - // Commit Cache - core.debug('Commiting cache'); - const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); - const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); - if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { - throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); - } - core.info('Cache saved successfully'); - }); +var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + +// The actual regexps go on exports.re +var re = exports.re = [] +var safeRe = exports.safeRe = [] +var src = exports.src = [] +var t = exports.tokens = {} +var R = 0 + +function tok (n) { + t[n] = R++ } -exports.saveCache = saveCache; -//# sourceMappingURL=cacheHttpClient.js.map -/***/ }), +var LETTERDASHNUMBER = '[a-zA-Z0-9-]' -/***/ 1518: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +var safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] -"use strict"; +function makeSafeRe (value) { + for (var i = 0; i < safeRegexReplacements.length; i++) { + var token = safeRegexReplacements[i][0] + var max = safeRegexReplacements[i][1] + value = value + .split(token + '*').join(token + '{0,' + max + '}') + .split(token + '+').join(token + '{1,' + max + '}') + } + return value +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const exec = __importStar(__nccwpck_require__(1514)); -const glob = __importStar(__nccwpck_require__(8090)); -const io = __importStar(__nccwpck_require__(7436)); -const fs = __importStar(__nccwpck_require__(7147)); -const path = __importStar(__nccwpck_require__(1017)); -const semver = __importStar(__nccwpck_require__(3771)); -const util = __importStar(__nccwpck_require__(3837)); -const uuid_1 = __nccwpck_require__(2155); -const constants_1 = __nccwpck_require__(8840); -// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 -function createTempDirectory() { - return __awaiter(this, void 0, void 0, function* () { - const IS_WINDOWS = process.platform === 'win32'; - let tempDirectory = process.env['RUNNER_TEMP'] || ''; - if (!tempDirectory) { - let baseLocation; - if (IS_WINDOWS) { - // On Windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\'; - } - else { - if (process.platform === 'darwin') { - baseLocation = '/Users'; - } - else { - baseLocation = '/home'; - } - } - tempDirectory = path.join(baseLocation, 'actions', 'temp'); - } - const dest = path.join(tempDirectory, (0, uuid_1.v4)()); - yield io.mkdirP(dest); - return dest; - }); -} -exports.createTempDirectory = createTempDirectory; -function getArchiveFileSizeInBytes(filePath) { - return fs.statSync(filePath).size; -} -exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; -function resolvePaths(patterns) { - var e_1, _a; - var _b; - return __awaiter(this, void 0, void 0, function* () { - const paths = []; - const workspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); - const globber = yield glob.create(patterns.join('\n'), { - implicitDescendants: false - }); - try { - for (var _c = __asyncValues(globber.globGenerator()), _d; _d = yield _c.next(), !_d.done;) { - const file = _d.value; - const relativeFile = path - .relative(workspace, file) - .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); - core.debug(`Matched: ${relativeFile}`); - // Paths are made relative so the tar entries are all relative to the root of the workspace. - if (relativeFile === '') { - // path.relative returns empty string if workspace and file are equal - paths.push('.'); - } - else { - paths.push(`${relativeFile}`); - } - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } - return paths; - }); -} -exports.resolvePaths = resolvePaths; -function unlinkFile(filePath) { - return __awaiter(this, void 0, void 0, function* () { - return util.promisify(fs.unlink)(filePath); - }); -} -exports.unlinkFile = unlinkFile; -function getVersion(app, additionalArgs = []) { - return __awaiter(this, void 0, void 0, function* () { - let versionOutput = ''; - additionalArgs.push('--version'); - core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); - try { - yield exec.exec(`${app}`, additionalArgs, { - ignoreReturnCode: true, - silent: true, - listeners: { - stdout: (data) => (versionOutput += data.toString()), - stderr: (data) => (versionOutput += data.toString()) - } - }); - } - catch (err) { - core.debug(err.message); - } - versionOutput = versionOutput.trim(); - core.debug(versionOutput); - return versionOutput; - }); +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +tok('NUMERICIDENTIFIER') +src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' +tok('NUMERICIDENTIFIERLOOSE') +src[t.NUMERICIDENTIFIERLOOSE] = '\\d+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +tok('NONNUMERICIDENTIFIER') +src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-]' + LETTERDASHNUMBER + '*' + +// ## Main Version +// Three dot-separated numeric identifiers. + +tok('MAINVERSION') +src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')' + +tok('MAINVERSIONLOOSE') +src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +tok('PRERELEASEIDENTIFIER') +src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +tok('PRERELEASEIDENTIFIERLOOSE') +src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +tok('PRERELEASE') +src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' + +tok('PRERELEASELOOSE') +src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +tok('BUILDIDENTIFIER') +src[t.BUILDIDENTIFIER] = LETTERDASHNUMBER + '+' + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +tok('BUILD') +src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + + '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +tok('FULL') +tok('FULLPLAIN') +src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + + src[t.PRERELEASE] + '?' + + src[t.BUILD] + '?' + +src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +tok('LOOSEPLAIN') +src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + + src[t.PRERELEASELOOSE] + '?' + + src[t.BUILD] + '?' + +tok('LOOSE') +src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + +tok('GTLT') +src[t.GTLT] = '((?:<|>)?=?)' + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +tok('XRANGEIDENTIFIERLOOSE') +src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +tok('XRANGEIDENTIFIER') +src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + +tok('XRANGEPLAIN') +src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:' + src[t.PRERELEASE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGEPLAINLOOSE') +src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[t.PRERELEASELOOSE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' + +tok('XRANGE') +src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' +tok('XRANGELOOSE') +src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +tok('COERCE') +src[t.COERCE] = '(^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' +tok('COERCERTL') +re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') +safeRe[t.COERCERTL] = new RegExp(makeSafeRe(src[t.COERCE]), 'g') + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +tok('LONETILDE') +src[t.LONETILDE] = '(?:~>?)' + +tok('TILDETRIM') +src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' +re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') +safeRe[t.TILDETRIM] = new RegExp(makeSafeRe(src[t.TILDETRIM]), 'g') +var tildeTrimReplace = '$1~' + +tok('TILDE') +src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' +tok('TILDELOOSE') +src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +tok('LONECARET') +src[t.LONECARET] = '(?:\\^)' + +tok('CARETTRIM') +src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' +re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') +safeRe[t.CARETTRIM] = new RegExp(makeSafeRe(src[t.CARETTRIM]), 'g') +var caretTrimReplace = '$1^' + +tok('CARET') +src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' +tok('CARETLOOSE') +src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +tok('COMPARATORLOOSE') +src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' +tok('COMPARATOR') +src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +tok('COMPARATORTRIM') +src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + + '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' + +// this one has to use the /g flag +re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') +safeRe[t.COMPARATORTRIM] = new RegExp(makeSafeRe(src[t.COMPARATORTRIM]), 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +tok('HYPHENRANGE') +src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAIN] + ')' + + '\\s*$' + +tok('HYPHENRANGELOOSE') +src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s*$' + +// Star ranges basically just allow anything at all. +tok('STAR') +src[t.STAR] = '(<|>)?=?\\s*\\*' + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + safeRe[i] = new RegExp(makeSafeRe(src[i])) + } } -// Use zstandard if possible to maximize cache performance -function getCompressionMethod() { - return __awaiter(this, void 0, void 0, function* () { - const versionOutput = yield getVersion('zstd', ['--quiet']); - const version = semver.clean(versionOutput); - core.debug(`zstd version: ${version}`); - if (versionOutput === '') { - return constants_1.CompressionMethod.Gzip; - } - else { - return constants_1.CompressionMethod.ZstdWithoutLong; - } - }); + +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + var r = options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } } -exports.getCompressionMethod = getCompressionMethod; -function getCacheFileName(compressionMethod) { - return compressionMethod === constants_1.CompressionMethod.Gzip - ? constants_1.CacheFilename.Gzip - : constants_1.CacheFilename.Zstd; + +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null } -exports.getCacheFileName = getCacheFileName; -function getGnuTarPathOnWindows() { - return __awaiter(this, void 0, void 0, function* () { - if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { - return constants_1.GnuTarPathOnWindows; - } - const versionOutput = yield getVersion('tar'); - return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; - }); + +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null } -exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; -function assertDefined(name, value) { - if (value === undefined) { - throw Error(`Expected ${name} but value was undefiend`); + +exports.SemVer = SemVer + +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } - return value; -} -exports.assertDefined = assertDefined; -function isGhes() { - const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; -} -exports.isGhes = isGhes; -//# sourceMappingURL=cacheUtils.js.map + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } -/***/ }), + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } -/***/ 8840: -/***/ ((__unused_webpack_module, exports) => { + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } -"use strict"; + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; -var CacheFilename; -(function (CacheFilename) { - CacheFilename["Gzip"] = "cache.tgz"; - CacheFilename["Zstd"] = "cache.tzst"; -})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); -var CompressionMethod; -(function (CompressionMethod) { - CompressionMethod["Gzip"] = "gzip"; - // Long range mode was added to zstd in v1.3.2. - // This enum is for earlier version of zstd that does not have --long support - CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; - CompressionMethod["Zstd"] = "zstd"; -})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); -var ArchiveToolType; -(function (ArchiveToolType) { - ArchiveToolType["GNU"] = "gnu"; - ArchiveToolType["BSD"] = "bsd"; -})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); -// The default number of retry attempts. -exports.DefaultRetryAttempts = 2; -// The default delay in milliseconds between retry attempts. -exports.DefaultRetryDelay = 5000; -// Socket timeout in milliseconds during download. If no traffic is received -// over the socket during this period, the socket is destroyed and the download -// is aborted. -exports.SocketTimeout = 5000; -// The default path of GNUtar on hosted Windows runners -exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; -// The default path of BSDtar on hosted Windows runners -exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; -exports.TarFilename = 'cache.tar'; -exports.ManifestFilename = 'manifest.txt'; -//# sourceMappingURL=constants.js.map + var m = version.trim().match(options.loose ? safeRe[t.LOOSE] : safeRe[t.FULL]) -/***/ }), + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } -/***/ 5500: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + this.raw = version -"use strict"; + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.downloadCacheStorageSDK = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const storage_blob_1 = __nccwpck_require__(4100); -const buffer = __importStar(__nccwpck_require__(4300)); -const fs = __importStar(__nccwpck_require__(7147)); -const stream = __importStar(__nccwpck_require__(2781)); -const util = __importStar(__nccwpck_require__(3837)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const requestUtils_1 = __nccwpck_require__(3981); -const abort_controller_1 = __nccwpck_require__(2557); -/** - * Pipes the body of a HTTP response to a stream - * - * @param response the HTTP response - * @param output the writable stream - */ -function pipeResponseToStream(response, output) { - return __awaiter(this, void 0, void 0, function* () { - const pipeline = util.promisify(stream.pipeline); - yield pipeline(response.message, output); - }); -} -/** - * Class for tracking the download state and displaying stats. - */ -class DownloadProgress { - constructor(contentLength) { - this.contentLength = contentLength; - this.segmentIndex = 0; - this.segmentSize = 0; - this.segmentOffset = 0; - this.receivedBytes = 0; - this.displayedComplete = false; - this.startTime = Date.now(); - } - /** - * Progress to the next segment. Only call this method when the previous segment - * is complete. - * - * @param segmentSize the length of the next segment - */ - nextSegment(segmentSize) { - this.segmentOffset = this.segmentOffset + this.segmentSize; - this.segmentIndex = this.segmentIndex + 1; - this.segmentSize = segmentSize; - this.receivedBytes = 0; - core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); - } - /** - * Sets the number of bytes received for the current segment. - * - * @param receivedBytes the number of bytes received - */ - setReceivedBytes(receivedBytes) { - this.receivedBytes = receivedBytes; - } - /** - * Returns the total number of bytes transferred. - */ - getTransferredBytes() { - return this.segmentOffset + this.receivedBytes; - } - /** - * Returns true if the download is complete. - */ - isDone() { - return this.getTransferredBytes() === this.contentLength; - } - /** - * Prints the current download stats. Once the download completes, this will print one - * last line and then stop. - */ - display() { - if (this.displayedComplete) { - return; - } - const transferredBytes = this.segmentOffset + this.receivedBytes; - const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); - const elapsedTime = Date.now() - this.startTime; - const downloadSpeed = (transferredBytes / - (1024 * 1024) / - (elapsedTime / 1000)).toFixed(1); - core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); - if (this.isDone()) { - this.displayedComplete = true; - } - } - /** - * Returns a function used to handle TransferProgressEvents. - */ - onProgress() { - return (progress) => { - this.setReceivedBytes(progress.loadedBytes); - }; - } - /** - * Starts the timer that displays the stats. - * - * @param delayInMs the delay between each write - */ - startDisplayTimer(delayInMs = 1000) { - const displayCallback = () => { - this.display(); - if (!this.isDone()) { - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - }; - this.timeoutHandle = setTimeout(displayCallback, delayInMs); - } - /** - * Stops the timer that displays the stats. As this typically indicates the download - * is complete, this will display one last line, unless the last line has already - * been written. - */ - stopDisplayTimer() { - if (this.timeoutHandle) { - clearTimeout(this.timeoutHandle); - this.timeoutHandle = undefined; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num } - this.display(); - } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() } -exports.DownloadProgress = DownloadProgress; -/** - * Download the cache using the Actions toolkit http-client - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - */ -function downloadCacheHttpClient(archiveLocation, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - const writeStream = fs.createWriteStream(archivePath); - const httpClient = new http_client_1.HttpClient('actions/cache'); - const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); - // Abort download if no traffic received over the socket. - downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { - downloadResponse.message.destroy(); - core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); - }); - yield pipeResponseToStream(downloadResponse, writeStream); - // Validate download size. - const contentLengthHeader = downloadResponse.message.headers['content-length']; - if (contentLengthHeader) { - const expectedLength = parseInt(contentLengthHeader); - const actualLength = utils.getArchiveFileSizeInBytes(archivePath); - if (actualLength !== expectedLength) { - throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); - } - } - else { - core.debug('Unable to validate download, no Content-Length header'); - } - }); + +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version } -exports.downloadCacheHttpClient = downloadCacheHttpClient; -/** - * Download the cache using the Azure Storage SDK. Only call this method if the - * URL points to an Azure Storage endpoint. - * - * @param archiveLocation the URL for the cache - * @param archivePath the local path where the cache is saved - * @param options the download options with the defaults set - */ -function downloadCacheStorageSDK(archiveLocation, archivePath, options) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { - retryOptions: { - // Override the timeout used when downloading each 4 MB chunk - // The default is 2 min / MB, which is way too slow - tryTimeoutInMs: options.timeoutInMs - } - }); - const properties = yield client.getProperties(); - const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; - if (contentLength < 0) { - // We should never hit this condition, but just in case fall back to downloading the - // file as one large stream - core.debug('Unable to determine content length, downloading file with http-client...'); - yield downloadCacheHttpClient(archiveLocation, archivePath); - } - else { - // Use downloadToBuffer for faster downloads, since internally it splits the - // file into 4 MB chunks which can then be parallelized and retried independently - // - // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB - // on 64-bit systems), split the download into multiple segments - // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. - // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast - const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); - const downloadProgress = new DownloadProgress(contentLength); - const fd = fs.openSync(archivePath, 'w'); - try { - downloadProgress.startDisplayTimer(); - const controller = new abort_controller_1.AbortController(); - const abortSignal = controller.signal; - while (!downloadProgress.isDone()) { - const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; - const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); - downloadProgress.nextSegment(segmentSize); - const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { - abortSignal, - concurrency: options.downloadConcurrency, - onProgress: downloadProgress.onProgress() - })); - if (result === 'timeout') { - controller.abort(); - throw new Error('Aborting cache download as the download time exceeded the timeout.'); - } - else if (Buffer.isBuffer(result)) { - fs.writeFileSync(fd, result); - } - } - } - finally { - downloadProgress.stopDisplayTimer(); - fs.closeSync(fd); - } - } - }); + +SemVer.prototype.toString = function () { + return this.version } -exports.downloadCacheStorageSDK = downloadCacheStorageSDK; -const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { - let timeoutHandle; - const timeoutPromise = new Promise(resolve => { - timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); - }); - return Promise.race([promise, timeoutPromise]).then(result => { - clearTimeout(timeoutHandle); - return result; - }); -}); -//# sourceMappingURL=downloadUtils.js.map -/***/ }), +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -/***/ 3981: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + return this.compareMain(other) || this.comparePre(other) +} -"use strict"; +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; -const core = __importStar(__nccwpck_require__(2186)); -const http_client_1 = __nccwpck_require__(6255); -const constants_1 = __nccwpck_require__(8840); -function isSuccessStatusCode(statusCode) { - if (!statusCode) { - return false; - } - return statusCode >= 200 && statusCode < 300; + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) } -exports.isSuccessStatusCode = isSuccessStatusCode; -function isServerErrorStatusCode(statusCode) { - if (!statusCode) { - return true; + +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - return statusCode >= 500; + } while (++i) } -exports.isServerErrorStatusCode = isServerErrorStatusCode; -function isRetryableStatusCode(statusCode) { - if (!statusCode) { - return false; + +SemVer.prototype.compareBuild = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + var i = 0 + do { + var a = this.build[i] + var b = other.build[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - const retryableStatusCodes = [ - http_client_1.HttpCodes.BadGateway, - http_client_1.HttpCodes.ServiceUnavailable, - http_client_1.HttpCodes.GatewayTimeout - ]; - return retryableStatusCodes.includes(statusCode); -} -exports.isRetryableStatusCode = isRetryableStatusCode; -function sleep(milliseconds) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise(resolve => setTimeout(resolve, milliseconds)); - }); -} -function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { - return __awaiter(this, void 0, void 0, function* () { - let errorMessage = ''; - let attempt = 1; - while (attempt <= maxAttempts) { - let response = undefined; - let statusCode = undefined; - let isRetryable = false; - try { - response = yield method(); - } - catch (error) { - if (onError) { - response = onError(error); - } - isRetryable = true; - errorMessage = error.message; - } - if (response) { - statusCode = getStatusCode(response); - if (!isServerErrorStatusCode(statusCode)) { - return response; - } - } - if (statusCode) { - isRetryable = isRetryableStatusCode(statusCode); - errorMessage = `Cache service responded with ${statusCode}`; - } - core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); - if (!isRetryable) { - core.debug(`${name} - Error is not retryable`); - break; - } - yield sleep(delay); - attempt++; - } - throw Error(`${name} failed: ${errorMessage}`); - }); -} -exports.retry = retry; -function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, - // If the error object contains the statusCode property, extract it and return - // an TypedResponse so it can be processed by the retry logic. - (error) => { - if (error instanceof http_client_1.HttpClientError) { - return { - statusCode: error.statusCode, - result: null, - headers: {}, - error - }; - } - else { - return undefined; - } - }); - }); -} -exports.retryTypedResponse = retryTypedResponse; -function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { - return __awaiter(this, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); - }); + } while (++i) } -exports.retryHttpClientResponse = retryHttpClientResponse; -//# sourceMappingURL=requestUtils.js.map - -/***/ }), -/***/ 6490: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.createTar = exports.extractTar = exports.listTar = void 0; -const exec_1 = __nccwpck_require__(1514); -const io = __importStar(__nccwpck_require__(7436)); -const fs_1 = __nccwpck_require__(7147); -const path = __importStar(__nccwpck_require__(1017)); -const utils = __importStar(__nccwpck_require__(1518)); -const constants_1 = __nccwpck_require__(8840); -const IS_WINDOWS = process.platform === 'win32'; -// Returns tar path and type: BSD or GNU -function getTarPath() { - return __awaiter(this, void 0, void 0, function* () { - switch (process.platform) { - case 'win32': { - const gnuTar = yield utils.getGnuTarPathOnWindows(); - const systemTar = constants_1.SystemTarPathOnWindows; - if (gnuTar) { - // Use GNUtar as default on windows - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else if ((0, fs_1.existsSync)(systemTar)) { - return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; - } - break; - } - case 'darwin': { - const gnuTar = yield io.which('gtar', false); - if (gnuTar) { - // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 - return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; - } - else { - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.BSD - }; - } - } - default: - break; - } - // Default assumption is GNU tar is present in path - return { - path: yield io.which('tar', true), - type: constants_1.ArchiveToolType.GNU - }; - }); -} -// Return arguments for tar as per tarPath, compressionMethod, method type and os -function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - const args = [`"${tarPath.path}"`]; - const cacheFileName = utils.getCacheFileName(compressionMethod); - const tarFile = 'cache.tar'; - const workingDirectory = getWorkingDirectory(); - // Speficic args for BSD tar on windows for workaround - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - // Method specific args - switch (type) { - case 'create': - args.push('--posix', '-cf', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD - ? tarFile - : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); - break; - case 'extract': - args.push('-xf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); - break; - case 'list': - args.push('-tf', BSD_TAR_ZSTD - ? tarFile - : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); - break; - } - // Platform specific args - if (tarPath.type === constants_1.ArchiveToolType.GNU) { - switch (process.platform) { - case 'win32': - args.push('--force-local'); - break; - case 'darwin': - args.push('--delay-directory-restore'); - break; - } - } - return args; - }); -} -// Returns commands to run tar and compression program -function getCommands(compressionMethod, type, archivePath = '') { - return __awaiter(this, void 0, void 0, function* () { - let args; - const tarPath = yield getTarPath(); - const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); - const compressionArgs = type !== 'create' - ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) - : yield getCompressionProgram(tarPath, compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - if (BSD_TAR_ZSTD && type !== 'create') { - args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; - } - else { - args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; - } - if (BSD_TAR_ZSTD) { - return args; - } - return [args.join(' ')]; - }); -} -function getWorkingDirectory() { - var _a; - return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); -} -// Common function for extractTar and listTar to get the compression method -function getDecompressionProgram(tarPath, compressionMethod, archivePath) { - return __awaiter(this, void 0, void 0, function* () { - // -d: Decompress. - // unzstd is equivalent to 'zstd -d' - // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. - // Using 30 here because we also support 32-bit self-hosted runners. - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --long=30 --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -d --force -o', - constants_1.TarFilename, - archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; - default: - return ['-z']; + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } } - }); -} -// Used for creating the archive -// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. -// zstdmt is equivalent to 'zstd -T0' -// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. -// Using 30 here because we also support 32-bit self-hosted runners. -// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. -function getCompressionProgram(tarPath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const cacheFileName = utils.getCacheFileName(compressionMethod); - const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && - compressionMethod !== constants_1.CompressionMethod.Gzip && - IS_WINDOWS; - switch (compressionMethod) { - case constants_1.CompressionMethod.Zstd: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --long=30 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : [ - '--use-compress-program', - IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' - ]; - case constants_1.CompressionMethod.ZstdWithoutLong: - return BSD_TAR_ZSTD - ? [ - 'zstd -T0 --force -o', - cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), - constants_1.TarFilename - ] - : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; - default: - return ['-z']; + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) } - }); -} -// Executes all commands as separate processes -function execCommands(commands, cwd) { - return __awaiter(this, void 0, void 0, function* () { - for (const command of commands) { - try { - yield (0, exec_1.exec)(command, undefined, { - cwd, - env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) - }); - } - catch (error) { - throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); - } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] } - }); -} -// List the contents of a tar -function listTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - const commands = yield getCommands(compressionMethod, 'list', archivePath); - yield execCommands(commands); - }); -} -exports.listTar = listTar; -// Extract a tar -function extractTar(archivePath, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Create directory to extract tar into - const workingDirectory = getWorkingDirectory(); - yield io.mkdirP(workingDirectory); - const commands = yield getCommands(compressionMethod, 'extract', archivePath); - yield execCommands(commands); - }); -} -exports.extractTar = extractTar; -// Create a tar -function createTar(archiveFolder, sourceDirectories, compressionMethod) { - return __awaiter(this, void 0, void 0, function* () { - // Write source directories to manifest.txt to avoid command length limits - (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); - const commands = yield getCommands(compressionMethod, 'create'); - yield execCommands(commands, archiveFolder); - }); -} -exports.createTar = createTar; -//# sourceMappingURL=tar.js.map + } + break -/***/ }), + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} -/***/ 6215: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } -"use strict"; + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getDownloadOptions = exports.getUploadOptions = void 0; -const core = __importStar(__nccwpck_require__(2186)); -/** - * Returns a copy of the upload options with defaults filled in. - * - * @param copy the original upload options - */ -function getUploadOptions(copy) { - const result = { - uploadConcurrency: 4, - uploadChunkSize: 32 * 1024 * 1024 - }; - if (copy) { - if (typeof copy.uploadConcurrency === 'number') { - result.uploadConcurrency = copy.uploadConcurrency; - } - if (typeof copy.uploadChunkSize === 'number') { - result.uploadChunkSize = copy.uploadChunkSize; - } +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' } - core.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core.debug(`Upload chunk size: ${result.uploadChunkSize}`); - return result; -} -exports.getUploadOptions = getUploadOptions; -/** - * Returns a copy of the download options with defaults filled in. - * - * @param copy the original download options - */ -function getDownloadOptions(copy) { - const result = { - useAzureSdk: true, - downloadConcurrency: 8, - timeoutInMs: 30000, - segmentTimeoutInMs: 600000, - lookupOnly: false - }; - if (copy) { - if (typeof copy.useAzureSdk === 'boolean') { - result.useAzureSdk = copy.useAzureSdk; - } - if (typeof copy.downloadConcurrency === 'number') { - result.downloadConcurrency = copy.downloadConcurrency; - } - if (typeof copy.timeoutInMs === 'number') { - result.timeoutInMs = copy.timeoutInMs; - } - if (typeof copy.segmentTimeoutInMs === 'number') { - result.segmentTimeoutInMs = copy.segmentTimeoutInMs; - } - if (typeof copy.lookupOnly === 'boolean') { - result.lookupOnly = copy.lookupOnly; + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key } + } } - const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; - if (segmentDownloadTimeoutMins && - !isNaN(Number(segmentDownloadTimeoutMins)) && - isFinite(Number(segmentDownloadTimeoutMins))) { - result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; - } - core.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core.debug(`Download concurrency: ${result.downloadConcurrency}`); - core.debug(`Request timeout (ms): ${result.timeoutInMs}`); - core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); - core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - core.debug(`Lookup only: ${result.lookupOnly}`); - return result; + return defaultResult // may be undefined + } } -exports.getDownloadOptions = getDownloadOptions; -//# sourceMappingURL=options.js.map - -/***/ }), -/***/ 3771: -/***/ ((module, exports) => { +exports.compareIdentifiers = compareIdentifiers -exports = module.exports = SemVer +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) + if (anum && bnum) { + a = +a + b = +b } -} else { - debug = function () {} -} - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var t = exports.tokens = {} -var R = 0 +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} -function tok (n) { - t[n] = R++ +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor } -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -tok('NUMERICIDENTIFIER') -src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' -tok('NUMERICIDENTIFIERLOOSE') -src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -tok('NONNUMERICIDENTIFIER') -src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - -// ## Main Version -// Three dot-separated numeric identifiers. +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} -tok('MAINVERSION') -src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')' +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} -tok('MAINVERSIONLOOSE') -src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. +exports.compareBuild = compareBuild +function compareBuild (a, b, loose) { + var versionA = new SemVer(a, loose) + var versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} -tok('PRERELEASEIDENTIFIER') -src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} -tok('PRERELEASEIDENTIFIERLOOSE') -src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(a, b, loose) + }) +} -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(b, a, loose) + }) +} -tok('PRERELEASE') -src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} -tok('PRERELEASELOOSE') -src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} -tok('BUILDIDENTIFIER') -src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} -tok('BUILD') -src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + - '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b -tok('FULL') -tok('FULLPLAIN') -src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + - src[t.PRERELEASE] + '?' + - src[t.BUILD] + '?' + case '': + case '=': + case '==': + return eq(a, b, loose) -src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + case '!=': + return neq(a, b, loose) -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -tok('LOOSEPLAIN') -src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + - src[t.PRERELEASELOOSE] + '?' + - src[t.BUILD] + '?' + case '>': + return gt(a, b, loose) -tok('LOOSE') -src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + case '>=': + return gte(a, b, loose) -tok('GTLT') -src[t.GTLT] = '((?:<|>)?=?)' + case '<': + return lt(a, b, loose) -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -tok('XRANGEIDENTIFIERLOOSE') -src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -tok('XRANGEIDENTIFIER') -src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + case '<=': + return lte(a, b, loose) -tok('XRANGEPLAIN') -src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:' + src[t.PRERELEASE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' + default: + throw new TypeError('Invalid operator: ' + op) + } +} -tok('XRANGEPLAINLOOSE') -src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[t.PRERELEASELOOSE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -tok('XRANGE') -src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' -tok('XRANGELOOSE') -src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -tok('COERCE') -src[t.COERCE] = '(^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' -tok('COERCERTL') -re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } -// Tilde ranges. -// Meaning is "reasonably at or greater than" -tok('LONETILDE') -src[t.LONETILDE] = '(?:~>?)' + comp = comp.trim().split(/\s+/).join(' ') + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) -tok('TILDETRIM') -src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' -re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') -var tildeTrimReplace = '$1~' + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } -tok('TILDE') -src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' -tok('TILDELOOSE') -src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' + debug('comp', this) +} -// Caret ranges. -// Meaning is "at least and backwards compatible with" -tok('LONECARET') -src[t.LONECARET] = '(?:\\^)' +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var m = comp.match(r) -tok('CARETTRIM') -src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' -re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') -var caretTrimReplace = '$1^' + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } -tok('CARET') -src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' -tok('CARETLOOSE') -src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } -// A simple gt/lt/eq thing, or just "" to indicate "any version" -tok('COMPARATORLOOSE') -src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' -tok('COMPARATOR') -src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -tok('COMPARATORTRIM') -src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + - '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' +Comparator.prototype.toString = function () { + return this.value +} -// this one has to use the /g flag -re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -tok('HYPHENRANGE') -src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAIN] + ')' + - '\\s*$' + if (this.semver === ANY || version === ANY) { + return true + } -tok('HYPHENRANGELOOSE') -src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s*$' + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } -// Star ranges basically just allow anything at all. -tok('STAR') -src[t.STAR] = '(<|>)?=?\\s*\\*' + return cmp(version, this.operator, this.semver, this.options) +} -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') } -} -exports.parse = parse -function parse (version, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, @@ -1817,985 +888,409 @@ function parse (version, options) { } } - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - var r = options.loose ? re[t.LOOSE] : re[t.FULL] - if (!r.test(version)) { - return null - } + var rangeTmp - try { - return new SemVer(version, options) - } catch (er) { - return null + if (this.operator === '') { + if (this.value === '') { + return true + } + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) } -} -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan } -exports.SemVer = SemVer - -function SemVer (version, options) { +exports.Range = Range +function Range (range, options) { if (!options || typeof options !== 'object') { options = { loose: !!options, includePrerelease: false } } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version + + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range } else { - version = version.version + return new Range(range.raw, options) } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) } - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + if (range instanceof Comparator) { + return new Range(range.value, options) } - if (!(this instanceof SemVer)) { - return new SemVer(version, options) + if (!(this instanceof Range)) { + return new Range(range, options) } - debug('SemVer', version, options) this.options = options this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease - var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) - - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } - - this.raw = version - - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. + this.raw = range + .trim() + .split(/\s+/) + .join(' ') - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } + // First, split based on boolean or || + this.set = this.raw.split('||').map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + this.raw) } - this.build = m[5] ? m[5].split('.') : [] this.format() } -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range } -SemVer.prototype.toString = function () { - return this.version +Range.prototype.toString = function () { + return this.range } -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? safeRe[t.HYPHENRANGELOOSE] : safeRe[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(safeRe[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, safeRe[t.COMPARATORTRIM]) - return this.compareMain(other) || this.comparePre(other) -} + // `~ 1.2.3` => `~1.2.3` + range = range.replace(safeRe[t.TILDETRIM], tildeTrimReplace) -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // `^ 1.2.3` => `^1.2.3` + range = range.replace(safeRe[t.CARETTRIM], caretTrimReplace) - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} + // normalize spaces + range = range.split(/\s+/).join(' ') -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + // At this point, the range is completely trimmed and + // ready to be split into comparators. - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 + var compRe = loose ? safeRe[t.COMPARATORLOOSE] : safeRe[t.COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return set } -SemVer.prototype.compareBuild = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') } - var i = 0 - do { - var a = this.build[i] - var b = other.build[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) + return this.set.some(function (thisComparators) { + return ( + isSatisfiable(thisComparators, options) && + range.set.some(function (rangeComparators) { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every(function (thisComparator) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) } -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break - - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} +// take a set of comparators and determine whether there +// exists a version which can satisfy it +function isSatisfiable (comparators, options) { + var result = true + var remainingComparators = comparators.slice() + var testComparator = remainingComparators.pop() -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } + while (result && remainingComparators.length) { + result = remainingComparators.every(function (otherComparator) { + return testComparator.intersects(otherComparator, options) + }) - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null + testComparator = remainingComparators.pop() } -} -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } + return result } -exports.compareIdentifiers = compareIdentifiers - -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) - - if (anum && bnum) { - a = +a - b = +b - } - - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) } -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp } -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' } -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') } -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} +function replaceTilde (comp, options) { + var r = options.loose ? safeRe[t.TILDELOOSE] : safeRe[t.TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) + debug('tilde return', ret) + return ret + }) } -exports.compareBuild = compareBuild -function compareBuild (a, b, loose) { - var versionA = new SemVer(a, loose) - var versionB = new SemVer(b, loose) - return versionA.compare(versionB) || versionA.compareBuild(versionB) +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') } -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? safeRe[t.CARETLOOSE] : safeRe[t.CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(a, b, loose) - }) -} + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(b, a, loose) + debug('caret return', ret) + return ret }) } -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') } -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? safeRe[t.XRANGELOOSE] : safeRe[t.XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} + if (gtlt === '=' && anyX) { + gtlt = '' + } -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b + ret = gtlt + M + '.' + m + '.' + p + pr + } else if (xm) { + ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr + } else if (xp) { + ret = '>=' + M + '.' + m + '.0' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + pr + } - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b + debug('xRange return', ret) - case '': - case '=': - case '==': - return eq(a, b, loose) + return ret + }) +} - case '!=': - return neq(a, b, loose) - - case '>': - return gt(a, b, loose) - - case '>=': - return gte(a, b, loose) - - case '<': - return lt(a, b, loose) - - case '<=': - return lte(a, b, loose) - - default: - throw new TypeError('Invalid operator: ' + op) - } -} - -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } - - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } - - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } - - debug('comp', this) -} - -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var m = comp.match(r) - - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } - - this.operator = m[1] !== undefined ? m[1] : '' - if (this.operator === '=') { - this.operator = '' - } - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} - -Comparator.prototype.toString = function () { - return this.value -} - -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) - - if (this.semver === ANY || version === ANY) { - return true - } - - if (typeof version === 'string') { - try { - version = new SemVer(version, this.options) - } catch (er) { - return false - } - } - - return cmp(version, this.operator, this.semver, this.options) -} - -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } - - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - var rangeTmp - - if (this.operator === '') { - if (this.value === '') { - return true - } - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - if (comp.value === '') { - return true - } - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} - -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease - - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } - - this.format() -} - -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} - -Range.prototype.toString = function () { - return this.range -} - -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[t.COMPARATORTRIM]) - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[t.CARETTRIM], caretTrimReplace) - - // normalize spaces - range = range.split(/\s+/).join(' ') - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) - - return set -} - -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } - - return this.set.some(function (thisComparators) { - return ( - isSatisfiable(thisComparators, options) && - range.set.some(function (rangeComparators) { - return ( - isSatisfiable(rangeComparators, options) && - thisComparators.every(function (thisComparator) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - ) - }) - ) - }) -} - -// take a set of comparators and determine whether there -// exists a version which can satisfy it -function isSatisfiable (comparators, options) { - var result = true - var remainingComparators = comparators.slice() - var testComparator = remainingComparators.pop() - - while (result && remainingComparators.length) { - result = remainingComparators.every(function (otherComparator) { - return testComparator.intersects(otherComparator, options) - }) - - testComparator = remainingComparators.pop() - } - - return result -} - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} - -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} - -function replaceTilde (comp, options) { - var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - - debug('tilde return', ret) - return ret - }) -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} - -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } - - debug('caret return', ret) - return ret - }) -} - -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} - -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp - - if (gtlt === '=' && anyX) { - gtlt = '' - } - - // if we're including prereleases in the match, then we need - // to fix this to -0, the lowest possible prerelease value - pr = options.includePrerelease ? '-0' : '' - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0-0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } - - ret = gtlt + M + '.' + m + '.' + p + pr - } else if (xm) { - ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr - } else if (xp) { - ret = '>=' + M + '.' + m + '.0' + pr + - ' <' + M + '.' + (+m + 1) + '.0' + pr - } - - debug('xRange return', ret) - - return ret - }) -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') -} +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(safeRe[t.STAR], '') +} // This function is passed to string.replace(re[t.HYPHENRANGE]) // M, m, patch, prerelease, build @@ -3120,7 +1615,7 @@ function coerce (version, options) { var match = null if (!options.rtl) { - match = version.match(re[t.COERCE]) + match = version.match(safeRe[t.COERCE]) } else { // Find the right-most coercible string that does not share // a terminus with a more left-ward coercible string. @@ -3131,17 +1626,17 @@ function coerce (version, options) { // Stop when we get a match that ends at the string end, since no // coercible string can be more right-ward without the same terminus. var next - while ((next = re[t.COERCERTL].exec(version)) && + while ((next = safeRe[t.COERCERTL].exec(version)) && (!match || match.index + match[0].length !== version.length) ) { if (!match || next.index + next[0].length !== match.index + match[0].length) { match = next } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + safeRe[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length } // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 + safeRe[t.COERCERTL].lastIndex = -1 } if (match === null) { @@ -7026,6 +5521,19 @@ class HttpClientResponse { })); }); } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } } exports.HttpClientResponse = HttpClientResponse; function isHttps(requestUrl) { @@ -7530,7 +6038,13 @@ function getProxyUrl(reqUrl) { } })(); if (proxyVar) { - return new URL(proxyVar); + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } } else { return undefined; @@ -7541,6 +6055,10 @@ function checkBypass(reqUrl) { if (!reqUrl.hostname) { return false; } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; if (!noProxy) { return false; @@ -7566,13 +6084,24 @@ function checkBypass(reqUrl) { .split(',') .map(x => x.trim().toUpperCase()) .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { + if (upperNoProxyItem === '*' || + upperReqHosts.some(x => x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)))) { return true; } } return false; } exports.checkBypass = checkBypass; +function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return (hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]')); +} //# sourceMappingURL=proxy.js.map /***/ }), @@ -48965,7 +47494,7 @@ function state(list, sortMethod) /***/ }), -/***/ 7942: +/***/ 834: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { var abort = __nccwpck_require__(1700) @@ -49006,7 +47535,7 @@ function terminator(callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -49080,7 +47609,7 @@ function serial(list, iterator, callback) var iterate = __nccwpck_require__(9023) , initState = __nccwpck_require__(2474) - , terminator = __nccwpck_require__(7942) + , terminator = __nccwpck_require__(834) ; // Public API @@ -62359,74 +60888,1750 @@ function wrappy (fn, cb) { /***/ }), -/***/ 2958: -/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { - -// Generated by CoffeeScript 1.12.7 -(function() { - var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; - - ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; - - XMLDOMImplementation = __nccwpck_require__(8310); - - XMLDocument = __nccwpck_require__(3730); - - XMLDocumentCB = __nccwpck_require__(7356); - - XMLStringWriter = __nccwpck_require__(5913); - - XMLStreamWriter = __nccwpck_require__(8601); - - NodeType = __nccwpck_require__(9267); +/***/ 2958: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +// Generated by CoffeeScript 1.12.7 +(function() { + var NodeType, WriterState, XMLDOMImplementation, XMLDocument, XMLDocumentCB, XMLStreamWriter, XMLStringWriter, assign, isFunction, ref; + + ref = __nccwpck_require__(8229), assign = ref.assign, isFunction = ref.isFunction; + + XMLDOMImplementation = __nccwpck_require__(8310); + + XMLDocument = __nccwpck_require__(3730); + + XMLDocumentCB = __nccwpck_require__(7356); + + XMLStringWriter = __nccwpck_require__(5913); + + XMLStreamWriter = __nccwpck_require__(8601); + + NodeType = __nccwpck_require__(9267); + + WriterState = __nccwpck_require__(9766); + + module.exports.create = function(name, xmldec, doctype, options) { + var doc, root; + if (name == null) { + throw new Error("Root element needs a name."); + } + options = assign({}, xmldec, doctype, options); + doc = new XMLDocument(options); + root = doc.element(name); + if (!options.headless) { + doc.declaration(options); + if ((options.pubID != null) || (options.sysID != null)) { + doc.dtd(options); + } + } + return root; + }; + + module.exports.begin = function(options, onData, onEnd) { + var ref1; + if (isFunction(options)) { + ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; + options = {}; + } + if (onData) { + return new XMLDocumentCB(options, onData, onEnd); + } else { + return new XMLDocument(options); + } + }; + + module.exports.stringWriter = function(options) { + return new XMLStringWriter(options); + }; + + module.exports.streamWriter = function(stream, options) { + return new XMLStreamWriter(stream, options); + }; + + module.exports.implementation = new XMLDOMImplementation(); + + module.exports.nodeType = NodeType; + + module.exports.writerState = WriterState; + +}).call(this); + + +/***/ }), + +/***/ 7942: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.restoreCache = exports.isFeatureAvailable = exports.ReserveCacheError = exports.ValidationError = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const cacheHttpClient = __importStar(__nccwpck_require__(7417)); +const tar_1 = __nccwpck_require__(7672); +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = 'ValidationError'; + Object.setPrototypeOf(this, ValidationError.prototype); + } +} +exports.ValidationError = ValidationError; +class ReserveCacheError extends Error { + constructor(message) { + super(message); + this.name = 'ReserveCacheError'; + Object.setPrototypeOf(this, ReserveCacheError.prototype); + } +} +exports.ReserveCacheError = ReserveCacheError; +function checkPaths(paths) { + if (!paths || paths.length === 0) { + throw new ValidationError(`Path Validation Error: At least one directory or file path is required`); + } +} +function checkKey(key) { + if (key.length > 512) { + throw new ValidationError(`Key Validation Error: ${key} cannot be larger than 512 characters.`); + } + const regex = /^[^,]*$/; + if (!regex.test(key)) { + throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`); + } +} +/** + * isFeatureAvailable to check the presence of Actions cache service + * + * @returns boolean return true if Actions cache service feature is available, otherwise false + */ +function isFeatureAvailable() { + return !!process.env['ACTIONS_CACHE_URL']; +} +exports.isFeatureAvailable = isFeatureAvailable; +/** + * Restores cache from keys + * + * @param paths a list of file paths to restore from the cache + * @param primaryKey an explicit key for restoring the cache + * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @param downloadOptions cache download options + * @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform + * @returns string returns the key for the cache hit, otherwise returns undefined + */ +function restoreCache(paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + restoreKeys = restoreKeys || []; + const keys = [primaryKey, ...restoreKeys]; + core.debug('Resolved Keys:'); + core.debug(JSON.stringify(keys)); + if (keys.length > 10) { + throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); + } + for (const key of keys) { + checkKey(key); + } + const compressionMethod = yield utils.getCompressionMethod(); + let archivePath = ''; + try { + // path are needed to compute version + const cacheEntry = yield cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod, + enableCrossOsArchive + }); + if (!(cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.archiveLocation)) { + // Cache not found + return undefined; + } + if (options === null || options === void 0 ? void 0 : options.lookupOnly) { + core.info('Lookup only - skipping download'); + return cacheEntry.cacheKey; + } + archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + // Download the cache from the cache entry + yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + yield (0, tar_1.extractTar)(archivePath, compressionMethod, extraTarArgs); + core.info('Cache restored successfully'); + return cacheEntry.cacheKey; + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else { + // Supress all non-validation cache related errors because caching should be optional + core.warning(`Failed to restore: ${error.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return undefined; + }); +} +exports.restoreCache = restoreCache; +/** + * Saves a list of files with the specified key + * + * @param paths a list of file paths to be cached + * @param key an explicit key for restoring the cache + * @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform + * @param options cache upload options + * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails + */ +function saveCache(paths, key, options, enableCrossOsArchive = false) { + var _a, _b, _c, _d, _e; + return __awaiter(this, void 0, void 0, function* () { + checkPaths(paths); + checkKey(key); + const compressionMethod = yield utils.getCompressionMethod(); + let cacheId = -1; + const cachePaths = yield utils.resolvePaths(paths); + core.debug('Cache Paths:'); + core.debug(`${JSON.stringify(cachePaths)}`); + if (cachePaths.length === 0) { + throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); + } + const archiveFolder = yield utils.createTempDirectory(); + const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); + core.debug(`Archive Path: ${archivePath}`); + try { + yield (0, tar_1.createTar)(archiveFolder, cachePaths, compressionMethod); + if (core.isDebug()) { + yield (0, tar_1.listTar)(archivePath, compressionMethod); + } + const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit + const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); + core.debug(`File Size: ${archiveFileSize}`); + // For GHES, this check will take place in ReserveCache API with enterprise file size limit + if (archiveFileSize > fileSizeLimit && !utils.isGhes()) { + throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); + } + core.debug('Reserving Cache'); + const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { + compressionMethod, + enableCrossOsArchive, + cacheSize: archiveFileSize + }); + if ((_a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _a === void 0 ? void 0 : _a.cacheId) { + cacheId = (_b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.result) === null || _b === void 0 ? void 0 : _b.cacheId; + } + else if ((reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.statusCode) === 400) { + throw new Error((_d = (_c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : `Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`); + } + else { + throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _e === void 0 ? void 0 : _e.message}`); + } + core.debug(`Saving Cache (ID: ${cacheId})`); + yield cacheHttpClient.saveCache(cacheId, archivePath, options); + } + catch (error) { + const typedError = error; + if (typedError.name === ValidationError.name) { + throw error; + } + else if (typedError.name === ReserveCacheError.name) { + core.info(`Failed to save: ${typedError.message}`); + } + else { + core.warning(`Failed to save: ${typedError.message}`); + } + } + finally { + // Try to delete the archive to save space + try { + yield utils.unlinkFile(archivePath); + } + catch (error) { + core.debug(`Failed to delete archive: ${error}`); + } + } + return cacheId; + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 7417: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.saveCache = exports.reserveCache = exports.downloadCache = exports.getCacheEntry = exports.getCacheVersion = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const auth_1 = __nccwpck_require__(5526); +const crypto = __importStar(__nccwpck_require__(6113)); +const fs = __importStar(__nccwpck_require__(7147)); +const url_1 = __nccwpck_require__(7310); +const utils = __importStar(__nccwpck_require__(4799)); +const downloadUtils_1 = __nccwpck_require__(8432); +const options_1 = __nccwpck_require__(3128); +const requestUtils_1 = __nccwpck_require__(204); +const versionSalt = '1.0'; +function getCacheApiUrl(resource) { + const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; + if (!baseUrl) { + throw new Error('Cache Service Url not found, unable to restore cache.'); + } + const url = `${baseUrl}_apis/artifactcache/${resource}`; + core.debug(`Resource Url: ${url}`); + return url; +} +function createAcceptHeader(type, apiVersion) { + return `${type};api-version=${apiVersion}`; +} +function getRequestOptions() { + const requestOptions = { + headers: { + Accept: createAcceptHeader('application/json', '6.0-preview.1') + } + }; + return requestOptions; +} +function createHttpClient() { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; + const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); + return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); +} +function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) { + const components = paths; + // Add compression method to cache version to restore + // compressed cache as per compression method + if (compressionMethod) { + components.push(compressionMethod); + } + // Only check for windows platforms if enableCrossOsArchive is false + if (process.platform === 'win32' && !enableCrossOsArchive) { + components.push('windows-only'); + } + // Add salt to cache version to support breaking changes in cache entry + components.push(versionSalt); + return crypto.createHash('sha256').update(components.join('|')).digest('hex'); +} +exports.getCacheVersion = getCacheVersion; +function getCacheEntry(keys, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + // Cache not found + if (response.statusCode === 204) { + // List cache for primary key only if cache miss occurs + if (core.isDebug()) { + yield printCachesListForDiagnostics(keys[0], httpClient, version); + } + return null; + } + if (!(0, requestUtils_1.isSuccessStatusCode)(response.statusCode)) { + throw new Error(`Cache service responded with ${response.statusCode}`); + } + const cacheResult = response.result; + const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; + if (!cacheDownloadUrl) { + // Cache achiveLocation not found. This should never happen, and hence bail out. + throw new Error('Cache not found.'); + } + core.setSecret(cacheDownloadUrl); + core.debug(`Cache Result:`); + core.debug(JSON.stringify(cacheResult)); + return cacheResult; + }); +} +exports.getCacheEntry = getCacheEntry; +function printCachesListForDiagnostics(key, httpClient, version) { + return __awaiter(this, void 0, void 0, function* () { + const resource = `caches?key=${encodeURIComponent(key)}`; + const response = yield (0, requestUtils_1.retryTypedResponse)('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + if (response.statusCode === 200) { + const cacheListResult = response.result; + const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; + if (totalCount && totalCount > 0) { + core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); + for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { + core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + } + } + } + }); +} +function downloadCache(archiveLocation, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const archiveUrl = new url_1.URL(archiveLocation); + const downloadOptions = (0, options_1.getDownloadOptions)(options); + if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) { + if (downloadOptions.useAzureSdk) { + // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. + yield (0, downloadUtils_1.downloadCacheStorageSDK)(archiveLocation, archivePath, downloadOptions); + } + else if (downloadOptions.concurrentBlobDownloads) { + // Use concurrent implementation with HttpClient to work around blob SDK issue + yield (0, downloadUtils_1.downloadCacheHttpClientConcurrent)(archiveLocation, archivePath, downloadOptions); + } + else { + // Otherwise, download using the Actions http-client. + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + } + else { + yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveLocation, archivePath); + } + }); +} +exports.downloadCache = downloadCache; +// Reserve Cache +function reserveCache(key, paths, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const reserveCacheRequest = { + key, + version, + cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize + }; + const response = yield (0, requestUtils_1.retryTypedResponse)('reserveCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest); + })); + return response; + }); +} +exports.reserveCache = reserveCache; +function getContentRange(start, end) { + // Format: `bytes start-end/filesize + // start and end are inclusive + // filesize can be * + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/* + return `bytes ${start}-${end}/*`; +} +function uploadChunk(httpClient, resourceUrl, openStream, start, end) { + return __awaiter(this, void 0, void 0, function* () { + core.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + const additionalHeaders = { + 'Content-Type': 'application/octet-stream', + 'Content-Range': getContentRange(start, end) + }; + const uploadChunkResponse = yield (0, requestUtils_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () { + return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); + })); + if (!(0, requestUtils_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { + throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); + } + }); +} +function uploadFile(httpClient, cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + // Upload Chunks + const fileSize = utils.getArchiveFileSizeInBytes(archivePath); + const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); + const fd = fs.openSync(archivePath, 'r'); + const uploadOptions = (0, options_1.getUploadOptions)(options); + const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); + const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); + const parallelUploads = [...new Array(concurrency).keys()]; + core.debug('Awaiting all uploads'); + let offset = 0; + try { + yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, maxChunkSize); + const start = offset; + const end = offset + chunkSize - 1; + offset += maxChunkSize; + yield uploadChunk(httpClient, resourceUrl, () => fs + .createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + .on('error', error => { + throw new Error(`Cache upload failed because file read failed with ${error.message}`); + }), start, end); + } + }))); + } + finally { + fs.closeSync(fd); + } + return; + }); +} +function commitCache(httpClient, cacheId, filesize) { + return __awaiter(this, void 0, void 0, function* () { + const commitCacheRequest = { size: filesize }; + return yield (0, requestUtils_1.retryTypedResponse)('commitCache', () => __awaiter(this, void 0, void 0, function* () { + return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest); + })); + }); +} +function saveCache(cacheId, archivePath, options) { + return __awaiter(this, void 0, void 0, function* () { + const httpClient = createHttpClient(); + core.debug('Upload cache'); + yield uploadFile(httpClient, cacheId, archivePath, options); + // Commit Cache + core.debug('Commiting cache'); + const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); + core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); + if (!(0, requestUtils_1.isSuccessStatusCode)(commitCacheResponse.statusCode)) { + throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); + } + core.info('Cache saved successfully'); + }); +} +exports.saveCache = saveCache; + + +/***/ }), + +/***/ 4799: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isGhes = exports.assertDefined = exports.getGnuTarPathOnWindows = exports.getCacheFileName = exports.getCompressionMethod = exports.unlinkFile = exports.resolvePaths = exports.getArchiveFileSizeInBytes = exports.createTempDirectory = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const exec = __importStar(__nccwpck_require__(1514)); +const glob = __importStar(__nccwpck_require__(8090)); +const io = __importStar(__nccwpck_require__(7436)); +const fs = __importStar(__nccwpck_require__(7147)); +const path = __importStar(__nccwpck_require__(1017)); +const semver = __importStar(__nccwpck_require__(1898)); +const util = __importStar(__nccwpck_require__(3837)); +const uuid_1 = __nccwpck_require__(2155); +const constants_1 = __nccwpck_require__(3608); +// From https://github.com/actions/toolkit/blob/main/packages/tool-cache/src/tool-cache.ts#L23 +function createTempDirectory() { + return __awaiter(this, void 0, void 0, function* () { + const IS_WINDOWS = process.platform === 'win32'; + let tempDirectory = process.env['RUNNER_TEMP'] || ''; + if (!tempDirectory) { + let baseLocation; + if (IS_WINDOWS) { + // On Windows use the USERPROFILE env variable + baseLocation = process.env['USERPROFILE'] || 'C:\\'; + } + else { + if (process.platform === 'darwin') { + baseLocation = '/Users'; + } + else { + baseLocation = '/home'; + } + } + tempDirectory = path.join(baseLocation, 'actions', 'temp'); + } + const dest = path.join(tempDirectory, (0, uuid_1.v4)()); + yield io.mkdirP(dest); + return dest; + }); +} +exports.createTempDirectory = createTempDirectory; +function getArchiveFileSizeInBytes(filePath) { + return fs.statSync(filePath).size; +} +exports.getArchiveFileSizeInBytes = getArchiveFileSizeInBytes; +function resolvePaths(patterns) { + var _a, e_1, _b, _c; + var _d; + return __awaiter(this, void 0, void 0, function* () { + const paths = []; + const workspace = (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); + const globber = yield glob.create(patterns.join('\n'), { + implicitDescendants: false + }); + try { + for (var _e = true, _f = __asyncValues(globber.globGenerator()), _g; _g = yield _f.next(), _a = _g.done, !_a;) { + _c = _g.value; + _e = false; + try { + const file = _c; + const relativeFile = path + .relative(workspace, file) + .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); + core.debug(`Matched: ${relativeFile}`); + // Paths are made relative so the tar entries are all relative to the root of the workspace. + if (relativeFile === '') { + // path.relative returns empty string if workspace and file are equal + paths.push('.'); + } + else { + paths.push(`${relativeFile}`); + } + } + finally { + _e = true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_e && !_a && (_b = _f.return)) yield _b.call(_f); + } + finally { if (e_1) throw e_1.error; } + } + return paths; + }); +} +exports.resolvePaths = resolvePaths; +function unlinkFile(filePath) { + return __awaiter(this, void 0, void 0, function* () { + return util.promisify(fs.unlink)(filePath); + }); +} +exports.unlinkFile = unlinkFile; +function getVersion(app, additionalArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let versionOutput = ''; + additionalArgs.push('--version'); + core.debug(`Checking ${app} ${additionalArgs.join(' ')}`); + try { + yield exec.exec(`${app}`, additionalArgs, { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) + } + }); + } + catch (err) { + core.debug(err.message); + } + versionOutput = versionOutput.trim(); + core.debug(versionOutput); + return versionOutput; + }); +} +// Use zstandard if possible to maximize cache performance +function getCompressionMethod() { + return __awaiter(this, void 0, void 0, function* () { + const versionOutput = yield getVersion('zstd', ['--quiet']); + const version = semver.clean(versionOutput); + core.debug(`zstd version: ${version}`); + if (versionOutput === '') { + return constants_1.CompressionMethod.Gzip; + } + else { + return constants_1.CompressionMethod.ZstdWithoutLong; + } + }); +} +exports.getCompressionMethod = getCompressionMethod; +function getCacheFileName(compressionMethod) { + return compressionMethod === constants_1.CompressionMethod.Gzip + ? constants_1.CacheFilename.Gzip + : constants_1.CacheFilename.Zstd; +} +exports.getCacheFileName = getCacheFileName; +function getGnuTarPathOnWindows() { + return __awaiter(this, void 0, void 0, function* () { + if (fs.existsSync(constants_1.GnuTarPathOnWindows)) { + return constants_1.GnuTarPathOnWindows; + } + const versionOutput = yield getVersion('tar'); + return versionOutput.toLowerCase().includes('gnu tar') ? io.which('tar') : ''; + }); +} +exports.getGnuTarPathOnWindows = getGnuTarPathOnWindows; +function assertDefined(name, value) { + if (value === undefined) { + throw Error(`Expected ${name} but value was undefiend`); + } + return value; +} +exports.assertDefined = assertDefined; +function isGhes() { + const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; +} +exports.isGhes = isGhes; + + +/***/ }), + +/***/ 3608: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.ManifestFilename = exports.TarFilename = exports.SystemTarPathOnWindows = exports.GnuTarPathOnWindows = exports.SocketTimeout = exports.DefaultRetryDelay = exports.DefaultRetryAttempts = exports.ArchiveToolType = exports.CompressionMethod = exports.CacheFilename = void 0; +var CacheFilename; +(function (CacheFilename) { + CacheFilename["Gzip"] = "cache.tgz"; + CacheFilename["Zstd"] = "cache.tzst"; +})(CacheFilename = exports.CacheFilename || (exports.CacheFilename = {})); +var CompressionMethod; +(function (CompressionMethod) { + CompressionMethod["Gzip"] = "gzip"; + // Long range mode was added to zstd in v1.3.2. + // This enum is for earlier version of zstd that does not have --long support + CompressionMethod["ZstdWithoutLong"] = "zstd-without-long"; + CompressionMethod["Zstd"] = "zstd"; +})(CompressionMethod = exports.CompressionMethod || (exports.CompressionMethod = {})); +var ArchiveToolType; +(function (ArchiveToolType) { + ArchiveToolType["GNU"] = "gnu"; + ArchiveToolType["BSD"] = "bsd"; +})(ArchiveToolType = exports.ArchiveToolType || (exports.ArchiveToolType = {})); +// The default number of retry attempts. +exports.DefaultRetryAttempts = 2; +// The default delay in milliseconds between retry attempts. +exports.DefaultRetryDelay = 5000; +// Socket timeout in milliseconds during download. If no traffic is received +// over the socket during this period, the socket is destroyed and the download +// is aborted. +exports.SocketTimeout = 5000; +// The default path of GNUtar on hosted Windows runners +exports.GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar.exe`; +// The default path of BSDtar on hosted Windows runners +exports.SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; +exports.TarFilename = 'cache.tar'; +exports.ManifestFilename = 'manifest.txt'; + + +/***/ }), + +/***/ 8432: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.downloadCacheStorageSDK = exports.downloadCacheHttpClientConcurrent = exports.downloadCacheHttpClient = exports.DownloadProgress = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const storage_blob_1 = __nccwpck_require__(4100); +const buffer = __importStar(__nccwpck_require__(4300)); +const fs = __importStar(__nccwpck_require__(7147)); +const stream = __importStar(__nccwpck_require__(2781)); +const util = __importStar(__nccwpck_require__(3837)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const requestUtils_1 = __nccwpck_require__(204); +const abort_controller_1 = __nccwpck_require__(2557); +/** + * Pipes the body of a HTTP response to a stream + * + * @param response the HTTP response + * @param output the writable stream + */ +function pipeResponseToStream(response, output) { + return __awaiter(this, void 0, void 0, function* () { + const pipeline = util.promisify(stream.pipeline); + yield pipeline(response.message, output); + }); +} +/** + * Class for tracking the download state and displaying stats. + */ +class DownloadProgress { + constructor(contentLength) { + this.contentLength = contentLength; + this.segmentIndex = 0; + this.segmentSize = 0; + this.segmentOffset = 0; + this.receivedBytes = 0; + this.displayedComplete = false; + this.startTime = Date.now(); + } + /** + * Progress to the next segment. Only call this method when the previous segment + * is complete. + * + * @param segmentSize the length of the next segment + */ + nextSegment(segmentSize) { + this.segmentOffset = this.segmentOffset + this.segmentSize; + this.segmentIndex = this.segmentIndex + 1; + this.segmentSize = segmentSize; + this.receivedBytes = 0; + core.debug(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + } + /** + * Sets the number of bytes received for the current segment. + * + * @param receivedBytes the number of bytes received + */ + setReceivedBytes(receivedBytes) { + this.receivedBytes = receivedBytes; + } + /** + * Returns the total number of bytes transferred. + */ + getTransferredBytes() { + return this.segmentOffset + this.receivedBytes; + } + /** + * Returns true if the download is complete. + */ + isDone() { + return this.getTransferredBytes() === this.contentLength; + } + /** + * Prints the current download stats. Once the download completes, this will print one + * last line and then stop. + */ + display() { + if (this.displayedComplete) { + return; + } + const transferredBytes = this.segmentOffset + this.receivedBytes; + const percentage = (100 * (transferredBytes / this.contentLength)).toFixed(1); + const elapsedTime = Date.now() - this.startTime; + const downloadSpeed = (transferredBytes / + (1024 * 1024) / + (elapsedTime / 1000)).toFixed(1); + core.info(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + if (this.isDone()) { + this.displayedComplete = true; + } + } + /** + * Returns a function used to handle TransferProgressEvents. + */ + onProgress() { + return (progress) => { + this.setReceivedBytes(progress.loadedBytes); + }; + } + /** + * Starts the timer that displays the stats. + * + * @param delayInMs the delay between each write + */ + startDisplayTimer(delayInMs = 1000) { + const displayCallback = () => { + this.display(); + if (!this.isDone()) { + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + }; + this.timeoutHandle = setTimeout(displayCallback, delayInMs); + } + /** + * Stops the timer that displays the stats. As this typically indicates the download + * is complete, this will display one last line, unless the last line has already + * been written. + */ + stopDisplayTimer() { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle); + this.timeoutHandle = undefined; + } + this.display(); + } +} +exports.DownloadProgress = DownloadProgress; +/** + * Download the cache using the Actions toolkit http-client + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClient(archiveLocation, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + const writeStream = fs.createWriteStream(archivePath); + const httpClient = new http_client_1.HttpClient('actions/cache'); + const downloadResponse = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { + downloadResponse.message.destroy(); + core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); + }); + yield pipeResponseToStream(downloadResponse, writeStream); + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length']; + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader); + const actualLength = utils.getArchiveFileSizeInBytes(archivePath); + if (actualLength !== expectedLength) { + throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); + } + } + else { + core.debug('Unable to validate download, no Content-Length header'); + } + }); +} +exports.downloadCacheHttpClient = downloadCacheHttpClient; +/** + * Download the cache using the Actions toolkit http-client concurrently + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const archiveDescriptor = yield fs.promises.open(archivePath, 'w'); + const httpClient = new http_client_1.HttpClient('actions/cache', undefined, { + socketTimeout: options.timeoutInMs, + keepAlive: true + }); + try { + const res = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCacheMetadata', () => __awaiter(this, void 0, void 0, function* () { return yield httpClient.request('HEAD', archiveLocation, null, {}); })); + const lengthHeader = res.message.headers['content-length']; + if (lengthHeader === undefined || lengthHeader === null) { + throw new Error('Content-Length not found on blob response'); + } + const length = parseInt(lengthHeader); + if (Number.isNaN(length)) { + throw new Error(`Could not interpret Content-Length: ${length}`); + } + const downloads = []; + const blockSize = 4 * 1024 * 1024; + for (let offset = 0; offset < length; offset += blockSize) { + const count = Math.min(blockSize, length - offset); + downloads.push({ + offset, + promiseGetter: () => __awaiter(this, void 0, void 0, function* () { + return yield downloadSegmentRetry(httpClient, archiveLocation, offset, count); + }) + }); + } + // reverse to use .pop instead of .shift + downloads.reverse(); + let actives = 0; + let bytesDownloaded = 0; + const progress = new DownloadProgress(length); + progress.startDisplayTimer(); + const progressFn = progress.onProgress(); + const activeDownloads = []; + let nextDownload; + const waitAndWrite = () => __awaiter(this, void 0, void 0, function* () { + const segment = yield Promise.race(Object.values(activeDownloads)); + yield archiveDescriptor.write(segment.buffer, 0, segment.count, segment.offset); + actives--; + delete activeDownloads[segment.offset]; + bytesDownloaded += segment.count; + progressFn({ loadedBytes: bytesDownloaded }); + }); + while ((nextDownload = downloads.pop())) { + activeDownloads[nextDownload.offset] = nextDownload.promiseGetter(); + actives++; + if (actives >= ((_a = options.downloadConcurrency) !== null && _a !== void 0 ? _a : 10)) { + yield waitAndWrite(); + } + } + while (actives > 0) { + yield waitAndWrite(); + } + } + finally { + httpClient.dispose(); + yield archiveDescriptor.close(); + } + }); +} +exports.downloadCacheHttpClientConcurrent = downloadCacheHttpClientConcurrent; +function downloadSegmentRetry(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const retries = 5; + let failures = 0; + while (true) { + try { + const timeout = 30000; + const result = yield promiseWithTimeout(timeout, downloadSegment(httpClient, archiveLocation, offset, count)); + if (typeof result === 'string') { + throw new Error('downloadSegmentRetry failed due to timeout'); + } + return result; + } + catch (err) { + if (failures >= retries) { + throw err; + } + failures++; + } + } + }); +} +function downloadSegment(httpClient, archiveLocation, offset, count) { + return __awaiter(this, void 0, void 0, function* () { + const partRes = yield (0, requestUtils_1.retryHttpClientResponse)('downloadCachePart', () => __awaiter(this, void 0, void 0, function* () { + return yield httpClient.get(archiveLocation, { + Range: `bytes=${offset}-${offset + count - 1}` + }); + })); + if (!partRes.readBodyBuffer) { + throw new Error('Expected HttpClientResponse to implement readBodyBuffer'); + } + return { + offset, + count, + buffer: yield partRes.readBodyBuffer() + }; + }); +} +/** + * Download the cache using the Azure Storage SDK. Only call this method if the + * URL points to an Azure Storage endpoint. + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + * @param options the download options with the defaults set + */ +function downloadCacheStorageSDK(archiveLocation, archivePath, options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const client = new storage_blob_1.BlockBlobClient(archiveLocation, undefined, { + retryOptions: { + // Override the timeout used when downloading each 4 MB chunk + // The default is 2 min / MB, which is way too slow + tryTimeoutInMs: options.timeoutInMs + } + }); + const properties = yield client.getProperties(); + const contentLength = (_a = properties.contentLength) !== null && _a !== void 0 ? _a : -1; + if (contentLength < 0) { + // We should never hit this condition, but just in case fall back to downloading the + // file as one large stream + core.debug('Unable to determine content length, downloading file with http-client...'); + yield downloadCacheHttpClient(archiveLocation, archivePath); + } + else { + // Use downloadToBuffer for faster downloads, since internally it splits the + // file into 4 MB chunks which can then be parallelized and retried independently + // + // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB + // on 64-bit systems), split the download into multiple segments + // ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly. + // Updated segment size to 128MB = 134217728 bytes, to complete a segment faster and fail fast + const maxSegmentSize = Math.min(134217728, buffer.constants.MAX_LENGTH); + const downloadProgress = new DownloadProgress(contentLength); + const fd = fs.openSync(archivePath, 'w'); + try { + downloadProgress.startDisplayTimer(); + const controller = new abort_controller_1.AbortController(); + const abortSignal = controller.signal; + while (!downloadProgress.isDone()) { + const segmentStart = downloadProgress.segmentOffset + downloadProgress.segmentSize; + const segmentSize = Math.min(maxSegmentSize, contentLength - segmentStart); + downloadProgress.nextSegment(segmentSize); + const result = yield promiseWithTimeout(options.segmentTimeoutInMs || 3600000, client.downloadToBuffer(segmentStart, segmentSize, { + abortSignal, + concurrency: options.downloadConcurrency, + onProgress: downloadProgress.onProgress() + })); + if (result === 'timeout') { + controller.abort(); + throw new Error('Aborting cache download as the download time exceeded the timeout.'); + } + else if (Buffer.isBuffer(result)) { + fs.writeFileSync(fd, result); + } + } + } + finally { + downloadProgress.stopDisplayTimer(); + fs.closeSync(fd); + } + } + }); +} +exports.downloadCacheStorageSDK = downloadCacheStorageSDK; +const promiseWithTimeout = (timeoutMs, promise) => __awaiter(void 0, void 0, void 0, function* () { + let timeoutHandle; + const timeoutPromise = new Promise(resolve => { + timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs); + }); + return Promise.race([promise, timeoutPromise]).then(result => { + clearTimeout(timeoutHandle); + return result; + }); +}); + + +/***/ }), + +/***/ 204: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.retryHttpClientResponse = exports.retryTypedResponse = exports.retry = exports.isRetryableStatusCode = exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; +const core = __importStar(__nccwpck_require__(2186)); +const http_client_1 = __nccwpck_require__(6255); +const constants_1 = __nccwpck_require__(3608); +function isSuccessStatusCode(statusCode) { + if (!statusCode) { + return false; + } + return statusCode >= 200 && statusCode < 300; +} +exports.isSuccessStatusCode = isSuccessStatusCode; +function isServerErrorStatusCode(statusCode) { + if (!statusCode) { + return true; + } + return statusCode >= 500; +} +exports.isServerErrorStatusCode = isServerErrorStatusCode; +function isRetryableStatusCode(statusCode) { + if (!statusCode) { + return false; + } + const retryableStatusCodes = [ + http_client_1.HttpCodes.BadGateway, + http_client_1.HttpCodes.ServiceUnavailable, + http_client_1.HttpCodes.GatewayTimeout + ]; + return retryableStatusCodes.includes(statusCode); +} +exports.isRetryableStatusCode = isRetryableStatusCode; +function sleep(milliseconds) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise(resolve => setTimeout(resolve, milliseconds)); + }); +} +function retry(name, method, getStatusCode, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay, onError = undefined) { + return __awaiter(this, void 0, void 0, function* () { + let errorMessage = ''; + let attempt = 1; + while (attempt <= maxAttempts) { + let response = undefined; + let statusCode = undefined; + let isRetryable = false; + try { + response = yield method(); + } + catch (error) { + if (onError) { + response = onError(error); + } + isRetryable = true; + errorMessage = error.message; + } + if (response) { + statusCode = getStatusCode(response); + if (!isServerErrorStatusCode(statusCode)) { + return response; + } + } + if (statusCode) { + isRetryable = isRetryableStatusCode(statusCode); + errorMessage = `Cache service responded with ${statusCode}`; + } + core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`); + break; + } + yield sleep(delay); + attempt++; + } + throw Error(`${name} failed: ${errorMessage}`); + }); +} +exports.retry = retry; +function retryTypedResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, + // If the error object contains the statusCode property, extract it and return + // an TypedResponse so it can be processed by the retry logic. + (error) => { + if (error instanceof http_client_1.HttpClientError) { + return { + statusCode: error.statusCode, + result: null, + headers: {}, + error + }; + } + else { + return undefined; + } + }); + }); +} +exports.retryTypedResponse = retryTypedResponse; +function retryHttpClientResponse(name, method, maxAttempts = constants_1.DefaultRetryAttempts, delay = constants_1.DefaultRetryDelay) { + return __awaiter(this, void 0, void 0, function* () { + return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); + }); +} +exports.retryHttpClientResponse = retryHttpClientResponse; + + +/***/ }), + +/***/ 7672: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.createTar = exports.extractTar = exports.listTar = void 0; +const exec_1 = __nccwpck_require__(1514); +const io = __importStar(__nccwpck_require__(7436)); +const fs_1 = __nccwpck_require__(7147); +const path = __importStar(__nccwpck_require__(1017)); +const utils = __importStar(__nccwpck_require__(4799)); +const constants_1 = __nccwpck_require__(3608); +const IS_WINDOWS = process.platform === 'win32'; +// Returns tar path and type: BSD or GNU +function getTarPath() { + return __awaiter(this, void 0, void 0, function* () { + switch (process.platform) { + case 'win32': { + const gnuTar = yield utils.getGnuTarPathOnWindows(); + const systemTar = constants_1.SystemTarPathOnWindows; + if (gnuTar) { + // Use GNUtar as default on windows + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else if ((0, fs_1.existsSync)(systemTar)) { + return { path: systemTar, type: constants_1.ArchiveToolType.BSD }; + } + break; + } + case 'darwin': { + const gnuTar = yield io.which('gtar', false); + if (gnuTar) { + // fix permission denied errors when extracting BSD tar archive with GNU tar - https://github.com/actions/cache/issues/527 + return { path: gnuTar, type: constants_1.ArchiveToolType.GNU }; + } + else { + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.BSD + }; + } + } + default: + break; + } + // Default assumption is GNU tar is present in path + return { + path: yield io.which('tar', true), + type: constants_1.ArchiveToolType.GNU + }; + }); +} +// Return arguments for tar as per tarPath, compressionMethod, method type and os +function getTarArgs(tarPath, compressionMethod, type, archivePath = '') { + return __awaiter(this, void 0, void 0, function* () { + const args = [`"${tarPath.path}"`]; + const cacheFileName = utils.getCacheFileName(compressionMethod); + const tarFile = 'cache.tar'; + const workingDirectory = getWorkingDirectory(); + // Speficic args for BSD tar on windows for workaround + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + // Method specific args + switch (type) { + case 'create': + args.push('--posix', '-cf', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD + ? tarFile + : cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '--files-from', constants_1.ManifestFilename); + break; + case 'extract': + args.push('-xf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/')); + break; + case 'list': + args.push('-tf', BSD_TAR_ZSTD + ? tarFile + : archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P'); + break; + } + // Platform specific args + if (tarPath.type === constants_1.ArchiveToolType.GNU) { + switch (process.platform) { + case 'win32': + args.push('--force-local'); + break; + case 'darwin': + args.push('--delay-directory-restore'); + break; + } + } + return args; + }); +} +// Returns commands to run tar and compression program +function getCommands(compressionMethod, type, archivePath = '', tarExtraArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + let args; + const tarPath = yield getTarPath(); + const tarArgs = yield getTarArgs(tarPath, compressionMethod, type, archivePath); + tarArgs.push(...tarExtraArgs); + const compressionArgs = type !== 'create' + ? yield getDecompressionProgram(tarPath, compressionMethod, archivePath) + : yield getCompressionProgram(tarPath, compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + if (BSD_TAR_ZSTD && type !== 'create') { + args = [[...compressionArgs].join(' '), [...tarArgs].join(' ')]; + } + else { + args = [[...tarArgs].join(' '), [...compressionArgs].join(' ')]; + } + if (BSD_TAR_ZSTD) { + return args; + } + return [args.join(' ')]; + }); +} +function getWorkingDirectory() { + var _a; + return (_a = process.env['GITHUB_WORKSPACE']) !== null && _a !== void 0 ? _a : process.cwd(); +} +// Common function for extractTar and listTar to get the compression method +function getDecompressionProgram(tarPath, compressionMethod, archivePath) { + return __awaiter(this, void 0, void 0, function* () { + // -d: Decompress. + // unzstd is equivalent to 'zstd -d' + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --long=30 --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -d --long=30"' : 'unzstd --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -d --force -o', + constants_1.TarFilename, + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -d"' : 'unzstd']; + default: + return ['-z']; + } + }); +} +// Used for creating the archive +// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. +// zstdmt is equivalent to 'zstd -T0' +// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. +// Using 30 here because we also support 32-bit self-hosted runners. +// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. +function getCompressionProgram(tarPath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const cacheFileName = utils.getCacheFileName(compressionMethod); + const BSD_TAR_ZSTD = tarPath.type === constants_1.ArchiveToolType.BSD && + compressionMethod !== constants_1.CompressionMethod.Gzip && + IS_WINDOWS; + switch (compressionMethod) { + case constants_1.CompressionMethod.Zstd: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --long=30 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : [ + '--use-compress-program', + IS_WINDOWS ? '"zstd -T0 --long=30"' : 'zstdmt --long=30' + ]; + case constants_1.CompressionMethod.ZstdWithoutLong: + return BSD_TAR_ZSTD + ? [ + 'zstd -T0 --force -o', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + constants_1.TarFilename + ] + : ['--use-compress-program', IS_WINDOWS ? '"zstd -T0"' : 'zstdmt']; + default: + return ['-z']; + } + }); +} +// Executes all commands as separate processes +function execCommands(commands, cwd) { + return __awaiter(this, void 0, void 0, function* () { + for (const command of commands) { + try { + yield (0, exec_1.exec)(command, undefined, { + cwd, + env: Object.assign(Object.assign({}, process.env), { MSYS: 'winsymlinks:nativestrict' }) + }); + } + catch (error) { + throw new Error(`${command.split(' ')[0]} failed with error: ${error === null || error === void 0 ? void 0 : error.message}`); + } + } + }); +} +// List the contents of a tar +function listTar(archivePath, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + const commands = yield getCommands(compressionMethod, 'list', archivePath); + yield execCommands(commands); + }); +} +exports.listTar = listTar; +// Extract a tar +function extractTar(archivePath, compressionMethod, extraTarArgs = []) { + return __awaiter(this, void 0, void 0, function* () { + // Create directory to extract tar into + const workingDirectory = getWorkingDirectory(); + yield io.mkdirP(workingDirectory); + const commands = yield getCommands(compressionMethod, 'extract', archivePath, extraTarArgs); + yield execCommands(commands); + }); +} +exports.extractTar = extractTar; +// Create a tar +function createTar(archiveFolder, sourceDirectories, compressionMethod) { + return __awaiter(this, void 0, void 0, function* () { + // Write source directories to manifest.txt to avoid command length limits + (0, fs_1.writeFileSync)(path.join(archiveFolder, constants_1.ManifestFilename), sourceDirectories.join('\n')); + const commands = yield getCommands(compressionMethod, 'create'); + yield execCommands(commands, archiveFolder); + }); +} +exports.createTar = createTar; + + +/***/ }), + +/***/ 3128: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - WriterState = __nccwpck_require__(9766); +"use strict"; - module.exports.create = function(name, xmldec, doctype, options) { - var doc, root; - if (name == null) { - throw new Error("Root element needs a name."); +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; } - options = assign({}, xmldec, doctype, options); - doc = new XMLDocument(options); - root = doc.element(name); - if (!options.headless) { - doc.declaration(options); - if ((options.pubID != null) || (options.sysID != null)) { - doc.dtd(options); - } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getDownloadOptions = exports.getUploadOptions = void 0; +const core = __importStar(__nccwpck_require__(2186)); +/** + * Returns a copy of the upload options with defaults filled in. + * + * @param copy the original upload options + */ +function getUploadOptions(copy) { + const result = { + uploadConcurrency: 4, + uploadChunkSize: 32 * 1024 * 1024 + }; + if (copy) { + if (typeof copy.uploadConcurrency === 'number') { + result.uploadConcurrency = copy.uploadConcurrency; + } + if (typeof copy.uploadChunkSize === 'number') { + result.uploadChunkSize = copy.uploadChunkSize; + } } - return root; - }; - - module.exports.begin = function(options, onData, onEnd) { - var ref1; - if (isFunction(options)) { - ref1 = [options, onData], onData = ref1[0], onEnd = ref1[1]; - options = {}; + core.debug(`Upload concurrency: ${result.uploadConcurrency}`); + core.debug(`Upload chunk size: ${result.uploadChunkSize}`); + return result; +} +exports.getUploadOptions = getUploadOptions; +/** + * Returns a copy of the download options with defaults filled in. + * + * @param copy the original download options + */ +function getDownloadOptions(copy) { + const result = { + useAzureSdk: false, + concurrentBlobDownloads: true, + downloadConcurrency: 8, + timeoutInMs: 30000, + segmentTimeoutInMs: 600000, + lookupOnly: false + }; + if (copy) { + if (typeof copy.useAzureSdk === 'boolean') { + result.useAzureSdk = copy.useAzureSdk; + } + if (typeof copy.concurrentBlobDownloads === 'boolean') { + result.concurrentBlobDownloads = copy.concurrentBlobDownloads; + } + if (typeof copy.downloadConcurrency === 'number') { + result.downloadConcurrency = copy.downloadConcurrency; + } + if (typeof copy.timeoutInMs === 'number') { + result.timeoutInMs = copy.timeoutInMs; + } + if (typeof copy.segmentTimeoutInMs === 'number') { + result.segmentTimeoutInMs = copy.segmentTimeoutInMs; + } + if (typeof copy.lookupOnly === 'boolean') { + result.lookupOnly = copy.lookupOnly; + } } - if (onData) { - return new XMLDocumentCB(options, onData, onEnd); - } else { - return new XMLDocument(options); + const segmentDownloadTimeoutMins = process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']; + if (segmentDownloadTimeoutMins && + !isNaN(Number(segmentDownloadTimeoutMins)) && + isFinite(Number(segmentDownloadTimeoutMins))) { + result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; } - }; - - module.exports.stringWriter = function(options) { - return new XMLStringWriter(options); - }; - - module.exports.streamWriter = function(stream, options) { - return new XMLStreamWriter(stream, options); - }; - - module.exports.implementation = new XMLDOMImplementation(); - - module.exports.nodeType = NodeType; - - module.exports.writerState = WriterState; - -}).call(this); + core.debug(`Use Azure SDK: ${result.useAzureSdk}`); + core.debug(`Download concurrency: ${result.downloadConcurrency}`); + core.debug(`Request timeout (ms): ${result.timeoutInMs}`); + core.debug(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); + core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core.debug(`Lookup only: ${result.lookupOnly}`); + return result; +} +exports.getDownloadOptions = getDownloadOptions; /***/ }), @@ -62440,35 +62645,40 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.RefKey = exports.Events = exports.State = exports.Outputs = exports.Inputs = void 0; var Inputs; (function (Inputs) { - Inputs["Key"] = "key"; - Inputs["Path"] = "path"; - Inputs["RestoreKeys"] = "restore-keys"; - Inputs["UploadChunkSize"] = "upload-chunk-size"; - Inputs["FailOnCacheMiss"] = "fail-on-cache-miss"; - Inputs["RestoreKeyHit"] = "restore-key-hit"; - Inputs["ExtraRestoreKeys"] = "extra-restore-keys"; - Inputs["GCMacos"] = "gc-macos"; + Inputs["PrimaryKey"] = "primary-key"; + Inputs["RestorePrefixesFirstMatch"] = "restore-prefixes-first-match"; + Inputs["RestorePrefixesAllMatches"] = "restore-prefixes-all-matches"; + Inputs["SkipRestoreOnHitPrimaryKey"] = "skip-restore-on-primary-key-hit"; + Inputs["FailOn"] = "fail-on"; + Inputs["Nix"] = "nix"; + Inputs["Save"] = "save"; + Inputs["Paths"] = "paths"; + Inputs["PathsMacos"] = "paths-macos"; + Inputs["PathsLinux"] = "paths-linux"; + Inputs["GCMaxStoreSize"] = "gc-max-store-size"; Inputs["GCMaxStoreSizeMacos"] = "gc-max-store-size-macos"; - Inputs["GCLinux"] = "gc-linux"; Inputs["GCMaxStoreSizeLinux"] = "gc-max-store-size-linux"; - Inputs["Token"] = "token"; Inputs["Purge"] = "purge"; - Inputs["PurgeKeys"] = "purge-keys"; - Inputs["PurgeAccessed"] = "purge-accessed"; - Inputs["PurgeAccessedMaxAge"] = "purge-accessed-max-age"; + Inputs["PurgeOverwrite"] = "purge-overwrite"; + Inputs["PurgePrefixes"] = "purge-prefixes"; + Inputs["PurgeLastAccessed"] = "purge-last-accessed"; Inputs["PurgeCreated"] = "purge-created"; - Inputs["PurgeCreatedMaxAge"] = "purge-created-max-age"; // Input for cache, save action + Inputs["UploadChunkSize"] = "upload-chunk-size"; + Inputs["Token"] = "token"; // Input for cache, save actions })(Inputs = exports.Inputs || (exports.Inputs = {})); var Outputs; (function (Outputs) { - Outputs["CacheHit"] = "cache-hit"; - Outputs["CachePrimaryKey"] = "cache-primary-key"; - Outputs["CacheMatchedKey"] = "cache-matched-key"; // Output from restore action + Outputs["PrimaryKey"] = "primary-key"; + Outputs["Hit"] = "hit"; + Outputs["HitPrimaryKey"] = "hit-primary-key"; + Outputs["HitFirstMatch"] = "hit-first-match"; + Outputs["RestoredKey"] = "restored-key"; + Outputs["RestoredKeys"] = "restored-keys"; // Output from cache, restore actions })(Outputs = exports.Outputs || (exports.Outputs = {})); var State; (function (State) { - State["CachePrimaryKey"] = "CACHE_KEY"; - State["CacheMatchedKey"] = "CACHE_RESULT"; + State["CachePrimaryKey"] = "CACHE_PRIMARY_KEY"; + State["CacheRestoredKey"] = "CACHE_RESTORED_KEY"; })(State = exports.State || (exports.State = {})); var Events; (function (Events) { @@ -62481,82 +62691,7 @@ exports.RefKey = "GITHUB_REF"; /***/ }), -/***/ 2193: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.collectGarbage = void 0; -const exec_1 = __nccwpck_require__(1514); -const constants_1 = __nccwpck_require__(9042); -const utils = __importStar(__nccwpck_require__(6850)); -function collectGarbage() { - return __awaiter(this, void 0, void 0, function* () { - utils.info("Collecting garbage"); - yield (0, exec_1.exec)("bash", ["-c", "sudo rm -rf /nix/.[!.]* /nix/..?*"]); - const gcEnabled = utils.getInputAsBool(process.platform == "darwin" ? constants_1.Inputs.GCMacos : constants_1.Inputs.GCLinux); - if (gcEnabled) { - const maxStoreSize = utils.getInputAsInt(process.platform == "darwin" - ? constants_1.Inputs.GCMaxStoreSizeMacos - : constants_1.Inputs.GCMaxStoreSizeLinux, { required: true }); - yield (0, exec_1.exec)("bash", [ - "-c", - ` - STORE_SIZE="$(nix path-info --json --all | jq 'map(.narSize) | add')" - printf "Current store size in bytes: $STORE_SIZE\\n" - - MAX_STORE_SIZE=${maxStoreSize} - - if (( STORE_SIZE > MAX_STORE_SIZE )); then - (( R1 = STORE_SIZE - MAX_STORE_SIZE )) - (( R2 = R1 > 0 ? R1 : 0 )) - printf "Max bytes to free: $R2\\n" - nix store gc --max "$R2" - fi - ` - ]); - } - }); -} -exports.collectGarbage = collectGarbage; - - -/***/ }), - -/***/ 6231: +/***/ 7063: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62584,121 +62719,63 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.purgeCaches = void 0; +exports.token = exports.uploadChunkSize = exports.purgeCreatedMaxAge = exports.purgeLastAccessed = exports.purgePrefixes = exports.purgeOverwrite = exports.purge = exports.gcMaxStoreSize = exports.paths = exports.save = exports.nix = exports.failOn = exports.skipRestoreOnHitPrimaryKey = exports.restorePrefixesAllMatches = exports.restorePrefixesFirstMatch = exports.primaryKey = void 0; const core = __importStar(__nccwpck_require__(2186)); -const github = __importStar(__nccwpck_require__(5438)); const constants_1 = __nccwpck_require__(9042); -const utils = __importStar(__nccwpck_require__(6850)); -function purgeByTime({ doUseLastAccessedTime, keys, lookupOnly, time }) { - return __awaiter(this, void 0, void 0, function* () { - const verb = doUseLastAccessedTime ? "last accessed" : "created"; - const maxDate = utils.getMaxDate({ doUseLastAccessedTime, time }); - utils.info(` - ${lookupOnly ? "Searching for" : "Purging"} caches ${verb} before ${maxDate.toISOString()} and having keys: - ${utils.stringify(keys)} - `); - const token = core.getInput(constants_1.Inputs.Token, { required: true }); - const caches = utils.filterCachesByTime({ - caches: yield utils.getCachesByKeys(token, keys), - doUseLastAccessedTime, - maxDate - }); - utils.info(` - Found ${caches.length} cache(s): - ${utils.stringify(caches)} - `); - if (lookupOnly) { - return caches; - } - const octokit = github.getOctokit(token); - caches.forEach((cache) => __awaiter(this, void 0, void 0, function* () { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - const atDatePretty = atDate.toISOString(); - if (atDate < maxDate) { - utils.info(`Deleting the cache having the key '${cache.key}' and ${verb} at ${atDatePretty}`); - try { - yield octokit.rest.actions.deleteActionsCacheById({ - per_page: 100, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - cache_id: cache.id - }); - } - catch (error) { - utils.info(` - Failed to delete cache ${cache.key} - - ${error} - `); - } - } - else { - utils.info(`Skipping the cache having the key '${cache.key}' and ${verb} at ${atDatePretty}`); - } - } - })); - return []; - }); -} -function purge({ key, lookupOnly, time }) { - return __awaiter(this, void 0, void 0, function* () { - const accessed = core.getInput(constants_1.Inputs.PurgeAccessed) === "true"; - const created = core.getInput(constants_1.Inputs.PurgeCreated) === "true"; - let purgeKeys = utils.getInputAsArray(constants_1.Inputs.PurgeKeys); - if (purgeKeys.length == 0) { - purgeKeys.push(...[key]); - } - purgeKeys = purgeKeys.filter(key => key.trim().length > 0); - const results = []; - if (accessed || created) { - if (accessed) { - results.push(...(yield purgeByTime({ - doUseLastAccessedTime: true, - keys: purgeKeys, - lookupOnly, - time - }))); - } - if (created) { - results.push(...(yield purgeByTime({ - doUseLastAccessedTime: false, - keys: purgeKeys, - lookupOnly, - time - }))); - } - } - else { - core.warning("Either `accessed` or `created` input should be `true`."); - } - return results; - }); -} -function purgeCaches({ key, lookupOnly, time }) { - return __awaiter(this, void 0, void 0, function* () { - const purgeEnabled = utils.getInputAsBool(constants_1.Inputs.Purge); - const results = []; - if (purgeEnabled) { - results.push(...(yield purge({ key, lookupOnly, time }))); - } - return results; - }); -} -exports.purgeCaches = purgeCaches; +const utils = __importStar(__nccwpck_require__(9378)); +exports.primaryKey = core.getInput(constants_1.Inputs.PrimaryKey, { required: true }); +exports.restorePrefixesFirstMatch = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesFirstMatch); +exports.restorePrefixesAllMatches = utils.getInputAsArray(constants_1.Inputs.RestorePrefixesAllMatches); +exports.skipRestoreOnHitPrimaryKey = utils.getInputAsBool(constants_1.Inputs.SkipRestoreOnHitPrimaryKey); +exports.failOn = (() => { + var _a; + const failOnRaw = (_a = new RegExp("^(primary|first-match)\\.(miss|not-restored)$") + .exec(core.getInput(constants_1.Inputs.FailOn))) === null || _a === void 0 ? void 0 : _a.slice(1); + if (!failOnRaw) { + return; + } + const [keyType, result] = failOnRaw; + if ((keyType == "primary" || keyType == "first-match") && + (result == "miss" || result == "not-restored")) { + return { keyType, result }; + } +})(); +exports.nix = utils.getInputAsBool(constants_1.Inputs.Nix); +exports.save = utils.getInputAsBool(constants_1.Inputs.Save); +exports.paths = (exports.nix ? ["/nix/", "~/.cache/nix", "~root/.cache/nix"] : []).concat((() => { + const paths = utils.getInputAsArray(constants_1.Inputs.Paths); + const pathsPlatform = utils.getInputAsArray(utils.isLinux ? constants_1.Inputs.PathsLinux : constants_1.Inputs.PathsMacos); + if (pathsPlatform.length > 0) { + return pathsPlatform; + } + else + return paths; +})()); +exports.gcMaxStoreSize = exports.nix + ? (() => { + const gcMaxStoreSize = utils.getInputAsInt(constants_1.Inputs.GCMaxStoreSize); + const gcMaxStoreSizePlatform = utils.getInputAsInt(utils.isLinux + ? constants_1.Inputs.GCMaxStoreSizeLinux + : constants_1.Inputs.GCMaxStoreSizeMacos); + return gcMaxStoreSizePlatform + ? gcMaxStoreSizePlatform + : gcMaxStoreSize; + })() + : undefined; +exports.purge = utils.getInputAsBool(constants_1.Inputs.Purge); +exports.purgeOverwrite = (() => { + const purgeOverwrite = core.getInput(constants_1.Inputs.PurgeOverwrite); + if (!(purgeOverwrite == "always" || purgeOverwrite == "never")) { + return "default"; + } + return purgeOverwrite; +})(); +exports.purgePrefixes = utils.getInputAsArray(constants_1.Inputs.PurgePrefixes); +exports.purgeLastAccessed = utils.getInputAsInt(constants_1.Inputs.PurgeLastAccessed); +exports.purgeCreatedMaxAge = utils.getInputAsInt(constants_1.Inputs.PurgeCreated); +exports.uploadChunkSize = utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) || 32 * 1024 * 1024; +exports.token = core.getInput(constants_1.Inputs.Token, { required: true }); /***/ }), @@ -62772,21 +62849,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -const cache = __importStar(__nccwpck_require__(7799)); +const cache = __importStar(__nccwpck_require__(7942)); const core = __importStar(__nccwpck_require__(2186)); -const github_1 = __nccwpck_require__(5438); -const github = __importStar(__nccwpck_require__(5438)); const constants_1 = __nccwpck_require__(9042); -const gc_1 = __nccwpck_require__(2193); -const purge_1 = __nccwpck_require__(6231); -const utils = __importStar(__nccwpck_require__(6850)); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +const collectGarbage_1 = __nccwpck_require__(9311); +const purge_1 = __nccwpck_require__(7612); // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to // throw an uncaught exception. Instead of failing this action, just warn. process.on("uncaughtException", e => utils.logError(e.message)); function saveImpl(stateProvider) { return __awaiter(this, void 0, void 0, function* () { - let cacheId = -1; + const cacheId = -1; + const time = Date.now(); try { if (!utils.isCacheFeatureAvailable()) { return; @@ -62796,57 +62873,55 @@ function saveImpl(stateProvider) { } // If restore has stored a primary key in state, reuse that // Else re-evaluate from inputs - const primaryKey = stateProvider.getState(constants_1.State.CachePrimaryKey) || - core.getInput(constants_1.Inputs.Key); - if (!primaryKey || primaryKey === "") { - throw new Error(` - Primary cache key not found. - You may want to specify it in your workflow file. - See "with.key" field at the step where you use this action. - `); - } - const cachePaths = utils.paths; - const restoreKeys = utils.getInputAsArray(constants_1.Inputs.RestoreKeys); - const restoredKey = yield utils.getCacheKey({ - paths: cachePaths, - primaryKey, - restoreKeys, - lookupOnly: true - }); - const time = Date.now(); - if (utils.isExactKeyMatch(primaryKey, restoredKey)) { - utils.info(`Cache hit occurred on the primary key "${primaryKey}".`); - const caches = yield (0, purge_1.purgeCaches)({ - key: primaryKey, - lookupOnly: true, - time + const primaryKey = stateProvider.getState(constants_1.State.CachePrimaryKey) || inputs.primaryKey; + if (inputs.purge) { + if (inputs.purgeOverwrite == "always") { + yield (0, purge_1.purgeCacheByKey)(primaryKey, `Purging the cache with the key "${primaryKey}" because of "${constants_1.Inputs.PurgeOverwrite}: always".`); + } + else { + yield (0, purge_1.purgeCachesByTime)({ + primaryKey, + time, + prefixes: [] + }); + } + } + // Save a cache using the primary key + { + utils.info(`Searching for a cache using the primary key "${primaryKey}".`); + const foundKey = yield utils.restoreCache({ + primaryKey, + restoreKeys: [], + lookupOnly: true }); - if (caches.map(cache => cache.key).includes(primaryKey)) { - utils.info(`Purging the cache with the key "${primaryKey}".`); - const token = core.getInput(constants_1.Inputs.Token, { required: true }); - const octokit = (0, github_1.getOctokit)(token); - octokit.rest.actions.deleteActionsCacheByKey({ - per_page: 100, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - key: primaryKey, - ref: github.context.ref + if (utils.isExactKeyMatch(primaryKey, foundKey)) { + utils.info(` + Cache hit occurred on the "${constants_1.Inputs.PrimaryKey}". + Not saving a new cache. + `); + } + else if (inputs.save) { + yield (0, collectGarbage_1.collectGarbage)(); + utils.info(`Saving a new cache with the key "${primaryKey}".`); + // can throw + yield cache.saveCache(inputs.paths, primaryKey, { + uploadChunkSize: inputs.uploadChunkSize }); + utils.info(`Saved a new cache.`); } else { - utils.info(`The cache with the key "${primaryKey}" won't be purged. Not saving a new cache.`); - yield (0, purge_1.purgeCaches)({ key: primaryKey, lookupOnly: false, time }); - return; + `Not saving a new cache because of "${constants_1.Inputs.Save}: false"`; } } - yield (0, gc_1.collectGarbage)(); - utils.info(`Saving a new cache with the key "${primaryKey}".`); - cacheId = yield cache.saveCache(cachePaths, primaryKey, { - uploadChunkSize: utils.getInputAsInt(constants_1.Inputs.UploadChunkSize) - }); - if (cacheId != -1) { - utils.info(`Cache saved with the key "${primaryKey}".`); - yield (0, purge_1.purgeCaches)({ key: primaryKey, lookupOnly: false, time }); + // Purge other caches + // This runs last so that in case of cache saving errors + // the action can be re-run with other caches + if (inputs.purge) { + yield (0, purge_1.purgeCachesByTime)({ + primaryKey, + time, + prefixes: inputs.purgePrefixes + }); } } catch (error) { @@ -62900,7 +62975,7 @@ class StateProviderBase { this.getState = (key) => ""; } getCacheState() { - const cacheKey = this.getState(constants_1.State.CacheMatchedKey); + const cacheKey = this.getState(constants_1.State.CacheRestoredKey); if (cacheKey) { core.debug(`Cache state/key: ${cacheKey}`); return cacheKey; @@ -62920,8 +62995,8 @@ class NullStateProvider extends StateProviderBase { constructor() { super(...arguments); this.stateToOutputMap = new Map([ - [constants_1.State.CacheMatchedKey, constants_1.Outputs.CacheMatchedKey], - [constants_1.State.CachePrimaryKey, constants_1.Outputs.CachePrimaryKey] + [constants_1.State.CacheRestoredKey, constants_1.Outputs.RestoredKey], + [constants_1.State.CachePrimaryKey, constants_1.Outputs.PrimaryKey] ]); this.setState = (key, value) => { core.setOutput(this.stateToOutputMap.get(key), value); @@ -62935,7 +63010,7 @@ exports.NullStateProvider = NullStateProvider; /***/ }), -/***/ 6850: +/***/ 4427: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -62976,12 +63051,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.filterCachesByTime = exports.getCachesByKeys = exports.getCacheKey = exports.paths = exports.isCacheFeatureAvailable = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; -const cache = __importStar(__nccwpck_require__(7799)); +exports.warning = exports.info = exports.stringify = exports.getMaxDate = exports.mkMessageWrongValue = exports.getCachesByKeys = exports.restoreCache = exports.isCacheFeatureAvailable = exports.isValidEvent = exports.logError = exports.logWarning = exports.isExactKeyMatch = exports.isGhes = void 0; +const cache = __importStar(__nccwpck_require__(7942)); const core = __importStar(__nccwpck_require__(2186)); const github = __importStar(__nccwpck_require__(5438)); const dedent_1 = __importDefault(__nccwpck_require__(5281)); const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); function isGhes() { const ghUrl = new URL(process.env["GITHUB_SERVER_URL"] || "https://github.com"); return ghUrl.hostname.toUpperCase() !== "GITHUB.COM"; @@ -62996,7 +63072,7 @@ function isExactKeyMatch(key, cacheKey) { exports.isExactKeyMatch = isExactKeyMatch; function logWarning(message) { const warningPrefix = "[warning]"; - core.info(`${warningPrefix}${message}`); + core.warning(`${warningPrefix} ${message}`); } exports.logWarning = logWarning; function logError(message) { @@ -63005,58 +63081,39 @@ function logError(message) { } exports.logError = logError; // Cache token authorized for all events that are tied to a ref -// See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context +// See GitHub Context https://docs.github.com/en/actions/learn-github-actions/contexts function isValidEvent() { return constants_1.RefKey in process.env && Boolean(process.env[constants_1.RefKey]); } exports.isValidEvent = isValidEvent; -function getInputAsArray(name, options) { - return core - .getInput(name, options) - .split("\n") - .map(s => s.replace(/^!\s+/, "!").trim()) - .filter(x => x !== ""); -} -exports.getInputAsArray = getInputAsArray; -function getInputAsInt(name, options) { - const value = parseInt(core.getInput(name, options)); - if (isNaN(value) || value < 0) { - return undefined; - } - return value; -} -exports.getInputAsInt = getInputAsInt; -function getInputAsBool(name, options) { - const result = core.getInput(name, options); - return result.toLowerCase() === "true"; -} -exports.getInputAsBool = getInputAsBool; function isCacheFeatureAvailable() { if (cache.isFeatureAvailable()) { return true; } if (isGhes()) { - logWarning(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. + logError(`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)`); return false; } - logWarning("An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions."); + logError(` + Actions cache service is unavailable. + Please check https://www.githubstatus.com/ for any ongoing issue in Actions. + `); return false; } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; -exports.paths = ["/nix/", "~/.cache/nix", "~root/.cache/nix"]; -function getCacheKey({ paths, primaryKey, restoreKeys, lookupOnly }) { +function restoreCache({ primaryKey, restoreKeys, lookupOnly }) { return __awaiter(this, void 0, void 0, function* () { return yield cache.restoreCache( // https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 - paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false); + inputs.paths.slice(), primaryKey, restoreKeys, { lookupOnly }, false, ["--skip-old-files"]); }); } -exports.getCacheKey = getCacheKey; -function getCachesByKeys(token, keys) { +exports.restoreCache = restoreCache; +function getCachesByKeys(keys) { return __awaiter(this, void 0, void 0, function* () { const caches = []; - const octokit = github.getOctokit(token); + const octokit = github.getOctokit(inputs.token); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; for (let page = 1; page <= 500; page += 1) { @@ -63071,32 +63128,22 @@ function getCachesByKeys(token, keys) { if (cachesRequest.actions_caches.length == 0) { break; } - caches.push(...cachesRequest.actions_caches); + if (isExactKeyMatch(inputs.primaryKey, key)) { + caches.push(...cachesRequest.actions_caches); + } } } return caches; }); } exports.getCachesByKeys = getCachesByKeys; -const filterCachesByTime = ({ caches, doUseLastAccessedTime, maxDate }) => caches.filter(cache => { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - return atDate < maxDate; - } - else - return false; -}); -exports.filterCachesByTime = filterCachesByTime; -const mkMessageWrongValue = (input, value) => `Wrong value for the input '${input}': ${value}`; +const mkMessageWrongValue = (input, value) => `Wrong value for the input "${input}": ${value}`; exports.mkMessageWrongValue = mkMessageWrongValue; function getMaxDate({ doUseLastAccessedTime, time }) { const inputMaxAge = doUseLastAccessedTime - ? constants_1.Inputs.PurgeAccessedMaxAge - : constants_1.Inputs.PurgeCreatedMaxAge; - const maxAge = core.getInput(inputMaxAge, { required: false }); + ? constants_1.Inputs.PurgeLastAccessed + : constants_1.Inputs.PurgeCreated; + const maxAge = core.getInput(inputMaxAge); const maxDate = new Date(time - Number.parseInt(maxAge) * 1000); if (maxDate === null) { throw new Error((0, exports.mkMessageWrongValue)(inputMaxAge, maxAge)); @@ -63106,10 +63153,287 @@ function getMaxDate({ doUseLastAccessedTime, time }) { exports.getMaxDate = getMaxDate; const stringify = (value) => JSON.stringify(value, null, 2); exports.stringify = stringify; -const info = (message) => { - core.info(dedent_1.default.withOptions({})(message)); -}; +const myDedent = dedent_1.default.withOptions({}); +const info = (message) => core.info(myDedent(message)); exports.info = info; +const warning = (message) => core.warning(myDedent(message)); +exports.warning = warning; + + +/***/ }), + +/***/ 9311: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.collectGarbage = void 0; +const exec_1 = __nccwpck_require__(1514); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +function collectGarbage() { + return __awaiter(this, void 0, void 0, function* () { + if (inputs.gcMaxStoreSize) { + utils.info("Collecting garbage."); + const printStoreSize = ` + STORE_SIZE="$(nix path-info --json --all | jq 'map(.narSize) | add')" + printf "Current store size in bytes: $STORE_SIZE\\n" + `; + const run = (command) => __awaiter(this, void 0, void 0, function* () { return yield (0, exec_1.exec)("bash", ["-c", command]); }); + yield run(printStoreSize); + yield run(` + sudo rm -rf /nix/.[!.]* /nix/..?* + + MAX_STORE_SIZE=${inputs.gcMaxStoreSize} + + if (( STORE_SIZE > MAX_STORE_SIZE )); then + (( R1 = STORE_SIZE - MAX_STORE_SIZE )) + (( R2 = R1 > 0 ? R1 : 0 )) + printf "Max bytes to free: $R2\\n" + nix store gc --max "$R2" + fi + `); + utils.info(`Finished collecting garbage.`); + yield run(printStoreSize); + } + }); +} +exports.collectGarbage = collectGarbage; + + +/***/ }), + +/***/ 9378: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.isLinux = exports.getInputAsBool = exports.getInputAsInt = exports.getInputAsArray = void 0; +const core = __importStar(__nccwpck_require__(2186)); +function getInputAsArray(name, options) { + return core + .getInput(name, options) + .split("\n") + .map(s => s.replace(/^!\s+/, "!").trim()) + .filter(x => x !== ""); +} +exports.getInputAsArray = getInputAsArray; +function getInputAsInt(name, options) { + const value = parseInt(core.getInput(name, options)); + if (isNaN(value) || value < 0) { + return undefined; + } + return value; +} +exports.getInputAsInt = getInputAsInt; +function getInputAsBool(name, options) { + const result = core.getInput(name, options); + return result.toLowerCase() === "true"; +} +exports.getInputAsBool = getInputAsBool; +exports.isLinux = process.platform === "linux"; + + +/***/ }), + +/***/ 7612: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.purgeCachesByTime = exports.filterCachesByTime = exports.purgeCacheByKey = void 0; +const github = __importStar(__nccwpck_require__(5438)); +const constants_1 = __nccwpck_require__(9042); +const inputs = __importStar(__nccwpck_require__(7063)); +const utils = __importStar(__nccwpck_require__(4427)); +function purgeCacheByKey(key, message) { + return __awaiter(this, void 0, void 0, function* () { + try { + utils.info(message || ""); + const octokit = github.getOctokit(inputs.token); + yield octokit.rest.actions.deleteActionsCacheByKey({ + per_page: 100, + owner: github.context.repo.owner, + repo: github.context.repo.repo, + key, + ref: github.context.ref + }); + } + catch (error) { + utils.info(` + Failed to delete the cache. + + ${error} + `); + } + }); +} +exports.purgeCacheByKey = purgeCacheByKey; +const filterCachesByTime = ({ caches, doUseLastAccessedTime, maxDate }) => caches.filter(cache => { + const at = doUseLastAccessedTime + ? cache.last_accessed_at + : cache.created_at; + if (at && cache.key) { + const atDate = new Date(at); + return atDate < maxDate; + } + else + return false; +}); +exports.filterCachesByTime = filterCachesByTime; +function purgeByTime({ primaryKey, doUseLastAccessedTime, prefixes, time }) { + return __awaiter(this, void 0, void 0, function* () { + const verb = doUseLastAccessedTime ? "last accessed" : "created"; + const maxDate = utils.getMaxDate({ doUseLastAccessedTime, time }); + let caches = []; + if (prefixes.length > 0) { + utils.info(` + Purging cache(s) ${verb} before ${maxDate.toISOString()} and having key prefixes: + + ${utils.stringify(prefixes)} + `); + caches = (0, exports.filterCachesByTime)({ + caches: yield utils.getCachesByKeys(prefixes), + doUseLastAccessedTime, + maxDate + }); + } + else { + utils.info(`Purging cache(s) ${verb} before ${maxDate.toISOString()} and having the key "${primaryKey}".`); + caches = (0, exports.filterCachesByTime)({ + caches: yield utils.getCachesByKeys([primaryKey]), + doUseLastAccessedTime, + maxDate + }).filter(x => utils.isExactKeyMatch(primaryKey, x.key)); + } + if (inputs.purgeOverwrite == "never") { + `The cache with the key "${primaryKey}" will be skipped because of "${constants_1.Inputs.PurgeOverwrite}: never".`; + caches.filter(x => !utils.isExactKeyMatch(primaryKey, x.key)); + } + utils.info(` + Found ${caches.length} cache(s): + + ${utils.stringify(caches)} + `); + for (const cache of caches) { + const at = doUseLastAccessedTime + ? cache.last_accessed_at + : cache.created_at; + if (at && cache.key) { + const atDate = new Date(at); + const atDatePretty = atDate.toISOString(); + yield purgeCacheByKey(cache.key, `Purging the cache ${verb} at ${atDatePretty} and having the key "${cache.key}".`); + } + } + utils.info(`Finished purging cache(s).`); + }); +} +function purgeCachesByTime({ primaryKey, time, prefixes }) { + return __awaiter(this, void 0, void 0, function* () { + // TODO https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 + for (const flag of [true, false]) { + if (flag ? inputs.purgeLastAccessed : inputs.purgeCreatedMaxAge) { + yield purgeByTime({ + primaryKey, + doUseLastAccessedTime: flag, + prefixes: prefixes.slice(), + time + }); + } + } + }); +} +exports.purgeCachesByTime = purgeCachesByTime; /***/ }), diff --git a/flake.nix b/flake.nix index 205bfe6e..f2e265d0 100644 --- a/flake.nix +++ b/flake.nix @@ -4,28 +4,49 @@ let flakes = inputs.flakes; in flakes.makeFlake { inputs = { - inherit (flakes.all) nixpkgs devshell; + inherit (flakes.all) nixpkgs devshell drv-tools; }; perSystem = { inputs, system }: let pkgs = inputs.nixpkgs.legacyPackages.${system}; inherit (inputs.devshell.lib.${system}) mkCommands mkRunCommands mkRunCommandsDir mkShell; + inherit (inputs.drv-tools.lib.${system}) writeYAML mkShellApps getExe; - tools = [ pkgs.nodejs_18 ]; + tools = [ pkgs.nodejs_18 pkgs.poetry ]; + packages = mkShellApps { + writeSave = writeYAML "save" "save/action.yml" (import ./action.nix { target = "save"; inherit (pkgs) lib; }); + writeRestore = writeYAML "restore" "restore/action.yml" (import ./action.nix { target = "restore"; inherit (pkgs) lib; }); + writeCache = writeYAML "cache" "action.yml" (import ./action.nix { target = "cache"; inherit (pkgs) lib; }); + write = { + runtimeInputs = [ pkgs.poetry ]; + text = '' + ${getExe packages.writeSave} + ${getExe packages.writeRestore} + ${getExe packages.writeCache} + + poetry run translate_table + ''; + + description = "write action.yml-s and tables for README-s"; + }; + init = { + runtimeInputs = [ pkgs.nodejs ]; + text = "${pkgs.nodejs}/bin/npm i"; + description = "install dependencies"; + }; + build = { + text = "${pkgs.nodejs_18}/bin/npm run build"; + description = "build project"; + }; + }; devShells.default = mkShell { packages = tools; - bash.extra = "export NODE_OPTIONS=--openssl-legacy-provider"; - commands = - mkCommands "tools" tools ++ - [ - { name = "init"; command = "${pkgs.nodejs_18}/bin/npm i"; help = "install dependencies"; } - { name = "build"; command = "NODE_OPTIONS=--openssl-legacy-provider ${pkgs.nodejs_18}/bin/npm run build"; help = "build project"; } - ]; + commands = mkRunCommands "scripts" { inherit (packages) write init build; }; }; in { - inherit devShells; + inherit packages devShells; }; }; } diff --git a/package-lock.json b/package-lock.json index 3808d5f3..22beb415 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "cache", - "version": "3.3.1", - "lockfileVersion": 2, + "version": "3.3.2", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cache", - "version": "3.3.1", + "version": "3.3.2", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@actions/cache": "^3.2.1", + "@actions/cache": "file:actions-toolkit/packages/cache", "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", @@ -41,40 +41,68 @@ "typescript": "^4.9.5" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@actions/cache": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.2.1.tgz", - "integrity": "sha512-QurbMiY//02+0kN1adJkMHN44RcZ5kAXfhSnKUZmtSmhMTNqLitGArG1xOkt93NNyByTlLGAc5wIOF/dZ2ENOQ==", + "actions-toolkit/packages/cache": { + "name": "@actions/cache", + "version": "3.2.2", + "license": "MIT", "dependencies": { "@actions/core": "^1.10.0", "@actions/exec": "^1.0.1", "@actions/glob": "^0.1.0", - "@actions/http-client": "^2.0.1", + "@actions/http-client": "^2.1.1", "@actions/io": "^1.0.1", "@azure/abort-controller": "^1.1.0", "@azure/ms-rest-js": "^2.6.0", "@azure/storage-blob": "^12.13.0", - "semver": "^6.1.0", + "semver": "^6.3.1", "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/semver": "^6.0.0", + "@types/uuid": "^3.4.5", + "typescript": "^5.2.2" } }, - "node_modules/@actions/cache/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "actions-toolkit/packages/cache/node_modules/@types/semver": { + "version": "6.2.7", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.7.tgz", + "integrity": "sha512-blctEWbzUFzQx799RZjzzIdBJOXmE37YYEyDtKkx5Dg+V7o/zyyAxLPiI98A2jdTtDgxZleMdfV+7p8WbRJ1OQ==", + "dev": true + }, + "actions-toolkit/packages/cache/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } }, + "actions-toolkit/packages/cache/node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@actions/cache": { + "resolved": "actions-toolkit/packages/cache", + "link": true + }, "node_modules/@actions/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", @@ -121,9 +149,9 @@ } }, "node_modules/@actions/http-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", - "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz", + "integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==", "dependencies": { "tunnel": "^0.0.6" } @@ -2001,6 +2029,12 @@ "@types/node": "*" } }, + "node_modules/@types/uuid": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.13.tgz", + "integrity": "sha512-pAeZeUbLE4Z9Vi9wsWV2bYPTweEHeJJy0G4pEjOA/FSvy1Ad5U5Km8iDV6TKre1mjBiVNfAdVHKruP8bAh4Q5A==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -3880,20 +3914,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -7918,5969 +7938,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@actions/cache": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.2.1.tgz", - "integrity": "sha512-QurbMiY//02+0kN1adJkMHN44RcZ5kAXfhSnKUZmtSmhMTNqLitGArG1xOkt93NNyByTlLGAc5wIOF/dZ2ENOQ==", - "requires": { - "@actions/core": "^1.10.0", - "@actions/exec": "^1.0.1", - "@actions/glob": "^0.1.0", - "@actions/http-client": "^2.0.1", - "@actions/io": "^1.0.1", - "@azure/abort-controller": "^1.1.0", - "@azure/ms-rest-js": "^2.6.0", - "@azure/storage-blob": "^12.13.0", - "semver": "^6.1.0", - "uuid": "^3.3.3" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "@actions/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", - "integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", - "requires": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@actions/exec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", - "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", - "requires": { - "@actions/io": "^1.0.1" - } - }, - "@actions/github": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", - "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", - "requires": { - "@actions/http-client": "^2.0.1", - "@octokit/core": "^3.6.0", - "@octokit/plugin-paginate-rest": "^2.17.0", - "@octokit/plugin-rest-endpoint-methods": "^5.13.0" - } - }, - "@actions/glob": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz", - "integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==", - "requires": { - "@actions/core": "^1.2.6", - "minimatch": "^3.0.4" - } - }, - "@actions/http-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", - "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==", - "requires": { - "tunnel": "^0.0.6" - } - }, - "@actions/io": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz", - "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw==" - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@azure/abort-controller": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", - "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", - "requires": { - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/core-auth": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", - "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/core-http": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz", - "integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@azure/core-lro": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", - "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-util": "^1.2.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/core-paging": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", - "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", - "requires": { - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", - "requires": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/core-util": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", - "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/logger": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", - "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", - "requires": { - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@azure/ms-rest-js": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz", - "integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==", - "requires": { - "@azure/core-auth": "^1.1.4", - "abort-controller": "^3.0.0", - "form-data": "^2.5.0", - "node-fetch": "^2.6.7", - "tslib": "^1.10.0", - "tunnel": "0.0.6", - "uuid": "^8.3.2", - "xml2js": "^0.5.0" - }, - "dependencies": { - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "@azure/storage-blob": { - "version": "12.14.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.14.0.tgz", - "integrity": "sha512-g8GNUDpMisGXzBeD+sKphhH5yLwesB4JkHr1U6be/X3F+cAMcyGLPD1P89g2M7wbEtUJWoikry1rlr83nNRBzg==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "events": "^3.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - } - } - }, - "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.5" - } - }, - "@babel/compat-data": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", - "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", - "dev": true - }, - "@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", - "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", - "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/traverse": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", - "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", - "dev": true, - "requires": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2" - }, - "dependencies": { - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - } - } - }, - "@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } - } - }, - "@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" - }, - "dependencies": { - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - } - } - }, - "@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - } - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, - "@octokit/plugin-paginate-rest": { - "version": "2.21.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", - "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", - "requires": { - "@octokit/types": "^6.40.0" - } - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "requires": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "requires": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "@opentelemetry/api": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", - "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==" - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.5.2", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", - "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", - "dev": true, - "requires": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/nock": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz", - "integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==", - "dev": true, - "requires": { - "nock": "*" - } - }, - "@types/node": { - "version": "16.18.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", - "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==" - }, - "@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/tunnel": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", - "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", - "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/type-utils": "5.60.1", - "@typescript-eslint/utils": "5.60.1", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", - "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", - "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", - "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.60.1", - "@typescript-eslint/utils": "5.60.1", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", - "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", - "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/visitor-keys": "5.60.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", - "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.1", - "@typescript-eslint/types": "5.60.1", - "@typescript-eslint/typescript-estree": "5.60.1", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", - "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@vercel/ncc": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.1.tgz", - "integrity": "sha512-S4cL7Taa9yb5qbv+6wLgiKVZ03Qfkc4jGRuiUQMQ8HGBD5pcNRnHeYM33zBvJE4/zJGjJJ8GScB+WmTsn9mORw==", - "dev": true - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dev": true, - "requires": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^28.1.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "requires": { - "semver": "^7.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001509", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001509.tgz", - "integrity": "sha512-2uDDk+TRiTX5hMcUYT/7CSyzMZxjfGu0vAUjS2g0LSD8UoXOv0LtpH4LxGMemsiPq6LCVIUjNwVM0erkOkGCDA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "requires": {} - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "electron-to-chromium": { - "version": "1.4.447", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.447.tgz", - "integrity": "sha512-sxX0LXh+uL41hSJsujAN86PjhrV/6c79XmpY0TvjZStV6VxIgarf8SRkUoUTuYmFcZQTemsoqo8qXOGw5npWfw==", - "dev": true - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - } - } - }, - "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true, - "requires": {} - }, - "eslint-config-standard": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", - "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", - "dev": true, - "requires": {} - }, - "eslint-config-standard-with-typescript": { - "version": "36.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-36.0.0.tgz", - "integrity": "sha512-8ZSEskfrDAkUF2lTQLMT0CBzgRNlx1uIM7l2I7L683dKAXUdHuEL2x+GxuGAsdsoWbx7W7Zv0xF67VCEZXIk0Q==", - "dev": true, - "requires": { - "@typescript-eslint/parser": "^5.50.0", - "eslint-config-standard": "17.0.0" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-es": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", - "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-jest": { - "version": "26.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.9.0.tgz", - "integrity": "sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "^5.10.0" - } - }, - "eslint-plugin-n": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", - "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", - "dev": true, - "requires": { - "builtins": "^5.0.1", - "eslint-plugin-es": "^4.1.0", - "eslint-utils": "^3.0.0", - "ignore": "^5.1.1", - "is-core-module": "^2.11.0", - "minimatch": "^3.1.2", - "resolve": "^1.22.1", - "semver": "^7.3.8" - } - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "dev": true, - "requires": {} - }, - "eslint-plugin-simple-import-sort": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-7.0.0.tgz", - "integrity": "sha512-U3vEDB5zhYPNfxT5TYR7u01dboFZp+HNpnGhkDB2g/2E4wZ/g1Q9Ton8UwCLfRV9yAKyYqDh62oHOamvkFxsvw==", - "dev": true, - "requires": {} - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true - }, - "espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", - "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/types": "^28.1.3", - "import-local": "^3.0.2", - "jest-cli": "^28.1.3" - } - }, - "jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - } - }, - "@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - } - }, - "@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "dev": true - }, - "jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - } - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - } - } - }, - "jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - } - } - }, - "jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - }, - "dependencies": { - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - } - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - } - } - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true - }, - "jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", - "dev": true, - "requires": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" - } - }, - "jest-runner": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - } - } - }, - "jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - } - } - }, - "jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - } - }, - "jest-snapshot": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "leven": "^3.1.0", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "nock": { - "version": "13.2.9", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", - "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", - "propagate": "^2.0.0" - } - }, - "node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - } - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", - "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - } - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index afe6c676..89e54a05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cache", - "version": "3.3.1", + "version": "3.3.2", "private": true, "description": "Cache dependencies and build outputs", "main": "dist/restore/index.js", @@ -24,7 +24,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@actions/cache": "^3.2.1", + "@actions/cache": "file:actions-toolkit/packages/cache", "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..a6131941 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,198 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "lxml" +version = "4.9.3" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +files = [ + {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, + {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, + {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, + {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, + {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, + {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, + {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, + {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, + {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, + {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, + {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, + {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, + {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, + {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, + {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, + {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, + {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, + {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, + {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, + {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, + {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, + {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, + {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, + {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, + {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, + {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, + {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, + {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, + {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, + {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, + {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, + {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, + {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.35)"] + +[[package]] +name = "markdown" +version = "3.5.1" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"}, + {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"}, +] + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.12" +description = "Typing stubs for PyYAML" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"}, + {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "0f24f68872eac2ca29f9c3bdc5ed4a99b900470b3030b2d378d7301a10e9efd4" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 00000000..ab1033bd --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..7e4f1095 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[tool.poetry] +name = "cache-nix-action" +version = "0.1.0" +description = "" +authors = ["Your Name "] +license = "MIT" +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.10" +pyyaml = "^6.0.1" +types-pyyaml = "^6.0.12.12" +lxml = "^4.9.3" +markdown = "^3.5.1" + +[tool.poetry.scripts] +translate_table = "cache_nix_action.translate_table:main" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/restore/README.md b/restore/README.md index 07d70a23..41f47eb8 100644 --- a/restore/README.md +++ b/restore/README.md @@ -6,19 +6,11 @@ The restore action restores a cache. It works similarly to the `cache` action ex ### Inputs -* `key` - An explicit key for a cache entry. See [creating a cache key](../README.md#creating-a-cache-key). -* `path` - A list of files, directories, and wildcard patterns to restore. See [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns. -* `restore-keys` - An ordered list of prefix-matched keys to use for restoring stale cache if no cache hit occurred for key. -* `fail-on-cache-miss` - Fail the workflow if cache entry is not found. Default: `false` +
namedescriptiondefaultrequired

primary-key

  • When a non-empty string, the action uses this key for restoring a cache.
  • Otherwise, the action fails.

true

restore-prefixes-first-match

  • When a newline-separated non-empty list of non-empty key prefixes, when there's a miss on the primary-key, the action searches in this list for the first prefix for which there exists a cache with a matching key and the action tries to restore that cache.
  • Otherwise, this input has no effect.

''

false

restore-prefixes-all-matches

  • When a newline-separated non-empty list of non-empty key prefixes, the action tries to restore all caches whose keys match these prefixes.
  • Otherwise, this input has no effect.

''

false

skip-restore-on-hit-primary-key

  • Can have an effect only when restore-prefixes-first-match has no effect.
  • When true, when there's a hit on the primary-key, the action doesn't restore caches.
  • Otherwise, this input has no effect.

false

false

fail-on

  • Input form: <key type>.<result>.
  • <key type> options: primary-key, first-match.
  • <result> options: miss, not-restored.
  • When the input satisfies the input form, when the event described in the input happens, the action fails.
  • Example:
    • Input: primary-key.not-restored.
    • Event: a cache could not be restored via the primary-key.
  • Otherwise, this input has no effect.

''

false

nix

  • When true, the action can do Nix-specific things.
  • Otherwise, the action doesn't do them.

true

false

save

  • When true, the action can save a cache with the primary-key.
  • Otherwise, the action can't save a cache.

true

false

paths

  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for restoring caches.
  • Otherwise, the action uses default paths for restoring caches.

''

false

paths-macos

  • Overrides paths.
  • Can have an effect only when on a macOS runner.
  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for restoring caches.
  • Otherwise, the action uses default paths for restoring caches.

''

false

paths-linux

  • Overrides paths.
  • Can have an effect only when on a Linux runner.
  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for restoring caches.
  • Otherwise, the action uses default paths for restoring caches.

''

false

token

The action uses it to communicate with GitHub API.

${{ github.token }}

false

### Outputs -* `cache-hit` - A boolean value to indicate an exact match was found for the key. -* `cache-primary-key` - Cache primary key passed in the input to use in subsequent steps of the workflow. -* `cache-matched-key` - Key of the cache that was restored, it could either be the primary key on cache-hit or a partial/complete match of one of the restore keys. - -> **Note** -`cache-hit` will be set to `true` only when cache hit occurs for the exact `key` match. For a partial key match via `restore-keys` or a cache miss, it will be set to `false`. +
namedescription

primary-key

  • A string.
  • The primary-key.

hit

  • A boolean value.
  • true when hit-primary-key is true or hit-first-match is true.
  • false otherwise.

hit-primary-key

  • A boolean value.
  • true when there was a hit on the primary-key.
  • false otherwise.

hit-first-match

  • A boolean value.
  • true when there was a hit on a key matching restore-prefixes-first-match.
  • false otherwise.

restored-key

  • A string.
  • The key of a cache restored via the primary-key or via the restore-prefixes-first-match.
  • An empty string otherwise.

restored-keys

  • A possibly empty array of strings (JSON).
  • Keys of restored caches.
  • Example: ["key1", "key2"].
### Environment Variables @@ -127,7 +119,7 @@ Usually you may want to use the same `key` with both `actions/cache/restore` and ### Using restore action outputs to make save action behave just like the cache action -The outputs `cache-primary-key` and `cache-matched-key` can be used to check if the restored cache is same as the given primary key. Alternatively, the `cache-hit` output can also be used to check if the restored was a complete match or a partially restored cache. +The outputs `cache-primary-key` and `cache-restored-key` can be used to check if the restored cache is same as the given primary key. Alternatively, the `cache-hit` output can also be used to check if the restored was a complete match or a partially restored cache. ### Ensuring proper restores and save happen across the actions diff --git a/restore/action.yml b/restore/action.yml index 6180674c..a4440fd8 100644 --- a/restore/action.yml +++ b/restore/action.yml @@ -1,39 +1,123 @@ -name: 'Restore Cache' -description: 'Restore Cache artifacts like dependencies and build outputs to improve workflow execution time' -author: 'GitHub' +name: "Restore Nix store" +description: "Restore Nix store using GitHub Actions cache to speed up workflows." +author: "GitHub" inputs: - path: - description: 'A list of files, directories, and wildcard patterns to restore' + primary-key: + description: | + - When a non-empty string, the action uses this key for restoring a cache. + - Otherwise, the action fails. required: true - key: - description: 'An explicit key for restoring the cache' - required: true - restore-keys: - description: 'An ordered list of keys to use for restoring stale cache if no cache hit occurred for key. Note `cache-hit` returns false in this case.' - required: false - extra-restore-keys: - description: 'A newline-separated list of key prefixes used for restoring multiple caches.' - default: '' - restore-key-hit: - description: "When true, if a cache key matching `restore-keys` exists, it counts as a cache hit. Thus, a job won't save a new cache." - default: 'false' - required: false - - fail-on-cache-miss: - description: 'Fail the workflow if cache entry is not found' - default: 'false' - required: false + + restore-prefixes-first-match: + description: | + - When a newline-separated non-empty list of non-empty key prefixes, when there's a miss on the `primary-key`, + the action searches in this list for the first prefix for which there exists a cache + with a matching key and the action tries to restore that cache. + - Otherwise, this input has no effect. + default: "" + restore-prefixes-all-matches: + description: | + - When a newline-separated non-empty list of non-empty key prefixes, the action tries to restore + all caches whose keys match these prefixes. + - Otherwise, this input has no effect. + default: "" + + skip-restore-on-hit-primary-key: + description: | + - Can have an effect only when `restore-prefixes-first-match` has no effect. + - When `true`, when there's a hit on the `primary-key`, the action doesn't restore caches. + - Otherwise, this input has no effect. + default: "false" + + fail-on: + description: | + - Input form: `.`. + - `` options: `primary-key`, `first-match`. + - `` options: `miss`, `not-restored`. + - When the input satisfies the input form, when the event described in the input happens, the action fails. + - Example: + - Input: `primary-key.not-restored`. + - Event: a cache could not be restored via the `primary-key`. + - Otherwise, this input has no effect. + default: "" + + nix: + description: | + - When `true`, the action can do Nix-specific things. + - Otherwise, the action doesn't do them. + default: "true" + + save: + description: | + - When `true`, the action can save a cache with the `primary-key`. + - Otherwise, the action can't save a cache. + default: "true" + paths: + description: | + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for restoring caches. + - Otherwise, the action uses default paths for restoring caches. + default: "" + paths-macos: + description: | + - Overrides `paths`. + - Can have an effect only when on a `macOS` runner. + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for restoring caches. + - Otherwise, the action uses default paths for restoring caches. + default: "" + paths-linux: + description: | + - Overrides `paths`. + - Can have an effect only when on a `Linux` runner. + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for restoring caches. + - Otherwise, the action uses default paths for restoring caches. + default: "" + + + + token: + description: The action uses it to communicate with GitHub API. + default: ${{ github.token }} outputs: - cache-hit: - description: 'A boolean value to indicate an exact match was found for the primary key' - cache-primary-key: - description: 'A resolved cache key for which cache match was attempted' - cache-matched-key: - description: 'Key of the cache that was restored, it could either be the primary key on cache-hit or a partial/complete match of one of the restore keys' + primary-key: + description: | + - A string. + - The `primary-key`. + + hit: + description: | + - A boolean value. + - `true` when `hit-primary-key` is `true` or `hit-first-match` is `true`. + - `false` otherwise. + hit-primary-key: + description: | + - A boolean value. + - `true` when there was a hit on the `primary-key`. + - `false` otherwise. + hit-first-match: + description: | + - A boolean value. + - `true` when there was a hit on a key matching `restore-prefixes-first-match`. + - `false` otherwise. + + restored-key: + description: | + - A string. + - The key of a cache restored via the `primary-key` or via the `restore-prefixes-first-match`. + - An empty string otherwise. + + restored-keys: + description: | + - A possibly empty array of strings (JSON). + - Keys of restored caches. + - Example: `["key1", "key2"]`. runs: - using: 'node16' - main: '../dist/restore-only/index.js' + using: "node16" + main: "../dist/restore-only/index.js" + branding: - icon: 'archive' - color: 'gray-dark' + icon: "archive" + color: "gray-dark" diff --git a/save/README.md b/save/README.md index 8ce56d29..0cbaa13e 100644 --- a/save/README.md +++ b/save/README.md @@ -6,9 +6,7 @@ The save action saves a cache. It works similarly to the `cache` action except t ### Inputs -* `key` - An explicit key for a cache entry. See [creating a cache key](../README.md#creating-a-cache-key). -* `path` - A list of files, directories, and wildcard patterns to cache. See [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns. -* `upload-chunk-size` - The chunk size used to split up large files during upload, in bytes +
namedescriptiondefaultrequired

primary-key

  • When a non-empty string, the action uses this key for saving a cache.
  • Otherwise, the action fails.

true

nix

  • When true, the action can do Nix-specific things.
  • Otherwise, the action doesn't do them.

true

false

save

  • When true, the action can save a cache with the primary-key.
  • Otherwise, the action can't save a cache.

true

false

paths

  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for saving caches.
  • Otherwise, the action uses default paths for saving caches.

''

false

paths-macos

  • Overrides paths.
  • Can have an effect only when on a macOS runner.
  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for saving caches.
  • Otherwise, the action uses default paths for saving caches.

''

false

paths-linux

  • Overrides paths.
  • Can have an effect only when on a Linux runner.
  • When nix: true, uses ["/nix", "~/.cache/nix", "~root/.cache/nix"] as default paths. Otherwise, uses an empty list as default paths.
  • When a newline-separated non-empty list of non-empty path patterns (see @actions/glob for supported patterns), the action appends it to default paths and uses the resulting list for saving caches.
  • Otherwise, the action uses default paths for saving caches.

''

false

gc-max-store-size

  • Can have an effect only when nix: true, save: true.
  • When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.
  • Otherwise, this input has no effect.

''

false

gc-max-store-size-macos

  • Can have an effect only when nix: true, save: true.
  • Overrides gc-max-store-size.
  • Can have an effect only when on a macOS runner.
  • When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.
  • Otherwise, this input has no effect.

''

false

gc-max-store-size-linux

  • Can have an effect only when nix: true, save: true.
  • Overrides gc-max-store-size.
  • Can have an effect only when on a Linux runner.
  • When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache.
  • Otherwise, this input has no effect.

''

false

purge

  • When true, the action purges (possibly zero) caches.
  • Otherwise, this input has no effect.

false

false

purge-overwrite

  • Can have an effect only when purge: true.
  • When always, the action always purges cache with the primary-key.
  • When never, the action never purges cache with the primary-key.
  • Otherwise, the action purges old caches using purging criteria.

''

false

purge-prefixes

  • Can have an effect only when purge: true.
  • When a newline-separated non-empty list of non-empty cache key prefixes, the action collects all cache keys that match these prefixes.
  • Otherwise, this input has no effect.

''

false

purge-last-accessed

  • Can have an effect only when purge: true.
  • When a number, the action selects for purging caches last accessed more than this number of seconds ago relative to the start of the Save phase.
  • Otherwise, this input has no effect.

''

false

purge-created

  • Can have an effect only when purge: true.
  • When a number, the action selects for purging caches created more than this number of seconds ago relative to the start of the Save phase.
  • Otherwise, this input has no effect.

''

false

upload-chunk-size

  • When a number, the action uses it as the chunk size (in bytes) to split up large files during upload.
  • Otherwise, the action uses the default value 33554432 (32MB).

''

false

token

The action uses it to communicate with GitHub API.

${{ github.token }}

false

### Outputs @@ -16,7 +14,6 @@ This action has no outputs. ## Use cases - ### Only save cache If you are using separate jobs for generating common artifacts and sharing them across jobs, this action will take care of your cache saving needs. @@ -54,6 +51,7 @@ with: ``` #### Case 1 - Where a user would want to reuse the key as it is + ```yaml uses: actions/cache/save@v3 with: diff --git a/save/action.yml b/save/action.yml index a7c48e6d..4180746f 100644 --- a/save/action.yml +++ b/save/action.yml @@ -1,55 +1,120 @@ -name: 'Save a cache' -description: 'Save Cache artifacts like dependencies and build outputs to improve workflow execution time' -author: 'GitHub' +name: "Save Nix store" +description: "Save Nix store using GitHub Actions cache to speed up workflows." +author: "GitHub" inputs: - path: - description: 'A list of files, directories, and wildcard patterns to cache' + primary-key: + description: | + - When a non-empty string, the action uses this key for saving a cache. + - Otherwise, the action fails. required: true - key: - description: 'An explicit key for saving the cache' - required: true - upload-chunk-size: - description: 'The chunk size used to split up large files during upload, in bytes' - required: false - gc-macos: - description: 'When `true`, enables on `macOS` runners Nix store garbage collection before saving a cache.' - default: 'false' + + + nix: + description: | + - When `true`, the action can do Nix-specific things. + - Otherwise, the action doesn't do them. + default: "true" + + save: + description: | + - When `true`, the action can save a cache with the `primary-key`. + - Otherwise, the action can't save a cache. + default: "true" + + paths: + description: | + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for saving caches. + - Otherwise, the action uses default paths for saving caches. + default: "" + paths-macos: + description: | + - Overrides `paths`. + - Can have an effect only when on a `macOS` runner. + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for saving caches. + - Otherwise, the action uses default paths for saving caches. + default: "" + paths-linux: + description: | + - Overrides `paths`. + - Can have an effect only when on a `Linux` runner. + - When `nix: true`, uses `["/nix", "~/.cache/nix", "~root/.cache/nix"]` as default paths. Otherwise, uses an empty list as default paths. + - When a newline-separated non-empty list of non-empty path patterns (see [`@actions/glob`](https://github.com/actions/toolkit/tree/main/packages/glob) for supported patterns), the action appends it to default paths and uses the resulting list for saving caches. + - Otherwise, the action uses default paths for saving caches. + default: "" + + gc-max-store-size: + description: | + - Can have an effect only when `nix: true`, `save: true`. + - When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache. + - Otherwise, this input has no effect. + default: "" gc-max-store-size-macos: - description: 'Maximum Nix store size in bytes on `macOS` runners. Requires `gc-macos: true`.' - gc-linux: - description: 'When `true`, enables on `Linux` runners Nix store garbage collection before saving a cache.' - default: 'false' + description: | + - Can have an effect only when `nix: true`, `save: true`. + - Overrides `gc-max-store-size`. + - Can have an effect only when on a `macOS` runner. + - When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache. + - Otherwise, this input has no effect. + default: "" gc-max-store-size-linux: - description: 'Maximum Nix store size in bytes on `Linux` runners. Requires `gc-linux: true`.' - + description: | + - Can have an effect only when `nix: true`, `save: true`. + - Overrides `gc-max-store-size`. + - Can have an effect only when on a `Linux` runner. + - When a number, the action collects garbage until Nix store size (in bytes) is at most this number just before the action tries to save a new cache. + - Otherwise, this input has no effect. + default: "" + purge: - description: 'When `true`, purge old caches before saving a new cache with a `key`.' - default: 'false' - purge-keys: - description: 'A newline-separated list of cache key prefixes used to purge caches. An empty string is equivalent to the `key` input.' - required: false - default: '' - purge-accessed: - description: 'When `true`, purge caches by `last accessed` time. Requires `purge: true`.' - default: 'false' - purge-accessed-max-age: - description: 'Purge caches accessed more than this number of seconds ago. Requires `purge-accessed: true`.' - default: '604800' + description: | + - When `true`, the action purges (possibly zero) caches. + - Otherwise, this input has no effect. + default: "false" + purge-overwrite: + description: | + - Can have an effect only when `purge: true`. + - When `always`, the action always purges cache with the `primary-key`. + - When `never`, the action never purges cache with the `primary-key`. + - Otherwise, the action purges old caches using purging criteria. + default: "" + purge-prefixes: + description: | + - Can have an effect only when `purge: true`. + - When a newline-separated non-empty list of non-empty cache key prefixes, the action collects all cache keys that match these prefixes. + - Otherwise, this input has no effect. + default: "" + purge-last-accessed: + description: | + - Can have an effect only when `purge: true`. + - When a number, the action selects for purging caches last accessed more than this number of seconds ago relative to the start of the Save phase. + - Otherwise, this input has no effect. + default: "" purge-created: - description: 'When `true`, purge caches by `created` time. Requires `purge: true`.' - default: 'true' - purge-created-max-age: - description: 'Purge caches created more than this number of seconds ago. Requires `purge-created: true`.' - default: '604800' - + description: | + - Can have an effect only when `purge: true`. + - When a number, the action selects for purging caches created more than this number of seconds ago relative to the start of the Save phase. + - Otherwise, this input has no effect. + default: "" + + upload-chunk-size: + # The original default value may be provided here (https://github.com/actions/cache/issues/1292) + # 32MB + description: | + - When a number, the action uses it as the chunk size (in bytes) to split up large files during upload. + - Otherwise, the action uses the default value `33554432` (32MB). + default: "" + token: - description: 'Used to communicate with GitHub API.' + description: The action uses it to communicate with GitHub API. default: ${{ github.token }} - required: true + runs: - using: 'node16' - main: '../dist/save-only/index.js' + using: "node16" + main: "../dist/save-only/index.js" + branding: - icon: 'archive' - color: 'gray-dark' + icon: "archive" + color: "gray-dark" diff --git a/src/constants.ts b/src/constants.ts index a0811e7c..027baea4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,38 +1,49 @@ export enum Inputs { - Key = "key", // Input for cache, restore, save action - Path = "path", // Input for cache, restore, save action - RestoreKeys = "restore-keys", // Input for cache, restore action - UploadChunkSize = "upload-chunk-size", // Input for cache, save action - FailOnCacheMiss = "fail-on-cache-miss", // Input for cache, restore action + PrimaryKey = "primary-key", // Input for cache, restore, save action - RestoreKeyHit = "restore-key-hit", + RestorePrefixesFirstMatch = "restore-prefixes-first-match", // Input for cache, restore actions + RestorePrefixesAllMatches = "restore-prefixes-all-matches", // Input for cache, restore actions - ExtraRestoreKeys = "extra-restore-keys", // Input for cache, restore action + SkipRestoreOnHitPrimaryKey = "skip-restore-on-primary-key-hit", // Input for cache, restore, actions - GCMacos = "gc-macos", // Input for cache, save action - GCMaxStoreSizeMacos = "gc-max-store-size-macos", // Input for cache, save action - GCLinux = "gc-linux", // Input for cache, save action - GCMaxStoreSizeLinux = "gc-max-store-size-linux", // Input for cache, save action + FailOn = "fail-on", // Input for cache, restore actions - Token = "token", // Input for cache, save action + Nix = "nix", - Purge = "purge", // Input for cache, save action - PurgeKeys = "purge-keys", // Input for cache, save action - PurgeAccessed = "purge-accessed", // Input for cache, save action - PurgeAccessedMaxAge = "purge-accessed-max-age", // Input for cache, save action - PurgeCreated = "purge-created", // Input for cache, save action - PurgeCreatedMaxAge = "purge-created-max-age" // Input for cache, save action + Save = "save", + + Paths = "paths", // Input for cache, save, restore actions + PathsMacos = "paths-macos", // Input for cache, save, restore actions + PathsLinux = "paths-linux", // Input for cache, save, restore actions + + GCMaxStoreSize = "gc-max-store-size", + GCMaxStoreSizeMacos = "gc-max-store-size-macos", // Input for cache, save actions + GCMaxStoreSizeLinux = "gc-max-store-size-linux", // Input for cache, save actions + + Purge = "purge", // Input for cache, save actions + PurgeOverwrite = "purge-overwrite", // Input for cache, save actions + PurgePrefixes = "purge-prefixes", // Input for cache, save actions + PurgeLastAccessed = "purge-last-accessed", // Input for cache, save actions + PurgeCreated = "purge-created", // Input for cache, save actions + + UploadChunkSize = "upload-chunk-size", // Input for cache, save actions + Token = "token" // Input for cache, save actions } export enum Outputs { - CacheHit = "cache-hit", // Output from cache, restore action - CachePrimaryKey = "cache-primary-key", // Output from restore action - CacheMatchedKey = "cache-matched-key" // Output from restore action + PrimaryKey = "primary-key", + + Hit = "hit", // Output from cache, restore actions + HitPrimaryKey = "hit-primary-key", // Output from cache, restore actions + HitFirstMatch = "hit-first-match", // Output from cache, restore actions + + RestoredKey = "restored-key", // Output from cache, restore actions + RestoredKeys = "restored-keys" // Output from cache, restore actions } export enum State { - CachePrimaryKey = "CACHE_KEY", - CacheMatchedKey = "CACHE_RESULT" + CachePrimaryKey = "CACHE_PRIMARY_KEY", + CacheRestoredKey = "CACHE_RESTORED_KEY" } export enum Events { diff --git a/src/gc.ts b/src/gc.ts deleted file mode 100644 index c1069faa..00000000 --- a/src/gc.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { exec } from "@actions/exec"; - -import { Inputs } from "./constants"; -import * as utils from "./utils/actionUtils"; - -export async function collectGarbage() { - utils.info("Collecting garbage"); - - await exec("bash", ["-c", "sudo rm -rf /nix/.[!.]* /nix/..?*"]); - - const gcEnabled = utils.getInputAsBool( - process.platform == "darwin" ? Inputs.GCMacos : Inputs.GCLinux - ); - - if (gcEnabled) { - const maxStoreSize = utils.getInputAsInt( - process.platform == "darwin" - ? Inputs.GCMaxStoreSizeMacos - : Inputs.GCMaxStoreSizeLinux, - { required: true } - ); - - await exec("bash", [ - "-c", - ` - STORE_SIZE="$(nix path-info --json --all | jq 'map(.narSize) | add')" - printf "Current store size in bytes: $STORE_SIZE\\n" - - MAX_STORE_SIZE=${maxStoreSize} - - if (( STORE_SIZE > MAX_STORE_SIZE )); then - (( R1 = STORE_SIZE - MAX_STORE_SIZE )) - (( R2 = R1 > 0 ? R1 : 0 )) - printf "Max bytes to free: $R2\\n" - nix store gc --max "$R2" - fi - ` - ]); - } -} diff --git a/src/inputs.ts b/src/inputs.ts new file mode 100644 index 00000000..cd59d5d3 --- /dev/null +++ b/src/inputs.ts @@ -0,0 +1,97 @@ +import * as core from "@actions/core"; + +import { Inputs } from "./constants"; +import * as utils from "./utils/inputs"; + +export const primaryKey = core.getInput(Inputs.PrimaryKey, { required: true }); + +export const restorePrefixesFirstMatch = utils.getInputAsArray( + Inputs.RestorePrefixesFirstMatch +); +export const restorePrefixesAllMatches = utils.getInputAsArray( + Inputs.RestorePrefixesAllMatches +); + +export const skipRestoreOnHitPrimaryKey = utils.getInputAsBool( + Inputs.SkipRestoreOnHitPrimaryKey +); + +interface FailOn { + keyType: "primary" | "first-match"; + result: "miss" | "not-restored"; +} + +export const failOn: FailOn | undefined = (() => { + const failOnRaw = new RegExp( + "^(primary|first-match)\\.(miss|not-restored)$" + ) + .exec(core.getInput(Inputs.FailOn)) + ?.slice(1); + + if (!failOnRaw) { + return; + } + + const [keyType, result] = failOnRaw; + if ( + (keyType == "primary" || keyType == "first-match") && + (result == "miss" || result == "not-restored") + ) { + return { keyType, result }; + } +})(); + +export const nix = utils.getInputAsBool(Inputs.Nix); + +export const save = utils.getInputAsBool(Inputs.Save); + +export const paths = ( + nix ? ["/nix/", "~/.cache/nix", "~root/.cache/nix"] : [] +).concat( + (() => { + const paths = utils.getInputAsArray(Inputs.Paths); + const pathsPlatform = utils.getInputAsArray( + utils.isLinux ? Inputs.PathsLinux : Inputs.PathsMacos + ); + if (pathsPlatform.length > 0) { + return pathsPlatform; + } else return paths; + })() +); + +export const gcMaxStoreSize = nix + ? (() => { + const gcMaxStoreSize = utils.getInputAsInt(Inputs.GCMaxStoreSize); + const gcMaxStoreSizePlatform = utils.getInputAsInt( + utils.isLinux + ? Inputs.GCMaxStoreSizeLinux + : Inputs.GCMaxStoreSizeMacos + ); + return gcMaxStoreSizePlatform + ? gcMaxStoreSizePlatform + : gcMaxStoreSize; + })() + : undefined; + +export const purge = utils.getInputAsBool(Inputs.Purge); + +export const purgeOverwrite = (() => { + const purgeOverwrite = core.getInput(Inputs.PurgeOverwrite); + + if (!(purgeOverwrite == "always" || purgeOverwrite == "never")) { + return "default"; + } + + return purgeOverwrite; +})(); + +export const purgePrefixes = utils.getInputAsArray(Inputs.PurgePrefixes); + +export const purgeLastAccessed = utils.getInputAsInt(Inputs.PurgeLastAccessed); + +export const purgeCreatedMaxAge = utils.getInputAsInt(Inputs.PurgeCreated); + +export const uploadChunkSize = + utils.getInputAsInt(Inputs.UploadChunkSize) || 32 * 1024 * 1024; + +export const token = core.getInput(Inputs.Token, { required: true }); diff --git a/src/purge.ts b/src/purge.ts deleted file mode 100644 index 7374bd0c..00000000 --- a/src/purge.ts +++ /dev/null @@ -1,160 +0,0 @@ -import * as core from "@actions/core"; -import * as github from "@actions/github"; - -import { Inputs } from "./constants"; -import * as utils from "./utils/actionUtils"; - -async function purgeByTime({ - doUseLastAccessedTime, - keys, - lookupOnly, - time -}: { - doUseLastAccessedTime: boolean; - keys: string[]; - lookupOnly: boolean; - time: number; -}): Promise { - const verb = doUseLastAccessedTime ? "last accessed" : "created"; - - const maxDate = utils.getMaxDate({ doUseLastAccessedTime, time }); - - utils.info( - ` - ${ - lookupOnly ? "Searching for" : "Purging" - } caches ${verb} before ${maxDate.toISOString()} and having keys: - ${utils.stringify(keys)} - ` - ); - - const token = core.getInput(Inputs.Token, { required: true }); - - const caches = utils.filterCachesByTime({ - caches: await utils.getCachesByKeys(token, keys), - doUseLastAccessedTime, - maxDate - }); - - utils.info( - ` - Found ${caches.length} cache(s): - ${utils.stringify(caches)} - ` - ); - - if (lookupOnly) { - return caches; - } - - const octokit = github.getOctokit(token); - - caches.forEach(async cache => { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - const atDatePretty = atDate.toISOString(); - if (atDate < maxDate) { - utils.info( - `Deleting the cache having the key '${cache.key}' and ${verb} at ${atDatePretty}` - ); - - try { - await octokit.rest.actions.deleteActionsCacheById({ - per_page: 100, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - cache_id: cache.id - }); - } catch (error) { - utils.info( - ` - Failed to delete cache ${cache.key} - - ${error} - ` - ); - } - } else { - utils.info( - `Skipping the cache having the key '${cache.key}' and ${verb} at ${atDatePretty}` - ); - } - } - }); - - return []; -} - -async function purge({ - key, - lookupOnly, - time -}: { - key: string; - lookupOnly: boolean; - time: number; -}): Promise { - const accessed = core.getInput(Inputs.PurgeAccessed) === "true"; - - const created = core.getInput(Inputs.PurgeCreated) === "true"; - - let purgeKeys = utils.getInputAsArray(Inputs.PurgeKeys); - - if (purgeKeys.length == 0) { - purgeKeys.push(...[key]); - } - - purgeKeys = purgeKeys.filter(key => key.trim().length > 0); - - const results: utils.Cache[] = []; - - if (accessed || created) { - if (accessed) { - results.push( - ...(await purgeByTime({ - doUseLastAccessedTime: true, - keys: purgeKeys, - lookupOnly, - time - })) - ); - } - if (created) { - results.push( - ...(await purgeByTime({ - doUseLastAccessedTime: false, - keys: purgeKeys, - lookupOnly, - time - })) - ); - } - } else { - core.warning("Either `accessed` or `created` input should be `true`."); - } - - return results; -} - -export async function purgeCaches({ - key, - lookupOnly, - time -}: { - key: string; - lookupOnly: boolean; - time: number; -}): Promise { - const purgeEnabled = utils.getInputAsBool(Inputs.Purge); - - const results: utils.Cache[] = []; - - if (purgeEnabled) { - results.push(...(await purge({ key, lookupOnly, time }))); - } - - return results; -} diff --git a/src/restore.ts b/src/restore.ts index 96a527af..4c53c55b 100644 --- a/src/restore.ts +++ b/src/restore.ts @@ -1,10 +1,3 @@ -import restoreImpl from "./restoreImpl"; -import { StateProvider } from "./stateProvider"; +import { restoreRun } from "./restoreImpl"; -async function run(): Promise { - await restoreImpl(new StateProvider()); -} - -run(); - -export default run; +restoreRun(true); diff --git a/src/restoreExtraCaches.ts b/src/restoreExtraCaches.ts deleted file mode 100644 index 983b21b3..00000000 --- a/src/restoreExtraCaches.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as core from "@actions/core"; - -import { Inputs } from "./constants"; -import * as utils from "./utils/actionUtils"; - -export async function restoreExtraCaches() { - const extraRestoreKeys = utils.getInputAsArray(Inputs.ExtraRestoreKeys); - - if (extraRestoreKeys.length == 0) { - return; - } - - const token = core.getInput(Inputs.Token, { required: true }); - - utils.info( - ` - Restoring extra caches with keys: - ${utils.stringify(extraRestoreKeys)} - ` - ); - - const results = await utils.getCachesByKeys(token, extraRestoreKeys); - - utils.info( - ` - Found ${results.length} cache(s): - ${utils.stringify(results)} - ` - ); - - const cachePaths = utils.paths; - - results.forEach(async cache => { - utils.info(`Restoring a cache with the key ${cache.key}`); - - if (cache.key !== undefined) { - const restoreKey = await utils.getCacheKey({ - paths: cachePaths, - primaryKey: cache.key, - restoreKeys: [], - lookupOnly: false - }); - - if (restoreKey) { - utils.info(`Restored a cache with the key ${cache.key}`); - } - } - }); -} diff --git a/src/restoreImpl.ts b/src/restoreImpl.ts index 899ccc80..da58de8b 100644 --- a/src/restoreImpl.ts +++ b/src/restoreImpl.ts @@ -1,35 +1,26 @@ import * as core from "@actions/core"; import { Events, Inputs, Outputs, State } from "./constants"; -import { restoreExtraCaches } from "./restoreExtraCaches"; -import { IStateProvider } from "./stateProvider"; -import * as utils from "./utils/actionUtils"; - -export async function restoreWithKey(key: string, paths: string[]) { - utils.info(`Restoring a cache with the key "${key}".`); - - utils.info( - `::group::Logs are hidden. Errors are due to attempts to overwrite read-only paths.` - ); - - await utils.getCacheKey({ - paths, - primaryKey: key, - restoreKeys: [], - lookupOnly: false - }); - - utils.info(`::endgroup::`); - - utils.info(`Finished restoring the cache.`); -} - -async function restoreImpl( +import * as inputs from "./inputs"; +import { + IStateProvider, + NullStateProvider, + StateProvider +} from "./stateProvider"; +import * as utils from "./utils/action"; +import * as restore from "./utils/restore"; + +export async function restoreImpl( stateProvider: IStateProvider ): Promise { try { + core.setOutput(Outputs.Hit, false); + core.setOutput(Outputs.HitPrimaryKey, false); + core.setOutput(Outputs.HitFirstMatch, false); + core.setOutput(Outputs.RestoredKey, false); + core.setOutput(Outputs.RestoredKeys, []); + if (!utils.isCacheFeatureAvailable()) { - core.setOutput(Outputs.CacheHit, "false"); return; } @@ -42,90 +33,162 @@ async function restoreImpl( ); } - const primaryKey = core.getInput(Inputs.Key, { required: true }); - stateProvider.setState(State.CachePrimaryKey, primaryKey); - - const restoreKeys = utils.getInputAsArray(Inputs.RestoreKeys); - const cachePaths = utils.paths; - const failOnCacheMiss = utils.getInputAsBool(Inputs.FailOnCacheMiss); - const restoreKeyHit = utils.getInputAsBool(Inputs.RestoreKeyHit); - - utils.info(`Searching for a cache with the key "${primaryKey}".`); + let restoredKey: string | undefined; + let lookedUpKey: string | undefined; + const restoredKeys: string[] = []; - let cacheKey = await utils.getCacheKey({ - paths: cachePaths, - primaryKey, - restoreKeys: [], - lookupOnly: true - }); - - if (cacheKey) { - await restoreWithKey(cacheKey, cachePaths); - } else { - utils.info( + const errorNot = (message: string) => + new Error( ` - No cache with the given primary key found. - Searching for a cache using restore keys: - ${JSON.stringify(restoreKeys)} + No cache with the given key ${message}. + Exiting as the input "${Inputs.FailOn}" is set. ` ); - const restoreKey = await utils.getCacheKey({ - paths: cachePaths, - primaryKey: "", - restoreKeys, + const errorNotFound = errorNot("was found"); + const errorNotRestored = errorNot("could be restored"); + + { + const primaryKey = inputs.primaryKey; + stateProvider.setState(State.CachePrimaryKey, primaryKey); + + utils.info(`Searching for a cache with the key "${primaryKey}".`); + lookedUpKey = await utils.restoreCache({ + primaryKey, + restoreKeys: [], lookupOnly: true }); - if (restoreKey) { - await restoreWithKey(restoreKey, cachePaths); + if (!lookedUpKey) { + if ( + inputs.failOn?.keyType == "primary" && + inputs.failOn?.result == "miss" + ) { + throw errorNotFound; + } else { + utils.info(`Could not find a cache.`); + } } - if (restoreKeyHit) { - cacheKey = restoreKey; + if (lookedUpKey && utils.isExactKeyMatch(primaryKey, lookedUpKey)) { + utils.info( + `Found a cache with the given "${Inputs.PrimaryKey}".` + ); + core.setOutput(Outputs.HitPrimaryKey, true); + + if (!inputs.skipRestoreOnHitPrimaryKey) { + restoredKey = await restore.restoreCache(primaryKey); + if (restoredKey) { + restoredKeys.push(...[restoredKey]); + } else if ( + inputs.failOn?.keyType == "primary" && + inputs.failOn?.result == "not-restored" + ) { + throw errorNotRestored; + } + } } } - if (!cacheKey) { - if (failOnCacheMiss) { - throw new Error( - ` - Failed to restore a cache with the key "${primaryKey}". - Exiting as ${Inputs.FailOnCacheMiss} is set. - ` - ); - } + if ( + inputs.restorePrefixesFirstMatch.length > 0 && + !restoredKey && + !(inputs.skipRestoreOnHitPrimaryKey && lookedUpKey) + ) { utils.info( ` - Cache not found for input keys: - ${utils.stringify([primaryKey, ...restoreKeys])} + Searching for a cache using the "${ + Inputs.RestorePrefixesFirstMatch + }": + ${JSON.stringify(inputs.restorePrefixesFirstMatch)} ` ); - await restoreExtraCaches(); + const foundKey = await utils.restoreCache({ + primaryKey: "", + restoreKeys: inputs.restorePrefixesFirstMatch, + lookupOnly: true + }); + + if (!foundKey) { + if ( + inputs.failOn?.keyType == "first-match" && + inputs.failOn.result == "miss" + ) { + throw errorNotFound; + } else { + utils.info(`Could not find a cache.`); + } + } - return; + if (foundKey) { + utils.info( + `Found a cache using the "${Inputs.RestorePrefixesFirstMatch}".` + ); + core.setOutput(Outputs.HitFirstMatch, true); + + restoredKey = await restore.restoreCache(foundKey); + if (restoredKey) { + restoredKeys.push(...[restoredKey]); + } else if ( + inputs.failOn?.keyType == "first-match" && + inputs.failOn?.result == "not-restored" + ) { + throw errorNotRestored; + } + } } - // Store the matched cache key in states - stateProvider.setState(State.CacheMatchedKey, cacheKey); - - const isExactKeyMatch = - utils.isExactKeyMatch( - core.getInput(Inputs.Key, { required: true }), - cacheKey - ) || restoreKeyHit; + if (!(inputs.skipRestoreOnHitPrimaryKey && lookedUpKey)) { + restoredKeys.push(...(await restore.restoreCaches())); + } - core.setOutput(Outputs.CacheHit, isExactKeyMatch.toString()); + restoredKey ||= ""; - utils.info(`Cache restored from key: "${cacheKey}"`); + // Store the matched cache key in states + stateProvider.setState(State.CacheRestoredKey, restoredKey); - await restoreExtraCaches(); + core.setOutput(Outputs.Hit, true); + core.setOutput(Outputs.RestoredKey, restoredKey); + core.setOutput(Outputs.RestoredKeys, restoredKeys); - return cacheKey; + return restoredKey; } catch (error: unknown) { core.setFailed((error as Error).message); } } -export default restoreImpl; +async function run( + stateProvider: IStateProvider, + earlyExit: boolean | undefined +): Promise { + try { + await restoreImpl(stateProvider); + } catch (err) { + console.error(err); + if (earlyExit) { + process.exit(1); + } + } + + // node will stay alive if any promises are not resolved, + // which is a possibility if HTTP requests are dangling + // due to retries or timeouts. We know that if we got here + // that all promises that we care about have successfully + // resolved, so simply exit with success. + if (earlyExit) { + process.exit(0); + } +} + +export async function restoreOnlyRun( + earlyExit?: boolean | undefined +): Promise { + await run(new NullStateProvider(), earlyExit); +} + +export async function restoreRun( + earlyExit?: boolean | undefined +): Promise { + await run(new StateProvider(), earlyExit); +} diff --git a/src/restoreOnly.ts b/src/restoreOnly.ts index af93e6dc..c773a4cf 100644 --- a/src/restoreOnly.ts +++ b/src/restoreOnly.ts @@ -1,10 +1,3 @@ -import restoreImpl from "./restoreImpl"; -import { NullStateProvider } from "./stateProvider"; +import { restoreOnlyRun } from "./restoreImpl"; -async function run(): Promise { - await restoreImpl(new NullStateProvider()); -} - -run(); - -export default run; +restoreOnlyRun(true); diff --git a/src/saveImpl.ts b/src/saveImpl.ts index 28a2f739..a4672831 100644 --- a/src/saveImpl.ts +++ b/src/saveImpl.ts @@ -1,13 +1,12 @@ -import * as cache from "@actions/cache"; +import * as cache from "@actions/cache/src/cache"; import * as core from "@actions/core"; -import { getOctokit } from "@actions/github"; -import * as github from "@actions/github"; import { Events, Inputs, State } from "./constants"; -import { collectGarbage } from "./gc"; -import { purgeCaches } from "./purge"; +import * as inputs from "./inputs"; import { type IStateProvider } from "./stateProvider"; -import * as utils from "./utils/actionUtils"; +import * as utils from "./utils/action"; +import { collectGarbage } from "./utils/collectGarbage"; +import { purgeCacheByKey, purgeCachesByTime } from "./utils/purge"; // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to @@ -15,7 +14,8 @@ import * as utils from "./utils/actionUtils"; process.on("uncaughtException", e => utils.logError(e.message)); async function saveImpl(stateProvider: IStateProvider): Promise { - let cacheId = -1; + const cacheId = -1; + const time = Date.now(); try { if (!utils.isCacheFeatureAvailable()) { return; @@ -32,78 +32,67 @@ async function saveImpl(stateProvider: IStateProvider): Promise { // If restore has stored a primary key in state, reuse that // Else re-evaluate from inputs const primaryKey = - stateProvider.getState(State.CachePrimaryKey) || - core.getInput(Inputs.Key); + stateProvider.getState(State.CachePrimaryKey) || inputs.primaryKey; - if (!primaryKey || primaryKey === "") { - throw new Error( - ` - Primary cache key not found. - You may want to specify it in your workflow file. - See "with.key" field at the step where you use this action. - ` - ); + if (inputs.purge) { + if (inputs.purgeOverwrite == "always") { + await purgeCacheByKey( + primaryKey, + `Purging the cache with the key "${primaryKey}" because of "${Inputs.PurgeOverwrite}: always".` + ); + } else { + await purgeCachesByTime({ + primaryKey, + time, + prefixes: [] + }); + } } - const cachePaths = utils.paths; - const restoreKeys = utils.getInputAsArray(Inputs.RestoreKeys); - - const restoredKey = await utils.getCacheKey({ - paths: cachePaths, - primaryKey, - restoreKeys, - lookupOnly: true - }); - - const time = Date.now(); - - if (utils.isExactKeyMatch(primaryKey, restoredKey)) { + // Save a cache using the primary key + { utils.info( - `Cache hit occurred on the primary key "${primaryKey}".` + `Searching for a cache using the primary key "${primaryKey}".` ); - const caches = await purgeCaches({ - key: primaryKey, - lookupOnly: true, - time + const foundKey = await utils.restoreCache({ + primaryKey, + restoreKeys: [], + lookupOnly: true }); - if (caches.map(cache => cache.key).includes(primaryKey)) { - utils.info(`Purging the cache with the key "${primaryKey}".`); - - const token = core.getInput(Inputs.Token, { required: true }); - const octokit = getOctokit(token); - - octokit.rest.actions.deleteActionsCacheByKey({ - per_page: 100, - owner: github.context.repo.owner, - repo: github.context.repo.repo, - key: primaryKey, - ref: github.context.ref - }); - } else { + if (utils.isExactKeyMatch(primaryKey, foundKey)) { utils.info( - `The cache with the key "${primaryKey}" won't be purged. Not saving a new cache.` + ` + Cache hit occurred on the "${Inputs.PrimaryKey}". + Not saving a new cache. + ` ); + } else if (inputs.save) { + await collectGarbage(); - await purgeCaches({ key: primaryKey, lookupOnly: false, time }); + utils.info(`Saving a new cache with the key "${primaryKey}".`); - return; + // can throw + await cache.saveCache(inputs.paths, primaryKey, { + uploadChunkSize: inputs.uploadChunkSize + }); + + utils.info(`Saved a new cache.`); + } else { + `Not saving a new cache because of "${Inputs.Save}: false"`; } } - await collectGarbage(); - - utils.info(`Saving a new cache with the key "${primaryKey}".`); - - cacheId = await cache.saveCache(cachePaths, primaryKey, { - uploadChunkSize: utils.getInputAsInt(Inputs.UploadChunkSize) - }); - - if (cacheId != -1) { - utils.info(`Cache saved with the key "${primaryKey}".`); - - await purgeCaches({ key: primaryKey, lookupOnly: false, time }); + // Purge other caches + // This runs last so that in case of cache saving errors + // the action can be re-run with other caches + if (inputs.purge) { + await purgeCachesByTime({ + primaryKey, + time, + prefixes: inputs.purgePrefixes + }); } } catch (error: unknown) { core.setFailed((error as Error).message); diff --git a/src/stateProvider.ts b/src/stateProvider.ts index beb41e5a..a7b66e3d 100644 --- a/src/stateProvider.ts +++ b/src/stateProvider.ts @@ -11,7 +11,7 @@ export interface IStateProvider { class StateProviderBase implements IStateProvider { getCacheState(): string | undefined { - const cacheKey = this.getState(State.CacheMatchedKey); + const cacheKey = this.getState(State.CacheRestoredKey); if (cacheKey) { core.debug(`Cache state/key: ${cacheKey}`); return cacheKey; @@ -34,8 +34,8 @@ export class StateProvider extends StateProviderBase { export class NullStateProvider extends StateProviderBase { stateToOutputMap = new Map([ - [State.CacheMatchedKey, Outputs.CacheMatchedKey], - [State.CachePrimaryKey, Outputs.CachePrimaryKey] + [State.CacheRestoredKey, Outputs.RestoredKey], + [State.CachePrimaryKey, Outputs.PrimaryKey] ]); setState = (key: string, value: string) => { diff --git a/src/utils/actionUtils.ts b/src/utils/action.ts similarity index 61% rename from src/utils/actionUtils.ts rename to src/utils/action.ts index dec4de38..c86017b7 100644 --- a/src/utils/actionUtils.ts +++ b/src/utils/action.ts @@ -1,9 +1,10 @@ -import * as cache from "@actions/cache"; +import * as cache from "@actions/cache/src/cache"; import * as core from "@actions/core"; import * as github from "@actions/github"; import dedent from "dedent"; import { Inputs, RefKey } from "../constants"; +import * as inputs from "../inputs"; export function isGhes(): boolean { const ghUrl = new URL( @@ -23,7 +24,7 @@ export function isExactKeyMatch(key: string, cacheKey?: string): boolean { export function logWarning(message: string): void { const warningPrefix = "[warning]"; - core.info(`${warningPrefix}${message}`); + core.warning(`${warningPrefix} ${message}`); } export function logError(message: string): void { @@ -32,80 +33,50 @@ export function logError(message: string): void { } // Cache token authorized for all events that are tied to a ref -// See GitHub Context https://help.github.com/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#github-context +// See GitHub Context https://docs.github.com/en/actions/learn-github-actions/contexts export function isValidEvent(): boolean { return RefKey in process.env && Boolean(process.env[RefKey]); } -export function getInputAsArray( - name: string, - options?: core.InputOptions -): string[] { - return core - .getInput(name, options) - .split("\n") - .map(s => s.replace(/^!\s+/, "!").trim()) - .filter(x => x !== ""); -} - -export function getInputAsInt( - name: string, - options?: core.InputOptions -): number | undefined { - const value = parseInt(core.getInput(name, options)); - if (isNaN(value) || value < 0) { - return undefined; - } - return value; -} - -export function getInputAsBool( - name: string, - options?: core.InputOptions -): boolean { - const result = core.getInput(name, options); - return result.toLowerCase() === "true"; -} - export function isCacheFeatureAvailable(): boolean { if (cache.isFeatureAvailable()) { return true; } if (isGhes()) { - logWarning( + logError( `Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not. Otherwise please upgrade to GHES version >= 3.5 and If you are also using Github Connect, please unretire the actions/cache namespace before upgrade (see https://docs.github.com/en/enterprise-server@3.5/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)` ); return false; } - logWarning( - "An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions." + logError( + ` + Actions cache service is unavailable. + Please check https://www.githubstatus.com/ for any ongoing issue in Actions. + ` ); return false; } -export const paths = ["/nix/", "~/.cache/nix", "~root/.cache/nix"]; - -export async function getCacheKey({ - paths, +export async function restoreCache({ primaryKey, restoreKeys, lookupOnly }: { - paths: string[]; primaryKey: string; restoreKeys: string[]; lookupOnly: boolean; }) { return await cache.restoreCache( // https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 - paths.slice(), + inputs.paths.slice(), primaryKey, restoreKeys, { lookupOnly }, - false + false, + ["--skip-old-files"] ); } @@ -119,10 +90,10 @@ export interface Cache { size_in_bytes?: number | undefined; } -export async function getCachesByKeys(token: string, keys: string[]) { +export async function getCachesByKeys(keys: string[]) { const caches: Cache[] = []; - const octokit = github.getOctokit(token); + const octokit = github.getOctokit(inputs.token); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; @@ -141,34 +112,17 @@ export async function getCachesByKeys(token: string, keys: string[]) { break; } - caches.push(...cachesRequest.actions_caches); + if (isExactKeyMatch(inputs.primaryKey, key)) { + caches.push(...cachesRequest.actions_caches); + } } } return caches; } -export const filterCachesByTime = ({ - caches, - doUseLastAccessedTime, - maxDate -}: { - caches: Cache[]; - doUseLastAccessedTime: boolean; - maxDate: Date; -}) => - caches.filter(cache => { - const at = doUseLastAccessedTime - ? cache.last_accessed_at - : cache.created_at; - if (at !== undefined && cache.id !== undefined) { - const atDate = new Date(at); - return atDate < maxDate; - } else return false; - }); - export const mkMessageWrongValue = (input: string, value: string) => - `Wrong value for the input '${input}': ${value}`; + `Wrong value for the input "${input}": ${value}`; export function getMaxDate({ doUseLastAccessedTime, @@ -178,10 +132,10 @@ export function getMaxDate({ time: number; }) { const inputMaxAge = doUseLastAccessedTime - ? Inputs.PurgeAccessedMaxAge - : Inputs.PurgeCreatedMaxAge; + ? Inputs.PurgeLastAccessed + : Inputs.PurgeCreated; - const maxAge = core.getInput(inputMaxAge, { required: false }); + const maxAge = core.getInput(inputMaxAge); const maxDate = new Date(time - Number.parseInt(maxAge) * 1000); @@ -194,6 +148,8 @@ export function getMaxDate({ export const stringify = (value: any) => JSON.stringify(value, null, 2); -export const info = (message: string) => { - core.info(dedent.withOptions({})(message)); -}; +const myDedent = dedent.withOptions({}); + +export const info = (message: string) => core.info(myDedent(message)); + +export const warning = (message: string) => core.warning(myDedent(message)); diff --git a/src/utils/collectGarbage.ts b/src/utils/collectGarbage.ts new file mode 100644 index 00000000..320c8e4f --- /dev/null +++ b/src/utils/collectGarbage.ts @@ -0,0 +1,39 @@ +import { exec } from "@actions/exec"; + +import * as inputs from "../inputs"; +import * as utils from "./action"; + +export async function collectGarbage() { + if (inputs.gcMaxStoreSize) { + utils.info("Collecting garbage."); + + const printStoreSize = ` + STORE_SIZE="$(nix path-info --json --all | jq 'map(.narSize) | add')" + printf "Current store size in bytes: $STORE_SIZE\\n" + `; + + const run = async (command: string) => + await exec("bash", ["-c", command]); + + await run(printStoreSize); + + await run( + ` + sudo rm -rf /nix/.[!.]* /nix/..?* + + MAX_STORE_SIZE=${inputs.gcMaxStoreSize} + + if (( STORE_SIZE > MAX_STORE_SIZE )); then + (( R1 = STORE_SIZE - MAX_STORE_SIZE )) + (( R2 = R1 > 0 ? R1 : 0 )) + printf "Max bytes to free: $R2\\n" + nix store gc --max "$R2" + fi + ` + ); + + utils.info(`Finished collecting garbage.`); + + await run(printStoreSize); + } +} diff --git a/src/utils/inputs.ts b/src/utils/inputs.ts new file mode 100644 index 00000000..751359a2 --- /dev/null +++ b/src/utils/inputs.ts @@ -0,0 +1,33 @@ +import * as core from "@actions/core"; + +export function getInputAsArray( + name: string, + options?: core.InputOptions +): string[] { + return core + .getInput(name, options) + .split("\n") + .map(s => s.replace(/^!\s+/, "!").trim()) + .filter(x => x !== ""); +} + +export function getInputAsInt( + name: string, + options?: core.InputOptions +): number | undefined { + const value = parseInt(core.getInput(name, options)); + if (isNaN(value) || value < 0) { + return undefined; + } + return value; +} + +export function getInputAsBool( + name: string, + options?: core.InputOptions +): boolean { + const result = core.getInput(name, options); + return result.toLowerCase() === "true"; +} + +export const isLinux = process.platform === "linux"; diff --git a/src/utils/purge.ts b/src/utils/purge.ts new file mode 100644 index 00000000..7b210600 --- /dev/null +++ b/src/utils/purge.ts @@ -0,0 +1,145 @@ +import * as github from "@actions/github"; + +import { Inputs } from "../constants"; +import * as inputs from "../inputs"; +import * as utils from "./action"; + +export async function purgeCacheByKey(key: string, message?: string) { + try { + utils.info(message || ""); + + const octokit = github.getOctokit(inputs.token); + + await octokit.rest.actions.deleteActionsCacheByKey({ + per_page: 100, + owner: github.context.repo.owner, + repo: github.context.repo.repo, + key, + ref: github.context.ref + }); + } catch (error) { + utils.info( + ` + Failed to delete the cache. + + ${error} + ` + ); + } +} + +export const filterCachesByTime = ({ + caches, + doUseLastAccessedTime, + maxDate +}: { + caches: utils.Cache[]; + doUseLastAccessedTime: boolean; + maxDate: Date; +}) => + caches.filter(cache => { + const at = doUseLastAccessedTime + ? cache.last_accessed_at + : cache.created_at; + if (at && cache.key) { + const atDate = new Date(at); + return atDate < maxDate; + } else return false; + }); + +async function purgeByTime({ + primaryKey, + doUseLastAccessedTime, + prefixes, + time +}: { + primaryKey: string; + doUseLastAccessedTime: boolean; + prefixes: string[]; + time: number; +}): Promise { + const verb = doUseLastAccessedTime ? "last accessed" : "created"; + + const maxDate = utils.getMaxDate({ doUseLastAccessedTime, time }); + + let caches: utils.Cache[] = []; + + if (prefixes.length > 0) { + utils.info( + ` + Purging cache(s) ${verb} before ${maxDate.toISOString()} and having key prefixes: + + ${utils.stringify(prefixes)} + ` + ); + + caches = filterCachesByTime({ + caches: await utils.getCachesByKeys(prefixes), + doUseLastAccessedTime, + maxDate + }); + } else { + utils.info( + `Purging cache(s) ${verb} before ${maxDate.toISOString()} and having the key "${primaryKey}".` + ); + + caches = filterCachesByTime({ + caches: await utils.getCachesByKeys([primaryKey]), + doUseLastAccessedTime, + maxDate + }).filter(x => utils.isExactKeyMatch(primaryKey, x.key)); + } + + if (inputs.purgeOverwrite == "never") { + `The cache with the key "${primaryKey}" will be skipped because of "${Inputs.PurgeOverwrite}: never".`; + + caches.filter(x => !utils.isExactKeyMatch(primaryKey, x.key)); + } + + utils.info( + ` + Found ${caches.length} cache(s): + + ${utils.stringify(caches)} + ` + ); + + for (const cache of caches) { + const at = doUseLastAccessedTime + ? cache.last_accessed_at + : cache.created_at; + if (at && cache.key) { + const atDate = new Date(at); + const atDatePretty = atDate.toISOString(); + await purgeCacheByKey( + cache.key, + `Purging the cache ${verb} at ${atDatePretty} and having the key "${cache.key}".` + ); + } + } + + utils.info(`Finished purging cache(s).`); +} + +export async function purgeCachesByTime({ + primaryKey, + time, + prefixes +}: { + primaryKey: string; + time: number; + prefixes: string[]; +}): Promise { + // TODO https://github.com/actions/toolkit/pull/1378#issuecomment-1478388929 + + for (const flag of [true, false]) { + if (flag ? inputs.purgeLastAccessed : inputs.purgeCreatedMaxAge) { + await purgeByTime({ + primaryKey, + doUseLastAccessedTime: flag, + prefixes: prefixes.slice(), + time + }); + } + } +} diff --git a/src/utils/restore.ts b/src/utils/restore.ts new file mode 100644 index 00000000..52ab8e96 --- /dev/null +++ b/src/utils/restore.ts @@ -0,0 +1,59 @@ +import { Inputs } from "../constants"; +import * as inputs from "../inputs"; +import * as utils from "./action"; + +export async function restoreCache(key: string) { + utils.info(`Restoring a cache with the key "${key}".`); + + const cacheKey = await utils.restoreCache({ + primaryKey: key, + restoreKeys: [], + lookupOnly: false + }); + + if (cacheKey) { + utils.info(`Finished restoring the cache.`); + return cacheKey; + } else { + utils.info(`Failed to restore the cache.`); + } +} + +export async function restoreCaches() { + const restoredCaches: string[] = []; + + if (inputs.restorePrefixesAllMatches.length == 0) { + return restoredCaches; + } + + utils.info( + ` + Searching for caches using the "${Inputs.RestorePrefixesAllMatches}": + + ${utils.stringify(inputs.restorePrefixesAllMatches)} + ` + ); + + const caches = await utils.getCachesByKeys( + inputs.restorePrefixesAllMatches + ); + + utils.info( + ` + Found ${caches.length} cache(s): + + ${utils.stringify(caches)} + ` + ); + + for (const cache of caches) { + if (cache.key) { + const cacheKey = await restoreCache(cache.key); + if (cacheKey) { + restoredCaches.push(...[cacheKey]); + } + } + } + + return restoredCaches; +} diff --git a/src/utils/testUtils.ts b/src/utils/testUtils.ts deleted file mode 100644 index 72570476..00000000 --- a/src/utils/testUtils.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Inputs } from "../constants"; - -// See: https://github.com/actions/toolkit/blob/master/packages/core/src/core.ts#L67 -function getInputName(name: string): string { - return `INPUT_${name.replace(/ /g, "_").toUpperCase()}`; -} - -export function setInput(name: string, value: string): void { - process.env[getInputName(name)] = value; -} - -interface CacheInput { - path: string; - key: string; - restoreKeys?: string[]; - enableCrossOsArchive?: boolean; - failOnCacheMiss?: boolean; - lookupOnly?: boolean; -} - -export function setInputs(input: CacheInput): void { - // setInput(Inputs.Path, input.path); - // setInput(Inputs.Key, input.key); - // input.restoreKeys && - // setInput(Inputs.RestoreKeys, input.restoreKeys.join("\n")); - // input.enableCrossOsArchive !== undefined && - // setInput( - // Inputs.EnableCrossOsArchive, - // input.enableCrossOsArchive.toString() - // ); - // input.failOnCacheMiss !== undefined && - // setInput(Inputs.FailOnCacheMiss, input.failOnCacheMiss.toString()); - // input.lookupOnly !== undefined && - // setInput(Inputs.LookupOnly, input.lookupOnly.toString()); -} - -export function clearInputs(): void { - // delete process.env[getInputName(Inputs.Path)]; - // delete process.env[getInputName(Inputs.Key)]; - // delete process.env[getInputName(Inputs.RestoreKeys)]; - // delete process.env[getInputName(Inputs.UploadChunkSize)]; - // delete process.env[getInputName(Inputs.EnableCrossOsArchive)]; - // delete process.env[getInputName(Inputs.FailOnCacheMiss)]; - // delete process.env[getInputName(Inputs.LookupOnly)]; -} diff --git a/tips-and-workarounds.md b/tips-and-workarounds.md index 125fa5fd..ebbb6cc2 100644 --- a/tips-and-workarounds.md +++ b/tips-and-workarounds.md @@ -26,15 +26,6 @@ A cache today is immutable and cannot be updated. But some use cases require the Reusing cache across feature branches is not allowed today to provide cache [isolation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache). However if both feature branches are from the default branch, a good way to achieve this is to ensure that the default branch has a cache. This cache will then be consumable by both feature branches. -## Cross OS cache - -From `v3.2.3` cache is cross-os compatible when `enableCrossOsArchive` input is passed as true. This means that a cache created on `ubuntu-latest` or `mac-latest` can be used by `windows-latest` and vice versa, provided the workflow which runs on `windows-latest` have input `enableCrossOsArchive` as true. This is useful to cache dependencies which are independent of the runner platform. This will help reduce the consumption of the cache quota and help build for multiple platforms from the same cache. Things to keep in mind while using this feature: - -- Only cache files that are compatible across OSs. -- Caching symlinks might cause issues while restoring them as they behave differently on different OSs. -- Be mindful when caching files from outside your github workspace directory as the directory is located at different places across OS. -- Avoid using directory pointers such as `${{ github.workspace }}` or `~` (home) which eventually evaluate to an absolute path that does not match across OSs. - ## Force deletion of caches overriding default cache eviction policy Caches have [branch scope restriction](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache) in place. This means that if caches for a specific branch are using a lot of storage quota, it may result into more frequently used caches from `default` branch getting thrashed. For example, if there are many pull requests happening on a repo and are creating caches, these cannot be used in default branch scope but will still occupy a lot of space till they get cleaned up by [eviction policy](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy). But sometime we want to clean them up on a faster cadence so as to ensure default branch is not thrashing. In order to achieve this, [gh-actions-cache cli](https://github.com/actions/gh-actions-cache/) can be used to delete caches for specific branches. diff --git a/tsconfig.json b/tsconfig.json index fb00af57..29dfd1b1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./lib", /* Redirect output structure to the directory. */ - "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */