diff --git a/packages/fs/.chglog/CHANGELOG.tpl.md b/packages/fs/.chglog/CHANGELOG.tpl.md new file mode 100755 index 00000000000..831ba67ac40 --- /dev/null +++ b/packages/fs/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,239 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +{{ if .Versions -}} +A list of unreleased changes can be found [here]({{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD). +{{ end -}} + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +### Reverts +{{ range .RevertCommits -}} +- {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + + +## [v3.0.5] - 2023-10-09 + + +## [v3.0.4] - 2023-05-23 +### Bug Fixes +- Do not prefix excludes when creating a reader ([#504](https://github.com/SAP/ui5-fs/issues/504)) [`f765894`](https://github.com/SAP/ui5-fs/commit/f765894df3854242dc308b32581e3d234bbcf27e) + + + +## [v3.0.3] - 2023-04-21 +### Dependency Updates +- Bump minimatch from 8.0.4 to 9.0.0 ([#497](https://github.com/SAP/ui5-fs/issues/497)) [`56d4542`](https://github.com/SAP/ui5-fs/commit/56d45423458564db7c2bdf7d1cdbd174c2057232) +- Bump minimatch from 7.4.4 to 8.0.2 [`a53a4e3`](https://github.com/SAP/ui5-fs/commit/a53a4e3e531cc880de29a4e8176844dd2c743196) + + + +## [v3.0.2] - 2023-03-01 +### Dependency Updates +- Bump minimatch from 6.2.0 to 7.2.0 [`d2c37d4`](https://github.com/SAP/ui5-fs/commit/d2c37d4be518b4cc812cb55b9a431796010725dc) + + + +## [v3.0.1] - 2023-02-16 +### Dependency Updates +- Bump minimatch from 6.1.8 to 6.2.0 [`20e9311`](https://github.com/SAP/ui5-fs/commit/20e931149ce87d374e607f0f4e4357ae0abe3f97) + + + +## [v3.0.0] - 2023-02-09 +### Breaking Changes +- Transform to ES Modules ([#398](https://github.com/SAP/ui5-fs/issues/398)) [`2b61580`](https://github.com/SAP/ui5-fs/commit/2b615807a610dd7dfeb5423496ec7aebc9169011) +- Throw an error on write of a resource when path does not starts with virBasePath of the respective adapter ([#453](https://github.com/SAP/ui5-fs/issues/453)) [`d76575f`](https://github.com/SAP/ui5-fs/commit/d76575f8f05a0b6695285200ba595e532620daed) +- Clone resources when writing in and reading from the Memory ([#448](https://github.com/SAP/ui5-fs/issues/448)) [`3454bc1`](https://github.com/SAP/ui5-fs/commit/3454bc15be8a6ecd455b49607cb289e69b41d0f0) +- **AbstractAdapter:** Virtual base path must end with slash [`6d1f411`](https://github.com/SAP/ui5-fs/commit/6d1f4117a2b8bb1226540fafeec8341e4966177d) +- **resourceFactory:** Remove #createCollectionsForTree [`a4d15f6`](https://github.com/SAP/ui5-fs/commit/a4d15f61ae0416051658280bfd2f8635c7ddf44e) +- Require Node.js >= 16.18.0 / npm >= 8 [`94029de`](https://github.com/SAP/ui5-fs/commit/94029deea4e85211fb8d84f7b213f1fd0c418240) + +### BREAKING CHANGE +An error is thrown when a resource shall be written to an adapter where the path of the resource does not starts with the virtual base path defined in the adapter. + +Resources stored in the adapters can not be modified by reference anymore. All modifications need to be persisted by using the #write method in order to be reflected in the adapter. + +This package has been transformed to ES Modules. Therefore it no longer provides a CommonJS export. +If your project uses CommonJS, it needs to be converted to ES Modules or use a dynamic import. + +For more information see also: + +- https://sap.github.io/ui5-tooling/updates/migrate-v3/ +- https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +Support for older Node.js and npm releases has been dropped. +Only Node.js v16.18.0 and npm v8 or higher are supported. + +### Features +- Add Link-reader and WriterCollection [`a0e5cf3`](https://github.com/SAP/ui5-fs/commit/a0e5cf3ef86a4b0cdc817d08963ed6574740f1bc) +- Introduce Readers "Filter" and "Transformer" ([#331](https://github.com/SAP/ui5-fs/issues/331)) [`f46e6d1`](https://github.com/SAP/ui5-fs/commit/f46e6d1b9aed5858f2d72b10b18635de6ed1f1e0) +- **Resource:** Add isModified method [`f6a590a`](https://github.com/SAP/ui5-fs/commit/f6a590a284a5ef2879d4d755b5b37be164cf3a45) + +### Bug Fixes +- Allow resource migration ([#407](https://github.com/SAP/ui5-fs/issues/407)) [`1722d71`](https://github.com/SAP/ui5-fs/commit/1722d71b78184cae0dfb092fd3d4c4156924dc28) +- **FileSystem Adapter:** Use fs.copy / Skip writing when resource is unchanged ([#370](https://github.com/SAP/ui5-fs/issues/370)) [`9ac6a39`](https://github.com/SAP/ui5-fs/commit/9ac6a39f3cb72e02c2a1298b07c4676a0ee92377) + +### Dependency Updates +- Bump minimatch from 5.1.4 to 6.1.5 [`e6b8d14`](https://github.com/SAP/ui5-fs/commit/e6b8d142517a19b138dab5fc19390ed98db425e1) + + +## [v2.0.6] - 2020-12-18 +### Bug Fixes +- **fsInterface on Windows:** Correctly handle project paths containing non-ASCII characters [`6bb44be`](https://github.com/SAP/ui5-fs/commit/6bb44be7c13f6b5c66855d3343694f42f9bdfe0e) + + +## [v2.0.5] - 2020-11-06 +### Performance Improvements +- Reduce install size by moving 'mock-require' to devDependencies [`173f4ff`](https://github.com/SAP/ui5-fs/commit/173f4ffc55f0e264025c6e2bce12721a6673790a) + + +## [v2.0.4] - 2020-10-22 +### Bug Fixes +- **fsInterface:** Improve error messages [`8b998f8`](https://github.com/SAP/ui5-fs/commit/8b998f8a47e89bce0c27f5a4b211bb5471ea2381) + + +## [v2.0.3] - 2020-08-12 +### Bug Fixes +- **ResourceTagCollection:** Do not validate class of resource [`c52c9f7`](https://github.com/SAP/ui5-fs/commit/c52c9f71e4f868cfa8ae5daf5eede9272654b187) + + +## [v2.0.2] - 2020-07-30 +### Bug Fixes +- TypeScript type definition support ([#252](https://github.com/SAP/ui5-fs/issues/252)) [`f379094`](https://github.com/SAP/ui5-fs/commit/f37909483b2740219da36c7e0931f7824d51e1a3) +- **Resource:** Keep stats size up to date ([#253](https://github.com/SAP/ui5-fs/issues/253)) [`0ef976f`](https://github.com/SAP/ui5-fs/commit/0ef976fe658765ae46afd1b4aa5b9100aa9829f8) +- **Resource:** getStream for empty string ([#249](https://github.com/SAP/ui5-fs/issues/249)) [`bc5eafb`](https://github.com/SAP/ui5-fs/commit/bc5eafb19bf71141b88dff749240354898849a66) +- **Resource#getSize:** Retrieve Resource's size ([#256](https://github.com/SAP/ui5-fs/issues/256)) [`9d2cc6c`](https://github.com/SAP/ui5-fs/commit/9d2cc6cf92b4a3f29e5c42db2f14afeeac9a0c98) +- **adapters/Memory:** Return cloned resources ([#235](https://github.com/SAP/ui5-fs/issues/235)) [`7bf3c6a`](https://github.com/SAP/ui5-fs/commit/7bf3c6a2ce55cd1c48b8aaaed83591bd85c228fa) + +### Reverts +- [FIX] adapters/Memory: Return cloned resources ([#235](https://github.com/SAP/ui5-fs/issues/235)) + + +## [v2.0.1] - 2020-04-30 +### Bug Fixes +- Namespaces in API Reference (JSDoc) [`b9d7b3c`](https://github.com/SAP/ui5-fs/commit/b9d7b3c70679436e6cbea07a789ac5e83bab337a) + + +## [v2.0.0] - 2020-03-31 +### Breaking Changes +- Remove deprecated parameter useNamespaces ([#223](https://github.com/SAP/ui5-fs/issues/223)) [`231b319`](https://github.com/SAP/ui5-fs/commit/231b319ea1899d6297e70e7b7746340cd5824217) +- Require Node.js >= 10 [`46651f1`](https://github.com/SAP/ui5-fs/commit/46651f13bcfe391ef985fabab96f99b6ecafe13e) + +### Dependency Updates +- Bump globby from 10.0.2 to 11.0.0 ([#207](https://github.com/SAP/ui5-fs/issues/207)) [`c81eb9d`](https://github.com/SAP/ui5-fs/commit/c81eb9dffc0d1281640efb2ad7e5419fe5451442) + +### BREAKING CHANGE + +Remove deprecated parameter "useNamespaces" from +method "resourceFactory.createCollectionsForTree". +Use parameter "getVirtualBasePathPrefix" instead. + +Support for older Node.js releases has been dropped. +Only Node.js v10 or higher is supported. + + +## [v1.1.2] - 2019-07-01 +### Dependency Updates +- Bump globby from 9.2.0 to 10.0.0 [`573e853`](https://github.com/SAP/ui5-fs/commit/573e8531827bf1c0320177d3870e37a183db7959) + + +## [v1.1.1] - 2019-06-18 +### Bug Fixes +- **fsInterface:** Handle non-POSIX paths correctly ([#147](https://github.com/SAP/ui5-fs/issues/147)) [`1ad0cc8`](https://github.com/SAP/ui5-fs/commit/1ad0cc8fdbcf74452ebfcebb23bd659faa956e54) + + +## [v1.1.0] - 2019-06-03 +### Features +- **AbstractAdapter:** Add excludes option ([#140](https://github.com/SAP/ui5-fs/issues/140)) [`daef31f`](https://github.com/SAP/ui5-fs/commit/daef31f4fb22405e8fa889615b1f3545099eb186) + + +## [v1.0.2] - 2019-03-21 +### Dependency Updates +- Bump [@ui5](https://github.com/ui5)/logger from 1.0.0 to 1.0.1 ([#122](https://github.com/SAP/ui5-fs/issues/122)) [`e2e2791`](https://github.com/SAP/ui5-fs/commit/e2e27917d936ad5a316c56a0bc0a17d91977d15e) + +### Features +- **fsInterface:** Add mkdir function [`5bd91ac`](https://github.com/SAP/ui5-fs/commit/5bd91acb86d64f03d16abdb186fe66ffa8a9f53a) + + +## [v1.0.1] - 2019-02-01 +### Bug Fixes +- Prevent FS write from draining Resources content [`370f121`](https://github.com/SAP/ui5-fs/commit/370f121ca4d571397c979e2dce72b6a1cf0d0005) + +### Dependency Updates +- **Yarn:** Pin dir-glob dependency to v2.0.0 [`e14457c`](https://github.com/SAP/ui5-fs/commit/e14457c5b3eda1fab3d3444bca3b8406be63db2f) + + +## [v1.0.0] - 2019-01-10 +### Breaking Changes +- **index.js export:** Remove top-level access to adapters [`f1f7831`](https://github.com/SAP/ui5-fs/commit/f1f7831ae9e908731a57f9d67952a61431c69d21) + +### Dependency Updates +- Pin dir-glob to v2.0.0 [`b921fbc`](https://github.com/SAP/ui5-fs/commit/b921fbceaa4f200737b8c6cf45eaf2d9bc3e1df2) +- Bump [@ui5](https://github.com/ui5)/logger from 0.2.2 to 1.0.0 ([#72](https://github.com/SAP/ui5-fs/issues/72)) [`4077f19`](https://github.com/SAP/ui5-fs/commit/4077f19251dee72933de0747de09a6eec5cb75cc) + +### BREAKING CHANGE + +Adapters "AbstractAdapter", "FileSystem" and "Memory" used to be accessible via the top-level export of index.js (example: require("[@ui5](https://github.com/ui5)/project").FileSystem). This is no longer possible. Adapters are now grouped in the top-level object "adapters" and can be accessed from there (example: require("[@ui5](https://github.com/ui5)/project").adapters.FileSystem). + + +## [v0.2.0] - 2018-07-11 + + +## [v0.1.0] - 2018-06-26 + + +## v0.0.1 - 2018-06-06 +### Bug Fixes +- **AbstractAdapter:** Fix normalization of globstar [`6d484e8`](https://github.com/SAP/ui5-fs/commit/6d484e847b62aa0829641f25a76dcc89b0840d44) + +{{- if .Versions }} +{{ range .Versions -}} +{{ if and .Tag.Previous (ne .Tag.Name "v3.0.0") -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} +[v3.0.5]: https://github.com/SAP/ui5-fs/compare/v3.0.4...v3.0.5 +[v3.0.4]: https://github.com/SAP/ui5-fs/compare/v3.0.3...v3.0.4 +[v3.0.3]: https://github.com/SAP/ui5-fs/compare/v3.0.2...v3.0.3 +[v3.0.2]: https://github.com/SAP/ui5-fs/compare/v3.0.1...v3.0.2 +[v3.0.1]: https://github.com/SAP/ui5-fs/compare/v3.0.0...v3.0.1 +[v3.0.0]: https://github.com/SAP/ui5-fs/compare/v2.0.6...v3.0.0 +[v2.0.6]: https://github.com/SAP/ui5-fs/compare/v2.0.5...v2.0.6 +[v2.0.5]: https://github.com/SAP/ui5-fs/compare/v2.0.4...v2.0.5 +[v2.0.4]: https://github.com/SAP/ui5-fs/compare/v2.0.3...v2.0.4 +[v2.0.3]: https://github.com/SAP/ui5-fs/compare/v2.0.2...v2.0.3 +[v2.0.2]: https://github.com/SAP/ui5-fs/compare/v2.0.1...v2.0.2 +[v2.0.1]: https://github.com/SAP/ui5-fs/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/SAP/ui5-fs/compare/v1.1.2...v2.0.0 +[v1.1.2]: https://github.com/SAP/ui5-fs/compare/v1.1.1...v1.1.2 +[v1.1.1]: https://github.com/SAP/ui5-fs/compare/v1.1.0...v1.1.1 +[v1.1.0]: https://github.com/SAP/ui5-fs/compare/v1.0.2...v1.1.0 +[v1.0.2]: https://github.com/SAP/ui5-fs/compare/v1.0.1...v1.0.2 +[v1.0.1]: https://github.com/SAP/ui5-fs/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/SAP/ui5-fs/compare/v0.2.0...v1.0.0 +[v0.2.0]: https://github.com/SAP/ui5-fs/compare/v0.1.0...v0.2.0 +[v0.1.0]: https://github.com/SAP/ui5-fs/compare/v0.0.1...v0.1.0 diff --git a/packages/fs/.chglog/RELEASE.tpl.md b/packages/fs/.chglog/RELEASE.tpl.md new file mode 100755 index 00000000000..efc482c385d --- /dev/null +++ b/packages/fs/.chglog/RELEASE.tpl.md @@ -0,0 +1,33 @@ +{{ range .Versions }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +### Reverts +{{ range .RevertCommits -}} +- {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} + +{{ if .Tag.Previous }} +### All changes +[`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`] +{{ end }} + +{{ if .Tag.Previous -}} +[`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} diff --git a/packages/fs/.chglog/config.yml b/packages/fs/.chglog/config.yml new file mode 100755 index 00000000000..206470c61ab --- /dev/null +++ b/packages/fs/.chglog/config.yml @@ -0,0 +1,33 @@ +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/SAP/ui5-fs +options: + commits: + filters: + Type: + - FEATURE + - FIX + - PERF + - DEPENDENCY + - BREAKING + commit_groups: + title_maps: + FEATURE: Features + FIX: Bug Fixes + PERF: Performance Improvements + DEPENDENCY: Dependency Updates + BREAKING: Breaking Changes + header: + pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" + pattern_maps: + - Type + - Scope + - Subject + issues: + prefix: + - "#" + notes: + keywords: + - BREAKING CHANGE diff --git a/packages/fs/.chglog/release-config.yml b/packages/fs/.chglog/release-config.yml new file mode 100755 index 00000000000..a43eed9db6c --- /dev/null +++ b/packages/fs/.chglog/release-config.yml @@ -0,0 +1,33 @@ +style: github +template: RELEASE.tpl.md +info: + repository_url: https://github.com/SAP/ui5-fs +options: + tag_filter_pattern: '^v[^0123]' # For release notes ignore versions below v4 to that we always compare the _last v4+_ tag with the current release + commits: + filters: + Type: + - FEATURE + - FIX + - PERF + - DEPENDENCY + - BREAKING + commit_groups: + title_maps: + FEATURE: Features + FIX: Bug Fixes + PERF: Performance Improvements + DEPENDENCY: Dependency Updates + BREAKING: Breaking Changes + header: + pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" + pattern_maps: + - Type + - Scope + - Subject + issues: + prefix: + - "#" + notes: + keywords: + - BREAKING CHANGE diff --git a/packages/fs/.editorconfig b/packages/fs/.editorconfig new file mode 100644 index 00000000000..b432804f7fc --- /dev/null +++ b/packages/fs/.editorconfig @@ -0,0 +1,20 @@ +# see http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = tab + +[*.{css,html,js,cjs,mjs,jsx,ts,tsx,less,txt,json,yml,md}] +trim_trailing_whitespace = true +end_of_line = lf +indent_size = 4 +insert_final_newline = true + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/fs/.gitattributes b/packages/fs/.gitattributes new file mode 100644 index 00000000000..6313b56c578 --- /dev/null +++ b/packages/fs/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/packages/fs/.github/ISSUE_TEMPLATE.md b/packages/fs/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..6ddeb2221a8 --- /dev/null +++ b/packages/fs/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,3 @@ +## 🚨 Issues Have Been Transferred to UI5 CLI Repository + +Please create new issues in the UI5 CLI repository: https://github.com/UI5/cli/issues/new/choose diff --git a/packages/fs/.github/ISSUE_TEMPLATE/config.yml b/packages/fs/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..34df09809c9 --- /dev/null +++ b/packages/fs/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Report UI5 CLI Issues or Request a Feature + url: https://github.com/UI5/cli/issues/new/choose + about: Please create new issues in the UI5 CLI repository diff --git a/packages/fs/.github/PULL_REQUEST_TEMPLATE.md b/packages/fs/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..2f043b5c5cc --- /dev/null +++ b/packages/fs/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +**Thank you for your contribution!** 🙌 + +To get it merged faster, kindly review the checklist below: + +## Pull Request Checklist +- [ ] Reviewed the [Contributing Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#-contributing-code) + + Especially the [How to Contribute](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#how-to-contribute) section +- [ ] [No merge commits](https://github.com/UI5/cli/blob/main/docs/Guidelines.md#no-merge-commits) +- [ ] [Correct commit message style](https://github.com/UI5/cli/blob/main/docs/Guidelines.md#commit-message-style) diff --git a/packages/fs/.github/dependabot.yml b/packages/fs/.github/dependabot.yml new file mode 100644 index 00000000000..ebf6ca750f8 --- /dev/null +++ b/packages/fs/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" +- package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + day: sunday + time: "10:00" + timezone: Etc/UCT + reviewers: + - "SAP/ui5-foundation" + versioning-strategy: increase + commit-message: + prefix: "[DEPENDENCY] " + prefix-development: "[INTERNAL] " diff --git a/packages/fs/.github/in-solidarity.yml b/packages/fs/.github/in-solidarity.yml new file mode 100644 index 00000000000..4ce829a6be3 --- /dev/null +++ b/packages/fs/.github/in-solidarity.yml @@ -0,0 +1 @@ +_extends: ietf/terminology diff --git a/packages/fs/.github/workflows/dependabot-auto-merge.yml b/packages/fs/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 00000000000..43d92c94fd6 --- /dev/null +++ b/packages/fs/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,28 @@ +name: Dependabot auto-merge +on: + pull_request: + branches: + - v4 + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' && github.event.pull_request.auto_merge == null }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Approve and auto-merge PRs for minor/patch updates of github-actions + if: | + steps.metadata.outputs.package-ecosystem == 'github_actions' && + contains(fromJSON('["version-update:semver-minor", "version-update:semver-patch"]'), steps.metadata.outputs.update-type) + run: gh pr review --approve "$PR_URL" && gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/packages/fs/.github/workflows/github-ci.yml b/packages/fs/.github/workflows/github-ci.yml new file mode 100644 index 00000000000..f649c335d83 --- /dev/null +++ b/packages/fs/.github/workflows/github-ci.yml @@ -0,0 +1,34 @@ +name: GitHub CI + +on: + push: + branches: + - v4 + pull_request: + branches: + - v4 + +# No permissions are required for this workflow +permissions: {} + +jobs: + test: + name: General checks, tests and coverage reporting + runs-on: ubuntu-24.04 + steps: + + - uses: actions/checkout@v5 + + - name: Use Node.js LTS 20.11.0 + uses: actions/setup-node@v5.0.0 + with: + node-version: 20.11.0 + + - name: Install dependencies + run: npm ci + + - name: Perform checks and tests + run: npm test + + - name: Send report to Coveralls + uses: coverallsapp/github-action@v2.3.6 diff --git a/packages/fs/.github/workflows/reuse-compliance.yml b/packages/fs/.github/workflows/reuse-compliance.yml new file mode 100644 index 00000000000..ee696a44f1b --- /dev/null +++ b/packages/fs/.github/workflows/reuse-compliance.yml @@ -0,0 +1,21 @@ +name: REUSE + +on: + push: + branches: + - v4 + pull_request: + branches: + - v4 + +# No permissions are required for this workflow +permissions: {} + +jobs: + compliance-check: + name: Compliance Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Execute REUSE Compliance Check + uses: fsfe/reuse-action@v5 diff --git a/packages/fs/.gitignore b/packages/fs/.gitignore new file mode 100644 index 00000000000..052409d713d --- /dev/null +++ b/packages/fs/.gitignore @@ -0,0 +1,62 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# CI (Azure Pipelines) xUnit test results +test-results.xml + +# IDEs +.vscode/ +*.~vsdx +.idea/ + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# Misc +yarn.lock +.DS_Store + +# Don't include private SSH key for deployment via Travis CI +deploy_key + +# Custom directories +test/tmp/ +jsdocs/ diff --git a/packages/fs/.npmrc b/packages/fs/.npmrc new file mode 100644 index 00000000000..93ec4f76ba6 --- /dev/null +++ b/packages/fs/.npmrc @@ -0,0 +1,3 @@ +# Enforce public npm registry +registry=https://registry.npmjs.org/ +lockfile-version=3 diff --git a/packages/fs/CHANGELOG.md b/packages/fs/CHANGELOG.md new file mode 100644 index 00000000000..5ca547b1446 --- /dev/null +++ b/packages/fs/CHANGELOG.md @@ -0,0 +1,235 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +A list of unreleased changes can be found [here](https://github.com/SAP/ui5-fs/compare/v4.0.2...HEAD). + + +## [v4.0.2] - 2025-09-11 +### Bug Fixes +- Rename project to UI5 CLI [`e8491c3`](https://github.com/SAP/ui5-fs/commit/e8491c39e879560eaa5056b1ee0e2377a4e88f0d) + + + +## [v4.0.1] - 2024-08-27 + + +## [v4.0.0] - 2024-07-23 +### Breaking Changes +- Drop node v21 support [`2d2f125`](https://github.com/SAP/ui5-fs/commit/2d2f125a6031361172ee820ae5f5abc6a6636f0b) +- Require Node.js 20.11.x/>=21.2.0 and npm >=10 [`b98bd9a`](https://github.com/SAP/ui5-fs/commit/b98bd9ababc1e0b6be8a50a1e97706265a4f805e) +- **DuplexCollection:** remove byGlobSource method ([#552](https://github.com/SAP/ui5-fs/issues/552)) [`e26338b`](https://github.com/SAP/ui5-fs/commit/e26338bf6c8fa3139fc9c06a8dff53d192d8feea) + +### Dependency Updates +- Bump minimatch from 9.0.5 to 10.0.1 [`7de313a`](https://github.com/SAP/ui5-fs/commit/7de313a139de63cef2d43daea01efa4ff7e778f3) +- Bump globby from 13.2.2 to 14.0.1 [`465638d`](https://github.com/SAP/ui5-fs/commit/465638d6d8b30fe8e2588b7b928fb8acd98960f2) + +### BREAKING CHANGE + +`byGlobSource` method is removed from `DuplexCollection` + +JIRA: CPOUI5FOUNDATION-802 +Relates to: https://github.com/SAP/ui5-tooling/issues/701 + +Support for older Node.js and npm releases has been dropped. +Only Node.js 20.11.x and >=21.2.0 as well as npm v10 or higher are supported. + + +## [v3.0.5] - 2023-10-09 + + +## [v3.0.4] - 2023-05-23 +### Bug Fixes +- Do not prefix excludes when creating a reader ([#504](https://github.com/SAP/ui5-fs/issues/504)) [`f765894`](https://github.com/SAP/ui5-fs/commit/f765894df3854242dc308b32581e3d234bbcf27e) + + + +## [v3.0.3] - 2023-04-21 +### Dependency Updates +- Bump minimatch from 8.0.4 to 9.0.0 ([#497](https://github.com/SAP/ui5-fs/issues/497)) [`56d4542`](https://github.com/SAP/ui5-fs/commit/56d45423458564db7c2bdf7d1cdbd174c2057232) +- Bump minimatch from 7.4.4 to 8.0.2 [`a53a4e3`](https://github.com/SAP/ui5-fs/commit/a53a4e3e531cc880de29a4e8176844dd2c743196) + + + +## [v3.0.2] - 2023-03-01 +### Dependency Updates +- Bump minimatch from 6.2.0 to 7.2.0 [`d2c37d4`](https://github.com/SAP/ui5-fs/commit/d2c37d4be518b4cc812cb55b9a431796010725dc) + + + +## [v3.0.1] - 2023-02-16 +### Dependency Updates +- Bump minimatch from 6.1.8 to 6.2.0 [`20e9311`](https://github.com/SAP/ui5-fs/commit/20e931149ce87d374e607f0f4e4357ae0abe3f97) + + + +## [v3.0.0] - 2023-02-09 +### Breaking Changes +- Transform to ES Modules ([#398](https://github.com/SAP/ui5-fs/issues/398)) [`2b61580`](https://github.com/SAP/ui5-fs/commit/2b615807a610dd7dfeb5423496ec7aebc9169011) +- Throw an error on write of a resource when path does not starts with virBasePath of the respective adapter ([#453](https://github.com/SAP/ui5-fs/issues/453)) [`d76575f`](https://github.com/SAP/ui5-fs/commit/d76575f8f05a0b6695285200ba595e532620daed) +- Clone resources when writing in and reading from the Memory ([#448](https://github.com/SAP/ui5-fs/issues/448)) [`3454bc1`](https://github.com/SAP/ui5-fs/commit/3454bc15be8a6ecd455b49607cb289e69b41d0f0) +- **AbstractAdapter:** Virtual base path must end with slash [`6d1f411`](https://github.com/SAP/ui5-fs/commit/6d1f4117a2b8bb1226540fafeec8341e4966177d) +- **resourceFactory:** Remove #createCollectionsForTree [`a4d15f6`](https://github.com/SAP/ui5-fs/commit/a4d15f61ae0416051658280bfd2f8635c7ddf44e) +- Require Node.js >= 16.18.0 / npm >= 8 [`94029de`](https://github.com/SAP/ui5-fs/commit/94029deea4e85211fb8d84f7b213f1fd0c418240) + +### BREAKING CHANGE +An error is thrown when a resource shall be written to an adapter where the path of the resource does not starts with the virtual base path defined in the adapter. + +Resources stored in the adapters can not be modified by reference anymore. All modifications need to be persisted by using the #write method in order to be reflected in the adapter. + +This package has been transformed to ES Modules. Therefore it no longer provides a CommonJS export. +If your project uses CommonJS, it needs to be converted to ES Modules or use a dynamic import. + +For more information see also: + +- https://sap.github.io/ui5-tooling/updates/migrate-v3/ +- https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +Support for older Node.js and npm releases has been dropped. +Only Node.js v16.18.0 and npm v8 or higher are supported. + +### Features +- Add Link-reader and WriterCollection [`a0e5cf3`](https://github.com/SAP/ui5-fs/commit/a0e5cf3ef86a4b0cdc817d08963ed6574740f1bc) +- Introduce Readers "Filter" and "Transformer" ([#331](https://github.com/SAP/ui5-fs/issues/331)) [`f46e6d1`](https://github.com/SAP/ui5-fs/commit/f46e6d1b9aed5858f2d72b10b18635de6ed1f1e0) +- **Resource:** Add isModified method [`f6a590a`](https://github.com/SAP/ui5-fs/commit/f6a590a284a5ef2879d4d755b5b37be164cf3a45) + +### Bug Fixes +- Allow resource migration ([#407](https://github.com/SAP/ui5-fs/issues/407)) [`1722d71`](https://github.com/SAP/ui5-fs/commit/1722d71b78184cae0dfb092fd3d4c4156924dc28) +- **FileSystem Adapter:** Use fs.copy / Skip writing when resource is unchanged ([#370](https://github.com/SAP/ui5-fs/issues/370)) [`9ac6a39`](https://github.com/SAP/ui5-fs/commit/9ac6a39f3cb72e02c2a1298b07c4676a0ee92377) + +### Dependency Updates +- Bump minimatch from 5.1.4 to 6.1.5 [`e6b8d14`](https://github.com/SAP/ui5-fs/commit/e6b8d142517a19b138dab5fc19390ed98db425e1) + + +## [v2.0.6] - 2020-12-18 +### Bug Fixes +- **fsInterface on Windows:** Correctly handle project paths containing non-ASCII characters [`6bb44be`](https://github.com/SAP/ui5-fs/commit/6bb44be7c13f6b5c66855d3343694f42f9bdfe0e) + + +## [v2.0.5] - 2020-11-06 +### Performance Improvements +- Reduce install size by moving 'mock-require' to devDependencies [`173f4ff`](https://github.com/SAP/ui5-fs/commit/173f4ffc55f0e264025c6e2bce12721a6673790a) + + +## [v2.0.4] - 2020-10-22 +### Bug Fixes +- **fsInterface:** Improve error messages [`8b998f8`](https://github.com/SAP/ui5-fs/commit/8b998f8a47e89bce0c27f5a4b211bb5471ea2381) + + +## [v2.0.3] - 2020-08-12 +### Bug Fixes +- **ResourceTagCollection:** Do not validate class of resource [`c52c9f7`](https://github.com/SAP/ui5-fs/commit/c52c9f71e4f868cfa8ae5daf5eede9272654b187) + + +## [v2.0.2] - 2020-07-30 +### Bug Fixes +- TypeScript type definition support ([#252](https://github.com/SAP/ui5-fs/issues/252)) [`f379094`](https://github.com/SAP/ui5-fs/commit/f37909483b2740219da36c7e0931f7824d51e1a3) +- **Resource:** Keep stats size up to date ([#253](https://github.com/SAP/ui5-fs/issues/253)) [`0ef976f`](https://github.com/SAP/ui5-fs/commit/0ef976fe658765ae46afd1b4aa5b9100aa9829f8) +- **Resource:** getStream for empty string ([#249](https://github.com/SAP/ui5-fs/issues/249)) [`bc5eafb`](https://github.com/SAP/ui5-fs/commit/bc5eafb19bf71141b88dff749240354898849a66) +- **Resource#getSize:** Retrieve Resource's size ([#256](https://github.com/SAP/ui5-fs/issues/256)) [`9d2cc6c`](https://github.com/SAP/ui5-fs/commit/9d2cc6cf92b4a3f29e5c42db2f14afeeac9a0c98) +- **adapters/Memory:** Return cloned resources ([#235](https://github.com/SAP/ui5-fs/issues/235)) [`7bf3c6a`](https://github.com/SAP/ui5-fs/commit/7bf3c6a2ce55cd1c48b8aaaed83591bd85c228fa) + +### Reverts +- [FIX] adapters/Memory: Return cloned resources ([#235](https://github.com/SAP/ui5-fs/issues/235)) + + +## [v2.0.1] - 2020-04-30 +### Bug Fixes +- Namespaces in API Reference (JSDoc) [`b9d7b3c`](https://github.com/SAP/ui5-fs/commit/b9d7b3c70679436e6cbea07a789ac5e83bab337a) + + +## [v2.0.0] - 2020-03-31 +### Breaking Changes +- Remove deprecated parameter useNamespaces ([#223](https://github.com/SAP/ui5-fs/issues/223)) [`231b319`](https://github.com/SAP/ui5-fs/commit/231b319ea1899d6297e70e7b7746340cd5824217) +- Require Node.js >= 10 [`46651f1`](https://github.com/SAP/ui5-fs/commit/46651f13bcfe391ef985fabab96f99b6ecafe13e) + +### Dependency Updates +- Bump globby from 10.0.2 to 11.0.0 ([#207](https://github.com/SAP/ui5-fs/issues/207)) [`c81eb9d`](https://github.com/SAP/ui5-fs/commit/c81eb9dffc0d1281640efb2ad7e5419fe5451442) + +### BREAKING CHANGE + +Remove deprecated parameter "useNamespaces" from +method "resourceFactory.createCollectionsForTree". +Use parameter "getVirtualBasePathPrefix" instead. + +Support for older Node.js releases has been dropped. +Only Node.js v10 or higher is supported. + + +## [v1.1.2] - 2019-07-01 +### Dependency Updates +- Bump globby from 9.2.0 to 10.0.0 [`573e853`](https://github.com/SAP/ui5-fs/commit/573e8531827bf1c0320177d3870e37a183db7959) + + +## [v1.1.1] - 2019-06-18 +### Bug Fixes +- **fsInterface:** Handle non-POSIX paths correctly ([#147](https://github.com/SAP/ui5-fs/issues/147)) [`1ad0cc8`](https://github.com/SAP/ui5-fs/commit/1ad0cc8fdbcf74452ebfcebb23bd659faa956e54) + + +## [v1.1.0] - 2019-06-03 +### Features +- **AbstractAdapter:** Add excludes option ([#140](https://github.com/SAP/ui5-fs/issues/140)) [`daef31f`](https://github.com/SAP/ui5-fs/commit/daef31f4fb22405e8fa889615b1f3545099eb186) + + +## [v1.0.2] - 2019-03-21 +### Dependency Updates +- Bump [@ui5](https://github.com/ui5)/logger from 1.0.0 to 1.0.1 ([#122](https://github.com/SAP/ui5-fs/issues/122)) [`e2e2791`](https://github.com/SAP/ui5-fs/commit/e2e27917d936ad5a316c56a0bc0a17d91977d15e) + +### Features +- **fsInterface:** Add mkdir function [`5bd91ac`](https://github.com/SAP/ui5-fs/commit/5bd91acb86d64f03d16abdb186fe66ffa8a9f53a) + + +## [v1.0.1] - 2019-02-01 +### Bug Fixes +- Prevent FS write from draining Resources content [`370f121`](https://github.com/SAP/ui5-fs/commit/370f121ca4d571397c979e2dce72b6a1cf0d0005) + +### Dependency Updates +- **Yarn:** Pin dir-glob dependency to v2.0.0 [`e14457c`](https://github.com/SAP/ui5-fs/commit/e14457c5b3eda1fab3d3444bca3b8406be63db2f) + + +## [v1.0.0] - 2019-01-10 +### Breaking Changes +- **index.js export:** Remove top-level access to adapters [`f1f7831`](https://github.com/SAP/ui5-fs/commit/f1f7831ae9e908731a57f9d67952a61431c69d21) + +### Dependency Updates +- Pin dir-glob to v2.0.0 [`b921fbc`](https://github.com/SAP/ui5-fs/commit/b921fbceaa4f200737b8c6cf45eaf2d9bc3e1df2) +- Bump [@ui5](https://github.com/ui5)/logger from 0.2.2 to 1.0.0 ([#72](https://github.com/SAP/ui5-fs/issues/72)) [`4077f19`](https://github.com/SAP/ui5-fs/commit/4077f19251dee72933de0747de09a6eec5cb75cc) + +### BREAKING CHANGE + +Adapters "AbstractAdapter", "FileSystem" and "Memory" used to be accessible via the top-level export of index.js (example: require("[@ui5](https://github.com/ui5)/project").FileSystem). This is no longer possible. Adapters are now grouped in the top-level object "adapters" and can be accessed from there (example: require("[@ui5](https://github.com/ui5)/project").adapters.FileSystem). + + +## [v0.2.0] - 2018-07-11 + + +## [v0.1.0] - 2018-06-26 + + +## v0.0.1 - 2018-06-06 +### Bug Fixes +- **AbstractAdapter:** Fix normalization of globstar [`6d484e8`](https://github.com/SAP/ui5-fs/commit/6d484e847b62aa0829641f25a76dcc89b0840d44) +[v4.0.2]: https://github.com/SAP/ui5-fs/compare/v4.0.1...v4.0.2 +[v4.0.1]: https://github.com/SAP/ui5-fs/compare/v4.0.0...v4.0.1 +[v4.0.0]: https://github.com/SAP/ui5-fs/compare/v3.0.5...v4.0.0 +[v3.0.5]: https://github.com/SAP/ui5-fs/compare/v3.0.4...v3.0.5 +[v3.0.4]: https://github.com/SAP/ui5-fs/compare/v3.0.3...v3.0.4 +[v3.0.3]: https://github.com/SAP/ui5-fs/compare/v3.0.2...v3.0.3 +[v3.0.2]: https://github.com/SAP/ui5-fs/compare/v3.0.1...v3.0.2 +[v3.0.1]: https://github.com/SAP/ui5-fs/compare/v3.0.0...v3.0.1 +[v3.0.0]: https://github.com/SAP/ui5-fs/compare/v2.0.6...v3.0.0 +[v2.0.6]: https://github.com/SAP/ui5-fs/compare/v2.0.5...v2.0.6 +[v2.0.5]: https://github.com/SAP/ui5-fs/compare/v2.0.4...v2.0.5 +[v2.0.4]: https://github.com/SAP/ui5-fs/compare/v2.0.3...v2.0.4 +[v2.0.3]: https://github.com/SAP/ui5-fs/compare/v2.0.2...v2.0.3 +[v2.0.2]: https://github.com/SAP/ui5-fs/compare/v2.0.1...v2.0.2 +[v2.0.1]: https://github.com/SAP/ui5-fs/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/SAP/ui5-fs/compare/v1.1.2...v2.0.0 +[v1.1.2]: https://github.com/SAP/ui5-fs/compare/v1.1.1...v1.1.2 +[v1.1.1]: https://github.com/SAP/ui5-fs/compare/v1.1.0...v1.1.1 +[v1.1.0]: https://github.com/SAP/ui5-fs/compare/v1.0.2...v1.1.0 +[v1.0.2]: https://github.com/SAP/ui5-fs/compare/v1.0.1...v1.0.2 +[v1.0.1]: https://github.com/SAP/ui5-fs/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/SAP/ui5-fs/compare/v0.2.0...v1.0.0 +[v0.2.0]: https://github.com/SAP/ui5-fs/compare/v0.1.0...v0.2.0 +[v0.1.0]: https://github.com/SAP/ui5-fs/compare/v0.0.1...v0.1.0 diff --git a/packages/fs/CONTRIBUTING.md b/packages/fs/CONTRIBUTING.md new file mode 100644 index 00000000000..2ddb6276ed5 --- /dev/null +++ b/packages/fs/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to the UI5 CLI + +See CONTRIBUTING.md in the [UI5/cli](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md) repository. diff --git a/packages/fs/LICENSE.txt b/packages/fs/LICENSE.txt new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/fs/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/fs/LICENSES/Apache-2.0.txt b/packages/fs/LICENSES/Apache-2.0.txt new file mode 100644 index 00000000000..4ed90b95224 --- /dev/null +++ b/packages/fs/LICENSES/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/packages/fs/README.md b/packages/fs/README.md new file mode 100644 index 00000000000..541b00d68bf --- /dev/null +++ b/packages/fs/README.md @@ -0,0 +1,26 @@ +![UI5 icon](https://raw.githubusercontent.com/UI5/cli/main/docs/images/UI5_logo_wide.png) + +# ui5-fs +> UI5-specific file system abstraction +> Part of the [UI5 CLI](https://github.com/UI5/cli) + +[![REUSE status](https://api.reuse.software/badge/github.com/SAP/ui5-fs)](https://api.reuse.software/info/github.com/SAP/ui5-fs) +[![Build Status](https://dev.azure.com/sap/opensource/_apis/build/status/SAP.ui5-fs?branchName=v4)](https://dev.azure.com/sap/opensource/_build/latest?definitionId=36&branchName=v4) +[![npm Package Version](https://badge.fury.io/js/%40ui5%2Ffs.svg)](https://www.npmjs.com/package/@ui5/fs) +[![Coverage Status](https://coveralls.io/repos/github/SAP/ui5-fs/badge.svg)](https://coveralls.io/github/SAP/ui5-fs) + +## Documentation +UI5 FS documentation can be found here: [ui5.github.io/cli](https://ui5.github.io/cli/v4/pages/FileSystem/) + +The UI5 FS API Reference can be found here: [`@ui5/fs`](https://ui5.github.io/cli/v4/api/) + +## Contributing +Please check our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md). + +## Support +Please follow our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#report-an-issue) on how to report an issue. + +Please report issues in the main [UI5 CLI](https://github.com/UI5/cli) repository. + +## Release History +See [CHANGELOG.md](CHANGELOG.md). diff --git a/packages/fs/REUSE.toml b/packages/fs/REUSE.toml new file mode 100644 index 00000000000..144f1aecd8f --- /dev/null +++ b/packages/fs/REUSE.toml @@ -0,0 +1,11 @@ +version = 1 +SPDX-PackageName = "ui5-fs" +SPDX-PackageSupplier = "SAP OpenUI5 " +SPDX-PackageDownloadLocation = "https://github.com/SAP/ui5-fs" +SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." + +[[annotations]] +path = "**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2025 SAP SE or an SAP affiliate company and UI5 CLI contributors" +SPDX-License-Identifier = "Apache-2.0" diff --git a/packages/fs/azure-pipelines.yml b/packages/fs/azure-pipelines.yml new file mode 100644 index 00000000000..929b3342876 --- /dev/null +++ b/packages/fs/azure-pipelines.yml @@ -0,0 +1,81 @@ +# Node.js +# Build a general Node.js project with npm. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- v4 + +variables: + CI: true + +strategy: + matrix: + linux_node_lts_20_min_version: + imageName: 'ubuntu-24.04' + node_version: 20.11.0 + linux_node_22_min_version: + imageName: 'ubuntu-24.04' + node_version: 22.1.0 + linux_node_lts_20: + imageName: 'ubuntu-24.04' + node_version: 20.x + mac_node_lts_20: + imageName: 'macos-13' + node_version: 20.x + windows_node_lts_20: + imageName: 'windows-2022' + node_version: 20.x + linux_node_22: + imageName: 'ubuntu-24.04' + node_version: 22.x + mac_node_22: + imageName: 'macos-13' + node_version: 22.x + windows_node_22: + imageName: 'windows-2022' + node_version: 22.x + linux_node_24: + imageName: 'ubuntu-24.04' + node_version: 24.x + mac_node_24: + imageName: 'macos-13' + node_version: 24.x + windows_node_24: + imageName: 'windows-2022' + node_version: 24.x + +pool: + vmImage: $(imageName) + +steps: +- task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: Install Node.js + +- script: npm ci + displayName: Install Dependencies + +- script: npm ls --prod + displayName: Check for missing / extraneous Dependencies + +- script: npm run test-azure + displayName: Run Tests + +- task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '$(System.DefaultWorkingDirectory)/test-results.xml' + +- task: PublishCodeCoverageResults@2 + displayName: Publish Test Coverage Results + condition: succeededOrFailed() + inputs: + summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml' + +- script: npm run coverage + displayName: Run Test Natively in Case of Failures + condition: failed() diff --git a/packages/fs/eslint.common.config.js b/packages/fs/eslint.common.config.js new file mode 100644 index 00000000000..07876d526f0 --- /dev/null +++ b/packages/fs/eslint.common.config.js @@ -0,0 +1,99 @@ +import jsdoc from "eslint-plugin-jsdoc"; +import ava from "eslint-plugin-ava"; +import globals from "globals"; +import js from "@eslint/js"; +import google from "eslint-config-google"; + +export default [{ + ignores: [ // Common ignore patterns across all tooling repos + "**/coverage/", + "test/tmp/", + "test/expected/", + "test/fixtures/", + "**/docs/", + "**/jsdocs/", + ], +}, js.configs.recommended, google, ava.configs["flat/recommended"], { + name: "Common ESLint config used for all tooling repos", + + plugins: { + jsdoc, + }, + + languageOptions: { + globals: { + ...globals.node, + }, + + ecmaVersion: 2023, + sourceType: "module", + }, + + settings: { + jsdoc: { + mode: "jsdoc", + + tagNamePreference: { + return: "returns", + augments: "extends", + }, + }, + }, + + rules: { + "indent": ["error", "tab"], + "linebreak-style": ["error", "unix"], + + "quotes": ["error", "double", { + allowTemplateLiterals: true, + }], + + "semi": ["error", "always"], + "no-negated-condition": "off", + "require-jsdoc": "off", + "no-mixed-requires": "off", + + "max-len": ["error", { + code: 120, + ignoreUrls: true, + ignoreRegExpLiterals: true, + }], + + "no-implicit-coercion": [2, { + allow: ["!!"], + }], + + "comma-dangle": "off", + "no-tabs": "off", + "no-console": 2, // Disallow console.log() + "no-eval": 2, + // The following rule must be disabled as of ESLint 9. + // It's removed and causes issues when present + // https://eslint.org/docs/latest/rules/valid-jsdoc + "valid-jsdoc": 0, + "jsdoc/check-examples": 0, + "jsdoc/check-param-names": 2, + "jsdoc/check-tag-names": 2, + "jsdoc/check-types": 2, + "jsdoc/no-undefined-types": 0, + "jsdoc/require-description": 0, + "jsdoc/require-description-complete-sentence": 0, + "jsdoc/require-example": 0, + "jsdoc/require-hyphen-before-param-description": 0, + "jsdoc/require-param": 2, + "jsdoc/require-param-description": 0, + "jsdoc/require-param-name": 2, + "jsdoc/require-param-type": 2, + "jsdoc/require-returns": 0, + "jsdoc/require-returns-description": 0, + "jsdoc/require-returns-type": 2, + + "jsdoc/tag-lines": [2, "any", { + startLines: 1, + }], + + "jsdoc/valid-types": 0, + "ava/assertion-arguments": 0, + }, +} +]; diff --git a/packages/fs/eslint.config.js b/packages/fs/eslint.config.js new file mode 100644 index 00000000000..f5873aa9df9 --- /dev/null +++ b/packages/fs/eslint.config.js @@ -0,0 +1,5 @@ +import eslintCommonConfig from "./eslint.common.config.js"; + +export default [ + ...eslintCommonConfig, // Load common ESLint config +]; diff --git a/packages/fs/jsdoc-plugin.cjs b/packages/fs/jsdoc-plugin.cjs new file mode 100644 index 00000000000..cd7ef446d0f --- /dev/null +++ b/packages/fs/jsdoc-plugin.cjs @@ -0,0 +1,9 @@ +/* + * This plugin fixes unexpected JSDoc behavior that prevents us from using types that start with an at-sign (@). + * JSDoc doesn't see "{@" as a valid type expression, probably as there's also {@link ...}. + */ +exports.handlers = { + jsdocCommentFound: function(e) { + e.comment = e.comment.replace(/{@ui5\//g, "{ @ui5/"); + } +}; diff --git a/packages/fs/jsdoc.json b/packages/fs/jsdoc.json new file mode 100644 index 00000000000..c8a84229dce --- /dev/null +++ b/packages/fs/jsdoc.json @@ -0,0 +1,61 @@ +{ + "tags": { + "allowUnknownTags": false + }, + "source": { + "include": ["README.md"], + "includePattern": ".+\\.js$", + "excludePattern": "(node_modules(\\\\|/))" + }, + "plugins": [ + "./jsdoc-plugin.cjs" + ], + "opts": { + "encoding": "utf8", + "destination": "jsdocs/", + "recurse": true, + "verbose": true, + "access": "public" + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "default": { + "useLongnameInNav": true + } + }, + "openGraph": { + "title": "UI5 CLI - API Reference", + "type": "website", + "image": "https://ui5.github.io/cli/v4/images/UI5_logo_wide.png", + "site_name": "UI5 CLI - API Reference", + "url": "https://ui5.github.io/cli/" + }, + "docdash": { + "sectionOrder": [ + "Modules", + "Namespaces", + "Classes", + "Externals", + "Events", + "Mixins", + "Tutorials", + "Interfaces" + ], + "meta": { + "title": "UI5 CLI - API Reference - UI5 FS", + "description": "UI5 CLI - API Reference - UI5 FS", + "keyword": "openui5 sapui5 ui5 build development tool api reference" + }, + "search": true, + "wrap": true, + "menu": { + "GitHub": { + "href": "https://github.com/SAP/ui5-fs", + "target": "_blank", + "class": "menu-item", + "id": "github_link" + } + } + } +} diff --git a/packages/fs/lib/AbstractReader.js b/packages/fs/lib/AbstractReader.js new file mode 100644 index 00000000000..8f5c3632f83 --- /dev/null +++ b/packages/fs/lib/AbstractReader.js @@ -0,0 +1,129 @@ +import randomInt from "random-int"; +import Trace from "./tracing/Trace.js"; + +/** + * Abstract resource locator implementing the general API for reading resources + * + * @abstract + * @public + * @class + * @alias @ui5/fs/AbstractReader + */ +class AbstractReader { + /** + * The constructor. + * + * @public + * @param {string} name Name of the reader. Typically used for tracing purposes + */ + constructor(name) { + if (new.target === AbstractReader) { + throw new TypeError("Class 'AbstractReader' is abstract"); + } + this._name = name; + } + + /* + * Returns the name of the reader instance. This can be used for logging/tracing purposes. + * + * @returns {string} Name of the reader + */ + getName() { + return this._name || ``; + } + + /** + * Locates resources by matching glob patterns. + * + * @example + * byGlob("**‏/*.{html,htm}"); + * byGlob("**‏/.library"); + * byGlob("/pony/*"); + * + * @public + * @param {string|string[]} virPattern glob pattern as string or array of glob patterns for + * virtual directory structure + * @param {object} [options] glob options + * @param {boolean} [options.nodir=true] Do not match directories + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + byGlob(virPattern, options = {nodir: true}) { + const trace = new Trace(virPattern); + return this._byGlob(virPattern, options, trace).then(function(result) { + trace.printReport(); + return result; + }).then((resources) => { + if (resources.length > 1) { + // Pseudo randomize result order to prevent consumers from relying on it: + // Swap the first object with a randomly chosen one + const x = 0; + const y = randomInt(0, resources.length - 1); + // Swap object at index "x" with "y" + resources[x] = [resources[y], resources[y]=resources[x]][0]; + } + return resources; + }); + } + + /** + * Locates resources by matching a given path. + * + * @public + * @param {string} virPath Virtual path + * @param {object} [options] Options + * @param {boolean} [options.nodir=true] Do not match directories + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource + */ + byPath(virPath, options = {nodir: true}) { + const trace = new Trace(virPath); + return this._byPath(virPath, options, trace).then(function(resource) { + trace.printReport(); + return resource; + }); + } + + /** + * Locates resources by one or more glob patterns. + * + * @abstract + * @protected + * @param {string|string[]} virPattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + _byGlob(virPattern, options, trace) { + throw new Error("Function '_byGlob' is not implemented"); + } + + /** + * Locate resources by matching a single glob pattern. + * + * @abstract + * @protected + * @param {string} pattern glob pattern + * @param {object} options glob options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + _runGlob(pattern, options, trace) { + throw new Error("Function '_runGlob' is not implemented"); + } + + /** + * Locates resources by path. + * + * @abstract + * @protected + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource + */ + _byPath(virPath, options, trace) { + throw new Error("Function '_byPath' is not implemented"); + } +} + +export default AbstractReader; diff --git a/packages/fs/lib/AbstractReaderWriter.js b/packages/fs/lib/AbstractReaderWriter.js new file mode 100644 index 00000000000..9e07da5ef88 --- /dev/null +++ b/packages/fs/lib/AbstractReaderWriter.js @@ -0,0 +1,71 @@ +import AbstractReader from "./AbstractReader.js"; + +/** + * Abstract resource locator implementing the general API for reading and writing resources + * + * @abstract + * @public + * @class + * @alias @ui5/fs/AbstractReaderWriter + * @extends @ui5/fs/AbstractReader + */ +class AbstractReaderWriter extends AbstractReader { + /** + * The constructor. + * + * @public + * @param {string} name Name of the reader/writer. Typically used for tracing purposes + */ + constructor(name) { + if (new.target === AbstractReaderWriter) { + throw new TypeError("Class 'AbstractReaderWriter' is abstract"); + } + super(name); + } + + /* + * Returns the name of the reader/writer instance. This can be used for logging/tracing purposes. + * + * @returns {string} Name of the reader/writer + */ + getName() { + return this._name || ``; + } + + /** + * Writes the content of a resource to a path. + * + * @public + * @param {@ui5/fs/Resource} resource Resource to write + * @param {object} [options] + * @param {boolean} [options.readOnly=false] Whether the resource content shall be written read-only + * Do not use in conjunction with the drain option. + * The written file will be used as the new source of this resources content. + * Therefore the written file should not be altered by any means. + * Activating this option might improve overall memory consumption. + * @param {boolean} [options.drain=false] Whether the resource content shall be emptied during the write process. + * Do not use in conjunction with the readOnly option. + * Activating this option might improve overall memory consumption. + * This should be used in cases where this is the last access to the resource. + * E.g. the final write of a resource after all processing is finished. + * @returns {Promise} Promise resolving once data has been written + */ + write(resource, options = {drain: false, readOnly: false}) { + return this._write(resource, options); + } + + /** + * Writes the content of a resource to a path. + * + * @abstract + * @protected + * @param {@ui5/fs/Resource} resource Resource to write + * @param {object} [options] Write options, see above + * @returns {Promise} Promise resolving once data has been written + */ + _write(resource, options) { + throw new Error("Not implemented"); + } +} + +export default AbstractReaderWriter; diff --git a/packages/fs/lib/DuplexCollection.js b/packages/fs/lib/DuplexCollection.js new file mode 100644 index 00000000000..6aec0ec75c5 --- /dev/null +++ b/packages/fs/lib/DuplexCollection.js @@ -0,0 +1,84 @@ +import AbstractReaderWriter from "./AbstractReaderWriter.js"; +import ReaderCollectionPrioritized from "./ReaderCollectionPrioritized.js"; + +/** + * Wrapper to keep readers and writers together + * + * @public + * @class + * @alias @ui5/fs/DuplexCollection + * @extends @ui5/fs/AbstractReaderWriter + */ +class DuplexCollection extends AbstractReaderWriter { + /** + * The Constructor. + * + * @param {object} parameters + * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers + * @param {@ui5/fs/AbstractReaderWriter} parameters.writer + * A ReaderWriter instance which is only used for writing files + * @param {string} [parameters.name=""] The collection name + */ + constructor({reader, writer, name = ""}) { + super(name); + + if (!reader) { + throw new Error(`Cannot create DuplexCollection ${this._name}: No reader provided`); + } + if (!writer) { + throw new Error(`Cannot create DuplexCollection ${this._name}: No writer provided`); + } + + this._reader = reader; + this._writer = writer; + + this._combo = new ReaderCollectionPrioritized({ + name: `${name} - ReaderCollectionPrioritized`, + readers: [ + writer, + reader + ] + }); + } + + /** + * Locates resources by glob. + * + * @private + * @param {string|string[]} virPattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving with a list of resources + */ + _byGlob(virPattern, options, trace) { + return this._combo._byGlob(virPattern, options, trace); + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource|null>} + * Promise resolving to a single resource or null if no resource is found + */ + _byPath(virPath, options, trace) { + return this._combo._byPath(virPath, options, trace); + } + + /** + * Writes the content of a resource to a path. + * + * @private + * @param {@ui5/fs/Resource} resource The Resource to write + * @returns {Promise} Promise resolving once data has been written + */ + _write(resource) { + return this._writer.write(resource); + } +} + +export default DuplexCollection; diff --git a/packages/fs/lib/ReaderCollection.js b/packages/fs/lib/ReaderCollection.js new file mode 100644 index 00000000000..f0fa9ffc83f --- /dev/null +++ b/packages/fs/lib/ReaderCollection.js @@ -0,0 +1,86 @@ +import AbstractReader from "./AbstractReader.js"; + +/** + * Resource Locator ReaderCollection + * + * @public + * @class + * @alias @ui5/fs/ReaderCollection + * @extends @ui5/fs/AbstractReader + */ +class ReaderCollection extends AbstractReader { + /** + * The constructor. + * + * @param {object} parameters Parameters + * @param {string} parameters.name The collection name + * @param {@ui5/fs/AbstractReader[]} [parameters.readers] + * List of resource readers (all tried in parallel). + * If none are provided, the collection will never return any results. + */ + constructor({name, readers}) { + super(name); + + // Remove any undefined (empty) readers from array + this._readers = readers.filter(($) => $); + } + + /** + * Locates resources by glob. + * + * @private + * @param {string|string[]} pattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + _byGlob(pattern, options, trace) { + return Promise.all(this._readers.map(function(resourceLocator) { + return resourceLocator._byGlob(pattern, options, trace); + })).then((result) => { + trace.collection(this._name); + return Array.prototype.concat.apply([], result); + }); + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource|null>} + * Promise resolving to a single resource or null if no resource is found + */ + _byPath(virPath, options, trace) { + const that = this; + const resourceLocatorCount = this._readers.length; + let resolveCount = 0; + + if (resourceLocatorCount === 0) { + // Short-circuit if there are no readers (Promise.race does not settle for empty arrays) + trace.collection(that._name); + return Promise.resolve(null); + } + + // Using Promise.race to deliver files that can be found as fast as possible + return Promise.race(this._readers.map(function(resourceLocator) { + return resourceLocator._byPath(virPath, options, trace).then(function(resource) { + return new Promise(function(resolve, reject) { + trace.collection(that._name); + resolveCount++; + if (resource) { + resource.pushCollection(that._name); + resolve(resource); + } else if (resolveCount === resourceLocatorCount) { + resolve(null); + } + }); + }); + })); + } +} + +export default ReaderCollection; diff --git a/packages/fs/lib/ReaderCollectionPrioritized.js b/packages/fs/lib/ReaderCollectionPrioritized.js new file mode 100644 index 00000000000..680b71357ca --- /dev/null +++ b/packages/fs/lib/ReaderCollectionPrioritized.js @@ -0,0 +1,90 @@ +import AbstractReader from "./AbstractReader.js"; + +/** + * Prioritized Resource Locator Collection + * + * @public + * @class + * @alias @ui5/fs/ReaderCollectionPrioritized + * @extends @ui5/fs/AbstractReader + */ +class ReaderCollectionPrioritized extends AbstractReader { + /** + * The constructor. + * + * @param {object} parameters + * @param {string} parameters.name The collection name + * @param {@ui5/fs/AbstractReader[]} [parameters.readers] + * Prioritized list of resource readers (tried in the order provided). + * If none are provided, the collection will never return any results. + */ + constructor({readers, name}) { + super(name); + + // Remove any undefined (empty) readers from array + this._readers = readers.filter(($) => $); + } + + /** + * Locates resources by glob. + * + * @private + * @param {string|string[]} pattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + _byGlob(pattern, options, trace) { + return Promise.all(this._readers.map(function(resourceLocator) { + return resourceLocator._byGlob(pattern, options, trace); + })).then((result) => { + const files = Object.create(null); + const resources = []; + // Prefer files found in preceding resource locators + for (let i = 0; i < result.length; i++) { + for (let j = 0; j < result[i].length; j++) { + const resource = result[i][j]; + const path = resource.getPath(); + if (!files[path]) { + files[path] = true; + resources.push(resource); + } + } + } + + trace.collection(this._name); + return resources; + }); + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource|null>} + * Promise resolving to a single resource or null if no resource is found + */ + _byPath(virPath, options, trace) { + const that = this; + const byPath = (i) => { + if (i > this._readers.length - 1) { + return null; + } + return this._readers[i]._byPath(virPath, options, trace).then((resource) => { + if (resource) { + resource.pushCollection(that._name); + return resource; + } else { + return byPath(++i); + } + }); + }; + return byPath(0); + } +} + +export default ReaderCollectionPrioritized; diff --git a/packages/fs/lib/Resource.js b/packages/fs/lib/Resource.js new file mode 100644 index 00000000000..c43edc2716f --- /dev/null +++ b/packages/fs/lib/Resource.js @@ -0,0 +1,498 @@ +import stream from "node:stream"; +import clone from "clone"; +import posixPath from "node:path/posix"; + +const fnTrue = () => true; +const fnFalse = () => false; +const ALLOWED_SOURCE_METADATA_KEYS = ["adapter", "fsPath", "contentModified"]; + +/** + * Resource. UI5 CLI specific representation of a file's content and metadata + * + * @public + * @class + * @alias @ui5/fs/Resource + */ +class Resource { + #project; + #buffer; + #buffering; + #collections; + #contentDrained; + #createStream; + #name; + #path; + #sourceMetadata; + #statInfo; + #stream; + #streamDrained; + #isModified; + + /** + * Function for dynamic creation of content streams + * + * @public + * @callback @ui5/fs/Resource~createStream + * @returns {stream.Readable} A readable stream of a resources content + */ + + /** + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.path Absolute virtual path of the resource + * @param {fs.Stats|object} [parameters.statInfo] File information. Instance of + * [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} or similar object + * @param {Buffer} [parameters.buffer] Content of this resources as a Buffer instance + * (cannot be used in conjunction with parameters string, stream or createStream) + * @param {string} [parameters.string] Content of this resources as a string + * (cannot be used in conjunction with parameters buffer, stream or createStream) + * @param {Stream} [parameters.stream] Readable stream of the content of this resource + * (cannot be used in conjunction with parameters buffer, string or createStream) + * @param {@ui5/fs/Resource~createStream} [parameters.createStream] Function callback that returns a readable + * stream of the content of this resource (cannot be used in conjunction with parameters buffer, + * string or stream). + * In some cases this is the most memory-efficient way to supply resource content + * @param {@ui5/project/specifications/Project} [parameters.project] Project this resource is associated with + * @param {object} [parameters.sourceMetadata] Source metadata for UI5 CLI internal use. + * Some information may be set by an adapter to store information for later retrieval. Also keeps track of whether + * a resource content has been modified since it has been read from a source + */ + constructor({path, statInfo, buffer, string, createStream, stream, project, sourceMetadata}) { + if (!path) { + throw new Error("Unable to create Resource: Missing parameter 'path'"); + } + if (buffer && createStream || buffer && string || string && createStream || buffer && stream || + string && stream || createStream && stream) { + throw new Error("Unable to create Resource: Please set only one content parameter. " + + "'buffer', 'string', 'stream' or 'createStream'"); + } + + if (sourceMetadata) { + if (typeof sourceMetadata !== "object") { + throw new Error(`Parameter 'sourceMetadata' must be of type "object"`); + } + + /* eslint-disable-next-line guard-for-in */ + for (const metadataKey in sourceMetadata) { // Also check prototype + if (!ALLOWED_SOURCE_METADATA_KEYS.includes(metadataKey)) { + throw new Error(`Parameter 'sourceMetadata' contains an illegal attribute: ${metadataKey}`); + } + if (!["string", "boolean"].includes(typeof sourceMetadata[metadataKey])) { + throw new Error( + `Attribute '${metadataKey}' of parameter 'sourceMetadata' ` + + `must be of type "string" or "boolean"`); + } + } + } + + this.setPath(path); + + this.#sourceMetadata = sourceMetadata || {}; + + // This flag indicates whether a resource has changed from its original source. + // resource.isModified() is not sufficient, since it only reflects the modification state of the + // current instance. + // Since the sourceMetadata object is inherited to clones, it is the only correct indicator + this.#sourceMetadata.contentModified ??= false; + + this.#isModified = false; + + this.#project = project; + + this.#statInfo = statInfo || { // TODO + isFile: fnTrue, + isDirectory: fnFalse, + isBlockDevice: fnFalse, + isCharacterDevice: fnFalse, + isSymbolicLink: fnFalse, + isFIFO: fnFalse, + isSocket: fnFalse, + atimeMs: new Date().getTime(), + mtimeMs: new Date().getTime(), + ctimeMs: new Date().getTime(), + birthtimeMs: new Date().getTime(), + atime: new Date(), + mtime: new Date(), + ctime: new Date(), + birthtime: new Date() + }; + + if (createStream) { + this.#createStream = createStream; + } else if (stream) { + this.#stream = stream; + } else if (buffer) { + // Use private setter, not to accidentally set any modified flags + this.#setBuffer(buffer); + } else if (typeof string === "string" || string instanceof String) { + // Use private setter, not to accidentally set any modified flags + this.#setBuffer(Buffer.from(string, "utf8")); + } + + // Tracing: + this.#collections = []; + } + + /** + * Gets a buffer with the resource content. + * + * @public + * @returns {Promise} Promise resolving with a buffer of the resource content. + */ + async getBuffer() { + if (this.#contentDrained) { + throw new Error(`Content of Resource ${this.#path} has been drained. ` + + "This might be caused by requesting resource content after a content stream has been " + + "requested and no new content (e.g. a new stream) has been set."); + } + if (this.#buffer) { + return this.#buffer; + } else if (this.#createStream || this.#stream) { + return this.#getBufferFromStream(); + } else { + throw new Error(`Resource ${this.#path} has no content`); + } + } + + /** + * Sets a Buffer as content. + * + * @public + * @param {Buffer} buffer Buffer instance + */ + setBuffer(buffer) { + this.#sourceMetadata.contentModified = true; + this.#isModified = true; + this.#setBuffer(buffer); + } + + #setBuffer(buffer) { + this.#createStream = null; + // if (this.#stream) { // TODO this may cause strange issues + // this.#stream.destroy(); + // } + this.#stream = null; + this.#buffer = buffer; + this.#contentDrained = false; + this.#streamDrained = false; + } + + /** + * Gets a string with the resource content. + * + * @public + * @returns {Promise} Promise resolving with the resource content. + */ + getString() { + if (this.#contentDrained) { + return Promise.reject(new Error(`Content of Resource ${this.#path} has been drained. ` + + "This might be caused by requesting resource content after a content stream has been " + + "requested and no new content (e.g. a new stream) has been set.")); + } + return this.getBuffer().then((buffer) => buffer.toString()); + } + + /** + * Sets a String as content + * + * @public + * @param {string} string Resource content + */ + setString(string) { + this.setBuffer(Buffer.from(string, "utf8")); + } + + /** + * Gets a readable stream for the resource content. + * + * Repetitive calls of this function are only possible if new content has been set in the meantime (through + * [setStream]{@link @ui5/fs/Resource#setStream}, [setBuffer]{@link @ui5/fs/Resource#setBuffer} + * or [setString]{@link @ui5/fs/Resource#setString}). This + * is to prevent consumers from accessing drained streams. + * + * @public + * @returns {stream.Readable} Readable stream for the resource content. + */ + getStream() { + if (this.#contentDrained) { + throw new Error(`Content of Resource ${this.#path} has been drained. ` + + "This might be caused by requesting resource content after a content stream has been " + + "requested and no new content (e.g. a new stream) has been set."); + } + let contentStream; + if (this.#buffer) { + const bufferStream = new stream.PassThrough(); + bufferStream.end(this.#buffer); + contentStream = bufferStream; + } else if (this.#createStream || this.#stream) { + contentStream = this.#getStream(); + } + if (!contentStream) { + throw new Error(`Resource ${this.#path} has no content`); + } + // If a stream instance is being returned, it will typically get drained be the consumer. + // In that case, further content access will result in a "Content stream has been drained" error. + // However, depending on the execution environment, a resources content stream might have been + // transformed into a buffer. In that case further content access is possible as a buffer can't be + // drained. + // To prevent unexpected "Content stream has been drained" errors caused by changing environments, we flag + // the resource content as "drained" every time a stream is requested. Even if actually a buffer or + // createStream callback is being used. + this.#contentDrained = true; + return contentStream; + } + + /** + * Sets a readable stream as content. + * + * @public + * @param {stream.Readable|@ui5/fs/Resource~createStream} stream Readable stream of the resource content or + callback for dynamic creation of a readable stream + */ + setStream(stream) { + this.#isModified = true; + this.#sourceMetadata.contentModified = true; + + this.#buffer = null; + // if (this.#stream) { // TODO this may cause strange issues + // this.#stream.destroy(); + // } + if (typeof stream === "function") { + this.#createStream = stream; + this.#stream = null; + } else { + this.#stream = stream; + this.#createStream = null; + } + this.#contentDrained = false; + this.#streamDrained = false; + } + + /** + * Gets the virtual resources path + * + * @public + * @returns {string} Virtual path of the resource + */ + getPath() { + return this.#path; + } + + /** + * Sets the virtual resources path + * + * @public + * @param {string} path Absolute virtual path of the resource + */ + setPath(path) { + path = posixPath.normalize(path); + if (!posixPath.isAbsolute(path)) { + throw new Error(`Unable to set resource path: Path must be absolute: ${path}`); + } + this.#path = path; + this.#name = posixPath.basename(path); + } + + /** + * Gets the resource name + * + * @public + * @returns {string} Name of the resource + */ + getName() { + return this.#name; + } + + /** + * Gets the resources stat info. + * Note that a resources stat information is not updated when the resource is being modified. + * Also, depending on the used adapter, some fields might be missing which would be present for a + * [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} instance. + * + * @public + * @returns {fs.Stats|object} Instance of [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} + * or similar object + */ + getStatInfo() { + return this.#statInfo; + } + + /** + * Size in bytes allocated by the underlying buffer. + * + * @see {TypedArray#byteLength} + * @returns {Promise} size in bytes, 0 if there is no content yet + */ + async getSize() { + // if resource does not have any content it should have 0 bytes + if (!this.#buffer && !this.#createStream && !this.#stream) { + return 0; + } + const buffer = await this.getBuffer(); + return buffer.byteLength; + } + + /** + * Adds a resource collection name that was involved in locating this resource. + * + * @param {string} name Resource collection name + */ + pushCollection(name) { + this.#collections.push(name); + } + + /** + * Returns a clone of the resource. The clones content is independent from that of the original resource + * + * @public + * @returns {Promise<@ui5/fs/Resource>} Promise resolving with the clone + */ + async clone() { + const options = await this.#getCloneOptions(); + return new Resource(options); + } + + async #getCloneOptions() { + const options = { + path: this.#path, + statInfo: clone(this.#statInfo), + sourceMetadata: clone(this.#sourceMetadata) + }; + + if (this.#stream) { + options.buffer = await this.#getBufferFromStream(); + } else if (this.#createStream) { + options.createStream = this.#createStream; + } else if (this.#buffer) { + options.buffer = this.#buffer; + } + + return options; + } + + /** + * Retrieve the project assigned to the resource + *
+ * Note for UI5 CLI extensions (i.e. custom tasks, custom middleware): + * In order to ensure compatibility across UI5 CLI versions, consider using the + * getProject(resource) method provided by + * [TaskUtil]{@link module:@ui5/project/build/helpers/TaskUtil} and + * [MiddlewareUtil]{@link module:@ui5/server.middleware.MiddlewareUtil}, which will + * return a Specification Version-compatible Project interface. + * + * @public + * @returns {@ui5/project/specifications/Project} Project this resource is associated with + */ + getProject() { + return this.#project; + } + + /** + * Assign a project to the resource + * + * @public + * @param {@ui5/project/specifications/Project} project Project this resource is associated with + */ + setProject(project) { + if (this.#project) { + throw new Error(`Unable to assign project ${project.getName()} to resource ${this.#path}: ` + + `Resource is already associated to project ${this.#project}`); + } + this.#project = project; + } + + /** + * Check whether a project has been assigned to the resource + * + * @public + * @returns {boolean} True if the resource is associated with a project + */ + hasProject() { + return !!this.#project; + } + + /** + * Check whether the content of this resource has been changed during its life cycle + * + * @public + * @returns {boolean} True if the resource's content has been changed + */ + isModified() { + return this.#isModified; + } + + /** + * Tracing: Get tree for printing out trace + * + * @returns {object} Trace tree + */ + getPathTree() { + const tree = Object.create(null); + + let pointer = tree[this.#path] = Object.create(null); + + for (let i = this.#collections.length - 1; i >= 0; i--) { + pointer = pointer[this.#collections[i]] = Object.create(null); + } + + return tree; + } + + /** + * Returns source metadata which may contain information specific to the adapter that created the resource + * Typically set by an adapter to store information for later retrieval. + * + * @returns {object} + */ + getSourceMetadata() { + return this.#sourceMetadata; + } + + /** + * Returns the content as stream. + * + * @private + * @returns {stream.Readable} Readable stream + */ + #getStream() { + if (this.#streamDrained) { + throw new Error(`Content stream of Resource ${this.#path} is flagged as drained.`); + } + if (this.#createStream) { + return this.#createStream(); + } + this.#streamDrained = true; + return this.#stream; + } + + /** + * Converts the buffer into a stream. + * + * @private + * @returns {Promise} Promise resolving with buffer. + */ + #getBufferFromStream() { + if (this.#buffering) { // Prevent simultaneous buffering, causing unexpected access to drained stream + return this.#buffering; + } + return this.#buffering = new Promise((resolve, reject) => { + const contentStream = this.#getStream(); + const buffers = []; + contentStream.on("data", (data) => { + buffers.push(data); + }); + contentStream.on("error", (err) => { + reject(err); + }); + contentStream.on("end", () => { + const buffer = Buffer.concat(buffers); + this.#setBuffer(buffer); + this.#buffering = null; + resolve(buffer); + }); + }); + } +} + +export default Resource; diff --git a/packages/fs/lib/ResourceFacade.js b/packages/fs/lib/ResourceFacade.js new file mode 100644 index 00000000000..58ba37b2a4d --- /dev/null +++ b/packages/fs/lib/ResourceFacade.js @@ -0,0 +1,262 @@ +import posixPath from "node:path/posix"; + +/** + * A {@link @ui5/fs/Resource Resource} with a different path than it's original + * + * @public + * @class + * @alias @ui5/fs/ResourceFacade + */ +class ResourceFacade { + #path; + #name; + #resource; + + /** + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.path Virtual path of the facade resource + * @param {@ui5/fs/Resource} parameters.resource Resource to conceal + */ + constructor({path, resource}) { + if (!path) { + throw new Error("Unable to create ResourceFacade: Missing parameter 'path'"); + } + if (!resource) { + throw new Error("Unable to create ResourceFacade: Missing parameter 'resource'"); + } + path = posixPath.normalize(path); + if (!posixPath.isAbsolute(path)) { + throw new Error(`Unable to create ResourceFacade: Parameter 'path' must be absolute: ${path}`); + } + this.#path = path; + this.#name = posixPath.basename(path); + this.#resource = resource; + } + + /** + * Gets the resources path + * + * @public + * @returns {string} (Virtual) path of the resource + */ + getPath() { + return this.#path; + } + + /** + * Gets the resource name + * + * @public + * @returns {string} Name of the resource + */ + getName() { + return this.#name; + } + + /** + * Sets the resources path + * + * @public + * @param {string} path (Virtual) path of the resource + */ + setPath(path) { + throw new Error(`The path of a ResourceFacade can't be changed`); + } + + /** + * Returns a clone of the resource. The clones content is independent from that of the original resource. + * A ResourceFacade becomes a Resource + * + * @public + * @returns {Promise<@ui5/fs/Resource>} Promise resolving with the clone + */ + async clone() { + // Cloning resolves the facade + const resourceClone = await this.#resource.clone(); + resourceClone.setPath(this.getPath()); + return resourceClone; + } + + /** + * ====================================================================== + * Call through functions to original resource + * ====================================================================== + */ + /** + * Gets a buffer with the resource content. + * + * @public + * @returns {Promise} Promise resolving with a buffer of the resource content. + */ + async getBuffer() { + return this.#resource.getBuffer(); + } + + /** + * Sets a Buffer as content. + * + * @public + * @param {Buffer} buffer Buffer instance + */ + setBuffer(buffer) { + return this.#resource.setBuffer(buffer); + } + + /** + * Gets a string with the resource content. + * + * @public + * @returns {Promise} Promise resolving with the resource content. + */ + getString() { + return this.#resource.getString(); + } + + /** + * Sets a String as content + * + * @public + * @param {string} string Resource content + */ + setString(string) { + return this.#resource.setString(string); + } + + /** + * Gets a readable stream for the resource content. + * + * Repetitive calls of this function are only possible if new content has been set in the meantime (through + * [setStream]{@link @ui5/fs/Resource#setStream}, [setBuffer]{@link @ui5/fs/Resource#setBuffer} + * or [setString]{@link @ui5/fs/Resource#setString}). This + * is to prevent consumers from accessing drained streams. + * + * @public + * @returns {stream.Readable} Readable stream for the resource content. + */ + getStream() { + return this.#resource.getStream(); + } + + /** + * Sets a readable stream as content. + * + * @public + * @param {stream.Readable|@ui5/fs/Resource~createStream} stream Readable stream of the resource content or + callback for dynamic creation of a readable stream + */ + setStream(stream) { + return this.#resource.setStream(stream); + } + + /** + * Gets the resources stat info. + * Note that a resources stat information is not updated when the resource is being modified. + * Also, depending on the used adapter, some fields might be missing which would be present for a + * [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} instance. + * + * @public + * @returns {fs.Stats|object} Instance of [fs.Stats]{@link https://nodejs.org/api/fs.html#fs_class_fs_stats} + * or similar object + */ + getStatInfo() { + return this.#resource.getStatInfo(); + } + + /** + * Size in bytes allocated by the underlying buffer. + * + * @see {TypedArray#byteLength} + * @returns {Promise} size in bytes, 0 if there is no content yet + */ + async getSize() { + return this.#resource.getSize(); + } + + /** + * Adds a resource collection name that was involved in locating this resource. + * + * @param {string} name Resource collection name + */ + pushCollection(name) { + return this.#resource.pushCollection(name); + } + + /** + * Tracing: Get tree for printing out trace + * + * @returns {object} Trace tree + */ + getPathTree() { + return this.#resource.getPathTree(); + } + + /** + * Retrieve the project assigned to the resource + *
+ * Note for UI5 CLI extensions (i.e. custom tasks, custom middleware): + * In order to ensure compatibility across UI5 CLI versions, consider using the + * getProject(resource) method provided by + * [TaskUtil]{@link module:@ui5/project/build/helpers/TaskUtil} and + * [MiddlewareUtil]{@link module:@ui5/server.middleware.MiddlewareUtil}, which will + * return a Specification Version-compatible Project interface. + * + * @public + * @returns {@ui5/project/specifications/Project} Project this resource is associated with + */ + getProject() { + return this.#resource.getProject(); + } + + /** + * Assign a project to the resource + * + * @public + * @param {@ui5/project/specifications/Project} project Project this resource is associated with + */ + setProject(project) { + return this.#resource.setProject(project); + } + + /** + * Check whether a project has been assigned to the resource + * + * @public + * @returns {boolean} True if the resource is associated with a project + */ + hasProject() { + return this.#resource.hasProject(); + } + /** + * Check whether the content of this resource has been changed during its life cycle + * + * @public + * @returns {boolean} True if the resource's content has been changed + */ + isModified() { + return this.#resource.isModified(); + } + + /** + * Returns source metadata if any where provided during the creation of this resource. + * Typically set by an adapter to store information for later retrieval. + * + * @returns {object|null} + */ + getSourceMetadata() { + return this.#resource.getSourceMetadata(); + } + + + /** + * Returns the resource concealed by this facade + * + * @returns {@ui5/fs/Resource} + */ + getConcealedResource() { + return this.#resource; + } +} + +export default ResourceFacade; diff --git a/packages/fs/lib/ResourceTagCollection.js b/packages/fs/lib/ResourceTagCollection.js new file mode 100644 index 00000000000..9214c15bd0b --- /dev/null +++ b/packages/fs/lib/ResourceTagCollection.js @@ -0,0 +1,124 @@ +const tagNamespaceRegExp = /^[a-z][a-z0-9]+$/; // part before the colon +const tagNameRegExp = /^[A-Z][A-Za-z0-9]+$/; // part after the colon +import ResourceFacade from "./ResourceFacade.js"; + +/** + * A ResourceTagCollection + * + * @private + * @class + * @alias @ui5/fs/internal/ResourceTagCollection + */ +class ResourceTagCollection { + constructor({allowedTags = [], allowedNamespaces = [], tags}) { + this._allowedTags = allowedTags; // Allowed tags are validated during use + this._allowedNamespaces = allowedNamespaces; + + if (allowedNamespaces.length) { + let allowedNamespacesRegExp = allowedNamespaces.reduce((regex, tagNamespace, idx) => { + // Ensure alphanum namespace to ensure working regex + if (!tagNamespaceRegExp.test(tagNamespace)) { + throw new Error(`Invalid namespace ${tagNamespace}: ` + + `Namespace must be alphanumeric, lowercase and start with a letter`); + } + return `${regex}${idx === 0 ? "" : "|"}${tagNamespace}`; + }, "^(?:"); + allowedNamespacesRegExp += "):.+$"; + this._allowedNamespacesRegExp = new RegExp(allowedNamespacesRegExp); + } else { + this._allowedNamespacesRegExp = null; + } + + this._pathTags = tags || Object.create(null); + } + + setTag(resourcePath, tag, value = true) { + resourcePath = this._getPath(resourcePath); + this._validateTag(tag); + this._validateValue(value); + + if (!this._pathTags[resourcePath]) { + this._pathTags[resourcePath] = Object.create(null); + } + this._pathTags[resourcePath][tag] = value; + } + + clearTag(resourcePath, tag) { + resourcePath = this._getPath(resourcePath); + this._validateTag(tag); + + if (this._pathTags[resourcePath]) { + this._pathTags[resourcePath][tag] = undefined; + } + } + + getTag(resourcePath, tag) { + resourcePath = this._getPath(resourcePath); + this._validateTag(tag); + + if (this._pathTags[resourcePath]) { + return this._pathTags[resourcePath][tag]; + } + } + + getAllTags() { + return this._pathTags; + } + + acceptsTag(tag) { + if (this._allowedTags.includes(tag) || this._allowedNamespacesRegExp?.test(tag)) { + return true; + } + return false; + } + + _getPath(resourcePath) { + if (typeof resourcePath !== "string") { + if (resourcePath instanceof ResourceFacade) { + resourcePath = resourcePath.getConcealedResource().getPath(); + } else { + resourcePath = resourcePath.getPath(); + } + } + if (!resourcePath) { + throw new Error(`Invalid Resource: Resource path must not be empty`); + } + return resourcePath; + } + + _validateTag(tag) { + if (!tag.includes(":")) { + throw new Error(`Invalid Tag "${tag}": Colon required after namespace`); + } + const parts = tag.split(":"); + if (parts.length > 2) { + throw new Error(`Invalid Tag "${tag}": Expected exactly one colon but found ${parts.length - 1}`); + } + + const [tagNamespace, tagName] = parts; + if (!tagNamespaceRegExp.test(tagNamespace)) { + throw new Error( + `Invalid Tag "${tag}": Namespace part must be alphanumeric, lowercase and start with a letter`); + } + if (!tagNameRegExp.test(tagName)) { + throw new Error(`Invalid Tag "${tag}": Name part must be alphanumeric and start with a capital letter`); + } + + if (!this.acceptsTag(tag)) { + throw new Error( + `Tag "${tag}" not accepted by this collection. Accepted tags are: ` + + `${this._allowedTags.join(", ") || "*none*"}. Accepted namespaces are: ` + + `${this._allowedNamespaces.join(", ") || "*none*"}`); + } + } + + _validateValue(value) { + const type = typeof value; + if (!["string", "number", "boolean"].includes(type)) { + throw new Error( + `Invalid Tag Value: Must be of type string, number or boolean but is ${type}`); + } + } +} + +export default ResourceTagCollection; diff --git a/packages/fs/lib/WriterCollection.js b/packages/fs/lib/WriterCollection.js new file mode 100644 index 00000000000..7c01f783f14 --- /dev/null +++ b/packages/fs/lib/WriterCollection.js @@ -0,0 +1,117 @@ +import AbstractReaderWriter from "./AbstractReaderWriter.js"; +import ReaderCollection from "./ReaderCollection.js"; +import escapeStringRegExp from "escape-string-regexp"; + +/** + * Resource Locator WriterCollection + * + * @public + * @class + * @alias @ui5/fs/WriterCollection + * @extends @ui5/fs/AbstractReaderWriter + */ +class WriterCollection extends AbstractReaderWriter { + /** + * The constructor. + * + * @param {object} parameters Parameters + * @param {string} parameters.name The collection name + * @param {object.} parameters.writerMapping + * Mapping of virtual base paths to writers. Path are matched greedy + * + * @example + * new WriterCollection({ + * name: "Writer Collection", + * writerMapping: { + * "/": writerA, + * "/my/path/": writerB, + * } + * }); + */ + constructor({name, writerMapping}) { + super(name); + + if (!writerMapping) { + throw new Error(`Cannot create WriterCollection ${this._name}: Missing parameter 'writerMapping'`); + } + const basePaths = Object.keys(writerMapping); + if (!basePaths.length) { + throw new Error(`Cannot create WriterCollection ${this._name}: Empty parameter 'writerMapping'`); + } + + // Create a regular expression (which is greedy by nature) from all paths to easily + // find the correct writer for any given resource path + this._basePathRegex = basePaths.sort().reduce((regex, basePath, idx) => { + // Validate base path + if (!basePath) { + throw new Error(`Empty path in path mapping of WriterCollection ${this._name}`); + } + if (!basePath.startsWith("/")) { + throw new Error( + `Missing leading slash in path mapping '${basePath}' of WriterCollection ${this._name}`); + } + if (!basePath.endsWith("/")) { + throw new Error( + `Missing trailing slash in path mapping '${basePath}' of WriterCollection ${this._name}`); + } + + return `${regex}(?:${escapeStringRegExp(basePath)})??`; + }, "^(") + ")+.*?$"; + + this._writerMapping = writerMapping; + this._readerCollection = new ReaderCollection({ + name: `Reader collection of writer collection '${this._name}'`, + readers: Object.values(writerMapping) + }); + } + + /** + * Locates resources by glob. + * + * @private + * @param {string|string[]} pattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + _byGlob(pattern, options, trace) { + return this._readerCollection._byGlob(pattern, options, trace); + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource + */ + _byPath(virPath, options, trace) { + return this._readerCollection._byPath(virPath, options, trace); + } + + /** + * Writes the content of a resource to a path. + * + * @private + * @param {@ui5/fs/Resource} resource The Resource to write + * @param {object} [options] Write options, see above + * @returns {Promise} Promise resolving once data has been written + */ + _write(resource, options) { + const resourcePath = resource.getPath(); + + const basePathMatch = resourcePath.match(this._basePathRegex); + if (!basePathMatch || basePathMatch.length < 2) { + throw new Error( + `Failed to find a writer for resource with path ${resourcePath} in WriterCollection ${this._name}. ` + + `Base paths handled by this collection are: ${Object.keys(this._writerMapping).join(", ")}`); + } + const writer = this._writerMapping[basePathMatch[1]]; + return writer._write(resource, options); + } +} + +export default WriterCollection; diff --git a/packages/fs/lib/adapters/AbstractAdapter.js b/packages/fs/lib/adapters/AbstractAdapter.js new file mode 100644 index 00000000000..96cf4154250 --- /dev/null +++ b/packages/fs/lib/adapters/AbstractAdapter.js @@ -0,0 +1,294 @@ +import path from "node:path/posix"; +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:adapters:AbstractAdapter"); +import {minimatch} from "minimatch"; +import micromatch from "micromatch"; +import AbstractReaderWriter from "../AbstractReaderWriter.js"; +import Resource from "../Resource.js"; + +/** + * Abstract Resource Adapter + * + * @abstract + * @public + * @class + * @alias @ui5/fs/adapters/AbstractAdapter + * @extends @ui5/fs/AbstractReaderWriter + */ +class AbstractAdapter extends AbstractReaderWriter { + /** + * The constructor + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.virBasePath + * Virtual base path. Must be absolute, POSIX-style, and must end with a slash + * @param {string[]} [parameters.excludes] List of glob patterns to exclude + * @param {object} [parameters.project] Experimental, internal parameter. Do not use + */ + constructor({virBasePath, excludes = [], project}) { + if (new.target === AbstractAdapter) { + throw new TypeError("Class 'AbstractAdapter' is abstract"); + } + super(); + + if (!virBasePath) { + throw new Error(`Unable to create adapter: Missing parameter 'virBasePath'`); + } + if (!path.isAbsolute(virBasePath)) { + throw new Error(`Unable to create adapter: Virtual base path must be absolute but is '${virBasePath}'`); + } + if (!virBasePath.endsWith("/")) { + throw new Error( + `Unable to create adapter: Virtual base path must end with a slash but is '${virBasePath}'`); + } + this._virBasePath = virBasePath; + this._virBaseDir = virBasePath.slice(0, -1); + this._excludes = excludes; + this._excludesNegated = excludes.map((pattern) => `!${pattern}`); + this._project = project; + } + /** + * Locates resources by glob. + * + * @abstract + * @private + * @param {string|string[]} virPattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} [options={}] glob options + * @param {boolean} [options.nodir=true] Do not match directories + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + async _byGlob(virPattern, options = {nodir: true}, trace) { + const excludes = this._excludesNegated; + + if (!(virPattern instanceof Array)) { + virPattern = [virPattern]; + } + + // Append static exclude patterns + virPattern = Array.prototype.concat.apply(virPattern, excludes); + let patterns = virPattern.map(this._normalizePattern, this); + patterns = Array.prototype.concat.apply([], patterns); + if (patterns.length === 0) { + return []; + } + + if (!options.nodir) { + for (let i = patterns.length - 1; i >= 0; i--) { + const idx = this._virBaseDir.indexOf(patterns[i]); + if (patterns[i] && idx !== -1 && idx < this._virBaseDir.length) { + const subPath = patterns[i]; + return [ + this._createResource({ + statInfo: { // TODO: make closer to fs stat info + isDirectory: function() { + return true; + } + }, + source: { + adapter: "Abstract" + }, + path: subPath + }) + ]; + } + } + } + return await this._runGlob(patterns, options, trace); + } + + /** + * Validate if virtual path should be excluded + * + * @param {string} virPath Virtual Path + * @returns {boolean} True if path is excluded, otherwise false + */ + _isPathExcluded(virPath) { + return micromatch(virPath, this._excludes).length > 0; + } + + /** + * Validate if virtual path should be handled by the adapter. + * This means that it either starts with the virtual base path of the adapter + * or equals the base directory (base path without a trailing slash) + * + * @param {string} virPath Virtual Path + * @returns {boolean} True if path should be handled + */ + _isPathHandled(virPath) { + // Check whether path starts with base path, or equals base directory + return virPath.startsWith(this._virBasePath) || virPath === this._virBaseDir; + } + + /** + * Normalizes virtual glob patterns. + * + * @private + * @param {string} virPattern glob pattern for virtual directory structure + * @returns {string[]} A list of normalized glob patterns + */ + _normalizePattern(virPattern) { + const that = this; + const mm = new minimatch.Minimatch(virPattern); + + const basePathParts = this._virBaseDir.split("/"); + + function matchSubset(subset) { + let i; + for (i = 0; i < basePathParts.length; i++) { + const globPart = subset[i]; + if (globPart === undefined) { + log.verbose("Ran out of glob parts to match (this should not happen):"); + if (that._project) { // project is optional + log.verbose(`Project: ${that._project.getName()}`); + } + log.verbose(`Virtual base path: ${that._virBaseDir}`); + log.verbose(`Pattern to match: ${virPattern}`); + log.verbose(`Current subset (tried index ${i}):`); + log.verbose(subset); + return {idx: i, virtualMatch: true}; + } + const basePathPart = basePathParts[i]; + if (typeof globPart === "string") { + if (globPart !== basePathPart) { + return null; + } else { + continue; + } + } else if (globPart === minimatch.GLOBSTAR) { + return {idx: i}; + } else { // Regex + if (!globPart.test(basePathPart)) { + return null; + } else { + continue; + } + } + } + if (subset.length === basePathParts.length) { + return {rootMatch: true}; + } + return {idx: i}; + } + + const resultGlobs = []; + for (let i = 0; i < mm.set.length; i++) { + const match = matchSubset(mm.set[i]); + if (match) { + let resultPattern; + if (match.virtualMatch) { + resultPattern = basePathParts.slice(0, match.idx).join("/"); + } else if (match.rootMatch) { // matched one up + resultPattern = ""; // root "/" + } else { // matched at some part of the glob + resultPattern = mm.globParts[i].slice(match.idx).join("/"); + if (resultPattern.startsWith("/")) { + resultPattern = resultPattern.substr(1); + } + } + if (mm.negate) { + resultPattern = "!" + resultPattern; + } + resultGlobs.push(resultPattern); + } + } + return resultGlobs; + } + + _createResource(parameters) { + if (this._project) { + parameters.project = this._project; + } + return new Resource(parameters); + } + + _migrateResource(resource) { + // This function only returns a promise if a migration is necessary. + // Since this is rarely the case, we therefore reduce the amount of + // created Promises by making this differentiation + + // Check if its a fs/Resource v3, function 'hasProject' was + // introduced with v3 therefore take it as the indicator + if (resource.hasProject) { + return resource; + } + return this._createFromLegacyResource(resource); + } + + async _createFromLegacyResource(resource) { + const options = { + path: resource._path, + statInfo: resource._statInfo, + source: resource._source + }; + + if (resource._stream) { + options.buffer = await resource._getBufferFromStream(); + } else if (resource._createStream) { + options.createStream = resource._createStream; + } else if (resource._buffer) { + options.buffer = resource._buffer; + } + return new Resource(options); + } + + _assignProjectToResource(resource) { + if (this._project) { + // Assign project to resource if necessary + if (resource.hasProject()) { + if (resource.getProject() !== this._project) { + throw new Error( + `Unable to write resource associated with project ` + + `${resource.getProject().getName()} into adapter of project ${this._project.getName()}: ` + + resource.getPath()); + } + return; + } + log.silly(`Associating resource ${resource.getPath()} with project ${this._project.getName()}`); + resource.setProject(this._project); + } + } + + _resolveVirtualPathToBase(inputVirPath, writeMode = false) { + if (!path.isAbsolute(inputVirPath)) { + throw new Error(`Failed to resolve virtual path '${inputVirPath}': Path must be absolute`); + } + // Resolve any ".." segments to make sure we compare the effective start of the path + // with the virBasePath + const virPath = path.normalize(inputVirPath); + + if (!writeMode) { + // When reading resources, validate against path excludes and return null if the given path + // does not match this adapters base path + if (!this._isPathHandled(virPath)) { + if (log.isLevelEnabled("silly")) { + log.silly(`Failed to resolve virtual path '${inputVirPath}': ` + + `Resolved path does not start with adapter base path '${this._virBasePath}' or equals ` + + `base dir: ${this._virBaseDir}`); + } + return null; + } + if (this._isPathExcluded(virPath)) { + if (log.isLevelEnabled("silly")) { + log.silly(`Failed to resolve virtual path '${inputVirPath}': ` + + `Resolved path is excluded by configuration of adapter with base path '${this._virBasePath}'`); + } + return null; + } + } else if (!this._isPathHandled(virPath)) { + // Resolved path is not within the configured base path and does + // not equal the virtual base directory. + // Since we don't want to write resources to foreign locations, we throw an error + throw new Error( + `Failed to write resource with virtual path '${inputVirPath}': Path must start with ` + + `the configured virtual base path of the adapter. Base path: '${this._virBasePath}'`); + } + + const relPath = virPath.substr(this._virBasePath.length); + return relPath; + } +} + +export default AbstractAdapter; diff --git a/packages/fs/lib/adapters/FileSystem.js b/packages/fs/lib/adapters/FileSystem.js new file mode 100644 index 00000000000..d086fac40dd --- /dev/null +++ b/packages/fs/lib/adapters/FileSystem.js @@ -0,0 +1,371 @@ +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:adapters:FileSystem"); +import path from "node:path"; +import {promisify} from "node:util"; +import fs from "graceful-fs"; +const copyFile = promisify(fs.copyFile); +const chmod = promisify(fs.chmod); +const mkdir = promisify(fs.mkdir); +const stat = promisify(fs.stat); +import {globby, isGitIgnored} from "globby"; +import {PassThrough} from "node:stream"; +import AbstractAdapter from "./AbstractAdapter.js"; + +const READ_ONLY_MODE = 0o444; +const ADAPTER_NAME = "FileSystem"; +/** + * File system resource adapter + * + * @public + * @class + * @alias @ui5/fs/adapters/FileSystem + * @extends @ui5/fs/adapters/AbstractAdapter + */ +class FileSystem extends AbstractAdapter { + /** + * The Constructor. + * + * @param {object} parameters Parameters + * @param {string} parameters.virBasePath + * Virtual base path. Must be absolute, POSIX-style, and must end with a slash + * @param {string} parameters.fsBasePath + * File System base path. Must be absolute and must use platform-specific path segment separators + * @param {string[]} [parameters.excludes] List of glob patterns to exclude + * @param {object} [parameters.useGitignore=false] + * Whether to apply any excludes defined in an optional .gitignore in the given fsBasePath directory + * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any) + */ + constructor({virBasePath, project, fsBasePath, excludes, useGitignore=false}) { + super({virBasePath, project, excludes}); + + if (!fsBasePath) { + throw new Error(`Unable to create adapter: Missing parameter 'fsBasePath'`); + } + + // Ensure path is resolved to an absolute path, ending with a slash (or backslash on Windows) + // path.resolve will always remove any trailing segment separator + this._fsBasePath = path.join(path.resolve(fsBasePath), path.sep); + this._useGitignore = !!useGitignore; + } + + /** + * Locate resources by glob. + * + * @private + * @param {Array} patterns Array of glob patterns + * @param {object} [options={}] glob options + * @param {boolean} [options.nodir=true] Do not match directories + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + async _runGlob(patterns, options = {nodir: true}, trace) { + const opt = { + cwd: this._fsBasePath, + dot: true, + onlyFiles: options.nodir, + followSymbolicLinks: false, + gitignore: this._useGitignore, + }; + trace.globCall(); + + const promises = []; + if (!opt.onlyFiles && patterns.includes("")) { // Match physical root directory + promises.push(new Promise((resolve, reject) => { + fs.stat(this._fsBasePath, (err, stat) => { + if (err) { + reject(err); + } else { + resolve(this._createResource({ + project: this._project, + statInfo: stat, + path: this._virBaseDir, + sourceMetadata: { + adapter: ADAPTER_NAME, + fsPath: this._fsBasePath + }, + createStream: () => { + return fs.createReadStream(this._fsBasePath); + } + })); + } + }); + })); + } + + // Remove empty string glob patterns + // Starting with globby v8 or v9 empty glob patterns "" act like "**" + // Micromatch throws on empty strings. We just ignore them since they are + // typically caused by our normalization in the AbstractAdapter + const globbyPatterns = patterns.filter((pattern) => { + return pattern !== ""; + }); + if (globbyPatterns.length > 0) { + const matches = await globby(globbyPatterns, opt); + for (let i = matches.length - 1; i >= 0; i--) { + promises.push(new Promise((resolve, reject) => { + const virPath = (this._virBasePath + matches[i]); + const relPath = this._resolveVirtualPathToBase(virPath); + if (relPath === null) { + // Match is likely outside adapter base path + log.verbose( + `Failed to resolve virtual path of glob match '${virPath}': Path must start with ` + + `the configured virtual base path of the adapter. Base path: '${this._virBasePath}'`); + resolve(null); + } + const fsPath = this._resolveToFileSystem(relPath); + + // Workaround for not getting the stat from the glob + fs.stat(fsPath, (err, stat) => { + if (err) { + reject(err); + } else { + resolve(this._createResource({ + project: this._project, + statInfo: stat, + path: virPath, + sourceMetadata: { + adapter: ADAPTER_NAME, + fsPath: fsPath + }, + createStream: () => { + return fs.createReadStream(fsPath); + } + })); + } + }); + })); + } + } + const results = await Promise.all(promises); + + // Remove 'null' results + return results.filter(($) => $); + } + + /** + * Locate a resource by path. + * + * @private + * @param {string} virPath Absolute virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource or null if not found + */ + async _byPath(virPath, options, trace) { + const relPath = this._resolveVirtualPathToBase(virPath); + + if (relPath === null) { + // Neither starts with basePath, nor equals baseDirectory + if (!options.nodir && this._virBasePath.startsWith(virPath)) { + // Create virtual directories for the virtual base path (which has to exist) + // TODO: Maybe improve this by actually matching the base paths segments to the virPath + return this._createResource({ + project: this._project, + statInfo: { // TODO: make closer to fs stat info + isDirectory: function() { + return true; + } + }, + path: virPath + }); + } else { + return null; + } + } + + const fsPath = this._resolveToFileSystem(relPath); + + trace.pathCall(); + + if (this._useGitignore) { + if (!this._isGitIgnored) { + this._isGitIgnored = await isGitIgnored({ + cwd: this._fsBasePath + }); + } + // Check whether path should be ignored + if (this._isGitIgnored(fsPath)) { + // Path is ignored by .gitignore + return null; + } + } + + try { + const statInfo = await stat(fsPath); + if (options.nodir && statInfo.isDirectory()) { + return null; + } + const resourceOptions = { + project: this._project, + statInfo, + path: virPath, + sourceMetadata: { + adapter: ADAPTER_NAME, + fsPath + } + }; + + if (!statInfo.isDirectory()) { + // Add content as lazy stream + resourceOptions.createStream = function() { + return fs.createReadStream(fsPath); + }; + } + + return this._createResource(resourceOptions); + } catch (err) { + if (err.code === "ENOENT") { // "File or directory does not exist" + return null; + } else { + throw err; + } + } + } + + /** + * Writes the content of a resource to a path. + * + * @private + * @param {@ui5/fs/Resource} resource Resource to write + * @param {object} [options] + * @param {boolean} [options.readOnly] Whether the resource content shall be written read-only + * Do not use in conjunction with the drain option. + * The written file will be used as the new source of this resources content. + * Therefore the written file should not be altered by any means. + * Activating this option might improve overall memory consumption. + * @param {boolean} [options.drain] Whether the resource content shall be emptied during the write process. + * Do not use in conjunction with the readOnly option. + * Activating this option might improve overall memory consumption. + * This should be used in cases where this is the last access to the resource. + * E.g. the final write of a resource after all processing is finished. + * @returns {Promise} Promise resolving once data has been written + */ + async _write(resource, {drain, readOnly}) { + resource = this._migrateResource(resource); + if (resource instanceof Promise) { + // Only await if the migrate function returned a promise + // Otherwise await would automatically create a Promise, causing unwanted overhead + resource = await resource; + } + this._assignProjectToResource(resource); + if (drain && readOnly) { + throw new Error(`Error while writing resource ${resource.getPath()}: ` + + "Do not use options 'drain' and 'readOnly' at the same time."); + } + + const relPath = this._resolveVirtualPathToBase(resource.getPath(), true); + const fsPath = this._resolveToFileSystem(relPath); + const dirPath = path.dirname(fsPath); + + await mkdir(dirPath, {recursive: true}); + + const sourceMetadata = resource.getSourceMetadata(); + if (sourceMetadata && sourceMetadata.adapter === ADAPTER_NAME && sourceMetadata.fsPath) { + // Resource has been created by FileSystem adapter. This means it might require special handling + + /* The following code covers these four conditions: + 1. FS-paths not equal + Resource not modified => Shortcut: Use fs.copyFile + 2. FS-paths equal + Resource not modified => Shortcut: Skip write altogether + 3. FS-paths equal + Resource modified => Drain stream into buffer. Later write from buffer as usual + 4. FS-paths not equal + Resource modified => No special handling. Write from stream or buffer + */ + + if (sourceMetadata.fsPath !== fsPath && !sourceMetadata.contentModified) { + // Shortcut: fs.copyFile can be used when the resource hasn't been modified + log.silly(`Resource hasn't been modified. Copying resource from ${sourceMetadata.fsPath} to ${fsPath}`); + await copyFile(sourceMetadata.fsPath, fsPath); + if (readOnly) { + await chmod(fsPath, READ_ONLY_MODE); + } + return; + } else if (sourceMetadata.fsPath === fsPath && !sourceMetadata.contentModified) { + log.silly( + `Resource hasn't been modified, target path equals source path. Skipping write to ${fsPath}`); + if (readOnly) { + await chmod(fsPath, READ_ONLY_MODE); + } + return; + } else if (sourceMetadata.fsPath === fsPath && sourceMetadata.contentModified) { + // Resource has been modified. Make sure all streams are drained to prevent + // issues caused by piping the original read-stream into a write-stream for the same path + await resource.getBuffer(); + } else {/* Different paths + modifications require no special handling */} + } + + log.silly(`Writing to ${fsPath}`); + + await new Promise((resolve, reject) => { + let contentStream; + + if (drain || readOnly) { + // Stream will be drained + contentStream = resource.getStream(); + + contentStream.on("error", (err) => { + reject(err); + }); + } else { + // Transform stream into buffer before writing + contentStream = new PassThrough(); + const buffers = []; + contentStream.on("error", (err) => { + reject(err); + }); + contentStream.on("data", (data) => { + buffers.push(data); + }); + contentStream.on("end", () => { + const buffer = Buffer.concat(buffers); + resource.setBuffer(buffer); + }); + resource.getStream().pipe(contentStream); + } + + const writeOptions = {}; + if (readOnly) { + writeOptions.mode = READ_ONLY_MODE; + } + + const write = fs.createWriteStream(fsPath, writeOptions); + write.on("error", (err) => { + reject(err); + }); + write.on("close", (ex) => { + resolve(); + }); + contentStream.pipe(write); + }); + + if (readOnly) { + if (sourceMetadata?.fsPath === fsPath) { + // When streaming into the same file, permissions need to be changed explicitly + await chmod(fsPath, READ_ONLY_MODE); + } + + // In case of readOnly, we drained the stream and can now set a new callback + // for creating a stream from written file + // This should be identical to buffering the resource content in memory, since the written file + // can not be modified. + // We chose this approach to be more memory efficient in scenarios where readOnly is used + resource.setStream(function() { + return fs.createReadStream(fsPath); + }); + } + } + + _resolveToFileSystem(relPath) { + const fsPath = path.join(this._fsBasePath, relPath); + + if (!fsPath.startsWith(this._fsBasePath)) { + log.verbose(`Failed to resolve virtual path internally: ${relPath}`); + log.verbose(` Adapter base path: ${this._fsBasePath}`); + log.verbose(` Resulting path: ${fsPath}`); + throw new Error( + `Error while resolving internal virtual path: '${relPath}' resolves ` + + `to a directory not accessible by this File System adapter instance`); + } + return fsPath; + } +} + +export default FileSystem; diff --git a/packages/fs/lib/adapters/Memory.js b/packages/fs/lib/adapters/Memory.js new file mode 100644 index 00000000000..35be99cf953 --- /dev/null +++ b/packages/fs/lib/adapters/Memory.js @@ -0,0 +1,177 @@ +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:adapters:Memory"); +import micromatch from "micromatch"; +import AbstractAdapter from "./AbstractAdapter.js"; + +const ADAPTER_NAME = "Memory"; + +/** + * Virtual resource Adapter + * + * @public + * @class + * @alias @ui5/fs/adapters/Memory + * @extends @ui5/fs/adapters/AbstractAdapter + */ +class Memory extends AbstractAdapter { + /** + * The constructor. + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.virBasePath + * Virtual base path. Must be absolute, POSIX-style, and must end with a slash + * @param {string[]} [parameters.excludes] List of glob patterns to exclude + * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any) + */ + constructor({virBasePath, project, excludes}) { + super({virBasePath, project, excludes}); + this._virFiles = Object.create(null); // map full of files + this._virDirs = Object.create(null); // map full of directories + } + + /** + * Matches and returns resources from a given map (either _virFiles or _virDirs). + * + * @private + * @param {string[]} patterns + * @param {object} resourceMap + * @returns {Promise} + */ + async _matchPatterns(patterns, resourceMap) { + const resourcePaths = Object.keys(resourceMap); + const matchedPaths = micromatch(resourcePaths, patterns, { + dot: true + }); + return await Promise.all(matchedPaths.map((virPath) => { + const resource = resourceMap[virPath]; + if (resource) { + return this._cloneResource(resource); + } + })); + } + + async _cloneResource(resource) { + const clonedResource = await resource.clone(); + if (this._project) { + clonedResource.setProject(this._project); + } + return clonedResource; + } + + /** + * Locate resources by glob. + * + * @private + * @param {Array} patterns array of glob patterns + * @param {object} [options={}] glob options + * @param {boolean} [options.nodir=true] Do not match directories + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + async _runGlob(patterns, options = {nodir: true}, trace) { + if (patterns[0] === "" && !options.nodir) { // Match virtual root directory + return [ + this._createResource({ + project: this._project, + statInfo: { // TODO: make closer to fs stat info + isDirectory: function() { + return true; + } + }, + sourceMetadata: { + adapter: ADAPTER_NAME + }, + path: this._virBasePath.slice(0, -1) + }) + ]; + } + + let matchedResources = await this._matchPatterns(patterns, this._virFiles); + + if (!options.nodir) { + const matchedDirs = await this._matchPatterns(patterns, this._virDirs); + matchedResources = matchedResources.concat(matchedDirs); + } + + return matchedResources; + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing.Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource + */ + async _byPath(virPath, options, trace) { + const relPath = this._resolveVirtualPathToBase(virPath); + if (relPath === null) { + return null; + } + + trace.pathCall(); + + const resource = this._virFiles[relPath]; + + if (!resource || (options.nodir && resource.getStatInfo().isDirectory())) { + return null; + } else { + return await this._cloneResource(resource); + } + } + + /** + * Writes the content of a resource to a path. + * + * @private + * @param {@ui5/fs/Resource} resource The Resource to write + * @returns {Promise} Promise resolving once data has been written + */ + async _write(resource) { + resource = this._migrateResource(resource); + if (resource instanceof Promise) { + // Only await if the migrate function returned a promise + // Otherwise await would automatically create a Promise, causing unwanted overhead + resource = await resource; + } + this._assignProjectToResource(resource); + const relPath = this._resolveVirtualPathToBase(resource.getPath(), true); + log.silly(`Writing to virtual path ${resource.getPath()}`); + this._virFiles[relPath] = await resource.clone(); + + // Add virtual directories for all path segments of the written resource + // TODO: Add tests for all this + const pathSegments = relPath.split("/"); + pathSegments.pop(); // Remove last segment representing the resource itself + + pathSegments.forEach((segment, i) => { + if (i >= 1) { + segment = pathSegments[i - 1] + "/" + segment; + } + pathSegments[i] = segment; + }); + + for (let i = pathSegments.length - 1; i >= 0; i--) { + const segment = pathSegments[i]; + if (!this._virDirs[segment]) { + this._virDirs[segment] = this._createResource({ + project: this._project, + sourceMetadata: { + adapter: ADAPTER_NAME + }, + statInfo: { // TODO: make closer to fs stat info + isDirectory: function() { + return true; + } + }, + path: this._virBasePath + segment + }); + } + } + } +} + +export default Memory; diff --git a/packages/fs/lib/fsInterface.js b/packages/fs/lib/fsInterface.js new file mode 100644 index 00000000000..99d97712daa --- /dev/null +++ b/packages/fs/lib/fsInterface.js @@ -0,0 +1,92 @@ +function toPosix(inputPath) { + return inputPath.replace(/\\/g, "/"); +} + +/** + * @public + * @module @ui5/fs/fsInterface + */ + +/** + * Wraps readers to access them through a [Node.js fs]{@link https://nodejs.org/api/fs.html} styled interface. + * + * @public + * @function default + * @static + * @param {@ui5/fs/AbstractReader} reader Resource Reader or Collection + * + * @returns {object} Object with [Node.js fs]{@link https://nodejs.org/api/fs.html} styled functions + * [readFile]{@link https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback}, + * [stat]{@link https://nodejs.org/api/fs.html#fs_fs_stat_path_options_callback}, + * [readdir]{@link https://nodejs.org/api/fs.html#fs_fs_readdir_path_options_callback} and + * [mkdir]{@link https://nodejs.org/api/fs.html#fs_fs_mkdir_path_options_callback} + */ +function fsInterface(reader) { + return { + readFile(fsPath, options, callback) { + if (typeof options === "function") { + callback = options; + options = undefined; + } + if (typeof options === "string") { + options = {encoding: options}; + } + const posixPath = toPosix(fsPath); + reader.byPath(posixPath, { + nodir: false + }).then(function(resource) { + if (!resource) { + const error = new Error(`ENOENT: no such file or directory, open '${fsPath}'`); + error.code = "ENOENT"; // "File or directory does not exist" + callback(error); + return; + } + + return resource.getBuffer().then(function(buffer) { + let res; + + if (options && options.encoding) { + res = buffer.toString(options.encoding); + } else { + res = buffer; + } + + callback(null, res); + }); + }).catch(callback); + }, + stat(fsPath, callback) { + const posixPath = toPosix(fsPath); + reader.byPath(posixPath, { + nodir: false + }).then(function(resource) { + if (!resource) { + const error = new Error(`ENOENT: no such file or directory, stat '${fsPath}'`); + error.code = "ENOENT"; // "File or directory does not exist" + callback(error); + } else { + callback(null, resource.getStatInfo()); + } + }).catch(callback); + }, + readdir(fsPath, callback) { + let posixPath = toPosix(fsPath); + if (!posixPath.match(/\/$/)) { + // Add trailing slash if not present + posixPath += "/"; + } + reader.byGlob(posixPath + "*", { + nodir: false + }).then((resources) => { + const files = resources.map((resource) => { + return resource.getName(); + }); + callback(null, files); + }).catch(callback); + }, + mkdir(fsPath, callback) { + setTimeout(callback, 0); + } + }; +} +export default fsInterface; diff --git a/packages/fs/lib/readers/Filter.js b/packages/fs/lib/readers/Filter.js new file mode 100644 index 00000000000..b95654daa29 --- /dev/null +++ b/packages/fs/lib/readers/Filter.js @@ -0,0 +1,75 @@ +import AbstractReader from "../AbstractReader.js"; + +/** + * A reader that allows dynamic filtering of resources passed through it + * + * @public + * @class + * @alias @ui5/fs/readers/Filter + * @extends @ui5/fs/AbstractReader + */ +class Filter extends AbstractReader { + /** + * Filter callback + * + * @public + * @callback @ui5/fs/readers/Filter~callback + * @param {@ui5/fs/Resource} resource Resource to test + * @returns {boolean} Whether to keep the resource + */ + + /** + * Constructor + * + * @public + * @param {object} parameters Parameters + * @param {@ui5/fs/AbstractReader} parameters.reader The resource reader or collection to wrap + * @param {@ui5/fs/readers/Filter~callback} parameters.callback + * Filter function. Will be called for every resource read through this reader. + */ + constructor({reader, callback}) { + super(); + if (!reader) { + throw new Error(`Missing parameter "reader"`); + } + if (!callback) { + throw new Error(`Missing parameter "callback"`); + } + this._reader = reader; + this._callback = callback; + } + + /** + * Locates resources by glob. + * + * @private + * @param {string|string[]} pattern glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing/Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + async _byGlob(pattern, options, trace) { + const result = await this._reader._byGlob(pattern, options, trace); + return result.filter(this._callback); + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing/Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource + */ + async _byPath(virPath, options, trace) { + const result = await this._reader._byPath(virPath, options, trace); + if (result && !this._callback(result)) { + return null; + } + return result; + } +} + +export default Filter; diff --git a/packages/fs/lib/readers/Link.js b/packages/fs/lib/readers/Link.js new file mode 100644 index 00000000000..dd2d40b3ce3 --- /dev/null +++ b/packages/fs/lib/readers/Link.js @@ -0,0 +1,139 @@ +import AbstractReader from "../AbstractReader.js"; +import ResourceFacade from "../ResourceFacade.js"; +import {prefixGlobPattern} from "../resourceFactory.js"; +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:readers:Link"); + +/** + * A reader that allows for rewriting paths segments of all resources passed through it. + * + * @example + * import Link from "@ui5/fs/readers/Link"; + * const linkedReader = new Link({ + * reader: sourceReader, + * pathMapping: { + * linkPath: `/app`, + * targetPath: `/resources/my-app-name/` + * } + * }); + * + * // The following resolves with a @ui5/fs/ResourceFacade of the resource + * // located at "/resources/my-app-name/Component.js" in the sourceReader + * const resource = await linkedReader.byPath("/app/Component.js"); + * + * @public + * @class + * @alias @ui5/fs/readers/Link + * @extends @ui5/fs/AbstractReader + */ +class Link extends AbstractReader { + /** + * Path mapping for a [Link]{@link @ui5/fs/readers/Link} + * + * @public + * @typedef {object} @ui5/fs/readers/Link/PathMapping + * @property {string} linkPath Path to match and replace in the requested path or pattern + * @property {string} targetPath Path to use as a replacement in the request for the source reader + */ + + /** + * Constructor + * + * @public + * @param {object} parameters Parameters + * @param {@ui5/fs/AbstractReader} parameters.reader The resource reader or collection to wrap + * @param {@ui5/fs/readers/Link/PathMapping} parameters.pathMapping + */ + constructor({reader, pathMapping}) { + super(); + if (!reader) { + throw new Error(`Missing parameter "reader"`); + } + if (!pathMapping) { + throw new Error(`Missing parameter "pathMapping"`); + } + this._reader = reader; + this._pathMapping = pathMapping; + Link._validatePathMapping(pathMapping); + } + + /** + * Locates resources by glob. + * + * @private + * @param {string|string[]} patterns glob pattern as string or an array of + * glob patterns for virtual directory structure + * @param {object} options glob options + * @param {@ui5/fs/tracing/Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources + */ + async _byGlob(patterns, options, trace) { + if (!(patterns instanceof Array)) { + patterns = [patterns]; + } + patterns = patterns.map((pattern) => { + if (pattern.startsWith(this._pathMapping.linkPath)) { + pattern = pattern.substr(this._pathMapping.linkPath.length); + } + return prefixGlobPattern(pattern, this._pathMapping.targetPath); + }); + + // Flatten prefixed patterns + patterns = Array.prototype.concat.apply([], patterns); + + // Keep resource's internal path unchanged for now + const resources = await this._reader._byGlob(patterns, options, trace); + return resources.map((resource) => { + const resourcePath = resource.getPath(); + if (resourcePath.startsWith(this._pathMapping.targetPath)) { + return new ResourceFacade({ + resource, + path: this._pathMapping.linkPath + resourcePath.substr(this._pathMapping.targetPath.length) + }); + } + }); + } + + /** + * Locates resources by path. + * + * @private + * @param {string} virPath Virtual path + * @param {object} options Options + * @param {@ui5/fs/tracing/Trace} trace Trace instance + * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource + */ + async _byPath(virPath, options, trace) { + if (!virPath.startsWith(this._pathMapping.linkPath)) { + return null; + } + const targetPath = this._pathMapping.targetPath + virPath.substr(this._pathMapping.linkPath.length); + log.silly(`byPath: Rewriting virtual path ${virPath} to ${targetPath}`); + + const resource = await this._reader._byPath(targetPath, options, trace); + if (resource) { + return new ResourceFacade({ + resource, + path: this._pathMapping.linkPath + resource.getPath().substr(this._pathMapping.targetPath.length) + }); + } + return null; + } + + static _validatePathMapping({linkPath, targetPath}) { + if (!linkPath) { + throw new Error(`Path mapping is missing attribute "linkPath"`); + } + if (!targetPath) { + throw new Error(`Path mapping is missing attribute "targetPath"`); + } + if (!linkPath.endsWith("/")) { + throw new Error(`Link path must end with a slash: ${linkPath}`); + } + if (!targetPath.endsWith("/")) { + throw new Error(`Target path must end with a slash: ${targetPath}`); + } + } +} + +export default Link; diff --git a/packages/fs/lib/resourceFactory.js b/packages/fs/lib/resourceFactory.js new file mode 100644 index 00000000000..ba48d76de1d --- /dev/null +++ b/packages/fs/lib/resourceFactory.js @@ -0,0 +1,280 @@ +import path from "node:path"; +import {minimatch} from "minimatch"; +import DuplexCollection from "./DuplexCollection.js"; +import FsAdapter from "./adapters/FileSystem.js"; +import MemAdapter from "./adapters/Memory.js"; +import ReaderCollection from "./ReaderCollection.js"; +import ReaderCollectionPrioritized from "./ReaderCollectionPrioritized.js"; +import Resource from "./Resource.js"; +import WriterCollection from "./WriterCollection.js"; +import Filter from "./readers/Filter.js"; +import Link from "./readers/Link.js"; +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:resourceFactory"); + +/** + * @module @ui5/fs/resourceFactory + * @description A collection of resource related APIs + * @public + */ + +/** + * Creates a resource ReaderWriter. + * + * If a file system base path is given, file system resource ReaderWriter is returned. + * In any other case a virtual one. + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.virBasePath Virtual base path. Must be absolute, POSIX-style, and must end with a slash + * @param {string} [parameters.fsBasePath] + * File System base path. + * If this parameter is supplied, a File System adapter will be created instead of a Memory adapter. + * The provided path must be absolute and must use platform-specific path segment separators. + * @param {string[]} [parameters.excludes] List of glob patterns to exclude + * @param {object} [parameters.useGitignore=false] + * Whether to apply any excludes defined in an optional .gitignore in the base directory. + * This parameter only takes effect in conjunction with the fsBasePath parameter. + * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any) + * @returns {@ui5/fs/adapters/FileSystem|@ui5/fs/adapters/Memory} File System- or Virtual Adapter + */ +export function createAdapter({fsBasePath, virBasePath, project, excludes, useGitignore}) { + if (fsBasePath) { + return new FsAdapter({fsBasePath, virBasePath, project, excludes, useGitignore}); + } else { + return new MemAdapter({virBasePath, project, excludes}); + } +} + +/** + * Creates a File System adapter and wraps it in a ReaderCollection + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.virBasePath Virtual base path. Must be absolute, POSIX-style, and must end with a slash + * @param {string} parameters.fsBasePath + * File System base path. Must be absolute and must use platform-specific path segment separators + * @param {object} [parameters.project] Experimental, internal parameter. Do not use + * @param {string[]} [parameters.excludes] List of glob patterns to exclude + * @param {string} [parameters.name] Name for the reader collection + * @returns {@ui5/fs/ReaderCollection} Reader collection wrapping an adapter + */ +export function createReader({fsBasePath, virBasePath, project, excludes = [], name}) { + if (!fsBasePath) { + // Creating a reader with a memory adapter seems pointless right now + // since there would be no way to fill the adapter with resources + throw new Error(`Unable to create reader: Missing parameter "fsBasePath"`); + } + let normalizedExcludes = excludes; + // If a project is supplied, and that project is of type application, + // Prefix all exclude patterns with the virtual base path (unless it already starts with that) + // TODO 4.0: // TODO specVersion 4.0: Disallow excludes without namespaced prefix in configuration + // Specifying an exclude for "/test" is disambigous as it neither reflects the source path nor the + // ui5 runtime path of the excluded resources. Therefore, only allow paths like /resources//test + // starting with specVersion 4.0 + if (excludes.length && project && project.getType() === "application") { + normalizedExcludes = excludes.map((pattern) => { + if (pattern.startsWith(virBasePath) || pattern.startsWith("!" + virBasePath)) { + return pattern; + } + log.verbose( + `Prefixing exclude pattern defined in application project ${project.getName()}: ${pattern}`); + return prefixGlobPattern(pattern, virBasePath); + }); + // Flatten list of patterns + normalizedExcludes = Array.prototype.concat.apply([], normalizedExcludes); + log.verbose(`Effective exclude patterns for application project ${project.getName()}:\n` + + normalizedExcludes.join(", ")); + } + return new ReaderCollection({ + name, + readers: [createAdapter({ + fsBasePath, + virBasePath, + project, + excludes: normalizedExcludes + })] + }); +} + +/** + * Creates a ReaderCollection + * + * @public + * @param {object} parameters Parameters + * @param {string} parameters.name The collection name + * @param {@ui5/fs/AbstractReader[]} parameters.readers List of resource readers (all tried in parallel) + * @returns {@ui5/fs/ReaderCollection} Reader collection wrapping provided readers + */ +export function createReaderCollection({name, readers}) { + return new ReaderCollection({ + name, + readers + }); +} + +/** + * Creates a ReaderCollectionPrioritized + * + * @public + * @param {object} parameters + * @param {string} parameters.name The collection name + * @param {@ui5/fs/AbstractReader[]} parameters.readers Prioritized list of resource readers + * (first is tried first) + * @returns {@ui5/fs/ReaderCollectionPrioritized} Reader collection wrapping provided readers + */ +export function createReaderCollectionPrioritized({name, readers}) { + return new ReaderCollectionPrioritized({ + name, + readers + }); +} + +/** + * Creates a WriterCollection + * + * @public + * @param {object} parameters + * @param {string} parameters.name The collection name + * @param {object.} parameters.writerMapping Mapping of virtual base + * paths to writers. Path are matched greedy + * @returns {@ui5/fs/WriterCollection} Writer collection wrapping provided writers + */ +export function createWriterCollection({name, writerMapping}) { + return new WriterCollection({ + name, + writerMapping + }); +} + +/** + * Creates a [Resource]{@link @ui5/fs/Resource}. + * Accepts the same parameters as the [Resource]{@link @ui5/fs/Resource} constructor. + * + * @public + * @param {object} parameters Parameters to be passed to the resource constructor + * @returns {@ui5/fs/Resource} Resource + */ +export function createResource(parameters) { + return new Resource(parameters); +} + +/** + * Creates a Workspace + * + * A workspace is a DuplexCollection which reads from the project sources. It is used during the build process + * to write modified files into a separate writer, this is usually a Memory adapter. If a file already exists it is + * fetched from the memory to work on it in further build steps. + * + * @public + * @param {object} parameters + * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers + * @param {@ui5/fs/AbstractReaderWriter} [parameters.writer] A ReaderWriter instance which is + * only used for writing files. If not supplied, a Memory adapter will be created. + * @param {string} [parameters.name="workspace"] Name of the collection + * @param {string} [parameters.virBasePath="/"] Virtual base path + * @returns {@ui5/fs/DuplexCollection} DuplexCollection which wraps the provided resource locators + */ +export function createWorkspace({reader, writer, virBasePath = "/", name = "workspace"}) { + if (!writer) { + writer = new MemAdapter({ + virBasePath + }); + } + + return new DuplexCollection({ + reader, + writer, + name + }); +} + +/** + * Create a [Filter-Reader]{@link @ui5/fs/readers/Filter} with the given reader. + * The provided callback is called for every resource that is retrieved through the + * reader and decides whether the resource shall be passed on or dropped. + * + * @public + * @param {object} parameters + * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers + * @param {@ui5/fs/readers/Filter~callback} parameters.callback + * Filter function. Will be called for every resource passed through this reader. + * @returns {@ui5/fs/readers/Filter} Reader instance + */ +export function createFilterReader(parameters) { + return new Filter(parameters); +} + +/** + * Create a [Link-Reader]{@link @ui5/fs/readers/Filter} with the given reader. + * The provided path mapping allows for rewriting paths segments of all resources passed through it. + * + * @example + * import {createLinkReader} from "@ui5/fs/resourceFactory"; + * const linkedReader = createLinkReader({ + * reader: sourceReader, + * pathMapping: { + * linkPath: `/app`, + * targetPath: `/resources/my-app-name/` + * } + * }); + * + * // The following resolves with a @ui5/fs/ResourceFacade of the resource + * // located at "/resources/my-app-name/Component.js" in the sourceReader + * const resource = await linkedReader.byPath("/app/Component.js"); + * + * @public + * @param {object} parameters + * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers + * @param {@ui5/fs/readers/Link/PathMapping} parameters.pathMapping + * @returns {@ui5/fs/readers/Link} Reader instance + */ +export function createLinkReader(parameters) { + return new Link(parameters); +} + +/** + * Create a [Link-Reader]{@link @ui5/fs/readers/Link} where all requests are prefixed with + * /resources/. + * + * This simulates "flat" resource access, which is for example common for projects of type + * "application". + * + * @public + * @param {object} parameters + * @param {@ui5/fs/AbstractReader} parameters.reader Single reader or collection of readers + * @param {string} parameters.namespace Project namespace + * @returns {@ui5/fs/readers/Link} Reader instance + */ +export function createFlatReader({reader, namespace}) { + return new Link({ + reader: reader, + pathMapping: { + linkPath: `/`, + targetPath: `/resources/${namespace}/` + } + }); +} + +/** + * Normalizes virtual glob patterns by prefixing them with + * a given virtual base directory path + * + * @param {string} virPattern glob pattern for virtual directory structure + * @param {string} virBaseDir virtual base directory path to prefix the given patterns with + * @returns {string[]} A list of normalized glob patterns + */ +export function prefixGlobPattern(virPattern, virBaseDir) { + const mm = new minimatch.Minimatch(virPattern); + + const resultGlobs = []; + for (let i = 0; i < mm.globSet.length; i++) { + let resultPattern = path.posix.join(virBaseDir, mm.globSet[i]); + + if (mm.negate) { + resultPattern = "!" + resultPattern; + } + resultGlobs.push(resultPattern); + } + return resultGlobs; +} diff --git a/packages/fs/lib/tracing/Trace.js b/packages/fs/lib/tracing/Trace.js new file mode 100644 index 00000000000..30ac9e19f4e --- /dev/null +++ b/packages/fs/lib/tracing/Trace.js @@ -0,0 +1,97 @@ +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:tracing:Trace"); +const logGlobs = getLogger("resources:tracing:Trace:globs"); +const logPaths = getLogger("resources:tracing:Trace:paths"); +import prettyHrtime from "pretty-hrtime"; +import summaryTrace from "./traceSummary.js"; +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Trace + * + * @private + * @class + */ +class Trace { + constructor(name) { + if (!log.isLevelEnabled("silly")) { + return; + } + this._name = name; + this._startTime = process.hrtime(); + this._globCalls = 0; + this._pathCalls = 0; + this._collections = Object.create(null); + summaryTrace.traceStarted(); + } + + globCall() { + if (!log.isLevelEnabled("silly")) { + return; + } + this._globCalls++; + summaryTrace.globCall(); + } + + pathCall() { + if (!log.isLevelEnabled("silly")) { + return; + } + this._pathCalls++; + summaryTrace.pathCall(); + } + + collection(name) { + if (!log.isLevelEnabled("silly")) { + return; + } + const collection = this._collections[name]; + if (collection) { + this._collections[name].calls++; + } else { + this._collections[name] = { + calls: 1 + }; + } + summaryTrace.collection(name); + } + + printReport() { + if (!log.isLevelEnabled("silly")) { + return; + } + let report = ""; + const timeDiff = process.hrtime(this._startTime); + const time = prettyHrtime(timeDiff); + const colCount = Object.keys(this._collections).length; + + report += `[Trace: ${this._name}\n`; + report += ` ${time} elapsed time \n`; + if (this._globCalls) { + report += ` ${this._globCalls} glob executions\n`; + } + if (this._pathCalls) { + report += ` ${this._pathCalls} path stats\n`; + } + report += ` ${colCount} reader-collections involed:\n`; + + for (const coll in this._collections) { + if (hasOwnProperty.call(this._collections, coll)) { + report += ` ${this._collections[coll].calls}x ${coll}\n`; + } + } + report += "======================]"; + + if (this._globCalls && this._pathCalls) { + log.silly(report); + } else if (this._globCalls) { + logGlobs.silly(report); + } else { + logPaths.silly(report); + } + + summaryTrace.traceEnded(); + } +} + +export default Trace; diff --git a/packages/fs/lib/tracing/traceSummary.js b/packages/fs/lib/tracing/traceSummary.js new file mode 100644 index 00000000000..79f89378840 --- /dev/null +++ b/packages/fs/lib/tracing/traceSummary.js @@ -0,0 +1,125 @@ +import {getLogger} from "@ui5/logger"; +const log = getLogger("resources:tracing:total"); + +import prettyHrtime from "pretty-hrtime"; +const hasOwnProperty = Object.prototype.hasOwnProperty; +let timeoutId; +let active = false; +let tracesRunning = 0; +let traceData; + +function init() { + traceData = { + startTime: process.hrtime(), + pathCalls: 0, + globCalls: 0, + collections: {}, + traceCalls: 0 + }; + active = true; +} + +function reset() { + traceData = null; + active = false; +} + +function report() { + let report = ""; + const time = prettyHrtime(traceData.timeDiff); + const colCount = Object.keys(traceData.collections).length; + + report += "==========================\n[=> TRACE SUMMARY:\n"; + report += ` ${time} elapsed time \n`; + report += ` ${traceData.traceCalls} trace calls \n`; + if (traceData.globCalls) { + report += ` ${traceData.globCalls} glob executions\n`; + } + if (traceData.pathCalls) { + report += ` ${traceData.pathCalls} path stats\n`; + } + report += ` ${colCount} rl-collections involed:\n`; + + for (const coll in traceData.collections) { + if (hasOwnProperty.call(traceData.collections, coll)) { + report += ` ${traceData.collections[coll].calls}x ${coll}\n`; + } + } + report += "======================]"; + log.silly(report); +} + +function someTraceStarted() { + if (!log.isLevelEnabled("silly")) { + return; + } + if (!traceData) { + init(); + } + tracesRunning++; + traceData.traceCalls++; + + if (timeoutId) { + clearTimeout(timeoutId); + } +} + +function someTraceEnded() { + return new Promise(function(resolve, reject) { + if (!active) { + resolve(); + return; + } + tracesRunning--; + if (tracesRunning > 0) { + resolve(); + return; + } + + if (timeoutId) { + clearTimeout(timeoutId); + } + traceData.timeDiff = process.hrtime(traceData.startTime); + timeoutId = setTimeout(function() { + report(); + reset(); + resolve(); + }, 2000); + }); +} + +function pathCall() { + if (!active) { + return; + } + traceData.pathCalls++; +} + +function globCall() { + if (!active) { + return; + } + traceData.globCalls++; +} + +function collection(name) { + if (!active) { + return; + } + const collection = traceData.collections[name]; + if (collection) { + traceData.collections[name].calls++; + } else { + traceData.collections[name] = { + calls: 1 + }; + } +} + +export default { + pathCall: pathCall, + globCall: globCall, + collection: collection, + traceStarted: someTraceStarted, + traceEnded: someTraceEnded +}; diff --git a/packages/fs/package-lock.json b/packages/fs/package-lock.json new file mode 100644 index 00000000000..77a1221aaa4 --- /dev/null +++ b/packages/fs/package-lock.json @@ -0,0 +1,7337 @@ +{ + "name": "@ui5/fs", + "version": "4.0.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@ui5/fs", + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "@ui5/logger": "^4.0.2", + "clone": "^2.1.2", + "escape-string-regexp": "^5.0.0", + "globby": "^14.1.0", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "minimatch": "^10.0.3", + "pretty-hrtime": "^1.0.3", + "random-int": "^3.1.0" + }, + "devDependencies": { + "@eslint/js": "^9.8.0", + "@istanbuljs/esm-loader-hook": "^0.3.0", + "ava": "^6.4.1", + "chokidar-cli": "^3.0.0", + "cross-env": "^7.0.3", + "depcheck": "^1.4.7", + "docdash": "^2.0.2", + "eslint": "^9.36.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-ava": "^15.1.0", + "eslint-plugin-jsdoc": "^60.0.0", + "esmock": "^2.7.3", + "globals": "^16.4.0", + "jsdoc": "^4.0.4", + "nyc": "^17.1.0", + "open-cli": "^8.0.0", + "rimraf": "^6.0.1", + "sinon": "^21.0.0", + "tap-xunit": "^2.4.1" + }, + "engines": { + "node": "^20.11.0 || >=22.0.0", + "npm": ">= 8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.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/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.58.0.tgz", + "integrity": "sha512-smMc5pDht/UVsCD3hhw/a/e/p8m0RdRYiluXToVfd+d4yaQQh7nn9bACjkk6nXJvat7EWPAxuFkMEFfrxeGa3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.43.0", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~5.4.0" + }, + "engines": { + "node": ">=20.11.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/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/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.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/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "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/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/@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/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/esm-loader-hook": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/esm-loader-hook/-/esm-loader-hook-0.3.0.tgz", + "integrity": "sha512-lEnYroBUYfNQuJDYrPvre8TSwPZnyIQv9qUT3gACvhr3igZr+BbrdyIcz4+2RnEXZzi12GqkUW600+QQPpIbVg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.7", + "@babel/plugin-syntax-decorators": "^7.25.9", + "@babel/preset-typescript": "^7.26.0", + "@istanbuljs/load-nyc-config": "^1.1.0", + "@istanbuljs/schema": "^0.1.3", + "babel-plugin-istanbul": "^6.0.0", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=16.12.0" + } + }, + "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/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/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", + "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", + "dev": true, + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "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==", + "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==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "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/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", + "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ui5/logger": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@ui5/logger/-/logger-4.0.2.tgz", + "integrity": "sha512-uscDCQyHFeenh4r2RbYuffTMn6IQdcNC1tXrQ4BF+apAFjmDGP11IHdAwVCKwxgyPrIC17HT2gub3ZugGM8kpQ==", + "dependencies": { + "chalk": "^5.6.0", + "cli-progress": "^3.12.0", + "figures": "^6.1.0" + }, + "engines": { + "node": "^20.11.0 || >=22.0.0", + "npm": ">= 8" + } + }, + "node_modules/@vercel/nft": { + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.29.4.tgz", + "integrity": "sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==", + "dev": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^10.4.5", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "dev": true + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "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/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "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/aggregate-error/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/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-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "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/anymatch/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/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "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/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-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true + }, + "node_modules/ava": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.4.1.tgz", + "integrity": "sha512-vxmPbi1gZx9zhAjHBgw81w/iEDKcrokeRk/fqDTyA2DQygZ0o+dUGRHFOtX8RA5N0heGJTTsIk7+xYxitDb61Q==", + "dev": true, + "dependencies": { + "@vercel/nft": "^0.29.4", + "acorn": "^8.15.0", + "acorn-walk": "^8.3.4", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.2.0", + "cbor": "^10.0.9", + "chalk": "^5.4.1", + "chunkd": "^2.0.1", + "ci-info": "^4.3.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.4.1", + "emittery": "^1.2.0", + "figures": "^6.1.0", + "globby": "^14.1.0", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.1.0", + "ms": "^2.1.3", + "p-map": "^7.0.3", + "package-config": "^5.0.0", + "picomatch": "^4.0.2", + "plur": "^5.1.0", + "pretty-ms": "^9.2.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^6.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^22 || ^23 || >=24" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "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/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/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "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": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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.2.1" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/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/caching-transform/node_modules/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/caniuse-lite": { + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "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/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cbor": { + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-10.0.11.tgz", + "integrity": "sha512-vIwORDd/WyB8Nc23o2zNN5RrtFGlR6Fca61TtjkUXueI3Jf2DOZDl1zsshvBntZ3wZHBM9ztjnkXSmzQDaq3WA==", + "dev": true, + "dependencies": { + "nofilter": "^3.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz", + "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "yargs": "^13.3.0" + }, + "bin": { + "chokidar": "index.js" + }, + "engines": { + "node": ">= 8.10.0" + } + }, + "node_modules/chokidar-cli/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/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/chokidar-cli/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/chokidar-cli/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/chokidar-cli/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar-cli/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/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/chokidar-cli/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/chokidar-cli/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/chokidar-cli/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true + }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "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-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "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/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/concordance/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "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/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "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/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/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "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/depcheck": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-1.4.7.tgz", + "integrity": "sha512-1lklS/bV5chOxwNKA/2XUUk/hPORp8zihZsXflr8x0kLwmcZ9Y9BsS6Hs3ssvA+2wUVbG0U2Ciqvm1SokNjPkA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.2", + "@vue/compiler-sfc": "^3.3.4", + "callsite": "^1.0.0", + "camelcase": "^6.3.0", + "cosmiconfig": "^7.1.0", + "debug": "^4.3.4", + "deps-regex": "^0.2.0", + "findup-sync": "^5.0.0", + "ignore": "^5.2.4", + "is-core-module": "^2.12.0", + "js-yaml": "^3.14.1", + "json5": "^2.2.3", + "lodash": "^4.17.21", + "minimatch": "^7.4.6", + "multimatch": "^5.0.0", + "please-upgrade-node": "^3.2.0", + "readdirp": "^3.6.0", + "require-package-name": "^2.0.1", + "resolve": "^1.22.3", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "yargs": "^16.2.0" + }, + "bin": { + "depcheck": "bin/depcheck.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/depcheck/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/depcheck/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/depcheck/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/depcheck/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/depcheck/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/depcheck/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/depcheck/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/depcheck/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/depcheck/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/depcheck/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/depcheck/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/depcheck/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/depcheck/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/deps-regex": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.2.0.tgz", + "integrity": "sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==", + "dev": true + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/docdash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/docdash/-/docdash-2.0.2.tgz", + "integrity": "sha512-3SDDheh9ddrwjzf6dPFe1a16M6ftstqTNjik2+1fx46l24H9dD2osT2q9y+nBEC1wWz4GIqA48JmicOLQ0R8xA==", + "dev": true, + "dependencies": { + "@jsdoc/salty": "^0.2.1" + } + }, + "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/electron-to-chromium": { + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "dev": true + }, + "node_modules/emittery": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.2.0.tgz", + "integrity": "sha512-KxdRyyFcS85pH3dnU8Y5yFUm2YJdaHwcBZWrfG8o89ZY9a13/f9itbN+YG3ELbBo9Pg5zvIozstmuV8bX13q6g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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==" + }, + "node_modules/enhance-visitors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/enhance-visitors/-/enhance-visitors-1.0.0.tgz", + "integrity": "sha512-+29eJLiUixTEDRaZ35Vu8jP3gPLNcQQkQkOQjLp2X+6cZGGPDD/uasbFzvLsJKnGZnvmyZ0srxudwOtskHeIDA==", + "dev": true, + "dependencies": { + "lodash": "^4.13.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-ava": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-15.1.0.tgz", + "integrity": "sha512-+6Zxk1uYW3mf7lxCLWIQsFYgn3hfuCMbsKc0MtqfloOz1F6fiV5/PaWEaLgkL1egrSQmnyR7vOFP1wSPJbVUbw==", + "dev": true, + "dependencies": { + "enhance-visitors": "^1.0.0", + "eslint-utils": "^3.0.0", + "espree": "^9.0.0", + "espurify": "^2.1.1", + "import-modules": "^2.1.0", + "micro-spelling-correcter": "^1.1.1", + "pkg-dir": "^5.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "peerDependencies": { + "eslint": ">=9" + } + }, + "node_modules/eslint-plugin-ava/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/eslint-plugin-ava/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/eslint-plugin-jsdoc": { + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-60.0.0.tgz", + "integrity": "sha512-K8D5fJOkeunX+exeOkOUx+P7ZCixdRBsM3QQ+M2aazSO+64pnjH6z03wWYGQ3XTjUxpI2QXOTSQ9E75bDKi5AA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.58.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.4.3", + "escape-string-regexp": "^4.0.0", + "espree": "^10.4.0", + "esquery": "^1.6.0", + "object-deep-merge": "^1.0.5", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=20.11.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/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-plugin-jsdoc/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/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/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/esmock": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esmock/-/esmock-2.7.3.tgz", + "integrity": "sha512-/M/YZOjgyLaVoY6K83pwCsGE1AJQnj4S4GyXLYgi/Y79KL8EeW6WU7Rmjc89UO7jv6ec8+j34rKeWOfiLeEu0A==", + "dev": true, + "engines": { + "node": ">=14.16.0" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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/espurify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/espurify/-/espurify-2.1.1.tgz", + "integrity": "sha512-zttWvnkhcDyGOhSH4vO2qCBILpdCMv/MX8lp4cqgRkQoDRGK2oZxi2GfWhlP2dIXmk7BaKeOTuzbHhyC68o8XQ==", + "dev": true + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "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/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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==", + "dev": true + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "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.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "18.7.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", + "dev": true, + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/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/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/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "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/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.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "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.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "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-fresh/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/import-modules": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-modules/-/import-modules-2.1.0.tgz", + "integrity": "sha512-8HEWcnkbGpovH9yInoisxaSoIg9Brbul+Ju3Kqe2UsYDUBJD/iQjSgEj0zPcTDPKfPp2fs5xlv1i+JSye/m1/A==", + "dev": true, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "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/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.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-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "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-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/istanbul-lib-processinfo/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/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/istanbul-lib-report/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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": "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/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-5.4.0.tgz", + "integrity": "sha512-F9GQ+F1ZU6qvSrZV8fNFpjDNf614YzR2eF6S0+XbDjAcUI28FSoXnYZFjQmb1kFx3rrJb5PnxUH3/Yti6fcM+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/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/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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/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/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "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/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/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "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.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "dev": true + }, + "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/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/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/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/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, + "node_modules/memoize": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.1.0.tgz", + "integrity": "sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micro-spelling-correcter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", + "integrity": "sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "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/multimatch/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/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "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-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "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/nyc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^3.3.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^6.0.2", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nyc/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/nyc/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/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/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/nyc/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/nyc/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/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/nyc/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nyc/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/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nyc/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/nyc/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/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-deep-merge": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-1.0.5.tgz", + "integrity": "sha512-3DioFgOzetbxbeUq8pB2NunXo8V0n4EvqsWM/cJoI6IA9zghd7cl/2pBOuWRf4dlvA+fcg5ugFMZaN2/RuoaGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "4.2.0" + } + }, + "node_modules/object-deep-merge/node_modules/type-fest": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.2.0.tgz", + "integrity": "sha512-5zknd7Dss75pMSED270A1RQS3KloqRJA9XbXLe0eCxyw7xXFb3rd+9B0UQ/0E+LQT6lnrLviEolYORlRWamn4w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-8.0.0.tgz", + "integrity": "sha512-3muD3BbfLyzl+aMVSEfn2FfOqGdPYR0O4KNnxXsLEPE2q9OSjBfJAaB6XKbrUzLgymoSMejvb5jpXJfru/Ko2A==", + "dev": true, + "dependencies": { + "file-type": "^18.7.0", + "get-stdin": "^9.0.0", + "meow": "^12.1.1", + "open": "^10.0.0", + "tempy": "^3.1.0" + }, + "bin": { + "open-cli": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/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/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "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/parent-module/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/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "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-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true + }, + "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.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "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/peek-readable": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz", + "integrity": "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-dir/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/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "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/random-int": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/random-int/-/random-int-3.1.0.tgz", + "integrity": "sha512-h8CRz8cpvzj0hC/iH/1Gapgcl2TQ6xtnCpyOI5WvWfXf/yrDx2DOU+tD9rX23j36IF11xg1KqB9W11Z18JPMdw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "dev": true, + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/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/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "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/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/require-package-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", + "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==", + "dev": true + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "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-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", + "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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/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/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/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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/sinon": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.5", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "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-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/spawn-wrap/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/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/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/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true + }, + "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/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-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==", + "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/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/string-width-cjs/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/string-width-cjs/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/string-width/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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-ansi-cjs/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/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-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/strtok3": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.1.1.tgz", + "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.1.3" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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/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/tap-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.2.2.tgz", + "integrity": "sha512-uXKcosa0qoSjeh73dhmX+OpJvpigDxUciOhBcbGUKtmwzEFJjUT1Ql5dpg4M9I1UjXT9b+6n1W05FB8QmKossA==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "optionalDependencies": { + "readable-stream": "^2" + } + }, + "node_modules/tap-parser/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, + "optional": 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/tap-parser/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, + "optional": true + }, + "node_modules/tap-parser/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, + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tap-xunit": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tap-xunit/-/tap-xunit-2.4.1.tgz", + "integrity": "sha512-qcZStDtjjYjMKAo7QNiCtOW256g3tuSyCSe5kNJniG1Q2oeOExJq4vm8CwboHZURpkXAHvtqMl4TVL7mcbMVVA==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "minimist": "~1.2.0", + "tap-parser": "~1.2.2", + "through2": "~2.0.0", + "xmlbuilder": "~4.2.0", + "xtend": "~4.0.0" + }, + "bin": { + "tap-xunit": "bin/tap-xunit", + "txunit": "bin/tap-xunit" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/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/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/test-exclude/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/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/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/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "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==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "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/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.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/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, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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.2.0", + "picocolors": "^1.1.1" + }, + "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": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/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==", + "dev": true + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/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/wrap-ansi/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/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": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha512-oEePiEefhQhAeUnwRnIBLBWmk/fsWWbQ53EEWsRuzECbQ3m5o/Esmq6H47CYYwSLW+Ynt0rS9hd0pd2ogMAWjg==", + "dev": true, + "dependencies": { + "lodash": "^4.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true + }, + "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/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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/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/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/packages/fs/package.json b/packages/fs/package.json new file mode 100644 index 00000000000..2f32a0c9b2b --- /dev/null +++ b/packages/fs/package.json @@ -0,0 +1,157 @@ +{ + "name": "@ui5/fs", + "version": "4.0.2", + "description": "UI5 CLI - File System Abstraction", + "author": { + "name": "SAP SE", + "email": "openui5@sap.com", + "url": "https://www.sap.com" + }, + "license": "Apache-2.0", + "keywords": [ + "openui5", + "sapui5", + "ui5", + "build", + "development", + "tool" + ], + "type": "module", + "exports": { + "./adapters/*": "./lib/adapters/*.js", + "./AbstractReader": "./lib/AbstractReader.js", + "./AbstractReaderWriter": "./lib/AbstractReaderWriter.js", + "./DuplexCollection": "./lib/DuplexCollection.js", + "./fsInterface": "./lib/fsInterface.js", + "./readers/*": "./lib/readers/*.js", + "./ReaderCollection": "./lib/ReaderCollection.js", + "./ReaderCollectionPrioritized": "./lib/ReaderCollectionPrioritized.js", + "./Resource": "./lib/Resource.js", + "./resourceFactory": "./lib/resourceFactory.js", + "./package.json": "./package.json", + "./internal/ResourceTagCollection": "./lib/ResourceTagCollection.js" + }, + "engines": { + "node": "^20.11.0 || >=22.0.0", + "npm": ">= 8" + }, + "scripts": { + "test": "npm run lint && npm run jsdoc-generate && npm run coverage && npm run depcheck", + "test-azure": "npm run coverage-xunit", + "lint": "eslint ./", + "unit": "rimraf test/tmp && ava", + "unit-verbose": "rimraf test/tmp && cross-env UI5_LOG_LVL=verbose ava --verbose --serial", + "unit-watch": "npm run unit -- --watch", + "unit-xunit": "rimraf test/tmp && ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\" --tap | tap-xunit --dontUseCommentsAsTestNames=true > test-results.xml", + "unit-inspect": "cross-env UI5_LOG_LVL=verbose ava debug --break", + "coverage": "rimraf test/tmp && nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\"", + "coverage-xunit": "nyc --reporter=text --reporter=text-summary --reporter=cobertura npm run unit-xunit", + "jsdoc": "npm run jsdoc-generate && open-cli jsdocs/index.html", + "jsdoc-generate": "jsdoc -c ./jsdoc.json -t $(node -p 'path.dirname(require.resolve(\"docdash\"))') ./lib/ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", + "jsdoc-watch": "npm run jsdoc && chokidar \"./lib/**/*.js\" -c \"npm run jsdoc-generate\"", + "preversion": "npm test", + "version": "git-chglog --sort semver --next-tag v$npm_package_version -o CHANGELOG.md v4.0.0.. && git add CHANGELOG.md", + "prepublishOnly": "git push --follow-tags", + "release-note": "git-chglog --sort semver -c .chglog/release-config.yml v$npm_package_version", + "depcheck": "depcheck --ignores @ui5/fs,docdash,@istanbuljs/esm-loader-hook" + }, + "files": [ + "CHANGELOG.md", + "CONTRIBUTING.md", + "jsdoc.json", + "lib/**", + "LICENSES/**", + ".reuse/**" + ], + "ava": { + "files": [ + "test/lib/**/*.js" + ], + "watchMode": { + "ignoreChanges": [ + "test/tmp/**" + ] + }, + "nodeArguments": [ + "--loader=esmock", + "--no-warnings" + ], + "workerThreads": false + }, + "nyc": { + "reporter": [ + "lcov", + "text", + "text-summary" + ], + "exclude": [ + "docs/**", + "jsdocs/**", + "coverage/**", + "test/**", + ".eslintrc.cjs", + "jsdoc-plugin.cjs" + ], + "check-coverage": true, + "statements": 85, + "branches": 80, + "functions": 90, + "lines": 85, + "watermarks": { + "statements": [ + 70, + 90 + ], + "branches": [ + 70, + 90 + ], + "functions": [ + 70, + 90 + ], + "lines": [ + 70, + 90 + ] + }, + "cache": true, + "all": true + }, + "repository": { + "type": "git", + "url": "git@github.com:SAP/ui5-fs.git" + }, + "dependencies": { + "@ui5/logger": "^4.0.2", + "clone": "^2.1.2", + "escape-string-regexp": "^5.0.0", + "globby": "^14.1.0", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "minimatch": "^10.0.3", + "pretty-hrtime": "^1.0.3", + "random-int": "^3.1.0" + }, + "devDependencies": { + "@eslint/js": "^9.8.0", + "@istanbuljs/esm-loader-hook": "^0.3.0", + "ava": "^6.4.1", + "chokidar-cli": "^3.0.0", + "cross-env": "^7.0.3", + "depcheck": "^1.4.7", + "docdash": "^2.0.2", + "eslint": "^9.36.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-ava": "^15.1.0", + "eslint-plugin-jsdoc": "^60.0.0", + "esmock": "^2.7.3", + "globals": "^16.4.0", + "jsdoc": "^4.0.4", + "nyc": "^17.1.0", + "open-cli": "^8.0.0", + "rimraf": "^6.0.1", + "sinon": "^21.0.0", + "tap-xunit": "^2.4.1" + } +} diff --git a/packages/fs/test/fixtures/application.a/package.json b/packages/fs/test/fixtures/application.a/package.json new file mode 100644 index 00000000000..7da37b86a56 --- /dev/null +++ b/packages/fs/test/fixtures/application.a/package.json @@ -0,0 +1,13 @@ +{ + "name": "application.a", + "version": "1.0.0", + "description": "Simple SAPUI5 based application", + "main": "index.html", + "dependencies": { + "library.d": "file:../library.d", + "collection": "file:../collection" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/packages/fs/test/fixtures/application.a/ui5.yaml b/packages/fs/test/fixtures/application.a/ui5.yaml new file mode 100644 index 00000000000..7e28c5d7ce5 --- /dev/null +++ b/packages/fs/test/fixtures/application.a/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "0.1" +type: application +metadata: + name: application.a diff --git a/packages/fs/test/fixtures/application.a/webapp/index.html b/packages/fs/test/fixtures/application.a/webapp/index.html new file mode 100644 index 00000000000..77b0207cc80 --- /dev/null +++ b/packages/fs/test/fixtures/application.a/webapp/index.html @@ -0,0 +1,9 @@ + + + + Application A + + + + + \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.a/webapp/test.js b/packages/fs/test/fixtures/application.a/webapp/test.js new file mode 100644 index 00000000000..a3df410c341 --- /dev/null +++ b/packages/fs/test/fixtures/application.a/webapp/test.js @@ -0,0 +1,5 @@ +function test(paramA) { + var variableA = paramA; + console.log(variableA); +} +test(); diff --git a/packages/fs/test/fixtures/application.b/package.json b/packages/fs/test/fixtures/application.b/package.json new file mode 100644 index 00000000000..0243e3a9001 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/package.json @@ -0,0 +1,13 @@ +{ + "name": "application.b", + "version": "1.0.0", + "description": "Simple SAPUI5 based application", + "main": "index.html", + "dependencies": { + "library.d": "file:../library.d", + "collection": "file:../collection" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/packages/fs/test/fixtures/application.b/ui5.yaml b/packages/fs/test/fixtures/application.b/ui5.yaml new file mode 100644 index 00000000000..25326df459f --- /dev/null +++ b/packages/fs/test/fixtures/application.b/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "0.1" +type: application +metadata: + name: application.b diff --git a/packages/fs/test/fixtures/application.b/webapp/embedded/i18n/i18n.properties b/packages/fs/test/fixtures/application.b/webapp/embedded/i18n/i18n.properties new file mode 100644 index 00000000000..d93c8a39c0e --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/embedded/i18n/i18n.properties @@ -0,0 +1 @@ +title=embedded-i18n \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/embedded/i18n/i18n_de.properties b/packages/fs/test/fixtures/application.b/webapp/embedded/i18n/i18n_de.properties new file mode 100644 index 00000000000..e513333c842 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/embedded/i18n/i18n_de.properties @@ -0,0 +1 @@ +title=embedded-i18n_de \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/embedded/i18n_fr.properties b/packages/fs/test/fixtures/application.b/webapp/embedded/i18n_fr.properties new file mode 100644 index 00000000000..85e162740f9 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/embedded/i18n_fr.properties @@ -0,0 +1 @@ +title=embedded-i18n_fr-wrong \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/embedded/manifest.json b/packages/fs/test/fixtures/application.b/webapp/embedded/manifest.json new file mode 100644 index 00000000000..5ef0c362425 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/embedded/manifest.json @@ -0,0 +1,13 @@ +{ + "_version": "1.1.0", + "sap.app": { + "_version": "1.1.0", + "id": "id1.embedded", + "type": "component", + "applicationVersion": { + "version": "1.2.2" + }, + "embeddedBy": "../", + "title": "{{title}}" + } +} \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/i18n.properties b/packages/fs/test/fixtures/application.b/webapp/i18n.properties new file mode 100644 index 00000000000..88b84f602f5 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/i18n.properties @@ -0,0 +1 @@ +title=app-i18n-wrong \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/i18n/i18n.properties b/packages/fs/test/fixtures/application.b/webapp/i18n/i18n.properties new file mode 100644 index 00000000000..575fb20d0c3 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/i18n/i18n.properties @@ -0,0 +1 @@ +title=app-i18n \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/i18n/i18n_de.properties b/packages/fs/test/fixtures/application.b/webapp/i18n/i18n_de.properties new file mode 100644 index 00000000000..f5885803892 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/i18n/i18n_de.properties @@ -0,0 +1 @@ +title=app-i18n_de \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/i18n/l10n.properties b/packages/fs/test/fixtures/application.b/webapp/i18n/l10n.properties new file mode 100644 index 00000000000..88b84f602f5 --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/i18n/l10n.properties @@ -0,0 +1 @@ +title=app-i18n-wrong \ No newline at end of file diff --git a/packages/fs/test/fixtures/application.b/webapp/manifest.json b/packages/fs/test/fixtures/application.b/webapp/manifest.json new file mode 100644 index 00000000000..781945df9dc --- /dev/null +++ b/packages/fs/test/fixtures/application.b/webapp/manifest.json @@ -0,0 +1,13 @@ +{ + "_version": "1.1.0", + "sap.app": { + "_version": "1.1.0", + "id": "id1", + "type": "application", + "applicationVersion": { + "version": "1.2.2" + }, + "embeds": ["embedded"], + "title": "{{title}}" + } +} \ No newline at end of file diff --git "a/packages/fs/test/fixtures/fsInterf\303\241ce/b\303\242r.txt" "b/packages/fs/test/fixtures/fsInterf\303\241ce/b\303\242r.txt" new file mode 100644 index 00000000000..6b584e8ece5 --- /dev/null +++ "b/packages/fs/test/fixtures/fsInterf\303\241ce/b\303\242r.txt" @@ -0,0 +1 @@ +content \ No newline at end of file diff --git "a/packages/fs/test/fixtures/fsInterf\303\241ce/foo.txt" "b/packages/fs/test/fixtures/fsInterf\303\241ce/foo.txt" new file mode 100644 index 00000000000..6b584e8ece5 --- /dev/null +++ "b/packages/fs/test/fixtures/fsInterf\303\241ce/foo.txt" @@ -0,0 +1 @@ +content \ No newline at end of file diff --git a/packages/fs/test/fixtures/glob/application.a/package.json b/packages/fs/test/fixtures/glob/application.a/package.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.a/ui5.yaml b/packages/fs/test/fixtures/glob/application.a/ui5.yaml new file mode 100644 index 00000000000..7e28c5d7ce5 --- /dev/null +++ b/packages/fs/test/fixtures/glob/application.a/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "0.1" +type: application +metadata: + name: application.a diff --git a/packages/fs/test/fixtures/glob/application.a/webapp/index.html b/packages/fs/test/fixtures/glob/application.a/webapp/index.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.a/webapp/test.js b/packages/fs/test/fixtures/glob/application.a/webapp/test.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/package.json b/packages/fs/test/fixtures/glob/application.b/package.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/ui5.yaml b/packages/fs/test/fixtures/glob/application.b/ui5.yaml new file mode 100644 index 00000000000..25326df459f --- /dev/null +++ b/packages/fs/test/fixtures/glob/application.b/ui5.yaml @@ -0,0 +1,5 @@ +--- +specVersion: "0.1" +type: application +metadata: + name: application.b diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/embedded/i18n/i18n.properties b/packages/fs/test/fixtures/glob/application.b/webapp/embedded/i18n/i18n.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/embedded/i18n/i18n_de.properties b/packages/fs/test/fixtures/glob/application.b/webapp/embedded/i18n/i18n_de.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/embedded/i18n_fr.properties b/packages/fs/test/fixtures/glob/application.b/webapp/embedded/i18n_fr.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/embedded/manifest.json b/packages/fs/test/fixtures/glob/application.b/webapp/embedded/manifest.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/i18n.properties b/packages/fs/test/fixtures/glob/application.b/webapp/i18n.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/i18n/i18n.properties b/packages/fs/test/fixtures/glob/application.b/webapp/i18n/i18n.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/i18n/i18n_de.properties b/packages/fs/test/fixtures/glob/application.b/webapp/i18n/i18n_de.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/i18n/l10n.properties b/packages/fs/test/fixtures/glob/application.b/webapp/i18n/l10n.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/application.b/webapp/manifest.json b/packages/fs/test/fixtures/glob/application.b/webapp/manifest.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/glob/package.json b/packages/fs/test/fixtures/glob/package.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/library.l/.gitignore b/packages/fs/test/fixtures/library.l/.gitignore new file mode 100644 index 00000000000..65e3ba2edad --- /dev/null +++ b/packages/fs/test/fixtures/library.l/.gitignore @@ -0,0 +1 @@ +test/ diff --git a/packages/fs/test/fixtures/library.l/package.json b/packages/fs/test/fixtures/library.l/package.json new file mode 100644 index 00000000000..db52483b8d5 --- /dev/null +++ b/packages/fs/test/fixtures/library.l/package.json @@ -0,0 +1,8 @@ +{ + "name": "library.l", + "version": "1.0.0", + "description": "Simple SAPUI5 based library - test for glob excludes", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/packages/fs/test/fixtures/library.l/src/library/l/.library b/packages/fs/test/fixtures/library.l/src/library/l/.library new file mode 100644 index 00000000000..eb743173b9e --- /dev/null +++ b/packages/fs/test/fixtures/library.l/src/library/l/.library @@ -0,0 +1,11 @@ + + + + library.l + SAP SE + ${copyright} + ${version} + + Library L + + diff --git a/packages/fs/test/fixtures/library.l/src/library/l/some.js b/packages/fs/test/fixtures/library.l/src/library/l/some.js new file mode 100644 index 00000000000..81e73436075 --- /dev/null +++ b/packages/fs/test/fixtures/library.l/src/library/l/some.js @@ -0,0 +1,4 @@ +/*! + * ${copyright} + */ +console.log('HelloWorld'); \ No newline at end of file diff --git a/packages/fs/test/fixtures/library.l/test/library/l/Test.html b/packages/fs/test/fixtures/library.l/test/library/l/Test.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/library.l/test/library/l/Test2.html b/packages/fs/test/fixtures/library.l/test/library/l/Test2.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/fs/test/fixtures/library.l/ui5.yaml b/packages/fs/test/fixtures/library.l/ui5.yaml new file mode 100644 index 00000000000..311b3fa8e63 --- /dev/null +++ b/packages/fs/test/fixtures/library.l/ui5.yaml @@ -0,0 +1,14 @@ +--- +specVersion: "0.1" +type: library +metadata: + name: library.l + copyright: |- + UI development toolkit for HTML5 (OpenUI5) + * (c) Copyright 2009-xxx SAP SE or an SAP affiliate company. + * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. +builder: + resources: + excludes: + - /resources/**/some.js + - /test-resources/** \ No newline at end of file diff --git a/packages/fs/test/lib/AbstractReader.js b/packages/fs/test/lib/AbstractReader.js new file mode 100644 index 00000000000..c5d9c00951e --- /dev/null +++ b/packages/fs/test/lib/AbstractReader.js @@ -0,0 +1,37 @@ +import test from "ava"; +import AbstractReader from "../../lib/AbstractReader.js"; + +test("AbstractReader: constructor throws an error", (t) => { + t.throws(() => { + new AbstractReader(); + }, { + instanceOf: TypeError, + message: "Class 'AbstractReader' is abstract" + }); +}); + +test("Incomplete AbstractReader subclass: Abstract functions throw error", (t) => { + class Dummy extends AbstractReader {} + + const instance = new Dummy(); + t.throws(() => { + instance._byGlob(); + }, { + instanceOf: Error, + message: "Function '_byGlob' is not implemented" + }); + + t.throws(() => { + instance._runGlob(); + }, { + instanceOf: Error, + message: "Function '_runGlob' is not implemented" + }); + + t.throws(() => { + instance._byPath(); + }, { + instanceOf: Error, + message: "Function '_byPath' is not implemented" + }); +}); diff --git a/packages/fs/test/lib/AbstractReaderWriter.js b/packages/fs/test/lib/AbstractReaderWriter.js new file mode 100644 index 00000000000..1cb5cec2d4e --- /dev/null +++ b/packages/fs/test/lib/AbstractReaderWriter.js @@ -0,0 +1,25 @@ +import test from "ava"; +import AbstractReaderWriter from "../../lib/AbstractReaderWriter.js"; + +test("AbstractReaderWriter: constructor throws an error", (t) => { + t.throws(() => { + new AbstractReaderWriter(); + }, { + instanceOf: TypeError, + message: "Class 'AbstractReaderWriter' is abstract" + }); +}); + +test("Incomplete AbstractReaderWriter subclass: Abstract functions throw error", (t) => { + class Dummy extends AbstractReaderWriter {} + + const instance = new Dummy(); + + t.throws(() => { + instance._write(); + }, { + instanceOf: Error, + message: "Not implemented" + }); +}); + diff --git a/packages/fs/test/lib/DuplexCollection.js b/packages/fs/test/lib/DuplexCollection.js new file mode 100644 index 00000000000..bba5b0962d4 --- /dev/null +++ b/packages/fs/test/lib/DuplexCollection.js @@ -0,0 +1,183 @@ +import test from "ava"; +import sinon from "sinon"; +import DuplexCollection from "../../lib/DuplexCollection.js"; +import ReaderCollectionPrioritized from "../../lib/ReaderCollectionPrioritized.js"; +import Resource from "../../lib/Resource.js"; + +test("DuplexCollection: constructor", (t) => { + const duplexCollection = new DuplexCollection({ + name: "myCollection", + reader: {}, + writer: {} + }); + + t.deepEqual(duplexCollection._reader, {}, "reader assigned"); + t.deepEqual(duplexCollection._writer, {}, "writer assigned"); + t.true(duplexCollection._combo instanceof ReaderCollectionPrioritized, "prioritized reader collection created"); + t.is(duplexCollection._combo.getName(), "myCollection - ReaderCollectionPrioritized", "name assigned"); + t.deepEqual(duplexCollection._combo._readers, [{}, {}], "reader and writer assigned to readers"); +}); + +test("DuplexCollection: constructor with setting default name of an empty string", (t) => { + const duplexCollection = new DuplexCollection({ + reader: {}, + writer: {} + }); + + t.deepEqual(duplexCollection._reader, {}, "reader assigned"); + t.deepEqual(duplexCollection._writer, {}, "writer assigned"); + t.true(duplexCollection._combo instanceof ReaderCollectionPrioritized, "prioritized reader collection created"); + t.is(duplexCollection._combo.getName(), " - ReaderCollectionPrioritized", "name assigned"); + t.deepEqual(duplexCollection._combo._readers, [{}, {}], "reader and writer assigned to readers"); +}); + +test("DuplexCollection: _byGlob w/o finding a resource", async (t) => { + t.plan(3); + + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve([])) + }; + const duplexCollection = new DuplexCollection({ + name: "myCollection", + reader: abstractReader, + writer: abstractReader + }); + const trace = { + collection: sinon.spy() + }; + const comboSpy = sinon.spy(duplexCollection._combo, "_byGlob"); + + const resources = await duplexCollection._byGlob("anyPattern", {someOption: true}, trace); + + t.true(Array.isArray(resources), "Found resources are returned as an array"); + t.true(resources.length === 0, "No resources found"); + t.true(comboSpy.calledWithExactly("anyPattern", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); +}); + +test("DuplexCollection: _byGlob", async (t) => { + t.plan(5); + + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve([resource])) + }; + const duplexCollection = new DuplexCollection({ + name: "myCollection", + reader: abstractReader, + writer: abstractReader + }); + const trace = { + collection: sinon.spy() + }; + const comboSpy = sinon.spy(duplexCollection._combo, "_byGlob"); + const resources = await duplexCollection._byGlob("anyPattern", {someOption: true}, trace); + const resourceContent = await resource.getString(); + + t.true(Array.isArray(resources), "Found resources are returned as an array"); + t.true(resources.length === 1, "Resource found"); + t.is(resource.getPath(), "/my/path", "Resource has expected path"); + t.true(comboSpy.calledWithExactly("anyPattern", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.is(resourceContent, "content", "Resource has expected content"); +}); + +test("DuplexCollection: _byPath with reader finding a resource", async (t) => { + t.plan(4); + + const resource = new Resource({ + path: "/path", + buffer: Buffer.from("content") + }); + const pushCollectionSpy = sinon.spy(resource, "pushCollection"); + const abstractReader = { + _byPath: sinon.stub().returns(Promise.resolve(resource)) + }; + const trace = { + collection: sinon.spy() + }; + const duplexCollection = new DuplexCollection({ + name: "myCollection", + reader: abstractReader, + writer: abstractReader + }); + const comboSpy = sinon.spy(duplexCollection._combo, "_byPath"); + const readResource = await duplexCollection._byPath("anyVirtualPath", {someOption: true}, trace); + const readResourceContent = await readResource.getString(); + + t.true(comboSpy.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(pushCollectionSpy.called, "pushCollection called on resource"); + t.is(readResource.getPath(), "/path", "Resource has expected path"); + t.is(readResourceContent, "content", "Resource has expected content"); +}); + +test("DuplexCollection: _byPath with two readers both finding no resource", async (t) => { + t.plan(3); + + const abstractReaderOne = { + _byPath: sinon.stub().returns(Promise.resolve()) + }; + const abstractReaderTwo = { + _byPath: sinon.stub().returns(Promise.resolve()) + }; + const trace = { + collection: sinon.stub() + }; + const duplexCollection = new DuplexCollection({ + name: "myCollection", + reader: abstractReaderOne, + writer: abstractReaderTwo + }); + const readResource = await duplexCollection._byPath("anyVirtualPath", {someOption: true}, trace); + + t.true(abstractReaderOne._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to reader one"); + t.true(abstractReaderTwo._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to reader two"); + t.falsy(readResource, "No resource found"); +}); + +test("DuplexCollection: _write successful", async (t) => { + t.plan(1); + + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const duplexCollection = new DuplexCollection({ + name: "myCollection", + reader: {}, + writer: { + write: sinon.stub().returns(Promise.resolve()) + } + }); + await duplexCollection._write(resource); + + t.pass("write on writer called"); +}); + +test("DuplexCollection: Throws for empty reader", (t) => { + t.throws(() => { + new DuplexCollection({ + name: "myReader", + writer: {} + }); + }, { + message: "Cannot create DuplexCollection myReader: No reader provided" + }); +}); + +test("DuplexCollection: Throws for empty writer", (t) => { + t.throws(() => { + new DuplexCollection({ + name: "myReader", + reader: {} + }); + }, { + message: "Cannot create DuplexCollection myReader: No writer provided" + }); +}); diff --git a/packages/fs/test/lib/ReaderCollection.js b/packages/fs/test/lib/ReaderCollection.js new file mode 100644 index 00000000000..d808591b708 --- /dev/null +++ b/packages/fs/test/lib/ReaderCollection.js @@ -0,0 +1,198 @@ +import test from "ava"; +import sinon from "sinon"; +import ReaderCollection from "../../lib/ReaderCollection.js"; +import Resource from "../../lib/Resource.js"; + +test("ReaderCollection: constructor", (t) => { + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [{}, {}, {}] + }); + + t.is(readerCollection.getName(), "myReader", "correct name assigned"); + t.deepEqual(readerCollection._readers, [{}, {}, {}], "correct readers assigned"); +}); + +test("ReaderCollection: _byGlob w/o finding a resource", async (t) => { + t.plan(4); + + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve([])) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [abstractReader] + }); + const resources = await readerCollection._byGlob("anyPattern", {someOption: true}, trace); + + t.true(Array.isArray(resources), "Found resources are returned as an array"); + t.true(resources.length === 0, "No resources found"); + t.true(abstractReader._byGlob.calledWithExactly("anyPattern", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(trace.collection.called, "Trace.collection called"); +}); + +test("ReaderCollection: _byGlob with finding a resource", async (t) => { + t.plan(6); + + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve([resource])) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [abstractReader] + }); + + const resources = await readerCollection._byGlob("anyPattern", {someOption: true}, trace); + const resourceContent = await resources[0].getString(); + + t.true(Array.isArray(resources), "Found resources are returned as an array"); + t.true(resources.length === 1, "Resource found"); + t.true(abstractReader._byGlob.calledWithExactly("anyPattern", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(trace.collection.called, "Trace.collection called"); + t.is(resources[0].getPath(), "/my/path", "Resource has expected path"); + t.is(resourceContent, "content", "Resource has expected content"); +}); + +test("ReaderCollection: _byPath with reader finding a resource", async (t) => { + t.plan(5); + + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const pushCollectionSpy = sinon.spy(resource, "pushCollection"); + const abstractReader = { + _byPath: sinon.stub().returns(Promise.resolve(resource)) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [abstractReader] + }); + + const readResource = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace); + const readResourceContent = await resource.getString(); + + t.true(abstractReader._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(trace.collection.called, "Trace.collection called"); + t.true(pushCollectionSpy.called, "pushCollection called on resource"); + t.is(readResource.getPath(), "/my/path", "Resource has expected path"); + t.is(readResourceContent, "content", "Resource has expected content"); +}); + +test("ReaderCollection: _byPath with two readers both finding no resource", async (t) => { + t.plan(4); + + const abstractReaderOne = { + _byPath: sinon.stub().returns(Promise.resolve()) + }; + const abstractReaderTwo = { + _byPath: sinon.stub().returns(Promise.resolve()) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [abstractReaderOne, abstractReaderTwo] + }); + + const resource = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace); + + t.falsy(resource, "No resource found"); + t.true(abstractReaderOne._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to reader one"); + t.true(abstractReaderTwo._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to reader two"); + t.true(trace.collection.calledTwice, "Trace.collection called"); +}); + +test("ReaderCollection: _byPath with empty readers array", async (t) => { + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [] + }); + + const resource = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace); + t.is(resource, null, "Promise resolves to null, as no readers got configured"); +}); + +test("ReaderCollection: _byPath with some empty readers", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReaderOne = { + _byPath: sinon.stub().resolves(resource) + }; + const abstractReaderTwo = { + _byPath: sinon.stub().resolves() + }; + + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [abstractReaderOne, undefined, abstractReaderTwo] + }); + + const res = await readerCollection._byPath("anyVirtualPath", {someOption: true}, trace); + t.is(res, resource, "Found expected resource"); +}); + +test("ReaderCollection: _byGlob with empty readers array", async (t) => { + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [] + }); + + const resource = await readerCollection.byGlob("anyPattern", {someOption: true}, trace); + t.deepEqual(resource, [], "Promise resolves to null, as no readers got configured"); +}); + +test("ReaderCollection: _byGlob with some empty readers", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReaderOne = { + _byGlob: sinon.stub().resolves([resource]) + }; + const abstractReaderTwo = { + _byGlob: sinon.stub().resolves([]) + }; + + const trace = { + collection: sinon.spy() + }; + const readerCollection = new ReaderCollection({ + name: "myReader", + readers: [abstractReaderOne, undefined, abstractReaderTwo] + }); + + const res = await readerCollection._byGlob("anyVirtualPath", {someOption: true}, trace); + t.is(res.length, 1, "Found one resource"); + t.is(res[0], resource, "Found expected resource"); +}); diff --git a/packages/fs/test/lib/ReaderCollectionPrioritized.js b/packages/fs/test/lib/ReaderCollectionPrioritized.js new file mode 100644 index 00000000000..e13cda12dae --- /dev/null +++ b/packages/fs/test/lib/ReaderCollectionPrioritized.js @@ -0,0 +1,188 @@ +import test from "ava"; +import sinon from "sinon"; +import ReaderCollectionPrioritized from "../../lib/ReaderCollectionPrioritized.js"; +import Resource from "../../lib/Resource.js"; + +test("ReaderCollectionPrioritized: constructor", (t) => { + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [{}, {}, {}] + }); + + t.is(readerCollectionPrioritized.getName(), "myReader", "correct name assigned"); + t.deepEqual(readerCollectionPrioritized._readers, [{}, {}, {}], "correct readers assigned"); +}); + +test("ReaderCollectionPrioritized: _byGlob w/o finding a resource", async (t) => { + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve([])) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [abstractReader] + }); + const resources = await readerCollectionPrioritized._byGlob("anyPattern", {someOption: true}, trace); + + t.true(Array.isArray(resources), "Found resources are returned as an array"); + t.true(resources.length === 0, "No resources found"); + t.true(abstractReader._byGlob.calledWithExactly("anyPattern", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(trace.collection.called, "Trace.collection called"); +}); + +test("ReaderCollectionPrioritized: _byGlob with finding a resource", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve([resource])) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [abstractReader] + }); + + const resources = await readerCollectionPrioritized._byGlob("anyPattern", {someOption: true}, trace); + const resourceContent = await resources[0].getString(); + + t.true(Array.isArray(resources), "Found resources are returned as an array"); + t.true(resources.length === 1, "Resource found"); + t.true(abstractReader._byGlob.calledWithExactly("anyPattern", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(trace.collection.called, "Trace.collection called"); + t.is(resources[0].getPath(), "/my/path", "Resource has expected path"); + t.is(resourceContent, "content", "Resource has expected content"); +}); + +test("ReaderCollectionPrioritized: _byPath with reader finding a resource", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const pushCollectionSpy = sinon.spy(resource, "pushCollection"); + const abstractReader = { + _byPath: sinon.stub().returns(Promise.resolve(resource)) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [abstractReader] + }); + + const readResource = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace); + const readResourceContent = await resource.getString(); + + t.true(abstractReader._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to readers"); + t.true(pushCollectionSpy.called, "pushCollection called on resource"); + t.is(readResource.getPath(), "/my/path", "Resource has expected path"); + t.is(readResourceContent, "content", "Resource has expected content"); +}); + +test("ReaderCollectionPrioritized: _byPath with two readers both finding no resource", async (t) => { + const abstractReaderOne = { + _byPath: sinon.stub().returns(Promise.resolve()) + }; + const abstractReaderTwo = { + _byPath: sinon.stub().returns(Promise.resolve()) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [abstractReaderOne, abstractReaderTwo] + }); + + const resource = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace); + + t.falsy(resource, "No resource found"); + t.true(abstractReaderOne._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to reader one"); + t.true(abstractReaderTwo._byPath.calledWithExactly("anyVirtualPath", {someOption: true}, trace), + "Delegated globbing task correctly to reader two"); +}); + +test("ReaderCollectionPrioritized: _byPath with empty readers array", async (t) => { + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [] + }); + + const resource = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace); + t.is(resource, null, "Promise resolves to null, as no readers got configured"); +}); + +test("ReaderCollectionPrioritized: _byPath with some empty readers", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReaderOne = { + _byPath: sinon.stub().resolves(resource) + }; + const abstractReaderTwo = { + _byPath: sinon.stub().resolves() + }; + + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [abstractReaderOne, undefined, abstractReaderTwo] + }); + + const res = await readerCollectionPrioritized._byPath("anyVirtualPath", {someOption: true}, trace); + t.is(res, resource, "Found expected resource"); +}); + +test("ReaderCollectionPrioritized: _byGlob with empty readers array", async (t) => { + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [] + }); + + const resource = await readerCollectionPrioritized.byGlob("anyPattern", {someOption: true}, trace); + t.deepEqual(resource, [], "Promise resolves to null, as no readers got configured"); +}); + +test("ReaderCollectionPrioritized: _byGlob with some empty readers", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("content") + }); + const abstractReaderOne = { + _byGlob: sinon.stub().resolves([resource]) + }; + const abstractReaderTwo = { + _byGlob: sinon.stub().resolves([]) + }; + + const trace = { + collection: sinon.spy() + }; + const readerCollectionPrioritized = new ReaderCollectionPrioritized({ + name: "myReader", + readers: [abstractReaderOne, undefined, abstractReaderTwo] + }); + + const res = await readerCollectionPrioritized._byGlob("anyVirtualPath", {someOption: true}, trace); + t.is(res.length, 1, "Found one resource"); + t.is(res[0], resource, "Found expected resource"); +}); diff --git a/packages/fs/test/lib/Resource.js b/packages/fs/test/lib/Resource.js new file mode 100644 index 00000000000..aca04728746 --- /dev/null +++ b/packages/fs/test/lib/Resource.js @@ -0,0 +1,699 @@ +import test from "ava"; +import {Stream, Transform} from "node:stream"; +import {promises as fs, createReadStream} from "node:fs"; +import path from "node:path"; +import Resource from "../../lib/Resource.js"; + +function createBasicResource() { + const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html"); + const resource = new Resource({ + path: "/app/index.html", + createStream: function() { + return createReadStream(fsPath); + }, + project: {}, + statInfo: {}, + fsPath + }); + return resource; +} + +/** + * Reads a readable stream and resolves with its content + * + * @param {stream.Readable} readableStream readable stream + * @returns {Promise} resolves with the read string + */ +const readStream = (readableStream) => { + return new Promise((resolve, reject) => { + let streamedResult = ""; + readableStream.on("data", (chunk) => { + streamedResult += chunk; + }); + readableStream.on("end", () => { + resolve(streamedResult); + }); + readableStream.on("error", (err) => { + reject(err); + }); + }); +}; + +test("Resource: constructor with missing path parameter", (t) => { + t.throws(() => { + new Resource({}); + }, { + instanceOf: Error, + message: "Unable to create Resource: Missing parameter 'path'" + }); +}); + +test("Resource: constructor with duplicated content parameter", (t) => { + const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html"); + [{ + path: "/my/path", + buffer: Buffer.from("Content"), + string: "Content", + }, { + path: "/my/path", + buffer: Buffer.from("Content"), + stream: createReadStream(fsPath), + }, { + path: "/my/path", + buffer: Buffer.from("Content"), + createStream: () => { + return createReadStream(fsPath); + }, + }, { + path: "/my/path", + string: "Content", + stream: createReadStream(fsPath), + }, { + path: "/my/path", + string: "Content", + createStream: () => { + return createReadStream(fsPath); + }, + }, { + path: "/my/path", + stream: createReadStream(fsPath), + createStream: () => { + return createReadStream(fsPath); + }, + }].forEach((resourceParams) => { + t.throws(() => { + new Resource(resourceParams); + }, { + instanceOf: Error, + message: "Unable to create Resource: Please set only one content parameter. " + + "'buffer', 'string', 'stream' or 'createStream'" + }, "Threw with expected error message"); + }); +}); + +test("Resource: From buffer", async (t) => { + const resource = new Resource({ + path: "/my/path", + buffer: Buffer.from("Content") + }); + t.is(await resource.getSize(), 7, "Content is set"); + t.false(resource.isModified(), "Content of new resource is not modified"); + t.false(resource.getSourceMetadata().contentModified, "Content of new resource is not modified"); +}); + +test("Resource: From string", async (t) => { + const resource = new Resource({ + path: "/my/path", + string: "Content" + }); + t.is(await resource.getSize(), 7, "Content is set"); + t.false(resource.isModified(), "Content of new resource is not modified"); + t.false(resource.getSourceMetadata().contentModified, "Content of new resource is not modified"); +}); + +test("Resource: From stream", async (t) => { + const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html"); + const resource = new Resource({ + path: "/my/path", + stream: createReadStream(fsPath) + }); + t.is(await resource.getSize(), 91, "Content is set"); + t.false(resource.isModified(), "Content of new resource is not modified"); + t.false(resource.getSourceMetadata().contentModified, "Content of new resource is not modified"); +}); + +test("Resource: From createStream", async (t) => { + const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html"); + const resource = new Resource({ + path: "/my/path", + createStream: () => { + return createReadStream(fsPath); + } + }); + t.is(await resource.getSize(), 91, "Content is set"); + t.false(resource.isModified(), "Content of new resource is not modified"); + t.false(resource.getSourceMetadata().contentModified, "Content of new resource is not modified"); +}); + +test("Resource: Source metadata", async (t) => { + const resource = new Resource({ + path: "/my/path", + string: "Content", + sourceMetadata: { + adapter: "My Adapter", + fsPath: "/some/path" + } + }); + t.is(await resource.getSize(), 7, "Content is set"); + t.false(resource.isModified(), "Content of new resource is not modified"); + t.false(resource.getSourceMetadata().contentModified, "Content of new resource is not modified"); + t.is(resource.getSourceMetadata().adapter, "My Adapter", "Correct source metadata 'adapter' value"); + t.is(resource.getSourceMetadata().fsPath, "/some/path", "Correct source metadata 'fsPath' value"); +}); +test("Resource: Source metadata with modified content", async (t) => { + const resource = new Resource({ + path: "/my/path", + string: "Content", + sourceMetadata: { + adapter: "My Adapter", + fsPath: "/some/path", + contentModified: true + } + }); + t.is(await resource.getSize(), 7, "Content is set"); + t.false(resource.isModified(), "Content of new resource is not modified"); + t.true(resource.getSourceMetadata().contentModified, "Content of new resource is already modified"); + t.is(resource.getSourceMetadata().adapter, "My Adapter", "Correct source metadata 'adapter' value"); + t.is(resource.getSourceMetadata().fsPath, "/some/path", "Correct source metadata 'fsPath' value"); +}); + +test("Resource: Illegal source metadata attribute", (t) => { + t.throws(() => { + new Resource({ + path: "/my/path", + string: "Content", + sourceMetadata: { + adapter: "My Adapter", + fsPath: "/some/path", + pony: "🦄" + } + }); + }, { + message: `Parameter 'sourceMetadata' contains an illegal attribute: pony` + }, "Threw with expected error message"); +}); + +test("Resource: Illegal source metadata value", (t) => { + t.throws(() => { + new Resource({ + path: "/my/path", + string: "Content", + sourceMetadata: { + adapter: "My Adapter", + fsPath: { + some: "value" + } + } + }); + }, { + message: `Attribute 'fsPath' of parameter 'sourceMetadata' must be of type "string" or "boolean"` + }, "Threw with expected error message"); +}); + +test("Resource: getBuffer with throwing an error", (t) => { + t.plan(1); + + const resource = new Resource({ + path: "/my/path/to/resource", + }); + + return resource.getBuffer().catch(function(error) { + t.is(error.message, "Resource /my/path/to/resource has no content", + "getBuffer called w/o having a resource content provided"); + }); +}); + +test("Resource: getPath / getName", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource.js", + buffer: Buffer.from("Content") + }); + t.is(resource.getPath(), "/my/path/to/resource.js", "Correct path"); + t.is(resource.getName(), "resource.js", "Correct name"); +}); + +test("Resource: setPath / getName", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource.js", + buffer: Buffer.from("Content") + }); + resource.setPath("/my/other/file.json"); + t.is(resource.getPath(), "/my/other/file.json", "Correct path"); + t.is(resource.getName(), "file.json", "Correct name"); +}); + +test("Resource: setPath with non-absolute path", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource.js", + buffer: Buffer.from("Content") + }); + t.throws(() => { + resource.setPath("my/other/file.json"); + }, { + message: "Unable to set resource path: Path must be absolute: my/other/file.json" + }, "Threw with expected error message"); + t.is(resource.getPath(), "/my/path/to/resource.js", "Path is unchanged"); + t.is(resource.getName(), "resource.js", "Name is unchanged"); +}); + +test("Create Resource with non-absolute path", (t) => { + t.throws(() => { + new Resource({ + path: "my/path/to/resource.js", + buffer: Buffer.from("Content") + }); + }, { + message: "Unable to set resource path: Path must be absolute: my/path/to/resource.js" + }, "Threw with expected error message"); +}); + +test("Resource: getStream", async (t) => { + t.plan(1); + + const resource = new Resource({ + path: "/my/path/to/resource", + buffer: Buffer.from("Content") + }); + + const result = await readStream(resource.getStream()); + t.is(result, "Content", "Stream has been read correctly"); +}); + +test("Resource: getStream for empty string", async (t) => { + t.plan(1); + + const resource = new Resource({ + path: "/my/path/to/resource", + string: "" + }); + + const result = await readStream(resource.getStream()); + t.is(result, "", "Stream has been read correctly for empty string"); +}); + +test("Resource: getStream for empty string instance", async (t) => { + t.plan(1); + + const resource = new Resource({ + path: "/my/path/to/resource", + // eslint-disable-next-line no-new-wrappers + string: new String("") + }); + + const result = await readStream(resource.getStream()); + t.is(result, "", "Stream has been read correctly for empty string"); +}); + +test("Resource: getStream throwing an error", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource" + }); + + t.throws(() => { + resource.getStream(); + }, { + instanceOf: Error, + message: "Resource /my/path/to/resource has no content" + }); +}); + +test("Resource: setString", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + sourceMetadata: {} // Needs to be passed in order to get the "modified" state + }); + + t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly"); + t.false(resource.isModified(), "Resource is not modified"); + + resource.setString("Content"); + + t.is(resource.getSourceMetadata().contentModified, true, "sourceMetadata modified flag updated correctly"); + t.true(resource.isModified(), "Resource is modified"); + + const value = await resource.getString(); + t.is(value, "Content", "String set"); +}); + +test("Resource: setBuffer", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + sourceMetadata: {} // Needs to be passed in order to get the "modified" state + }); + + t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly"); + t.false(resource.isModified(), "Resource is not modified"); + + resource.setBuffer(Buffer.from("Content")); + + t.is(resource.getSourceMetadata().contentModified, true, "sourceMetadata modified flag updated correctly"); + t.true(resource.isModified(), "Resource is modified"); + + const value = await resource.getString(); + t.is(value, "Content", "String set"); +}); + +test("Resource: size modification", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource" + }); + t.is(await resource.getSize(), 0, "initial size without content"); + + // string + resource.setString("Content"); + + t.is(await resource.getSize(), 7, "size after manually setting the string"); + t.is(await new Resource({ + path: "/my/path/to/resource", + string: "Content" + }).getSize(), 7, "size when passing string to constructor"); + + // buffer + resource.setBuffer(Buffer.from("Super")); + + t.is(await resource.getSize(), 5, "size after manually setting the string"); + + const clonedResource1 = await resource.clone(); + t.is(await clonedResource1.getSize(), 5, "size after cloning the resource"); + + // buffer with alloc + const buf = Buffer.alloc(1234); + buf.write("some string", 0, "utf8"); + resource.setBuffer(buf); + + t.is(await resource.getSize(), 1234, "buffer with alloc after setting the buffer"); + t.is(await new Resource({ + path: "/my/path/to/resource", + buffer: buf + }).getSize(), 1234, "buffer with alloc when passing buffer to constructor"); + + const clonedResource2 = await resource.clone(); + t.is(await clonedResource2.getSize(), 1234, "buffer with alloc after clone"); + + // stream + const streamResource = new Resource({ + path: "/my/path/to/resource", + }); + const stream = new Stream.Readable(); + stream._read = function() {}; + stream.push("I am a "); + stream.push("readable "); + stream.push("stream!"); + stream.push(null); + + streamResource.setStream(stream); + + // stream is read and stored in buffer + // test parallel size retrieval + const [size1, size2] = await Promise.all([streamResource.getSize(), streamResource.getSize()]); + t.is(size1, 23, "size for streamResource, parallel 1"); + t.is(size2, 23, "size for streamResource, parallel 2"); + t.is(await streamResource.getSize(), 23, "size for streamResource read again"); +}); + +test("Resource: setStream (Stream)", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + sourceMetadata: {} // Needs to be passed in order to get the "modified" state + }); + + t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly"); + t.false(resource.isModified(), "Resource is not modified"); + + const stream = new Stream.Readable(); + stream._read = function() {}; + stream.push("I am a "); + stream.push("readable "); + stream.push("stream!"); + stream.push(null); + + resource.setStream(stream); + + t.is(resource.getSourceMetadata().contentModified, true, "sourceMetadata modified flag updated correctly"); + t.true(resource.isModified(), "Resource is modified"); + + const value = await resource.getString(); + t.is(value, "I am a readable stream!", "Stream set correctly"); +}); + +test("Resource: setStream (Create stream callback)", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + sourceMetadata: {} // Needs to be passed in order to get the "modified" state + }); + + t.is(resource.getSourceMetadata().contentModified, false, "sourceMetadata modified flag set correctly"); + t.false(resource.isModified(), "Resource is not modified"); + + resource.setStream(() => { + const stream = new Stream.Readable(); + stream._read = function() {}; + stream.push("I am a "); + stream.push("readable "); + stream.push("stream!"); + stream.push(null); + return stream; + }); + + t.is(resource.getSourceMetadata().contentModified, true, "sourceMetadata modified flag updated correctly"); + t.true(resource.isModified(), "Resource is modified"); + + const value = await resource.getString(); + t.is(value, "I am a readable stream!", "Stream set correctly"); +}); + +test("Resource: clone resource with buffer", async (t) => { + t.plan(2); + + const resource = new Resource({ + path: "/my/path/to/resource", + buffer: Buffer.from("Content") + }); + + const clonedResource = await resource.clone(); + t.pass("Resource cloned"); + + const clonedResourceContent = await clonedResource.getString(); + t.is(clonedResourceContent, "Content", "Cloned resource has correct content string"); +}); + +test("Resource: clone resource with stream", async (t) => { + t.plan(2); + + const resource = new Resource({ + path: "/my/path/to/resource" + }); + const stream = new Stream.Readable(); + stream._read = function() {}; + stream.push("Content"); + stream.push(null); + + resource.setStream(stream); + + const clonedResource = await resource.clone(); + t.pass("Resource cloned"); + + const clonedResourceContent = await clonedResource.getString(); + t.is(clonedResourceContent, "Content", "Cloned resource has correct content string"); +}); + +test("Resource: clone resource with sourceMetadata", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + sourceMetadata: { + adapter: "FileSystem", + fsPath: "/resources/my.js" + } + }); + + const clonedResource = await resource.clone(); + + t.not(resource.getSourceMetadata(), clonedResource.getSourceMetadata(), + "Clone has de-referenced instance of sourceMetadata"); + t.deepEqual(clonedResource.getSourceMetadata(), { + adapter: "FileSystem", + fsPath: "/resources/my.js", + contentModified: false, + }); + + // Change existing resource and clone + resource.setString("New Content"); + + const clonedResource2 = await resource.clone(); + + t.not(clonedResource2.getSourceMetadata(), resource.getSourceMetadata(), + "Clone has de-referenced instance of sourceMetadata"); + t.deepEqual(clonedResource2.getSourceMetadata(), { + adapter: "FileSystem", + fsPath: "/resources/my.js", + contentModified: true, + }); + + t.true(resource.isModified(), "Original resource is flagged as modified"); + t.false(clonedResource.isModified(), "Cloned resource 1 is not flagged as modified"); + t.false(clonedResource2.isModified(), "Cloned resource 2 is not flagged as modified"); +}); + +test("Resource: clone resource with project removes project", async (t) => { + const myProject = { + name: "my project" + }; + + const resource = new Resource({ + path: "/my/path/to/resource", + project: myProject + }); + + const clonedResource = await resource.clone(); + t.pass("Resource cloned"); + + const clonedResourceProject = await clonedResource.getProject(); + t.falsy(clonedResourceProject, "Cloned resource should not have a project"); +}); + +test("Resource: create resource with sourceMetadata.contentModified: true", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + sourceMetadata: { + adapter: "FileSystem", + fsPath: "/resources/my.js", + contentModified: true + } + }); + + t.true(resource.getSourceMetadata().contentModified, "Modified flag is still true"); + t.false(resource.isModified(), "Resource is not modified"); +}); + +test("getStream with createStream callback content: Subsequent content requests should throw error due " + + "to drained content", async (t) => { + const resource = createBasicResource(); + resource.getStream(); + t.throws(() => { + resource.getStream(); + }, {message: /Content of Resource \/app\/index.html has been drained/}); + await t.throwsAsync(resource.getBuffer(), {message: /Content of Resource \/app\/index.html has been drained/}); + await t.throwsAsync(resource.getString(), {message: /Content of Resource \/app\/index.html has been drained/}); +}); + +test("getStream with Buffer content: Subsequent content requests should throw error due to drained " + + "content", async (t) => { + const resource = createBasicResource(); + await resource.getBuffer(); + resource.getStream(); + t.throws(() => { + resource.getStream(); + }, {message: /Content of Resource \/app\/index.html has been drained/}); + await t.throwsAsync(resource.getBuffer(), {message: /Content of Resource \/app\/index.html has been drained/}); + await t.throwsAsync(resource.getString(), {message: /Content of Resource \/app\/index.html has been drained/}); +}); + +test("getStream with Stream content: Subsequent content requests should throw error due to drained " + + "content", async (t) => { + const resource = createBasicResource(); + const tStream = new Transform({ + transform(chunk, encoding, callback) { + this.push(chunk.toString()); + callback(); + } + }); + const stream = resource.getStream(); + stream.pipe(tStream); + resource.setStream(tStream); + + resource.getStream(); + t.throws(() => { + resource.getStream(); + }, {message: /Content of Resource \/app\/index.html has been drained/}); + await t.throwsAsync(resource.getBuffer(), {message: /Content of Resource \/app\/index.html has been drained/}); + await t.throwsAsync(resource.getString(), {message: /Content of Resource \/app\/index.html has been drained/}); +}); + +test("getBuffer from Stream content: Subsequent content requests should not throw error due to drained " + + "content", async (t) => { + const resource = createBasicResource(); + const tStream = new Transform({ + transform(chunk, encoding, callback) { + this.push(chunk.toString()); + callback(); + } + }); + const stream = resource.getStream(); + stream.pipe(tStream); + resource.setStream(tStream); + + const p1 = resource.getBuffer(); + const p2 = resource.getBuffer(); + + await t.notThrowsAsync(p1); + + // Race condition in _getBufferFromStream used to cause p2 + // to throw "Content stream of Resource /app/index.html is flagged as drained." + await t.notThrowsAsync(p2); +}); + +test("Resource: getProject", (t) => { + t.plan(1); + const resource = new Resource({ + path: "/my/path/to/resource", + project: {getName: () => "Mock Project"} + }); + const project = resource.getProject(); + t.is(project.getName(), "Mock Project"); +}); + +test("Resource: setProject", (t) => { + t.plan(1); + const resource = new Resource({ + path: "/my/path/to/resource" + }); + const project = {getName: () => "Mock Project"}; + resource.setProject(project); + t.is(resource.getProject().getName(), "Mock Project"); +}); + +test("Resource: reassign with setProject", (t) => { + t.plan(2); + const resource = new Resource({ + path: "/my/path/to/resource", + project: {getName: () => "Mock Project"} + }); + const project = {getName: () => "New Mock Project"}; + const error = t.throws(() => resource.setProject(project)); + t.is(error.message, "Unable to assign project New Mock Project to resource /my/path/to/resource: " + + "Resource is already associated to project " + project); +}); + +test("Resource: constructor with stream", async (t) => { + const stream = new Stream.Readable(); + stream._read = function() {}; + stream.push("I am a "); + stream.push("readable "); + stream.push("stream!"); + stream.push(null); + + const resource = new Resource({ + path: "/my/path/to/resource", + stream, + sourceMetadata: {} // Needs to be passed in order to get the "modified" state + }); + + t.is(resource.getSourceMetadata().contentModified, false); + + const value = await resource.getString(); + t.is(value, "I am a readable stream!"); + + // modified should still be false although setBuffer is called internally + t.is(resource.getSourceMetadata().contentModified, false); +}); + +test("integration stat - resource size", async (t) => { + const fsPath = path.join("test", "fixtures", "application.a", "webapp", "index.html"); + const statInfo = await fs.stat(fsPath); + + const resource = new Resource({ + path: "/some/path", + statInfo, + createStream: () => { + return createReadStream(fsPath); + } + }); + t.is(await resource.getSize(), 91); + + // Setting the same content again should end up with the same size + resource.setString(await resource.getString()); + t.is(await resource.getSize(), 91); + + resource.setString("myvalue"); + t.is(await resource.getSize(), 7); +}); diff --git a/packages/fs/test/lib/ResourceFacade.js b/packages/fs/test/lib/ResourceFacade.js new file mode 100644 index 00000000000..5dee2fc8f1c --- /dev/null +++ b/packages/fs/test/lib/ResourceFacade.js @@ -0,0 +1,98 @@ +import test from "ava"; +import sinon from "sinon"; +import Resource from "../../lib/Resource.js"; +import ResourceFacade from "../../lib/ResourceFacade.js"; + +test.afterEach.always( (t) => { + sinon.restore(); +}); + +test("Create instance", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + string: "my content" + }); + const resourceFacade = new ResourceFacade({ + path: "/my/path", + resource + }); + t.is(resourceFacade.getPath(), "/my/path", "Returns correct path"); + t.is(resourceFacade.getName(), "path", "Returns correct name"); + t.is(resourceFacade.getConcealedResource(), resource, "Returns correct concealed resource"); +}); + +test("Create instance: Missing parameters", (t) => { + t.throws(() => { + new ResourceFacade({ + path: "/my/path", + }); + }, { + instanceOf: Error, + message: "Unable to create ResourceFacade: Missing parameter 'resource'" + }); + t.throws(() => { + new ResourceFacade({ + resource: {}, + }); + }, { + instanceOf: Error, + message: "Unable to create ResourceFacade: Missing parameter 'path'" + }); +}); + +test("ResourceFacade #clone", async (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + string: "my content" + }); + const resourceFacade = new ResourceFacade({ + path: "/my/path", + resource + }); + + const clone = await resourceFacade.clone(); + t.true(clone instanceof Resource, "Cloned resource facade is an instance of Resource"); + t.is(clone.getPath(), "/my/path", "Cloned resource has path of resource facade"); +}); + +test("ResourceFacade #setPath", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + string: "my content" + }); + const resourceFacade = new ResourceFacade({ + path: "/my/path", + resource + }); + + const err = t.throws(() => { + resourceFacade.setPath("my/other/path"); + }); + t.is(err.message, "The path of a ResourceFacade can't be changed", "Threw with expected error message"); +}); + +test("ResourceFacade provides same public functions as Resource", (t) => { + const resource = new Resource({ + path: "/my/path/to/resource", + string: "my content" + }); + const resourceFacade = new ResourceFacade({ + path: "/my/path", + resource + }); + + const methods = Object.getOwnPropertyNames(Resource.prototype) + .filter((p) => (!p.startsWith("_") && typeof resource[p] === "function")); + + methods.forEach((method) => { + t.truthy(resourceFacade[method], `resourceFacade provides function #${method}`); + if (["constructor", "getPath", "getName", "setPath", "clone"].includes(method)) { + // special functions with separate tests + return; + } + const stub = sinon.stub(resource, method); + resourceFacade[method]("argument"); + t.is(stub.callCount, 1, `Resource#${method} stub got called once by resourceFacade#${method}`); + stub.restore(); + }); +}); diff --git a/packages/fs/test/lib/ResourceTagCollection.js b/packages/fs/test/lib/ResourceTagCollection.js new file mode 100644 index 00000000000..1329187c471 --- /dev/null +++ b/packages/fs/test/lib/ResourceTagCollection.js @@ -0,0 +1,328 @@ +import test from "ava"; +import sinon from "sinon"; +import Resource from "../../lib/Resource.js"; +import ResourceTagCollection from "../../lib/ResourceTagCollection.js"; + +test.afterEach.always((t) => { + sinon.restore(); +}); + +test("setTag", (t) => { + const resource = new Resource({ + path: "/some/path" + }); + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + + const validateResourceSpy = sinon.spy(tagCollection, "_getPath"); + const validateTagSpy = sinon.spy(tagCollection, "_validateTag"); + const validateValueSpy = sinon.spy(tagCollection, "_validateValue"); + + tagCollection.setTag(resource, "abc:MyTag", "my value"); + + t.deepEqual(tagCollection._pathTags, { + "/some/path": { + "abc:MyTag": "my value" + } + }, "Tag correctly stored"); + + t.is(validateResourceSpy.callCount, 1, "_getPath called once"); + t.is(validateResourceSpy.getCall(0).args[0], resource, + "_getPath called with correct arguments"); + + t.is(validateTagSpy.callCount, 1, "_validateTag called once"); + t.is(validateTagSpy.getCall(0).args[0], "abc:MyTag", + "_validateTag called with correct arguments"); + + t.is(validateValueSpy.callCount, 1, "_validateValue called once"); + t.is(validateValueSpy.getCall(0).args[0], "my value", + "_validateValue called with correct arguments"); +}); + +test("setTag: Value defaults to true", (t) => { + const resource = new Resource({ + path: "/some/path" + }); + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + tagCollection.setTag(resource, "abc:MyTag"); + + t.deepEqual(tagCollection._pathTags, { + "/some/path": { + "abc:MyTag": true + } + }, "Tag correctly stored"); +}); + +test("getTag", (t) => { + const resource = new Resource({ + path: "/some/path" + }); + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + tagCollection.setTag(resource, "abc:MyTag", 123); + + const validateResourceSpy = sinon.spy(tagCollection, "_getPath"); + const validateTagSpy = sinon.spy(tagCollection, "_validateTag"); + + const value = tagCollection.getTag(resource, "abc:MyTag"); + + t.is(value, 123, "Got correct tag value"); + + t.is(validateResourceSpy.callCount, 1, "_getPath called once"); + t.is(validateResourceSpy.getCall(0).args[0], resource, + "_getPath called with correct arguments"); + + t.is(validateTagSpy.callCount, 1, "_validateTag called once"); + t.is(validateTagSpy.getCall(0).args[0], "abc:MyTag", + "_validateTag called with correct arguments"); +}); + +test("getTag with prefilled tags", (t) => { + const resource = new Resource({ + path: "/some/path" + }); + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"], + tags: { + "/some/path": { + "abc:MyTag": 123 + } + } + }); + + const validateResourceSpy = sinon.spy(tagCollection, "_getPath"); + const validateTagSpy = sinon.spy(tagCollection, "_validateTag"); + + const value = tagCollection.getTag(resource, "abc:MyTag"); + + t.is(value, 123, "Got correct tag value"); + + t.is(validateResourceSpy.callCount, 1, "_getPath called once"); + t.is(validateResourceSpy.getCall(0).args[0], resource, + "_getPath called with correct arguments"); + + t.is(validateTagSpy.callCount, 1, "_validateTag called once"); + t.is(validateTagSpy.getCall(0).args[0], "abc:MyTag", + "_validateTag called with correct arguments"); +}); + +test("clearTag", (t) => { + const resource = new Resource({ + path: "/some/path" + }); + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + + tagCollection.setTag(resource, "abc:MyTag", 123); + + const validateResourceSpy = sinon.spy(tagCollection, "_getPath"); + const validateTagSpy = sinon.spy(tagCollection, "_validateTag"); + + tagCollection.clearTag(resource, "abc:MyTag"); + + t.deepEqual(tagCollection._pathTags, { + "/some/path": { + "abc:MyTag": undefined + } + }, "Tag value set to undefined"); + + t.is(validateResourceSpy.callCount, 1, "_getPath called once"); + t.is(validateResourceSpy.getCall(0).args[0], resource, + "_getPath called with correct arguments"); + + t.is(validateTagSpy.callCount, 1, "_validateTag called once"); + t.is(validateTagSpy.getCall(0).args[0], "abc:MyTag", + "_validateTag called with correct arguments"); +}); + +test("_validateTag: Not in list of allowed tags", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + t.throws(() => { + tagCollection._validateTag("abc:MyOtherTag"); + }, { + instanceOf: Error, + message: `Tag "abc:MyOtherTag" not accepted by this collection. ` + + `Accepted tags are: abc:MyTag. Accepted namespaces are: *none*` + }); +}); + +test("_validateTag: Empty list of tags and namespaces", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: [], + allowedNamespaces: [] + }); + t.throws(() => { + tagCollection._validateTag("abc:MyOtherTag"); + }, { + instanceOf: Error, + message: `Tag "abc:MyOtherTag" not accepted by this collection. ` + + `Accepted tags are: *none*. Accepted namespaces are: *none*` + }); +}); + +test("_validateTag: Missing colon", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["aBcMyTag"] + }); + t.throws(() => { + tagCollection._validateTag("aBcMyTag"); + }, { + instanceOf: Error, + message: `Invalid Tag "aBcMyTag": Colon required after namespace` + }); +}); + +test("_validateTag: Too many colons", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["aBc:My:Tag"] + }); + t.throws(() => { + tagCollection._validateTag("aBc:My:Tag"); + }, { + instanceOf: Error, + message: `Invalid Tag "aBc:My:Tag": Expected exactly one colon but found 2` + }); +}); + +test("_validateTag: Invalid namespace with uppercase letter", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["aBc:MyTag"] + }); + t.throws(() => { + tagCollection._validateTag("aBc:MyTag"); + }, { + instanceOf: Error, + message: `Invalid Tag "aBc:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter` + }); +}); + +test("_validateTag: Invalid namespace starting with number", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["0abc:MyTag"] + }); + t.throws(() => { + tagCollection._validateTag("0abc:MyTag"); + }, { + instanceOf: Error, + message: `Invalid Tag "0abc:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter` + }); +}); + +test("_validateTag: Invalid namespace containing an illegal character", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["a🦦c:MyTag"] + }); + t.throws(() => { + tagCollection._validateTag("a🦦c:MyTag"); + }, { + instanceOf: Error, + message: `Invalid Tag "a🦦c:MyTag": Namespace part must be alphanumeric, lowercase and start with a letter` + }); +}); + +test("_validateTag: Invalid tag name starting with number", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:0MyTag"] + }); + t.throws(() => { + tagCollection._validateTag("abc:0MyTag"); + }, { + instanceOf: Error, + message: `Invalid Tag "abc:0MyTag": Name part must be alphanumeric and start with a capital letter` + }); +}); + +test("_validateTag: Invalid tag name starting with lowercase letter", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:myTag"] + }); + t.throws(() => { + tagCollection._validateTag("abc:myTag"); + }, { + instanceOf: Error, + message: `Invalid Tag "abc:myTag": Name part must be alphanumeric and start with a capital letter` + }); +}); + +test("_validateTag: Invalid tag name containing an illegal character", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:My/Tag"] + }); + t.throws(() => { + tagCollection._validateTag("abc:My/Tag"); + }, { + instanceOf: Error, + message: `Invalid Tag "abc:My/Tag": Name part must be alphanumeric and start with a capital letter` + }); +}); + +test("_validateValue: Valid values", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + tagCollection._validateValue("bla"); + tagCollection._validateValue(""); + tagCollection._validateValue(true); + tagCollection._validateValue(false); + tagCollection._validateValue(123); + tagCollection._validateValue(0); + tagCollection._validateValue(NaN); // Is a number 🤷 + t.pass("No exception thrown"); +}); + +test("_validateValue: Invalid value of type object", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + t.throws(() => { + tagCollection._validateValue({foo: "bar"}); + }, { + instanceOf: Error, + message: "Invalid Tag Value: Must be of type string, number or boolean but is object" + }); +}); + +test("_validateValue: Invalid value undefined", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + t.throws(() => { + tagCollection._validateValue(undefined); + }, { + instanceOf: Error, + message: "Invalid Tag Value: Must be of type string, number or boolean but is undefined" + }); +}); + +test("_validateValue: Invalid value null", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + t.throws(() => { + tagCollection._validateValue(null); + }, { + instanceOf: Error, + message: "Invalid Tag Value: Must be of type string, number or boolean but is object" + }); +}); + +test("_getPath: Empty path", (t) => { + const tagCollection = new ResourceTagCollection({ + allowedTags: ["abc:MyTag"] + }); + t.throws(() => { + tagCollection._getPath({ + getPath: () => "" + }); + }, { + instanceOf: Error, + message: "Invalid Resource: Resource path must not be empty" + }); +}); diff --git a/packages/fs/test/lib/WriterCollection.js b/packages/fs/test/lib/WriterCollection.js new file mode 100644 index 00000000000..7e3894527a0 --- /dev/null +++ b/packages/fs/test/lib/WriterCollection.js @@ -0,0 +1,212 @@ +import test from "ava"; +import sinon from "sinon"; +import WriterCollection from "../../lib/WriterCollection.js"; +import Resource from "../../lib/Resource.js"; + +test("Constructor: Path mapping regex", (t) => { + const myWriter = {}; + const writer = new WriterCollection({ + name: "myCollection", + writerMapping: { + "/": myWriter, + "/my/path/": myWriter, + "/my/": myWriter, + } + }); + t.is(writer._basePathRegex.toString(), "^((?:/)??(?:/my/)??(?:/my/path/)??)+.*?$", + "Created correct path mapping regular expression"); +}); + +test("Constructor: Path mapping regex has correct escaping", (t) => { + const myWriter = {}; + const writer = new WriterCollection({ + name: "myCollection", + writerMapping: { + "/My\\Weird.Path/": myWriter, + "/my/pa$h/": myWriter, + "/my/": myWriter, + } + }); + t.is(writer._basePathRegex.toString(), "^((?:/My\\\\Weird\\.Path/)??(?:/my/)??(?:/my/pa\\$h/)??)+.*?$", + "Created correct path mapping regular expression"); +}); + +test("Constructor: Throws for missing path mapping", (t) => { + const err = t.throws(() => { + new WriterCollection({ + name: "myCollection" + }); + }); + t.is(err.message, "Cannot create WriterCollection myCollection: Missing parameter 'writerMapping'", + "Threw with expected error message"); +}); + +test("Constructor: Throws for empty path mapping", (t) => { + const err = t.throws(() => { + new WriterCollection({ + name: "myCollection", + writerMapping: {} + }); + }); + t.is(err.message, "Cannot create WriterCollection myCollection: Empty parameter 'writerMapping'", + "Threw with expected error message"); +}); + +test("Constructor: Throws for empty path", (t) => { + const myWriter = { + _write: sinon.stub() + }; + const err = t.throws(() => { + new WriterCollection({ + name: "myCollection", + writerMapping: { + "": myWriter + } + }); + }); + t.is(err.message, "Empty path in path mapping of WriterCollection myCollection", + "Threw with expected error message"); +}); + +test("Constructor: Throws for missing leading slash", (t) => { + const myWriter = { + _write: sinon.stub() + }; + const err = t.throws(() => { + new WriterCollection({ + name: "myCollection", + writerMapping: { + "my/path/": myWriter + } + }); + }); + t.is(err.message, "Missing leading slash in path mapping 'my/path/' of WriterCollection myCollection", + "Threw with expected error message"); +}); + +test("Constructor: Throws for missing trailing slash", (t) => { + const myWriter = { + _write: sinon.stub() + }; + const err = t.throws(() => { + new WriterCollection({ + name: "myCollection", + writerMapping: { + "/my/path": myWriter + } + }); + }); + t.is(err.message, "Missing trailing slash in path mapping '/my/path' of WriterCollection myCollection", + "Threw with expected error message"); +}); + +test("Write", async (t) => { + const myPathWriter = { + _write: sinon.stub() + }; + const myWriter = { + _write: sinon.stub() + }; + const generalWriter = { + _write: sinon.stub() + }; + const writerCollection = new WriterCollection({ + name: "myCollection", + writerMapping: { + "/my/path/": myPathWriter, + "/my/": myWriter, + "/": generalWriter + } + }); + + const myPathResource = new Resource({ + path: "/my/path/resource.res", + string: "content" + }); + const myResource = new Resource({ + path: "/my/resource.res", + string: "content" + }); + const resource = new Resource({ + path: "/resource.res", + string: "content" + }); + + await writerCollection.write(myPathResource, "options 1"); + await writerCollection.write(myResource, "options 2"); + await writerCollection.write(resource, "options 3"); + + t.is(myPathWriter._write.callCount, 1, "One write to /my/path/ writer"); + t.is(myWriter._write.callCount, 1, "One write to /my/ writer"); + t.is(generalWriter._write.callCount, 1, "One write to / writer"); + + t.is(myPathWriter._write.getCall(0).args[0], myPathResource, "Correct resource for /my/path/ writer"); + t.is(myPathWriter._write.getCall(0).args[1], "options 1", "Correct write options for /my/path/ writer"); + t.is(myWriter._write.getCall(0).args[0], myResource, "Correct resource for /my/ writer"); + t.is(myWriter._write.getCall(0).args[1], "options 2", "Correct write options for /my/ writer"); + t.is(generalWriter._write.getCall(0).args[0], resource, "Correct resource for / writer"); + t.is(generalWriter._write.getCall(0).args[1], "options 3", "Correct write options for / writer"); +}); + +test("byGlob", async (t) => { + const myPathWriter = { + _byGlob: sinon.stub().resolves([]) + }; + const myWriter = { + _byGlob: sinon.stub().resolves([]) + }; + const generalWriter = { + _byGlob: sinon.stub().resolves([]) + }; + const writerCollection = new WriterCollection({ + name: "myCollection", + writerMapping: { + "/my/path/": myPathWriter, + "/my/": myWriter, + "/": generalWriter + } + }); + + await writerCollection.byGlob("/**"); + + t.is(myPathWriter._byGlob.callCount, 1, "One _byGlob call to /my/path/ writer"); + t.is(myWriter._byGlob.callCount, 1, "One _byGlob call to /my/ writer"); + t.is(generalWriter._byGlob.callCount, 1, "One _byGlob call to / writer"); + + t.is(myPathWriter._byGlob.getCall(0).args[0], "/**", "Correct glob pattern passed to /my/path/ writer"); + t.is(myWriter._byGlob.getCall(0).args[0], "/**", "Correct glob pattern passed to /my/ writer"); + t.is(generalWriter._byGlob.getCall(0).args[0], "/**", "Correct glob pattern passed to / writer"); +}); + +test("byPath", async (t) => { + const myPathWriter = { + _byPath: sinon.stub().resolves(null) + }; + const myWriter = { + _byPath: sinon.stub().resolves(null) + }; + const generalWriter = { + _byPath: sinon.stub().resolves(null) + }; + const writerCollection = new WriterCollection({ + name: "myCollection", + writerMapping: { + "/my/path/": myPathWriter, + "/my/": myWriter, + "/": generalWriter + } + }); + + await writerCollection.byPath("/my/resource.res"); + + t.is(myPathWriter._byPath.callCount, 1, "One _byPath to /my/path/ writer"); + t.is(myWriter._byPath.callCount, 1, "One _byPath to /my/ writer"); + t.is(generalWriter._byPath.callCount, 1, "One _byPath to / writer"); + + t.is(myPathWriter._byPath.getCall(0).args[0], "/my/resource.res", + "Correct _byPath argument passed to /my/path/ writer"); + t.is(myWriter._byPath.getCall(0).args[0], "/my/resource.res", + "Correct _byPath argument passed to /my/ writer"); + t.is(generalWriter._byPath.getCall(0).args[0], "/my/resource.res", + "Correct _byPath argument passed to / writer"); +}); diff --git a/packages/fs/test/lib/adapters/AbstractAdapter.js b/packages/fs/test/lib/adapters/AbstractAdapter.js new file mode 100644 index 00000000000..16a06708b41 --- /dev/null +++ b/packages/fs/test/lib/adapters/AbstractAdapter.js @@ -0,0 +1,304 @@ +import test from "ava"; +import AbstractAdapter from "../../../lib/adapters/AbstractAdapter.js"; +import {createResource} from "../../../lib/resourceFactory.js"; + +class MyAbstractAdapter extends AbstractAdapter { } + +test("Missing paramter: virBasePath", (t) => { + t.throws(() => { + new MyAbstractAdapter({}); + }, { + message: "Unable to create adapter: Missing parameter 'virBasePath'" + }, "Threw with expected error message"); +}); + +test("virBasePath must be absolute", (t) => { + t.throws(() => { + new MyAbstractAdapter({ + virBasePath: "foo" + }); + }, { + message: "Unable to create adapter: Virtual base path must be absolute but is 'foo'" + }, "Threw with expected error message"); +}); + +test("virBasePath must end with a slash", (t) => { + t.throws(() => { + new MyAbstractAdapter({ + virBasePath: "/foo" + }); + }, { + message: "Unable to create adapter: Virtual base path must end with a slash but is '/foo'" + }, "Threw with expected error message"); +}); + +test("_migrateResource", async (t) => { + // Any JS object which might be a kind of resource + const resource = { + _path: "/test.js" + }; + + const writer = new MyAbstractAdapter({ + virBasePath: "/" + }); + + const migratedResource = await writer._migrateResource(resource); + + t.is(migratedResource.getPath(), "/test.js"); +}); + +test("_assignProjectToResource: Resource is already assigned to another project than provided in the adapter", (t) => { + const resource = createResource({ + path: "/test.js", + project: { + getName: () => "test.lib", + getVersion: () => "2.0.0" + } + }); + + const writer = new MyAbstractAdapter({ + virBasePath: "/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + const error = t.throws(() => writer._assignProjectToResource(resource)); + t.is(error.message, + "Unable to write resource associated with project test.lib into adapter of project test.lib1: /test.js"); +}); + +test("_isPathHandled", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.true(writer._isPathHandled("/dest2/writer/test.js"), "Returned expected result"); + t.true(writer._isPathHandled("/dest2/writer/"), "Returned expected result"); + t.true(writer._isPathHandled("/dest2/writer"), "Returned expected result"); + t.false(writer._isPathHandled("/dest2/write"), "Returned expected result"); + t.false(writer._isPathHandled("/dest2/writerisimo"), "Returned expected result"); + t.false(writer._isPathHandled(""), "Returned expected result"); +}); +test("_resolveVirtualPathToBase (read mode)", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.is(writer._resolveVirtualPathToBase("/dest2/writer/test.js"), "test.js", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer/../writer/test.js"), "test.js", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer"), "", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer/"), "", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/../../dest2/writer/test.js"), "test.js", "Returned expected path"); +}); + +test("_resolveVirtualPathToBase (read mode): Path does not starting with path configured in the adapter", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.is(writer._resolveVirtualPathToBase("/dest2/tmp/test.js"), null, "Returned null"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer/../reader/"), null, "Returned null"); + t.is(writer._resolveVirtualPathToBase("/dest2/write"), null, "Returned null"); + t.is(writer._resolveVirtualPathToBase("/..//write"), null, "Returned null"); +}); + +test("_resolveVirtualPathToBase (read mode): Path Must be absolute", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.throws(() => writer._resolveVirtualPathToBase("./dest2/write"), { + message: + `Failed to resolve virtual path './dest2/write': Path must be absolute` + }, "Threw with expected error message"); +}); + +test("_resolveVirtualPathToBase (write mode)", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.is(writer._resolveVirtualPathToBase("/dest2/writer/test.js", true), "test.js", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer/../writer/test.js", true), "test.js", + "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer", true), "", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/dest2/writer/", true), "", "Returned expected path"); + t.is(writer._resolveVirtualPathToBase("/../../dest2/writer/test.js", true), "test.js", "Returned expected path"); +}); + +test("_resolveVirtualPathToBase (write mode): Path does not starting with path configured in the adapter", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.throws(() => writer._resolveVirtualPathToBase("/dest2/tmp/test.js", true), { + message: + `Failed to write resource with virtual path '/dest2/tmp/test.js': ` + + `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'` + }, "Threw with expected error message"); + + t.throws(() => writer._resolveVirtualPathToBase("/dest2/writer/../reader", true), { + message: + `Failed to write resource with virtual path '/dest2/writer/../reader': ` + + `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'` + }, "Threw with expected error message"); + + t.throws(() => writer._resolveVirtualPathToBase("/dest2/write", true), { + message: + `Failed to write resource with virtual path '/dest2/write': ` + + `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'` + }, "Threw with expected error message"); + + t.throws(() => writer._resolveVirtualPathToBase("/..//write", true), { + message: + `Failed to write resource with virtual path '/..//write': ` + + `Path must start with the configured virtual base path of the adapter. Base path: '/dest2/writer/'` + }, "Threw with expected error message"); +}); + +test("_resolveVirtualPathToBase (write mode): Path Must be absolute", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/dest2/writer/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.throws(() => writer._resolveVirtualPathToBase("./dest2/write", true), { + message: + `Failed to resolve virtual path './dest2/write': Path must be absolute` + }, "Threw with expected error message"); +}); + +test("_normalizePattern", async (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(await writer._normalizePattern("/*/{src,test}/**"), [ + "src/**", + "test/**" + ], "Returned expected patterns"); +}); + +test("_normalizePattern: Match base directory", async (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(await writer._normalizePattern("/*"), [""], + "Returned an empty pattern since the input pattern matches the base directory only"); +}); + +test("_normalizePattern: Match sub-directory", async (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(await writer._normalizePattern("/path/*"), ["*"], + "Returned expected patterns"); +}); + +test("_normalizePattern: Match all", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(writer._normalizePattern("/**/*"), ["**/*"], + "Returned expected patterns"); +}); + +test("_normalizePattern: Relative path segment above virtual root directory", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(writer._normalizePattern("/path/../../*"), [], + "Returned no pattern"); +}); + +test("_normalizePattern: Relative path segment resolving to base directory", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(writer._normalizePattern("/*/../*"), [""], + "Returned an empty pattern since the input pattern matches the base directory only"); +}); + +test("_normalizePattern: Relative path segment", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(writer._normalizePattern("/path/../*"), [""], + "Returned an empty pattern since the input pattern matches the base directory only"); +}); + +test("_normalizePattern: Relative path segment within base directory, matching all", (t) => { + const writer = new MyAbstractAdapter({ + virBasePath: "/path/", + project: { + getName: () => "test.lib1", + getVersion: () => "2.0.0" + } + }); + + t.deepEqual(writer._normalizePattern("/path/path2/../**/*"), ["**/*"], + "Returned expected patterns"); +}); diff --git a/packages/fs/test/lib/adapters/FileSystem.js b/packages/fs/test/lib/adapters/FileSystem.js new file mode 100644 index 00000000000..9e41965b5bb --- /dev/null +++ b/packages/fs/test/lib/adapters/FileSystem.js @@ -0,0 +1,13 @@ +import test from "ava"; + +import FileSystem from "../../../lib/adapters/FileSystem.js"; + +test.serial("Missing parameter: fsBasePath", (t) => { + t.throws(() => { + new FileSystem({ + virBasePath: "/" + }); + }, { + message: "Unable to create adapter: Missing parameter 'fsBasePath'" + }, "Threw with expected error message"); +}); diff --git a/packages/fs/test/lib/adapters/FileSystem_read.js b/packages/fs/test/lib/adapters/FileSystem_read.js new file mode 100644 index 00000000000..d316d1e3faa --- /dev/null +++ b/packages/fs/test/lib/adapters/FileSystem_read.js @@ -0,0 +1,633 @@ +import test from "ava"; +import sinon from "sinon"; +import esmock from "esmock"; +import path from "node:path"; +import {createAdapter} from "../../../lib/resourceFactory.js"; +import {fileURLToPath} from "node:url"; +import Resource from "../../../lib/Resource.js"; + +test("glob resources from application.a w/ virtual base path prefix", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const resources = await readerWriter.byGlob("/app/**/*.html"); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob resources from application.a w/o virtual base path prefix", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const resources = await readerWriter.byGlob("/**/*.html"); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/o virtual base path prefix", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const resources = await readerWriter.byGlob("/*", {nodir: false}); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/o virtual base path prefix and multiple patterns", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const resources = await readerWriter.byGlob([ + "/*", + "/*" + ], {nodir: false}); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/ virtual base path prefix", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob("/app/*", {nodir: false}); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/o virtual base path prefix and nodir: true", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const resources = await readerWriter.byGlob("/*", {nodir: true}); + t.is(resources.length, 0, "Found no resources"); +}); + +test("glob virtual directory w/o virtual base path prefix and nodir: true and multiple patterns", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const resources = await readerWriter.byGlob([ + "/*", + "/*" + ], {nodir: true}); + t.is(resources.length, 0, "Found no resources"); +}); + +test("glob virtual directory w/ virtual base path prefix and nodir: true", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob("/app/*", {nodir: true}); + t.is(resources.length, 0, "Found no resources"); +}); + +test("glob resources from application.a with directory exclude", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + await readerWriter.byGlob("/!(pony,unicorn)/**").then(function(resources) { + t.is(resources.length, 2, "Found exactly two resource"); + }); +}); + +test("glob all above virtual base path (path traversal)", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const res = await readerWriter.byGlob([ + "/app/../**", + ], {nodir: false}); + t.is(res.length, 2, "Returned resources in virtual base dir only"); +}); + +test("glob virtual directory above virtual base path (path traversal)", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + + const res = await readerWriter.byGlob([ + "/*/../../application.b/**", + ], {nodir: false}); + t.is(res.length, 0, "Returned no resources"); +}); + +test("glob not existing directory with existing file path (nodir: true)", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/", + virBasePath: "/", + }); + + // globby will throw if it is attempted to glob the content of a directory + // which is actually a file + await t.throwsAsync(srcReader.byGlob("/test/library/l/Test.html/*", {nodir: true}), { + message: /ENOTDIR/ + }, "Threw with expected error message"); +}); + +test("glob not existing directory with existing file path (nodir: false)", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/", + virBasePath: "/", + }); + + // globby will throw if it is attempted to glob the content of a directory + // which is actually a file + await t.throwsAsync(srcReader.byGlob("/test/library/l/Test.html/*", {nodir: false}), { + message: /ENOTDIR/ + }, "Threw with expected error message"); +}); + +test("byPath", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath virtual directory above base path (path traversal)", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + const resource = await readerWriter.byPath("/resources/app/../package.json", {nodir: true}); + t.is(resource, null, "Found no resource"); +}); + +test("byPath: Root dir w/ trailing slash", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + const resource = await readerWriter.byPath("/resources/app/", {nodir: false}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath: Root dir w/o trailing slash", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + const resource = await readerWriter.byPath("/resources/app", {nodir: false}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath: Virtual directory w/ trailing slash", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + const resource = await readerWriter.byPath("/resources/", {nodir: false}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath: Virtual directory w/o trailing slash", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + const resource = await readerWriter.byPath("/resources", {nodir: false}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath: Incorrect virtual directory w/o trailing slash", async (t) => { + // Adding a trailing slash would make the path not match the base path, which is already + // tested elsewhere + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + + // TODO: This should actually not match + const resource = await readerWriter.byPath("/resour", {nodir: false}); + t.truthy(resource, "Found one resource"); +}); + +function getPathFromResource(resource) { + return resource.getPath(); +} + +test("static excludes: glob library src and test", async (t) => { + const excludes = [ + "/resources/**/some.js", + + // double negation has no effect over following "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + + "/test-resources/**", + ]; + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes + }); + + const testReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/", + excludes + }); + + const srcResources = await srcReader.byGlob("/**/*", {nodir: true}); + const testResources = await testReader.byGlob("/**/*", {nodir: true}); + + t.is(srcResources.length, 1, "Found one src resource"); + t.deepEqual(srcResources.map(getPathFromResource), [ + "/resources/library/l/.library" + ], "Found expected src resources"); + + t.is(testResources.length, 0, "Found no test resources"); +}); + +test("static excludes: glob library src and test with double negation", async (t) => { + const excludes = [ + "/resources/**/some.js", + "/test-resources/**", + + // double negation has effect over preceding "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + ]; + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes + }); + + const testReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/", + excludes + }); + + const srcResources = await srcReader.byGlob("/**/*", {nodir: true}); + const testResources = await testReader.byGlob("/**/*", {nodir: true}); + + t.is(srcResources.length, 1, "Found one src resource"); + t.deepEqual(srcResources.map(getPathFromResource), [ + "/resources/library/l/.library" + ], "Found expected src resources"); + + t.is(testResources.length, 1, "Found one test resource"); + t.deepEqual(testResources.map(getPathFromResource), [ + "/test-resources/library/l/Test2.html" + ], "Found expected test resources"); +}); + +test("static excludes: glob library test with double negation", async (t) => { + const excludes = [ + "/test-resources/**", + + // double negation has effect over preceding "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + ]; + + const testReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/", + excludes + }); + + const testResources = await testReader.byGlob("/**/*", {nodir: true}); + + t.is(testResources.length, 1, "Found one test resource"); + t.deepEqual(testResources.map(getPathFromResource), [ + "/test-resources/library/l/Test2.html" + ], "Found expected test resources"); +}); + +test("static excludes: glob with virtual root exclude", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes: [ + "/**" + ] + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: true}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("static excludes: glob with negated directory exclude, excluding resources", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes: [ + "/!({pony,unicorn})/**" + ] + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: true}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("static excludes: glob with negated directory exclude, not excluding resources", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes: [ + "/!(resources)/**" + ] + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: true}); + + t.is(resources.length, 2, "Found two resources"); +}); + +test("static excludes: byPath exclude everything in sub-directory", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + excludes: ["/resources/app/**"] + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.is(resource, null, "Found no resource"); +}); + +test("static excludes: byPath exclude with negation", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + excludes: [ + "/resources/app/**", + "!/resources/app/index.html" + ] + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.truthy(resource, "Found one resource"); +}); + +test("static excludes: byPath exclude with unused negation", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + excludes: [ + "!/resources/app/index.html", + "/resources/app/**" + ] + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.is(resource, null, "Resource is excluded"); +}); + +test("static excludes: byPath exclude with negated directory pattern, excluding resources", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + excludes: [ + "/!({pony,unicorn})/**" + ] + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.is(resource, null, "Found no resource"); +}); + +test("static excludes: byPath exclude with negated directory pattern, not excluding resources", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + excludes: [ + "/!(resources)/**" + ] + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath: exclude with unused negation", async (t) => { + const readerWriter = createAdapter({ + fsBasePath: "./test/fixtures/application.b/webapp", + virBasePath: "/resources/app/", + excludes: [ + "!/resources/app/i18n/**", + "/resources/app/**", + "!/resources/app/manifest.json" + ] + }); + + const [manifest, i18n, i18ni18n] = await Promise.all([ + readerWriter.byPath("/resources/app/manifest.json", {nodir: true}), + readerWriter.byPath("/resources/app/i18n.properties", {nodir: true}), + readerWriter.byPath("/resources/app/i18n/i18n.properties", {nodir: true}) + ]); + t.truthy(manifest, "Found manifest.json resource"); + t.is(i18n, null, "i18n resource is excluded"); + t.is(i18ni18n, null, "i18n in i18n directory resource is excluded"); +}); + +test("static excludes: glob library src and test with double negation (nodir: false)", async (t) => { + const excludes = [ + "/resources/**/some.js", + "/test-resources/**", + + // double negation has effect over preceding "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + ]; + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes + }); + + const testReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/", + excludes + }); + + const srcResources = await srcReader.byGlob("/**/*", {nodir: false}); + const testResources = await testReader.byGlob("/**/*", {nodir: false}); + + t.is(srcResources.length, 3, "Found one src resource and two directories"); + + t.is(testResources.length, 1, "Found one test resource"); + t.deepEqual(testResources.map(getPathFromResource), [ + "/test-resources/library/l/Test2.html" + ], "Found expected test resources"); +}); + +test("static excludes: glob with virtual root exclude (nodir: false)", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes: [ + "/**" + ] + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: false}); + + t.is(resources.length, 0, "Found no resources"); +}); +test("static excludes: glob with negated directory exclude, excluding resources (nodir: false)", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes: [ + "/!({pony,unicorn})/**" + ] + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: false}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("static excludes: glob with negated directory exclude, not excluding resources (nodir: false)", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + excludes: [ + "/!(resources)/**" + ] + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: false}); + + t.is(resources.length, 4, "Found two resources and two directories"); +}); + +test("static excludes: glob with exclude (nodir: false)", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/", + virBasePath: "/", + excludes: [ + "/test/**" + ] + }); + + const resources = await srcReader.byGlob("/test/library/l/Test.html", {nodir: false}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("glob with useGitignore: true", async (t) => { + const srcReader = createAdapter({ + fsBasePath: "./test/fixtures/library.l/", + virBasePath: "/", + useGitignore: true + }); + + const resources = await srcReader.byGlob("/**/*"); + + t.is(resources.length, 5, "Found five resources"); + t.deepEqual(resources.map(getPathFromResource).sort(), [ + "/.gitignore", + "/package.json", + "/src/library/l/.library", + "/src/library/l/some.js", + "/ui5.yaml", + ], "Found expected resources"); +}); + +test("byPath with useGitignore: true", async (t) => { + const {isGitIgnored} = await import("globby"); + const isGitIgnoredSpy = sinon.stub().callsFake(isGitIgnored); + + const FileSystem = await esmock("../../../lib/adapters/FileSystem.js", { + "globby": { + isGitIgnored: isGitIgnoredSpy + } + }); + + const fsBasePath = "./test/fixtures/library.l/"; + const srcReader = new FileSystem({ + fsBasePath, + virBasePath: "/", + useGitignore: true + }); + + const testResource = await srcReader.byPath("/test/library/l/Test.html"); + t.is(testResource, null, "Ignored resource cannot be found"); + + t.is(isGitIgnoredSpy.callCount, 1); + t.deepEqual( + isGitIgnoredSpy.getCall(0).args, + [{cwd: path.resolve(fsBasePath) + path.sep}], + "isGitIgnored should be called with the correct cwd" + ); + + const srcResource = await srcReader.byPath("/src/library/l/some.js"); + t.truthy(srcResource, "Not-ignored resource can be found"); + t.is(srcResource.getPath(), "/src/library/l/some.js", "Found resource has correct path"); + + t.is(isGitIgnoredSpy.callCount, 1, "isGitIgnored should only be called once per FileSystem instance"); +}); + +test("byGlob with a larger number of results", async (t) => { + const globbyStub = sinon.stub().callsFake(async (_patterns, _options) => { + return Array.from({length: 1_000_000}).map((_, i) => `/file${i}.js`); + }); + + const fakeStat = { + isFile: () => true, + isDirectory: () => false + }; + + const FileSystem = await esmock("../../../lib/adapters/FileSystem.js", { + "globby": { + globby: globbyStub, + isGitIgnored: async () => false + }, + "graceful-fs": { + stat: ((filePath, cb) => { + if (filePath.startsWith(fsBasePath)) { + cb(null, fakeStat); + } else { + cb({code: "ENOENT"}); + } + }) + } + }); + + const fsBasePath = fileURLToPath(new URL("../../tmp/virtual/large-project/", import.meta.url)); + + const reader = new FileSystem({ + fsBasePath, + virBasePath: "/" + }); + + const resources = await reader.byGlob("/**/*.js"); + + t.is(resources.length, 1_000_000); + + // Picking just one resource in between for testing + const resource = resources[500_000]; + + t.true(resource instanceof Resource, "Resource should be an instance of Resource"); + t.true( + resource.getPath().startsWith("/file") && resource.getPath().endsWith(".js"), + "Resource should have correct path" + ); +}); diff --git a/packages/fs/test/lib/adapters/FileSystem_write.js b/packages/fs/test/lib/adapters/FileSystem_write.js new file mode 100644 index 00000000000..8386c2a5487 --- /dev/null +++ b/packages/fs/test/lib/adapters/FileSystem_write.js @@ -0,0 +1,307 @@ +import path from "node:path"; +import {readFile} from "node:fs/promises"; +import {access as fsAccess, constants as fsConstants, mkdir} from "node:fs/promises"; +import {fileURLToPath} from "node:url"; +import test from "ava"; +import {rimraf} from "rimraf"; +import sinon from "sinon"; + +import {createAdapter, createResource} from "../../../lib/resourceFactory.js"; + +function getFileContent(path) { + return readFile(path, "utf8"); +} + +async function fileEqual(t, actual, expected) { + const actualContent = await getFileContent(actual); + const expectedContent = await getFileContent(expected); + t.is(actualContent, expectedContent); +} + +async function fileContent(t, path, expected) { + const actualContent = await getFileContent(path); + t.is(actualContent, expected); +} + +test.beforeEach(async (t) => { + const tmpDirName = t.title.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase(); // Generate tmp dir name from test name + + // Create a tmp directory for every test + t.context.tmpDirPath = fileURLToPath(new URL("../../tmp/adapters/FileSystemWrite/" + tmpDirName, import.meta.url)); + await mkdir(t.context.tmpDirPath, {recursive: true}); + + t.context.readerWriters = { + source: createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }), + dest: createAdapter({ + fsBasePath: "./test/tmp/adapters/FileSystemWrite/" + tmpDirName, + virBasePath: "/app/" + }) + }; +}); + +test.afterEach.always((t) => { + // Cleanup tmp directory + return rimraf(t.context.tmpDirPath); +}); + +test("Write resource", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Get resource from one readerWriter + const resource = await readerWriters.source.byPath("/app/index.html"); + // Write resource content to another readerWriter + + await readerWriters.dest.write(resource); + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), "Resource content can still be accessed"); +}); + +test("Write resource in readOnly mode", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Get resource from one readerWriter + const resource = await readerWriters.source.byPath("/app/index.html"); + // Write resource content to another readerWriter + await readerWriters.dest.write(resource, {readOnly: true}); + + await t.notThrowsAsync(fsAccess(destFsPath, fsConstants.R_OK), "File can be read"); + await t.throwsAsync(fsAccess(destFsPath, fsConstants.W_OK), + {message: /EACCES: permission denied|EPERM: operation not permitted/}, + "File can not be written"); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), "Resource content can still be accessed"); +}); + +test("Write resource in drain mode", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Get resource from one readerWriter + const resource = await readerWriters.source.byPath("/app/index.html"); + // Write resource content to another readerWriter + await readerWriters.dest.write(resource, {drain: true}); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + + // Should not fail as resource hasn't been modified + await t.notThrowsAsync(resource.getBuffer()); +}); + +test("Write modified resource in drain mode", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Get resource from one readerWriter + const resource = await readerWriters.source.byPath("/app/index.html"); + + resource.setString(` + + + Application A + + + + +` + ); + + // Write resource content to another readerWriter + await readerWriters.dest.write(resource, {drain: true}); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.throwsAsync(resource.getBuffer(), + {message: /Content of Resource \/app\/index.html has been drained/}); +}); + +test("Write with readOnly and drain options set should fail", async (t) => { + const readerWriters = t.context.readerWriters; + + // Get resource from one readerWriter + const resource = await readerWriters.source.byPath("/app/index.html"); + // Write resource content to another readerWriter + await t.throwsAsync(readerWriters.dest.write(resource, {readOnly: true, drain: true}), { + message: "Error while writing resource /app/index.html: " + + "Do not use options 'drain' and 'readOnly' at the same time." + }); +}); + +test("Write unmodified resource into same file", async (t) => { + const {source, dest} = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Preparation: Create resource in dest + const sourceResource = await source.byPath("/app/index.html"); + await dest.write(sourceResource); + + const resource = await dest.byPath("/app/index.html"); + resource.setStream(resource.getStream()); + await dest.write(resource); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), "Resource content can still be accessed"); +}); + +test("Write unmodified resource into same file in drain mode", async (t) => { + const {source, dest} = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Preparation: Create resource in dest + const sourceResource = await source.byPath("/app/index.html"); + await dest.write(sourceResource); + + const resource = await dest.byPath("/app/index.html"); + await dest.write(resource, {drain: true}); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), + "Resource content can still be accessed, since stream had to be buffered"); +}); + +test("Write unmodified resource into same file in read-only mode", async (t) => { + const {source, dest} = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Preparation: Create resource in dest + const sourceResource = await source.byPath("/app/index.html"); + await dest.write(sourceResource); + + const resource = await dest.byPath("/app/index.html"); + await dest.write(resource, {readOnly: true}); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), + "Resource content can still be accessed, since stream had to be buffered"); + + await t.notThrowsAsync(fsAccess(destFsPath, fsConstants.R_OK), "File can be read"); + await t.throwsAsync(fsAccess(destFsPath, fsConstants.W_OK), + {message: /EACCES: permission denied|EPERM: operation not permitted/}, + "File can not be written"); +}); + +test("Write modified resource into same file", async (t) => { + const {source, dest} = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Preparation: Create resource in dest + const sourceResource = await source.byPath("/app/index.html"); + await dest.write(sourceResource); + + const resource = await dest.byPath("/app/index.html"); + // Simulate a modification by setting a stream + resource.setStream(resource.getStream()); + await dest.write(resource); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), "Resource content can still be accessed"); +}); + +test("Write modified resource into same file in drain mode", async (t) => { + const {source, dest} = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Preparation: Create resource in dest + const sourceResource = await source.byPath("/app/index.html"); + await dest.write(sourceResource); + + const resource = await dest.byPath("/app/index.html"); + // Simulate a modification by setting a stream + resource.setStream(resource.getStream()); + await dest.write(resource, {drain: true}); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.throwsAsync(resource.getBuffer(), + {message: /Content of Resource \/app\/index.html has been drained/}); +}); + +test("Write modified resource into same file in read-only mode", async (t) => { + const {source, dest} = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Preparation: Create resource in dest + const sourceResource = await source.byPath("/app/index.html"); + await dest.write(sourceResource); + + const resource = await dest.byPath("/app/index.html"); + // Simulate a modification by setting a stream + resource.setStream(resource.getStream()); + await dest.write(resource, {readOnly: true}); + + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), + "Resource content can still be accessed"); + + await t.notThrowsAsync(fsAccess(destFsPath, fsConstants.R_OK), "File can be read"); + await t.throwsAsync(fsAccess(destFsPath, fsConstants.W_OK), + {message: /EACCES: permission denied|EPERM: operation not permitted/}, + "File can not be written"); +}); + +test("Write new resource", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + const resource = createResource({ + path: "/app/index.html", + string: "Resource content", + }); + + await readerWriters.dest.write(resource); + await t.notThrowsAsync(fileContent(t, destFsPath, "Resource content")); + await t.notThrowsAsync(resource.getBuffer(), "Resource content can still be accessed"); +}); + +test("Write new resource in drain mode", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + const resource = createResource({ + path: "/app/index.html", + string: "Resource content", + }); + + await readerWriters.dest.write(resource, {drain: true}); + await t.notThrowsAsync(fileContent(t, destFsPath, "Resource content")); + await t.throwsAsync(resource.getBuffer(), + {message: /Content of Resource \/app\/index.html has been drained/}); +}); + +test("Write new resource in read-only mode", async (t) => { + const readerWriters = t.context.readerWriters; + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + const resource = createResource({ + path: "/app/index.html", + string: "Resource content", + }); + + await readerWriters.dest.write(resource, {readOnly: true}); + await t.notThrowsAsync(fileContent(t, destFsPath, "Resource content")); + await t.notThrowsAsync(resource.getBuffer(), + "Resource content can still be accessed"); + await t.notThrowsAsync(fsAccess(destFsPath, fsConstants.R_OK), "File can be read"); + await t.throwsAsync(fsAccess(destFsPath, fsConstants.W_OK), + {message: /EACCES: permission denied|EPERM: operation not permitted/}, + "File can not be written"); +}); + +test("Migration of resource is executed", async (t) => { + const readerWriters = t.context.readerWriters; + const migrateResourceWriterSpy = sinon.spy(readerWriters.dest, "_migrateResource"); + const destFsPath = path.join(t.context.tmpDirPath, "index.html"); + + // Get resource from one readerWriter + const resource = await readerWriters.source.byPath("/app/index.html"); + + // Write resource content to another readerWriter + await readerWriters.dest.write(resource); + + t.is(migrateResourceWriterSpy.callCount, 1); + await t.notThrowsAsync(fileEqual(t, destFsPath, "./test/fixtures/application.a/webapp/index.html")); + await t.notThrowsAsync(resource.getBuffer(), "Resource content can still be accessed"); +}); diff --git a/packages/fs/test/lib/adapters/FileSystem_write_large_file.js b/packages/fs/test/lib/adapters/FileSystem_write_large_file.js new file mode 100644 index 00000000000..bb8fbfb6660 --- /dev/null +++ b/packages/fs/test/lib/adapters/FileSystem_write_large_file.js @@ -0,0 +1,40 @@ +import test from "ava"; +import {fileURLToPath} from "node:url"; +import {Buffer} from "node:buffer"; +import {readFile} from "node:fs/promises"; + +import FileSystem from "../../../lib/adapters/FileSystem.js"; +import Resource from "../../../lib/Resource.js"; +import path from "node:path"; + +test.serial("Stream a large file from source to target", async (t) => { + // This test used to fail. The FileSystem adapter would directly pipe the read-stream into a write stream with the + // same path. Leading to only the first chunk of the source file or nothing at all being written into the target + // This has been fixed with https://github.com/SAP/ui5-fs/pull/472 + + const fsBasePath = fileURLToPath(new URL("../../tmp/adapters/FileSystemWriteLargeFile/", import.meta.url)); + + const fileSystem = new FileSystem({ + fsBasePath, + virBasePath: "/" + }); + + const largeBuffer = Buffer.alloc(1048576); // 1MB + + await fileSystem.write(new Resource({ + path: "/large-file.txt", + buffer: largeBuffer + })); + + t.deepEqual(await readFile(path.join(fsBasePath, "large-file.txt")), largeBuffer, + "Large file should be written as expected"); + + const largeResource = await fileSystem.byPath("/large-file.txt"); + + largeResource.setStream(largeResource.getStream()); + + await fileSystem.write(largeResource); + + t.deepEqual((await readFile(path.join(fsBasePath, "large-file.txt"))).length, largeBuffer.length, + "Large file should be overwritten with exact same contents"); +}); diff --git a/packages/fs/test/lib/adapters/Memory_read.js b/packages/fs/test/lib/adapters/Memory_read.js new file mode 100644 index 00000000000..70a034b27d4 --- /dev/null +++ b/packages/fs/test/lib/adapters/Memory_read.js @@ -0,0 +1,720 @@ +import test from "ava"; +import {createAdapter, createResource} from "../../../lib/resourceFactory.js"; + +async function fillFromFs(readerWriter, {fsBasePath = "./test/fixtures/glob", virBasePath = "/app/"} = {}) { + const fsReader = createAdapter({ + fsBasePath, + virBasePath, + }); + + const fsResources = await fsReader.byGlob("/**/*"); + return Promise.all(fsResources.map(function(resource) { + return readerWriter.write(resource); + })); +} + +function matchGlobResult(t, resources, expectedResources) { + t.deepEqual(resources.length, expectedResources.length, "Amount of files matches expected result."); + + const matchedResources = resources.map((resource) => { + return resource.getPath(); + }); + + for (let i = 0; i < expectedResources.length; i++) { + const expectedResource = expectedResources[i]; + t.true( + matchedResources.indexOf(expectedResource) !== -1, + "File '" + expectedResource + "' was found." + ); + } +} + +test("glob resources from application.a w/ virtual base path prefix", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + + const resource = createResource({ + path: "/app/index.html", + string: "test" + }); + await readerWriter.write(resource); + + const resources = await readerWriter.byGlob("/app/*.html"); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob resources from application.a w/o virtual base path prefix", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + + const resource = createResource({ + path: "/app/index.html", + string: "test" + }); + await readerWriter.write(resource); + + const resources = await readerWriter.byGlob("/**/*.html"); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/o virtual base path prefix", async (t) => { + // TODO: Add similar test (globbing on empty directory) for FS RL + const readerWriter = createAdapter({ + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob("/*", {nodir: false}); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/o virtual base path prefix and multiple patterns", async (t) => { + // TODO: Add similar test (globbing on empty directory) for FS RL + const readerWriter = createAdapter({ + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob([ + "/*", + "/*" + ], {nodir: false}); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/ virtual base path prefix", async (t) => { + // TODO: Add similar test (globbing on empty directory) for FS RL + const readerWriter = createAdapter({ + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob("/app/*", {nodir: false}); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("glob virtual directory w/o virtual base path prefix and nodir: true", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob("/*", {nodir: true}); + t.is(resources.length, 0, "Found no resources"); +}); + +test("glob virtual directory w/ virtual base path prefix and nodir: true", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob("/app/*", {nodir: true}); + t.is(resources.length, 0, "Found no resources"); +}); + +test("glob virtual directory w/ virtual base path prefix and nodir: true and multiple patterns", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/one/two/" + }); + + const resources = await readerWriter.byGlob([ + "/*", + "/*" + ], {nodir: true}); + t.is(resources.length, 0, "Found no resources"); +}); + +/* Load more data from FS into memory */ +test("glob all", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + const resources = await readerWriter.byGlob("/**/*.*"); + t.is(resources.length, 16, "Found all resources"); +}); + +test("glob all from root", async (t) => { + t.plan(2); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + const resources = await readerWriter.byGlob("/*/*.*"); + matchGlobResult(t, resources, ["/app/package.json"]); +}); + +test("glob all with virtual path included", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob("/app/**/*.*"); + t.is(resources.length, 16, "Found all resources"); +}); + +test("glob a specific filetype (yaml)", async (t) => { + t.plan(2); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob("/**/*.yaml"); + resources.forEach((res) => { + t.is(res.getName(), "ui5.yaml"); + }); +}); + +test("glob two specific filetype (yaml and js)", async (t) => { + t.plan(4); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob("/**/*.{yaml,js}"); + const expectedFiles = [ + "/app/application.b/ui5.yaml", + "/app/application.a/ui5.yaml", + "/app/application.a/webapp/test.js" + ]; + matchGlobResult(t, resources, expectedFiles); +}); + +test("glob a specific filetype (json) with exclude pattern", async (t) => { + t.plan(3); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob([ + "/**/*.json", + "!/**/*package.json" + ]); + const expectedFiles = [ + "/app/application.b/webapp/manifest.json", + "/app/application.b/webapp/embedded/manifest.json" + ]; + matchGlobResult(t, resources, expectedFiles); +}); + +test("glob a specific filetype (json) with multiple exclude pattern", async (t) => { + t.plan(2); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob([ + "/**/*.json", + "!/**/*package.json", + "!/**/embedded/manifest.json" + ]); + matchGlobResult(t, resources, ["/app/application.b/webapp/manifest.json"]); +}); + +test("glob (normalized) root directory (=> fs root)", async (t) => { + t.plan(2); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob([ + "/*", + ], {nodir: false}); + resources.forEach((res) => { + t.is(res.getName(), "app"); + t.is(res.getStatInfo().isDirectory(), true); + }); +}); + +test("glob root directory", async (t) => { + t.plan(2); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob("/app/", {nodir: false}); + matchGlobResult(t, resources, ["/app"]); +}); + +test("glob subdirectory", async (t) => { + t.plan(3); + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter); + + const resources = await readerWriter.byGlob([ + "/app/application.a", + ], {nodir: false}); + + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/app/application.a"); + t.is(resources[0].getStatInfo().isDirectory(), true); +}); + +test("glob all resources above virtual base path (path traversal)", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/", + }); + + const res = await readerWriter.byGlob([ + "/app/../**", + ], {nodir: false}); + t.is(res.length, 2, "Returned resources in virtual base dir only"); +}); + +test("glob virtual directory above virtual base path (path traversal)", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/", + }); + + const res = await readerWriter.byGlob([ + "/*/../../application.b/**", // try to get resources from another project + ], {nodir: false}); + t.is(res.length, 0, "Returned no resources"); +}); + +test("byPath", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/" + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath virtual directory above base path (path traversal)", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/" + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/../package.json", {nodir: true}); + t.is(resource, null, "Found no resource"); +}); + + +function getPathFromResource(resource) { + return resource.getPath(); +} + +test("static excludes: glob library src and test", async (t) => { + const excludes = [ + "/resources/**/some.js", + + // double negation has no effect over following "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + + "/test-resources/**", + ]; + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const testReader = createAdapter({ + virBasePath: "/test-resources/", + excludes + }); + await fillFromFs(testReader, { + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/", + }); + + const srcResources = await srcReader.byGlob("/**/*", {nodir: true}); + const testResources = await testReader.byGlob("/**/*", {nodir: true}); + + t.is(srcResources.length, 1, "Found one src resource"); + t.deepEqual(srcResources.map(getPathFromResource), [ + "/resources/library/l/.library" + ], "Found expected src resources"); + + t.is(testResources.length, 0, "Found no test resources"); +}); + +test("static excludes: glob library src and test with double negation", async (t) => { + const excludes = [ + "/resources/**/some.js", + "/test-resources/**", + + // double negation has effect over preceding "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + ]; + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const testReader = createAdapter({ + virBasePath: "/test-resources/", + excludes + }); + await fillFromFs(testReader, { + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/" + }); + + const srcResources = await srcReader.byGlob("/**/*", {nodir: true}); + const testResources = await testReader.byGlob("/**/*", {nodir: true}); + + t.is(srcResources.length, 1, "Found one src resource"); + t.deepEqual(srcResources.map(getPathFromResource), [ + "/resources/library/l/.library" + ], "Found expected src resources"); + + t.is(testResources.length, 1, "Found one test resource"); + t.deepEqual(testResources.map(getPathFromResource), [ + "/test-resources/library/l/Test2.html" + ], "Found expected test resources"); +}); + +test("static excludes: glob library test with double negation", async (t) => { + const excludes = [ + "/test-resources/**", + + // double negation has effect over preceding "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + ]; + + const testReader = createAdapter({ + virBasePath: "/test-resources/", + excludes + }); + await fillFromFs(testReader, { + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/" + }); + + const testResources = await testReader.byGlob("/**/*", {nodir: true}); + + t.is(testResources.length, 1, "Found one test resource"); + t.deepEqual(testResources.map(getPathFromResource), [ + "/test-resources/library/l/Test2.html" + ], "Found expected test resources"); +}); + +test("static excludes: glob with virtual root exclude", async (t) => { + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes: [ + "/**" + ] + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: true}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("static excludes: glob with negated directory exclude, excluding resources", async (t) => { + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes: [ + "/!({pony,unicorn})/**" + ] + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: true}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("static excludes: glob with negated directory exclude, not excluding resources", async (t) => { + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes: [ + "/!(resources)/**" + ] + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: true}); + + t.is(resources.length, 2, "Found two resources"); +}); + +test("static excludes: byPath exclude everything in sub-directory", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/", + excludes: ["/resources/app/**"] + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.is(resource, null, "Found no resource"); +}); + +test("static excludes: byPath exclude with negation", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/", + excludes: [ + "/resources/app/**", + "!/resources/app/index.html" + ] + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.truthy(resource, "Found one resource"); +}); + +test("static excludes: byPath exclude with unused negation", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/", + excludes: [ + "!/resources/app/index.html", + "/resources/app/**" + ] + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.is(resource, null, "Resource is excluded"); +}); + +test("static excludes: byPath exclude with negated directory pattern, excluding resources", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/", + excludes: [ + "/!({pony,unicorn})/**" + ] + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.is(resource, null, "Found no resource"); +}); + +test("static excludes: byPath exclude with negated directory pattern, not excluding resources", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/", + excludes: [ + "/!(resources)/**" + ] + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + }); + + const resource = await readerWriter.byPath("/resources/app/index.html", {nodir: true}); + t.truthy(resource, "Found one resource"); +}); + +test("byPath: exclude with unused negation", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/resources/app/", + excludes: [ + "!/resources/app/i18n/**", + "/resources/app/**", + "!/resources/app/manifest.json" + ] + }); + await fillFromFs(readerWriter, { + fsBasePath: "./test/fixtures/application.b/webapp", + virBasePath: "/resources/app/", + }); + + const [manifest, i18n, i18ni18n] = await Promise.all([ + readerWriter.byPath("/resources/app/manifest.json", {nodir: true}), + readerWriter.byPath("/resources/app/i18n.properties", {nodir: true}), + readerWriter.byPath("/resources/app/i18n/i18n.properties", {nodir: true}) + ]); + t.truthy(manifest, "Found manifest.json resource"); + t.is(i18n, null, "i18n resource is excluded"); + t.is(i18ni18n, null, "i18n in i18n directory resource is excluded"); +}); + +test("static excludes: glob library src and test with double negation (nodir: false)", async (t) => { + const excludes = [ + "/resources/**/some.js", + "/test-resources/**", + + // double negation has effect over preceding "/test-resources/**" exclude + "!/test-resources/library/l/Test2.html", + ]; + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const testReader = createAdapter({ + virBasePath: "/test-resources/", + excludes + }); + await fillFromFs(testReader, { + fsBasePath: "./test/fixtures/library.l/test", + virBasePath: "/test-resources/", + }); + + const srcResources = await srcReader.byGlob("/**/*", {nodir: false}); + const testResources = await testReader.byGlob("/**/*", {nodir: false}); + + t.is(srcResources.length, 3, "Found one src resource and two directories"); + + t.is(testResources.length, 1, "Found one test resource"); + t.deepEqual(testResources.map(getPathFromResource), [ + "/test-resources/library/l/Test2.html" + ], "Found expected test resources"); +}); + +test("static excludes: glob with virtual root exclude (nodir: false)", async (t) => { + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes: [ + "/**" + ] + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: false}); + + t.is(resources.length, 0, "Found no resources"); +}); +test("static excludes: glob with negated directory exclude, excluding resources (nodir: false)", async (t) => { + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes: [ + "/!({pony,unicorn})/**" + ] + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: false}); + + t.is(resources.length, 0, "Found no resources"); +}); + +test("static excludes: glob with negated directory exclude, not excluding resources (nodir: false)", async (t) => { + const srcReader = createAdapter({ + virBasePath: "/resources/", + excludes: [ + "/!(resources)/**" + ] + }); + await fillFromFs(srcReader, { + fsBasePath: "./test/fixtures/library.l/src", + virBasePath: "/resources/", + }); + + const resources = await srcReader.byGlob("/**/*", {nodir: false}); + + t.is(resources.length, 4, "Found two resources and two directories"); +}); + +test("byPath returns new resource", async (t) => { + const originalResource = createResource({ + path: "/app/index.html", + string: "test" + }); + + const memoryAdapter = createAdapter({virBasePath: "/"}); + + await memoryAdapter.write(originalResource); + + const returnedResource = await memoryAdapter.byPath("/app/index.html"); + + t.deepEqual(returnedResource, originalResource, + "Returned resource should be deep equal to original resource"); + t.not(returnedResource, originalResource, + "Returned resource should not have same reference as original resource"); + + const anotherReturnedResource = await memoryAdapter.byPath("/app/index.html"); + + t.deepEqual(anotherReturnedResource, originalResource, + "Returned resource should be deep equal to original resource"); + t.not(anotherReturnedResource, originalResource, + "Returned resource should not have same reference as original resource"); + + t.not(returnedResource, anotherReturnedResource, + "Both returned resources should not have same reference"); +}); + +test("byGlob returns new resources", async (t) => { + const originalResource = createResource({ + path: "/app/index.html", + string: "test" + }); + + const memoryAdapter = createAdapter({virBasePath: "/"}); + + await memoryAdapter.write(originalResource); + + const [returnedResource] = await memoryAdapter.byGlob("/**"); + + t.deepEqual(returnedResource, originalResource, + "Returned resource should be deep equal to the original resource"); + t.not(returnedResource, originalResource, + "Returned resource should not have same reference as the original resource"); + + const [anotherReturnedResource] = await memoryAdapter.byGlob("/**"); + + t.deepEqual(anotherReturnedResource, originalResource, + "Another returned resource should be deep equal to the original resource"); + t.not(anotherReturnedResource, originalResource, + "Another returned resource should not have same reference as the original resource"); + + t.not(returnedResource, anotherReturnedResource, + "Both returned resources should not have same reference"); +}); diff --git a/packages/fs/test/lib/adapters/Memory_write.js b/packages/fs/test/lib/adapters/Memory_write.js new file mode 100644 index 00000000000..d1cbad8acbe --- /dev/null +++ b/packages/fs/test/lib/adapters/Memory_write.js @@ -0,0 +1,160 @@ +import test from "ava"; +import {createAdapter, createResource} from "../../../lib/resourceFactory.js"; +import sinon from "sinon"; + +test("glob resources from application.a w/ virtual base path prefix", async (t) => { + const dest = createAdapter({ + virBasePath: "/app/" + }); + + const res = createResource({ + path: "/app/index.html" + }); + await dest.write(res); + const resources = await dest.byGlob("/app/*.html"); + t.is(resources.length, 1, "Found exactly one resource"); + t.not(resources[0], res, "Not the same resource instance"); +}); + +test("glob resources from application.a w/o virtual base path prefix", async (t) => { + const dest = createAdapter({ + virBasePath: "/app/" + }); + + const res = createResource({ + path: "/app/index.html" + }); + await dest.write(res); + const resources = await dest.byGlob("/**/*.html"); + t.is(resources.length, 1, "Found exactly one resource"); +}); + +test("Write resource w/ virtual base path", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/" + }); + + const res = createResource({ + path: "/app/test.html" + }); + await readerWriter.write(res); + + t.deepEqual(readerWriter._virFiles, { + "test.html": res + }, "Adapter added resource with correct path"); + + t.deepEqual(Object.keys(readerWriter._virDirs), [], "Adapter added correct virtual directories"); + t.not(readerWriter._virFiles["test.html"], res, "Not the same resource instance"); +}); + +test("Write resource w/o virtual base path", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/" + }); + + const res = createResource({ + path: "/one/two/three/test.html" + }); + await readerWriter.write(res); + + t.deepEqual(readerWriter._virFiles, { + "one/two/three/test.html": res + }, "Adapter added resource with correct path"); + + t.deepEqual(Object.keys(readerWriter._virDirs), [ + "one/two/three", + "one/two", + "one" + ], "Adapter added correct virtual directories"); + + const dirRes = readerWriter._virDirs["one/two/three"]; + t.is(dirRes.getStatInfo().isDirectory(), true, "Directory resource is a directory"); + t.is(dirRes.getPath(), "/one/two/three", "Directory resource has correct path"); +}); + +test("Write resource w/ deep virtual base path", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/a/" + }); + + const res = createResource({ + path: "/app/a/one/two/three/test.html" + }); + await readerWriter.write(res); + + t.deepEqual(readerWriter._virFiles, { + "one/two/three/test.html": res + }, "Adapter added resource with correct path"); + + t.deepEqual(Object.keys(readerWriter._virDirs), [ + "one/two/three", + "one/two", + "one" + ], "Adapter added correct virtual directories"); + + const dirRes = readerWriter._virDirs["one/two/three"]; + t.is(dirRes.getStatInfo().isDirectory(), true, "Directory resource is a directory"); + t.is(dirRes.getPath(), "/app/a/one/two/three", "Directory resource has correct path"); +}); + +test("Write resource w/ crazy virtual base path", async (t) => { + const readerWriter = createAdapter({ + virBasePath: "/app/🐛/" + }); + + const res = createResource({ + path: "/app/🐛/one\\/2/3️⃣/test" + }); + await readerWriter.write(res); + + t.deepEqual(readerWriter._virFiles, { + "one\\/2/3️⃣/test": res + }, "Adapter added resource with correct path"); + + t.deepEqual(Object.keys(readerWriter._virDirs), [ + "one\\/2/3️⃣", + "one\\/2", + "one\\" + ], "Adapter added correct virtual directories"); +}); + +test("Migration of resource is executed", async (t) => { + const writer = createAdapter({ + virBasePath: "/" + }); + + const resource = createResource({ + path: "/test.js" + }); + + const migrateResourceWriterSpy = sinon.spy(writer, "_migrateResource"); + await writer.write(resource); + t.is(migrateResourceWriterSpy.callCount, 1); +}); + +test("Resource: Change instance after write", async (t) => { + const writer = createAdapter({ + virBasePath: "/" + }); + + const resource = createResource({ + path: "/test.js", + string: "MyInitialContent" + }); + + await writer.write(resource); + + resource.setString("MyNewContent"); + + const resource1 = await writer.byPath("/test.js"); + + t.is(await resource.getString(), "MyNewContent"); + t.is(await resource1.getString(), "MyInitialContent"); + + await writer.write(resource); + + const resource2 = await writer.byPath("/test.js"); + t.is(await resource.getString(), "MyNewContent"); + t.is(await resource1.getString(), "MyInitialContent"); + t.is(await resource2.getString(), "MyNewContent"); +}); diff --git a/packages/fs/test/lib/fsInterface.js b/packages/fs/test/lib/fsInterface.js new file mode 100644 index 00000000000..4d00222bec0 --- /dev/null +++ b/packages/fs/test/lib/fsInterface.js @@ -0,0 +1,123 @@ +import test from "ava"; +import {promisify} from "node:util"; +import {Buffer} from "node:buffer"; +import path from "node:path"; +import {fileURLToPath} from "node:url"; +import fsSync from "node:fs"; +const stat = promisify(fsSync.stat); +import {readFile} from "node:fs/promises"; +import fsInterface from "../../lib/fsInterface.js"; +import MemAdapter from "../../lib/adapters/Memory.js"; +import FsAdapter from "../../lib/adapters/FileSystem.js"; +import Resource from "../../lib/Resource.js"; + +const assertReadFile = async (t, readFile, basepath, filepath, content) => { + content = content || "content of " + filepath; + const fullpath = path.join(basepath, filepath); + let buffer = await readFile(fullpath); + t.true(Buffer.isBuffer(buffer)); + t.deepEqual(buffer.toString(), content); + + buffer = await readFile(fullpath, {}); + t.true(Buffer.isBuffer(buffer)); + t.deepEqual(buffer.toString(), content); + buffer = await readFile(fullpath, {encoding: null}); + t.true(Buffer.isBuffer(buffer)); + t.deepEqual(buffer.toString(), content); + buffer = await readFile(fullpath, "utf8"); + t.is(typeof buffer, "string"); + t.deepEqual(buffer, content); + + buffer = await readFile(fullpath, {encoding: "utf8"}); + t.is(typeof buffer, "string"); + t.deepEqual(content, content); +}; + +function getPath() { + return fileURLToPath(new URL("../fixtures/fsInterfáce", import.meta.url)); +} + +test("MemAdapter: readFile", async (t) => { + const memAdapter = new MemAdapter({ + virBasePath: "/" + }); + const fs = fsInterface(memAdapter); + const readFile = promisify(fs.readFile); + + const fsPath = path.join("/", "foo.txt"); + await memAdapter.write(new Resource({ + path: "/foo.txt", + string: `content of ${fsPath}` + })); + `content of ${fsPath}`; + await assertReadFile(t, readFile, "", fsPath); +}); + +test("FsAdapter: readFile with non-ASCII characters in path", async (t) => { + const fsAdapter = new FsAdapter({ + virBasePath: "/", + fsBasePath: getPath() + }); + const fs = fsInterface(fsAdapter); + const readFile = promisify(fs.readFile); + + await assertReadFile(t, readFile, "", path.join("/", "bâr.txt"), "content"); +}); + +test("fs: readFile", async (t) => { + await assertReadFile(t, readFile, + getPath(), path.join("/", "foo.txt"), "content"); +}); + +const assertStat = async (t, stat, basepath, filepath) => { + const fullpath = path.join(basepath, filepath); + const stats = await stat(fullpath); + + t.is(stats.isFile(), true); + t.is(stats.isDirectory(), false); + t.is(stats.isBlockDevice(), false); + t.is(stats.isCharacterDevice(), false); + t.is(stats.isSymbolicLink(), false); + t.is(stats.isFIFO(), false); + t.is(stats.isSocket(), false); +}; + +test("MemAdapter: stat", async (t) => { + const memAdapter = new MemAdapter({ + virBasePath: "/" + }); + const fs = fsInterface(memAdapter); + const stat = promisify(fs.stat); + + const fsPath = path.join("/", "foo.txt"); + await memAdapter.write(new Resource({ + path: "/foo.txt", + string: `content of ${fsPath}` + })); + await assertStat(t, stat, "", fsPath); +}); + +test("FsAdapter: stat", async (t) => { + const fsAdapter = new FsAdapter({ + virBasePath: "/", + fsBasePath: getPath() + }); + const fs = fsInterface(fsAdapter); + const stat = promisify(fs.stat); + + await assertStat(t, stat, "", path.join("/", "foo.txt")); +}); + +test("fs: stat", async (t) => { + await assertStat(t, stat, getPath(), path.join("/", "foo.txt")); +}); + +test("MemAdapter: mkdir", async (t) => { + const memAdapter = new MemAdapter({ + virBasePath: "/" + }); + const fs = fsInterface(memAdapter); + const mkdir = promisify(fs.mkdir); + + await t.notThrowsAsync(mkdir("pony"), "mkdir executes successfully"); +}); diff --git a/packages/fs/test/lib/glob.js b/packages/fs/test/lib/glob.js new file mode 100644 index 00000000000..4e8c1b61c9a --- /dev/null +++ b/packages/fs/test/lib/glob.js @@ -0,0 +1,406 @@ +import test from "ava"; +import FsAdapter from "../../lib/adapters/FileSystem.js"; + +// Create readerWriter before running tests +test.beforeEach((t) => { + t.context.readerWriter = { + filesystem: new FsAdapter({ + fsBasePath: "./test/fixtures/glob", + virBasePath: "/test-resources/" + }) + }; +}); + +function matchGlobResult(t, resources, expectedResources) { + t.deepEqual(resources.length, expectedResources.length, "Amount of files matches expected result."); + + const matchedResources = resources.map((resource) => { + return resource.getPath(); + }); + + for (let i = 0; i < expectedResources.length; i++) { + const expectedResource = expectedResources[i]; + t.true( + matchedResources.indexOf(expectedResource) !== -1, + "File '" + expectedResource + "' was found." + ); + } +} + +// From FileSystem +test("glob all", async (t) => { + const resources = await t.context.readerWriter.filesystem.byGlob("/**/*.*"); + + t.is(resources.length, 16, "Found all resources"); +}); + +test("glob all from root only", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob("/*/*.*"); + + matchGlobResult(t, resources, ["/test-resources/package.json"]); +}); + +test("glob all with virtual base path fully matching", async (t) => { + t.plan(1); + + const resources = await t.context.readerWriter.filesystem.byGlob("/test-resources/**/*.*"); + + t.is(resources.length, 16, "Found all resources"); +}); + +test("glob with virtual base path partially matching", async (t) => { + t.plan(1); + + const adapter = new FsAdapter({ + fsBasePath: "./test/fixtures/glob/application.a", + virBasePath: "/test-resources/application/a/" + }); + + const resources = await adapter.byGlob("/test-resources/**/*.*"); + + t.is(resources.length, 4, "Found all resources"); +}); + +test("Check for unstable order of glob result", async (t) => { + t.plan(1); + + const resources = await t.context.readerWriter.filesystem.byGlob("/**/*.*"); + + const firstMatch = resources.map((resource) => { + return resource.getPath(); + }); + + async function readNext(tries) { + const resources = await t.context.readerWriter.filesystem.byGlob("/**/*.*"); + const nextMatch = resources.map((resource) => { + return resource.getPath(); + }); + let foundDifference = false; + for (let i = 0; i < firstMatch.length; i++) { + if (firstMatch[i] !== nextMatch[i]) { + foundDifference = true; + } + } + + if (!foundDifference && tries > 0) { + return readNext(--tries); + } else { + return nextMatch; + } + } + + const nextMatch = await readNext(100); + + t.notDeepEqual(nextMatch, firstMatch, "Result sets are of unstable order"); +}); + +test("glob with multiple patterns", async (t) => { + t.plan(5); + + const resources = + await t.context.readerWriter.filesystem.byGlob(["/**/*.yaml", "/test-resources/**/i18n_de.properties"]); + + const expectedResources = [ + "/test-resources/application.b/webapp/i18n/i18n_de.properties", + "/test-resources/application.b/webapp/embedded/i18n/i18n_de.properties", + "/test-resources/application.b/ui5.yaml", + "/test-resources/application.a/ui5.yaml" + ]; + + matchGlobResult(t, resources, expectedResources); +}); + +test("glob only a specific filetype (yaml)", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob("/**/*.yaml"); + + resources.forEach((res) => { + t.is(res.getName(), "ui5.yaml"); + }); +}); + +test("glob two specific filetype (yaml and js)", async (t) => { + t.plan(4); + + const resources = await t.context.readerWriter.filesystem.byGlob("/**/*.{yaml,js}"); + + const expectedResources = [ + "/test-resources/application.a/webapp/test.js", + "/test-resources/application.b/ui5.yaml", + "/test-resources/application.a/ui5.yaml" + ]; + + matchGlobResult(t, resources, expectedResources); +}); + +test("glob only a specific filetype (json) with exclude pattern", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob([ + "/**/*.json", + "!/**/*package.json" + ]); + + resources.forEach((res) => { + t.is(res.getName(), "manifest.json"); + }); +}); + +test("glob only a specific filetype (json) with multiple exclude pattern", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob([ + "/**/*.json", + "!/**/*package.json", + "!/**/embedded/manifest.json" + ]); + + matchGlobResult(t, resources, ["/test-resources/application.b/webapp/manifest.json"]); +}); + +test("glob (normalized) root directory (=> fs root)", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob([ + "/*/", + ], {nodir: false}); + + resources.forEach((res) => { + t.is(res.getName(), "test-resources"); + t.is(res.getStatInfo().isDirectory(), true); + }); +}); + +test("glob root directory", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob("/test-resources/", {nodir: false}); + + matchGlobResult(t, resources, ["/test-resources"]); +}); + +test("glob subdirectory", async (t) => { + t.plan(2); + + const resources = await t.context.readerWriter.filesystem.byGlob([ + "/test-resources/app*a", + ], {nodir: false}); + + resources.forEach((res) => { + t.is(res.getName(), "application.a"); + t.is(res.getStatInfo().isDirectory(), true); + }); +}); + +test("glob with multiple patterns with static exclude", async (t) => { + t.plan(4); + + const resources = await new FsAdapter({ + fsBasePath: "./test/fixtures/glob", + virBasePath: "/test-resources/", + excludes: [ + "/test-resources/application.b/**", + "!/test-resources/application.b/**/manifest.json" + ] + }).byGlob(["/**/*.yaml", "/test-resources/**/i18n_de.properties"]); + + const expectedResources = [ + "/test-resources/application.a/ui5.yaml", + "/test-resources/application.b/webapp/manifest.json", + "/test-resources/application.b/webapp/embedded/manifest.json" + ]; + matchGlobResult(t, resources, expectedResources); +}); + +/* + Generic Micromatch tests for understanding glob exclude behavior. + Not active since they only test external code and our adapter-tests should + already test for any incompatible changes. +*/ +// test("micromatch exclude test", async (t) => { +// const micromatch = require("micromatch"); + +// const excludePatterns = [ +// "!/resources/app/fileA", +// "/resources/app/**", +// ]; + +// const fileAPath = "/resources/app/fileA"; +// const fileBPath = "/resources/app/fileB"; + +// let matches = micromatch(fileAPath, excludePatterns); +// t.deepEqual(matches.length, 1, "File A is excluded"); + +// matches = micromatch(fileBPath, excludePatterns); +// t.deepEqual(matches.length, 1, "File B is included"); +// }); + +// test("micromatch exclude test 2", async (t) => { +// const micromatch = require("micromatch"); + +// const excludePatterns = [ +// "/resources/app/**", +// "!/resources/app/fileA", +// ]; + +// const fileAPath = "/resources/app/fileA"; +// const fileBPath = "/resources/app/fileB"; + +// let matches = micromatch(fileAPath, excludePatterns); +// t.deepEqual(matches.length, 0, "File A is excluded"); + +// matches = micromatch(fileBPath, excludePatterns); +// t.deepEqual(matches.length, 1, "File B is included"); +// }); + +// test("micromatch exclude test 3", async (t) => { +// const micromatch = require("micromatch"); + +// const excludePatterns = [ +// "!/resources/app/i18n/**", +// "/resources/app/**", +// "!/resources/app/manifest.json" +// ]; + +// const paths = [ +// "/resources/app/manifest.json", +// "/resources/app/i18n.properties", +// "/resources/app/i18n/i18n.properties" +// ]; + +// const matches = micromatch(paths, excludePatterns); +// t.deepEqual(matches, [ +// "/resources/app/i18n.properties", +// "/resources/app/i18n/i18n.properties" +// ], "Top level i18n.properties file is excluded"); +// }); + +// test("micromatch exclude test 4", async (t) => { +// const micromatch = require("micromatch"); + +// const excludePatterns = [ +// "/!(pony)/**", +// ]; + +// const paths = [ +// "/resources/app/manifest.json", +// "/resources/app/i18n.properties", +// "/resources/app/i18n/i18n.properties" +// ]; + +// const matches = micromatch(paths, excludePatterns); +// t.deepEqual(matches, paths, "All resources should match"); +// }); + +// test("micromatch exclude test 5", async (t) => { +// const micromatch = require("micromatch"); + +// const excludePatterns = [ +// "/!(resources)/**", +// ]; + +// const paths = [ +// "/resources/app/manifest.json", +// "/resources/app/i18n.properties", +// "/resources/app/i18n/i18n.properties" +// ]; + +// const matches = micromatch(paths, excludePatterns); +// t.deepEqual(matches, [], "No resources should match"); +// }); + +// test("micromatch glob test 1", async (t) => { +// const micromatch = require("micromatch"); + +// const patterns = [ +// "**/*", +// "!**", +// "library/l/Test2.html", +// ]; + +// const paths = [ +// "library/l/Test.html", +// "library/l/Test2.html" +// ]; + +// const matches = micromatch(paths, patterns); +// t.deepEqual(matches, [ +// "library/l/Test2.html" +// ], "Resources should match"); +// }); + +// test("micromatch glob test 2", async (t) => { +// const micromatch = require("micromatch"); + +// const patterns = [ +// "!**/*", +// "!!**", +// "!library/l/Test2.html", +// ]; + +// const paths = [ +// "library/l/Test.html", +// "library/l/Test2.html" +// ]; + +// const matches = micromatch(paths, patterns); +// t.deepEqual(matches, [ +// "library/l/Test.html" +// ], "Resources should match"); +// }); + +// test("micromatch glob test 3", async (t) => { +// const micromatch = require("micromatch"); + +// const patterns = [ +// "**/*", +// "!library/l/Test2.html", +// "library/l/Test2.html", +// ]; + +// const paths = [ +// "library/l/Test.html", +// "library/l/Test2.html" +// ]; + +// const matches = micromatch(paths, patterns); +// t.deepEqual(matches, [ +// "library/l/Test.html", +// "library/l/Test2.html" +// ], "Resources should match"); +// }); + +// test("micromatch glob test 4", async (t) => { +// const micromatch = require("micromatch"); + +// const patterns = [ +// "" +// ]; + +// const paths = [ +// "library/l/Test.html", +// "library/l/Test2.html" +// ]; + +// const err = t.throws(() => { +// micromatch(paths, patterns); +// }); +// t.deepEqual(err.message, "Expected pattern to be a non-empty string", "Micromatch throws with correct exception"); +// }); + +// test("globby test 1", async (t) => { +// const glob = require("globby"); + +// const opt = { +// cwd: "./test/fixtures/glob", +// dot: true, +// onlyFiles: false +// }; +// // Globby in version 9 somehow transforms "" to "**" while micromatch throws an exception on empty strings +// const matches = await glob([""], opt); +// t.deepEqual(matches.length, 23, "Resources should match"); +// }); + diff --git a/packages/fs/test/lib/package-exports.js b/packages/fs/test/lib/package-exports.js new file mode 100644 index 00000000000..13201c3d901 --- /dev/null +++ b/packages/fs/test/lib/package-exports.js @@ -0,0 +1,83 @@ +import test from "ava"; +import {createRequire} from "node:module"; + +// Using CommonsJS require since JSON module imports are still experimental +const require = createRequire(import.meta.url); + +// package.json should be exported to allow reading version (e.g. from @ui5/cli) +test("export of package.json", (t) => { + t.truthy(require("@ui5/fs/package.json").version); +}); + +// Check number of definied exports +test("check number of exports", (t) => { + const packageJson = require("@ui5/fs/package.json"); + t.is(Object.keys(packageJson.exports).length, 12); +}); + +// Public API contract (exported modules) +[ + { + exportedSpecifier: "@ui5/fs/adapters/AbstractAdapter", + mappedModule: "../../lib/adapters/AbstractAdapter.js" + }, + { + exportedSpecifier: "@ui5/fs/adapters/FileSystem", + mappedModule: "../../lib/adapters/FileSystem.js" + }, + { + exportedSpecifier: "@ui5/fs/adapters/Memory", + mappedModule: "../../lib/adapters/Memory.js" + }, + { + exportedSpecifier: "@ui5/fs/AbstractReader", + mappedModule: "../../lib/AbstractReader.js" + }, + { + exportedSpecifier: "@ui5/fs/AbstractReaderWriter", + mappedModule: "../../lib/AbstractReaderWriter.js" + }, + { + exportedSpecifier: "@ui5/fs/DuplexCollection", + mappedModule: "../../lib/DuplexCollection.js" + }, + { + exportedSpecifier: "@ui5/fs/fsInterface", + mappedModule: "../../lib/fsInterface.js" + }, + { + exportedSpecifier: "@ui5/fs/ReaderCollection", + mappedModule: "../../lib/ReaderCollection.js" + }, + { + exportedSpecifier: "@ui5/fs/ReaderCollectionPrioritized", + mappedModule: "../../lib/ReaderCollectionPrioritized.js" + }, + { + exportedSpecifier: "@ui5/fs/readers/Filter", + mappedModule: "../../lib/readers/Filter.js" + }, + { + exportedSpecifier: "@ui5/fs/readers/Link", + mappedModule: "../../lib/readers/Link.js" + }, + { + exportedSpecifier: "@ui5/fs/Resource", + mappedModule: "../../lib/Resource.js" + }, + { + exportedSpecifier: "@ui5/fs/resourceFactory", + mappedModule: "../../lib/resourceFactory.js" + }, + // Internal modules (only to be used by @ui5/* packages) + { + exportedSpecifier: "@ui5/fs/internal/ResourceTagCollection", + mappedModule: "../../lib/ResourceTagCollection.js" + }, +].forEach(({exportedSpecifier, mappedModule}) => { + test(`${exportedSpecifier}`, async (t) => { + const actual = await import(exportedSpecifier); + const expected = await import(mappedModule); + t.is(actual, expected, "Correct module exported"); + }); +}); diff --git a/packages/fs/test/lib/readers/Filter.js b/packages/fs/test/lib/readers/Filter.js new file mode 100644 index 00000000000..c48264b4220 --- /dev/null +++ b/packages/fs/test/lib/readers/Filter.js @@ -0,0 +1,66 @@ +import test from "ava"; +import sinon from "sinon"; +import Filter from "../../../lib/readers/Filter.js"; + +test("_byGlob: Basic filter", async (t) => { + const abstractReader = { + _byGlob: sinon.stub().returns(Promise.resolve(["resource a", "resource b"])) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Filter({ + reader: abstractReader, + callback: function(resource) { + if (resource === "resource a") { + return false; + } + return true; + } + }); + + const resources = await readerCollection._byGlob("anyPattern", {}, trace); + t.deepEqual(resources, ["resource b"], "Correct resource in result"); +}); + +test("_byPath: Negative filter", async (t) => { + const abstractReader = { + _byPath: sinon.stub().returns(Promise.resolve("resource a")) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Filter({ + reader: abstractReader, + callback: function(resource) { + if (resource === "resource a") { + return false; + } + return true; + } + }); + + const resource = await readerCollection._byPath("anyPattern", {}, trace); + t.is(resource, null, "Correct empty result"); +}); + +test("_byPath: Positive filter", async (t) => { + const abstractReader = { + _byPath: sinon.stub().returns(Promise.resolve("resource b")) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Filter({ + reader: abstractReader, + callback: function(resource) { + if (resource === "resource a") { + return false; + } + return true; + } + }); + + const resource = await readerCollection._byPath("anyPattern", {}, trace); + t.is(resource, "resource b", "Correct resource in result"); +}); diff --git a/packages/fs/test/lib/readers/Link.js b/packages/fs/test/lib/readers/Link.js new file mode 100644 index 00000000000..01abc5d68ad --- /dev/null +++ b/packages/fs/test/lib/readers/Link.js @@ -0,0 +1,314 @@ +import test from "ava"; +import sinon from "sinon"; +import Link from "../../../lib/readers/Link.js"; +import ResourceFacade from "../../../lib/ResourceFacade.js"; + +test("_byGlob: Basic Link", async (t) => { + const dummyResourceA = { + getPath: () => "/resources/some/lib/FileA.js" + }; + const dummyResourceB = { + getPath: () => "/resources/some/lib/dir/FileB.js" + }; + const abstractReader = { + _byGlob: sinon.stub().resolves([dummyResourceA, dummyResourceB]) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/`, + targetPath: `/resources/some/lib/` + } + }); + const options = "options"; + const resources = await readerCollection._byGlob("anyPattern", options, trace); + t.is(resources.length, 2, "Glob returned two resources"); + + t.true(resources[0] instanceof ResourceFacade, "First resource is an instance of ResourceFacade"); + t.is(resources[0].getConcealedResource(), dummyResourceA, "First resource contains dummy resource A"); + t.is(resources[0].getPath(), "/FileA.js", "First resource has correct rewritten path"); + + t.true(resources[1] instanceof ResourceFacade, "Second resource is an instance of ResourceFacade"); + t.is(resources[1].getConcealedResource(), dummyResourceB, "Second resource contains dummy resource B"); + t.is(resources[1].getPath(), "/dir/FileB.js", "Second resource has correct rewritten path"); + + t.is(abstractReader._byGlob.callCount, 1, "Mocked _byGlob got called once"); + t.deepEqual(abstractReader._byGlob.getCall(0).args[0], ["/resources/some/lib/anyPattern"], + "Mocked _byGlob got called with expected patterns"); + t.is(abstractReader._byGlob.getCall(0).args[1], options, + "Mocked _byGlob got called with expected options"); + t.is(abstractReader._byGlob.getCall(0).args[2], trace, + "Mocked _byGlob got called with expected trace object"); +}); + +test("_byGlob: Complex pattern", async (t) => { + const abstractReader = { + _byGlob: sinon.stub().resolves([]) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/`, + targetPath: `/resources/some/lib/` + } + }); + + const options = "options"; + const resources = await readerCollection._byGlob("{anyPattern,otherPattern}/**", options, trace); + t.is(resources.length, 0, "Glob returned no resources"); + + t.is(abstractReader._byGlob.callCount, 1, "Mocked _byGlob got called once"); + t.deepEqual(abstractReader._byGlob.getCall(0).args[0], [ + "/resources/some/lib/anyPattern/**", + "/resources/some/lib/otherPattern/**", + ], "Mocked _byGlob got called with expected patterns"); + t.is(abstractReader._byGlob.getCall(0).args[1], options, + "Mocked _byGlob got called with expected options"); + t.is(abstractReader._byGlob.getCall(0).args[2], trace, + "Mocked _byGlob got called with expected trace object"); +}); + +test("_byGlob: Request prefixed with target path", async (t) => { + const abstractReader = { + _byGlob: sinon.stub().resolves([]) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/my/lib/`, + targetPath: `/some/lib/` + } + }); + const options = "options"; + const resources = await readerCollection._byGlob("/some/lib/dir/**", options, trace); + t.is(resources.length, 0, "Glob returned no resources"); + + t.is(abstractReader._byGlob.callCount, 1, "Mocked _byGlob got called once"); + t.deepEqual(abstractReader._byGlob.getCall(0).args[0], [ + "/some/lib/some/lib/dir/**", // Weird, but expected. We prefix everything with the targetPath + ], "Mocked _byGlob got called with expected patterns"); + t.is(abstractReader._byGlob.getCall(0).args[1], options, + "Mocked _byGlob got called with expected options"); + t.is(abstractReader._byGlob.getCall(0).args[2], trace, + "Mocked _byGlob got called with expected trace object"); +}); + +test("_byGlob: Request prefixed with link path", async (t) => { + const abstractReader = { + _byGlob: sinon.stub().resolves([]) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/my/lib/`, + targetPath: `/some/lib/` + } + }); + const options = "options"; + const resources = await readerCollection._byGlob("/my/lib/dir/**", options, trace); + t.is(resources.length, 0, "Glob returned no resources"); + + t.is(abstractReader._byGlob.callCount, 1, "Mocked _byGlob got called once"); + t.deepEqual(abstractReader._byGlob.getCall(0).args[0], [ + "/some/lib/dir/**", + ], "Mocked _byGlob got called with expected patterns"); + t.is(abstractReader._byGlob.getCall(0).args[1], options, + "Mocked _byGlob got called with expected options"); + t.is(abstractReader._byGlob.getCall(0).args[2], trace, + "Mocked _byGlob got called with expected trace object"); +}); + +test("_byPath: Basic Link", async (t) => { + const dummyResource = { + getPath: () => "/resources/some/lib/dir/File.js" + }; + const abstractReader = { + _byPath: sinon.stub().resolves(dummyResource) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/`, + targetPath: `/resources/some/lib/` + } + }); + const options = "options"; + const resource = await readerCollection._byPath("/dir/File.js", options, trace); + + t.true(resource instanceof ResourceFacade, "First resource is an instance of ResourceFacade"); + t.is(resource.getConcealedResource(), dummyResource, "First resource contains dummy resource A"); + t.is(resource.getPath(), "/dir/File.js", "First resource has correct rewritten path"); + + t.is(abstractReader._byPath.callCount, 1, "Mocked _byPath got called once"); + t.is(abstractReader._byPath.getCall(0).args[0], "/resources/some/lib/dir/File.js", + "Mocked _byPath got called with expected patterns"); + t.is(abstractReader._byPath.getCall(0).args[1], options, + "Mocked _byPath got called with expected options"); + t.is(abstractReader._byPath.getCall(0).args[2], trace, + "Mocked _byPath got called with expected trace object"); +}); + +test("_byPath: Rewrite on same level", async (t) => { + const dummyResource = { + getPath: () => "/some/lib/dir/File.js" + }; + const abstractReader = { + _byPath: sinon.stub().resolves(dummyResource) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/my/lib/`, + targetPath: `/some/lib/` + } + }); + const options = "options"; + const resource = await readerCollection._byPath("/my/lib/dir/File.js", options, trace); + + t.true(resource instanceof ResourceFacade, "First resource is an instance of ResourceFacade"); + t.is(resource.getConcealedResource(), dummyResource, "First resource contains dummy resource A"); + t.is(resource.getPath(), "/my/lib/dir/File.js", "First resource has correct rewritten path"); + + t.is(abstractReader._byPath.callCount, 1, "Mocked _byPath got called once"); + t.is(abstractReader._byPath.getCall(0).args[0], "/some/lib/dir/File.js", + "Mocked _byPath got called with expected patterns"); + t.is(abstractReader._byPath.getCall(0).args[1], options, + "Mocked _byPath got called with expected options"); + t.is(abstractReader._byPath.getCall(0).args[2], trace, + "Mocked _byPath got called with expected trace object"); +}); + +test("_byPath: No resource found", async (t) => { + const abstractReader = { + _byPath: sinon.stub().resolves(null) + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/`, + targetPath: `/some/lib/` + } + }); + const options = "options"; + const resource = await readerCollection._byPath("/dir/File.js", options, trace); + + t.is(resource, null, "No resource returned"); + + t.is(abstractReader._byPath.callCount, 1, "Mocked _byPath got called once"); + t.is(abstractReader._byPath.getCall(0).args[0], "/some/lib/dir/File.js", + "Mocked _byPath got called with expected patterns"); + t.is(abstractReader._byPath.getCall(0).args[1], options, + "Mocked _byPath got called with expected options"); + t.is(abstractReader._byPath.getCall(0).args[2], trace, + "Mocked _byPath got called with expected trace object"); +}); + +test("_byPath: Request different prefix", async (t) => { + const abstractReader = { + _byPath: sinon.stub() + }; + const trace = { + collection: sinon.spy() + }; + const readerCollection = new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/my/lib/`, + targetPath: `/some/lib/` + } + }); + const options = "options"; + const resource = await readerCollection._byPath("/some/lib/dir/File.js", options, trace); + + t.is(resource, null, "No resource returned"); + t.is(abstractReader._byPath.callCount, 0, "Mocked _byPath never got called"); +}); + +test("Missing attributes", (t) => { + const abstractReader = {}; + let err = t.throws(() => { + new Link({ + pathMapping: { + linkPath: `/`, + targetPath: `/`, + } + }); + }); + t.is(err.message, `Missing parameter "reader"`, + "Threw with expected error message"); + + err = t.throws(() => { + new Link({ + reader: abstractReader + }); + }); + t.is(err.message, `Missing parameter "pathMapping"`, + "Threw with expected error message"); + + err = t.throws(() => { + new Link({ + reader: abstractReader, + pathMapping: { + targetPath: `/`, + } + }); + }); + t.is(err.message, `Path mapping is missing attribute "linkPath"`, + "Threw with expected error message"); + + err = t.throws(() => { + new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/`, + } + }); + }); + t.is(err.message, `Path mapping is missing attribute "targetPath"`, + "Threw with expected error message"); + + err = t.throws(() => { + new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/path`, + targetPath: `/`, + } + }); + }); + t.is(err.message, `Link path must end with a slash: /path`, + "Threw with expected error message"); + + err = t.throws(() => { + new Link({ + reader: abstractReader, + pathMapping: { + linkPath: `/`, + targetPath: `/path`, + } + }); + }); + t.is(err.message, `Target path must end with a slash: /path`, + "Threw with expected error message"); +}); diff --git a/packages/fs/test/lib/resourceFactory.js b/packages/fs/test/lib/resourceFactory.js new file mode 100644 index 00000000000..c535ff90440 --- /dev/null +++ b/packages/fs/test/lib/resourceFactory.js @@ -0,0 +1,352 @@ +import test from "ava"; +import { + createAdapter, createReader, createReaderCollection, createReaderCollectionPrioritized, + createResource, createWriterCollection, createWorkspace, prefixGlobPattern} from "../../lib/resourceFactory.js"; +import FileSystem from "../../lib/adapters/FileSystem.js"; +import Memory from "../../lib/adapters/Memory.js"; +import ReaderCollection from "../../lib/ReaderCollection.js"; +import {setLogLevel} from "@ui5/logger"; + +// Set log level to silly to activate tracing +setLogLevel("silly"); + +test("prefixGlobPattern", (t) => { + t.deepEqual( + prefixGlobPattern("{/sub-directory-1/,/sub-directory-2/}**", "/pony/path/a"), + [ + "/pony/path/a/sub-directory-1/**", + "/pony/path/a/sub-directory-2/**" + ], + "GLOBs correctly prefixed"); + + t.deepEqual( + prefixGlobPattern("/pony-path/**", "/pony/path/a"), + ["/pony/path/a/pony-path/**"], + "GLOBs correctly prefixed"); + + t.deepEqual( + prefixGlobPattern("!/duck*path/**", "/pony/path/a"), + ["!/pony/path/a/duck*path/**"], + "GLOBs correctly prefixed"); + + t.deepEqual( + prefixGlobPattern("!**.json", "/pony/path/a"), + ["!/pony/path/a/**.json"], + "GLOBs correctly prefixed"); + + t.deepEqual( + prefixGlobPattern("!**.json", "/pony/path/a/"), // trailing slash + ["!/pony/path/a/**.json"], + "GLOBs correctly prefixed"); + + t.deepEqual( + prefixGlobPattern("pony-path/**", "/pony/path/a/"), // trailing slash + ["/pony/path/a/pony-path/**"], + "GLOBs correctly prefixed"); +}); + +test("createAdapter: FS Adapter", async (t) => { + const adapter = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + }, + excludes: ["**/*.html"] + }); + + t.true(adapter instanceof FileSystem, "Returned a FileSystem adapter"); + + const resources = await adapter.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/app/test.js", "Found correct resource"); +}); + +test("createAdapter: Memory", async (t) => { + const adapter = createAdapter({ + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + }, + excludes: ["**/*.html"] + }); + + t.true(adapter instanceof Memory, "Returned a Memory adapter"); + + const resource1 = createResource({ + path: "/resources/app/File.js" + }); + await adapter.write(resource1); + + const resource2 = createResource({ + path: "/resources/app/index.html" + }); + await adapter.write(resource2); + + const resources = await adapter.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/app/File.js", "Found correct resource"); +}); + +test("createReader: application project", async (t) => { + const reader = createReader({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + project: { + getName: () => "my.project", + getType: () => "application" + }, + excludes: [ + "**/*.html", + "/resources/app/test/**", + "/test/**", + "test/**", + "!/resources/app/test/**", + "!/test/**/*.html" + ], + name: "reader name" + }); + + t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection"); + t.is(reader._name, "reader name", "Reader has correct name"); + + const resources = await reader.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/app/test.js", "Found correct resource"); + + t.deepEqual(reader._readers[0]._excludes, [ + "/resources/app/**/*.html", + "/resources/app/test/**", // Was already prefixed correctly + "/resources/app/test/**", + "/resources/app/test/**", + "!/resources/app/test/**", + "!/resources/app/test/**/*.html", + ], "Some excludes got prefixed."); +}); + +test("createReader: library project", async (t) => { + const reader = createReader({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/lib/", + project: { + getName: () => "my.project", + getType: () => "library" + }, + excludes: [ + "**/*.html", + "/resources/lib/dir/**", + "/test-resources/lib/dir/**", + "/test/**", + "test/**" + ], + name: "reader name" + }); + + t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection"); + t.is(reader._name, "reader name", "Reader has correct name"); + + const resources = await reader.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/lib/test.js", "Found correct resource"); + + t.deepEqual(reader._readers[0]._excludes, [ + "**/*.html", + "/resources/lib/dir/**", + "/test-resources/lib/dir/**", + "/test/**", + "test/**", + ], "Excludes do not get prefixed."); +}); + +test("createReader: No project", async (t) => { + const reader = createReader({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + excludes: [ + "**/*.html", + "/resources/app/dir/**", + "/test-resources/app/dir/**", + "/test/**", + "test/**" + ], + name: "reader name" + }); + + t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection"); + t.is(reader._name, "reader name", "Reader has correct name"); + + const resources = await reader.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/app/test.js", "Found correct resource"); + + t.deepEqual(reader._readers[0]._excludes, [ + "**/*.html", + "/resources/app/dir/**", + "/test-resources/app/dir/**", + "/test/**", + "test/**" + ], "Excludes do not get prefixed."); +}); + +test("createReader: Throw error missing 'fsBasePath'", (t) => { + const error = t.throws(() => createReader({ + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + }, + excludes: ["**/*.html"], + name: "reader name" + })); + t.is(error.message, "Unable to create reader: Missing parameter \"fsBasePath\""); +}); + +test("createReaderCollection", async (t) => { + const adapter = createAdapter({ + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + }, + excludes: ["**/*.html"] + }); + const resource1 = createResource({ + path: "/resources/app/File.js" + }); + const resource2 = createResource({ + path: "/resources/app/index.html" + }); + await adapter.write(resource1); + await adapter.write(resource2); + + const reader = createReaderCollection({ + name: "reader name", + readers: [adapter] + }); + t.true(reader instanceof ReaderCollection, "Returned a ReaderCollection"); + t.is(reader._name, "reader name", "Reader has correct name"); + + const resources = await adapter.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/app/File.js", "Found correct resource"); +}); + +test("createReaderCollectionPrioritized", async (t) => { + const {default: ReaderCollectionPrioritized} = await import("../../lib/ReaderCollectionPrioritized.js"); + const adapter = createAdapter({ + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + }, + excludes: ["**/*.html"] + }); + const resource1 = createResource({ + path: "/resources/app/File.js" + }); + const resource2 = createResource({ + path: "/resources/app/index.html" + }); + await adapter.write(resource1); + await adapter.write(resource2); + + const reader = createReaderCollectionPrioritized({ + name: "reader name", + readers: [adapter] + }); + t.true(reader instanceof ReaderCollectionPrioritized, "Returned a ReaderCollection"); + t.is(reader._name, "reader name", "Reader has correct name"); + + const resources = await adapter.byGlob("**/*"); + t.is(resources.length, 1, "Found one resource"); + t.is(resources[0].getPath(), "/resources/app/File.js", "Found correct resource"); +}); + +test("createWriterCollection", async (t) => { + const {default: WriterCollection} = await import("../../lib/WriterCollection.js"); + const adapter1 = createAdapter({ + virBasePath: "/", + project: { + getName: () => "my.project" + } + }); + const adapter2 = createAdapter({ + virBasePath: "/", + project: { + getName: () => "my.other.project" + } + }); + const resource1 = createResource({ + path: "/resources/app/File.js" + }); + const resource2 = createResource({ + path: "/resources/app2/index.html" + }); + + const writerCollection = createWriterCollection({ + name: "writer collection name", + writerMapping: { + "/resources/app/": adapter1, + "/resources/app2/": adapter2 + } + }); + t.true(writerCollection instanceof WriterCollection, "Returned a ReaderCollection"); + await writerCollection.write(resource1); + await writerCollection.write(resource2); + + const resources1 = await adapter1.byGlob("**/*"); + t.is(resources1.length, 1, "Found one resource"); + t.is(resources1[0].getPath(), "/resources/app/File.js", "Found correct resource"); + const resources2 = await adapter2.byGlob("**/*"); + t.is(resources2.length, 1, "Found one resource"); + t.is(resources2[0].getPath(), "/resources/app2/index.html", "Found correct resource"); +}); + +test("createWorkspace", async (t) => { + const {default: DuplexCollection} = await import("../../lib/DuplexCollection.js"); + const reader = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + } + }); + const readerWriter = createAdapter({ + virBasePath: "/", + project: { + getName: () => "my.other.project" + } + }); + + const writerCollection = createWorkspace({ + name: "writer collection name", + reader, + writer: readerWriter + }); + t.true(writerCollection instanceof DuplexCollection, "Returned a ReaderCollection"); + + const resource1 = createResource({ + path: "/resources/app/File.js" + }); + + await writerCollection.write(resource1); + + const resources = await writerCollection.byGlob("**/*"); + t.is(resources.length, 3, "Found three resources"); +}); + +test("createWorkspace: Without writer", async (t) => { + const {default: DuplexCollection} = await import("../../lib/DuplexCollection.js"); + const {default: Memory} = await import("../../lib/adapters/Memory.js"); + const reader = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/", + project: { + getName: () => "my.project" + } + }); + const writerCollection = createWorkspace({ + name: "writer collection name", + reader + }); + t.true(writerCollection instanceof DuplexCollection, "Returned a ReaderCollection"); + t.true(writerCollection._writer instanceof Memory, "Internal Writer is created and a MemAdapter"); +}); + diff --git a/packages/fs/test/lib/resources.js b/packages/fs/test/lib/resources.js new file mode 100644 index 00000000000..64b861c56fa --- /dev/null +++ b/packages/fs/test/lib/resources.js @@ -0,0 +1,257 @@ +import test from "ava"; +import sinon from "sinon"; +import {readFile} from "node:fs/promises"; + +import {createAdapter, createFilterReader, + createFlatReader, createLinkReader, createResource} from "../../lib/resourceFactory.js"; + +test.afterEach.always((t) => { + sinon.restore(); +}); + +function getFileContent(path) { + return readFile(path, "utf8"); +} + +async function fileEqual(t, actual, expected) { + const actualContent = await getFileContent(actual); + const expectedContent = await getFileContent(expected); + t.is(actualContent, expectedContent); +} + +["FileSystem", "Memory"].forEach((adapter) => { + async function getAdapter(config) { + if (adapter === "Memory") { + const fsAdapter = createAdapter(config); + const fsResources = await fsAdapter.byGlob("**/*"); + // By removing the fsBasePath a MemAdapter will be created + delete config.fsBasePath; + const memAdapter = createAdapter(config); + for (const resource of fsResources) { + await memAdapter.write(resource); + } + return memAdapter; + } else if (adapter === "FileSystem") { + return createAdapter(config); + } + } + + /* BEWARE: + Always make sure that every test writes to a separate file! By default, tests are running concurrent. + */ + test(adapter + + ": Get resource from application.a (/index.html) and write it to /dest/ using a ReadableStream", async (t) => { + const source = await getAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + const dest = await getAdapter({ + fsBasePath: "./test/tmp/readerWriters/application.a/simple-read-write", + virBasePath: "/dest/" + }); + + // Get resource from one readerWriter + const resource = await source.byPath("/app/index.html"); + + // Write resource content to another readerWriter + resource.setPath("/dest/index_readableStreamTest.html"); + await dest.write(resource); + + t.notThrows(async () => { + if (adapter === "FileSystem") { + await fileEqual( + t, + "./test/tmp/readerWriters/application.a/simple-read-write/index_readableStreamTest.html", + "./test/fixtures/application.a/webapp/index.html"); + } else { + const destResource = await dest.byPath("/dest/index_readableStreamTest.html"); + t.deepEqual(await destResource.getString(), await resource.getString()); + } + }); + }); + + test(adapter + ": Create resource, write and change content", async (t) => { + const dest = await getAdapter({ + fsBasePath: "./test/tmp/writer/", + virBasePath: "/dest/writer/" + }); + + const resource = createResource({ + path: "/dest/writer/content/test.js", + string: "MyInitialContent" + }); + + await dest.write(resource); + + resource.setString("MyNewContent"); + + const resource1 = await dest.byPath("/dest/writer/content/test.js"); + + t.is(await resource.getString(), "MyNewContent"); + t.is(await resource1.getString(), "MyInitialContent"); + + t.is(await resource.getString(), "MyNewContent"); + t.is(await resource1.getString(), "MyInitialContent"); + + await dest.write(resource); + + const resource2 = await dest.byPath("/dest/writer/content/test.js"); + t.is(await resource.getString(), "MyNewContent"); + t.is(await resource2.getString(), "MyNewContent"); + }); + + test(adapter + ": Create resource, write and change path", async (t) => { + const dest = await getAdapter({ + fsBasePath: "./test/tmp/writer/", + virBasePath: "/dest/writer/" + }); + + const resource = createResource({ + path: "/dest/writer/path/test.js", + string: "MyInitialContent" + }); + + await dest.write(resource); + + resource.setPath("/dest/writer/path/test2.js"); + + const resourceOldPath = await dest.byPath("/dest/writer/path/test.js"); + const resourceNewPath = await dest.byPath("/dest/writer/path/test2.js"); + + t.is(await resource.getPath(), "/dest/writer/path/test2.js"); + t.truthy(resourceOldPath); + t.is(await resourceOldPath.getString(), await resource.getString()); + t.is(await resourceOldPath.getPath(), "/dest/writer/path/test.js"); + t.not(resourceNewPath); + + await dest.write(resource); + + const resourceOldPath1 = await dest.byPath("/dest/writer/path/test.js"); + const resourceNewPath1 = await dest.byPath("/dest/writer/path/test2.js"); + + t.is(await resource.getPath(), "/dest/writer/path/test2.js"); + t.truthy(resourceNewPath1); + t.is(await resourceNewPath1.getString(), await resource.getString()); + t.is(await resourceNewPath1.getPath(), "/dest/writer/path/test2.js"); + t.not(resourceOldPath1); + }); + + test(adapter + + ": Create a resource with a path different from the path configured in the adapter", async (t) => { + t.pass(2); + const dest = await getAdapter({ + fsBasePath: "./test/tmp/writer/", + virBasePath: "/dest2/writer/" + }); + + const resource = createResource({ + path: "/dest2/tmp/test.js", + string: "MyContent" + }); + + const error = await t.throwsAsync(dest.write(resource)); + t.is(error.message, + "Failed to write resource with virtual path '/dest2/tmp/test.js': Path must start with the " + + "configured virtual base path of the adapter. Base path: '/dest2/writer/'", + "Threw with expected error message"); + }); + + test(adapter + + ": Create a resource with a path above the path configured in the adapter", async (t) => { + t.pass(2); + const dest = await getAdapter({ + fsBasePath: "./test/tmp/writer/", + virBasePath: "/dest2/writer/" + }); + + const resource = createResource({ + path: "/dest2/test.js", + string: "MyContent" + }); + + const error = await t.throwsAsync(dest.write(resource)); + t.is(error.message, + "Failed to write resource with virtual path '/dest2/test.js': Path must start with the " + + "configured virtual base path of the adapter. Base path: '/dest2/writer/'", + "Threw with expected error message"); + }); + + test(adapter + + ": Create a resource with a path resolving outside the path configured in the adapter", async (t) => { + t.pass(2); + const dest = await getAdapter({ + fsBasePath: "./test/tmp/writer/", + virBasePath: "/dest/writer/" + }); + + const resource = createResource({ + path: "/dest/writer/../relative.js", + string: "MyContent" + }); + // Resource will already resolve relative path segments + t.is(resource.getPath(), "/dest/relative.js", "Resource path resolved"); + + // This would cause the base path to not match + // So we cheat, simulating a misbehaving or old Resource instance, using a relative path segment + sinon.stub(resource, "getPath").returns("/dest/writer/../relative.js"); + + await t.throwsAsync(dest.write(resource), { + message: + "Failed to write resource with virtual path '/dest/writer/../relative.js': " + + "Path must start with the configured virtual base path of the adapter. Base path: '/dest/writer/'" + }, "Threw with expected error message"); + }); + + test(adapter + ": Filter resources", async (t) => { + const source = createAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/app/" + }); + const filteredSource = createFilterReader({ + reader: source, + callback: (resource) => { + return resource.getPath().endsWith(".js"); + } + }); + const sourceResources = await source.byGlob("**"); + t.is(sourceResources.length, 2, "Found two resources in source"); + + const resources = await filteredSource.byGlob("**"); + + t.is(resources.length, 1, "Found exactly one resource via filter"); + t.is(resources[0].getPath(), "/app/test.js", "Found correct resource"); + }); + + test(adapter + ": Flatten resources", async (t) => { + const source = await getAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + const transformedSource = createFlatReader({ + reader: source, + namespace: "app" + }); + + const resources = await transformedSource.byGlob("**/*.js"); + t.is(resources.length, 1, "Found one resource via transformer"); + t.is(resources[0].getPath(), "/test.js", "Found correct resource"); + }); + + test(adapter + ": Link resources", async (t) => { + const source = await getAdapter({ + fsBasePath: "./test/fixtures/application.a/webapp", + virBasePath: "/resources/app/" + }); + const transformedSource = createLinkReader({ + reader: source, + pathMapping: { + linkPath: "/wow/this/is/a/beautiful/path/just/wow/", + targetPath: "/resources/" + } + }); + + const resources = await transformedSource.byGlob("**/*.js"); + t.is(resources.length, 1, "Found one resource via transformer"); + t.is(resources[0].getPath(), "/wow/this/is/a/beautiful/path/just/wow/app/test.js", "Found correct resource"); + }); +}); diff --git a/packages/fs/test/lib/tracing/traceSummary.js b/packages/fs/test/lib/tracing/traceSummary.js new file mode 100644 index 00000000000..94bbd746673 --- /dev/null +++ b/packages/fs/test/lib/tracing/traceSummary.js @@ -0,0 +1,91 @@ +import test from "ava"; +import sinon from "sinon"; +import esmock from "esmock"; + +async function createMock(t, isLevelEnabled=true) { + t.context.loggerStub = { + silly: sinon.stub(), + isLevelEnabled: () => { + return isLevelEnabled; + } + }; + t.context.traceSummary = await esmock("../../../lib/tracing/traceSummary.js", { + "@ui5/logger": { + getLogger: sinon.stub().returns(t.context.loggerStub) + } + }); + return t.context; +} + +test.afterEach.always((t) => { + sinon.restore(); +}); + +test.serial("traceSummary", async (t) => { + t.plan(2); + + const {traceSummary, loggerStub} = await createMock(t); + + // Measure always constant time + sinon.stub(process, "hrtime").returns([3, 426604599]); + + const expectedReport = "==========================\n[=> TRACE SUMMARY:\n" + + " 3.43 s elapsed time \n" + + " 1 trace calls \n" + + " 1 glob executions\n" + + " 1 path stats\n" + + " 1 rl-collections involed:\n" + + " 2x collection_two\n" + + "======================]"; + + // Add collection, byPath call and byGlob call w/o having a an active tracing started yet. + // Those calls will not be traced. + traceSummary.collection("collection_one"); + traceSummary.pathCall(); + traceSummary.globCall(); + + // Start tracing + traceSummary.traceStarted(); + + traceSummary.collection("collection_two"); + + // Add an already existing collection + traceSummary.collection("collection_two"); + + traceSummary.pathCall(); + traceSummary.globCall(); + + // Print reporting and reset tracing + await traceSummary.traceEnded(); + + t.is(loggerStub.silly.callCount, 1, "Logger has been called exactly once"); + t.deepEqual(loggerStub.silly.getCall(0).args[0], expectedReport, "Correct report logged to the console"); +}); + +test.serial("traceSummary no silly logging", async (t) => { + t.plan(1); + + const {traceSummary, loggerStub} = await createMock(t, false); + + // Add collection, byPath call and byGlob call w/o having a an active tracing started yet. + // Those calls will not be traced. + traceSummary.collection("collection_one"); + traceSummary.pathCall(); + traceSummary.globCall(); + + // Start tracing + traceSummary.traceStarted(); + + traceSummary.collection("collection_two"); + + // Add an already existing collection + traceSummary.collection("collection_two"); + + traceSummary.pathCall(); + traceSummary.globCall(); + + // Print reporting and reset tracing + await traceSummary.traceEnded(); + + t.is(loggerStub.silly.callCount, 0, "Logger has not been called (due to disabled silly logging)"); +}); diff --git a/packages/logger/.chglog/CHANGELOG.tpl.md b/packages/logger/.chglog/CHANGELOG.tpl.md new file mode 100755 index 00000000000..5a448f0ecb2 --- /dev/null +++ b/packages/logger/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,144 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +{{ if .Versions -}} +A list of unreleased changes can be found [here]({{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD). +{{ end -}} + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +### Reverts +{{ range .RevertCommits -}} +- {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + + +## [v3.0.0] - 2023-02-09 +### Breaking Changes + +- Replace npmlog with @ui5/logger/Logger (#363) [`66a159a`](https://github.com/SAP/ui5-logger/commit/66a159acd9b67a27dd66d1e8056c362585f51bcf) +- Deprecate advanced APIs in preparation of refactoring [`3aea5e7`](https://github.com/SAP/ui5-logger/commit/3aea5e766f9bda156e8c7e62a2e8c65f613ef7e9) +- Transform to ES Modules ([#306](https://github.com/SAP/ui5-logger/issues/306)) [`c79608b`](https://github.com/SAP/ui5-logger/commit/c79608b0e432168ca8570530b63a456b9ddd12cb) +- Require Node.js ^16.18.0, >=18.12.0 / npm >= 8 [`a8af8a7`](https://github.com/SAP/ui5-logger/commit/a8af8a7a82c6f657ac10b5018e654939d90fd81f) + +### BREAKING CHANGE + +The @ui5/logger got refactored and as a result its API went public. + +Remove the usage of npmlog and refactor @ui5/logger modules to emit log events which are then caught in dedicated handlers. This is somewhat inspired by npm's proc-log module. + +This breaking change removes capabilities that are likely to change and should not be part of a public API. + +This will ensure that later changes to the module can be donen in a +compatible manner. + +Relevant changes: + +- Restrict log-methods to two argument only. The use of placeholders + like '%s' is no longer supported. A warning will be logged if more + than two argument is supplied. Placeholders will be replaced with a + deprecation message. We suggest the use of template literals. +- Deprecate #getGroupLogger method. Calling it throws an error. + It will be removed in one of the next patch releases +- Deprecate #setShowProgress method. Calling it throws an error. + It will be removed in one of the next patch releases +- Remove GroupLogger and TaskLogger classes. Similar functionality might + be re-added in a later release. + +This package has been transformed to ES Modules. Therefore it no longer provides a CommonJS export. +If your project uses CommonJS, it needs to be converted to ES Modules or use a dynamic import. + +For more information see also: + +- https://sap.github.io/ui5-tooling/updates/migrate-v3/ +- https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +Support for older Node.js and npm releases has been dropped. +Only Node.js versions v16.18.0, v18.12.0 or higher as well as npm v8 or higher are supported. + +### Features + +- Add new log level "perf" [`acf0c71`](https://github.com/SAP/ui5-logger/commit/acf0c717612f440ea7a114e757c05d358ae523a7) + + + +## [v2.0.1] - 2020-10-22 +### Bug Fixes +- Typos in error messages [`1d25902`](https://github.com/SAP/ui5-logger/commit/1d2590223c4332f5ea6f1326b23ecf584fea5934) + + +## [v2.0.0] - 2020-03-31 +### Breaking Changes +- Require Node.js >= 10 [`1825d10`](https://github.com/SAP/ui5-logger/commit/1825d1013a88f164cbbfbf579c3e8e02df2b5082) + +### BREAKING CHANGE + +Support for older Node.js releases has been dropped. +Only Node.js v10 or higher is supported. + + +## [v1.0.2] - 2019-10-14 +### Bug Fixes +- Fix handling of log level "silent" [`020ced8`](https://github.com/SAP/ui5-logger/commit/020ced85a82d33c94e429aa28983affa0d8341ba) + + +## [v1.0.1] - 2019-03-21 + + +## [v1.0.0] - 2019-01-09 + + +## [v0.2.2] - 2018-11-16 +### Features +- Add UI5_LOG_LVL environment variable [`c3e65c4`](https://github.com/SAP/ui5-logger/commit/c3e65c444045832773e4dc43ffa2baf903a27e52) + + +## [v0.2.1] - 2018-10-29 + + +## [v0.2.0] - 2018-07-11 + + +## [v0.1.0] - 2018-06-26 + + +## v0.0.1 - 2018-06-06 + +{{- if .Versions }} +{{ range .Versions -}} +{{ if and .Tag.Previous (ne .Tag.Name "v3.0.0") -}} +[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} +{{ end -}} +[v3.0.0]: https://github.com/SAP/ui5-logger/compare/v2.0.1...v3.0.0 +[v2.0.1]: https://github.com/SAP/ui5-logger/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/SAP/ui5-logger/compare/v1.0.2...v2.0.0 +[v1.0.2]: https://github.com/SAP/ui5-logger/compare/v1.0.1...v1.0.2 +[v1.0.1]: https://github.com/SAP/ui5-logger/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/SAP/ui5-logger/compare/v0.2.2...v1.0.0 +[v0.2.2]: https://github.com/SAP/ui5-logger/compare/v0.2.1...v0.2.2 +[v0.2.1]: https://github.com/SAP/ui5-logger/compare/v0.2.0...v0.2.1 +[v0.2.0]: https://github.com/SAP/ui5-logger/compare/v0.1.0...v0.2.0 +[v0.1.0]: https://github.com/SAP/ui5-logger/compare/v0.0.1...v0.1.0 diff --git a/packages/logger/.chglog/RELEASE.tpl.md b/packages/logger/.chglog/RELEASE.tpl.md new file mode 100755 index 00000000000..efc482c385d --- /dev/null +++ b/packages/logger/.chglog/RELEASE.tpl.md @@ -0,0 +1,33 @@ +{{ range .Versions }} +{{ range .CommitGroups -}} +### {{ .Title }} +{{ range .Commits -}} +- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [`{{ .Hash.Short }}`]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Long }}) +{{ end }} +{{ end -}} + +{{- if .RevertCommits -}} +### Reverts +{{ range .RevertCommits -}} +- {{ .Revert.Header }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} + +{{ if .Tag.Previous }} +### All changes +[`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`] +{{ end }} + +{{ if .Tag.Previous -}} +[`{{ .Tag.Previous.Name }}...{{ .Tag.Name }}`]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} +{{ end -}} +{{ end -}} diff --git a/packages/logger/.chglog/config.yml b/packages/logger/.chglog/config.yml new file mode 100755 index 00000000000..54f3609580d --- /dev/null +++ b/packages/logger/.chglog/config.yml @@ -0,0 +1,33 @@ +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/SAP/ui5-logger +options: + commits: + filters: + Type: + - FEATURE + - FIX + - PERF + - DEPENDENCY + - BREAKING + commit_groups: + title_maps: + FEATURE: Features + FIX: Bug Fixes + PERF: Performance Improvements + DEPENDENCY: Dependency Updates + BREAKING: Breaking Changes + header: + pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" + pattern_maps: + - Type + - Scope + - Subject + issues: + prefix: + - "#" + notes: + keywords: + - BREAKING CHANGE diff --git a/packages/logger/.chglog/release-config.yml b/packages/logger/.chglog/release-config.yml new file mode 100755 index 00000000000..f4a7ac95c0b --- /dev/null +++ b/packages/logger/.chglog/release-config.yml @@ -0,0 +1,33 @@ +style: github +template: RELEASE.tpl.md +info: + repository_url: https://github.com/SAP/ui5-logger +options: + tag_filter_pattern: '^v[^0123]' # For release notes ignore versions below v4 to that we always compare the _last v4+_ tag with the current release + commits: + filters: + Type: + - FEATURE + - FIX + - PERF + - DEPENDENCY + - BREAKING + commit_groups: + title_maps: + FEATURE: Features + FIX: Bug Fixes + PERF: Performance Improvements + DEPENDENCY: Dependency Updates + BREAKING: Breaking Changes + header: + pattern: "^\\[(\\w*)\\]\\s(?:([^\\:]*)\\:\\s)?(.*)$" + pattern_maps: + - Type + - Scope + - Subject + issues: + prefix: + - "#" + notes: + keywords: + - BREAKING CHANGE diff --git a/packages/logger/.editorconfig b/packages/logger/.editorconfig new file mode 100644 index 00000000000..b432804f7fc --- /dev/null +++ b/packages/logger/.editorconfig @@ -0,0 +1,20 @@ +# see http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = tab + +[*.{css,html,js,cjs,mjs,jsx,ts,tsx,less,txt,json,yml,md}] +trim_trailing_whitespace = true +end_of_line = lf +indent_size = 4 +insert_final_newline = true + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/logger/.gitattributes b/packages/logger/.gitattributes new file mode 100644 index 00000000000..6313b56c578 --- /dev/null +++ b/packages/logger/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/packages/logger/.github/ISSUE_TEMPLATE.md b/packages/logger/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..6ddeb2221a8 --- /dev/null +++ b/packages/logger/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,3 @@ +## 🚨 Issues Have Been Transferred to UI5 CLI Repository + +Please create new issues in the UI5 CLI repository: https://github.com/UI5/cli/issues/new/choose diff --git a/packages/logger/.github/ISSUE_TEMPLATE/config.yml b/packages/logger/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..34df09809c9 --- /dev/null +++ b/packages/logger/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Report UI5 CLI Issues or Request a Feature + url: https://github.com/UI5/cli/issues/new/choose + about: Please create new issues in the UI5 CLI repository diff --git a/packages/logger/.github/PULL_REQUEST_TEMPLATE.md b/packages/logger/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..2f043b5c5cc --- /dev/null +++ b/packages/logger/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +**Thank you for your contribution!** 🙌 + +To get it merged faster, kindly review the checklist below: + +## Pull Request Checklist +- [ ] Reviewed the [Contributing Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#-contributing-code) + + Especially the [How to Contribute](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#how-to-contribute) section +- [ ] [No merge commits](https://github.com/UI5/cli/blob/main/docs/Guidelines.md#no-merge-commits) +- [ ] [Correct commit message style](https://github.com/UI5/cli/blob/main/docs/Guidelines.md#commit-message-style) diff --git a/packages/logger/.github/dependabot.yml b/packages/logger/.github/dependabot.yml new file mode 100644 index 00000000000..ebf6ca750f8 --- /dev/null +++ b/packages/logger/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" +- package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + day: sunday + time: "10:00" + timezone: Etc/UCT + reviewers: + - "SAP/ui5-foundation" + versioning-strategy: increase + commit-message: + prefix: "[DEPENDENCY] " + prefix-development: "[INTERNAL] " diff --git a/packages/logger/.github/in-solidarity.yml b/packages/logger/.github/in-solidarity.yml new file mode 100644 index 00000000000..4ce829a6be3 --- /dev/null +++ b/packages/logger/.github/in-solidarity.yml @@ -0,0 +1 @@ +_extends: ietf/terminology diff --git a/packages/logger/.github/workflows/dependabot-auto-merge.yml b/packages/logger/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 00000000000..43d92c94fd6 --- /dev/null +++ b/packages/logger/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,28 @@ +name: Dependabot auto-merge +on: + pull_request: + branches: + - v4 + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' && github.event.pull_request.auto_merge == null }} + steps: + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + - name: Approve and auto-merge PRs for minor/patch updates of github-actions + if: | + steps.metadata.outputs.package-ecosystem == 'github_actions' && + contains(fromJSON('["version-update:semver-minor", "version-update:semver-patch"]'), steps.metadata.outputs.update-type) + run: gh pr review --approve "$PR_URL" && gh pr merge --auto --rebase "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/packages/logger/.github/workflows/github-ci.yml b/packages/logger/.github/workflows/github-ci.yml new file mode 100644 index 00000000000..f649c335d83 --- /dev/null +++ b/packages/logger/.github/workflows/github-ci.yml @@ -0,0 +1,34 @@ +name: GitHub CI + +on: + push: + branches: + - v4 + pull_request: + branches: + - v4 + +# No permissions are required for this workflow +permissions: {} + +jobs: + test: + name: General checks, tests and coverage reporting + runs-on: ubuntu-24.04 + steps: + + - uses: actions/checkout@v5 + + - name: Use Node.js LTS 20.11.0 + uses: actions/setup-node@v5.0.0 + with: + node-version: 20.11.0 + + - name: Install dependencies + run: npm ci + + - name: Perform checks and tests + run: npm test + + - name: Send report to Coveralls + uses: coverallsapp/github-action@v2.3.6 diff --git a/packages/logger/.github/workflows/reuse-compliance.yml b/packages/logger/.github/workflows/reuse-compliance.yml new file mode 100644 index 00000000000..ee696a44f1b --- /dev/null +++ b/packages/logger/.github/workflows/reuse-compliance.yml @@ -0,0 +1,21 @@ +name: REUSE + +on: + push: + branches: + - v4 + pull_request: + branches: + - v4 + +# No permissions are required for this workflow +permissions: {} + +jobs: + compliance-check: + name: Compliance Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Execute REUSE Compliance Check + uses: fsfe/reuse-action@v5 diff --git a/packages/logger/.gitignore b/packages/logger/.gitignore new file mode 100644 index 00000000000..052409d713d --- /dev/null +++ b/packages/logger/.gitignore @@ -0,0 +1,62 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# CI (Azure Pipelines) xUnit test results +test-results.xml + +# IDEs +.vscode/ +*.~vsdx +.idea/ + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# Misc +yarn.lock +.DS_Store + +# Don't include private SSH key for deployment via Travis CI +deploy_key + +# Custom directories +test/tmp/ +jsdocs/ diff --git a/packages/logger/.npmrc b/packages/logger/.npmrc new file mode 100644 index 00000000000..93ec4f76ba6 --- /dev/null +++ b/packages/logger/.npmrc @@ -0,0 +1,3 @@ +# Enforce public npm registry +registry=https://registry.npmjs.org/ +lockfile-version=3 diff --git a/packages/logger/CHANGELOG.md b/packages/logger/CHANGELOG.md new file mode 100644 index 00000000000..976f82a35b3 --- /dev/null +++ b/packages/logger/CHANGELOG.md @@ -0,0 +1,133 @@ +# Changelog +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +A list of unreleased changes can be found [here](https://github.com/SAP/ui5-logger/compare/v4.0.2...HEAD). + + +## [v4.0.2] - 2025-09-11 +### Bug Fixes +- Rename project to UI5 CLI [`18d935a`](https://github.com/SAP/ui5-logger/commit/18d935a7caf7d53312b4f3976b0075aeae608902) +- **Console:** Handle consequitive builds correctly [`20e303a`](https://github.com/SAP/ui5-logger/commit/20e303ac157778aa1be0e20bb203e713fff2299c) + + + +## [v4.0.1] - 2024-07-23 + + +## [v4.0.0] - 2024-07-16 +### Breaking Changes +- Require Node.js 20.11.x/>=21.2.0 and npm >=10 [`37620d7`](https://github.com/SAP/ui5-logger/commit/37620d7ca67b94b5093670c5ac5383bbb9109c33) + +### Dependency Updates +- Bump figures from 5.0.0 to 6.0.1 [`550a3cd`](https://github.com/SAP/ui5-logger/commit/550a3cdd43eee3b2ea28a5db67c1a4485ec52a45) + +### BREAKING CHANGE + +Support for older Node.js and npm releases has been dropped. +Only Node.js 20.11.x and >=21.2.0 as well as npm v10 or higher are supported. + + +## [v3.0.0] - 2023-02-09 +### Breaking Changes + +- Replace npmlog with @ui5/logger/Logger (#363) [`66a159a`](https://github.com/SAP/ui5-logger/commit/66a159acd9b67a27dd66d1e8056c362585f51bcf) +- Deprecate advanced APIs in preparation of refactoring [`3aea5e7`](https://github.com/SAP/ui5-logger/commit/3aea5e766f9bda156e8c7e62a2e8c65f613ef7e9) +- Transform to ES Modules ([#306](https://github.com/SAP/ui5-logger/issues/306)) [`c79608b`](https://github.com/SAP/ui5-logger/commit/c79608b0e432168ca8570530b63a456b9ddd12cb) +- Require Node.js ^16.18.0, >=18.12.0 / npm >= 8 [`a8af8a7`](https://github.com/SAP/ui5-logger/commit/a8af8a7a82c6f657ac10b5018e654939d90fd81f) + +### BREAKING CHANGE + +The @ui5/logger got refactored and as a result its API went public. + +Remove the usage of npmlog and refactor @ui5/logger modules to emit log events which are then caught in dedicated handlers. This is somewhat inspired by npm's proc-log module. + +This breaking change removes capabilities that are likely to change and should not be part of a public API. + +This will ensure that later changes to the module can be donen in a +compatible manner. + +Relevant changes: + +- Restrict log-methods to two argument only. The use of placeholders + like '%s' is no longer supported. A warning will be logged if more + than two argument is supplied. Placeholders will be replaced with a + deprecation message. We suggest the use of template literals. +- Deprecate #getGroupLogger method. Calling it throws an error. + It will be removed in one of the next patch releases +- Deprecate #setShowProgress method. Calling it throws an error. + It will be removed in one of the next patch releases +- Remove GroupLogger and TaskLogger classes. Similar functionality might + be re-added in a later release. + +This package has been transformed to ES Modules. Therefore it no longer provides a CommonJS export. +If your project uses CommonJS, it needs to be converted to ES Modules or use a dynamic import. + +For more information see also: + +- https://sap.github.io/ui5-tooling/updates/migrate-v3/ +- https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +Support for older Node.js and npm releases has been dropped. +Only Node.js versions v16.18.0, v18.12.0 or higher as well as npm v8 or higher are supported. + +### Features + +- Add new log level "perf" [`acf0c71`](https://github.com/SAP/ui5-logger/commit/acf0c717612f440ea7a114e757c05d358ae523a7) + + + +## [v2.0.1] - 2020-10-22 +### Bug Fixes +- Typos in error messages [`1d25902`](https://github.com/SAP/ui5-logger/commit/1d2590223c4332f5ea6f1326b23ecf584fea5934) + + +## [v2.0.0] - 2020-03-31 +### Breaking Changes +- Require Node.js >= 10 [`1825d10`](https://github.com/SAP/ui5-logger/commit/1825d1013a88f164cbbfbf579c3e8e02df2b5082) + +### BREAKING CHANGE + +Support for older Node.js releases has been dropped. +Only Node.js v10 or higher is supported. + + +## [v1.0.2] - 2019-10-14 +### Bug Fixes +- Fix handling of log level "silent" [`020ced8`](https://github.com/SAP/ui5-logger/commit/020ced85a82d33c94e429aa28983affa0d8341ba) + + +## [v1.0.1] - 2019-03-21 + + +## [v1.0.0] - 2019-01-09 + + +## [v0.2.2] - 2018-11-16 +### Features +- Add UI5_LOG_LVL environment variable [`c3e65c4`](https://github.com/SAP/ui5-logger/commit/c3e65c444045832773e4dc43ffa2baf903a27e52) + + +## [v0.2.1] - 2018-10-29 + + +## [v0.2.0] - 2018-07-11 + + +## [v0.1.0] - 2018-06-26 + + +## v0.0.1 - 2018-06-06 +[v4.0.2]: https://github.com/SAP/ui5-logger/compare/v4.0.1...v4.0.2 +[v4.0.1]: https://github.com/SAP/ui5-logger/compare/v4.0.0...v4.0.1 +[v4.0.0]: https://github.com/SAP/ui5-logger/compare/v3.0.1-rc.3...v4.0.0 +[v3.0.0]: https://github.com/SAP/ui5-logger/compare/v2.0.1...v3.0.0 +[v2.0.1]: https://github.com/SAP/ui5-logger/compare/v2.0.0...v2.0.1 +[v2.0.0]: https://github.com/SAP/ui5-logger/compare/v1.0.2...v2.0.0 +[v1.0.2]: https://github.com/SAP/ui5-logger/compare/v1.0.1...v1.0.2 +[v1.0.1]: https://github.com/SAP/ui5-logger/compare/v1.0.0...v1.0.1 +[v1.0.0]: https://github.com/SAP/ui5-logger/compare/v0.2.2...v1.0.0 +[v0.2.2]: https://github.com/SAP/ui5-logger/compare/v0.2.1...v0.2.2 +[v0.2.1]: https://github.com/SAP/ui5-logger/compare/v0.2.0...v0.2.1 +[v0.2.0]: https://github.com/SAP/ui5-logger/compare/v0.1.0...v0.2.0 +[v0.1.0]: https://github.com/SAP/ui5-logger/compare/v0.0.1...v0.1.0 diff --git a/packages/logger/CONTRIBUTING.md b/packages/logger/CONTRIBUTING.md new file mode 100644 index 00000000000..2ddb6276ed5 --- /dev/null +++ b/packages/logger/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to the UI5 CLI + +See CONTRIBUTING.md in the [UI5/cli](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md) repository. diff --git a/packages/logger/LICENSE.txt b/packages/logger/LICENSE.txt new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/logger/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/logger/LICENSES/Apache-2.0.txt b/packages/logger/LICENSES/Apache-2.0.txt new file mode 100644 index 00000000000..4ed90b95224 --- /dev/null +++ b/packages/logger/LICENSES/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. diff --git a/packages/logger/README.md b/packages/logger/README.md new file mode 100644 index 00000000000..aa6d1d4c685 --- /dev/null +++ b/packages/logger/README.md @@ -0,0 +1,27 @@ +![UI5 icon](https://raw.githubusercontent.com/UI5/cli/main/docs/images/UI5_logo_wide.png) + +# ui5-logger +> Providing logging functionality for all UI5 CLI modules and extensions. +> Features log levels and rendering of a progress bar. +> Part of [UI5 CLI](https://github.com/UI5/cli) + +[![REUSE status](https://api.reuse.software/badge/github.com/SAP/ui5-logger)](https://api.reuse.software/info/github.com/SAP/ui5-logger) +[![Build Status](https://dev.azure.com/sap/opensource/_apis/build/status/SAP.ui5-logger?branchName=v4)](https://dev.azure.com/sap/opensource/_build/latest?definitionId=37&branchName=v4) +[![npm Package Version](https://badge.fury.io/js/%40ui5%2Flogger.svg)](https://www.npmjs.com/package/@ui5/logger) +[![Coverage Status](https://coveralls.io/repos/github/SAP/ui5-logger/badge.svg)](https://coveralls.io/github/SAP/ui5-logger) + +## Documentation +General UI5 CLI documentation can be found here: [ui5.github.io/cli](https://ui5.github.io/cli/) + +The UI5 Logger API Reference can be found here: [`@ui5/logger`](https://ui5.github.io/cli/v3/api/module-@ui5_logger.html) + +## Contributing +Please check our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md). + +## Support +Please follow our [Contribution Guidelines](https://github.com/UI5/cli/blob/main/CONTRIBUTING.md#report-an-issue) on how to report an issue. + +Please report issues in the main [UI5 CLI](https://github.com/UI5/cli) repository. + +## Release History +See [CHANGELOG.md](CHANGELOG.md). diff --git a/packages/logger/REUSE.toml b/packages/logger/REUSE.toml new file mode 100644 index 00000000000..b30d9e1d4d9 --- /dev/null +++ b/packages/logger/REUSE.toml @@ -0,0 +1,11 @@ +version = 1 +SPDX-PackageName = "ui5-logger" +SPDX-PackageSupplier = "SAP OpenUI5 " +SPDX-PackageDownloadLocation = "https://github.com/SAP/ui5-logger" +SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls." + +[[annotations]] +path = "**" +precedence = "aggregate" +SPDX-FileCopyrightText = "2025 SAP SE or an SAP affiliate company and UI5 CLI contributors" +SPDX-License-Identifier = "Apache-2.0" diff --git a/packages/logger/azure-pipelines.yml b/packages/logger/azure-pipelines.yml new file mode 100644 index 00000000000..929b3342876 --- /dev/null +++ b/packages/logger/azure-pipelines.yml @@ -0,0 +1,81 @@ +# Node.js +# Build a general Node.js project with npm. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- v4 + +variables: + CI: true + +strategy: + matrix: + linux_node_lts_20_min_version: + imageName: 'ubuntu-24.04' + node_version: 20.11.0 + linux_node_22_min_version: + imageName: 'ubuntu-24.04' + node_version: 22.1.0 + linux_node_lts_20: + imageName: 'ubuntu-24.04' + node_version: 20.x + mac_node_lts_20: + imageName: 'macos-13' + node_version: 20.x + windows_node_lts_20: + imageName: 'windows-2022' + node_version: 20.x + linux_node_22: + imageName: 'ubuntu-24.04' + node_version: 22.x + mac_node_22: + imageName: 'macos-13' + node_version: 22.x + windows_node_22: + imageName: 'windows-2022' + node_version: 22.x + linux_node_24: + imageName: 'ubuntu-24.04' + node_version: 24.x + mac_node_24: + imageName: 'macos-13' + node_version: 24.x + windows_node_24: + imageName: 'windows-2022' + node_version: 24.x + +pool: + vmImage: $(imageName) + +steps: +- task: NodeTool@0 + inputs: + versionSpec: $(node_version) + displayName: Install Node.js + +- script: npm ci + displayName: Install Dependencies + +- script: npm ls --prod + displayName: Check for missing / extraneous Dependencies + +- script: npm run test-azure + displayName: Run Tests + +- task: PublishTestResults@2 + displayName: Publish Test Results + condition: succeededOrFailed() + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: '$(System.DefaultWorkingDirectory)/test-results.xml' + +- task: PublishCodeCoverageResults@2 + displayName: Publish Test Coverage Results + condition: succeededOrFailed() + inputs: + summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml' + +- script: npm run coverage + displayName: Run Test Natively in Case of Failures + condition: failed() diff --git a/packages/logger/eslint.common.config.js b/packages/logger/eslint.common.config.js new file mode 100644 index 00000000000..07876d526f0 --- /dev/null +++ b/packages/logger/eslint.common.config.js @@ -0,0 +1,99 @@ +import jsdoc from "eslint-plugin-jsdoc"; +import ava from "eslint-plugin-ava"; +import globals from "globals"; +import js from "@eslint/js"; +import google from "eslint-config-google"; + +export default [{ + ignores: [ // Common ignore patterns across all tooling repos + "**/coverage/", + "test/tmp/", + "test/expected/", + "test/fixtures/", + "**/docs/", + "**/jsdocs/", + ], +}, js.configs.recommended, google, ava.configs["flat/recommended"], { + name: "Common ESLint config used for all tooling repos", + + plugins: { + jsdoc, + }, + + languageOptions: { + globals: { + ...globals.node, + }, + + ecmaVersion: 2023, + sourceType: "module", + }, + + settings: { + jsdoc: { + mode: "jsdoc", + + tagNamePreference: { + return: "returns", + augments: "extends", + }, + }, + }, + + rules: { + "indent": ["error", "tab"], + "linebreak-style": ["error", "unix"], + + "quotes": ["error", "double", { + allowTemplateLiterals: true, + }], + + "semi": ["error", "always"], + "no-negated-condition": "off", + "require-jsdoc": "off", + "no-mixed-requires": "off", + + "max-len": ["error", { + code: 120, + ignoreUrls: true, + ignoreRegExpLiterals: true, + }], + + "no-implicit-coercion": [2, { + allow: ["!!"], + }], + + "comma-dangle": "off", + "no-tabs": "off", + "no-console": 2, // Disallow console.log() + "no-eval": 2, + // The following rule must be disabled as of ESLint 9. + // It's removed and causes issues when present + // https://eslint.org/docs/latest/rules/valid-jsdoc + "valid-jsdoc": 0, + "jsdoc/check-examples": 0, + "jsdoc/check-param-names": 2, + "jsdoc/check-tag-names": 2, + "jsdoc/check-types": 2, + "jsdoc/no-undefined-types": 0, + "jsdoc/require-description": 0, + "jsdoc/require-description-complete-sentence": 0, + "jsdoc/require-example": 0, + "jsdoc/require-hyphen-before-param-description": 0, + "jsdoc/require-param": 2, + "jsdoc/require-param-description": 0, + "jsdoc/require-param-name": 2, + "jsdoc/require-param-type": 2, + "jsdoc/require-returns": 0, + "jsdoc/require-returns-description": 0, + "jsdoc/require-returns-type": 2, + + "jsdoc/tag-lines": [2, "any", { + startLines: 1, + }], + + "jsdoc/valid-types": 0, + "ava/assertion-arguments": 0, + }, +} +]; diff --git a/packages/logger/eslint.config.js b/packages/logger/eslint.config.js new file mode 100644 index 00000000000..74539640b1d --- /dev/null +++ b/packages/logger/eslint.config.js @@ -0,0 +1,9 @@ +import eslintCommonConfig from "./eslint.common.config.js"; + +export default [ + ...eslintCommonConfig, // Load common ESLint config + { + // Add project-specific ESLint config rules here + // in order to override common config + } +]; diff --git a/packages/logger/jsdoc-plugin.cjs b/packages/logger/jsdoc-plugin.cjs new file mode 100644 index 00000000000..cd7ef446d0f --- /dev/null +++ b/packages/logger/jsdoc-plugin.cjs @@ -0,0 +1,9 @@ +/* + * This plugin fixes unexpected JSDoc behavior that prevents us from using types that start with an at-sign (@). + * JSDoc doesn't see "{@" as a valid type expression, probably as there's also {@link ...}. + */ +exports.handlers = { + jsdocCommentFound: function(e) { + e.comment = e.comment.replace(/{@ui5\//g, "{ @ui5/"); + } +}; diff --git a/packages/logger/jsdoc.json b/packages/logger/jsdoc.json new file mode 100644 index 00000000000..c453cabd3d0 --- /dev/null +++ b/packages/logger/jsdoc.json @@ -0,0 +1,61 @@ +{ + "tags": { + "allowUnknownTags": false + }, + "source": { + "include": ["README.md"], + "includePattern": ".+\\.js$", + "excludePattern": "(node_modules(\\\\|/))" + }, + "plugins": [ + "jsdoc-plugin.cjs" + ], + "opts": { + "encoding": "utf8", + "destination": "jsdocs/", + "recurse": true, + "verbose": true, + "access": "public" + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "default": { + "useLongnameInNav": true + } + }, + "openGraph": { + "title": "UI5 CLI - API Reference", + "type": "website", + "image": "https://ui5.github.io/cli/v4/images/UI5_logo_wide.png", + "site_name": "UI5 CLI - API Reference", + "url": "https://ui5.github.io/cli/" + }, + "docdash": { + "sectionOrder": [ + "Modules", + "Namespaces", + "Classes", + "Externals", + "Events", + "Mixins", + "Tutorials", + "Interfaces" + ], + "meta": { + "title": "UI5 CLI - API Reference - UI5 Logger", + "description": "UI5 CLI - API Reference - UI5 Logger", + "keyword": "openui5 sapui5 ui5 build development tool api reference" + }, + "search": true, + "wrap": true, + "menu": { + "GitHub": { + "href": "https://github.com/SAP/ui5-logger", + "target": "_blank", + "class": "menu-item", + "id": "github_link" + } + } + } +} diff --git a/packages/logger/lib/index.js b/packages/logger/lib/index.js new file mode 100644 index 00000000000..b3472823992 --- /dev/null +++ b/packages/logger/lib/index.js @@ -0,0 +1,51 @@ +import Logger from "./loggers/Logger.js"; + +/** + * Interface for the UI5 CLI logging module + * + * @public + * @module @ui5/logger + */ + +/** + * Convenience function to create an instance of [@ui5/logger/Logger]{@link @ui5/logger/Logger} + * + * @public + * @param {string} moduleName Identifier for messages created by the logger. + * Example: module:submodule:Class + * @returns {@ui5/logger/Logger} + */ +export function getLogger(moduleName) { + return new Logger(moduleName); +} + +/** + * Tests whether the provided log level is enabled by the current log level + * + * @public + * @function + * @param {string} levelName Log level to test + * @returns {boolean} True if the provided level is enabled + */ +export const isLogLevelEnabled = Logger.isLevelEnabled; + +/** + * Sets the standard log level. + *
+ * Example: Setting it to perf would suppress all silly and verbose + * logging, and only show perf, info, warn and error logs. + * + * @public + * @function + * @param {string} levelName New log level + */ +export const setLogLevel = Logger.setLevel; + +/** + * Gets the current log level + * + * @public + * @function + * @returns {string} The current log level. Defaults to info + */ +export const getLogLevel = Logger.getLevel; diff --git a/packages/logger/lib/loggers/Build.js b/packages/logger/lib/loggers/Build.js new file mode 100644 index 00000000000..09c8eb000ce --- /dev/null +++ b/packages/logger/lib/loggers/Build.js @@ -0,0 +1,91 @@ +import Logger from "./Logger.js"; + +/** + * Logger for emitting status events on the progress a UI5 CLI build process. + *

+ * Emits ui5.log, ui5.build-metadata and ui5.build-status events on the + * [process]{@link https://nodejs.org/api/process.html} object, which can which can be handled + * by dedicated writers, like [@ui5/logger/writers/Console]{@link @ui5/logger/writers/Console}. + *

+ * If no listener is attached to the the event, messages are written directly to the process.stderr stream. + * + * @private + * @class + * @alias @ui5/logger/loggers/Build + */ +class Build extends Logger { + #projectsToBuild; + + static BUILD_METADATA_EVENT_NAME = "ui5.build-metadata"; + static BUILD_STATUS_EVENT_NAME = "ui5.build-status"; + + setProjects(projects) { + if (!projects || !Array.isArray(projects)) { + throw new Error("loggers/Build#setProjects: Missing or incorrect projects parameter"); + } + this.#projectsToBuild = projects; + + this._emit(Build.BUILD_METADATA_EVENT_NAME, { + projectsToBuild: projects + }); + } + + startProjectBuild(projectName, projectType) { + if (!this.#projectsToBuild || !this.#projectsToBuild.includes(projectName)) { + throw new Error(`loggers/Build#startProjectBuild: Unknown project ${projectName}`); + } + if (!projectType) { + throw new Error(`loggers/Build#startProjectBuild: Missing projectType parameter`); + } + const level = "info"; + const hasListeners = this._emit(Build.BUILD_STATUS_EVENT_NAME, { + level, + projectName, + projectType, + status: "project-build-start", + }); + if (!hasListeners) { + this._log(level, `Building ${projectType} project ${projectName}...`); + } + } + + endProjectBuild(projectName, projectType) { + if (!this.#projectsToBuild || !this.#projectsToBuild.includes(projectName)) { + throw new Error(`loggers/Build#endProjectBuild: Unknown project ${projectName}`); + } + if (!projectType) { + throw new Error(`loggers/Build#endProjectBuild: Missing projectType parameter`); + } + const level = "verbose"; + const hasListeners = this._emit(Build.BUILD_STATUS_EVENT_NAME, { + level, + projectName, + projectType, + status: "project-build-end", + }); + if (!hasListeners) { + this._log(level, `Finished building ${projectType} project ${projectName}`); + } + } + + skipProjectBuild(projectName, projectType) { + if (!this.#projectsToBuild || !this.#projectsToBuild.includes(projectName)) { + throw new Error(`loggers/Build#skipProjectBuild: Unknown project ${projectName}`); + } + if (!projectType) { + throw new Error(`loggers/Build#skipProjectBuild: Missing projectType parameter`); + } + const level = "info"; + const hasListeners = this._emit(Build.BUILD_STATUS_EVENT_NAME, { + level, + projectName, + projectType, + status: "project-build-skip", + }); + if (!hasListeners) { + this._log(level, `Skipping build of ${projectType} project ${projectName}`); + } + } +} + +export default Build; diff --git a/packages/logger/lib/loggers/Logger.js b/packages/logger/lib/loggers/Logger.js new file mode 100644 index 00000000000..8daa6d05ea1 --- /dev/null +++ b/packages/logger/lib/loggers/Logger.js @@ -0,0 +1,240 @@ +import process from "node:process"; +import {inspect} from "node:util"; + +// Module name must not contain any other characters than alphanumerical and some specials +const rIllegalModuleNameChars = /[^0-9a-zA-Z-_:@./]/i; + +/** + * Standard logging module for UI5 CLI and extensions. + *

+ * Emits ui5.log events on the [process]{@link https://nodejs.org/api/process.html} object, + * which can be handled by dedicated writers, + * like [@ui5/logger/writers/Console]{@link @ui5/logger/writers/Console}. + *

+ * If no listener is attached to an event, messages are written directly to the process.stderr stream. + * + * @public + * @class + * @alias @ui5/logger/Logger + */ +class Logger { + /** + * Available log levels, ordered by priority: + *
+ *
    + *
  1. silly
  2. + *
  3. verbose
  4. + *
  5. perf
  6. + *
  7. info (default)
  8. + *
  9. warn
  10. + *
  11. error
  12. + *
  13. silent
  14. + *
+ * + * Log level silent is special in the sense that no messages can be submitted with that level. + * It can be used to suppress all logging. + * + * @member {string[]} + * @public + */ + static LOG_LEVELS = ["silly", "verbose", "perf", "info", "warn", "error", "silent"]; + + /** + * Event name used for emitting new log-message event on the + * [process]{@link https://nodejs.org/api/process.html} object + * + * @member {string} + * @public + */ + static LOG_EVENT_NAME = "ui5.log"; + + /** + * Sets the standard log level. + *
+ * Example: Setting it to perf would suppress all silly and verbose + * logging, and only show perf, info, warn and error logs. + * + * @public + * @param {string} levelName New log level + */ + static setLevel(levelName) { + process.env.UI5_LOG_LVL = levelName; + } + + /** + * Gets the current log level + * + * @public + * @returns {string} The current log level. Defaults to info + */ + static getLevel() { + if (process.env.UI5_LOG_LVL) { + // Check whether set log level is valid + const levelName = process.env.UI5_LOG_LVL; + if (!Logger.LOG_LEVELS.includes(levelName)) { + throw new Error( + `UI5 Logger: Environment variable UI5_LOG_LVL is set to an unknown log level "${levelName}". ` + + `Valid levels are ${Logger.LOG_LEVELS.join(", ")}`); + } + return levelName; + } else { + return "info"; + } + } + + /** + * Tests whether the provided log level is enabled by the current log level + * + * @public + * @param {string} levelName Log level to test + * @returns {boolean} True if the provided level is enabled + */ + static isLevelEnabled(levelName) { + const currIdx = Logger.LOG_LEVELS.indexOf(Logger.getLevel()); + const reqIdx = Logger.LOG_LEVELS.indexOf(levelName); + if (reqIdx === -1) { + throw new Error(`Unknown log level "${levelName}"`); + } + return reqIdx >= currIdx; + } + + /** + * Formats a given parameter into a string + * + * @param {any} message Single log message parameter passed by a program + * @returns {string} String representation for the given message + */ + static _formatMessage(message) { + if (typeof message === "string" || message instanceof String) { + return message; + } + return inspect(message, { + depth: 3, + compact: 2, + }); + } + + #moduleName; + + /** + * + * + * @public + * @param {string} moduleName Identifier for messages created by this logger. + * Example: module:submodule:Class + */ + constructor(moduleName) { + if (!moduleName) { + throw new Error("Logger: Missing moduleName parameter"); + } + if (rIllegalModuleNameChars.test(moduleName)) { + throw new Error(`Logger: Invalid module name: ${moduleName}`); + } + this.#moduleName = moduleName; + } + + /** + * Tests whether the provided log level is enabled by the current log level + * + * @public + * @param {string} levelName Log level to test + * @returns {boolean} True if the provided level is enabled + */ + isLevelEnabled(levelName) { + return Logger.isLevelEnabled(levelName); + } + + _emit(eventName, payload) { + return process.emit(eventName, payload); + } + + _log(level, message) { + if (this.isLevelEnabled(level)) { + process.stderr.write(`[${level}] ${message}\n`); + } + } + + _emitOrLog(level, message) { + const hasListeners = this._emit(Logger.LOG_EVENT_NAME, { + level, + message, + moduleName: this.#moduleName, + }); + if (!hasListeners) { + this._log(level, `${this.#moduleName}: ${message}`); + } + } +} + +/** + * Create a log entry with the silly level + * + * @public + * @name @ui5/logger/Logger#silly + * @function + * @memberof @ui5/logger/Logger + * @param {...any} message Messages to log. An automatic string conversion is applied if necessary + */ + +/** + * Create a log entry with the verbose level + * + * @public + * @name @ui5/logger/Logger#verbose + * @function + * @memberof @ui5/logger/Logger + * @param {...any} message Messages to log. An automatic string conversion is applied if necessary + */ + +/** + * Create a log entry with the perf level + * + * @public + * @name @ui5/logger/Logger#perf + * @function + * @memberof @ui5/logger/Logger + * @param {...any} message Messages to log. An automatic string conversion is applied if necessary + */ + +/** + * Create a log entry with the info level + * + * @public + * @name @ui5/logger/Logger#info + * @function + * @memberof @ui5/logger/Logger + * @param {...any} message Messages to log. An automatic string conversion is applied if necessary + */ + +/** + * Create a log entry with the warn level + * + * @public + * @name @ui5/logger/Logger#warn + * @function + * @memberof @ui5/logger/Logger + * @param {...any} message Messages to log. An automatic string conversion is applied if necessary + */ + +/** + * Create a log entry with the error level + * + * @public + * @name @ui5/logger/Logger#error + * @function + * @memberof @ui5/logger/Logger + * @param {...any} message Messages to log. An automatic string conversion is applied if necessary + */ + +Logger.LOG_LEVELS.forEach((logLevel) => { + if (logLevel === "silent") { + // This level is to suppress any logging. Hence we do not provide a dedicated log-function + return; + } + Logger.prototype[logLevel] = function(...args) { + const message = args.map(Logger._formatMessage).join(" "); + this._emitOrLog(logLevel, message); + }; +}); + +export default Logger; diff --git a/packages/logger/lib/loggers/ProjectBuild.js b/packages/logger/lib/loggers/ProjectBuild.js new file mode 100644 index 00000000000..83f39497081 --- /dev/null +++ b/packages/logger/lib/loggers/ProjectBuild.js @@ -0,0 +1,88 @@ +import Logger from "./Logger.js"; + +/** + * Logger for emitting status events on the progress a single project's build process. + *

+ * Emits ui5.log, ui5.project-build-metadata and ui5.project-build-status + * events on the [process]{@link https://nodejs.org/api/process.html} object, + * which can be handled by dedicated writers, + * like [@ui5/logger/writers/Console]{@link @ui5/logger/writers/Console}. + *

+ * If no listener is attached to the the event, messages are written directly to the process.stderr stream. + * + * @private + * @class + * @alias @ui5/logger/loggers/ProjectBuild + */ +class ProjectBuild extends Logger { + #projectName; + #projectType; + #tasksToRun; + + static PROJECT_BUILD_METADATA_EVENT_NAME = "ui5.project-build-metadata"; + static PROJECT_BUILD_STATUS_EVENT_NAME = "ui5.project-build-status"; + + constructor({moduleName, projectName, projectType}) { + super(moduleName); + + if (!projectName) { + throw new Error("loggers/ProjectBuild: Missing projectName parameter"); + } + if (!projectType) { + throw new Error("loggers/ProjectBuild: Missing projectType parameter"); + } + this.#projectName = projectName; + this.#projectType = projectType; + } + + setTasks(tasks) { + if (!tasks || !Array.isArray(tasks)) { + throw new Error("loggers/ProjectBuild#setTasks: Missing or incorrect tasks parameter"); + } + this.#tasksToRun = tasks; + + this._emit(ProjectBuild.PROJECT_BUILD_METADATA_EVENT_NAME, { + projectName: this.#projectName, + projectType: this.#projectType, + tasksToRun: tasks + }); + } + + startTask(taskName) { + if (!this.#tasksToRun || !this.#tasksToRun.includes(taskName)) { + throw new Error(`loggers/ProjectBuild#startTask: Unknown task ${taskName}`); + } + const level = "info"; + const hasListeners = this._emit(ProjectBuild.PROJECT_BUILD_STATUS_EVENT_NAME, { + level, + projectName: this.#projectName, + projectType: this.#projectType, + taskName, + status: "task-start", + }); + + if (!hasListeners) { + this._log(level, `${this.#projectName}: Running task ${taskName}...`); + } + } + + endTask(taskName) { + if (!this.#tasksToRun || !this.#tasksToRun.includes(taskName)) { + throw new Error(`loggers/ProjectBuild#endTask: Unknown task ${taskName}`); + } + const level = "verbose"; + const hasListeners = this._emit(ProjectBuild.PROJECT_BUILD_STATUS_EVENT_NAME, { + level, + projectName: this.#projectName, + projectType: this.#projectType, + taskName, + status: "task-end", + }); + + if (!hasListeners) { + this._log(level, `${this.#projectName}: Finished task ${taskName}`); + } + } +} + +export default ProjectBuild; diff --git a/packages/logger/lib/writers/Console.js b/packages/logger/lib/writers/Console.js new file mode 100644 index 00000000000..b2fc29f1563 --- /dev/null +++ b/packages/logger/lib/writers/Console.js @@ -0,0 +1,379 @@ +import process from "node:process"; +import {chalkStderr as chalk} from "chalk"; +import figures from "figures"; +import {MultiBar} from "cli-progress"; +import Logger from "../loggers/Logger.js"; + +/** + * Standard handler for events emitted by @ui5/logger modules. Writes messages to + * [process.stderr]{@link https://nodejs.org/api/process.html#processstderr} stream + * and renders a progress bar for UI5 CLI build processes. + *

+ * The progress bar is only used in interactive terminals. If verbose logging is enabled, the progress + * bar is disabled. + * + * @public + * @class + * @alias @ui5/logger/writers/Console + */ +class Console { + #projectMetadata = new Map(); + #progressBarContainer; + #progressBar; + #progressProjectWeight; + + constructor() { + this._handleLogEvent = this.#handleLogEvent.bind(this); + this._handleBuildStatusEvent = this.#handleBuildStatusEvent.bind(this); + this._handleProjectBuildStatusEvent = this.#handleProjectBuildStatusEvent.bind(this); + this._handleBuildMetadataEvent = this.#handleBuildMetadataEvent.bind(this); + this._handleProjectBuildMetadataEvent = this.#handleProjectBuildMetadataEvent.bind(this); + this._handleStop = this.disable.bind(this); + } + + /** + * Attaches all event listeners and starts writing to output stream + * + * @public + */ + enable() { + process.on("ui5.log", this._handleLogEvent); + process.on("ui5.build-metadata", this._handleBuildMetadataEvent); + process.on("ui5.project-build-metadata", this._handleProjectBuildMetadataEvent); + process.on("ui5.build-status", this._handleBuildStatusEvent); + process.on("ui5.project-build-status", this._handleProjectBuildStatusEvent); + process.on("ui5.log.stop-console", this._handleStop); + } + + /** + * Detaches all event listeners and stops writing to output stream + * + * @public + */ + disable() { + process.off("ui5.log", this._handleLogEvent); + process.off("ui5.build-metadata", this._handleBuildMetadataEvent); + process.off("ui5.project-build-metadata", this._handleProjectBuildMetadataEvent); + process.off("ui5.build-status", this._handleBuildStatusEvent); + process.off("ui5.project-build-status", this._handleProjectBuildStatusEvent); + process.off("ui5.log.stop-console", this._handleStop); + if (this.#progressBarContainer) { + this.#progressBar.stop(); + this.#progressBarContainer.stop(); // Will fire internal stop event + } + } + + /* + * Progress bar is only required when building projects. So we create it lazily + */ + _getProgressBar() { + // Do not use a progress bar if there is no text terminal attached or verbose logging is enabled + // * If log output is piped to a static output (= no TTY), no progress bar should be rendered + // * Since logging through the progress bar is asynchronous (controlled by the FPS setting), + // exceptions might lead to log messages being dropped. Therefore do not use a progress bar + // for verbose logging + // * If log-level is set to "silent", we should never render a progress bar + if (process.stderr.isTTY !== true || Logger.isLevelEnabled("verbose") || Logger.getLevel() === "silent") { + return null; + } + if (!this.#progressBarContainer) { + // We use a "MultiBar" instance even though we intend to only render a single progress bar + // This is because only MultiBar provides a "log" method as of today + this.#progressBarContainer = new MultiBar({ + format: `{bar} {message}`, + barsize: 20, + linewrap: true, + emptyOnZero: 0, + hideCursor: true, + + // FPS also controls how fast a log message will be rendered above the progress bar + fps: 120, + + // Disable progress bar explicitly for non-TTY, even though this is already checked above + noTTYOutput: false, + + // Graceful exit is required to ensure all terminal settings (e.g. hideCursor) + // are restored on SIGINT + gracefulExit: true, + + // Required to prevent flickering when logging + forceRedraw: true, + + clearOnComplete: true, + stopOnComplete: true, + + barCompleteChar: figures.square, + barIncompleteChar: figures.squareLightShade, + }); + + // Initialize empty progress bar to enable logging through the multibar instance + this.#progressBar = this.#progressBarContainer.create(0, 0, {message: ""}); + + this.#progressBarContainer.on("stop", () => { + // Progress bar has finished and will remove itself from the output. + // Therefore, further logging needs to be done directly to process.stderr. Otherwise it would disappear. + // Therefore, we de-reference all instances now + this.#progressBarContainer = null; + this.#progressBar = null; + }); + } + return this.#progressBar; + } + + #writeMessage(level, message) { + if (!Logger.isLevelEnabled(level)) { + return; + } + const levelPrefix = this.#getLevelPrefix(level); + const msg = `${levelPrefix} ${message}\n`; + + if (this.#progressBarContainer) { + // If a progress bar is in use, we have to log through it's API + // cli-progress requires full control of the stderr output to ensure correct rendering + this.#progressBarContainer.log(msg); + } else { + process.stderr.write(msg); + } + } + + #handleLogEvent({level, message, moduleName}) { + this.#writeMessage(level, `${chalk.blue(moduleName)} ${message}`); + } + + #handleBuildMetadataEvent({projectsToBuild}) { + this.#projectMetadata = new Map(); + projectsToBuild.forEach((projectName) => { + this.#projectMetadata.set(projectName, { + buildStarted: false, + buildSkipped: false, + buildEnded: false, + buildStartIndex: null, + projectTasks: new Map(), + }); + }); + this.#updateProgressBarTotal(); + } + + #handleProjectBuildMetadataEvent({tasksToRun, projectName, projectType}) { + const projectMetadata = this.#getProjectMetadata(projectName); + tasksToRun.forEach((taskName) => { + projectMetadata.projectTasks.set(taskName, { + executionStarted: false, + executionEnded: false, + executionStartIndex: null, + }); + }); + this.#updateProgressBarTotal(); + } + + #getProjectMetadata(projectName) { + const projectMetadata = this.#projectMetadata.get(projectName); + if (!projectMetadata) { + throw new Error(`writers/Console: Unknown project ${projectName}`); + } + return projectMetadata; + } + + #updateProgressBarTotal() { + let numberOfTasks = 0; + this.#projectMetadata.forEach(({projectTasks}) => { + numberOfTasks += projectTasks.size; + }); + + // Project progress should weigh more than single task progress + // This is proportional to the number of projects (since that also multiplies the number of tasks) + this.#progressProjectWeight = this.#projectMetadata.size; + this._getProgressBar()?.setTotal( + (this.#progressProjectWeight * this.#projectMetadata.size) + numberOfTasks); + } + + #handleBuildStatusEvent({level, projectName, projectType, status}) { + const projectMetadata = this.#getProjectMetadata(projectName); + if (projectMetadata.buildStartIndex === null) { + let nextIdx = 1; + this.#projectMetadata.forEach((metadata) => { + if (metadata.buildStartIndex !== null && metadata.buildStartIndex >= nextIdx) { + nextIdx = metadata.buildStartIndex + 1; + } + }); + projectMetadata.buildStartIndex = nextIdx; + } + const buildIndex = `Project ${projectMetadata.buildStartIndex} of ${this.#projectMetadata.size}`; + + let message; + switch (status) { + case "project-build-start": + if (projectMetadata.buildEnded) { + throw new Error( + `writers/Console: Unexpected project-build-start event for project ${projectName}. ` + + `Project build already ended`); + } + if (projectMetadata.buildStarted) { + throw new Error( + `writers/Console: Unexpected duplicate project-build-start event for project ${projectName}`); + } + if (projectMetadata.buildSkipped) { + throw new Error( + `writers/Console: Unexpected project-build-start event for project ${projectName}. ` + + `Project build already skipped`); + } + projectMetadata.buildStarted = true; + message = `${chalk.blue(figures.pointer)} ` + + `Building ${projectType} project ${chalk.bold(projectName)}...`; + + // Update progress bar message with current project + this._getProgressBar()?.update({ + message: `${figures.pointer} Building ${projectType} project ${projectName}...` + }); + break; + case "project-build-end": + if (projectMetadata.buildEnded) { + throw new Error( + `writers/Console: Unexpected duplicate project-build-end event for project ${projectName}`); + } + if (projectMetadata.buildSkipped) { + throw new Error( + `writers/Console: Unexpected project-build-end event for project ${projectName}. ` + + `Project build already skipped`); + } + if (!projectMetadata.buildStarted) { + throw new Error( + `writers/Console: Unexpected project-build-end event for project ${projectName}. ` + + `No corresponding project-build-start event handled`); + } + projectMetadata.buildEnded = true; + message = `${chalk.green(figures.tick)} ` + + `Finished building ${projectType} project ${chalk.bold(projectName)}`; + + // Update progress bar (if used) + this._getProgressBar()?.increment(this.#progressProjectWeight); + break; + case "project-build-skip": + if (projectMetadata.buildSkipped) { + throw new Error( + `writers/Console: Unexpected duplicate project-build-skip event for project ${projectName}`); + } + if (projectMetadata.buildEnded) { + throw new Error( + `writers/Console: Unexpected project-build-skip event for project ${projectName}. ` + + `Project build already ended`); + } + if (projectMetadata.buildStarted) { + throw new Error( + `writers/Console: Unexpected project-build-skip event for project ${projectName}. ` + + `Project build already started`); + } + projectMetadata.buildSkipped = true; + message = `${chalk.yellow(figures.tick)} ` + + `Skipping build of ${projectType} project ${chalk.bold(projectName)}`; + + // Update progress bar (if used) + // All tasks of this projects are completed + this._getProgressBar()?.increment(this.#progressProjectWeight + projectMetadata.projectTasks.size); + break; + default: + this.#writeMessage("verbose", + `writers/Console: Received unknown build-status ${status} for project ${projectName}`); + return; + } + + this.#writeMessage(level, `${chalk.grey(buildIndex)}: ${message}`); + } + + #handleProjectBuildStatusEvent({level, projectName, projectType, taskName, status}) { + const {projectTasks} = this.#getProjectMetadata(projectName); + const taskMetadata = projectTasks.get(taskName); + if (!taskMetadata) { + throw new Error(`writers/Console: Unknown task ${taskName} for project ${projectName}`); + } + if (taskMetadata.executionStartIndex === null) { + let nextIdx = 1; + projectTasks.forEach((metadata) => { + if (metadata.executionStartIndex !== null && metadata.executionStartIndex >= nextIdx) { + nextIdx = metadata.executionStartIndex + 1; + } + }); + taskMetadata.executionStartIndex = nextIdx; + } + let taskIndex = ""; + if (Logger.isLevelEnabled("verbose")) { + taskIndex = chalk.grey(`Task ${taskMetadata.executionStartIndex} of ${projectTasks.size} `); + } + + let message; + switch (status) { + case "task-start": + if (taskMetadata.executionEnded) { + throw new Error( + `writers/Console: Unexpected task-start event for project ${projectName}, task ${taskName}. ` + + `Task execution already ended`); + } + if (taskMetadata.executionStarted) { + throw new Error(`writers/Console: Unexpected duplicate task-start event ` + + `for project ${projectName}, task ${taskName}`); + } + taskMetadata.executionStarted = true; + message = `${chalk.blue(figures.pointerSmall)} Running task ${chalk.bold(taskName)}...`; + break; + case "task-end": + if (taskMetadata.executionEnded) { + throw new Error(`writers/Console: ` + + `Unexpected duplicate task-end event for project ${projectName}, task ${taskName}`); + } + if (!taskMetadata.executionStarted) { + throw new Error( + `writers/Console: Unexpected task-end event for project ${projectName}, task ${taskName}. ` + + `No corresponding task-start event handled`); + } + taskMetadata.executionEnded = true; + message = `${chalk.green(figures.tick)} Finished task ${chalk.bold(taskName)}`; + + // Update progress bar (if used) + this._getProgressBar()?.increment(1); + break; + default: + this.#writeMessage("verbose", + `writers/Console: Received unknown project-build-status ${status} for project ${projectName}`); + return; + } + + this.#writeMessage(level, `${chalk.blue(`${(projectName)}`)} ${taskIndex}${message}`); + } + + #getLevelPrefix(level) { + switch (level) { + case "silly": + return chalk.inverse(level); + case "verbose": + return chalk.cyan("verb"); + case "perf": + return chalk.bgYellow.red(level); + case "info": + return chalk.green(level); + case "warn": + return chalk.yellow(level); + case "error": + return chalk.bgRed.white(level); + // Log level silent does not produce messages + default: + return level; + } + } + + /** + * Creates a new instance and subscribes it to all events + * + * @public + */ + static init() { + const cH = new Console(); + cH.enable(); + return cH; + } + + static stop() { + process.emit("ui5.log.stop-console"); + } +} + +export default Console; diff --git a/packages/logger/package-lock.json b/packages/logger/package-lock.json new file mode 100644 index 00000000000..5e35738eb9b --- /dev/null +++ b/packages/logger/package-lock.json @@ -0,0 +1,7360 @@ +{ + "name": "@ui5/logger", + "version": "4.0.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@ui5/logger", + "version": "4.0.2", + "license": "Apache-2.0", + "dependencies": { + "chalk": "^5.6.2", + "cli-progress": "^3.12.0", + "figures": "^6.1.0" + }, + "devDependencies": { + "@eslint/js": "^9.8.0", + "@istanbuljs/esm-loader-hook": "^0.3.0", + "ava": "^6.4.1", + "chokidar-cli": "^3.0.0", + "cross-env": "^10.0.0", + "depcheck": "^1.4.7", + "docdash": "^2.0.2", + "eslint": "^9.36.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-ava": "^15.1.0", + "eslint-plugin-jsdoc": "^60.0.0", + "globals": "^16.4.0", + "jsdoc": "^4.0.4", + "nyc": "^17.1.0", + "open-cli": "^8.0.0", + "rimraf": "^6.0.1", + "sinon": "^21.0.0", + "strip-ansi": "^7.1.2", + "tap-xunit": "^2.4.1" + }, + "engines": { + "node": "^20.11.0 || >=22.0.0", + "npm": ">= 8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.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/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.58.0.tgz", + "integrity": "sha512-smMc5pDht/UVsCD3hhw/a/e/p8m0RdRYiluXToVfd+d4yaQQh7nn9bACjkk6nXJvat7EWPAxuFkMEFfrxeGa3Q==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.43.0", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~5.4.0" + }, + "engines": { + "node": ">=20.11.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/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/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/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/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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/@eslint/js": { + "version": "9.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.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/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "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/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/@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/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/esm-loader-hook": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/esm-loader-hook/-/esm-loader-hook-0.3.0.tgz", + "integrity": "sha512-lEnYroBUYfNQuJDYrPvre8TSwPZnyIQv9qUT3gACvhr3igZr+BbrdyIcz4+2RnEXZzi12GqkUW600+QQPpIbVg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.8.7", + "@babel/plugin-syntax-decorators": "^7.25.9", + "@babel/preset-typescript": "^7.26.0", + "@istanbuljs/load-nyc-config": "^1.1.0", + "@istanbuljs/schema": "^0.1.3", + "babel-plugin-istanbul": "^6.0.0", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=16.12.0" + } + }, + "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/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/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", + "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", + "dev": true, + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/@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/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "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/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz", + "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vercel/nft": { + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.29.4.tgz", + "integrity": "sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==", + "dev": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^10.4.5", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "dev": true + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "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/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "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/aggregate-error/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/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-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "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/anymatch/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/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "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/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-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true + }, + "node_modules/ava": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.4.1.tgz", + "integrity": "sha512-vxmPbi1gZx9zhAjHBgw81w/iEDKcrokeRk/fqDTyA2DQygZ0o+dUGRHFOtX8RA5N0heGJTTsIk7+xYxitDb61Q==", + "dev": true, + "dependencies": { + "@vercel/nft": "^0.29.4", + "acorn": "^8.15.0", + "acorn-walk": "^8.3.4", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.2.0", + "cbor": "^10.0.9", + "chalk": "^5.4.1", + "chunkd": "^2.0.1", + "ci-info": "^4.3.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.4.1", + "emittery": "^1.2.0", + "figures": "^6.1.0", + "globby": "^14.1.0", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.1.0", + "ms": "^2.1.3", + "p-map": "^7.0.3", + "package-config": "^5.0.0", + "picomatch": "^4.0.2", + "plur": "^5.1.0", + "pretty-ms": "^9.2.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^6.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^22 || ^23 || >=24" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "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/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/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "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": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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.2.1" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/caching-transform/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/caching-transform/node_modules/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, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/caniuse-lite": { + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "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/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cbor": { + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-10.0.11.tgz", + "integrity": "sha512-vIwORDd/WyB8Nc23o2zNN5RrtFGlR6Fca61TtjkUXueI3Jf2DOZDl1zsshvBntZ3wZHBM9ztjnkXSmzQDaq3WA==", + "dev": true, + "dependencies": { + "nofilter": "^3.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz", + "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "yargs": "^13.3.0" + }, + "bin": { + "chokidar": "index.js" + }, + "engines": { + "node": ">= 8.10.0" + } + }, + "node_modules/chokidar-cli/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/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/chokidar-cli/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/chokidar-cli/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/chokidar-cli/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar-cli/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/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/chokidar-cli/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar-cli/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/chokidar-cli/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/chokidar-cli/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true + }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "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-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", + "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "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/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/concordance/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "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/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-env": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.0.0.tgz", + "integrity": "sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "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/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/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "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/depcheck": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-1.4.7.tgz", + "integrity": "sha512-1lklS/bV5chOxwNKA/2XUUk/hPORp8zihZsXflr8x0kLwmcZ9Y9BsS6Hs3ssvA+2wUVbG0U2Ciqvm1SokNjPkA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.2", + "@vue/compiler-sfc": "^3.3.4", + "callsite": "^1.0.0", + "camelcase": "^6.3.0", + "cosmiconfig": "^7.1.0", + "debug": "^4.3.4", + "deps-regex": "^0.2.0", + "findup-sync": "^5.0.0", + "ignore": "^5.2.4", + "is-core-module": "^2.12.0", + "js-yaml": "^3.14.1", + "json5": "^2.2.3", + "lodash": "^4.17.21", + "minimatch": "^7.4.6", + "multimatch": "^5.0.0", + "please-upgrade-node": "^3.2.0", + "readdirp": "^3.6.0", + "require-package-name": "^2.0.1", + "resolve": "^1.22.3", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "yargs": "^16.2.0" + }, + "bin": { + "depcheck": "bin/depcheck.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/depcheck/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/depcheck/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/depcheck/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/depcheck/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/depcheck/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/depcheck/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/depcheck/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/depcheck/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/depcheck/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/depcheck/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/depcheck/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/deps-regex": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.2.0.tgz", + "integrity": "sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==", + "dev": true + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/docdash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/docdash/-/docdash-2.0.2.tgz", + "integrity": "sha512-3SDDheh9ddrwjzf6dPFe1a16M6ftstqTNjik2+1fx46l24H9dD2osT2q9y+nBEC1wWz4GIqA48JmicOLQ0R8xA==", + "dev": true, + "dependencies": { + "@jsdoc/salty": "^0.2.1" + } + }, + "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/electron-to-chromium": { + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "dev": true + }, + "node_modules/emittery": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.2.0.tgz", + "integrity": "sha512-KxdRyyFcS85pH3dnU8Y5yFUm2YJdaHwcBZWrfG8o89ZY9a13/f9itbN+YG3ELbBo9Pg5zvIozstmuV8bX13q6g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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==" + }, + "node_modules/enhance-visitors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/enhance-visitors/-/enhance-visitors-1.0.0.tgz", + "integrity": "sha512-+29eJLiUixTEDRaZ35Vu8jP3gPLNcQQkQkOQjLp2X+6cZGGPDD/uasbFzvLsJKnGZnvmyZ0srxudwOtskHeIDA==", + "dev": true, + "dependencies": { + "lodash": "^4.13.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "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": "9.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-ava": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-15.1.0.tgz", + "integrity": "sha512-+6Zxk1uYW3mf7lxCLWIQsFYgn3hfuCMbsKc0MtqfloOz1F6fiV5/PaWEaLgkL1egrSQmnyR7vOFP1wSPJbVUbw==", + "dev": true, + "dependencies": { + "enhance-visitors": "^1.0.0", + "eslint-utils": "^3.0.0", + "espree": "^9.0.0", + "espurify": "^2.1.1", + "import-modules": "^2.1.0", + "micro-spelling-correcter": "^1.1.1", + "pkg-dir": "^5.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "peerDependencies": { + "eslint": ">=9" + } + }, + "node_modules/eslint-plugin-ava/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/eslint-plugin-ava/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/eslint-plugin-jsdoc": { + "version": "60.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-60.0.0.tgz", + "integrity": "sha512-K8D5fJOkeunX+exeOkOUx+P7ZCixdRBsM3QQ+M2aazSO+64pnjH6z03wWYGQ3XTjUxpI2QXOTSQ9E75bDKi5AA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.58.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.4.3", + "escape-string-regexp": "^4.0.0", + "espree": "^10.4.0", + "esquery": "^1.6.0", + "object-deep-merge": "^1.0.5", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=20.11.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/eslint/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/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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/espurify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/espurify/-/espurify-2.1.1.tgz", + "integrity": "sha512-zttWvnkhcDyGOhSH4vO2qCBILpdCMv/MX8lp4cqgRkQoDRGK2oZxi2GfWhlP2dIXmk7BaKeOTuzbHhyC68o8XQ==", + "dev": true + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "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/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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-to-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", + "integrity": "sha512-inRWzRY7nG+aXZxBzEqYKB3HPgwflZRopAjDCHv0whhRx+MTUr1ei0ICZUypdyE0HRm4L2d5VEcIqLD6yl+BFA==", + "dev": true + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "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.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "18.7.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.7.0.tgz", + "integrity": "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw==", + "dev": true, + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/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/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/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "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/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.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "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.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "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-fresh/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/import-modules": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-modules/-/import-modules-2.1.0.tgz", + "integrity": "sha512-8HEWcnkbGpovH9yInoisxaSoIg9Brbul+Ju3Kqe2UsYDUBJD/iQjSgEj0zPcTDPKfPp2fs5xlv1i+JSye/m1/A==", + "dev": true, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "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/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-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-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-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "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-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/istanbul-lib-processinfo/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/istanbul-lib-processinfo/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/istanbul-lib-report/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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": "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/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-5.4.0.tgz", + "integrity": "sha512-F9GQ+F1ZU6qvSrZV8fNFpjDNf614YzR2eF6S0+XbDjAcUI28FSoXnYZFjQmb1kFx3rrJb5PnxUH3/Yti6fcM+g==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/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/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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/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/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "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/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/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "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.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "dev": true + }, + "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/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/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/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/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/matcher/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, + "node_modules/memoize": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.1.0.tgz", + "integrity": "sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/micro-spelling-correcter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", + "integrity": "sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/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/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "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/multimatch/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/multimatch/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/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "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-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "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/nyc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", + "dev": true, + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^3.3.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^6.0.2", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nyc/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/nyc/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/nyc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/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/nyc/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/nyc/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/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/nyc/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nyc/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/nyc/node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nyc/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/nyc/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/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-deep-merge": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-1.0.5.tgz", + "integrity": "sha512-3DioFgOzetbxbeUq8pB2NunXo8V0n4EvqsWM/cJoI6IA9zghd7cl/2pBOuWRf4dlvA+fcg5ugFMZaN2/RuoaGg==", + "dev": true, + "dependencies": { + "type-fest": "4.2.0" + } + }, + "node_modules/object-deep-merge/node_modules/type-fest": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.2.0.tgz", + "integrity": "sha512-5zknd7Dss75pMSED270A1RQS3KloqRJA9XbXLe0eCxyw7xXFb3rd+9B0UQ/0E+LQT6lnrLviEolYORlRWamn4w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open-cli": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/open-cli/-/open-cli-8.0.0.tgz", + "integrity": "sha512-3muD3BbfLyzl+aMVSEfn2FfOqGdPYR0O4KNnxXsLEPE2q9OSjBfJAaB6XKbrUzLgymoSMejvb5jpXJfru/Ko2A==", + "dev": true, + "dependencies": { + "file-type": "^18.7.0", + "get-stdin": "^9.0.0", + "meow": "^12.1.1", + "open": "^10.0.0", + "tempy": "^3.1.0" + }, + "bin": { + "open-cli": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/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/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "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/parent-module/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/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "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-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true + }, + "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.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "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/peek-readable": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz", + "integrity": "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-dir/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/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "dev": true, + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/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/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "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/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/require-package-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", + "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==", + "dev": true + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "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-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", + "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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/sinon": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.5", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "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-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "dependencies": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/spawn-wrap/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/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/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/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true + }, + "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/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-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==", + "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/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/string-width-cjs/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/string-width-cjs/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/string-width/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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-ansi-cjs/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/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-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/strtok3": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.1.1.tgz", + "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.1.3" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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/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/tap-parser": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-1.2.2.tgz", + "integrity": "sha512-uXKcosa0qoSjeh73dhmX+OpJvpigDxUciOhBcbGUKtmwzEFJjUT1Ql5dpg4M9I1UjXT9b+6n1W05FB8QmKossA==", + "dev": true, + "dependencies": { + "events-to-array": "^1.0.1", + "inherits": "~2.0.1", + "js-yaml": "^3.2.7" + }, + "bin": { + "tap-parser": "bin/cmd.js" + }, + "optionalDependencies": { + "readable-stream": "^2" + } + }, + "node_modules/tap-parser/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, + "optional": 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/tap-parser/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, + "optional": true + }, + "node_modules/tap-parser/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, + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tap-xunit": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tap-xunit/-/tap-xunit-2.4.1.tgz", + "integrity": "sha512-qcZStDtjjYjMKAo7QNiCtOW256g3tuSyCSe5kNJniG1Q2oeOExJq4vm8CwboHZURpkXAHvtqMl4TVL7mcbMVVA==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "minimist": "~1.2.0", + "tap-parser": "~1.2.2", + "through2": "~2.0.0", + "xmlbuilder": "~4.2.0", + "xtend": "~4.0.0" + }, + "bin": { + "tap-xunit": "bin/tap-xunit", + "txunit": "bin/tap-xunit" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/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/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/test-exclude/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/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/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/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "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/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "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/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.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/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, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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.2.0", + "picocolors": "^1.1.1" + }, + "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": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/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==", + "dev": true + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/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/wrap-ansi/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/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": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha512-oEePiEefhQhAeUnwRnIBLBWmk/fsWWbQ53EEWsRuzECbQ3m5o/Esmq6H47CYYwSLW+Ynt0rS9hd0pd2ogMAWjg==", + "dev": true, + "dependencies": { + "lodash": "^4.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true + }, + "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/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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/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/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/packages/logger/package.json b/packages/logger/package.json new file mode 100644 index 00000000000..9da09bc45fa --- /dev/null +++ b/packages/logger/package.json @@ -0,0 +1,138 @@ +{ + "name": "@ui5/logger", + "version": "4.0.2", + "description": "UI5 CLI - Internal Logger", + "author": { + "name": "SAP SE", + "email": "openui5@sap.com", + "url": "https://www.sap.com" + }, + "license": "Apache-2.0", + "keywords": [ + "openui5", + "sapui5", + "ui5", + "build", + "development", + "tool" + ], + "type": "module", + "exports": { + ".": "./lib/index.js", + "./Logger": "./lib/loggers/Logger.js", + "./writers/*": "./lib/writers/*.js", + "./package.json": "./package.json", + "./internal/loggers/*": "./lib/loggers/*.js" + }, + "engines": { + "node": "^20.11.0 || >=22.0.0", + "npm": ">= 8" + }, + "scripts": { + "test": "npm run lint && npm run jsdoc-generate && npm run coverage && npm run depcheck", + "test-azure": "npm run coverage-xunit", + "lint": "eslint ./", + "unit": "rimraf test/tmp && ava", + "unit-verbose": "rimraf test/tmp && cross-env UI5_LOG_LVL=verbose ava --verbose --serial", + "unit-watch": "npm run unit -- --watch", + "unit-xunit": "rimraf test/tmp && ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\" --tap | tap-xunit --dontUseCommentsAsTestNames=true > test-results.xml", + "unit-inspect": "cross-env UI5_LOG_LVL=verbose ava debug --break", + "coverage": "rimraf test/tmp && nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\"", + "coverage-xunit": "nyc --reporter=text --reporter=text-summary --reporter=cobertura npm run unit-xunit", + "jsdoc": "npm run jsdoc-generate && open-cli jsdocs/index.html", + "jsdoc-generate": "jsdoc -c ./jsdoc.json -t $(node -p 'path.dirname(require.resolve(\"docdash\"))') ./lib/ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", + "jsdoc-watch": "npm run jsdoc && chokidar \"./lib/**/*.js\" -c \"npm run jsdoc-generate\"", + "preversion": "npm test", + "version": "git-chglog --sort semver --next-tag v$npm_package_version -o CHANGELOG.md v4.0.0.. && git add CHANGELOG.md", + "prepublishOnly": "git push --follow-tags", + "release-note": "git-chglog --sort semver -c .chglog/release-config.yml v$npm_package_version", + "depcheck": "depcheck --ignores @ui5/logger,rimraf,docdash,@istanbuljs/esm-loader-hook,cross-env" + }, + "files": [ + "CHANGELOG.md", + "CONTRIBUTING.md", + "jsdoc.json", + "lib/**", + "LICENSES/**", + ".reuse/**" + ], + "ava": { + "files": [ + "test/lib/**/*.js" + ], + "nodeArguments": [ + "--no-warnings" + ], + "workerThreads": false + }, + "nyc": { + "reporter": [ + "lcov", + "text", + "text-summary" + ], + "exclude": [ + "docs/**", + "jsdocs/**", + "coverage/**", + "test/**", + ".eslintrc.cjs", + "jsdoc-plugin.cjs" + ], + "check-coverage": true, + "statements": 90, + "branches": 90, + "functions": 90, + "lines": 90, + "watermarks": { + "statements": [ + 70, + 90 + ], + "branches": [ + 70, + 90 + ], + "functions": [ + 70, + 90 + ], + "lines": [ + 70, + 90 + ] + }, + "cache": true, + "all": true + }, + "repository": { + "type": "git", + "url": "git@github.com:SAP/ui5-logger.git" + }, + "dependencies": { + "chalk": "^5.6.2", + "cli-progress": "^3.12.0", + "figures": "^6.1.0" + }, + "devDependencies": { + "@eslint/js": "^9.8.0", + "@istanbuljs/esm-loader-hook": "^0.3.0", + "ava": "^6.4.1", + "chokidar-cli": "^3.0.0", + "cross-env": "^10.0.0", + "depcheck": "^1.4.7", + "docdash": "^2.0.2", + "eslint": "^9.36.0", + "eslint-config-google": "^0.14.0", + "eslint-plugin-ava": "^15.1.0", + "eslint-plugin-jsdoc": "^60.0.0", + "globals": "^16.4.0", + "jsdoc": "^4.0.4", + "nyc": "^17.1.0", + "open-cli": "^8.0.0", + "rimraf": "^6.0.1", + "sinon": "^21.0.0", + "strip-ansi": "^7.1.2", + "tap-xunit": "^2.4.1" + } +} diff --git a/packages/logger/test/lib/index.js b/packages/logger/test/lib/index.js new file mode 100644 index 00000000000..289593e3e42 --- /dev/null +++ b/packages/logger/test/lib/index.js @@ -0,0 +1,33 @@ +import test from "ava"; +import {getLogger, isLogLevelEnabled, setLogLevel, getLogLevel} from "../../lib/index.js"; +import Logger from "../../lib/loggers/Logger.js"; + +test.serial.afterEach.always((t) => { + delete process.env.UI5_LOG_LVL; +}); + +test.serial("getLogger", (t) => { + const myLogger = getLogger("my-module"); + t.true(myLogger instanceof Logger, "Returned logger should be Logger instance"); +}); + +test.serial("isLogLevelEnabled", (t) => { + // On info only info,warn,error,silent should be enabled + process.env.UI5_LOG_LVL = "info"; + t.false(isLogLevelEnabled("silly")); + t.false(isLogLevelEnabled("verbose")); + t.false(isLogLevelEnabled("perf")); + t.true(isLogLevelEnabled("info")); + t.true(isLogLevelEnabled("warn")); + t.true(isLogLevelEnabled("error")); + t.true(isLogLevelEnabled("silent")); +}); + +test.serial("setLevel", (t) => { + setLogLevel("silent"); + t.is(process.env.UI5_LOG_LVL, "silent", "Log level set correctly"); +}); + +test.serial("getLevel", (t) => { + t.is(getLogLevel(), "info", "Returned default log level"); +}); diff --git a/packages/logger/test/lib/loggers/Build.js b/packages/logger/test/lib/loggers/Build.js new file mode 100644 index 00000000000..bc4b7ecfe9f --- /dev/null +++ b/packages/logger/test/lib/loggers/Build.js @@ -0,0 +1,290 @@ +import test from "ava"; +import sinon from "sinon"; +import BuildLogger from "../../../lib/loggers/Build.js"; + +test.serial.beforeEach((t) => { + t.context.buildLogger = new BuildLogger("my:module:name"); + t.context.logStub = sinon.stub(t.context.buildLogger, "_log"); + + t.context.logHandler = sinon.stub(); + t.context.metadataHandler = sinon.stub(); + t.context.statusHandler = sinon.stub(); + process.on(BuildLogger.LOG_EVENT_NAME, t.context.logHandler); + process.on(BuildLogger.BUILD_METADATA_EVENT_NAME, t.context.metadataHandler); + process.on(BuildLogger.BUILD_STATUS_EVENT_NAME, t.context.statusHandler); + process.env.UI5_LOG_LVL = "silent"; // Should have no impact +}); + +test.serial.afterEach.always((t) => { + process.off(BuildLogger.LOG_EVENT_NAME, t.context.logHandler); + process.off(BuildLogger.BUILD_METADATA_EVENT_NAME, t.context.metadataHandler); + process.off(BuildLogger.BUILD_STATUS_EVENT_NAME, t.context.statusHandler); + delete process.env.UI5_LOG_LVL; + sinon.restore(); +}); + +test.serial("Correct build metadata event name", (t) => { + t.is(BuildLogger.BUILD_METADATA_EVENT_NAME, "ui5.build-metadata", "Correct build metadata event name exposed"); +}); + +test.serial("Correct build status event name", (t) => { + t.is(BuildLogger.BUILD_STATUS_EVENT_NAME, "ui5.build-status", "Correct build status event name exposed"); +}); + +test.serial("Log messages", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + + BuildLogger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + buildLogger[level]("Message 1"); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level, + message: "Message 1", + moduleName: "my:module:name", + }, "Emitted correct log event"); + logHandler.reset(); + }); + + t.is(metadataHandler.callCount, 0, "No build-metadata event emitted"); + t.is(statusHandler.callCount, 0, "No build-status event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("Set projects", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + buildLogger.setProjects(["project.a", "project.b"]); + + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.deepEqual(metadataHandler.getCall(0).args[0], { + projectsToBuild: ["project.a", "project.b"] + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(statusHandler.callCount, 0, "No build-status event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("Start project build", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + buildLogger.setProjects(["project.a"]); + + buildLogger.startProjectBuild("project.a", "project type"); + + t.is(statusHandler.callCount, 1, "One build-status event emitted"); + t.deepEqual(statusHandler.getCall(0).args[0], { + level: "info", + projectName: "project.a", + projectType: "project type", + status: "project-build-start", + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("End project build", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + buildLogger.setProjects(["project.a"]); + + buildLogger.endProjectBuild("project.a", "project type"); + + t.is(statusHandler.callCount, 1, "One build-status event emitted"); + t.deepEqual(statusHandler.getCall(0).args[0], { + level: "verbose", + projectName: "project.a", + projectType: "project type", + status: "project-build-end", + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("Skip project build", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + buildLogger.setProjects(["project.a"]); + + buildLogger.skipProjectBuild("project.a", "project type"); + + t.is(statusHandler.callCount, 1, "One build-status event emitted"); + t.deepEqual(statusHandler.getCall(0).args[0], { + level: "info", + projectName: "project.a", + projectType: "project type", + status: "project-build-skip", + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("No event listener: Set projects", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + process.off(BuildLogger.BUILD_METADATA_EVENT_NAME, metadataHandler); + buildLogger.setProjects(["project.a", "project.b"]); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(statusHandler.callCount, 0, "No build-status event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("No event listener: Start project build", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + process.off(BuildLogger.BUILD_STATUS_EVENT_NAME, statusHandler); + buildLogger.setProjects(["project.a"]); + + buildLogger.startProjectBuild("project.a", "project type"); + t.is(logStub.callCount, 1, "_log got called once"); + t.is(logStub.getCall(0).args[0], "info", "Logged with expected log-level"); + t.is(logStub.getCall(0).args[1], + "Building project type project project.a...", + "Logged expected message"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); +}); + +test.serial("No event listener: End project build", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + process.off(BuildLogger.BUILD_STATUS_EVENT_NAME, statusHandler); + buildLogger.setProjects(["project.a"]); + + buildLogger.endProjectBuild("project.a", "project type"); + t.is(logStub.callCount, 1, "_log got called once"); + t.is(logStub.getCall(0).args[0], "verbose", "Logged with expected log-level"); + t.is(logStub.getCall(0).args[1], + "Finished building project type project project.a", + "Logged expected message"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); +}); + +test.serial("No event listener: Skip project build", (t) => { + const {buildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + process.off(BuildLogger.BUILD_STATUS_EVENT_NAME, statusHandler); + buildLogger.setProjects(["project.a"]); + + buildLogger.skipProjectBuild("project.a", "project type"); + t.is(logStub.callCount, 1, "_log got called once"); + t.is(logStub.getCall(0).args[0], "info", "Logged with expected log-level"); + t.is(logStub.getCall(0).args[1], + "Skipping build of project type project project.a", + "Logged expected message"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); +}); + +test.serial("Set projects: Missing parameter", (t) => { + const {buildLogger} = t.context; + + t.throws(() => { + buildLogger.setProjects(); + }, { + message: `loggers/Build#setProjects: Missing or incorrect projects parameter` + }, "Threw with expected error message"); + + t.throws(() => { + buildLogger.setProjects(new Set("no array")); + }, { + message: `loggers/Build#setProjects: Missing or incorrect projects parameter` + }, "Threw with expected error message"); +}); + +test.serial("Start project build: Unknown project", (t) => { + const {buildLogger} = t.context; + + // Throws because no projects are set + t.throws(() => { + buildLogger.startProjectBuild("project.x", "application"); + }, { + message: `loggers/Build#startProjectBuild: Unknown project project.x` + }, "Threw with expected error message"); + + buildLogger.setProjects(["project.a"]); + // Throws because given project is unknown + t.throws(() => { + buildLogger.startProjectBuild("project.x", "application"); + }, { + message: `loggers/Build#startProjectBuild: Unknown project project.x` + }, "Threw with expected error message"); +}); + +test.serial("End project build: Unknown project", (t) => { + const {buildLogger} = t.context; + + // Throws because no projects are set + t.throws(() => { + buildLogger.endProjectBuild("project.x", "application"); + }, { + message: `loggers/Build#endProjectBuild: Unknown project project.x` + }, "Threw with expected error message"); + + buildLogger.setProjects(["project.a"]); + // Throws because given project is unknown + t.throws(() => { + buildLogger.endProjectBuild("project.x", "application"); + }, { + message: `loggers/Build#endProjectBuild: Unknown project project.x` + }, "Threw with expected error message"); +}); + +test.serial("Skip project build: Unknown project", (t) => { + const {buildLogger} = t.context; + + // Throws because no projects are set + t.throws(() => { + buildLogger.skipProjectBuild("project.x", "application"); + }, { + message: `loggers/Build#skipProjectBuild: Unknown project project.x` + }, "Threw with expected error message"); + + buildLogger.setProjects(["project.a"]); + // Throws because given project is unknown + t.throws(() => { + buildLogger.skipProjectBuild("project.x", "application"); + }, { + message: `loggers/Build#skipProjectBuild: Unknown project project.x` + }, "Threw with expected error message"); +}); + +test.serial("Start project build: Missing projectType parameter", (t) => { + const {buildLogger} = t.context; + + buildLogger.setProjects(["project.a"]); + t.throws(() => { + buildLogger.startProjectBuild("project.a"); + }, { + message: `loggers/Build#startProjectBuild: Missing projectType parameter` + }, "Threw with expected error message"); +}); + +test.serial("End project build: Missing projectType parameter", (t) => { + const {buildLogger} = t.context; + + buildLogger.setProjects(["project.a"]); + t.throws(() => { + buildLogger.endProjectBuild("project.a"); + }, { + message: `loggers/Build#endProjectBuild: Missing projectType parameter` + }, "Threw with expected error message"); +}); + +test.serial("Skip project build: Missing projectType parameter", (t) => { + const {buildLogger} = t.context; + + buildLogger.setProjects(["project.a"]); + t.throws(() => { + buildLogger.skipProjectBuild("project.a"); + }, { + message: `loggers/Build#skipProjectBuild: Missing projectType parameter` + }, "Threw with expected error message"); +}); diff --git a/packages/logger/test/lib/loggers/Logger.js b/packages/logger/test/lib/loggers/Logger.js new file mode 100644 index 00000000000..2fa13d07e70 --- /dev/null +++ b/packages/logger/test/lib/loggers/Logger.js @@ -0,0 +1,245 @@ +import test from "ava"; +import sinon from "sinon"; +import Logger from "../../../lib/loggers/Logger.js"; + +test.serial.beforeEach((t) => { + t.context.logHandler = sinon.stub(); + t.context.stderrWriteStub = sinon.stub(process.stderr, "write"); + process.on(Logger.LOG_EVENT_NAME, t.context.logHandler); +}); + +test.serial.afterEach.always((t) => { + process.off(Logger.LOG_EVENT_NAME, t.context.logHandler); + delete process.env.UI5_LOG_LVL; + sinon.restore(); +}); + +test.serial("isLevelEnabled", (t) => { + // On silly all levels should be enabled + process.env.UI5_LOG_LVL = "silly"; + t.true(Logger.isLevelEnabled("silly")); + t.true(Logger.isLevelEnabled("verbose")); + t.true(Logger.isLevelEnabled("perf")); + t.true(Logger.isLevelEnabled("info")); + t.true(Logger.isLevelEnabled("warn")); + t.true(Logger.isLevelEnabled("error")); + t.true(Logger.isLevelEnabled("silent")); + + // On silent only silent should be enabled + // (which does not allow any logging) + process.env.UI5_LOG_LVL = "silent"; + t.false(Logger.isLevelEnabled("silly")); + t.false(Logger.isLevelEnabled("verbose")); + t.false(Logger.isLevelEnabled("perf")); + t.false(Logger.isLevelEnabled("info")); + t.false(Logger.isLevelEnabled("warn")); + t.false(Logger.isLevelEnabled("error")); + t.true(Logger.isLevelEnabled("silent")); + + // On info only info,warn,error,silent should be enabled + process.env.UI5_LOG_LVL = "info"; + t.false(Logger.isLevelEnabled("silly")); + t.false(Logger.isLevelEnabled("verbose")); + t.false(Logger.isLevelEnabled("perf")); + t.true(Logger.isLevelEnabled("info")); + t.true(Logger.isLevelEnabled("warn")); + t.true(Logger.isLevelEnabled("error")); + t.true(Logger.isLevelEnabled("silent")); + + t.throws(() => { + Logger.isLevelEnabled("all"); + }, {message: `Unknown log level "all"`}); + + process.env.UI5_LOG_LVL = "unknown level"; + t.throws(() => { + Logger.isLevelEnabled("warn"); + }, {message: `UI5 Logger: Environment variable UI5_LOG_LVL is set to an unknown log level "unknown level". ` + + `Valid levels are silly, verbose, perf, info, warn, error, silent`}); +}); + +test.serial("Correct log event name", (t) => { + t.is(Logger.LOG_EVENT_NAME, "ui5.log", "Correct log event name exposed"); +}); + +test.serial("Setting all log levels via API", (t) => { + const levels = ["silly", "verbose", "perf", "info", "warn", "error", "silent"]; + + for (const level of levels) { + Logger.setLevel(level); + t.is(process.env.UI5_LOG_LVL, level, "Environment variable updated correctly"); + t.true(Logger.isLevelEnabled(level), `Set level ${level} should be enabled`); + } +}); + +test.serial("Setting all log levels via environment variable)", (t) => { + const levels = ["silly", "verbose", "perf", "info", "warn", "error", "silent"]; + + for (const level of levels) { + process.env.UI5_LOG_LVL = level; + t.true(Logger.isLevelEnabled(level), `Set level ${level} should be enabled`); + } +}); + +test.serial("Missing parameter: module name", (t) => { + t.throws(() => { + new Logger(); + }, { + message: "Logger: Missing moduleName parameter" + }, "Threw with expected error message"); +}); + +test.serial("Legal module names", (t) => { + function testNotThrows(moduleName) { + t.notThrows(() => { + new Logger(moduleName); + }, "Did not throw"); + } + testNotThrows("Module:Name"); + testNotThrows("@module"); + testNotThrows("m0-dule_.nAme"); + testNotThrows("module/name"); +}); + +test.serial("Illegal module names", (t) => { + function testThrows(moduleName) { + t.throws(() => { + new Logger(moduleName); + }, { + message: `Logger: Invalid module name: ${moduleName}` + }, "Threw with expected error message"); + } + testThrows("module name"); + testThrows("module\\name"); + testThrows("näme"); + testThrows("🧗"); +}); + +test.serial("Log messages", (t) => { + const {logHandler, stderrWriteStub} = t.context; + const myLogger = new Logger("my:module:name"); + + // Log level does not influence events + process.env.UI5_LOG_LVL = "error"; + + Logger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + myLogger[level]("Message 1"); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level, + message: "Message 1", + moduleName: "my:module:name", + }, "Emitted correct log event"); + logHandler.reset(); + }); + + t.is(stderrWriteStub.callCount, 0, "stderr.write was never called"); +}); + +test.serial("Log object", (t) => { + const {logHandler} = t.context; + const myLogger = new Logger("my:module:name"); + + myLogger.error({ + this: { + is: { + a: { + deep: [ + "object" + ] + } + } + } + }); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level: "error", + message: `{ + this: { + is: { a: { deep: [Array] } } + } +}`, + moduleName: "my:module:name", + }, "Emitted correct log event"); +}); + +test.serial("Log other non-strings", (t) => { + const {logHandler} = t.context; + const myLogger = new Logger("my:module:name"); + + function compareLog(value, expectdMessage) { + myLogger.info(value); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level: "info", + message: expectdMessage, + moduleName: "my:module:name", + }, "Emitted correct log event"); + logHandler.reset(); + } + + compareLog(undefined, "undefined"); + compareLog(null, "null"); + compareLog(0, "0"); +}); + +test.serial("Log multiple arguments", (t) => { + const {logHandler} = t.context; + const myLogger = new Logger("my:module:name"); + + // eslint-disable-next-line no-new-wrappers + myLogger.info("This", new String("is"), "a", ["test"]); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level: "info", + message: `This is a [ 'test' ]`, + moduleName: "my:module:name", + }, "Emitted correct log event"); +}); + +test.serial("No string substitution", (t) => { + const {logHandler} = t.context; + const myLogger = new Logger("my:module:name"); + + myLogger.info("Some message %s with a placeholder", "project.a"); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level: "info", + + // Placeholder not replaced + message: `Some message %s with a placeholder project.a`, + moduleName: "my:module:name", + }, "Emitted correct log event"); +}); + +test.serial("No event listener", (t) => { + const {logHandler, stderrWriteStub} = t.context; + const myLogger = new Logger("my:module:name"); + process.off(Logger.LOG_EVENT_NAME, logHandler); + + myLogger.info("Message 1"); + myLogger.verbose("Message 2"); // Not logged for default log-level "info" + myLogger.error("Message 3"); + t.is(stderrWriteStub.callCount, 2, "stderr.write got called twice"); + t.is(stderrWriteStub.getCall(0).args[0], "[info] my:module:name: Message 1\n", "Logged expected message"); + t.is(stderrWriteStub.getCall(1).args[0], "[error] my:module:name: Message 3\n", "Logged expected message"); +}); + +test.serial("No logging for silent level", (t) => { + const {logHandler, stderrWriteStub} = t.context; + const myLogger = new Logger("my:module:name"); + process.off(Logger.LOG_EVENT_NAME, logHandler); // Remove listener to get direct console logging + process.env.UI5_LOG_LVL = "silent"; + + Logger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + myLogger[level]("Message"); + }); + t.is(stderrWriteStub.callCount, 0, "stderr.write never got called"); +}); diff --git a/packages/logger/test/lib/loggers/ProjectBuild.js b/packages/logger/test/lib/loggers/ProjectBuild.js new file mode 100644 index 00000000000..a22506cf3d9 --- /dev/null +++ b/packages/logger/test/lib/loggers/ProjectBuild.js @@ -0,0 +1,230 @@ +import test from "ava"; +import sinon from "sinon"; +import ProjectBuildLogger from "../../../lib/loggers/ProjectBuild.js"; + +test.serial.beforeEach((t) => { + t.context.projectBuildLogger = new ProjectBuildLogger({ + moduleName: "my:module:name", + projectName: "projectName", + projectType: "projectType", + }); + t.context.logStub = sinon.stub(t.context.projectBuildLogger, "_log"); + + t.context.logHandler = sinon.stub(); + t.context.metadataHandler = sinon.stub(); + t.context.statusHandler = sinon.stub(); + process.on(ProjectBuildLogger.LOG_EVENT_NAME, t.context.logHandler); + process.on(ProjectBuildLogger.PROJECT_BUILD_METADATA_EVENT_NAME, t.context.metadataHandler); + process.on(ProjectBuildLogger.PROJECT_BUILD_STATUS_EVENT_NAME, t.context.statusHandler); + process.env.UI5_LOG_LVL = "silent"; // Should have no impact +}); + +test.serial.afterEach.always((t) => { + process.off(ProjectBuildLogger.LOG_EVENT_NAME, t.context.logHandler); + process.off(ProjectBuildLogger.PROJECT_BUILD_METADATA_EVENT_NAME, t.context.metadataHandler); + process.off(ProjectBuildLogger.PROJECT_BUILD_STATUS_EVENT_NAME, t.context.statusHandler); + delete process.env.UI5_LOG_LVL; + sinon.restore(); +}); + +test.serial("Correct build metadata event name", (t) => { + t.is(ProjectBuildLogger.PROJECT_BUILD_METADATA_EVENT_NAME, "ui5.project-build-metadata", + "Correct build metadata event name exposed"); +}); + +test.serial("Correct build status event name", (t) => { + t.is(ProjectBuildLogger.PROJECT_BUILD_STATUS_EVENT_NAME, "ui5.project-build-status", + "Correct build status event name exposed"); +}); + +test.serial("Missing parameters", (t) => { + t.throws(() => { + new ProjectBuildLogger({}); + }, { + message: "Logger: Missing moduleName parameter" + }, "Threw with expected error message"); + + t.throws(() => { + new ProjectBuildLogger({ + moduleName: "module:name", + projectName: "projectName", + }); + }, { + message: "loggers/ProjectBuild: Missing projectType parameter" + }, "Threw with expected error message"); + + t.throws(() => { + new ProjectBuildLogger({ + moduleName: "module:name", + projectType: "projectType", + }); + }, { + message: "loggers/ProjectBuild: Missing projectName parameter" + }, "Threw with expected error message"); +}); + +test.serial("Log messages", (t) => { + const {projectBuildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + + ProjectBuildLogger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + projectBuildLogger[level]("Message 1"); + t.is(logHandler.callCount, 1, "Emitted and captured one log event"); + t.deepEqual(logHandler.getCall(0).args[0], { + level, + message: "Message 1", + moduleName: "my:module:name", + }, "Emitted correct log event"); + logHandler.reset(); + }); + + t.is(metadataHandler.callCount, 0, "No build-metadata event emitted"); + t.is(statusHandler.callCount, 0, "No build-status event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("Set tasks", (t) => { + const {projectBuildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + projectBuildLogger.setTasks(["task.a", "task.b"]); + + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.deepEqual(metadataHandler.getCall(0).args[0], { + projectName: "projectName", + projectType: "projectType", + tasksToRun: ["task.a", "task.b"] + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(statusHandler.callCount, 0, "No build-status event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("Start task", (t) => { + const {projectBuildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + projectBuildLogger.setTasks(["task.a"]); + + projectBuildLogger.startTask("task.a"); + + t.is(statusHandler.callCount, 1, "One build-status event emitted"); + t.deepEqual(statusHandler.getCall(0).args[0], { + level: "info", + projectName: "projectName", + projectType: "projectType", + status: "task-start", + taskName: "task.a", + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("End task", (t) => { + const {projectBuildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + projectBuildLogger.setTasks(["task.a"]); + + projectBuildLogger.endTask("task.a"); + + t.is(statusHandler.callCount, 1, "One build-status event emitted"); + t.deepEqual(statusHandler.getCall(0).args[0], { + level: "verbose", + projectName: "projectName", + projectType: "projectType", + status: "task-end", + taskName: "task.a", + }, "Metadata event has expected payload"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); + t.is(logStub.callCount, 0, "_log was never called"); +}); + +test.serial("No event listener: Start task", (t) => { + const {projectBuildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + process.off(ProjectBuildLogger.PROJECT_BUILD_STATUS_EVENT_NAME, statusHandler); + projectBuildLogger.setTasks(["task.a"]); + + projectBuildLogger.startTask("task.a"); + t.is(logStub.callCount, 1, "_log got called once"); + t.is(logStub.getCall(0).args[0], "info", "Logged with expected log-level"); + t.is(logStub.getCall(0).args[1], + "projectName: Running task task.a...", + "Logged expected message"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); +}); + +test.serial("No event listener: End task", (t) => { + const {projectBuildLogger, logHandler, metadataHandler, statusHandler, logStub} = t.context; + process.off(ProjectBuildLogger.PROJECT_BUILD_STATUS_EVENT_NAME, statusHandler); + projectBuildLogger.setTasks(["task.a"]); + + projectBuildLogger.endTask("task.a"); + t.is(logStub.callCount, 1, "_log got called once"); + t.is(logStub.getCall(0).args[0], "verbose", "Logged with expected log-level"); + t.is(logStub.getCall(0).args[1], + "projectName: Finished task task.a", + "Logged expected message"); + + t.is(logHandler.callCount, 0, "No log event emitted"); + t.is(metadataHandler.callCount, 1, "One build-metadata event emitted"); +}); + +test.serial("Set tasks: Missing parameter", (t) => { + const {projectBuildLogger} = t.context; + + t.throws(() => { + projectBuildLogger.setTasks(); + }, { + message: `loggers/ProjectBuild#setTasks: Missing or incorrect tasks parameter` + }, "Threw with expected error message"); + + t.throws(() => { + projectBuildLogger.setTasks(new Set("no array")); + }, { + message: `loggers/ProjectBuild#setTasks: Missing or incorrect tasks parameter` + }, "Threw with expected error message"); +}); + +test.serial("Start task: Unknown task", (t) => { + const {projectBuildLogger} = t.context; + + // Throws because no projects are set + t.throws(() => { + projectBuildLogger.startTask("task.x"); + }, { + message: `loggers/ProjectBuild#startTask: Unknown task task.x` + }, "Threw with expected error message"); + + projectBuildLogger.setTasks(["task.a"]); + // Throws because given project is unknown + t.throws(() => { + projectBuildLogger.startTask("task.x"); + }, { + message: `loggers/ProjectBuild#startTask: Unknown task task.x` + }, "Threw with expected error message"); +}); + +test.serial("End task: Unknown task", (t) => { + const {projectBuildLogger} = t.context; + + // Throws because no projects are set + t.throws(() => { + projectBuildLogger.endTask("task.x"); + }, { + message: `loggers/ProjectBuild#endTask: Unknown task task.x` + }, "Threw with expected error message"); + + projectBuildLogger.setTasks(["task.a"]); + // Throws because given project is unknown + t.throws(() => { + projectBuildLogger.endTask("task.x"); + }, { + message: `loggers/ProjectBuild#endTask: Unknown task task.x` + }, "Threw with expected error message"); +}); + diff --git a/packages/logger/test/lib/package-exports.js b/packages/logger/test/lib/package-exports.js new file mode 100644 index 00000000000..4eb43349a8c --- /dev/null +++ b/packages/logger/test/lib/package-exports.js @@ -0,0 +1,45 @@ +import test from "ava"; +import {createRequire} from "node:module"; + +// Using CommonsJS require since JSON module imports are still experimental +const require = createRequire(import.meta.url); + +// package.json should be exported to allow reading version (e.g. from @ui5/cli) +test("export of package.json", (t) => { + t.truthy(require("@ui5/logger/package.json").version); +}); + +// Check number of definied exports +test("check number of exports", (t) => { + const packageJson = require("@ui5/logger/package.json"); + t.is(Object.keys(packageJson.exports).length, 5); +}); + +// Public API contract (exported modules) +[ + {exportedSpecifier: "Logger", mappedModule: "../../lib/loggers/Logger.js"}, + "writers/Console", + + // Internal modules (only to be used by @ui5/* packages) + {exportedSpecifier: "internal/loggers/Logger", mappedModule: "../../lib/loggers/Logger.js"}, + {exportedSpecifier: "internal/loggers/Build", mappedModule: "../../lib/loggers/Build.js"}, + {exportedSpecifier: "internal/loggers/ProjectBuild", mappedModule: "../../lib/loggers/ProjectBuild.js"}, +].forEach((v) => { + let exportedSpecifier; let mappedModule; + if (typeof v === "string") { + exportedSpecifier = v; + } else { + exportedSpecifier = v.exportedSpecifier; + mappedModule = v.mappedModule; + } + if (!mappedModule) { + mappedModule = `../../lib/${exportedSpecifier}.js`; + } + const spec = `@ui5/logger/${exportedSpecifier}`; + test(`${spec}`, async (t) => { + const actual = await import(spec); + const expected = await import(mappedModule); + t.is(actual, expected, "Correct module exported"); + }); +}); + diff --git a/packages/logger/test/lib/writers/Console.integration.js b/packages/logger/test/lib/writers/Console.integration.js new file mode 100644 index 00000000000..37f9a9a498b --- /dev/null +++ b/packages/logger/test/lib/writers/Console.integration.js @@ -0,0 +1,343 @@ +import test from "ava"; +import sinon from "sinon"; +import stripAnsi from "strip-ansi"; +import figures from "figures"; +import ConsoleWriter from "../../../lib/writers/Console.js"; +import Logger from "../../../lib/loggers/Logger.js"; +import BuildLogger from "../../../lib/loggers/Build.js"; +import ProjectBuildLogger from "../../../lib/loggers/ProjectBuild.js"; + +test.serial.beforeEach((t) => { + t.context.consoleWriter = ConsoleWriter.init(); + t.context.stderrWriteStub = sinon.stub(process.stderr, "write"); + t.context.originalIsTty = process.stderr.isTTY; + process.env.UI5_LOG_LVL = "silly"; +}); + +test.serial.afterEach.always((t) => { + t.context.consoleWriter.disable(); + sinon.restore(); + process.stderr.isTTY = t.context.originalIsTty; + delete process.env.UI5_LOG_LVL; +}); + +async function findMessageInProgressBarLog(t, indicator) { + const {stderrWriteStub} = t.context; + + await new Promise((resolve) => { + // Wait for progress bar to flush log buffer + setTimeout(resolve, 20); + }); + + const allWriteCalls = stderrWriteStub.getCalls(); + t.true(allWriteCalls.length > 2, "Multiple write calls indicate progress bar is active"); + + const call = allWriteCalls.find((call) => { + return call.firstArg.includes(indicator); + }); + return call && call.firstArg; +} + +test.serial("Log standard messages", (t) => { + const {stderrWriteStub} = t.context; + const myLogger = new Logger("my:module"); + + Logger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + myLogger[level]("Message 1"); + if (level === "verbose") { + // "verbose" is abbreviated + level = "verb"; + } + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), `${level} my:module Message 1\n`, + "Logged expected message"); + stderrWriteStub.resetHistory(); + }); +}); + +test.serial("Log restricted by log level setting", (t) => { + const {stderrWriteStub} = t.context; + const myLogger = new Logger("my:module"); + + process.env.UI5_LOG_LVL = "info"; + + myLogger.verbose("Message 1"); + t.is(stderrWriteStub.callCount, 0, "Logged no message"); +}); + +test.serial("Log Build messages", (t) => { + const {stderrWriteStub} = t.context; + const myLogger = new BuildLogger("Builder"); + + BuildLogger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + myLogger[level]("Message 1"); + if (level === "verbose") { + // "verbose" is abbreviated + level = "verb"; + } + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), `${level} Builder Message 1\n`, + "Logged expected message"); + stderrWriteStub.resetHistory(); + }); +}); + +test.serial("Log Build status", (t) => { + const {stderrWriteStub} = t.context; + const myLogger = new BuildLogger("Builder"); + + myLogger.setProjects(["project.a", "project.b"]); + myLogger.skipProjectBuild("project.a", "project-type"); + myLogger.startProjectBuild("project.b", "project-type"); + myLogger.endProjectBuild("project.b", "project-type"); + + t.is(stderrWriteStub.callCount, 3, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info Project 1 of 2: ${figures.tick} Skipping build of project-type project project.a\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(1).args[0]), + `info Project 2 of 2: ${figures.pointer} Building project-type project project.b...\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(2).args[0]), + `verb Project 2 of 2: ${figures.tick} Finished building project-type project project.b\n`, + "Logged expected message"); +}); +test.serial("Build status log restricted by log level", (t) => { + const {stderrWriteStub} = t.context; + const myLogger = new BuildLogger("Builder"); + + process.env.UI5_LOG_LVL = "silent"; + + myLogger.setProjects(["project.a", "project.b"]); + myLogger.skipProjectBuild("project.a", "project-type"); + myLogger.startProjectBuild("project.b", "project-type"); + myLogger.endProjectBuild("project.b", "project-type"); + + t.is(stderrWriteStub.callCount, 0, "Logged zero message"); +}); + +test.serial("Log ProjectBuild messages", (t) => { + const {stderrWriteStub} = t.context; + const myLogger = new ProjectBuildLogger({ + moduleName: "project:build", + projectName: "project.name", + projectType: "project-type" + }); + + ProjectBuildLogger.LOG_LEVELS.forEach((level) => { + if (level === "silent") { + // Can't log silent messages + return; + } + myLogger[level]("Message 1"); + if (level === "verbose") { + // "verbose" is abbreviated + level = "verb"; + } + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), `${level} project:build Message 1\n`, + "Logged expected message"); + stderrWriteStub.resetHistory(); + }); +}); + +test.serial("Log ProjectBuild status", (t) => { + const {stderrWriteStub} = t.context; + + // Make ConsoleWriter aware of the project first + const buildLogger = new BuildLogger("Builder"); + buildLogger.setProjects(["project.a"]); + + const myLogger = new ProjectBuildLogger({ + moduleName: "build:module", + projectName: "project.a", + projectType: "project-type" + }); + + myLogger.setTasks(["task.a", "task.b"]); + myLogger.startTask("task.a"); + myLogger.endTask("task.a"); + myLogger.startTask("task.b"); + myLogger.endTask("task.b"); + + t.is(stderrWriteStub.callCount, 4, "Logged four messages"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info project.a Task 1 of 2 › Running task task.a...\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(1).args[0]), + `verb project.a Task 1 of 2 ${figures.tick} Finished task task.a\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(2).args[0]), + `info project.a Task 2 of 2 › Running task task.b...\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(3).args[0]), + `verb project.a Task 2 of 2 ${figures.tick} Finished task task.b\n`, + "Logged expected message"); +}); + +test.serial("ProjectBuild status restricted by log level", (t) => { + const {stderrWriteStub} = t.context; + + // Make ConsoleWriter aware of the project first + const buildLogger = new BuildLogger("Builder"); + buildLogger.setProjects(["project.a"]); + + const myLogger = new ProjectBuildLogger({ + moduleName: "build:module", + projectName: "project.a", + projectType: "project-type" + }); + + process.env.UI5_LOG_LVL = "silent"; + + myLogger.setTasks(["task.a", "task.b"]); + myLogger.startTask("task.a"); + myLogger.endTask("task.a"); + myLogger.startTask("task.b"); + myLogger.endTask("task.b"); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Log through progress bar", async (t) => { + const buildLogger = new BuildLogger("Builder"); + + process.env.UI5_LOG_LVL = "info"; + + // Force TTY in test env to enable progress bar + process.stderr.isTTY = true; + + buildLogger.setProjects(["project.a", "project.b"]); + const myLogger = new Logger("my:module"); + myLogger.info("Message 1"); + + const msg = await findMessageInProgressBarLog(t, "Message 1"); + t.truthy(msg, "Logged expected message"); + t.is(stripAnsi(msg), + `info my:module Message 1\n`, + "Logged expected message"); +}); + +test.serial("Do not use progress bar for log level verbose", async (t) => { + const {stderrWriteStub} = t.context; + const buildLogger = new BuildLogger("Builder"); + + process.env.UI5_LOG_LVL = "verbose"; + + // Forced TTY would enable progress bar, however verbose log level disables it again + process.stderr.isTTY = true; + + buildLogger.setProjects(["project.a", "project.b"]); + const myLogger = new Logger("my:module"); + myLogger.info("Message 1"); + + await new Promise((resolve) => { + setTimeout(resolve, 20); + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one messages"); + t.is(stripAnsi(stderrWriteStub.firstCall.firstArg), + `info my:module Message 1\n`, + "Logged expected message"); +}); + +test.serial("Return to direct logging once progress bar stopped", async (t) => { + const {consoleWriter, stderrWriteStub} = t.context; + const buildLogger = new BuildLogger("Builder"); + + process.env.UI5_LOG_LVL = "info"; + + // Force TTY in test env to enable progress bar + process.stderr.isTTY = true; + + buildLogger.setProjects(["project.a", "project.b"]); + const pb = consoleWriter._getProgressBar(); + + t.true(pb.isActive, "Progress bar is active"); + + // Complete all projects (note that here are no tasks to complete) + buildLogger.skipProjectBuild("project.a", "project-type"); + buildLogger.startProjectBuild("project.b", "project-type"); + buildLogger.endProjectBuild("project.b", "project-type"); + + t.false(pb.isActive, "Progress bar is not active anymore"); + + // Wait for async progress bar completion (also fires stop event) + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); + + // Clear stub history + stderrWriteStub.resetHistory(); + + // Log a normal message + const myLogger = new Logger("my:module"); + myLogger.info("Message 1"); + + t.is(stderrWriteStub.callCount, 1, "Logged one messages"); + t.is(stripAnsi(stderrWriteStub.firstCall.firstArg), + `info my:module Message 1\n`, + "Logged expected message"); +}); + +test.serial("Progress bar progress", (t) => { + const {consoleWriter} = t.context; + const buildLogger = new BuildLogger("Builder"); + const projectBuildLoggerA = new ProjectBuildLogger({ + moduleName: "ProjectBuilder", + projectName: "project.a", + projectType: "project-type", + }); + const projectBuildLoggerB = new ProjectBuildLogger({ + moduleName: "ProjectBuilder", + projectName: "project.b", + projectType: "project-type", + }); + + process.env.UI5_LOG_LVL = "info"; + + // Force TTY in test env to enable progress bar + process.stderr.isTTY = true; + + const pb = consoleWriter._getProgressBar(); + buildLogger.setProjects(["project.a", "project.b"]); + t.is(pb.getTotal(), 4, "Correct progress total after setting projects"); + t.is(pb.getProgress(), 0, "No progress after setting projects to build"); + + // Set tasks + projectBuildLoggerA.setTasks(["task.a", "task.b", "task.c"]); + t.is(pb.getTotal(), 7, "Correct progress total after setting tasks"); + projectBuildLoggerB.setTasks(["task.a", "task.b", "task.c"]); + t.is(pb.getTotal(), 10, "Correct progress total after setting tasks"); + t.is(pb.getProgress(), 0, "No progress after setting all projects and tasks"); + + // Complete all projects + buildLogger.skipProjectBuild("project.a", "project-type"); + t.is(pb.getProgress(), 0.5, "Correct progress after skipping project.a and all it's tasks"); + + buildLogger.startProjectBuild("project.b", "project-type"); + t.is(pb.getProgress(), 0.5, "Unchanged progress after starting project.b"); + + projectBuildLoggerB.startTask("task.a"); + projectBuildLoggerB.endTask("task.a"); + t.is(pb.getProgress(), 0.6, "Correct progress after ending task.a"); + + projectBuildLoggerB.startTask("task.b"); + projectBuildLoggerB.endTask("task.b"); + t.is(pb.getProgress(), 0.7, "Correct progress after ending task.b"); + + projectBuildLoggerB.startTask("task.c"); + projectBuildLoggerB.endTask("task.c"); + t.is(pb.getProgress(), 0.8, "Correct progress after ending task.c"); + + buildLogger.endProjectBuild("project.b", "project-type"); + t.is(pb.getProgress(), 1, "Correct progress after starting project.b"); +}); diff --git a/packages/logger/test/lib/writers/Console.js b/packages/logger/test/lib/writers/Console.js new file mode 100644 index 00000000000..204be01d96c --- /dev/null +++ b/packages/logger/test/lib/writers/Console.js @@ -0,0 +1,969 @@ +import test from "ava"; +import sinon from "sinon"; +import stripAnsi from "strip-ansi"; +import figures from "figures"; +import ConsoleWriter from "../../../lib/writers/Console.js"; + +test.serial.beforeEach((t) => { + t.context.consoleWriter = ConsoleWriter.init(); + t.context.stderrWriteStub = sinon.stub(process.stderr, "write"); + t.context.originalIsTty = process.stderr.isTTY; + process.env.UI5_LOG_LVL = "info"; +}); + +test.serial.afterEach.always((t) => { + t.context.consoleWriter.disable(); + sinon.restore(); + process.stderr.isTTY = t.context.originalIsTty; + delete process.env.UI5_LOG_LVL; +}); + +test.serial("Log event", (t) => { + const {stderrWriteStub} = t.context; + + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), `info my:module Message 1\n`, + "Logged expected message"); +}); + +test.serial("Enable", (t) => { + const {stderrWriteStub} = t.context; + + // Disable and overwrite writer created in beforeEach hook + t.context.consoleWriter.disable(); + const consoleWriter = t.context.consoleWriter = new ConsoleWriter(); + + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message after creation"); + + consoleWriter.enable(); + + process.emit("ui5.log", { + level: "info", + message: "Message 2", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message after being enabled"); +}); + +test.serial("Disable", (t) => { + const {consoleWriter, stderrWriteStub} = t.context; + consoleWriter.disable(); + + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message"); +}); + +test.serial("Disable + Enable", (t) => { + const {consoleWriter, stderrWriteStub} = t.context; + consoleWriter.disable(); + + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message"); + + consoleWriter.enable(); + process.emit("ui5.log", { + level: "info", + message: "Message 2", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 1, "Logged no message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info my:module Message 2\n`, + "Logged expected message"); +}); + +test.serial("Stop", (t) => { + const {stderrWriteStub} = t.context; + + ConsoleWriter.stop(); + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message"); +}); + +test.serial("Stop disables all instances", (t) => { + const {stderrWriteStub} = t.context; + + ConsoleWriter.init(); + ConsoleWriter.init(); + + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 3, "Logged three message"); + stderrWriteStub.resetHistory(); + + ConsoleWriter.stop(); + + process.emit("ui5.log", { + level: "info", + message: "Message 2", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message"); +}); + +test.serial("Logging restricted by log level setting", (t) => { + const {stderrWriteStub} = t.context; + + process.emit("ui5.log", { + level: "verbose", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message"); +}); + +test.serial("Logging with unknown log level", (t) => { + const {stderrWriteStub} = t.context; + + t.throws(() => { + process.emit("ui5.log", { + level: "foo", + message: "Message 1", + moduleName: "my:module" + }); + }, { + message: `Unknown log level "foo"` + }); + + t.is(stderrWriteStub.callCount, 0, "Logged no message"); +}); + +test.serial("Build status events", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "verbose", // Should not get logged + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info Project 1 of 1: ${figures.tick} Finished building project-type project project.a\n`, + "Logged expected message"); +}); + +test.serial("Build status: Unknown project", (t) => { + const {stderrWriteStub} = t.context; + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + }, { + message: "writers/Console: Unknown project project.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (start): Duplicate project build start", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + }, { + message: "writers/Console: Unexpected duplicate project-build-start event for project project.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (start): Project build already ended", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + }, { + message: + "writers/Console: Unexpected project-build-start event for project project.a. Project build already ended" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (start): Project build already skipped", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + }, { + message: + "writers/Console: Unexpected project-build-start event for project project.a. " + + "Project build already skipped" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (end): Duplicate project build end", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + }, { + message: "writers/Console: Unexpected duplicate project-build-end event for project project.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (end): Project build not started", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + }, { + message: + "writers/Console: Unexpected project-build-end event for project project.a. " + + "No corresponding project-build-start event handled" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (end): Project build already skipped", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + }, { + message: + "writers/Console: Unexpected project-build-end event for project project.a. " + + "Project build already skipped" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (skip): Duplicate project build skip", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + }, { + message: "writers/Console: Unexpected duplicate project-build-skip event for project project.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (skip): Project build already started", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + }, { + message: + "writers/Console: Unexpected project-build-skip event for project project.a. " + + "Project build already started" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status (skip): Project build already ended", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + t.throws(() => { + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + }, { + message: + "writers/Console: Unexpected project-build-skip event for project project.a. " + + "Project build already ended" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("Build status: Unknown status", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.env.UI5_LOG_LVL = "verbose"; + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "foo", + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.firstCall.firstArg), + `verb writers/Console: Received unknown build-status foo for project project.a\n`, + "Logged expected message"); +}); + +test.serial("ProjectBuild status events", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.project-build-metadata", { + projectName: "project.a", + projectType: "project-type", + tasksToRun: ["task.a"] + }); + + process.emit("ui5.project-build-status", { + level: "verbose", // Should not get logged + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-start", + }); + + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info project.a ${figures.tick} Finished task task.a\n`, + "Logged expected message"); +}); + +test.serial("ProjectBuild status: Unknown project", (t) => { + const {stderrWriteStub} = t.context; + + t.throws(() => { + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + }, { + message: "writers/Console: Unknown project project.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("ProjectBuild status: Unknown task", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + t.throws(() => { + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + }, { + message: "writers/Console: Unknown task task.a for project project.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("ProjectBuild status (start): Duplicate task execution start", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.project-build-metadata", { + projectName: "project.a", + projectType: "project-type", + tasksToRun: ["task.a"] + }); + + process.emit("ui5.project-build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-start", + }); + + t.throws(() => { + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-start", + }); + }, { + message: + "writers/Console: Unexpected duplicate task-start event for project project.a, task task.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("ProjectBuild status (start): Task execution already ended", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.project-build-metadata", { + projectName: "project.a", + projectType: "project-type", + tasksToRun: ["task.a"] + }); + + process.emit("ui5.project-build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-start", + }); + + process.emit("ui5.project-build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + + t.throws(() => { + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-start", + }); + }, { + message: + "writers/Console: Unexpected task-start event for project project.a, task task.a. " + + "Task execution already ended" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("ProjectBuild status (end): Duplicate task execution end", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.project-build-metadata", { + projectName: "project.a", + projectType: "project-type", + tasksToRun: ["task.a"] + }); + + process.emit("ui5.project-build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-start", + }); + + process.emit("ui5.project-build-status", { + level: "silly", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + + t.throws(() => { + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + }, { + message: + "writers/Console: Unexpected duplicate task-end event for project project.a, task task.a" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("ProjectBuild status (end): Task execution not started", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.project-build-metadata", { + projectName: "project.a", + projectType: "project-type", + tasksToRun: ["task.a"] + }); + + t.throws(() => { + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "task-end", + }); + }, { + message: + "writers/Console: Unexpected task-end event for project project.a, task task.a. " + + "No corresponding task-start event handled" + }); + + t.is(stderrWriteStub.callCount, 0, "Logged zero messages"); +}); + +test.serial("ProjectBuild status: Unknown status", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.project-build-metadata", { + projectName: "project.a", + projectType: "project-type", + tasksToRun: ["task.a"] + }); + + process.env.UI5_LOG_LVL = "verbose"; + process.emit("ui5.project-build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + taskName: "task.a", + status: "foo", + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.firstCall.firstArg), + `verb writers/Console: Received unknown project-build-status foo for project project.a\n`, + "Logged expected message"); +}); + +test.serial("No progress bar in test environment", (t) => { + const {consoleWriter} = t.context; + + // Since there is no interactive terminal, progress bar should not be used/returned + t.falsy(consoleWriter._getProgressBar(), "No progress bar returned"); +}); + +test.serial("No progress bar for log level verbose", (t) => { + const {consoleWriter} = t.context; + + process.stderr.isTTY = true; + process.env.UI5_LOG_LVL = "verbose"; + + // Since there is no interactive terminal, progress bar should not be used/returned + t.falsy(consoleWriter._getProgressBar(), "No progress bar returned"); +}); + +test.serial("Progress bar completion does not drop any logs", async (t) => { + const {consoleWriter, stderrWriteStub} = t.context; + + // Force TTY in test env to enable progress bar + process.stderr.isTTY = true; + + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + const pb = consoleWriter._getProgressBar(); + + t.true(pb.isActive, "Progress bar is active"); + + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + // Complete all progress + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-skip", + }); + + process.emit("ui5.log", { + level: "info", + message: "Message 2", + moduleName: "my:module" + }); + + t.false(pb.isActive, "Progress bar is not active anymore"); + + // Wait for async progress bar completion (also fires stop event) + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + + process.emit("ui5.log", { + level: "info", + message: "Message 3", + moduleName: "my:module" + }); + + const allWriteCalls = stderrWriteStub.getCalls(); + t.true(allWriteCalls.length > 2, "Multiple write calls indicate progress bar was active"); + + const firstMessage = allWriteCalls.find((call) => { + return call.firstArg.includes("Message 1"); + }); + t.truthy(firstMessage, "Logged first message"); + t.is(stripAnsi(firstMessage.firstArg), + `info my:module Message 1\n`, + "Logged expected first message"); + + const secondMessage = allWriteCalls.find((call) => { + return call.firstArg.includes("Message 2"); + }); + t.truthy(secondMessage, "Logged second message"); + t.is(stripAnsi(secondMessage.firstArg), + `info my:module Message 2\n`, + "Logged expected second message"); + + const thirdMessage = allWriteCalls.find((call) => { + return call.firstArg.includes("Message 3"); + }); + t.truthy(thirdMessage, "Logged third message"); + t.is(stripAnsi(thirdMessage.firstArg), + `info my:module Message 3\n`, + "Logged expected third message"); +}); + +test.serial("Disable: Stops progress bar", (t) => { + const {consoleWriter, stderrWriteStub} = t.context; + + // Force TTY in test env to enable progress bar + process.stderr.isTTY = true; + + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + const pb = consoleWriter._getProgressBar(); + + t.true(pb.isActive, "Progress bar is active"); + + consoleWriter.disable(); + stderrWriteStub.resetHistory(); + + t.false(pb.isActive, "Progress bar is not active anymore"); + + // Re-enable and log a message + consoleWriter.enable(); + process.emit("ui5.log", { + level: "info", + message: "Message 1", + moduleName: "my:module" + }); + + t.is(stderrWriteStub.callCount, 1, "Logged one message"); +}); + +test.serial("Build metadata events (multiple projects)", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "verbose", // Should not get logged + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + // Independent build of another project + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.b"] + }); + + process.emit("ui5.build-status", { + level: "verbose", // Should not get logged + projectName: "project.b", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "info", + projectName: "project.b", + projectType: "project-type", + status: "project-build-end", + }); + + + t.is(stderrWriteStub.callCount, 2, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info Project 1 of 1: ${figures.tick} Finished building project-type project project.a\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(1).args[0]), + `info Project 1 of 1: ${figures.tick} Finished building project-type project project.b\n`, + "Logged expected message"); +}); + +test.serial("Build metadata events (same project)", (t) => { + const {stderrWriteStub} = t.context; + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "verbose", // Should not get logged + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + // Repeated build of the same project + process.emit("ui5.build-metadata", { + projectsToBuild: ["project.a"] + }); + + process.emit("ui5.build-status", { + level: "verbose", // Should not get logged + projectName: "project.a", + projectType: "project-type", + status: "project-build-start", + }); + + process.emit("ui5.build-status", { + level: "info", + projectName: "project.a", + projectType: "project-type", + status: "project-build-end", + }); + + + t.is(stderrWriteStub.callCount, 2, "Logged one message"); + t.is(stripAnsi(stderrWriteStub.getCall(0).args[0]), + `info Project 1 of 1: ${figures.tick} Finished building project-type project project.a\n`, + "Logged expected message"); + t.is(stripAnsi(stderrWriteStub.getCall(1).args[0]), + `info Project 1 of 1: ${figures.tick} Finished building project-type project project.a\n`, + "Logged expected message"); +});